diff options
author | 2023-11-08 12:20:24 -0800 | |
---|---|---|
committer | 2023-12-05 13:12:53 -0800 | |
commit | deb46f31bf04f17e8a636ec2eead8f75268f64ec (patch) | |
tree | b9aa91d5b4d1cd9558e8154cbe96009ee4cc74bb | |
parent | 87f82bb9ba06450ebba5b65261dc707761dea54a (diff) |
Handle 9patches being used for frros
This adds an api that allows creation of nine patch frros.
We process them the same as aapt2 does so that
when they are used at runtime they work correctly.
API-Coverage-Bug: 314168567
Test: manual and automatic
Bug: 296324826
Change-Id: I40da020189e9ec914fbea0c17f181209347d83de
20 files changed, 267 insertions, 52 deletions
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index 55ec7dae16b1..6e51f009f76c 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -86,6 +86,7 @@ cc_library { static_libs: [ "libidmap2_policies", "libidmap2_protos", + "libpng", ], shared_libs: [ "libandroidfw", @@ -107,6 +108,7 @@ cc_library { "libcutils", "libidmap2_policies", "libidmap2_protos", + "libpng", "libprotobuf-cpp-lite", "libutils", "libz", @@ -185,6 +187,7 @@ cc_test { static_libs: [ "libgmock", "libidmap2_protos", + "libpng", ], target: { android: { @@ -258,6 +261,7 @@ cc_binary { "libbase", "libcutils", "libidmap2", + "libpng", "libprotobuf-cpp-lite", "libutils", "libz", @@ -275,6 +279,7 @@ cc_binary { "libidmap2", "libidmap2_policies", "liblog", + "libpng", "libprotobuf-cpp-lite", "libutils", "libziparchive", diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index d76ca5bdce42..f264125cfde5 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -266,7 +266,8 @@ Status Idmap2Service::createFabricatedOverlay( } else if (res.binaryData.has_value()) { builder.SetResourceValue(res.resourceName, res.binaryData->get(), res.binaryDataOffset, res.binaryDataSize, - res.configuration.value_or(std::string())); + res.configuration.value_or(std::string()), + res.isNinePatch); } else { builder.SetResourceValue(res.resourceName, res.dataType, res.data, res.configuration.value_or(std::string())); diff --git a/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl index 8ebd454705f0..bca2ff3c86f1 100644 --- a/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl +++ b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl @@ -28,4 +28,5 @@ parcelable FabricatedOverlayInternalEntry { @nullable @utf8InCpp String configuration; long binaryDataOffset; long binaryDataSize; + boolean isNinePatch; }
\ No newline at end of file diff --git a/cmds/idmap2/include/idmap2/FabricatedOverlay.h b/cmds/idmap2/include/idmap2/FabricatedOverlay.h index 1e7d4c28f45c..bfcd4b9f5941 100644 --- a/cmds/idmap2/include/idmap2/FabricatedOverlay.h +++ b/cmds/idmap2/include/idmap2/FabricatedOverlay.h @@ -19,6 +19,8 @@ #include <libidmap2/proto/fabricated_v1.pb.h> +#include "androidfw/Streams.h" + #include <istream> #include <map> #include <memory> @@ -51,7 +53,8 @@ struct FabricatedOverlay { std::optional<android::base::borrowed_fd>&& binary_value, off64_t data_binary_offset, size_t data_binary_size, - const std::string& configuration); + const std::string& configuration, + bool nine_patch); inline Builder& setFrroPath(std::string frro_path) { frro_path_ = std::move(frro_path); @@ -70,6 +73,7 @@ struct FabricatedOverlay { off64_t data_binary_offset; size_t data_binary_size; std::string configuration; + bool nine_patch; }; std::string package_name_; @@ -81,7 +85,7 @@ struct FabricatedOverlay { }; struct BinaryData { - android::base::borrowed_fd file_descriptor; + std::unique_ptr<android::InputStream> input_stream; off64_t offset; size_t size; }; diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h index d4490ef47b25..9e463c9a9fca 100644 --- a/cmds/idmap2/include/idmap2/ResourceUtils.h +++ b/cmds/idmap2/include/idmap2/ResourceUtils.h @@ -45,6 +45,7 @@ struct TargetValue { std::optional<android::base::borrowed_fd> data_binary_value; off64_t data_binary_offset; size_t data_binary_size; + bool nine_patch; }; struct TargetValueWithConfig { diff --git a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp index 47daf23c6381..16bb896e939c 100644 --- a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp +++ b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp @@ -20,8 +20,16 @@ #include <sys/types.h> // umask #include <android-base/file.h> +#include <android-base/strings.h> +#include <androidfw/BigBuffer.h> +#include <androidfw/BigBufferStream.h> +#include <androidfw/FileStream.h> +#include <androidfw/Image.h> +#include <androidfw/Png.h> #include <androidfw/ResourceUtils.h> +#include <androidfw/StringPiece.h> #include <androidfw/StringPool.h> +#include <androidfw/Streams.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/io/zero_copy_stream_impl.h> #include <utils/ByteOrder.h> @@ -32,9 +40,9 @@ #include <memory> #include <string> #include <utility> +#include <sys/utsname.h> namespace android::idmap2 { - constexpr auto kBufferSize = 1024; namespace { @@ -81,7 +89,7 @@ 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, "", std::nullopt, 0, 0, configuration}); + Entry{resource_name, data_type, data_value, "", std::nullopt, 0, 0, configuration, false}); return *this; } @@ -89,18 +97,90 @@ 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, std::nullopt, 0, 0, configuration}); + Entry{resource_name, + data_type, + 0, + data_string_value, + std::nullopt, + 0, + 0, + configuration, + false}); return *this; } FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue( const std::string& resource_name, std::optional<android::base::borrowed_fd>&& binary_value, - off64_t data_binary_offset, size_t data_binary_size, const std::string& configuration) { + off64_t data_binary_offset, size_t data_binary_size, const std::string& configuration, + bool nine_patch) { entries_.emplace_back(Entry{resource_name, 0, 0, "", binary_value, - data_binary_offset, data_binary_size, configuration}); + data_binary_offset, data_binary_size, configuration, nine_patch}); return *this; } +static Result<FabricatedOverlay::BinaryData> buildBinaryData( + pb::ResourceValue* pb_value, const TargetValue &value) { + pb_value->set_data_type(Res_value::TYPE_STRING); + size_t binary_size; + off64_t binary_offset; + std::unique_ptr<android::InputStream> binary_stream; + + if (value.nine_patch) { + std::string file_contents; + file_contents.resize(value.data_binary_size); + if (!base::ReadFullyAtOffset(value.data_binary_value->get(), file_contents.data(), + value.data_binary_size, value.data_binary_offset)) { + return Error("Failed to read binary file data."); + } + const StringPiece content(file_contents.c_str(), file_contents.size()); + android::PngChunkFilter png_chunk_filter(content); + android::AndroidLogDiagnostics diag; + auto png = android::ReadPng(&png_chunk_filter, &diag); + if (!png) { + return Error("Error opening file as png"); + } + + std::string err; + std::unique_ptr<NinePatch> nine_patch = NinePatch::Create(png->rows.get(), + png->width, png->height, + &err); + if (!nine_patch) { + return Error("%s", err.c_str()); + } + + png->width -= 2; + png->height -= 2; + memmove(png->rows.get(), png->rows.get() + 1, png->height * sizeof(uint8_t**)); + for (int32_t h = 0; h < png->height; h++) { + memmove(png->rows[h], png->rows[h] + 4, png->width * 4); + } + + android::BigBuffer buffer(value.data_binary_size); + android::BigBufferOutputStream buffer_output_stream(&buffer); + if (!android::WritePng(png.get(), nine_patch.get(), &buffer_output_stream, {}, + &diag, false)) { + return Error("Error writing frro png"); + } + + binary_size = buffer.size(); + binary_offset = 0; + android::BigBufferInputStream *buffer_input_stream + = new android::BigBufferInputStream(std::move(buffer)); + binary_stream.reset(buffer_input_stream); + } else { + binary_size = value.data_binary_size; + binary_offset = value.data_binary_offset; + android::FileInputStream *fis + = new android::FileInputStream(value.data_binary_value.value()); + binary_stream.reset(fis); + } + + return FabricatedOverlay::BinaryData{ + std::move(binary_stream), + binary_offset, + binary_size}; +} + Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() { using ConfigMap = std::map<std::string, TargetValue, std::less<>>; using EntryMap = std::map<std::string, ConfigMap, std::less<>>; @@ -150,7 +230,8 @@ Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() { value->second = TargetValue{res_entry.data_type, res_entry.data_value, res_entry.data_string_value, res_entry.data_binary_value, - res_entry.data_binary_offset, res_entry.data_binary_size}; + res_entry.data_binary_offset, res_entry.data_binary_size, + res_entry.nine_patch}; } pb::FabricatedOverlay overlay_pb; @@ -183,18 +264,20 @@ Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() { 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); - 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> (value.second.data_binary_size)); - total_binary_bytes += value.second.data_binary_size; - binary_files.emplace_back(FabricatedOverlay::BinaryData{ - value.second.data_binary_value->get(), - value.second.data_binary_offset, - value.second.data_binary_size}); - auto ref = string_pool.MakeRef(std::move(uri)); - pb_value->set_data_value(ref.index()); + auto binary_data = buildBinaryData(pb_value, value.second); + if (!binary_data) { + return binary_data.GetError(); + } + pb_value->set_data_type(Res_value::TYPE_STRING); + + 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> (binary_data->size)); + total_binary_bytes += binary_data->size; + binary_files.emplace_back(std::move(*binary_data)); + 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); } @@ -311,9 +394,9 @@ Result<Unit> FabricatedOverlay::ToBinaryStream(std::ostream& stream) const { Write32(stream, (*data)->pb_crc); Write32(stream, total_binary_bytes_); std::string file_contents; - for (const FabricatedOverlay::BinaryData fd : binary_files_) { - file_contents.resize(fd.size); - if (!ReadFullyAtOffset(fd.file_descriptor, file_contents.data(), fd.size, fd.offset)) { + for (const FabricatedOverlay::BinaryData& bd : binary_files_) { + file_contents.resize(bd.size); + if (!bd.input_stream->ReadFullyAtOffset(file_contents.data(), bd.size, bd.offset)) { return Error("Failed to read binary file data."); } stream.write(file_contents.data(), file_contents.length()); diff --git a/cmds/idmap2/self_targeting/SelfTargeting.cpp b/cmds/idmap2/self_targeting/SelfTargeting.cpp index c7f5cf3632c5..7f9c4686c55a 100644 --- a/cmds/idmap2/self_targeting/SelfTargeting.cpp +++ b/cmds/idmap2/self_targeting/SelfTargeting.cpp @@ -53,7 +53,7 @@ CreateFrroFile(std::string& out_err_result, const std::string& packageName, if (entry_params.data_binary_value.has_value()) { builder.SetResourceValue(entry_params.resource_name, *entry_params.data_binary_value, entry_params.binary_data_offset, entry_params.binary_data_size, - entry_params.configuration); + entry_params.configuration, entry_params.nine_patch); } else if (dataType >= Res_value::TYPE_FIRST_INT && dataType <= Res_value::TYPE_LAST_INT) { builder.SetResourceValue(entry_params.resource_name, dataType, entry_params.data_value, entry_params.configuration); diff --git a/cmds/idmap2/tests/FabricatedOverlayTests.cpp b/cmds/idmap2/tests/FabricatedOverlayTests.cpp index b460bb33f559..6b1c7e83c826 100644 --- a/cmds/idmap2/tests/FabricatedOverlayTests.cpp +++ b/cmds/idmap2/tests/FabricatedOverlayTests.cpp @@ -59,7 +59,7 @@ TEST(FabricatedOverlayTests, SetResourceValue) { Res_value::TYPE_STRING, "foobar", "en-rUS-normal-xxhdpi-v21") - .SetResourceValue("com.example.target:drawable/dr1", fd, 0, 8341, "port-xxhdpi-v7") + .SetResourceValue("com.example.target:drawable/dr1", fd, 0, 8341, "port-xxhdpi-v7", false) .setFrroPath("/foo/bar/biz.frro") .Build(); ASSERT_TRUE(overlay); diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index a3448fda60d9..a384305da43d 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -269,7 +269,7 @@ TEST(IdmapTests, FabricatedOverlay) { .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, 0, 8341, "port-xxhdpi-v7") + .SetResourceValue("drawable/dr1", fd, 0, 8341, "port-xxhdpi-v7", false) .setFrroPath("/foo/bar/biz.frro") .Build(); diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp index 40f98c2f351b..db44c23a41f9 100644 --- a/cmds/idmap2/tests/ResourceMappingTests.cpp +++ b/cmds/idmap2/tests/ResourceMappingTests.cpp @@ -212,7 +212,7 @@ TEST(ResourceMappingTests, FabricatedOverlay) { .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, 0, 8341, "") + .SetResourceValue("drawable/dr1", fd, 0, 8341, "", false) .setFrroPath("/foo/bar/biz.frro") .Build(); diff --git a/core/api/current.txt b/core/api/current.txt index f01563a90fb3..3e229bb66fa2 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -11787,6 +11787,7 @@ package android.content.om { public class FabricatedOverlay { ctor public FabricatedOverlay(@NonNull String, @NonNull String); method @NonNull public android.content.om.OverlayIdentifier getIdentifier(); + method @FlaggedApi("android.content.res.nine_patch_frro") @NonNull public void setNinePatchResourceValue(@NonNull String, @NonNull android.os.ParcelFileDescriptor, @Nullable String); method @NonNull public void setResourceValue(@NonNull String, @IntRange(from=android.util.TypedValue.TYPE_FIRST_INT, to=android.util.TypedValue.TYPE_LAST_INT) int, int, @Nullable String); method @NonNull public void setResourceValue(@NonNull String, int, @NonNull String, @Nullable String); method @NonNull public void setResourceValue(@NonNull String, @NonNull android.os.ParcelFileDescriptor, @Nullable String); diff --git a/core/java/android/content/om/FabricatedOverlay.java b/core/java/android/content/om/FabricatedOverlay.java index df2d7e70880f..40ffb0ff5c80 100644 --- a/core/java/android/content/om/FabricatedOverlay.java +++ b/core/java/android/content/om/FabricatedOverlay.java @@ -281,8 +281,8 @@ public class FabricatedOverlay { @NonNull ParcelFileDescriptor value, @Nullable String configuration) { ensureValidResourceName(resourceName); - mEntries.add( - generateFabricatedOverlayInternalEntry(resourceName, value, configuration)); + mEntries.add(generateFabricatedOverlayInternalEntry( + resourceName, value, configuration, false)); return this; } @@ -361,6 +361,16 @@ public class FabricatedOverlay { } /** + * Set the package that owns the overlay + * + * @param owningPackage the package that should own the overlay. + * @hide + */ + public void setOwningPackage(@NonNull String owningPackage) { + mOverlay.packageName = owningPackage; + } + + /** * Set the target overlayable name of the overlay * * The target package defines may define several overlayables. The {@link FabricatedOverlay} @@ -442,13 +452,14 @@ public class FabricatedOverlay { @NonNull private static FabricatedOverlayInternalEntry generateFabricatedOverlayInternalEntry( @NonNull String resourceName, @NonNull ParcelFileDescriptor parcelFileDescriptor, - @Nullable String configuration) { + @Nullable String configuration, boolean isNinePatch) { final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry(); entry.resourceName = resourceName; entry.binaryData = Objects.requireNonNull(parcelFileDescriptor); entry.configuration = configuration; entry.binaryDataOffset = 0; entry.binaryDataSize = parcelFileDescriptor.getStatSize(); + entry.isNinePatch = isNinePatch; return entry; } @@ -534,7 +545,26 @@ public class FabricatedOverlay { @Nullable String configuration) { ensureValidResourceName(resourceName); mOverlay.entries.add( - generateFabricatedOverlayInternalEntry(resourceName, value, configuration)); + generateFabricatedOverlayInternalEntry(resourceName, value, configuration, false)); + } + + /** + * Sets the resource value in the fabricated overlay from a nine patch. + * + * @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 + */ + @NonNull + @FlaggedApi(android.content.res.Flags.FLAG_NINE_PATCH_FRRO) + public void setNinePatchResourceValue( + @NonNull String resourceName, + @NonNull ParcelFileDescriptor value, + @Nullable String configuration) { + ensureValidResourceName(resourceName); + mOverlay.entries.add( + generateFabricatedOverlayInternalEntry(resourceName, value, configuration, true)); } /** diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig index 40592a151fa7..3a00d91bfb9f 100644 --- a/core/java/android/content/res/flags.aconfig +++ b/core/java/android/content/res/flags.aconfig @@ -24,3 +24,10 @@ flag { # This flag is read in PackageParser at boot time, and in aapt2 which is a build tool. is_fixed_read_only: true } + +flag { + name: "nine_patch_frro" + namespace: "resource_manager" + description: "Feature flag for creating an frro from a 9-patch" + bug: "309232726" +} diff --git a/core/jni/com_android_internal_content_om_OverlayManagerImpl.cpp b/core/jni/com_android_internal_content_om_OverlayManagerImpl.cpp index 0c39a6928391..358531a81979 100644 --- a/core/jni/com_android_internal_content_om_OverlayManagerImpl.cpp +++ b/core/jni/com_android_internal_content_om_OverlayManagerImpl.cpp @@ -46,6 +46,7 @@ static struct fabricated_overlay_internal_entry_offsets_t { jfieldID configuration; jfieldID binaryDataOffset; jfieldID binaryDataSize; + jfieldID isNinePatch; } gFabricatedOverlayInternalEntryOffsets; static struct parcel_file_descriptor_offsets_t { @@ -288,13 +289,16 @@ static void CreateFrroFile(JNIEnv* env, jclass /*clazz*/, jstring jsFrroFilePath env->GetLongField(entry, gFabricatedOverlayInternalEntryOffsets.binaryDataOffset); const auto data_size = env->GetLongField(entry, gFabricatedOverlayInternalEntryOffsets.binaryDataSize); + const auto nine_patch = + env->GetBooleanField(entry, gFabricatedOverlayInternalEntryOffsets.isNinePatch); entries_params.push_back( FabricatedOverlayEntryParameters{resourceName.c_str(), (DataType)dataType, (DataValue)data, string_data.value_or(std::string()), binary_data, static_cast<off64_t>(data_offset), static_cast<size_t>(data_size), - configuration.value_or(std::string())}); + configuration.value_or(std::string()), + static_cast<bool>(nine_patch)}); ALOGV("resourceName = %s, dataType = 0x%08x, data = 0x%08x, dataString = %s," " binaryData = %d, configuration = %s", resourceName.c_str(), dataType, data, string_data.value_or(std::string()).c_str(), @@ -455,6 +459,9 @@ int register_com_android_internal_content_om_OverlayManagerImpl(JNIEnv* env) { gFabricatedOverlayInternalEntryOffsets.binaryDataSize = GetFieldIDOrDie(env, gFabricatedOverlayInternalEntryOffsets.classObject, "binaryDataSize", "J"); + gFabricatedOverlayInternalEntryOffsets.isNinePatch = + GetFieldIDOrDie(env, gFabricatedOverlayInternalEntryOffsets.classObject, "isNinePatch", + "Z"); jclass parcelFileDescriptorClass = android::FindClassOrDie(env, "android/os/ParcelFileDescriptor"); diff --git a/libs/androidfw/FileStream.cpp b/libs/androidfw/FileStream.cpp index b86c9cb729d4..e8989490fe2c 100644 --- a/libs/androidfw/FileStream.cpp +++ b/libs/androidfw/FileStream.cpp @@ -22,6 +22,7 @@ #include "android-base/errors.h" #include "android-base/file.h" // for O_BINARY +#include "android-base/logging.h" #include "android-base/macros.h" #include "android-base/utf8.h" @@ -37,9 +38,9 @@ using ::android::base::unique_fd; namespace android { FileInputStream::FileInputStream(const std::string& path, size_t buffer_capacity) - : buffer_capacity_(buffer_capacity) { + : should_close_(true), buffer_capacity_(buffer_capacity) { int mode = O_RDONLY | O_CLOEXEC | O_BINARY; - fd_.reset(TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), mode))); + fd_ = TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), mode)); if (fd_ == -1) { error_ = SystemErrorCodeToString(errno); } else { @@ -48,7 +49,17 @@ FileInputStream::FileInputStream(const std::string& path, size_t buffer_capacity } FileInputStream::FileInputStream(int fd, size_t buffer_capacity) - : fd_(fd), buffer_capacity_(buffer_capacity) { + : fd_(fd), should_close_(true), buffer_capacity_(buffer_capacity) { + if (fd_ < 0) { + error_ = "Bad File Descriptor"; + } else { + buffer_.reset(new uint8_t[buffer_capacity_]); + } +} + +FileInputStream::FileInputStream(android::base::borrowed_fd fd, size_t buffer_capacity) + : fd_(fd.get()), should_close_(false), buffer_capacity_(buffer_capacity) { + if (fd_ < 0) { error_ = "Bad File Descriptor"; } else { @@ -56,6 +67,7 @@ FileInputStream::FileInputStream(int fd, size_t buffer_capacity) } } + bool FileInputStream::Next(const void** data, size_t* size) { if (HadError()) { return false; @@ -73,7 +85,12 @@ bool FileInputStream::Next(const void** data, size_t* size) { ssize_t n = TEMP_FAILURE_RETRY(read(fd_, buffer_.get(), buffer_capacity_)); if (n < 0) { error_ = SystemErrorCodeToString(errno); - fd_.reset(); + if (fd_ != -1) { + if (should_close_) { + close(fd_); + } + fd_ = -1; + } buffer_.reset(); return false; } diff --git a/libs/androidfw/include/androidfw/BigBufferStream.h b/libs/androidfw/include/androidfw/BigBufferStream.h index e55fe0d653cc..c23194bae423 100644 --- a/libs/androidfw/include/androidfw/BigBufferStream.h +++ b/libs/androidfw/include/androidfw/BigBufferStream.h @@ -24,8 +24,13 @@ namespace android { class BigBufferInputStream : public KnownSizeInputStream { public: inline explicit BigBufferInputStream(const BigBuffer* buffer) - : buffer_(buffer), iter_(buffer->begin()) { + : owning_buffer_(0), buffer_(buffer), iter_(buffer->begin()) { } + + inline explicit BigBufferInputStream(android::BigBuffer&& buffer) + : owning_buffer_(std::move(buffer)), buffer_(&owning_buffer_), iter_(buffer_->begin()) { + } + virtual ~BigBufferInputStream() = default; bool Next(const void** data, size_t* size) override; @@ -47,6 +52,7 @@ class BigBufferInputStream : public KnownSizeInputStream { private: DISALLOW_COPY_AND_ASSIGN(BigBufferInputStream); + android::BigBuffer owning_buffer_; const BigBuffer* buffer_; BigBuffer::const_iterator iter_; size_t offset_ = 0; diff --git a/libs/androidfw/include/androidfw/FileStream.h b/libs/androidfw/include/androidfw/FileStream.h index fb84a91a00de..87c42d12f825 100644 --- a/libs/androidfw/include/androidfw/FileStream.h +++ b/libs/androidfw/include/androidfw/FileStream.h @@ -18,6 +18,7 @@ #include <memory> #include <string> +#include <unistd.h> #include "Streams.h" #include "android-base/macros.h" @@ -35,6 +36,16 @@ class FileInputStream : public InputStream { // Take ownership of `fd`. explicit FileInputStream(int fd, size_t buffer_capacity = kDefaultBufferCapacity); + // Take ownership of `fd`. + explicit FileInputStream(android::base::borrowed_fd fd, + size_t buffer_capacity = kDefaultBufferCapacity); + + ~FileInputStream() { + if (should_close_ && (fd_ != -1)) { + close(fd_); + } + } + bool Next(const void** data, size_t* size) override; void BackUp(size_t count) override; @@ -50,8 +61,9 @@ class FileInputStream : public InputStream { private: DISALLOW_COPY_AND_ASSIGN(FileInputStream); - android::base::unique_fd fd_; + int fd_ = -1; std::string error_; + bool should_close_; std::unique_ptr<uint8_t[]> buffer_; size_t buffer_capacity_ = 0u; size_t buffer_offset_ = 0u; diff --git a/libs/androidfw/include/androidfw/IDiagnostics.h b/libs/androidfw/include/androidfw/IDiagnostics.h index 865a298f8389..d1dda818d97c 100644 --- a/libs/androidfw/include/androidfw/IDiagnostics.h +++ b/libs/androidfw/include/androidfw/IDiagnostics.h @@ -17,10 +17,15 @@ #ifndef _ANDROID_DIAGNOSTICS_H #define _ANDROID_DIAGNOSTICS_H +// on some systems ERROR is defined as 0 so android::base::ERROR becomes android::base::0 +// which doesn't compile. We undef it here to avoid that and because we don't ever need that def. +#undef ERROR + #include <sstream> #include <string> #include "Source.h" +#include "android-base/logging.h" #include "android-base/macros.h" #include "androidfw/StringPiece.h" @@ -144,6 +149,36 @@ class NoOpDiagnostics : public IDiagnostics { DISALLOW_COPY_AND_ASSIGN(NoOpDiagnostics); }; +class AndroidLogDiagnostics : public IDiagnostics { + public: + AndroidLogDiagnostics() = default; + + void Log(Level level, DiagMessageActual& actual_msg) override { + android::base::LogSeverity severity; + switch (level) { + case Level::Error: + severity = android::base::ERROR; + break; + + case Level::Warn: + severity = android::base::WARNING; + break; + + case Level::Note: + severity = android::base::INFO; + break; + } + if (!actual_msg.source.path.empty()) { + LOG(severity) << actual_msg.source << ": " + actual_msg.message; + } else { + LOG(severity) << actual_msg.message; + } + } + + DISALLOW_COPY_AND_ASSIGN(AndroidLogDiagnostics); +}; + + } // namespace android #endif /* _ANDROID_DIAGNOSTICS_H */ diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index fdb355192676..c0514fdff469 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -1875,6 +1875,7 @@ struct FabricatedOverlayEntryParameters { off64_t binary_data_offset; size_t binary_data_size; std::string configuration; + bool nine_patch; }; class AssetManager2; diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java index f77d789891f7..d9c8ec6e5ed1 100644 --- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java +++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java @@ -317,11 +317,11 @@ final class OverlayManagerShellCommand extends ShellCommand { return 1; } final String overlayPackageName = "com.android.shell"; - FabricatedOverlay.Builder overlayBuilder = new FabricatedOverlay.Builder( - overlayPackageName, name, targetPackage) - .setTargetOverlayable(targetOverlayable); + FabricatedOverlay overlay = new FabricatedOverlay(name, targetPackage); + overlay.setTargetOverlayable(targetOverlayable); + overlay.setOwningPackage(overlayPackageName); if (filename != null) { - int result = addOverlayValuesFromXml(overlayBuilder, targetPackage, filename); + int result = addOverlayValuesFromXml(overlay, targetPackage, filename); if (result != 0) { return result; } @@ -329,18 +329,18 @@ final class OverlayManagerShellCommand extends ShellCommand { final String resourceName = getNextArgRequired(); final String typeStr = getNextArgRequired(); final String strData = String.join(" ", peekRemainingArgs()); - if (addOverlayValue(overlayBuilder, resourceName, typeStr, strData, config) != 0) { + if (addOverlayValue(overlay, resourceName, typeStr, strData, config) != 0) { return 1; } } mInterface.commit(new OverlayManagerTransaction.Builder() - .registerFabricatedOverlay(overlayBuilder.build()).build()); + .registerFabricatedOverlay(overlay).build()); return 0; } private int addOverlayValuesFromXml( - FabricatedOverlay.Builder overlayBuilder, String targetPackage, String filename) { + FabricatedOverlay overlay, String targetPackage, String filename) { final PrintWriter err = getErrPrintWriter(); File file = new File(filename); if (!file.exists()) { @@ -388,7 +388,7 @@ final class OverlayManagerShellCommand extends ShellCommand { return 1; } String config = parser.getAttributeValue(null, "config"); - if (addOverlayValue(overlayBuilder, targetPackage + ':' + target, + if (addOverlayValue(overlay, targetPackage + ':' + target, overlayType, value, config) != 0) { return 1; } @@ -405,8 +405,8 @@ final class OverlayManagerShellCommand extends ShellCommand { return 0; } - private int addOverlayValue(FabricatedOverlay.Builder overlayBuilder, - String resourceName, String typeString, String valueString, String configuration) { + private int addOverlayValue(FabricatedOverlay overlay, String resourceName, String typeString, + String valueString, String configuration) { final int type; typeString = typeString.toLowerCase(Locale.getDefault()); if (TYPE_MAP.containsKey(typeString)) { @@ -419,10 +419,14 @@ final class OverlayManagerShellCommand extends ShellCommand { } } if (type == TypedValue.TYPE_STRING) { - overlayBuilder.setResourceValue(resourceName, type, valueString, configuration); + overlay.setResourceValue(resourceName, type, valueString, configuration); } else if (type < 0) { ParcelFileDescriptor pfd = openFileForSystem(valueString, "r"); - overlayBuilder.setResourceValue(resourceName, pfd, configuration); + if (valueString.endsWith(".9.png")) { + overlay.setNinePatchResourceValue(resourceName, pfd, configuration); + } else { + overlay.setResourceValue(resourceName, pfd, configuration); + } } else { final int intData; if (valueString.startsWith("0x")) { @@ -430,7 +434,7 @@ final class OverlayManagerShellCommand extends ShellCommand { } else { intData = Integer.parseUnsignedInt(valueString); } - overlayBuilder.setResourceValue(resourceName, type, intData, configuration); + overlay.setResourceValue(resourceName, type, intData, configuration); } return 0; } |