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
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 55ec7da..6e51f00 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -86,6 +86,7 @@
static_libs: [
"libidmap2_policies",
"libidmap2_protos",
+ "libpng",
],
shared_libs: [
"libandroidfw",
@@ -107,6 +108,7 @@
"libcutils",
"libidmap2_policies",
"libidmap2_protos",
+ "libpng",
"libprotobuf-cpp-lite",
"libutils",
"libz",
@@ -185,6 +187,7 @@
static_libs: [
"libgmock",
"libidmap2_protos",
+ "libpng",
],
target: {
android: {
@@ -258,6 +261,7 @@
"libbase",
"libcutils",
"libidmap2",
+ "libpng",
"libprotobuf-cpp-lite",
"libutils",
"libz",
@@ -275,6 +279,7 @@
"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 d76ca5b..f264125 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -266,7 +266,8 @@
} 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 8ebd454..bca2ff3 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 @@
@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 1e7d4c2..bfcd4b9 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 @@
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 @@
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 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 d4490ef4..9e463c9 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -45,6 +45,7 @@
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 47daf23..16bb896 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 @@
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 @@
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 @@
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 @@
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 @@
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 c7f5cf3..7f9c468 100644
--- a/cmds/idmap2/self_targeting/SelfTargeting.cpp
+++ b/cmds/idmap2/self_targeting/SelfTargeting.cpp
@@ -53,7 +53,7 @@
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 b460bb3..6b1c7e8 100644
--- a/cmds/idmap2/tests/FabricatedOverlayTests.cpp
+++ b/cmds/idmap2/tests/FabricatedOverlayTests.cpp
@@ -59,7 +59,7 @@
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 a3448fd..a384305 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -269,7 +269,7 @@
.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 40f98c2..db44c23 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -212,7 +212,7 @@
.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 f01563a..3e229bb 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11787,6 +11787,7 @@
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 df2d7e7..40ffb0f 100644
--- a/core/java/android/content/om/FabricatedOverlay.java
+++ b/core/java/android/content/om/FabricatedOverlay.java
@@ -281,8 +281,8 @@
@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 @@
}
/**
+ * 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 @@
@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 @@
@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 40592a1..3a00d91 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -24,3 +24,10 @@
# 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 0c39a69..358531a 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 @@
jfieldID configuration;
jfieldID binaryDataOffset;
jfieldID binaryDataSize;
+ jfieldID isNinePatch;
} gFabricatedOverlayInternalEntryOffsets;
static struct parcel_file_descriptor_offsets_t {
@@ -288,13 +289,16 @@
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 @@
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 b86c9cb..e898949 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 @@
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,7 @@
}
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 {
@@ -56,6 +57,17 @@
}
}
+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 {
+ buffer_.reset(new uint8_t[buffer_capacity_]);
+ }
+}
+
+
bool FileInputStream::Next(const void** data, size_t* size) {
if (HadError()) {
return false;
@@ -73,7 +85,12 @@
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 e55fe0d..c23194b 100644
--- a/libs/androidfw/include/androidfw/BigBufferStream.h
+++ b/libs/androidfw/include/androidfw/BigBufferStream.h
@@ -24,8 +24,13 @@
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 @@
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 fb84a91..87c42d1 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 @@
// 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 @@
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 865a298..d1dda81 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 @@
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 fdb3551..c0514fd 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1875,6 +1875,7 @@
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 f77d7898..d9c8ec6 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 @@
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 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 @@
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 @@
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 @@
}
}
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 @@
} else {
intData = Integer.parseUnsignedInt(valueString);
}
- overlayBuilder.setResourceValue(resourceName, type, intData, configuration);
+ overlay.setResourceValue(resourceName, type, intData, configuration);
}
return 0;
}