summaryrefslogtreecommitdiff
path: root/libs/androidfw
diff options
context:
space:
mode:
Diffstat (limited to 'libs/androidfw')
-rw-r--r--libs/androidfw/Android.bp10
-rw-r--r--libs/androidfw/ApkAssets.cpp565
-rw-r--r--libs/androidfw/Asset.cpp57
-rw-r--r--libs/androidfw/AssetManager2.cpp533
-rw-r--r--libs/androidfw/CursorWindow.cpp6
-rw-r--r--libs/androidfw/DisplayEventDispatcher.cpp153
-rw-r--r--libs/androidfw/DynamicLibManager.cpp34
-rw-r--r--libs/androidfw/Idmap.cpp270
-rw-r--r--libs/androidfw/LoadedArsc.cpp77
-rw-r--r--libs/androidfw/ResourceTypes.cpp53
-rw-r--r--libs/androidfw/TEST_MAPPING3
-rw-r--r--libs/androidfw/include/androidfw/ApkAssets.h147
-rw-r--r--libs/androidfw/include/androidfw/Asset.h30
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h128
-rw-r--r--libs/androidfw/include/androidfw/DisplayEventDispatcher.h51
-rw-r--r--libs/androidfw/include/androidfw/DynamicLibManager.h48
-rw-r--r--libs/androidfw/include/androidfw/Idmap.h159
-rw-r--r--libs/androidfw/include/androidfw/LoadedArsc.h73
-rw-r--r--libs/androidfw/include/androidfw/MutexGuard.h3
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h153
-rw-r--r--libs/androidfw/include/androidfw/Util.h7
-rw-r--r--libs/androidfw/tests/ApkAssets_test.cpp51
-rw-r--r--libs/androidfw/tests/AssetManager2_test.cpp58
-rw-r--r--libs/androidfw/tests/AttributeResolution_test.cpp2
-rw-r--r--libs/androidfw/tests/Idmap_test.cpp269
-rw-r--r--libs/androidfw/tests/LoadedArsc_test.cpp111
-rw-r--r--libs/androidfw/tests/Theme_test.cpp2
-rw-r--r--libs/androidfw/tests/data/lib_two/R.h12
-rw-r--r--libs/androidfw/tests/data/lib_two/lib_two.apkbin1426 -> 1586 bytes
-rw-r--r--libs/androidfw/tests/data/lib_two/res/values/values.xml11
-rw-r--r--libs/androidfw/tests/data/libclient/R.h1
-rw-r--r--libs/androidfw/tests/data/libclient/libclient.apkbin1982 -> 2168 bytes
-rw-r--r--libs/androidfw/tests/data/libclient/res/values/values.xml6
-rw-r--r--libs/androidfw/tests/data/loader/AndroidManifest.xml20
-rwxr-xr-xlibs/androidfw/tests/data/loader/build27
-rw-r--r--libs/androidfw/tests/data/loader/res/values/public.xml19
-rw-r--r--libs/androidfw/tests/data/loader/res/values/values.xml19
-rw-r--r--libs/androidfw/tests/data/loader/resources.arscbin0 -> 652 bytes
-rw-r--r--libs/androidfw/tests/data/overlay/AndroidManifest.xml8
-rw-r--r--libs/androidfw/tests/data/overlay/R.h38
-rwxr-xr-xlibs/androidfw/tests/data/overlay/build11
-rw-r--r--libs/androidfw/tests/data/overlay/overlay.apkbin5211 -> 2988 bytes
-rw-r--r--libs/androidfw/tests/data/overlay/overlay.idmapbin0 -> 1137 bytes
-rw-r--r--libs/androidfw/tests/data/overlay/res/layout/hello_view.xml20
-rw-r--r--libs/androidfw/tests/data/overlay/res/values/values.xml14
-rw-r--r--libs/androidfw/tests/data/overlay/res/xml/overlays.xml47
-rw-r--r--libs/androidfw/tests/data/overlayable/R.h37
-rwxr-xr-xlibs/androidfw/tests/data/overlayable/build5
-rw-r--r--libs/androidfw/tests/data/overlayable/overlayable.apkbin3443 -> 6306 bytes
-rw-r--r--libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml20
-rw-r--r--libs/androidfw/tests/data/overlayable/res/values/overlayable.xml54
-rw-r--r--libs/androidfw/tests/data/overlayable/res/values/public.xml17
-rw-r--r--libs/androidfw/tests/data/overlayable/res/values/values.xml11
-rw-r--r--libs/androidfw/tests/data/system/R.h7
-rw-r--r--libs/androidfw/tests/data/system/res/values/public.xml20
-rw-r--r--libs/androidfw/tests/data/system/res/values/values.xml20
-rw-r--r--libs/androidfw/tests/data/system/system.apkbin1624 -> 1976 bytes
57 files changed, 2436 insertions, 1061 deletions
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index eeaefc5b157c..f87f98a59a12 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -44,6 +44,7 @@ cc_library {
"AttributeResolution.cpp",
"ChunkIterator.cpp",
"ConfigDescription.cpp",
+ "DynamicLibManager.cpp",
"Idmap.cpp",
"LoadedArsc.cpp",
"Locale.cpp",
@@ -67,7 +68,6 @@ cc_library {
"BackupData.cpp",
"BackupHelpers.cpp",
"CursorWindow.cpp",
- "DisplayEventDispatcher.cpp",
],
shared_libs: [
"libziparchive",
@@ -75,7 +75,6 @@ cc_library {
"libbinder",
"liblog",
"libcutils",
- "libgui",
"libutils",
"libz",
],
@@ -166,7 +165,11 @@ cc_test {
static_libs: common_test_libs + ["liblog", "libz"],
},
},
- data: ["tests/data/**/*.apk"],
+ data: [
+ "tests/data/**/*.apk",
+ "tests/data/**/*.arsc",
+ "tests/data/**/*.idmap",
+ ],
test_suites: ["device-tests"],
}
@@ -188,4 +191,3 @@ cc_benchmark {
shared_libs: common_test_libs,
data: ["tests/data/**/*.apk"],
}
-
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 09705e1aae67..918e7af12d31 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -21,6 +21,7 @@
#include "android-base/errors.h"
#include "android-base/file.h"
#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
#include "android-base/unique_fd.h"
#include "android-base/utf8.h"
#include "utils/Compat.h"
@@ -40,23 +41,342 @@ using base::unique_fd;
static const std::string kResourcesArsc("resources.arsc");
-ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle,
- const std::string& path,
- time_t last_mod_time)
- : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time) {
+ApkAssets::ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,
+ std::string path,
+ time_t last_mod_time,
+ package_property_t property_flags)
+ : assets_provider_(std::move(assets_provider)),
+ path_(std::move(path)),
+ last_mod_time_(last_mod_time),
+ property_flags_(property_flags) {
}
-std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) {
- return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, false /*load_as_shared_library*/);
+// Provides asset files from a zip file.
+class ZipAssetsProvider : public AssetsProvider {
+ public:
+ ~ZipAssetsProvider() override = default;
+
+ static std::unique_ptr<const AssetsProvider> Create(const std::string& path) {
+ ::ZipArchiveHandle unmanaged_handle;
+ const int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
+ if (result != 0) {
+ LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
+ ::CloseArchive(unmanaged_handle);
+ return {};
+ }
+
+ return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider(path, path, unmanaged_handle));
+ }
+
+ static std::unique_ptr<const AssetsProvider> Create(
+ unique_fd fd, const std::string& friendly_name, const off64_t offset = 0,
+ const off64_t length = ApkAssets::kUnknownLength) {
+
+ ::ZipArchiveHandle unmanaged_handle;
+ const int32_t result = (length == ApkAssets::kUnknownLength)
+ ? ::OpenArchiveFd(fd.release(), friendly_name.c_str(), &unmanaged_handle)
+ : ::OpenArchiveFdRange(fd.release(), friendly_name.c_str(), &unmanaged_handle, length,
+ offset);
+
+ if (result != 0) {
+ LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset
+ << " and length " << length << ": " << ::ErrorCodeString(result);
+ ::CloseArchive(unmanaged_handle);
+ return {};
+ }
+
+ return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider({}, friendly_name,
+ unmanaged_handle));
+ }
+
+ // Iterate over all files and directories within the zip. The order of iteration is not
+ // guaranteed to be the same as the order of elements in the central directory but is stable for a
+ // given zip file.
+ bool ForEachFile(const std::string& root_path,
+ const std::function<void(const StringPiece&, FileType)>& f) const override {
+ // If this is a resource loader from an .arsc, there will be no zip handle
+ if (zip_handle_ == nullptr) {
+ return false;
+ }
+
+ std::string root_path_full = root_path;
+ if (root_path_full.back() != '/') {
+ root_path_full += '/';
+ }
+
+ void* cookie;
+ if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
+ return false;
+ }
+
+ std::string name;
+ ::ZipEntry entry{};
+
+ // We need to hold back directories because many paths will contain them and we want to only
+ // surface one.
+ std::set<std::string> dirs{};
+
+ int32_t result;
+ while ((result = ::Next(cookie, &entry, &name)) == 0) {
+ StringPiece full_file_path(name);
+ StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
+
+ if (!leaf_file_path.empty()) {
+ auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
+ if (iter != leaf_file_path.end()) {
+ std::string dir =
+ leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
+ dirs.insert(std::move(dir));
+ } else {
+ f(leaf_file_path, kFileTypeRegular);
+ }
+ }
+ }
+ ::EndIteration(cookie);
+
+ // Now present the unique directories.
+ for (const std::string& dir : dirs) {
+ f(dir, kFileTypeDirectory);
+ }
+
+ // -1 is end of iteration, anything else is an error.
+ return result == -1;
+ }
+
+ protected:
+ std::unique_ptr<Asset> OpenInternal(
+ const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
+ if (file_exists) {
+ *file_exists = false;
+ }
+
+ ::ZipEntry entry;
+ int32_t result = ::FindEntry(zip_handle_.get(), path, &entry);
+ if (result != 0) {
+ return {};
+ }
+
+ if (file_exists) {
+ *file_exists = true;
+ }
+
+ const int fd = ::GetFileDescriptor(zip_handle_.get());
+ const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get());
+ if (entry.method == kCompressDeflated) {
+ std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
+ if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.compressed_length,
+ true /*readOnly*/)) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
+ return {};
+ }
+
+ std::unique_ptr<Asset> asset =
+ Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
+ if (asset == nullptr) {
+ LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << friendly_name_ << "'";
+ return {};
+ }
+ return asset;
+ } else {
+ std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
+ if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.uncompressed_length,
+ true /*readOnly*/)) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
+ return {};
+ }
+
+ unique_fd ufd;
+ if (!GetPath()) {
+ // If the `path` is not set, create a new `fd` for the new Asset to own in order to create
+ // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used
+ // to create new file descriptors.
+ ufd = unique_fd(dup(fd));
+ if (!ufd.ok()) {
+ LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'";
+ return {};
+ }
+ }
+
+ std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map),
+ std::move(ufd), mode);
+ if (asset == nullptr) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
+ return {};
+ }
+ return asset;
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ZipAssetsProvider);
+
+ explicit ZipAssetsProvider(std::string path,
+ std::string friendly_name,
+ ZipArchiveHandle unmanaged_handle)
+ : zip_handle_(unmanaged_handle, ::CloseArchive),
+ path_(std::move(path)),
+ friendly_name_(std::move(friendly_name)) { }
+
+ const char* GetPath() const {
+ return path_.empty() ? nullptr : path_.c_str();
+ }
+
+ using ZipArchivePtr = std::unique_ptr<ZipArchive, void (*)(ZipArchiveHandle)>;
+ ZipArchivePtr zip_handle_;
+ std::string path_;
+ std::string friendly_name_;
+};
+
+class DirectoryAssetsProvider : AssetsProvider {
+ public:
+ ~DirectoryAssetsProvider() override = default;
+
+ static std::unique_ptr<const AssetsProvider> Create(const std::string& path) {
+ struct stat sb{};
+ const int result = stat(path.c_str(), &sb);
+ if (result == -1) {
+ LOG(ERROR) << "Failed to find directory '" << path << "'.";
+ return nullptr;
+ }
+
+ if (!S_ISDIR(sb.st_mode)) {
+ LOG(ERROR) << "Path '" << path << "' is not a directory.";
+ return nullptr;
+ }
+
+ return std::unique_ptr<AssetsProvider>(new DirectoryAssetsProvider(path));
+ }
+
+ protected:
+ std::unique_ptr<Asset> OpenInternal(
+ const std::string& path, Asset::AccessMode /* mode */, bool* file_exists) const override {
+ const std::string resolved_path = ResolvePath(path);
+ if (file_exists) {
+ struct stat sb{};
+ const int result = stat(resolved_path.c_str(), &sb);
+ *file_exists = result != -1 && S_ISREG(sb.st_mode);
+ }
+
+ return ApkAssets::CreateAssetFromFile(resolved_path);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DirectoryAssetsProvider);
+
+ explicit DirectoryAssetsProvider(std::string path) : path_(std::move(path)) { }
+
+ inline std::string ResolvePath(const std::string& path) const {
+ return base::StringPrintf("%s%c%s", path_.c_str(), OS_PATH_SEPARATOR, path.c_str());
+ }
+
+ const std::string path_;
+};
+
+// AssetProvider implementation that does not provide any assets. Used for ApkAssets::LoadEmpty.
+class EmptyAssetsProvider : public AssetsProvider {
+ public:
+ EmptyAssetsProvider() = default;
+ ~EmptyAssetsProvider() override = default;
+
+ protected:
+ std::unique_ptr<Asset> OpenInternal(const std::string& /*path */,
+ Asset::AccessMode /* mode */,
+ bool* file_exists) const override {
+ if (file_exists) {
+ *file_exists = false;
+ }
+ return nullptr;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(EmptyAssetsProvider);
+};
+
+// AssetProvider implementation
+class MultiAssetsProvider : public AssetsProvider {
+ public:
+ ~MultiAssetsProvider() override = default;
+
+ static std::unique_ptr<const AssetsProvider> Create(
+ std::unique_ptr<const AssetsProvider> child, std::unique_ptr<const AssetsProvider> parent) {
+ CHECK(parent != nullptr) << "parent provider must not be null";
+ return (!child) ? std::move(parent)
+ : std::unique_ptr<const AssetsProvider>(new MultiAssetsProvider(
+ std::move(child), std::move(parent)));
+ }
+
+ bool ForEachFile(const std::string& root_path,
+ const std::function<void(const StringPiece&, FileType)>& f) const override {
+ // TODO: Only call the function once for files defined in the parent and child
+ return child_->ForEachFile(root_path, f) && parent_->ForEachFile(root_path, f);
+ }
+
+ protected:
+ std::unique_ptr<Asset> OpenInternal(
+ const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
+ auto asset = child_->Open(path, mode, file_exists);
+ return (asset) ? std::move(asset) : parent_->Open(path, mode, file_exists);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MultiAssetsProvider);
+
+ MultiAssetsProvider(std::unique_ptr<const AssetsProvider> child,
+ std::unique_ptr<const AssetsProvider> parent)
+ : child_(std::move(child)), parent_(std::move(parent)) { }
+
+ std::unique_ptr<const AssetsProvider> child_;
+ std::unique_ptr<const AssetsProvider> parent_;
+};
+
+// Opens the archive using the file path. Calling CloseArchive on the zip handle will close the
+// file.
+std::unique_ptr<const ApkAssets> ApkAssets::Load(
+ const std::string& path, const package_property_t flags,
+ std::unique_ptr<const AssetsProvider> override_asset) {
+ auto assets = ZipAssetsProvider::Create(path);
+ return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
+ : nullptr;
+}
+
+// Opens the archive using the file file descriptor with the specified file offset and read length.
+// If the `assume_ownership` parameter is 'true' calling CloseArchive will close the file.
+std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(
+ unique_fd fd, const std::string& friendly_name, const package_property_t flags,
+ std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset,
+ const off64_t length) {
+ CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
+ CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
+ << kUnknownLength;
+
+ auto assets = ZipAssetsProvider::Create(std::move(fd), friendly_name, offset, length);
+ return (assets) ? LoadImpl(std::move(assets), friendly_name, flags, std::move(override_asset))
+ : nullptr;
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path,
- bool system) {
- return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, true /*load_as_shared_library*/);
+std::unique_ptr<const ApkAssets> ApkAssets::LoadTable(
+ const std::string& path, const package_property_t flags,
+ std::unique_ptr<const AssetsProvider> override_asset) {
+
+ auto assets = CreateAssetFromFile(path);
+ return (assets) ? LoadTableImpl(std::move(assets), path, flags, std::move(override_asset))
+ : nullptr;
+}
+
+std::unique_ptr<const ApkAssets> ApkAssets::LoadTableFromFd(
+ unique_fd fd, const std::string& friendly_name, const package_property_t flags,
+ std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset,
+ const off64_t length) {
+
+ auto assets = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length);
+ return (assets) ? LoadTableImpl(std::move(assets), friendly_name, flags,
+ std::move(override_asset))
+ : nullptr;
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
- bool system) {
+ const package_property_t flags) {
+ CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders";
std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path);
if (idmap_asset == nullptr) {
return {};
@@ -70,92 +390,115 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap
LOG(ERROR) << "failed to load IDMAP " << idmap_path;
return {};
}
- auto apkPath = loaded_idmap->OverlayApkPath();
- return LoadImpl({} /*fd*/, apkPath,
- std::move(idmap_asset),
- std::move(loaded_idmap),
- system, false /*load_as_shared_library*/);
+
+ auto overlay_path = loaded_idmap->OverlayApkPath();
+ auto assets = ZipAssetsProvider::Create(overlay_path);
+ return (assets) ? LoadImpl(std::move(assets), overlay_path, flags | PROPERTY_OVERLAY,
+ nullptr /* override_asset */, std::move(idmap_asset),
+ std::move(loaded_idmap))
+ : nullptr;
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd,
- const std::string& friendly_name,
- bool system, bool force_shared_lib) {
- return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/,
- system, force_shared_lib);
+std::unique_ptr<const ApkAssets> ApkAssets::LoadFromDir(
+ const std::string& path, const package_property_t flags,
+ std::unique_ptr<const AssetsProvider> override_asset) {
+
+ auto assets = DirectoryAssetsProvider::Create(path);
+ return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
+ : nullptr;
+}
+
+std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty(
+ const package_property_t flags, std::unique_ptr<const AssetsProvider> override_asset) {
+
+ auto assets = (override_asset) ? std::move(override_asset)
+ : std::unique_ptr<const AssetsProvider>(new EmptyAssetsProvider());
+ std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(std::move(assets), "empty" /* path */,
+ -1 /* last_mod-time */, flags));
+ loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
+ // Need to force a move for mingw32.
+ return std::move(loaded_apk);
}
std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
- if (fd == -1) {
+ if (!fd.ok()) {
LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno);
return {};
}
- const off64_t file_len = lseek64(fd, 0, SEEK_END);
- if (file_len < 0) {
- LOG(ERROR) << "Failed to get size of file '" << path << "': " << SystemErrorCodeToString(errno);
- return {};
+ return CreateAssetFromFd(std::move(fd), path.c_str());
+}
+
+std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd,
+ const char* path,
+ off64_t offset,
+ off64_t length) {
+ CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
+ CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
+ << kUnknownLength;
+ if (length == kUnknownLength) {
+ length = lseek64(fd, 0, SEEK_END);
+ if (length < 0) {
+ LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': "
+ << SystemErrorCodeToString(errno);
+ return {};
+ }
}
std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>();
- if (!file_map->create(path.c_str(), fd, 0, static_cast<size_t>(file_len), true /*readOnly*/)) {
- LOG(ERROR) << "Failed to mmap file '" << path << "': " << SystemErrorCodeToString(errno);
+ if (!file_map->create(path, fd, offset, static_cast<size_t>(length), true /*readOnly*/)) {
+ LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': "
+ << SystemErrorCodeToString(errno);
return {};
}
- return Asset::createFromUncompressedMap(std::move(file_map), Asset::AccessMode::ACCESS_RANDOM);
+
+ // If `path` is set, do not pass ownership of the `fd` to the new Asset since
+ // Asset::openFileDescriptor can use `path` to create new file descriptors.
+ return Asset::createFromUncompressedMap(std::move(file_map),
+ (path) ? base::unique_fd(-1) : std::move(fd),
+ Asset::AccessMode::ACCESS_RANDOM);
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
- unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset,
- std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library) {
- ::ZipArchiveHandle unmanaged_handle;
- int32_t result;
- if (fd >= 0) {
- result =
- ::OpenArchiveFd(fd.release(), path.c_str(), &unmanaged_handle, true /*assume_ownership*/);
- } else {
- result = ::OpenArchive(path.c_str(), &unmanaged_handle);
- }
+ std::unique_ptr<const AssetsProvider> assets, const std::string& path,
+ package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets,
+ std::unique_ptr<Asset> idmap_asset, std::unique_ptr<const LoadedIdmap> idmap) {
- if (result != 0) {
- LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
- return {};
- }
+ const time_t last_mod_time = getFileModDate(path.c_str());
+
+ // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
+ bool resources_asset_exists = false;
+ auto resources_asset_ = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER,
+ &resources_asset_exists);
- time_t last_mod_time = getFileModDate(path.c_str());
+ assets = MultiAssetsProvider::Create(std::move(override_assets), std::move(assets));
// Wrap the handle in a unique_ptr so it gets automatically closed.
- std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time));
+ std::unique_ptr<ApkAssets>
+ loaded_apk(new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
- // Find the resource table.
- ::ZipEntry entry;
- result = ::FindEntry(loaded_apk->zip_handle_.get(), kResourcesArsc, &entry);
- if (result != 0) {
- // There is no resources.arsc, so create an empty LoadedArsc and return.
+ if (!resources_asset_exists) {
loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
return std::move(loaded_apk);
}
- if (entry.method == kCompressDeflated) {
- LOG(WARNING) << kResourcesArsc << " in APK '" << path << "' is compressed.";
- }
-
- // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
- loaded_apk->resources_asset_ = loaded_apk->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER);
- if (loaded_apk->resources_asset_ == nullptr) {
+ loaded_apk->resources_asset_ = std::move(resources_asset_);
+ if (!loaded_apk->resources_asset_) {
LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'.";
return {};
}
// Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid.
loaded_apk->idmap_asset_ = std::move(idmap_asset);
+ loaded_apk->loaded_idmap_ = std::move(idmap);
const StringPiece data(
reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
loaded_apk->resources_asset_->getLength());
- loaded_apk->loaded_arsc_ =
- LoadedArsc::Load(data, loaded_idmap.get(), system, load_as_shared_library);
- if (loaded_apk->loaded_arsc_ == nullptr) {
+ loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(),
+ property_flags);
+ if (!loaded_apk->loaded_arsc_) {
LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
return {};
}
@@ -164,96 +507,38 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
return std::move(loaded_apk);
}
-std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const {
- CHECK(zip_handle_ != nullptr);
-
- ::ZipEntry entry;
- int32_t result = ::FindEntry(zip_handle_.get(), path, &entry);
- if (result != 0) {
- return {};
- }
-
- if (entry.method == kCompressDeflated) {
- std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
- if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
- entry.compressed_length, true /*readOnly*/)) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
- return {};
- }
-
- std::unique_ptr<Asset> asset =
- Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
- if (asset == nullptr) {
- LOG(ERROR) << "Failed to decompress '" << path << "'.";
- return {};
- }
- return asset;
- } else {
- std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
- if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
- entry.uncompressed_length, true /*readOnly*/)) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
- return {};
- }
-
- std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map), mode);
- if (asset == nullptr) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
- return {};
- }
- return asset;
- }
-}
-
-bool ApkAssets::ForEachFile(const std::string& root_path,
- const std::function<void(const StringPiece&, FileType)>& f) const {
- CHECK(zip_handle_ != nullptr);
+std::unique_ptr<const ApkAssets> ApkAssets::LoadTableImpl(
+ std::unique_ptr<Asset> resources_asset, const std::string& path,
+ package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets) {
- std::string root_path_full = root_path;
- if (root_path_full.back() != '/') {
- root_path_full += '/';
- }
+ const time_t last_mod_time = getFileModDate(path.c_str());
- void* cookie;
- if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
- return false;
- }
+ auto assets = (override_assets) ? std::move(override_assets)
+ : std::unique_ptr<AssetsProvider>(new EmptyAssetsProvider());
- std::string name;
- ::ZipEntry entry;
-
- // We need to hold back directories because many paths will contain them and we want to only
- // surface one.
- std::set<std::string> dirs;
-
- int32_t result;
- while ((result = ::Next(cookie, &entry, &name)) == 0) {
- StringPiece full_file_path(name);
- StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
-
- if (!leaf_file_path.empty()) {
- auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
- if (iter != leaf_file_path.end()) {
- std::string dir =
- leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
- dirs.insert(std::move(dir));
- } else {
- f(leaf_file_path, kFileTypeRegular);
- }
- }
- }
- ::EndIteration(cookie);
+ std::unique_ptr<ApkAssets> loaded_apk(
+ new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
+ loaded_apk->resources_asset_ = std::move(resources_asset);
- // Now present the unique directories.
- for (const std::string& dir : dirs) {
- f(dir, kFileTypeDirectory);
+ const StringPiece data(
+ reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
+ loaded_apk->resources_asset_->getLength());
+ loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags);
+ if (loaded_apk->loaded_arsc_ == nullptr) {
+ LOG(ERROR) << "Failed to load '" << kResourcesArsc << path;
+ return {};
}
- // -1 is end of iteration, anything else is an error.
- return result == -1;
+ // Need to force a move for mingw32.
+ return std::move(loaded_apk);
}
bool ApkAssets::IsUpToDate() const {
+ if (IsLoader()) {
+ // Loaders are invalidated by the app, not the system, so assume they are up to date.
+ return true;
+ }
+
return last_mod_time_ == getFileModDate(path_.c_str());
}
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index 9a95fdf80cb5..cd30c184d5a4 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -133,14 +133,24 @@ Asset::Asset(void)
*/
/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode)
{
+ return createFromFd(open(fileName, O_RDONLY | O_BINARY), fileName, mode);
+}
+
+/*
+ * Create a new Asset from a file on disk. There is a fair chance that
+ * the file doesn't actually exist.
+ *
+ * We can use "mode" to decide how we want to go about it.
+ */
+/*static*/ Asset* Asset::createFromFd(const int fd, const char* fileName, AccessMode mode)
+{
+ if (fd < 0) {
+ return NULL;
+ }
+
_FileAsset* pAsset;
status_t result;
off64_t length;
- int fd;
-
- fd = open(fileName, O_RDONLY | O_BINARY);
- if (fd < 0)
- return NULL;
/*
* Under Linux, the lseek fails if we actually opened a directory. To
@@ -253,8 +263,10 @@ Asset::Asset(void)
pAsset = new _FileAsset;
result = pAsset->openChunk(NULL, fd, offset, length);
- if (result != NO_ERROR)
+ if (result != NO_ERROR) {
+ delete pAsset;
return NULL;
+ }
pAsset->mAccessMode = mode;
return pAsset;
@@ -273,8 +285,10 @@ Asset::Asset(void)
pAsset = new _CompressedAsset;
result = pAsset->openChunk(fd, offset, compressionMethod,
uncompressedLen, compressedLen);
- if (result != NO_ERROR)
+ if (result != NO_ERROR) {
+ delete pAsset;
return NULL;
+ }
pAsset->mAccessMode = mode;
return pAsset;
@@ -284,14 +298,13 @@ Asset::Asset(void)
/*
* Create a new Asset from a memory mapping.
*/
-/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap,
- AccessMode mode)
+/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, AccessMode mode)
{
_FileAsset* pAsset;
status_t result;
pAsset = new _FileAsset;
- result = pAsset->openChunk(dataMap);
+ result = pAsset->openChunk(dataMap, base::unique_fd(-1));
if (result != NO_ERROR) {
delete pAsset;
return NULL;
@@ -302,11 +315,11 @@ Asset::Asset(void)
}
/*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,
- AccessMode mode)
+ base::unique_fd fd, AccessMode mode)
{
std::unique_ptr<_FileAsset> pAsset = util::make_unique<_FileAsset>();
- status_t result = pAsset->openChunk(dataMap.get());
+ status_t result = pAsset->openChunk(dataMap.get(), std::move(fd));
if (result != NO_ERROR) {
return NULL;
}
@@ -328,8 +341,10 @@ Asset::Asset(void)
pAsset = new _CompressedAsset;
result = pAsset->openChunk(dataMap, uncompressedLen);
- if (result != NO_ERROR)
+ if (result != NO_ERROR) {
+ delete pAsset;
return NULL;
+ }
pAsset->mAccessMode = mode;
return pAsset;
@@ -399,7 +414,7 @@ off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t m
* Constructor.
*/
_FileAsset::_FileAsset(void)
- : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL)
+ : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mMap(NULL), mBuf(NULL)
{
// Register the Asset with the global list here after it is fully constructed and its
// vtable pointer points to this concrete type. b/31113965
@@ -469,7 +484,7 @@ status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, siz
/*
* Create the chunk from the map.
*/
-status_t _FileAsset::openChunk(FileMap* dataMap)
+status_t _FileAsset::openChunk(FileMap* dataMap, base::unique_fd fd)
{
assert(mFp == NULL); // no reopen
assert(mMap == NULL);
@@ -478,6 +493,7 @@ status_t _FileAsset::openChunk(FileMap* dataMap)
mMap = dataMap;
mStart = -1; // not used
mLength = dataMap->getDataLength();
+ mFd = std::move(fd);
assert(mOffset == 0);
return NO_ERROR;
@@ -676,6 +692,17 @@ const void* _FileAsset::getBuffer(bool wordAligned)
int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
{
if (mMap != NULL) {
+ if (mFd.ok()) {
+ *outStart = mMap->getDataOffset();
+ *outLength = mMap->getDataLength();
+ const int fd = dup(mFd);
+ if (fd < 0) {
+ ALOGE("Unable to dup fd (%d).", mFd.get());
+ return -1;
+ }
+ lseek64(fd, 0, SEEK_SET);
+ return fd;
+ }
const char* fname = mMap->getFileName();
if (fname == NULL) {
fname = mFileName;
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index d20aecaaf0f6..f20e18453f8b 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -22,10 +22,12 @@
#include <iterator>
#include <map>
#include <set>
-#include <sstream>
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
+#include "androidfw/DynamicLibManager.h"
+#include "androidfw/ResourceUtils.h"
+#include "androidfw/Util.h"
#include "utils/ByteOrder.h"
#include "utils/Trace.h"
@@ -35,21 +37,13 @@
#endif
#endif
-#ifdef __ANDROID__
-#define ANDROID_LOG(x) LOG(x)
-#else
-#define ANDROID_LOG(x) std::stringstream()
-#endif
-
-#include "androidfw/ResourceUtils.h"
-
namespace android {
struct FindEntryResult {
// A pointer to the resource table entry for this resource.
// If the size of the entry is > sizeof(ResTable_entry), it can be cast to
// a ResTable_map_entry and processed as a bag/map.
- const ResTable_entry* entry;
+ ResTable_entry_handle entry;
// The configuration for which the resulting entry was defined. This is already swapped to host
// endianness.
@@ -61,6 +55,9 @@ struct FindEntryResult {
// The dynamic package ID map for the package from which this resource came from.
const DynamicRefTable* dynamic_ref_table;
+ // The package name of the resource.
+ const std::string* package_name;
+
// The string pool reference to the type's name. This uses a different string pool than
// the global string pool, but this is hidden from the caller.
StringPoolRef type_string_ref;
@@ -70,7 +67,12 @@ struct FindEntryResult {
StringPoolRef entry_string_ref;
};
-AssetManager2::AssetManager2() {
+AssetManager2::AssetManager2() : dynamic_lib_manager_(std::make_unique<DynamicLibManager>()) {
+ memset(&configuration_, 0, sizeof(configuration_));
+}
+
+AssetManager2::AssetManager2(DynamicLibManager* dynamic_lib_manager)
+ : dynamic_lib_manager_(dynamic_lib_manager) {
memset(&configuration_, 0, sizeof(configuration_));
}
@@ -89,29 +91,84 @@ void AssetManager2::BuildDynamicRefTable() {
package_groups_.clear();
package_ids_.fill(0xff);
- // 0x01 is reserved for the android package.
- int next_package_id = 0x02;
- const size_t apk_assets_count = apk_assets_.size();
- for (size_t i = 0; i < apk_assets_count; i++) {
- const LoadedArsc* loaded_arsc = apk_assets_[i]->GetLoadedArsc();
-
- for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) {
- // Get the package ID or assign one if a shared library.
- int package_id;
- if (package->IsDynamic()) {
- package_id = next_package_id++;
- } else {
- package_id = package->GetPackageId();
+ // Overlay resources are not directly referenced by an application so their resource ids
+ // can change throughout the application's lifetime. Assign overlay package ids last.
+ std::vector<const ApkAssets*> sorted_apk_assets(apk_assets_);
+ std::stable_partition(sorted_apk_assets.begin(), sorted_apk_assets.end(), [](const ApkAssets* a) {
+ return !a->IsOverlay();
+ });
+
+ std::unordered_map<std::string, uint8_t> apk_assets_package_ids;
+ std::unordered_map<std::string, uint8_t> package_name_package_ids;
+
+ // Assign stable package ids to application packages.
+ uint8_t next_available_package_id = 0U;
+ for (const auto& apk_assets : sorted_apk_assets) {
+ for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) {
+ uint8_t package_id = package->GetPackageId();
+ if (package->IsOverlay()) {
+ package_id = GetDynamicLibManager()->FindUnassignedId(next_available_package_id);
+ next_available_package_id = package_id + 1;
+ } else if (package->IsDynamic()) {
+ package_id = GetDynamicLibManager()->GetAssignedId(package->GetPackageName());
}
+ // Map the path of the apk assets to the package id of its first loaded package.
+ apk_assets_package_ids[apk_assets->GetPath()] = package_id;
+
+ // Map the package name of the package to the first loaded package with that package id.
+ package_name_package_ids[package->GetPackageName()] = package_id;
+ }
+ }
+
+ const int apk_assets_count = apk_assets_.size();
+ for (int i = 0; i < apk_assets_count; i++) {
+ const auto& apk_assets = apk_assets_[i];
+ for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) {
+ const auto package_id_entry = package_name_package_ids.find(package->GetPackageName());
+ CHECK(package_id_entry != package_name_package_ids.end())
+ << "no package id assgined to package " << package->GetPackageName();
+ const uint8_t package_id = package_id_entry->second;
+
// Add the mapping for package ID to index if not present.
uint8_t idx = package_ids_[package_id];
if (idx == 0xff) {
package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size());
package_groups_.push_back({});
- DynamicRefTable& ref_table = package_groups_.back().dynamic_ref_table;
- ref_table.mAssignedPackageId = package_id;
- ref_table.mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
+
+ if (apk_assets->IsOverlay()) {
+ // The target package must precede the overlay package in the apk assets paths in order
+ // to take effect.
+ const auto& loaded_idmap = apk_assets->GetLoadedIdmap();
+ auto target_package_iter = apk_assets_package_ids.find(loaded_idmap->TargetApkPath());
+ if (target_package_iter == apk_assets_package_ids.end()) {
+ LOG(INFO) << "failed to find target package for overlay "
+ << loaded_idmap->OverlayApkPath();
+ } else {
+ const uint8_t target_package_id = target_package_iter->second;
+ const uint8_t target_idx = package_ids_[target_package_id];
+ CHECK(target_idx != 0xff) << "overlay added to apk_assets_package_ids but does not"
+ << " have an assigned package group";
+
+ PackageGroup& target_package_group = package_groups_[target_idx];
+
+ // Create a special dynamic reference table for the overlay to rewrite references to
+ // overlay resources as references to the target resources they overlay.
+ auto overlay_table = std::make_shared<OverlayDynamicRefTable>(
+ loaded_idmap->GetOverlayDynamicRefTable(target_package_id));
+ package_groups_.back().dynamic_ref_table = overlay_table;
+
+ // Add the overlay resource map to the target package's set of overlays.
+ target_package_group.overlays_.push_back(
+ ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id,
+ overlay_table.get()),
+ static_cast<ApkAssetsCookie>(i)});
+ }
+ }
+
+ DynamicRefTable* ref_table = package_groups_.back().dynamic_ref_table.get();
+ ref_table->mAssignedPackageId = package_id;
+ ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
}
PackageGroup* package_group = &package_groups_[idx];
@@ -122,7 +179,7 @@ void AssetManager2::BuildDynamicRefTable() {
// Add the package name -> build time ID mappings.
for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) {
String16 package_name(entry.package_name.c_str(), entry.package_name.size());
- package_group->dynamic_ref_table.mEntries.replaceValueFor(
+ package_group->dynamic_ref_table->mEntries.replaceValueFor(
package_name, static_cast<uint8_t>(entry.package_id));
}
}
@@ -133,8 +190,8 @@ void AssetManager2::BuildDynamicRefTable() {
for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) {
const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName();
for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) {
- iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()),
- iter->dynamic_ref_table.mAssignedPackageId);
+ iter2->dynamic_ref_table->addMapping(String16(package_name.c_str(), package_name.size()),
+ iter->dynamic_ref_table->mAssignedPackageId);
}
}
}
@@ -167,13 +224,13 @@ void AssetManager2::DumpToLog() const {
(loaded_package->IsDynamic() ? " dynamic" : ""));
}
LOG(INFO) << base::StringPrintf("PG (%02x): ",
- package_group.dynamic_ref_table.mAssignedPackageId)
+ package_group.dynamic_ref_table->mAssignedPackageId)
<< list;
for (size_t i = 0; i < 256; i++) {
- if (package_group.dynamic_ref_table.mLookupTable[i] != 0) {
+ if (package_group.dynamic_ref_table->mLookupTable[i] != 0) {
LOG(INFO) << base::StringPrintf(" e[0x%02x] -> 0x%02x", (uint8_t) i,
- package_group.dynamic_ref_table.mLookupTable[i]);
+ package_group.dynamic_ref_table->mLookupTable[i]);
}
}
}
@@ -195,14 +252,15 @@ const DynamicRefTable* AssetManager2::GetDynamicRefTableForPackage(uint32_t pack
if (idx == 0xff) {
return nullptr;
}
- return &package_groups_[idx].dynamic_ref_table;
+ return package_groups_[idx].dynamic_ref_table.get();
}
-const DynamicRefTable* AssetManager2::GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const {
+std::shared_ptr<const DynamicRefTable> AssetManager2::GetDynamicRefTableForCookie(
+ ApkAssetsCookie cookie) const {
for (const PackageGroup& package_group : package_groups_) {
for (const ApkAssetsCookie& package_cookie : package_group.cookies_) {
if (package_cookie == cookie) {
- return &package_group.dynamic_ref_table;
+ return package_group.dynamic_ref_table;
}
}
}
@@ -230,6 +288,62 @@ const std::unordered_map<std::string, std::string>*
return &loaded_package->GetOverlayableMap();
}
+bool AssetManager2::GetOverlayablesToString(const android::StringPiece& package_name,
+ std::string* out) const {
+ uint8_t package_id = 0U;
+ for (const auto& apk_assets : apk_assets_) {
+ const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc();
+ if (loaded_arsc == nullptr) {
+ continue;
+ }
+
+ const auto& loaded_packages = loaded_arsc->GetPackages();
+ if (loaded_packages.empty()) {
+ continue;
+ }
+
+ const auto& loaded_package = loaded_packages[0];
+ if (loaded_package->GetPackageName() == package_name) {
+ package_id = GetAssignedPackageId(loaded_package.get());
+ break;
+ }
+ }
+
+ if (package_id == 0U) {
+ ANDROID_LOG(ERROR) << base::StringPrintf("No package with name '%s", package_name.data());
+ return false;
+ }
+
+ const size_t idx = package_ids_[package_id];
+ if (idx == 0xff) {
+ return false;
+ }
+
+ std::string output;
+ for (const ConfiguredPackage& package : package_groups_[idx].packages_) {
+ const LoadedPackage* loaded_package = package.loaded_package_;
+ for (auto it = loaded_package->begin(); it != loaded_package->end(); it++) {
+ const OverlayableInfo* info = loaded_package->GetOverlayableInfo(*it);
+ if (info != nullptr) {
+ ResourceName res_name;
+ if (!GetResourceName(*it, &res_name)) {
+ ANDROID_LOG(ERROR) << base::StringPrintf(
+ "Unable to retrieve name of overlayable resource 0x%08x", *it);
+ return false;
+ }
+
+ const std::string name = ToFormattedResourceString(&res_name);
+ output.append(base::StringPrintf(
+ "resource='%s' overlayable='%s' actor='%s' policy='0x%08x'\n",
+ name.c_str(), info->name.c_str(), info->actor.c_str(), info->policy_flags));
+ }
+ }
+ }
+
+ *out = std::move(output);
+ return true;
+}
+
void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
const int diff = configuration_.diff(configuration);
configuration_ = configuration;
@@ -240,21 +354,45 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
}
}
+std::set<std::string> AssetManager2::GetNonSystemOverlayPaths() const {
+ std::set<std::string> non_system_overlays;
+ for (const PackageGroup& package_group : package_groups_) {
+ bool found_system_package = false;
+ for (const ConfiguredPackage& package : package_group.packages_) {
+ if (package.loaded_package_->IsSystem()) {
+ found_system_package = true;
+ break;
+ }
+ }
+
+ if (!found_system_package) {
+ for (const ConfiguredOverlay& overlay : package_group.overlays_) {
+ non_system_overlays.insert(apk_assets_[overlay.cookie]->GetPath());
+ }
+ }
+ }
+
+ return non_system_overlays;
+}
+
std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
bool exclude_mipmap) const {
ATRACE_NAME("AssetManager::GetResourceConfigurations");
+ const auto non_system_overlays =
+ (exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>();
+
std::set<ResTable_config> configurations;
for (const PackageGroup& package_group : package_groups_) {
- bool found_system_package = false;
- for (const ConfiguredPackage& package : package_group.packages_) {
+ for (size_t i = 0; i < package_group.packages_.size(); i++) {
+ const ConfiguredPackage& package = package_group.packages_[i];
if (exclude_system && package.loaded_package_->IsSystem()) {
- found_system_package = true;
continue;
}
- if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) {
- // Overlays must appear after the target package to take effect. Any overlay found in the
- // same package as a system package is able to overlay system resources.
+ auto apk_assets = apk_assets_[package_group.cookies_[i]];
+ if (exclude_system && apk_assets->IsOverlay()
+ && non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) {
+ // Exclude overlays that target system resources.
continue;
}
@@ -268,17 +406,20 @@ std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system,
bool merge_equivalent_languages) const {
ATRACE_NAME("AssetManager::GetResourceLocales");
std::set<std::string> locales;
+ const auto non_system_overlays =
+ (exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>();
+
for (const PackageGroup& package_group : package_groups_) {
- bool found_system_package = false;
- for (const ConfiguredPackage& package : package_group.packages_) {
+ for (size_t i = 0; i < package_group.packages_.size(); i++) {
+ const ConfiguredPackage& package = package_group.packages_[i];
if (exclude_system && package.loaded_package_->IsSystem()) {
- found_system_package = true;
continue;
}
- if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) {
- // Overlays must appear after the target package to take effect. Any overlay found in the
- // same package as a system package is able to overlay system resources.
+ auto apk_assets = apk_assets_[package_group.cookies_[i]];
+ if (exclude_system && apk_assets->IsOverlay()
+ && non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) {
+ // Exclude overlays that target system resources.
continue;
}
@@ -322,7 +463,7 @@ std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) con
files->add(info);
};
- if (!apk_assets->ForEachFile(full_path, func)) {
+ if (!apk_assets->GetAssetsProvider()->ForEachFile(full_path, func)) {
return {};
}
}
@@ -346,7 +487,7 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
continue;
}
- std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode);
+ std::unique_ptr<Asset> asset = apk_assets_[i]->GetAssetsProvider()->Open(filename, mode);
if (asset) {
if (out_cookie != nullptr) {
*out_cookie = i;
@@ -367,13 +508,19 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
return {};
}
- return apk_assets_[cookie]->Open(filename, mode);
+ return apk_assets_[cookie]->GetAssetsProvider()->Open(filename, mode);
}
ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
bool /*stop_at_first_match*/,
bool ignore_configuration,
FindEntryResult* out_entry) const {
+ if (resource_resolution_logging_enabled_) {
+ // Clear the last logged resource resolution.
+ ResetResourceResolution();
+ last_resolution_.resid = resid;
+ }
+
// Might use this if density_override != 0.
ResTable_config density_override_config;
@@ -385,6 +532,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
desired_config = &density_override_config;
}
+ // Retrieve the package group from the package id of the resource id.
if (!is_valid_resid(resid)) {
LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
return kInvalidCookie;
@@ -393,8 +541,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
const uint32_t package_id = get_package_id(resid);
const uint8_t type_idx = get_type_id(resid) - 1;
const uint16_t entry_idx = get_entry_id(resid);
-
- const uint8_t package_idx = package_ids_[package_id];
+ uint8_t package_idx = package_ids_[package_id];
if (package_idx == 0xff) {
ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.",
package_id, resid);
@@ -402,8 +549,71 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
}
const PackageGroup& package_group = package_groups_[package_idx];
- const size_t package_count = package_group.packages_.size();
+ ApkAssetsCookie cookie = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
+ false /* stop_at_first_match */,
+ ignore_configuration, out_entry);
+ if (UNLIKELY(cookie == kInvalidCookie)) {
+ return kInvalidCookie;
+ }
+
+ if (!apk_assets_[cookie]->IsLoader()) {
+ for (const auto& id_map : package_group.overlays_) {
+ auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
+ if (!overlay_entry) {
+ // No id map entry exists for this target resource.
+ continue;
+ }
+
+ if (overlay_entry.IsTableEntry()) {
+ // The target resource is overlaid by an inline value not represented by a resource.
+ out_entry->entry = overlay_entry.GetTableEntry();
+ out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
+ cookie = id_map.cookie;
+ continue;
+ }
+ FindEntryResult overlay_result;
+ ApkAssetsCookie overlay_cookie = FindEntry(overlay_entry.GetResourceId(), density_override,
+ false /* stop_at_first_match */,
+ ignore_configuration, &overlay_result);
+ if (UNLIKELY(overlay_cookie == kInvalidCookie)) {
+ continue;
+ }
+
+ if (!overlay_result.config.isBetterThan(out_entry->config, desired_config)
+ && overlay_result.config.compare(out_entry->config) != 0) {
+ // The configuration of the entry for the overlay must be equal to or better than the target
+ // configuration to be chosen as the better value.
+ continue;
+ }
+
+ cookie = overlay_cookie;
+ out_entry->entry = std::move(overlay_result.entry);
+ out_entry->config = overlay_result.config;
+ out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
+ if (resource_resolution_logging_enabled_) {
+ last_resolution_.steps.push_back(
+ Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(),
+ overlay_result.package_name});
+ }
+ }
+ }
+
+ if (resource_resolution_logging_enabled_) {
+ last_resolution_.cookie = cookie;
+ last_resolution_.type_string_ref = out_entry->type_string_ref;
+ last_resolution_.entry_string_ref = out_entry->entry_string_ref;
+ }
+
+ return cookie;
+}
+
+ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_group,
+ uint8_t type_idx, uint16_t entry_idx,
+ const ResTable_config& desired_config,
+ bool /*stop_at_first_match*/,
+ bool ignore_configuration,
+ FindEntryResult* out_entry) const {
ApkAssetsCookie best_cookie = kInvalidCookie;
const LoadedPackage* best_package = nullptr;
const ResTable_type* best_type = nullptr;
@@ -412,13 +622,14 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
uint32_t best_offset = 0u;
uint32_t type_flags = 0u;
- Resolution::Step::Type resolution_type;
+ Resolution::Step::Type resolution_type = Resolution::Step::Type::NO_ENTRY;
std::vector<Resolution::Step> resolution_steps;
// If desired_config is the same as the set configuration, then we can use our filtered list
// and we don't need to match the configurations, since they already matched.
- const bool use_fast_path = !ignore_configuration && desired_config == &configuration_;
+ const bool use_fast_path = !ignore_configuration && &desired_config == &configuration_;
+ const size_t package_count = package_group.packages_.size();
for (size_t pi = 0; pi < package_count; pi++) {
const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_;
@@ -431,20 +642,10 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
continue;
}
- uint16_t local_entry_idx = entry_idx;
-
- // If there is an IDMAP supplied with this package, translate the entry ID.
- if (type_spec->idmap_entries != nullptr) {
- if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) {
- // There is no mapping, so the resource is not meant to be in this overlay package.
- continue;
- }
- }
-
- type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx);
-
- // If the package is an overlay, then even configurations that are the same MUST be chosen.
- const bool package_is_overlay = loaded_package->IsOverlay();
+ // If the package is an overlay or custom loader,
+ // then even configurations that are the same MUST be chosen.
+ const bool package_is_loader = loaded_package->IsCustomLoader();
+ type_flags |= type_spec->GetFlagsForEntryIndex(entry_idx);
if (use_fast_path) {
const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
@@ -457,19 +658,37 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
// configurations that do NOT match have been filtered-out.
if (best_config == nullptr) {
resolution_type = Resolution::Step::Type::INITIAL;
- } else if (this_config.isBetterThan(*best_config, desired_config)) {
- resolution_type = Resolution::Step::Type::BETTER_MATCH;
- } else if (package_is_overlay && this_config.compare(*best_config) == 0) {
- resolution_type = Resolution::Step::Type::OVERLAID;
+ } else if (this_config.isBetterThan(*best_config, &desired_config)) {
+ resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER
+ : Resolution::Step::Type::BETTER_MATCH;
+ } else if (package_is_loader && this_config.compare(*best_config) == 0) {
+ resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
} else {
+ if (resource_resolution_logging_enabled_) {
+ resolution_type = (package_is_loader) ? Resolution::Step::Type::SKIPPED_LOADER
+ : Resolution::Step::Type::SKIPPED;
+ resolution_steps.push_back(Resolution::Step{resolution_type,
+ this_config.toString(),
+ &loaded_package->GetPackageName()});
+ }
continue;
}
// The configuration matches and is better than the previous selection.
// Find the entry value if it exists for this configuration.
const ResTable_type* type = filtered_group.types[i];
- const uint32_t offset = LoadedPackage::GetEntryOffset(type, local_entry_idx);
+ const uint32_t offset = LoadedPackage::GetEntryOffset(type, entry_idx);
if (offset == ResTable_type::NO_ENTRY) {
+ if (resource_resolution_logging_enabled_) {
+ if (package_is_loader) {
+ resolution_type = Resolution::Step::Type::NO_ENTRY_LOADER;
+ } else {
+ resolution_type = Resolution::Step::Type::NO_ENTRY;
+ }
+ resolution_steps.push_back(Resolution::Step{resolution_type,
+ this_config.toString(),
+ &loaded_package->GetPackageName()});
+ }
continue;
}
@@ -480,9 +699,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
best_offset = offset;
if (resource_resolution_logging_enabled_) {
- resolution_steps.push_back(Resolution::Step{resolution_type,
- this_config.toString(),
- &loaded_package->GetPackageName()});
+ last_resolution_.steps.push_back(Resolution::Step{resolution_type,
+ this_config.toString(),
+ &loaded_package->GetPackageName()});
}
}
} else {
@@ -497,16 +716,17 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
if (!ignore_configuration) {
this_config.copyFromDtoH((*iter)->config);
- if (!this_config.match(*desired_config)) {
+ if (!this_config.match(desired_config)) {
continue;
}
if (best_config == nullptr) {
resolution_type = Resolution::Step::Type::INITIAL;
- } else if (this_config.isBetterThan(*best_config, desired_config)) {
- resolution_type = Resolution::Step::Type::BETTER_MATCH;
- } else if (package_is_overlay && this_config.compare(*best_config) == 0) {
- resolution_type = Resolution::Step::Type::OVERLAID;
+ } else if (this_config.isBetterThan(*best_config, &desired_config)) {
+ resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER
+ : Resolution::Step::Type::BETTER_MATCH;
+ } else if (package_is_loader && this_config.compare(*best_config) == 0) {
+ resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
} else {
continue;
}
@@ -514,7 +734,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
// The configuration matches and is better than the previous selection.
// Find the entry value if it exists for this configuration.
- const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx);
+ const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, entry_idx);
if (offset == ResTable_type::NO_ENTRY) {
continue;
}
@@ -532,9 +752,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
}
if (resource_resolution_logging_enabled_) {
- resolution_steps.push_back(Resolution::Step{resolution_type,
- this_config.toString(),
- &loaded_package->GetPackageName()});
+ last_resolution_.steps.push_back(Resolution::Step{resolution_type,
+ this_config.toString(),
+ &loaded_package->GetPackageName()});
}
}
}
@@ -549,38 +769,30 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
return kInvalidCookie;
}
- out_entry->entry = best_entry;
+ out_entry->entry = ResTable_entry_handle::unmanaged(best_entry);
out_entry->config = *best_config;
out_entry->type_flags = type_flags;
+ out_entry->package_name = &best_package->GetPackageName();
out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
out_entry->entry_string_ref =
- StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
- out_entry->dynamic_ref_table = &package_group.dynamic_ref_table;
-
- if (resource_resolution_logging_enabled_) {
- last_resolution.resid = resid;
- last_resolution.cookie = best_cookie;
- last_resolution.steps = resolution_steps;
-
- // Cache only the type/entry refs since that's all that's needed to build name
- last_resolution.type_string_ref =
- StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
- last_resolution.entry_string_ref =
- StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
- }
+ StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
+ out_entry->dynamic_ref_table = package_group.dynamic_ref_table.get();
return best_cookie;
}
+void AssetManager2::ResetResourceResolution() const {
+ last_resolution_.cookie = kInvalidCookie;
+ last_resolution_.resid = 0;
+ last_resolution_.steps.clear();
+ last_resolution_.type_string_ref = StringPoolRef();
+ last_resolution_.entry_string_ref = StringPoolRef();
+}
+
void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) {
resource_resolution_logging_enabled_ = enabled;
-
if (!enabled) {
- last_resolution.cookie = kInvalidCookie;
- last_resolution.resid = 0;
- last_resolution.steps.clear();
- last_resolution.type_string_ref = StringPoolRef();
- last_resolution.entry_string_ref = StringPoolRef();
+ ResetResourceResolution();
}
}
@@ -590,24 +802,24 @@ std::string AssetManager2::GetLastResourceResolution() const {
return std::string();
}
- auto cookie = last_resolution.cookie;
+ auto cookie = last_resolution_.cookie;
if (cookie == kInvalidCookie) {
LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path.";
return std::string();
}
- uint32_t resid = last_resolution.resid;
- std::vector<Resolution::Step>& steps = last_resolution.steps;
+ uint32_t resid = last_resolution_.resid;
+ std::vector<Resolution::Step>& steps = last_resolution_.steps;
ResourceName resource_name;
std::string resource_name_string;
const LoadedPackage* package =
- apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
+ apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
if (package != nullptr) {
- ToResourceName(last_resolution.type_string_ref,
- last_resolution.entry_string_ref,
+ ToResourceName(last_resolution_.type_string_ref,
+ last_resolution_.entry_string_ref,
package->GetPackageName(),
&resource_name);
resource_name_string = ToFormattedResourceString(&resource_name);
@@ -628,9 +840,27 @@ std::string AssetManager2::GetLastResourceResolution() const {
case Resolution::Step::Type::BETTER_MATCH:
prefix = "Found better";
break;
+ case Resolution::Step::Type::BETTER_MATCH_LOADER:
+ prefix = "Found better in loader";
+ break;
case Resolution::Step::Type::OVERLAID:
prefix = "Overlaid";
break;
+ case Resolution::Step::Type::OVERLAID_LOADER:
+ prefix = "Overlaid by loader";
+ break;
+ case Resolution::Step::Type::SKIPPED:
+ prefix = "Skipped";
+ break;
+ case Resolution::Step::Type::SKIPPED_LOADER:
+ prefix = "Skipped loader";
+ break;
+ case Resolution::Step::Type::NO_ENTRY:
+ prefix = "No entry";
+ break;
+ case Resolution::Step::Type::NO_ENTRY_LOADER:
+ prefix = "No entry for loader";
+ break;
}
if (!prefix.empty()) {
@@ -654,25 +884,9 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons
return false;
}
- const uint8_t package_idx = package_ids_[get_package_id(resid)];
- if (package_idx == 0xff) {
- LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.",
- get_package_id(resid), resid);
- return false;
- }
-
- const PackageGroup& package_group = package_groups_[package_idx];
- auto cookie_iter = std::find(package_group.cookies_.begin(),
- package_group.cookies_.end(), cookie);
- if (cookie_iter == package_group.cookies_.end()) {
- return false;
- }
-
- long package_pos = std::distance(package_group.cookies_.begin(), cookie_iter);
- const LoadedPackage* package = package_group.packages_[package_pos].loaded_package_;
return ToResourceName(entry.type_string_ref,
entry.entry_string_ref,
- package->GetPackageName(),
+ *entry.package_name,
out_name);
}
@@ -699,7 +913,8 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
return kInvalidCookie;
}
- if (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) {
+ const ResTable_entry* table_entry = *entry.entry;
+ if (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) {
if (!may_be_bag) {
LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
return kInvalidCookie;
@@ -714,7 +929,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
}
const Res_value* device_value = reinterpret_cast<const Res_value*>(
- reinterpret_cast<const uint8_t*>(entry.entry) + dtohs(entry.entry->size));
+ reinterpret_cast<const uint8_t*>(table_entry) + dtohs(table_entry->size));
out_value->copyFrom_dtoh(*device_value);
// Convert the package ID to the runtime assigned package ID.
@@ -777,6 +992,11 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) {
return bag;
}
+static bool compare_bag_entries(const ResolvedBag::Entry& entry1,
+ const ResolvedBag::Entry& entry2) {
+ return entry1.key < entry2.key;
+}
+
const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& child_resids) {
auto cached_iter = cached_bags_.find(resid);
if (cached_iter != cached_bags_.end()) {
@@ -795,13 +1015,14 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>&
// Check that the size of the entry header is at least as big as
// the desired ResTable_map_entry. Also verify that the entry
// was intended to be a map.
- if (dtohs(entry.entry->size) < sizeof(ResTable_map_entry) ||
- (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) {
+ const ResTable_entry* table_entry = *entry.entry;
+ if (dtohs(table_entry->size) < sizeof(ResTable_map_entry) ||
+ (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) {
// Not a bag, nothing to do.
return nullptr;
}
- const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry.entry);
+ const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(table_entry);
const ResTable_map* map_entry =
reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size);
const ResTable_map* const map_entry_end = map_entry + dtohl(map->count);
@@ -811,13 +1032,15 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>&
child_resids.push_back(resid);
uint32_t parent_resid = dtohl(map->parent.ident);
- if (parent_resid == 0 || std::find(child_resids.begin(), child_resids.end(), parent_resid)
+ if (parent_resid == 0U || std::find(child_resids.begin(), child_resids.end(), parent_resid)
!= child_resids.end()) {
- // There is no parent or that a circular dependency exist, meaning there is nothing to
- // inherit and we can do a simple copy of the entries in the map.
+ // There is no parent or a circular dependency exist, meaning there is nothing to inherit and
+ // we can do a simple copy of the entries in the map.
const size_t entry_count = map_entry_end - map_entry;
util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))};
+
+ bool sort_entries = false;
ResolvedBag::Entry* new_entry = new_bag->entries;
for (; map_entry != map_entry_end; ++map_entry) {
uint32_t new_key = dtohl(map_entry->name.ident);
@@ -843,8 +1066,15 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>&
new_entry->value.data, new_key);
return nullptr;
}
+ sort_entries = sort_entries ||
+ (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
++new_entry;
}
+
+ if (sort_entries) {
+ std::sort(new_bag->entries, new_bag->entries + entry_count, compare_bag_entries);
+ }
+
new_bag->type_spec_flags = entry.type_flags;
new_bag->entry_count = static_cast<uint32_t>(entry_count);
ResolvedBag* result = new_bag.get();
@@ -875,6 +1105,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>&
const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count;
// The keys are expected to be in sorted order. Merge the two bags.
+ bool sort_entries = false;
while (map_entry != map_entry_end && parent_entry != parent_entry_end) {
uint32_t child_key = dtohl(map_entry->name.ident);
if (!is_internal_resid(child_key)) {
@@ -907,6 +1138,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>&
memcpy(new_entry, parent_entry, sizeof(*new_entry));
}
+ sort_entries = sort_entries ||
+ (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
if (child_key >= parent_entry->key) {
// Move to the next parent entry if we used it or it was overridden.
++parent_entry;
@@ -937,6 +1170,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>&
new_entry->value.dataType, new_entry->value.data, new_key);
return nullptr;
}
+ sort_entries = sort_entries ||
+ (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
++map_entry;
++new_entry;
}
@@ -956,6 +1191,10 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>&
new_bag.release(), sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry)))));
}
+ if (sort_entries) {
+ std::sort(new_bag->entries, new_bag->entries + actual_count, compare_bag_entries);
+ }
+
// Combine flags from the parent and our own bag.
new_bag->type_spec_flags = entry.type_flags | parent_bag->type_spec_flags;
new_bag->entry_count = static_cast<uint32_t>(actual_count);
@@ -1026,7 +1265,7 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
}
if (resid != 0u) {
- return fix_package_id(resid, package_group.dynamic_ref_table.mAssignedPackageId);
+ return fix_package_id(resid, package_group.dynamic_ref_table->mAssignedPackageId);
}
}
}
@@ -1079,17 +1318,27 @@ void AssetManager2::InvalidateCaches(uint32_t diff) {
}
}
-uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) {
+uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const {
for (auto& package_group : package_groups_) {
for (auto& package2 : package_group.packages_) {
if (package2.loaded_package_ == package) {
- return package_group.dynamic_ref_table.mAssignedPackageId;
+ return package_group.dynamic_ref_table->mAssignedPackageId;
}
}
}
return 0;
}
+DynamicLibManager* AssetManager2::GetDynamicLibManager() const {
+ auto dynamic_lib_manager =
+ std::get_if<std::unique_ptr<DynamicLibManager>>(&dynamic_lib_manager_);
+ if (dynamic_lib_manager) {
+ return (*dynamic_lib_manager).get();
+ } else {
+ return *std::get_if<DynamicLibManager*>(&dynamic_lib_manager_);
+ }
+}
+
std::unique_ptr<Theme> AssetManager2::NewTheme() {
return std::unique_ptr<Theme>(new Theme(this));
}
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index e1067fcd4d3d..6f05cbd0ebb3 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -69,7 +69,7 @@ status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** o
result = window->clear();
if (!result) {
LOG_WINDOW("Created new CursorWindow: freeOffset=%d, "
- "numRows=%d, numColumns=%d, mSize=%d, mData=%p",
+ "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
window->mHeader->freeOffset,
window->mHeader->numRows,
window->mHeader->numColumns,
@@ -124,7 +124,7 @@ status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursor
CursorWindow* window = new CursorWindow(name, dupAshmemFd,
data, size, true /*readOnly*/);
LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, "
- "numRows=%d, numColumns=%d, mSize=%d, mData=%p",
+ "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
window->mHeader->freeOffset,
window->mHeader->numRows,
window->mHeader->numColumns,
@@ -200,7 +200,7 @@ status_t CursorWindow::allocRow() {
FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(fieldDirOffset));
memset(fieldDir, 0, fieldDirSize);
- LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n",
+ LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %zu bytes at offset %u\n",
mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset);
rowSlot->offset = fieldDirOffset;
return OK;
diff --git a/libs/androidfw/DisplayEventDispatcher.cpp b/libs/androidfw/DisplayEventDispatcher.cpp
deleted file mode 100644
index d8a3f42690f4..000000000000
--- a/libs/androidfw/DisplayEventDispatcher.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "DisplayEventDispatcher"
-
-#include <cinttypes>
-#include <cstdint>
-
-#include <androidfw/DisplayEventDispatcher.h>
-#include <gui/DisplayEventReceiver.h>
-#include <utils/Log.h>
-#include <utils/Looper.h>
-
-#include <utils/Timers.h>
-
-namespace android {
-
-// Number of events to read at a time from the DisplayEventDispatcher pipe.
-// The value should be large enough that we can quickly drain the pipe
-// using just a few large reads.
-static const size_t EVENT_BUFFER_SIZE = 100;
-
-DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
- ISurfaceComposer::VsyncSource vsyncSource,
- ISurfaceComposer::ConfigChanged configChanged) :
- mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) {
- ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
-}
-
-status_t DisplayEventDispatcher::initialize() {
- status_t result = mReceiver.initCheck();
- if (result) {
- ALOGW("Failed to initialize display event receiver, status=%d", result);
- return result;
- }
-
- int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,
- this, NULL);
- if (rc < 0) {
- return UNKNOWN_ERROR;
- }
- return OK;
-}
-
-void DisplayEventDispatcher::dispose() {
- ALOGV("dispatcher %p ~ Disposing display event dispatcher.", this);
-
- if (!mReceiver.initCheck()) {
- mLooper->removeFd(mReceiver.getFd());
- }
-}
-
-status_t DisplayEventDispatcher::scheduleVsync() {
- if (!mWaitingForVsync) {
- ALOGV("dispatcher %p ~ Scheduling vsync.", this);
-
- // Drain all pending events.
- nsecs_t vsyncTimestamp;
- PhysicalDisplayId vsyncDisplayId;
- uint32_t vsyncCount;
- if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
- ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "",
- this, ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
- }
-
- status_t status = mReceiver.requestNextVsync();
- if (status) {
- ALOGW("Failed to request next vsync, status=%d", status);
- return status;
- }
-
- mWaitingForVsync = true;
- }
- return OK;
-}
-
-int DisplayEventDispatcher::handleEvent(int, int events, void*) {
- if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
- ALOGE("Display event receiver pipe was closed or an error occurred. "
- "events=0x%x", events);
- return 0; // remove the callback
- }
-
- if (!(events & Looper::EVENT_INPUT)) {
- ALOGW("Received spurious callback for unhandled poll event. "
- "events=0x%x", events);
- return 1; // keep the callback
- }
-
- // Drain all pending events, keep the last vsync.
- nsecs_t vsyncTimestamp;
- PhysicalDisplayId vsyncDisplayId;
- uint32_t vsyncCount;
- if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
- ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", displayId=%"
- ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d",
- this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
- mWaitingForVsync = false;
- dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
- }
-
- return 1; // keep the callback
-}
-
-bool DisplayEventDispatcher::processPendingEvents(
- nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount) {
- bool gotVsync = false;
- DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
- ssize_t n;
- while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
- ALOGV("dispatcher %p ~ Read %d events.", this, int(n));
- for (ssize_t i = 0; i < n; i++) {
- const DisplayEventReceiver::Event& ev = buf[i];
- switch (ev.header.type) {
- case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
- // Later vsync events will just overwrite the info from earlier
- // ones. That's fine, we only care about the most recent.
- gotVsync = true;
- *outTimestamp = ev.header.timestamp;
- *outDisplayId = ev.header.displayId;
- *outCount = ev.vsync.count;
- break;
- case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
- dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
- break;
- case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
- dispatchConfigChanged(ev.header.timestamp, ev.header.displayId, ev.config.configId);
- break;
- default:
- ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
- break;
- }
- }
- }
- if (n < 0) {
- ALOGW("Failed to get events from display event dispatcher, status=%d", status_t(n));
- }
- return gotVsync;
-}
-}
diff --git a/libs/androidfw/DynamicLibManager.cpp b/libs/androidfw/DynamicLibManager.cpp
new file mode 100644
index 000000000000..895b7695bf26
--- /dev/null
+++ b/libs/androidfw/DynamicLibManager.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "androidfw/DynamicLibManager.h"
+
+namespace android {
+
+uint8_t DynamicLibManager::GetAssignedId(const std::string& library_package_name) {
+ auto lib_entry = shared_lib_package_ids_.find(library_package_name);
+ if (lib_entry != shared_lib_package_ids_.end()) {
+ return lib_entry->second;
+ }
+
+ return shared_lib_package_ids_[library_package_name] = next_package_id_++;
+}
+
+uint8_t DynamicLibManager::FindUnassignedId(uint8_t start_package_id) {
+ return (start_package_id < next_package_id_) ? next_package_id_ : start_package_id;
+}
+
+} // namespace android
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 7c1ee5cd7cfa..0b2fd9ec982d 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -20,6 +20,8 @@
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/Util.h"
#include "utils/ByteOrder.h"
#include "utils/Trace.h"
@@ -29,40 +31,132 @@
#endif
#endif
-#include "androidfw/ResourceTypes.h"
-
using ::android::base::StringPrintf;
namespace android {
-constexpr static inline bool is_valid_package_id(uint16_t id) {
- return id != 0 && id <= 255;
+static bool compare_target_entries(const Idmap_target_entry &e1, const uint32_t target_id) {
+ return dtohl(e1.target_id) < target_id;
}
-constexpr static inline bool is_valid_type_id(uint16_t id) {
- // Type IDs and package IDs have the same constraints in the IDMAP.
- return is_valid_package_id(id);
+static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
+ return dtohl(e1.overlay_id) < overlay_id;
}
-bool LoadedIdmap::Lookup(const IdmapEntry_header* header, uint16_t input_entry_id,
- uint16_t* output_entry_id) {
- if (input_entry_id < dtohs(header->entry_id_offset)) {
- // After applying the offset, the entry is not present.
- return false;
+size_t Idmap_header::Size() const {
+ return sizeof(Idmap_header) + sizeof(uint8_t) * dtohl(debug_info_size);
+}
+
+OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
+ : data_header_(loaded_idmap->data_header_),
+ idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
+
+OverlayStringPool::~OverlayStringPool() {
+ uninit();
+}
+
+const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const {
+ const size_t offset = dtohl(data_header_->string_pool_index_offset);
+ if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
+ return idmap_string_pool_->stringAt(idx - offset, outLen);
}
- input_entry_id -= dtohs(header->entry_id_offset);
- if (input_entry_id >= dtohs(header->entry_count)) {
- // The entry is not present.
- return false;
+ return ResStringPool::stringAt(idx, outLen);
+}
+
+const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const {
+ const size_t offset = dtohl(data_header_->string_pool_index_offset);
+ if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
+ return idmap_string_pool_->string8At(idx - offset, outLen);
}
- uint32_t result = dtohl(header->entries[input_entry_id]);
- if (result == 0xffffffffu) {
- return false;
+ return ResStringPool::string8At(idx, outLen);
+}
+
+size_t OverlayStringPool::size() const {
+ return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U);
+}
+
+OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
+ const Idmap_overlay_entry* entries,
+ uint8_t target_assigned_package_id)
+ : data_header_(data_header),
+ entries_(entries),
+ target_assigned_package_id_(target_assigned_package_id) { };
+
+status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
+ const Idmap_overlay_entry* first_entry = entries_;
+ const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
+ auto entry = std::lower_bound(first_entry, end_entry, *resId, compare_overlay_entries);
+
+ if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
+ // A mapping for the target resource id could not be found.
+ return DynamicRefTable::lookupResourceId(resId);
}
- *output_entry_id = static_cast<uint16_t>(result);
- return true;
+
+ *resId = (0x00FFFFFFU & dtohl(entry->target_id))
+ | (((uint32_t) target_assigned_package_id_) << 24);
+ return NO_ERROR;
+}
+
+status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const {
+ return DynamicRefTable::lookupResourceId(resId);
+}
+
+IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
+ const Idmap_target_entry* entries,
+ uint8_t target_assigned_package_id,
+ const OverlayDynamicRefTable* overlay_ref_table)
+ : data_header_(data_header),
+ entries_(entries),
+ target_assigned_package_id_(target_assigned_package_id),
+ overlay_ref_table_(overlay_ref_table) { };
+
+IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
+ if ((target_res_id >> 24) != target_assigned_package_id_) {
+ // The resource id must have the same package id as the target package.
+ return {};
+ }
+
+ // The resource ids encoded within the idmap are build-time resource ids.
+ target_res_id = (0x00FFFFFFU & target_res_id)
+ | (((uint32_t) data_header_->target_package_id) << 24);
+
+ const Idmap_target_entry* first_entry = entries_;
+ const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count);
+ auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries);
+
+ if (entry == end_entry || dtohl(entry->target_id) != target_res_id) {
+ // A mapping for the target resource id could not be found.
+ return {};
+ }
+
+ // A reference should be treated as an alias of the resource. Instead of returning the table
+ // entry, return the alias resource id to look up. The alias resource might not reside within the
+ // overlay package, so the resource id must be fixed with the dynamic reference table of the
+ // overlay before returning.
+ if (entry->type == Res_value::TYPE_REFERENCE
+ || entry->type == Res_value::TYPE_DYNAMIC_REFERENCE) {
+ uint32_t overlay_resource_id = dtohl(entry->value);
+
+ // Lookup the resource without rewriting the overlay resource id back to the target resource id
+ // being looked up.
+ overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
+ return Result(overlay_resource_id);
+ }
+
+ // Copy the type and value into the ResTable_entry structure needed by asset manager.
+ uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value);
+ auto table_entry = reinterpret_cast<ResTable_entry*>(malloc(malloc_size));
+ memset(table_entry, 0, malloc_size);
+ table_entry->size = htods(sizeof(ResTable_entry));
+
+ auto table_value = reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(table_entry)
+ + sizeof(ResTable_entry));
+ table_value->dataType = entry->type;
+ table_value->data = entry->value;
+
+ return Result(ResTable_entry_handle::managed(table_entry));
}
static bool is_word_aligned(const void* data) {
@@ -95,24 +189,27 @@ static bool IsValidIdmapHeader(const StringPiece& data) {
return false;
}
- if (!is_valid_package_id(dtohs(header->target_package_id))) {
- LOG(ERROR) << StringPrintf("Target package ID in Idmap is invalid: 0x%02x",
- dtohs(header->target_package_id));
- return false;
- }
-
- if (dtohs(header->type_count) > 255) {
- LOG(ERROR) << StringPrintf("Idmap has too many type mappings (was %d, max 255)",
- (int)dtohs(header->type_count));
- return false;
- }
return true;
}
-LoadedIdmap::LoadedIdmap(const Idmap_header* header) : header_(header) {
+LoadedIdmap::LoadedIdmap(const Idmap_header* header,
+ const Idmap_data_header* data_header,
+ const Idmap_target_entry* target_entries,
+ const Idmap_overlay_entry* overlay_entries,
+ ResStringPool* string_pool)
+ : header_(header),
+ data_header_(data_header),
+ target_entries_(target_entries),
+ overlay_entries_(overlay_entries),
+ string_pool_(string_pool) {
+
size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path),
arraysize(header_->overlay_path));
overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length);
+
+ length = strnlen(reinterpret_cast<const char*>(header_->target_path),
+ arraysize(header_->target_path));
+ target_apk_path_.assign(reinterpret_cast<const char*>(header_->target_path), length);
}
std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_data) {
@@ -121,70 +218,67 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_da
return {};
}
- const Idmap_header* header = reinterpret_cast<const Idmap_header*>(idmap_data.data());
-
- // Can't use make_unique because LoadedImpl constructor is private.
- std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(new LoadedIdmap(header));
+ auto header = reinterpret_cast<const Idmap_header*>(idmap_data.data());
+ const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + header->Size();
+ size_t data_size = idmap_data.size() - header->Size();
+
+ // Currently idmap2 can only generate one data block.
+ auto data_header = reinterpret_cast<const Idmap_data_header*>(data_ptr);
+ data_ptr += sizeof(*data_header);
+ data_size -= sizeof(*data_header);
+
+ // Make sure there is enough space for the target entries declared in the header.
+ const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr);
+ if (data_size / sizeof(Idmap_target_entry) <
+ static_cast<size_t>(dtohl(data_header->target_entry_count))) {
+ LOG(ERROR) << StringPrintf("Idmap too small for the number of target entries (%d)",
+ (int)dtohl(data_header->target_entry_count));
+ return {};
+ }
- const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + sizeof(*header);
- size_t data_size = idmap_data.size() - sizeof(*header);
+ // Advance the data pointer past the target entries.
+ const size_t target_entry_size_bytes =
+ (dtohl(data_header->target_entry_count) * sizeof(Idmap_target_entry));
+ data_ptr += target_entry_size_bytes;
+ data_size -= target_entry_size_bytes;
+
+ // Make sure there is enough space for the overlay entries declared in the header.
+ const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr);
+ if (data_size / sizeof(Idmap_overlay_entry) <
+ static_cast<size_t>(dtohl(data_header->overlay_entry_count))) {
+ LOG(ERROR) << StringPrintf("Idmap too small for the number of overlay entries (%d)",
+ (int)dtohl(data_header->overlay_entry_count));
+ return {};
+ }
- size_t type_maps_encountered = 0u;
- while (data_size >= sizeof(IdmapEntry_header)) {
- if (!is_word_aligned(data_ptr)) {
- LOG(ERROR) << "Type mapping in Idmap is not word aligned";
- return {};
- }
+ // Advance the data pointer past the target entries.
+ const size_t overlay_entry_size_bytes =
+ (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry));
+ data_ptr += overlay_entry_size_bytes;
+ data_size -= overlay_entry_size_bytes;
- // Validate the type IDs.
- const IdmapEntry_header* entry_header = reinterpret_cast<const IdmapEntry_header*>(data_ptr);
- if (!is_valid_type_id(dtohs(entry_header->target_type_id)) || !is_valid_type_id(dtohs(entry_header->overlay_type_id))) {
- LOG(ERROR) << StringPrintf("Invalid type map (0x%02x -> 0x%02x)",
- dtohs(entry_header->target_type_id),
- dtohs(entry_header->overlay_type_id));
- return {};
- }
+ // Read the idmap string pool that holds the value of inline string entries.
+ if (data_size < dtohl(data_header->string_pool_length)) {
+ LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)",
+ (int)dtohl(data_header->string_pool_length));
+ return {};
+ }
- // Make sure there is enough space for the entries declared in the header.
- if ((data_size - sizeof(*entry_header)) / sizeof(uint32_t) <
- static_cast<size_t>(dtohs(entry_header->entry_count))) {
- LOG(ERROR) << StringPrintf("Idmap too small for the number of entries (%d)",
- (int)dtohs(entry_header->entry_count));
+ auto idmap_string_pool = util::make_unique<ResStringPool>();
+ if (dtohl(data_header->string_pool_length) > 0) {
+ status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length));
+ if (err != NO_ERROR) {
+ LOG(ERROR) << "idmap string pool corrupt.";
return {};
}
-
- // Only add a non-empty overlay.
- if (dtohs(entry_header->entry_count != 0)) {
- loaded_idmap->type_map_[static_cast<uint8_t>(dtohs(entry_header->overlay_type_id))] =
- entry_header;
- }
-
- const size_t entry_size_bytes =
- sizeof(*entry_header) + (dtohs(entry_header->entry_count) * sizeof(uint32_t));
- data_ptr += entry_size_bytes;
- data_size -= entry_size_bytes;
- type_maps_encountered++;
}
- // Verify that we parsed all the type maps.
- if (type_maps_encountered != static_cast<size_t>(dtohs(header->type_count))) {
- LOG(ERROR) << "Parsed " << type_maps_encountered << " type maps but expected "
- << (int)dtohs(header->type_count);
- return {};
- }
- return std::move(loaded_idmap);
-}
+ // Can't use make_unique because LoadedIdmap constructor is private.
+ std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(
+ new LoadedIdmap(header, data_header, target_entries, overlay_entries,
+ idmap_string_pool.release()));
-uint8_t LoadedIdmap::TargetPackageId() const {
- return static_cast<uint8_t>(dtohs(header_->target_package_id));
-}
-
-const IdmapEntry_header* LoadedIdmap::GetEntryMapForType(uint8_t type_id) const {
- auto iter = type_map_.find(type_id);
- if (iter != type_map_.end()) {
- return iter->second;
- }
- return nullptr;
+ return std::move(loaded_idmap);
}
} // namespace android
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 72873abc6a42..70bb441f94cb 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -51,9 +51,8 @@ namespace {
// the Type structs.
class TypeSpecPtrBuilder {
public:
- explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header,
- const IdmapEntry_header* idmap_header)
- : header_(header), idmap_header_(idmap_header) {
+ explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header)
+ : header_(header) {
}
void AddType(const ResTable_type* type) {
@@ -70,7 +69,6 @@ class TypeSpecPtrBuilder {
TypeSpec* type_spec =
(TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType)));
type_spec->type_spec = header_;
- type_spec->idmap_entries = idmap_header_;
type_spec->type_count = types_.size();
memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType));
return TypeSpecPtr(type_spec);
@@ -80,7 +78,6 @@ class TypeSpecPtrBuilder {
DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder);
const ResTable_typeSpec* header_;
- const IdmapEntry_header* idmap_header_;
std::vector<const ResTable_type*> types_;
};
@@ -400,8 +397,7 @@ const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const {
}
std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
- const LoadedIdmap* loaded_idmap,
- bool system, bool load_as_shared_library) {
+ package_property_t property_flags) {
ATRACE_NAME("LoadedPackage::Load");
std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage());
@@ -415,19 +411,24 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
return {};
}
- loaded_package->system_ = system;
+ if ((property_flags & PROPERTY_SYSTEM) != 0) {
+ loaded_package->property_flags_ |= PROPERTY_SYSTEM;
+ }
- loaded_package->package_id_ = dtohl(header->id);
- if (loaded_package->package_id_ == 0 ||
- (loaded_package->package_id_ == kAppPackageId && load_as_shared_library)) {
- // Package ID of 0 means this is a shared library.
- loaded_package->dynamic_ = true;
+ if ((property_flags & PROPERTY_LOADER) != 0) {
+ loaded_package->property_flags_ |= PROPERTY_LOADER;
}
- if (loaded_idmap != nullptr) {
- // This is an overlay and so it needs to pretend to be the target package.
- loaded_package->package_id_ = loaded_idmap->TargetPackageId();
- loaded_package->overlay_ = true;
+ if ((property_flags & PROPERTY_OVERLAY) != 0) {
+ // Overlay resources must have an exclusive resource id space for referencing internal
+ // resources.
+ loaded_package->property_flags_ |= PROPERTY_OVERLAY | PROPERTY_DYNAMIC;
+ }
+
+ loaded_package->package_id_ = dtohl(header->id);
+ if (loaded_package->package_id_ == 0 ||
+ (loaded_package->package_id_ == kAppPackageId && (property_flags & PROPERTY_DYNAMIC) != 0)) {
+ loaded_package->property_flags_ |= PROPERTY_DYNAMIC;
}
if (header->header.headerSize >= sizeof(ResTable_package)) {
@@ -511,16 +512,9 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
return {};
}
- // If this is an overlay, associate the mapping of this type to the target type
- // from the IDMAP.
- const IdmapEntry_header* idmap_entry_header = nullptr;
- if (loaded_idmap != nullptr) {
- idmap_entry_header = loaded_idmap->GetEntryMapForType(type_spec->id);
- }
-
std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type_spec->id - 1];
if (builder_ptr == nullptr) {
- builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec, idmap_entry_header);
+ builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec);
loaded_package->resource_ids_.set(type_spec->id, entry_count);
} else {
LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x",
@@ -681,28 +675,24 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
return {};
}
- // We only add the type to the package if there is no IDMAP, or if the type is
- // overlaying something.
- if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) {
- // If this is an overlay, insert it at the target type ID.
- if (type_spec_ptr->idmap_entries != nullptr) {
- type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1;
- }
- loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr);
- }
+ loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr);
}
return std::move(loaded_package);
}
bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
- bool load_as_shared_library) {
+ package_property_t property_flags) {
const ResTable_header* header = chunk.header<ResTable_header>();
if (header == nullptr) {
LOG(ERROR) << "RES_TABLE_TYPE too small.";
return false;
}
+ if (loaded_idmap != nullptr) {
+ global_string_pool_ = util::make_unique<OverlayStringPool>(loaded_idmap);
+ }
+
const size_t package_count = dtohl(header->packageCount);
size_t packages_seen = 0;
@@ -714,9 +704,9 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
switch (child_chunk.type()) {
case RES_STRING_POOL_TYPE:
// Only use the first string pool. Ignore others.
- if (global_string_pool_.getError() == NO_INIT) {
- status_t err = global_string_pool_.setTo(child_chunk.header<ResStringPool_header>(),
- child_chunk.size());
+ if (global_string_pool_->getError() == NO_INIT) {
+ status_t err = global_string_pool_->setTo(child_chunk.header<ResStringPool_header>(),
+ child_chunk.size());
if (err != NO_ERROR) {
LOG(ERROR) << "RES_STRING_POOL_TYPE corrupt.";
return false;
@@ -735,7 +725,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
packages_seen++;
std::unique_ptr<const LoadedPackage> loaded_package =
- LoadedPackage::Load(child_chunk, loaded_idmap, system_, load_as_shared_library);
+ LoadedPackage::Load(child_chunk, property_flags);
if (!loaded_package) {
return false;
}
@@ -758,20 +748,19 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
}
std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data,
- const LoadedIdmap* loaded_idmap, bool system,
- bool load_as_shared_library) {
- ATRACE_NAME("LoadedArsc::LoadTable");
+ const LoadedIdmap* loaded_idmap,
+ const package_property_t property_flags) {
+ ATRACE_NAME("LoadedArsc::Load");
// Not using make_unique because the constructor is private.
std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
- loaded_arsc->system_ = system;
ChunkIterator iter(data.data(), data.size());
while (iter.HasNext()) {
const Chunk chunk = iter.Next();
switch (chunk.type()) {
case RES_TABLE_TYPE:
- if (!loaded_arsc->LoadTable(chunk, loaded_idmap, load_as_shared_library)) {
+ if (!loaded_arsc->LoadTable(chunk, loaded_idmap, property_flags)) {
return {};
}
break;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 66df59b4c83b..4d7e5dfea4f7 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1063,6 +1063,11 @@ size_t ResStringPool::bytes() const
return (mError == NO_ERROR) ? mHeader->header.size : 0;
}
+const void* ResStringPool::data() const
+{
+ return mHeader;
+}
+
bool ResStringPool::isSorted() const
{
return (mHeader->flags&ResStringPool_header::SORTED_FLAG)!=0;
@@ -1358,11 +1363,10 @@ int32_t ResXMLParser::getAttributeData(size_t idx) const
(((const uint8_t*)tag)
+ dtohs(tag->attributeStart)
+ (dtohs(tag->attributeSize)*idx));
- if (attr->typedValue.dataType != Res_value::TYPE_DYNAMIC_REFERENCE ||
- mTree.mDynamicRefTable == NULL) {
+ if (mTree.mDynamicRefTable == NULL ||
+ !mTree.mDynamicRefTable->requiresLookup(&attr->typedValue)) {
return dtohl(attr->typedValue.data);
}
-
uint32_t data = dtohl(attr->typedValue.data);
if (mTree.mDynamicRefTable->lookupResourceId(&data) == NO_ERROR) {
return data;
@@ -1608,10 +1612,9 @@ uint32_t ResXMLParser::getSourceResourceId() const
static volatile int32_t gCount = 0;
-ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable)
+ResXMLTree::ResXMLTree(std::shared_ptr<const DynamicRefTable> dynamicRefTable)
: ResXMLParser(*this)
- , mDynamicRefTable((dynamicRefTable != nullptr) ? dynamicRefTable->clone()
- : std::unique_ptr<DynamicRefTable>(nullptr))
+ , mDynamicRefTable(std::move(dynamicRefTable))
, mError(NO_INIT), mOwnedData(NULL)
{
if (kDebugResXMLTree) {
@@ -1622,7 +1625,7 @@ ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable)
ResXMLTree::ResXMLTree()
: ResXMLParser(*this)
- , mDynamicRefTable(std::unique_ptr<DynamicRefTable>(nullptr))
+ , mDynamicRefTable(nullptr)
, mError(NO_INIT), mOwnedData(NULL)
{
if (kDebugResXMLTree) {
@@ -4784,7 +4787,7 @@ void ResTable::setParameters(const ResTable_config* params)
packageGroup->clearBagCache();
// Find which configurations match the set of parameters. This allows for a much
- // faster lookup in getEntry() if the set of values is narrowed down.
+ // faster lookup in Lookup() if the set of values is narrowed down.
for (size_t t = 0; t < packageGroup->types.size(); t++) {
if (packageGroup->types[t].isEmpty()) {
continue;
@@ -6892,19 +6895,11 @@ DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib)
mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID;
}
-std::unique_ptr<DynamicRefTable> DynamicRefTable::clone() const {
- std::unique_ptr<DynamicRefTable> clone = std::unique_ptr<DynamicRefTable>(
- new DynamicRefTable(mAssignedPackageId, mAppAsLib));
- clone->addMappings(*this);
- return clone;
-}
-
status_t DynamicRefTable::load(const ResTable_lib_header* const header)
{
const uint32_t entryCount = dtohl(header->count);
- const uint32_t sizeOfEntries = sizeof(ResTable_lib_entry) * entryCount;
const uint32_t expectedSize = dtohl(header->header.size) - dtohl(header->header.headerSize);
- if (sizeOfEntries > expectedSize) {
+ if (entryCount > (expectedSize / sizeof(ResTable_lib_entry))) {
ALOGE("ResTable_lib_header size %u is too small to fit %u entries (x %u).",
expectedSize, entryCount, (uint32_t)sizeof(ResTable_lib_entry));
return UNKNOWN_ERROR;
@@ -7016,21 +7011,29 @@ status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
return NO_ERROR;
}
+bool DynamicRefTable::requiresLookup(const Res_value* value) const {
+ // Only resolve non-dynamic references and attributes if the package is loaded as a
+ // library or if a shared library is attempting to retrieve its own resource
+ if ((value->dataType == Res_value::TYPE_REFERENCE ||
+ value->dataType == Res_value::TYPE_ATTRIBUTE) &&
+ (mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) {
+ return true;
+ }
+ return value->dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE ||
+ value->dataType == Res_value::TYPE_DYNAMIC_REFERENCE;
+}
+
status_t DynamicRefTable::lookupResourceValue(Res_value* value) const {
+ if (!requiresLookup(value)) {
+ return NO_ERROR;
+ }
+
uint8_t resolvedType = Res_value::TYPE_REFERENCE;
switch (value->dataType) {
case Res_value::TYPE_ATTRIBUTE:
resolvedType = Res_value::TYPE_ATTRIBUTE;
FALLTHROUGH_INTENDED;
case Res_value::TYPE_REFERENCE:
- // Only resolve non-dynamic references and attributes if the package is loaded as a
- // library or if a shared library is attempting to retrieve its own resource
- if (!(mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) {
- return NO_ERROR;
- }
-
- // If the package is loaded as shared library, the resource reference
- // also need to be fixed.
break;
case Res_value::TYPE_DYNAMIC_ATTRIBUTE:
resolvedType = Res_value::TYPE_ATTRIBUTE;
diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING
index a58b47fcff9d..d1a6a5c18299 100644
--- a/libs/androidfw/TEST_MAPPING
+++ b/libs/androidfw/TEST_MAPPING
@@ -3,6 +3,9 @@
{
"name": "libandroidfw_tests",
"host": true
+ },
+ {
+ "name": "FrameworksResourceLoaderTests"
}
]
} \ No newline at end of file
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 49fc82bff11e..879b050b65bd 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -24,6 +24,7 @@
#include "android-base/unique_fd.h"
#include "androidfw/Asset.h"
+#include "androidfw/Idmap.h"
#include "androidfw/LoadedArsc.h"
#include "androidfw/misc.h"
@@ -34,79 +35,157 @@ namespace android {
class LoadedIdmap;
+// Interface for retrieving assets provided by an ApkAssets.
+class AssetsProvider {
+ public:
+ virtual ~AssetsProvider() = default;
+
+ // Opens a file for reading.
+ std::unique_ptr<Asset> Open(const std::string& path,
+ Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM,
+ bool* file_exists = nullptr) const {
+ return OpenInternal(path, mode, file_exists);
+ }
+
+ // Iterate over all files and directories provided by the zip. The order of iteration is stable.
+ virtual bool ForEachFile(const std::string& /* path */,
+ const std::function<void(const StringPiece&, FileType)>& /* f */) const {
+ return true;
+ }
+
+ protected:
+ AssetsProvider() = default;
+
+ virtual std::unique_ptr<Asset> OpenInternal(const std::string& path,
+ Asset::AccessMode mode,
+ bool* file_exists) const = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AssetsProvider);
+};
+
+class ZipAssetsProvider;
+
// Holds an APK.
class ApkAssets {
public:
+ // This means the data extends to the end of the file.
+ static constexpr off64_t kUnknownLength = -1;
+
// Creates an ApkAssets.
// If `system` is true, the package is marked as a system package, and allows some functions to
// filter out this package when computing what configurations/resources are available.
- static std::unique_ptr<const ApkAssets> Load(const std::string& path, bool system = false);
+ static std::unique_ptr<const ApkAssets> Load(
+ const std::string& path, package_property_t flags = 0U,
+ std::unique_ptr<const AssetsProvider> override_asset = nullptr);
- // Creates an ApkAssets, but forces any package with ID 0x7f to be loaded as a shared library.
- // If `system` is true, the package is marked as a system package, and allows some functions to
- // filter out this package when computing what configurations/resources are available.
- static std::unique_ptr<const ApkAssets> LoadAsSharedLibrary(const std::string& path,
- bool system = false);
+ // Creates an ApkAssets from the given file descriptor, and takes ownership of the file
+ // descriptor. The `friendly_name` is some name that will be used to identify the source of
+ // this ApkAssets in log messages and other debug scenarios.
+ // If `length` equals kUnknownLength, offset must equal 0; otherwise, the apk data will be read
+ // using the `offset` into the file descriptor and will be `length` bytes long.
+ static std::unique_ptr<const ApkAssets> LoadFromFd(
+ base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U,
+ std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0,
+ off64_t length = kUnknownLength);
+
+ // Creates an ApkAssets from the given path which points to a resources.arsc.
+ static std::unique_ptr<const ApkAssets> LoadTable(
+ const std::string& path, package_property_t flags = 0U,
+ std::unique_ptr<const AssetsProvider> override_asset = nullptr);
+
+ // Creates an ApkAssets from the given file descriptor which points to an resources.arsc, and
+ // takes ownership of the file descriptor.
+ // If `length` equals kUnknownLength, offset must equal 0; otherwise, the .arsc data will be read
+ // using the `offset` into the file descriptor and will be `length` bytes long.
+ static std::unique_ptr<const ApkAssets> LoadTableFromFd(
+ base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U,
+ std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0,
+ off64_t length = kUnknownLength);
// Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay
// data.
- // If `system` is true, the package is marked as a system package, and allows some functions to
- // filter out this package when computing what configurations/resources are available.
static std::unique_ptr<const ApkAssets> LoadOverlay(const std::string& idmap_path,
- bool system = false);
-
- // Creates an ApkAssets from the given file descriptor, and takes ownership of the file
- // descriptor. The `friendly_name` is some name that will be used to identify the source of
- // this ApkAssets in log messages and other debug scenarios.
- // If `system` is true, the package is marked as a system package, and allows some functions to
- // filter out this package when computing what configurations/resources are available.
- // If `force_shared_lib` is true, any package with ID 0x7f is loaded as a shared library.
- static std::unique_ptr<const ApkAssets> LoadFromFd(base::unique_fd fd,
- const std::string& friendly_name, bool system,
- bool force_shared_lib);
+ package_property_t flags = 0U);
- std::unique_ptr<Asset> Open(const std::string& path,
- Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const;
+ // Creates an ApkAssets from the directory path. File-based resources are read within the
+ // directory as if the directory is an APK.
+ static std::unique_ptr<const ApkAssets> LoadFromDir(
+ const std::string& path, package_property_t flags = 0U,
+ std::unique_ptr<const AssetsProvider> override_asset = nullptr);
- bool ForEachFile(const std::string& path,
- const std::function<void(const StringPiece&, FileType)>& f) const;
+ // Creates a totally empty ApkAssets with no resources table and no file entries.
+ static std::unique_ptr<const ApkAssets> LoadEmpty(
+ package_property_t flags = 0U,
+ std::unique_ptr<const AssetsProvider> override_asset = nullptr);
inline const std::string& GetPath() const {
return path_;
}
+ inline const AssetsProvider* GetAssetsProvider() const {
+ return assets_provider_.get();
+ }
+
// This is never nullptr.
inline const LoadedArsc* GetLoadedArsc() const {
return loaded_arsc_.get();
}
+ inline const LoadedIdmap* GetLoadedIdmap() const {
+ return loaded_idmap_.get();
+ }
+
+ inline bool IsLoader() const {
+ return (property_flags_ & PROPERTY_LOADER) != 0;
+ }
+
inline bool IsOverlay() const {
- return idmap_asset_.get() != nullptr;
+ return loaded_idmap_ != nullptr;
}
bool IsUpToDate() const;
+ // Creates an Asset from a file on disk.
+ static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
+
+ // Creates an Asset from a file descriptor.
+ //
+ // The asset takes ownership of the file descriptor. If `length` equals kUnknownLength, offset
+ // must equal 0; otherwise, the asset data will be read using the `offset` into the file
+ // descriptor and will be `length` bytes long.
+ static std::unique_ptr<Asset> CreateAssetFromFd(base::unique_fd fd,
+ const char* path,
+ off64_t offset = 0,
+ off64_t length = kUnknownLength);
private:
DISALLOW_COPY_AND_ASSIGN(ApkAssets);
- static std::unique_ptr<const ApkAssets> LoadImpl(base::unique_fd fd, const std::string& path,
- std::unique_ptr<Asset> idmap_asset,
- std::unique_ptr<const LoadedIdmap> loaded_idmap,
- bool system, bool load_as_shared_library);
-
- // Creates an Asset from any file on the file system.
- static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
+ static std::unique_ptr<const ApkAssets> LoadImpl(
+ std::unique_ptr<const AssetsProvider> assets, const std::string& path,
+ package_property_t property_flags,
+ std::unique_ptr<const AssetsProvider> override_assets = nullptr,
+ std::unique_ptr<Asset> idmap_asset = nullptr,
+ std::unique_ptr<const LoadedIdmap> idmap = nullptr);
- ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path, time_t last_mod_time);
+ static std::unique_ptr<const ApkAssets> LoadTableImpl(
+ std::unique_ptr<Asset> resources_asset, const std::string& path,
+ package_property_t property_flags,
+ std::unique_ptr<const AssetsProvider> override_assets = nullptr);
- using ZipArchivePtr = std::unique_ptr<ZipArchive, void(*)(ZipArchiveHandle)>;
+ ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,
+ std::string path,
+ time_t last_mod_time,
+ package_property_t property_flags);
- ZipArchivePtr zip_handle_;
+ std::unique_ptr<const AssetsProvider> assets_provider_;
const std::string path_;
time_t last_mod_time_;
+ package_property_t property_flags_ = 0U;
std::unique_ptr<Asset> resources_asset_;
std::unique_ptr<Asset> idmap_asset_;
std::unique_ptr<const LoadedArsc> loaded_arsc_;
+ std::unique_ptr<const LoadedIdmap> loaded_idmap_;
};
} // namespace android
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 9d12a35395c9..298509eb37a1 100644
--- a/libs/androidfw/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -26,6 +26,7 @@
#include <memory>
+#include <android-base/unique_fd.h>
#include <utils/Compat.h>
#include <utils/Errors.h>
#include <utils/String8.h>
@@ -121,6 +122,11 @@ public:
*/
const char* getAssetSource(void) const { return mAssetSource.string(); }
+ /*
+ * Create the asset from a file descriptor.
+ */
+ static Asset* createFromFd(const int fd, const char* fileName, AccessMode mode);
+
protected:
/*
* Adds this Asset to the global Asset list for debugging and
@@ -153,6 +159,7 @@ private:
/* AssetManager needs access to our "create" functions */
friend class AssetManager;
friend class ApkAssets;
+ friend class ZipAssetsProvider;
/*
* Create the asset from a named file on disk.
@@ -197,8 +204,14 @@ private:
*/
static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode);
+ /*
+ * Create the asset from a memory-mapped file segment.
+ *
+ * The asset takes ownership of the FileMap and the file descriptor "fd". The file descriptor is
+ * used to request new file descriptors using "openFileDescriptor".
+ */
static std::unique_ptr<Asset> createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,
- AccessMode mode);
+ base::unique_fd fd, AccessMode mode);
/*
* Create the asset from a memory-mapped file segment with compressed
@@ -251,9 +264,9 @@ public:
/*
* Use a memory-mapped region.
*
- * On success, the object takes ownership of "dataMap".
+ * On success, the object takes ownership of "dataMap" and "fd".
*/
- status_t openChunk(FileMap* dataMap);
+ status_t openChunk(FileMap* dataMap, base::unique_fd fd);
/*
* Standard Asset interfaces.
@@ -268,11 +281,12 @@ public:
virtual bool isAllocated(void) const { return mBuf != NULL; }
private:
- off64_t mStart; // absolute file offset of start of chunk
- off64_t mLength; // length of the chunk
- off64_t mOffset; // current local offset, 0 == mStart
- FILE* mFp; // for read/seek
- char* mFileName; // for opening
+ off64_t mStart; // absolute file offset of start of chunk
+ off64_t mLength; // length of the chunk
+ off64_t mOffset; // current local offset, 0 == mStart
+ FILE* mFp; // for read/seek
+ char* mFileName; // for opening
+ base::unique_fd mFd; // for opening file descriptors
/*
* To support getBuffer() we either need to read the entire thing into
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 1e2b36cb1703..b2cec2a42994 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -27,6 +27,7 @@
#include "androidfw/ApkAssets.h"
#include "androidfw/Asset.h"
#include "androidfw/AssetManager.h"
+#include "androidfw/DynamicLibManager.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/Util.h"
@@ -94,6 +95,7 @@ class AssetManager2 {
};
AssetManager2();
+ explicit AssetManager2(DynamicLibManager* dynamic_lib_manager);
// Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets
// are not owned by the AssetManager, and must have a longer lifetime.
@@ -122,7 +124,11 @@ class AssetManager2 {
// Returns the DynamicRefTable for the ApkAssets represented by the cookie.
// This may be nullptr if the APK represented by `cookie` has no resource table.
- const DynamicRefTable* GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const;
+ std::shared_ptr<const DynamicRefTable> GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const;
+
+ // Returns a string representation of the overlayable API of a package.
+ bool GetOverlayablesToString(const android::StringPiece& package_name,
+ std::string* out) const;
const std::unordered_map<std::string, std::string>*
GetOverlayableMapForPackage(uint32_t package_id) const;
@@ -232,12 +238,14 @@ class AssetManager2 {
ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
uint32_t* out_last_reference) const;
- // Enables or disables resource resolution logging. Clears stored steps when
- // disabled.
+ // Resets the resource resolution structures in preparation for the next resource retrieval.
+ void ResetResourceResolution() const;
+
+ // Enables or disables resource resolution logging. Clears stored steps when disabled.
void SetResourceResolutionLoggingEnabled(bool enabled);
- // Returns formatted log of last resource resolution path, or empty if no
- // resource has been resolved yet.
+ // Returns formatted log of last resource resolution path, or empty if no resource has been
+ // resolved yet.
std::string GetLastResourceResolution() const;
const std::vector<uint32_t> GetBagResIdStack(uint32_t resid);
@@ -257,10 +265,13 @@ class AssetManager2 {
// Creates a new Theme from this AssetManager.
std::unique_ptr<Theme> NewTheme();
- void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func) const {
+ void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func,
+ package_property_t excluded_property_flags = 0U) const {
for (const PackageGroup& package_group : package_groups_) {
- if (!func(package_group.packages_.front().loaded_package_->GetPackageName(),
- package_group.dynamic_ref_table.mAssignedPackageId)) {
+ const auto loaded_package = package_group.packages_.front().loaded_package_;
+ if ((loaded_package->GetPropertyFlags() & excluded_property_flags) == 0U
+ && !func(loaded_package->GetPackageName(),
+ package_group.dynamic_ref_table->mAssignedPackageId)) {
return;
}
}
@@ -271,6 +282,50 @@ class AssetManager2 {
private:
DISALLOW_COPY_AND_ASSIGN(AssetManager2);
+ // A collection of configurations and their associated ResTable_type that match the current
+ // AssetManager configuration.
+ struct FilteredConfigGroup {
+ std::vector<ResTable_config> configurations;
+ std::vector<const ResTable_type*> types;
+ };
+
+ // Represents an single package.
+ struct ConfiguredPackage {
+ // A pointer to the immutable, loaded package info.
+ const LoadedPackage* loaded_package_;
+
+ // A mutable AssetManager-specific list of configurations that match the AssetManager's
+ // current configuration. This is used as an optimization to avoid checking every single
+ // candidate configuration when looking up resources.
+ ByteBucketArray<FilteredConfigGroup> filtered_configs_;
+ };
+
+ // Represents a Runtime Resource Overlay that overlays resources in the logical package.
+ struct ConfiguredOverlay {
+ // The set of package groups that overlay this package group.
+ IdmapResMap overlay_res_maps_;
+
+ // The cookie of the overlay assets.
+ ApkAssetsCookie cookie;
+ };
+
+ // Represents a logical package, which can be made up of many individual packages. Each package
+ // in a PackageGroup shares the same package name and package ID.
+ struct PackageGroup {
+ // The set of packages that make-up this group.
+ std::vector<ConfiguredPackage> packages_;
+
+ // The cookies associated with each package in the group. They share the same order as
+ // packages_.
+ std::vector<ApkAssetsCookie> cookies_;
+
+ // Runtime Resource Overlays that overlay resources in this package group.
+ std::vector<ConfiguredOverlay> overlays_;
+
+ // A library reference table that contains build-package ID to runtime-package ID mappings.
+ std::shared_ptr<DynamicRefTable> dynamic_ref_table = std::make_shared<DynamicRefTable>();
+ };
+
// Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple
// Res_value, or a complex map/bag type. If successful, it is available in `out_entry`.
// Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with
@@ -291,6 +346,11 @@ class AssetManager2 {
ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
bool ignore_configuration, FindEntryResult* out_entry) const;
+ ApkAssetsCookie FindEntryInternal(const PackageGroup& package_group, uint8_t type_idx,
+ uint16_t entry_idx, const ResTable_config& desired_config,
+ bool /*stop_at_first_match*/,
+ bool ignore_configuration, FindEntryResult* out_entry) const;
+
// Assigns package IDs to all shared library ApkAssets.
// Should be called whenever the ApkAssets are changed.
void BuildDynamicRefTable();
@@ -303,49 +363,22 @@ class AssetManager2 {
// This should always be called when mutating the AssetManager's configuration or ApkAssets set.
void RebuildFilterList(bool filter_incompatible_configs = true);
+ // Retrieves the APK paths of overlays that overlay non-system packages.
+ std::set<std::string> GetNonSystemOverlayPaths() const;
+
// AssetManager2::GetBag(resid) wraps this function to track which resource ids have already
// been seen while traversing bag parents.
const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids);
// Retrieve the assigned package id of the package if loaded into this AssetManager
- uint8_t GetAssignedPackageId(const LoadedPackage* package);
+ uint8_t GetAssignedPackageId(const LoadedPackage* package) const;
+
+ DynamicLibManager* GetDynamicLibManager() const;
// The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
// have a longer lifetime.
std::vector<const ApkAssets*> apk_assets_;
- // A collection of configurations and their associated ResTable_type that match the current
- // AssetManager configuration.
- struct FilteredConfigGroup {
- std::vector<ResTable_config> configurations;
- std::vector<const ResTable_type*> types;
- };
-
- // Represents an single package.
- struct ConfiguredPackage {
- // A pointer to the immutable, loaded package info.
- const LoadedPackage* loaded_package_;
-
- // A mutable AssetManager-specific list of configurations that match the AssetManager's
- // current configuration. This is used as an optimization to avoid checking every single
- // candidate configuration when looking up resources.
- ByteBucketArray<FilteredConfigGroup> filtered_configs_;
- };
-
- // Represents a logical package, which can be made up of many individual packages. Each package
- // in a PackageGroup shares the same package name and package ID.
- struct PackageGroup {
- // The set of packages that make-up this group.
- std::vector<ConfiguredPackage> packages_;
-
- // The cookies associated with each package in the group. They share the same order as
- // packages_.
- std::vector<ApkAssetsCookie> cookies_;
-
- // A library reference table that contains build-package ID to runtime-package ID mappings.
- DynamicRefTable dynamic_ref_table;
- };
-
// DynamicRefTables for shared library package resolution.
// These are ordered according to apk_assets_. The mappings may change depending on what is
// in apk_assets_, therefore they must be stored in the AssetManager and not in the
@@ -360,6 +393,9 @@ class AssetManager2 {
// may need to be purged.
ResTable_config configuration_;
+ // Component responsible for assigning package ids to shared libraries.
+ std::variant<std::unique_ptr<DynamicLibManager>, DynamicLibManager*> dynamic_lib_manager_;
+
// Cached set of bags. These are cached because they can inherit keys from parent bags,
// which involves some calculation.
std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
@@ -378,7 +414,13 @@ class AssetManager2 {
enum class Type {
INITIAL,
BETTER_MATCH,
- OVERLAID
+ BETTER_MATCH_LOADER,
+ OVERLAID,
+ OVERLAID_LOADER,
+ SKIPPED,
+ SKIPPED_LOADER,
+ NO_ENTRY,
+ NO_ENTRY_LOADER,
};
// Marks what kind of override this step was.
@@ -408,7 +450,7 @@ class AssetManager2 {
};
// Record of the last resolved resource's resolution path.
- mutable Resolution last_resolution;
+ mutable Resolution last_resolution_;
};
class Theme {
diff --git a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
deleted file mode 100644
index 8bc25202b3ab..000000000000
--- a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gui/DisplayEventReceiver.h>
-#include <utils/Log.h>
-#include <utils/Looper.h>
-
-namespace android {
-
-class DisplayEventDispatcher : public LooperCallback {
-public:
- explicit DisplayEventDispatcher(const sp<Looper>& looper,
- ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,
- ISurfaceComposer::ConfigChanged configChanged = ISurfaceComposer::eConfigChangedSuppress);
-
- status_t initialize();
- void dispose();
- status_t scheduleVsync();
-
-protected:
- virtual ~DisplayEventDispatcher() = default;
-
-private:
- sp<Looper> mLooper;
- DisplayEventReceiver mReceiver;
- bool mWaitingForVsync;
-
- virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
- virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
- bool connected) = 0;
- virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
- int32_t configId) = 0;
-
- virtual int handleEvent(int receiveFd, int events, void* data);
- bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
- uint32_t* outCount);
-};
-}
diff --git a/libs/androidfw/include/androidfw/DynamicLibManager.h b/libs/androidfw/include/androidfw/DynamicLibManager.h
new file mode 100644
index 000000000000..1ff7079573d2
--- /dev/null
+++ b/libs/androidfw/include/androidfw/DynamicLibManager.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROIDFW_DYNAMICLIBMANAGER_H
+#define ANDROIDFW_DYNAMICLIBMANAGER_H
+
+#include <string>
+#include <unordered_map>
+
+#include "android-base/macros.h"
+
+namespace android {
+
+// Manages assigning resource ids for dynamic resources.
+class DynamicLibManager {
+ public:
+ DynamicLibManager() = default;
+
+ // Retrieves the assigned package id for the library.
+ uint8_t GetAssignedId(const std::string& library_package_name);
+
+ // Queries in ascending order for the first available package id that is not currently assigned to
+ // a library.
+ uint8_t FindUnassignedId(uint8_t start_package_id);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DynamicLibManager);
+
+ uint8_t next_package_id_ = 0x02;
+ std::unordered_map<std::string, uint8_t> shared_lib_package_ids_;
+};
+
+} // namespace android
+
+#endif //ANDROIDFW_DYNAMICLIBMANAGER_H
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index fd02e6f63b74..ccb57f373473 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -20,20 +20,123 @@
#include <memory>
#include <string>
#include <unordered_map>
+#include <variant>
#include "android-base/macros.h"
-
#include "androidfw/StringPiece.h"
+#include "androidfw/ResourceTypes.h"
+#include "utils/ByteOrder.h"
namespace android {
-struct Idmap_header;
-struct IdmapEntry_header;
+class LoadedIdmap;
+class IdmapResMap;
+
+// A string pool for overlay apk assets. The string pool holds the strings of the overlay resources
+// table and additionally allows for loading strings from the idmap string pool. The idmap string
+// pool strings are offset after the end of the overlay resource table string pool entries so
+// queries for strings defined inline in the idmap do not conflict with queries for overlay
+// resource table strings.
+class OverlayStringPool : public ResStringPool {
+ public:
+ virtual ~OverlayStringPool();
+ const char16_t* stringAt(size_t idx, size_t* outLen) const override;
+ const char* string8At(size_t idx, size_t* outLen) const override;
+ size_t size() const override;
+
+ explicit OverlayStringPool(const LoadedIdmap* loaded_idmap);
+ private:
+ const Idmap_data_header* data_header_;
+ const ResStringPool* idmap_string_pool_;
+};
+
+// A dynamic reference table for loaded overlay packages that rewrites the resource id of overlay
+// resources to the resource id of corresponding target resources.
+class OverlayDynamicRefTable : public DynamicRefTable {
+ public:
+ ~OverlayDynamicRefTable() override = default;
+ status_t lookupResourceId(uint32_t* resId) const override;
+
+ private:
+ explicit OverlayDynamicRefTable(const Idmap_data_header* data_header,
+ const Idmap_overlay_entry* entries,
+ uint8_t target_assigned_package_id);
+
+ // Rewrites a compile-time overlay resource id to the runtime resource id of corresponding target
+ // resource.
+ virtual status_t lookupResourceIdNoRewrite(uint32_t* resId) const;
+
+ const Idmap_data_header* data_header_;
+ const Idmap_overlay_entry* entries_;
+ const int8_t target_assigned_package_id_;
+
+ friend LoadedIdmap;
+ friend IdmapResMap;
+};
+
+// A mapping of target resource ids to a values or resource ids that should overlay the target.
+class IdmapResMap {
+ public:
+ // Represents the result of a idmap lookup. The result can be one of three possibillities:
+ // 1) The result is a resource id which represents the overlay resource that should act as an
+ // alias of the target resource.
+ // 2) The result is a table entry which overlays the type and value of the target resource.
+ // 3) The result is neither and the target resource is not overlaid.
+ class Result {
+ public:
+ Result() : data_(nullptr) {};
+ explicit Result(uint32_t value) : data_(value) {};
+ explicit Result(ResTable_entry_handle&& value) : data_(value) { };
+
+ // Returns `true` if the resource is overlaid.
+ inline explicit operator bool() const {
+ return !std::get_if<nullptr_t>(&data_);
+ }
+
+ inline bool IsResourceId() const {
+ return std::get_if<uint32_t>(&data_);
+ }
+
+ inline uint32_t GetResourceId() const {
+ return *std::get_if<uint32_t>(&data_);
+ }
+
+ inline bool IsTableEntry() const {
+ return std::get_if<ResTable_entry_handle>(&data_);
+ }
+
+ inline const ResTable_entry_handle& GetTableEntry() const {
+ return *std::get_if<ResTable_entry_handle>(&data_);
+ }
+
+ private:
+ std::variant<uint32_t, nullptr_t, ResTable_entry_handle> data_;
+ };
+
+ // Looks up the value that overlays the target resource id.
+ Result Lookup(uint32_t target_res_id) const;
+
+ inline const OverlayDynamicRefTable* GetOverlayDynamicRefTable() const {
+ return overlay_ref_table_;
+ }
+
+ private:
+ explicit IdmapResMap(const Idmap_data_header* data_header,
+ const Idmap_target_entry* entries,
+ uint8_t target_assigned_package_id,
+ const OverlayDynamicRefTable* overlay_ref_table);
+
+ const Idmap_data_header* data_header_;
+ const Idmap_target_entry* entries_;
+ const uint8_t target_assigned_package_id_;
+ const OverlayDynamicRefTable* overlay_ref_table_;
+
+ friend LoadedIdmap;
+};
// Represents a loaded/parsed IDMAP for a Runtime Resource Overlay (RRO).
-// An RRO and its target APK have different resource IDs assigned to their resources. Overlaying
-// a resource is done by resource name. An IDMAP is a generated mapping between the resource IDs
-// of the RRO and the target APK for each resource with the same name.
+// An RRO and its target APK have different resource IDs assigned to their resources.
+// An IDMAP is a generated mapping between the resource IDs of the RRO and the target APK.
// A LoadedIdmap can be set alongside the overlay's LoadedArsc to allow the overlay ApkAssets to
// masquerade as the target ApkAssets resources.
class LoadedIdmap {
@@ -41,34 +144,52 @@ class LoadedIdmap {
// Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed.
static std::unique_ptr<const LoadedIdmap> Load(const StringPiece& idmap_data);
- // Performs a lookup of the expected entry ID for the given IDMAP entry header.
- // Returns true if the mapping exists and fills `output_entry_id` with the result.
- static bool Lookup(const IdmapEntry_header* header, uint16_t input_entry_id,
- uint16_t* output_entry_id);
-
- // Returns the package ID for which this overlay should apply.
- uint8_t TargetPackageId() const;
-
// Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated.
inline const std::string& OverlayApkPath() const {
return overlay_apk_path_;
}
- // Returns the mapping of target entry ID to overlay entry ID for the given target type.
- const IdmapEntry_header* GetEntryMapForType(uint8_t type_id) const;
+ // Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated.
+ inline const std::string& TargetApkPath() const {
+ return target_apk_path_;
+ }
+
+ // Returns a mapping from target resource ids to overlay values.
+ inline const IdmapResMap GetTargetResourcesMap(
+ uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const {
+ return IdmapResMap(data_header_, target_entries_, target_assigned_package_id,
+ overlay_ref_table);
+ }
+
+ // Returns a dynamic reference table for a loaded overlay package.
+ inline const OverlayDynamicRefTable GetOverlayDynamicRefTable(
+ uint8_t target_assigned_package_id) const {
+ return OverlayDynamicRefTable(data_header_, overlay_entries_, target_assigned_package_id);
+ }
protected:
// Exposed as protected so that tests can subclass and mock this class out.
LoadedIdmap() = default;
- const Idmap_header* header_ = nullptr;
+ const Idmap_header* header_;
+ const Idmap_data_header* data_header_;
+ const Idmap_target_entry* target_entries_;
+ const Idmap_overlay_entry* overlay_entries_;
+ const std::unique_ptr<ResStringPool> string_pool_;
+
std::string overlay_apk_path_;
- std::unordered_map<uint8_t, const IdmapEntry_header*> type_map_;
+ std::string target_apk_path_;
private:
DISALLOW_COPY_AND_ASSIGN(LoadedIdmap);
- explicit LoadedIdmap(const Idmap_header* header);
+ explicit LoadedIdmap(const Idmap_header* header,
+ const Idmap_data_header* data_header,
+ const Idmap_target_entry* target_entries,
+ const Idmap_overlay_entry* overlay_entries,
+ ResStringPool* string_pool);
+
+ friend OverlayStringPool;
};
} // namespace android
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 950f5413f550..89ff9f52125d 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -51,10 +51,6 @@ struct TypeSpec {
// and under which configurations it varies.
const ResTable_typeSpec* type_spec;
- // Pointer to the mmapped data where the IDMAP mappings for this type
- // exist. May be nullptr if no IDMAP exists.
- const IdmapEntry_header* idmap_entries;
-
// The number of types that follow this struct.
// There is a type for each configuration that entries are defined for.
size_t type_count;
@@ -73,6 +69,26 @@ struct TypeSpec {
}
};
+// Flags that change the behavior of loaded packages.
+// Keep in sync with f/b/android/content/res/ApkAssets.java
+using package_property_t = uint32_t;
+enum : package_property_t {
+ // The package contains framework resource values specified by the system.
+ // This allows some functions to filter out this package when computing
+ // what configurations/resources are available.
+ PROPERTY_SYSTEM = 1U << 0U,
+
+ // The package is a shared library or has a package id of 7f and is loaded as a shared library by
+ // force.
+ PROPERTY_DYNAMIC = 1U << 1U,
+
+ // The package has been loaded dynamically using a ResourcesProvider.
+ PROPERTY_LOADER = 1U << 2U,
+
+ // The package is a RRO.
+ PROPERTY_OVERLAY = 1U << 3U,
+};
+
// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of
// ResTable_type pointers.
// TypeSpecPtr is a managed pointer that knows how to delete itself.
@@ -136,8 +152,7 @@ class LoadedPackage {
}
static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk,
- const LoadedIdmap* loaded_idmap, bool system,
- bool load_as_shared_library);
+ package_property_t property_flags);
~LoadedPackage();
@@ -174,17 +189,26 @@ class LoadedPackage {
// Returns true if this package is dynamic (shared library) and needs to have an ID assigned.
inline bool IsDynamic() const {
- return dynamic_;
+ return (property_flags_ & PROPERTY_DYNAMIC) != 0;
+ }
+
+ // Returns true if this package is a Runtime Resource Overlay.
+ inline bool IsOverlay() const {
+ return (property_flags_ & PROPERTY_OVERLAY) != 0;
}
// Returns true if this package originates from a system provided resource.
inline bool IsSystem() const {
- return system_;
+ return (property_flags_ & PROPERTY_SYSTEM) != 0;
}
- // Returns true if this package is from an overlay ApkAssets.
- inline bool IsOverlay() const {
- return overlay_;
+ // Returns true if this package is a custom loader and should behave like an overlay.
+ inline bool IsCustomLoader() const {
+ return (property_flags_ & PROPERTY_LOADER) != 0;
+ }
+
+ inline package_property_t GetPropertyFlags() const {
+ return property_flags_;
}
// Returns the map of package name to package ID used in this LoadedPackage. At runtime, a
@@ -216,9 +240,6 @@ class LoadedPackage {
const TypeSpecPtr& ptr = type_specs_[i];
if (ptr != nullptr) {
uint8_t type_id = ptr->type_spec->id;
- if (ptr->idmap_entries != nullptr) {
- type_id = ptr->idmap_entries->target_type_id;
- }
f(ptr.get(), type_id - 1);
}
}
@@ -255,17 +276,17 @@ class LoadedPackage {
ResStringPool type_string_pool_;
ResStringPool key_string_pool_;
std::string package_name_;
+ bool defines_overlayable_ = false;
int package_id_ = -1;
int type_id_offset_ = 0;
- bool dynamic_ = false;
- bool system_ = false;
- bool overlay_ = false;
- bool defines_overlayable_ = false;
+ package_property_t property_flags_ = 0U;
ByteBucketArray<TypeSpecPtr> type_specs_;
ByteBucketArray<uint32_t> resource_ids_;
std::vector<DynamicPackageEntry> dynamic_package_map_;
std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;
+
+ // A map of overlayable name to actor
std::unordered_map<std::string, std::string> overlayable_map_;
};
@@ -281,8 +302,7 @@ class LoadedArsc {
// ID.
static std::unique_ptr<const LoadedArsc> Load(const StringPiece& data,
const LoadedIdmap* loaded_idmap = nullptr,
- bool system = false,
- bool load_as_shared_library = false);
+ package_property_t property_flags = 0U);
// Create an empty LoadedArsc. This is used when an APK has no resources.arsc.
static std::unique_ptr<const LoadedArsc> CreateEmpty();
@@ -290,7 +310,7 @@ class LoadedArsc {
// Returns the string pool where all string resource values
// (Res_value::dataType == Res_value::TYPE_STRING) are indexed.
inline const ResStringPool* GetStringPool() const {
- return &global_string_pool_;
+ return global_string_pool_.get();
}
// Gets a pointer to the package with the specified package ID, or nullptr if no such package
@@ -302,20 +322,15 @@ class LoadedArsc {
return packages_;
}
- // Returns true if this is a system provided resource.
- inline bool IsSystem() const {
- return system_;
- }
-
private:
DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
LoadedArsc() = default;
- bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library);
+ bool LoadTable(
+ const Chunk& chunk, const LoadedIdmap* loaded_idmap, package_property_t property_flags);
- ResStringPool global_string_pool_;
+ std::unique_ptr<ResStringPool> global_string_pool_ = util::make_unique<ResStringPool>();
std::vector<std::unique_ptr<const LoadedPackage>> packages_;
- bool system_ = false;
};
} // namespace android
diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h
index 64924f433245..8891512958f0 100644
--- a/libs/androidfw/include/androidfw/MutexGuard.h
+++ b/libs/androidfw/include/androidfw/MutexGuard.h
@@ -47,7 +47,8 @@ class Guarded {
static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer");
public:
- explicit Guarded() : guarded_() {
+ template <typename ...Args>
+ explicit Guarded(Args&& ...args) : guarded_(std::forward<Args>(args)...) {
}
template <typename U = T>
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index fc635aaeb0d8..2bfc7fc38d1c 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -22,6 +22,7 @@
#include <androidfw/Asset.h>
#include <androidfw/LocaleData.h>
+#include <androidfw/StringPiece.h>
#include <utils/Errors.h>
#include <utils/String16.h>
#include <utils/Vector.h>
@@ -34,12 +35,13 @@
#include <android/configuration.h>
+#include <array>
#include <memory>
namespace android {
constexpr const static uint32_t kIdmapMagic = 0x504D4449u;
-constexpr const static uint32_t kIdmapCurrentVersion = 0x00000001u;
+constexpr const static uint32_t kIdmapCurrentVersion = 0x00000003u;
/**
* In C++11, char16_t is defined as *at least* 16 bits. We do a lot of
@@ -492,7 +494,7 @@ class ResStringPool
public:
ResStringPool();
ResStringPool(const void* data, size_t size, bool copyData=false);
- ~ResStringPool();
+ virtual ~ResStringPool();
void setToEmpty();
status_t setTo(const void* data, size_t size, bool copyData=false);
@@ -506,10 +508,10 @@ public:
inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const {
return stringAt(ref.index, outLen);
}
- const char16_t* stringAt(size_t idx, size_t* outLen) const;
+ virtual const char16_t* stringAt(size_t idx, size_t* outLen) const;
// Note: returns null if the string pool is not UTF8.
- const char* string8At(size_t idx, size_t* outLen) const;
+ virtual const char* string8At(size_t idx, size_t* outLen) const;
// Return string whether the pool is UTF8 or UTF16. Does not allow you
// to distinguish null.
@@ -520,9 +522,11 @@ public:
ssize_t indexOfString(const char16_t* str, size_t strLen) const;
- size_t size() const;
+ virtual size_t size() const;
size_t styleCount() const;
size_t bytes() const;
+ const void* data() const;
+
bool isSorted() const;
bool isUTF8() const;
@@ -810,7 +814,7 @@ public:
* The tree stores a clone of the specified DynamicRefTable, so any changes to the original
* DynamicRefTable will not affect this tree after instantiation.
**/
- explicit ResXMLTree(const DynamicRefTable* dynamicRefTable);
+ explicit ResXMLTree(std::shared_ptr<const DynamicRefTable> dynamicRefTable);
ResXMLTree();
~ResXMLTree();
@@ -825,7 +829,7 @@ private:
status_t validateNode(const ResXMLTree_node* node) const;
- std::unique_ptr<const DynamicRefTable> mDynamicRefTable;
+ std::shared_ptr<const DynamicRefTable> mDynamicRefTable;
status_t mError;
void* mOwnedData;
@@ -1582,6 +1586,50 @@ struct ResTable_map
Res_value value;
};
+
+// A ResTable_entry variant that either holds an unmanaged pointer to a constant ResTable_entry or
+// holds a ResTable_entry which is tied to the lifetime of the handle.
+class ResTable_entry_handle {
+ public:
+ ResTable_entry_handle() = default;
+
+ ResTable_entry_handle(const ResTable_entry_handle& handle) {
+ entry_ = handle.entry_;
+ }
+
+ ResTable_entry_handle(ResTable_entry_handle&& handle) noexcept {
+ entry_ = handle.entry_;
+ }
+
+ inline static ResTable_entry_handle managed(ResTable_entry* entry) {
+ return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry));
+ }
+
+ inline static ResTable_entry_handle unmanaged(const ResTable_entry* entry) {
+ return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, [](auto /*p */){}));
+ }
+
+ inline ResTable_entry_handle& operator=(const ResTable_entry_handle& handle) noexcept {
+ entry_ = handle.entry_;
+ return *this;
+ }
+
+ inline ResTable_entry_handle& operator=(ResTable_entry_handle&& handle) noexcept {
+ entry_ = handle.entry_;
+ return *this;
+ }
+
+ inline const ResTable_entry* operator*() & {
+ return entry_.get();
+ }
+
+ private:
+ explicit ResTable_entry_handle(std::shared_ptr<const ResTable_entry> entry)
+ : entry_(std::move(entry)) { }
+
+ std::shared_ptr<const ResTable_entry> entry_;
+};
+
/**
* A package-id to package name mapping for any shared libraries used
* in this resource table. The package-id's encoded in this resource
@@ -1630,43 +1678,66 @@ struct ResTable_overlayable_header
*/
struct ResTable_overlayable_policy_header
{
- struct ResChunk_header header;
-
+ /**
+ * Flags for a bitmask for all possible overlayable policy options.
+ *
+ * Any changes to this set should also update aidl/android/os/OverlayablePolicy.aidl
+ */
enum PolicyFlags : uint32_t {
+ // Base
+ NONE = 0x00000000,
+
// Any overlay can overlay these resources.
- POLICY_PUBLIC = 0x00000001,
+ PUBLIC = 0x00000001,
// The overlay must reside of the system partition or must have existed on the system partition
// before an upgrade to overlay these resources.
- POLICY_SYSTEM_PARTITION = 0x00000002,
+ SYSTEM_PARTITION = 0x00000002,
// The overlay must reside of the vendor partition or must have existed on the vendor partition
// before an upgrade to overlay these resources.
- POLICY_VENDOR_PARTITION = 0x00000004,
+ VENDOR_PARTITION = 0x00000004,
// The overlay must reside of the product partition or must have existed on the product
// partition before an upgrade to overlay these resources.
- POLICY_PRODUCT_PARTITION = 0x00000008,
+ PRODUCT_PARTITION = 0x00000008,
- // The overlay must be signed with the same signature as the actor of the target resource,
- // which can be separate or the same as the target package with the resource.
- POLICY_SIGNATURE = 0x00000010,
+ // The overlay must be signed with the same signature as the package containing the target
+ // resource
+ SIGNATURE = 0x00000010,
// The overlay must reside of the odm partition or must have existed on the odm
// partition before an upgrade to overlay these resources.
- POLICY_ODM_PARTITION = 0x00000020,
+ ODM_PARTITION = 0x00000020,
// The overlay must reside of the oem partition or must have existed on the oem
// partition before an upgrade to overlay these resources.
- POLICY_OEM_PARTITION = 0x00000040,
+ OEM_PARTITION = 0x00000040,
+
+ // The overlay must be signed with the same signature as the actor declared for the target
+ // resource
+ ACTOR_SIGNATURE = 0x00000080,
};
- uint32_t policy_flags;
+
+ using PolicyBitmask = uint32_t;
+
+ struct ResChunk_header header;
+
+ PolicyFlags policy_flags;
// The number of ResTable_ref that follow this header.
uint32_t entry_count;
};
-struct alignas(uint32_t) Idmap_header {
+inline ResTable_overlayable_policy_header::PolicyFlags& operator |=(
+ ResTable_overlayable_policy_header::PolicyFlags& first,
+ ResTable_overlayable_policy_header::PolicyFlags second) {
+ first = static_cast<ResTable_overlayable_policy_header::PolicyFlags>(first | second);
+ return first;
+}
+
+#pragma pack(push, 1)
+struct Idmap_header {
// Always 0x504D4449 ('IDMP')
uint32_t magic;
@@ -1678,17 +1749,32 @@ struct alignas(uint32_t) Idmap_header {
uint8_t target_path[256];
uint8_t overlay_path[256];
- uint16_t target_package_id;
- uint16_t type_count;
-} __attribute__((packed));
+ uint32_t debug_info_size;
+ uint8_t debug_info[0];
-struct alignas(uint32_t) IdmapEntry_header {
- uint16_t target_type_id;
- uint16_t overlay_type_id;
- uint16_t entry_count;
- uint16_t entry_id_offset;
- uint32_t entries[0];
-} __attribute__((packed));
+ size_t Size() const;
+};
+
+struct Idmap_data_header {
+ uint8_t target_package_id;
+ uint8_t overlay_package_id;
+ uint32_t target_entry_count;
+ uint32_t overlay_entry_count;
+ uint32_t string_pool_index_offset;
+ uint32_t string_pool_length;
+};
+
+struct Idmap_target_entry {
+ uint32_t target_id;
+ uint8_t type;
+ uint32_t value;
+};
+
+struct Idmap_overlay_entry {
+ uint32_t overlay_id;
+ uint32_t target_id;
+};
+#pragma pack(pop)
class AssetManager2;
@@ -1706,6 +1792,7 @@ class DynamicRefTable
public:
DynamicRefTable();
DynamicRefTable(uint8_t packageId, bool appAsLib);
+ virtual ~DynamicRefTable() = default;
// Loads an unmapped reference table from the package.
status_t load(const ResTable_lib_header* const header);
@@ -1719,12 +1806,12 @@ public:
void addMapping(uint8_t buildPackageId, uint8_t runtimePackageId);
- // Creates a new clone of the reference table
- std::unique_ptr<DynamicRefTable> clone() const;
+ // Returns whether or not the value must be looked up.
+ bool requiresLookup(const Res_value* value) const;
// Performs the actual conversion of build-time resource ID to run-time
// resource ID.
- status_t lookupResourceId(uint32_t* resId) const;
+ virtual status_t lookupResourceId(uint32_t* resId) const;
status_t lookupResourceValue(Res_value* value) const;
inline const KeyedVector<String16, uint8_t>& entries() const {
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index aa1466fde778..9a3646b49db8 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -19,12 +19,19 @@
#include <cstdlib>
#include <memory>
+#include <sstream>
#include <vector>
#include "android-base/macros.h"
#include "androidfw/StringPiece.h"
+#ifdef __ANDROID__
+#define ANDROID_LOG(x) LOG(x)
+#else
+#define ANDROID_LOG(x) std::stringstream()
+#endif
+
namespace android {
namespace util {
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
index e2b9f0040989..19db25ce8811 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -42,7 +42,7 @@ TEST(ApkAssetsTest, LoadApk) {
const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
ASSERT_THAT(loaded_arsc, NotNull());
ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull());
- ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull());
+ ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml"), NotNull());
}
TEST(ApkAssetsTest, LoadApkFromFd) {
@@ -50,14 +50,13 @@ TEST(ApkAssetsTest, LoadApkFromFd) {
unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY));
ASSERT_THAT(fd.get(), Ge(0));
- std::unique_ptr<const ApkAssets> loaded_apk =
- ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/);
+ std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::LoadFromFd(std::move(fd), path);
ASSERT_THAT(loaded_apk, NotNull());
const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
ASSERT_THAT(loaded_arsc, NotNull());
ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull());
- ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull());
+ ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml"), NotNull());
}
TEST(ApkAssetsTest, LoadApkAsSharedLibrary) {
@@ -70,7 +69,7 @@ TEST(ApkAssetsTest, LoadApkAsSharedLibrary) {
ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u));
EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic());
- loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk");
+ loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk", PROPERTY_DYNAMIC);
ASSERT_THAT(loaded_apk, NotNull());
loaded_arsc = loaded_apk->GetLoadedArsc();
@@ -79,47 +78,16 @@ TEST(ApkAssetsTest, LoadApkAsSharedLibrary) {
EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic());
}
-TEST(ApkAssetsTest, LoadApkWithIdmap) {
- std::string contents;
- ResTable target_table;
- const std::string target_path = GetTestDataPath() + "/basic/basic.apk";
- ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents));
- ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/),
- Eq(NO_ERROR));
-
- ResTable overlay_table;
- const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk";
- ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents));
- ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/),
- Eq(NO_ERROR));
-
- util::unique_cptr<void> idmap_data;
- void* temp_data;
- size_t idmap_len;
-
- ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(),
- overlay_path.c_str(), &temp_data, &idmap_len),
- Eq(NO_ERROR));
- idmap_data.reset(temp_data);
-
- TemporaryFile tf;
- ASSERT_TRUE(base::WriteFully(tf.fd, idmap_data.get(), idmap_len));
- close(tf.fd);
-
- // Open something so that the destructor of TemporaryFile closes a valid fd.
- tf.fd = open("/dev/null", O_WRONLY);
-
- ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull());
-}
-
TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) {
std::unique_ptr<const ApkAssets> loaded_apk =
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
ASSERT_THAT(loaded_apk, NotNull());
- { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); }
+ { ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml",
+ Asset::ACCESS_BUFFER), NotNull()); }
- { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); }
+ { ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml",
+ Asset::ACCESS_BUFFER), NotNull()); }
}
TEST(ApkAssetsTest, OpenUncompressedAssetFd) {
@@ -127,7 +95,8 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) {
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
ASSERT_THAT(loaded_apk, NotNull());
- auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN);
+ auto asset = loaded_apk->GetAssetsProvider()->Open("assets/uncompressed.txt",
+ Asset::ACCESS_UNKNOWN);
ASSERT_THAT(asset, NotNull());
off64_t start, length;
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 40c8e46e4d84..ac32699c6dfd 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -63,10 +63,12 @@ class AssetManager2Test : public ::testing::Test {
libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk");
ASSERT_NE(nullptr, libclient_assets_);
- appaslib_assets_ = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk");
+ appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk",
+ PROPERTY_DYNAMIC);
ASSERT_NE(nullptr, appaslib_assets_);
- system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/);
+ system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk",
+ PROPERTY_SYSTEM);
ASSERT_NE(nullptr, system_assets_);
app_assets_ = ApkAssets::Load(GetTestDataPath() + "/app/app.apk");
@@ -214,6 +216,25 @@ TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) {
EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data);
}
+TEST_F(AssetManager2Test, AssignsUnchangingPackageIdToSharedLibrary) {
+ DynamicLibManager lib_manager;
+ AssetManager2 assetmanager(&lib_manager);
+ assetmanager.SetApkAssets(
+ {lib_one_assets_.get(), lib_two_assets_.get(), libclient_assets_.get()});
+
+ AssetManager2 assetmanager2(&lib_manager);
+ assetmanager2.SetApkAssets(
+ {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
+
+ uint32_t res_id = assetmanager.GetResourceId("com.android.lib_one:string/foo");
+ ASSERT_NE(0U, res_id);
+
+ uint32_t res_id_2 = assetmanager2.GetResourceId("com.android.lib_one:string/foo");
+ ASSERT_NE(0U, res_id_2);
+
+ ASSERT_EQ(res_id, res_id_2);
+}
+
TEST_F(AssetManager2Test, GetSharedLibraryResourceName) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({lib_one_assets_.get()});
@@ -266,6 +287,27 @@ TEST_F(AssetManager2Test, FindsBagResourceFromSharedLibrary) {
EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
}
+TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) {
+ AssetManager2 assetmanager;
+
+ // libclient is built with lib_one and then lib_two in order.
+ // Reverse the order to test that proper package ID re-assignment is happening.
+ assetmanager.SetApkAssets(
+ {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
+
+ const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib);
+ ASSERT_NE(nullptr, bag);
+ ASSERT_EQ(bag->entry_count, 2u);
+
+ // First attribute comes from lib_two.
+ EXPECT_EQ(2, bag->entries[0].cookie);
+ EXPECT_EQ(0x02, get_package_id(bag->entries[0].key));
+
+ // The next two attributes come from lib_one.
+ EXPECT_EQ(2, bag->entries[1].cookie);
+ EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
+}
+
TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) {
AssetManager2 assetmanager;
@@ -707,7 +749,7 @@ TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) {
EXPECT_EQ("", resultDisabled);
}
-TEST_F(AssetManager2Test, GetOverlayableMap) {
+TEST_F(AssetManager2Test, GetOverlayablesToString) {
ResTable_config desired_config;
memset(&desired_config, 0, sizeof(desired_config));
@@ -718,9 +760,17 @@ TEST_F(AssetManager2Test, GetOverlayableMap) {
const auto map = assetmanager.GetOverlayableMapForPackage(0x7f);
ASSERT_NE(nullptr, map);
- ASSERT_EQ(2, map->size());
+ ASSERT_EQ(3, map->size());
ASSERT_EQ(map->at("OverlayableResources1"), "overlay://theme");
ASSERT_EQ(map->at("OverlayableResources2"), "overlay://com.android.overlayable");
+ ASSERT_EQ(map->at("OverlayableResources3"), "");
+
+ std::string api;
+ ASSERT_TRUE(assetmanager.GetOverlayablesToString("com.android.overlayable", &api));
+ ASSERT_EQ(api.find("not_overlayable"), std::string::npos);
+ ASSERT_NE(api.find("resource='com.android.overlayable:string/overlayable2' overlayable='OverlayableResources1' actor='overlay://theme' policy='0x0000000a'\n"),
+ std::string::npos);
+
}
} // namespace android
diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp
index c8dbe205fee2..24361b5817f4 100644
--- a/libs/androidfw/tests/AttributeResolution_test.cpp
+++ b/libs/androidfw/tests/AttributeResolution_test.cpp
@@ -67,7 +67,7 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest {
TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) {
AssetManager2 assetmanager;
- auto apk_assets = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/styles/styles.apk");
+ auto apk_assets = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk", PROPERTY_DYNAMIC);
ASSERT_NE(nullptr, apk_assets);
assetmanager.SetApkAssets({apk_assets.get()});
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 10b83a75304d..41ba637da5d7 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -14,114 +14,231 @@
* limitations under the License.
*/
+#include "android-base/file.h"
+#include "androidfw/ApkAssets.h"
+#include "androidfw/AssetManager2.h"
#include "androidfw/ResourceTypes.h"
#include "utils/String16.h"
#include "utils/String8.h"
#include "TestHelpers.h"
-#include "data/basic/R.h"
+#include "data/overlay/R.h"
+#include "data/overlayable/R.h"
+#include "data/system/R.h"
-using ::com::android::basic::R;
+namespace overlay = com::android::overlay;
+namespace overlayable = com::android::overlayable;
namespace android {
+namespace {
+
class IdmapTest : public ::testing::Test {
protected:
void SetUp() override {
- std::string contents;
- ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc",
- &contents));
- ASSERT_EQ(NO_ERROR, target_table_.add(contents.data(), contents.size(), 0, true));
-
- ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk",
- "resources.arsc", &overlay_data_));
- ResTable overlay_table;
- ASSERT_EQ(NO_ERROR, overlay_table.add(overlay_data_.data(), overlay_data_.size()));
-
- char target_name[256] = "com.android.basic";
- ASSERT_EQ(NO_ERROR, overlay_table.createIdmap(target_table_, 0, 0, target_name, target_name,
- &data_, &data_size_));
- }
+ // Move to the test data directory so the idmap can locate the overlay APK.
+ std::string original_path = base::GetExecutableDirectory();
+ chdir(GetTestDataPath().c_str());
+
+ system_assets_ = ApkAssets::Load("system/system.apk");
+ ASSERT_NE(nullptr, system_assets_);
- void TearDown() override {
- ::free(data_);
+ overlay_assets_ = ApkAssets::LoadOverlay("overlay/overlay.idmap");
+ ASSERT_NE(nullptr, overlay_assets_);
+
+ overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk");
+ ASSERT_NE(nullptr, overlayable_assets_);
+ chdir(original_path.c_str());
}
- ResTable target_table_;
- std::string overlay_data_;
- void* data_ = nullptr;
- size_t data_size_ = 0;
+ protected:
+ std::unique_ptr<const ApkAssets> system_assets_;
+ std::unique_ptr<const ApkAssets> overlay_assets_;
+ std::unique_ptr<const ApkAssets> overlayable_assets_;
};
-TEST_F(IdmapTest, CanLoadIdmap) {
- ASSERT_EQ(NO_ERROR,
- target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
+std::string GetStringFromApkAssets(const AssetManager2& asset_manager, const Res_value& value,
+ ApkAssetsCookie cookie) {
+ auto assets = asset_manager.GetApkAssets();
+ const ResStringPool* string_pool = assets[cookie]->GetLoadedArsc()->GetStringPool();
+ return GetStringFromPool(string_pool, value.data);
+}
+
}
TEST_F(IdmapTest, OverlayOverridesResourceValue) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
Res_value val;
- ssize_t block = target_table_.getResource(R::string::test2, &val, false);
- ASSERT_GE(block, 0);
- ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
- const ResStringPool* pool = target_table_.getTableStringBlock(block);
- ASSERT_TRUE(pool != NULL);
- ASSERT_LT(val.data, pool->size());
-
- size_t str_len;
- const char16_t* target_str16 = pool->stringAt(val.data, &str_len);
- ASSERT_TRUE(target_str16 != NULL);
- ASSERT_EQ(String16("test2"), String16(target_str16, str_len));
-
- ASSERT_EQ(NO_ERROR,
- target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
-
- ssize_t new_block = target_table_.getResource(R::string::test2, &val, false);
- ASSERT_GE(new_block, 0);
- ASSERT_NE(block, new_block);
- ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
- pool = target_table_.getTableStringBlock(new_block);
- ASSERT_TRUE(pool != NULL);
- ASSERT_LT(val.data, pool->size());
-
- target_str16 = pool->stringAt(val.data, &str_len);
- ASSERT_TRUE(target_str16 != NULL);
- ASSERT_EQ(String16("test2-overlay"), String16(target_str16, str_len));
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable5,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
+ ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Overlay One");
}
-TEST_F(IdmapTest, OverlaidResourceHasSameName) {
- ASSERT_EQ(NO_ERROR,
- target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
+TEST_F(IdmapTest, OverlayOverridesResourceValueUsingDifferentPackage) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable10,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 0U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
+ ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "yes");
+}
+
+TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInternalResource) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable8,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE);
+ ASSERT_EQ(val.data, (overlay::R::string::internal & 0x00ffffff) | (0x02 << 24));
+}
+
+TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineInteger) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::integer::config_integer,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_INT_DEC);
+ ASSERT_EQ(val.data, 42);
+}
+
+TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineString) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
+ ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Hardcoded string");
+}
- ResTable::resource_name res_name;
- ASSERT_TRUE(target_table_.getResourceName(R::array::integerArray1, true, &res_name));
+TEST_F(IdmapTest, OverlayOverridesResourceValueUsingOverlayingResource) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable9,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE);
+ ASSERT_EQ(val.data, overlayable::R::string::overlayable7);
+}
- ASSERT_TRUE(res_name.package != NULL);
- ASSERT_TRUE(res_name.type != NULL);
- ASSERT_TRUE(res_name.name8 != NULL);
+TEST_F(IdmapTest, OverlayOverridesXmlParser) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::layout::hello_view,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
+ ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "res/layout/hello_view.xml");
+
+ auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", cookie,
+ Asset::ACCESS_RANDOM);
+ auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(cookie);
+ auto xml_tree = util::make_unique<ResXMLTree>(std::move(dynamic_ref_table));
+ status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), false);
+ ASSERT_EQ(err, NO_ERROR);
+
+ while (xml_tree->next() != ResXMLParser::START_TAG) { }
+
+ // The resource id of @id/hello_view should be rewritten to the resource id/hello_view within the
+ // target.
+ ASSERT_EQ(xml_tree->getAttributeNameResID(0), 0x010100d0 /* android:attr/id */);
+ ASSERT_EQ(xml_tree->getAttributeDataType(0), Res_value::TYPE_REFERENCE);
+ ASSERT_EQ(xml_tree->getAttributeData(0), overlayable::R::id::hello_view);
+
+ // The resource id of @android:string/yes should not be rewritten even though it overlays
+ // string/overlayable10 in the target.
+ ASSERT_EQ(xml_tree->getAttributeNameResID(1), 0x0101014f /* android:attr/text */);
+ ASSERT_EQ(xml_tree->getAttributeDataType(1), Res_value::TYPE_REFERENCE);
+ ASSERT_EQ(xml_tree->getAttributeData(1), 0x01040013 /* android:string/yes */);
+
+ // The resource id of the attribute within the overlay should be rewritten to the resource id of
+ // the attribute in the target.
+ ASSERT_EQ(xml_tree->getAttributeNameResID(2), overlayable::R::attr::max_lines);
+ ASSERT_EQ(xml_tree->getAttributeDataType(2), Res_value::TYPE_INT_DEC);
+ ASSERT_EQ(xml_tree->getAttributeData(2), 4);
+}
- EXPECT_EQ(String16("com.android.basic"), String16(res_name.package, res_name.packageLen));
- EXPECT_EQ(String16("array"), String16(res_name.type, res_name.typeLen));
- EXPECT_EQ(String8("integerArray1"), String8(res_name.name8, res_name.nameLen));
+TEST_F(IdmapTest, OverlaidResourceHasSameName) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+
+ AssetManager2::ResourceName name;
+ ASSERT_TRUE(asset_manager.GetResourceName(overlayable::R::string::overlayable9, &name));
+ ASSERT_EQ(std::string(name.package), "com.android.overlayable");
+ ASSERT_EQ(String16(name.type16), u"string");
+ ASSERT_EQ(std::string(name.entry), "overlayable9");
}
-constexpr const uint32_t kNonOverlaidResourceId = 0x7fff0000u;
+TEST_F(IdmapTest, OverlayLoaderInterop) {
+ std::string contents;
+ auto loader_assets = ApkAssets::LoadTable(GetTestDataPath() + "/loader/resources.arsc",
+ PROPERTY_LOADER);
-TEST_F(IdmapTest, OverlayDoesNotIncludeNonOverlaidResources) {
- // First check that the resource we're trying to not include when overlaid is present when
- // the overlay is loaded as a standalone APK.
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(overlay_data_.data(), overlay_data_.size(), 0, true));
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(),
+ overlay_assets_.get()});
Res_value val;
- ssize_t block = table.getResource(kNonOverlaidResourceId, &val, false /*mayBeBag*/);
- ASSERT_GE(block, 0);
-
- // Now add the overlay and verify that the unoverlaid resource is gone.
- ASSERT_EQ(NO_ERROR,
- target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
- block = target_table_.getResource(kNonOverlaidResourceId, &val, false /*mayBeBag*/);
- ASSERT_LT(block, 0);
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ std::cout << asset_manager.GetLastResourceResolution();
+ ASSERT_EQ(cookie, 1U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
+ ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "loader");
}
} // namespace
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index d58e8d20c8aa..2d69dfe4f429 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -25,6 +25,7 @@
#include "data/overlayable/R.h"
#include "data/sparse/R.h"
#include "data/styles/R.h"
+#include "data/system/R.h"
namespace app = com::android::app;
namespace basic = com::android::basic;
@@ -40,6 +41,8 @@ using ::testing::NotNull;
using ::testing::SizeIs;
using ::testing::StrEq;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace android {
TEST(LoadedArscTest, LoadSinglePackageArsc) {
@@ -143,8 +146,7 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) {
"resources.arsc", &contents));
std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/,
- true /*load_as_shared_library*/);
+ LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, PROPERTY_DYNAMIC);
ASSERT_THAT(loaded_arsc, NotNull());
const auto& packages = loaded_arsc->GetPackages();
@@ -221,68 +223,12 @@ TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) {
ASSERT_THAT(type_spec->types[0], NotNull());
}
-class MockLoadedIdmap : public LoadedIdmap {
- public:
- MockLoadedIdmap() : LoadedIdmap() {
- local_header_.magic = kIdmapMagic;
- local_header_.version = kIdmapCurrentVersion;
- local_header_.target_package_id = 0x08;
- local_header_.type_count = 1;
- header_ = &local_header_;
-
- entry_header = util::unique_cptr<IdmapEntry_header>(
- (IdmapEntry_header*)::malloc(sizeof(IdmapEntry_header) + sizeof(uint32_t)));
- entry_header->target_type_id = 0x03;
- entry_header->overlay_type_id = 0x02;
- entry_header->entry_id_offset = 1;
- entry_header->entry_count = 1;
- entry_header->entries[0] = 0x00000000u;
- type_map_[entry_header->overlay_type_id] = entry_header.get();
- }
-
- private:
- Idmap_header local_header_;
- util::unique_cptr<IdmapEntry_header> entry_header;
-};
-
-TEST(LoadedArscTest, LoadOverlay) {
- std::string contents;
- ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc",
- &contents));
-
- MockLoadedIdmap loaded_idmap;
-
- std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(contents), &loaded_idmap);
- ASSERT_THAT(loaded_arsc, NotNull());
-
- const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u);
- ASSERT_THAT(package, NotNull());
-
- const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1);
- ASSERT_THAT(type_spec, NotNull());
- ASSERT_THAT(type_spec->type_count, Ge(1u));
- ASSERT_THAT(type_spec->types[0], NotNull());
-
- // The entry being overlaid doesn't exist at the original entry index.
- ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull());
-
- // Since this is an overlay, the actual entry ID must be mapped.
- ASSERT_THAT(type_spec->idmap_entries, NotNull());
- uint16_t target_entry_id = 0u;
- ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id));
- ASSERT_THAT(target_entry_id, Eq(0x0u));
- ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull());
-}
-
TEST(LoadedArscTest, LoadOverlayable) {
std::string contents;
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
"resources.arsc", &contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/,
- false /*load_as_shared_library*/);
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
ASSERT_THAT(loaded_arsc, NotNull());
const LoadedPackage* package = loaded_arsc->GetPackageById(
@@ -296,29 +242,29 @@ TEST(LoadedArscTest, LoadOverlayable) {
ASSERT_THAT(info, NotNull());
EXPECT_THAT(info->name, Eq("OverlayableResources1"));
EXPECT_THAT(info->actor, Eq("overlay://theme"));
- EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
+ EXPECT_THAT(info->policy_flags, Eq(PolicyFlags::PUBLIC));
info = package->GetOverlayableInfo(overlayable::R::string::overlayable2);
ASSERT_THAT(info, NotNull());
EXPECT_THAT(info->name, Eq("OverlayableResources1"));
EXPECT_THAT(info->actor, Eq("overlay://theme"));
EXPECT_THAT(info->policy_flags,
- Eq(ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION
- | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
+ Eq(PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION));
info = package->GetOverlayableInfo(overlayable::R::string::overlayable3);
ASSERT_THAT(info, NotNull());
EXPECT_THAT(info->name, Eq("OverlayableResources2"));
EXPECT_THAT(info->actor, Eq("overlay://com.android.overlayable"));
EXPECT_THAT(info->policy_flags,
- Eq(ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION
- | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
+ Eq(PolicyFlags::VENDOR_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION));
info = package->GetOverlayableInfo(overlayable::R::string::overlayable4);
EXPECT_THAT(info->name, Eq("OverlayableResources1"));
EXPECT_THAT(info->actor, Eq("overlay://theme"));
ASSERT_THAT(info, NotNull());
- EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
+ EXPECT_THAT(info->policy_flags, Eq(PolicyFlags::PUBLIC));
}
TEST(LoadedArscTest, ResourceIdentifierIterator) {
@@ -382,9 +328,42 @@ TEST(LoadedArscTest, GetOverlayableMap) {
ASSERT_EQ(std::string("com.android.overlayable"), packages[0]->GetPackageName());
const auto map = packages[0]->GetOverlayableMap();
- ASSERT_EQ(2, map.size());
+ ASSERT_EQ(3, map.size());
ASSERT_EQ(map.at("OverlayableResources1"), "overlay://theme");
ASSERT_EQ(map.at("OverlayableResources2"), "overlay://com.android.overlayable");
+ ASSERT_EQ(map.at("OverlayableResources3"), "");
+}
+
+TEST(LoadedArscTest, LoadCustomLoader) {
+ std::string contents;
+
+ std::unique_ptr<Asset>
+ asset = ApkAssets::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
+
+ const StringPiece data(
+ reinterpret_cast<const char*>(asset->getBuffer(true /*wordAligned*/)),
+ asset->getLength());
+
+ std::unique_ptr<const LoadedArsc> loaded_arsc =
+ LoadedArsc::Load(data, nullptr, PROPERTY_LOADER);
+ ASSERT_THAT(loaded_arsc, NotNull());
+
+ const LoadedPackage* package =
+ loaded_arsc->GetPackageById(get_package_id(overlayable::R::string::overlayable11));
+ ASSERT_THAT(package, NotNull());
+ EXPECT_THAT(package->GetPackageName(), StrEq("com.android.loader"));
+ EXPECT_THAT(package->GetPackageId(), Eq(0x7f));
+
+ const uint8_t type_index = get_type_id(overlayable::R::string::overlayable11) - 1;
+ const uint16_t entry_index = get_entry_id(overlayable::R::string::overlayable11);
+
+ const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
+ ASSERT_THAT(type_spec, NotNull());
+ ASSERT_THAT(type_spec->type_count, Ge(1u));
+
+ const ResTable_type* type = type_spec->types[0];
+ ASSERT_THAT(type, NotNull());
+ ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
}
// structs with size fields (like Res_value, ResTable_entry) should be
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index be5ecd94a588..16b9c75982fb 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -36,7 +36,7 @@ namespace android {
class ThemeTest : public ::testing::Test {
public:
void SetUp() override {
- system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/);
+ system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", PROPERTY_SYSTEM);
ASSERT_NE(nullptr, system_assets_);
style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
diff --git a/libs/androidfw/tests/data/lib_two/R.h b/libs/androidfw/tests/data/lib_two/R.h
index 92b9cc10e7a8..fd5a910961cb 100644
--- a/libs/androidfw/tests/data/lib_two/R.h
+++ b/libs/androidfw/tests/data/lib_two/R.h
@@ -30,16 +30,22 @@ struct R {
};
};
+ struct integer {
+ enum : uint32_t {
+ bar = 0x02020000, // default
+ };
+ };
+
struct string {
enum : uint32_t {
- LibraryString = 0x02020000, // default
- foo = 0x02020001, // default
+ LibraryString = 0x02030000, // default
+ foo = 0x02030001, // default
};
};
struct style {
enum : uint32_t {
- Theme = 0x02030000, // default
+ Theme = 0x02040000, // default
};
};
};
diff --git a/libs/androidfw/tests/data/lib_two/lib_two.apk b/libs/androidfw/tests/data/lib_two/lib_two.apk
index 486c23000276..8193db637eed 100644
--- a/libs/androidfw/tests/data/lib_two/lib_two.apk
+++ b/libs/androidfw/tests/data/lib_two/lib_two.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/lib_two/res/values/values.xml b/libs/androidfw/tests/data/lib_two/res/values/values.xml
index 340d14c34c5d..4e1d69aa5a5a 100644
--- a/libs/androidfw/tests/data/lib_two/res/values/values.xml
+++ b/libs/androidfw/tests/data/lib_two/res/values/values.xml
@@ -18,14 +18,17 @@
<public type="attr" name="attr3" id="0x00010000" />
<attr name="attr3" format="integer" />
- <public type="string" name="LibraryString" id="0x00020000" />
+ <public type="integer" name="bar" id="0x00020000" />
+ <integer name="bar">1337</integer>
+
+ <public type="string" name="LibraryString" id="0x00030000" />
<string name="LibraryString">Hi from library two</string>
- <public type="string" name="foo" id="0x00020001" />
+ <public type="string" name="foo" id="0x00030001" />
<string name="foo">Foo from lib_two</string>
- <public type="style" name="Theme" id="0x02030000" />
+ <public type="style" name="Theme" id="0x00040000" />
<style name="Theme">
- <item name="com.android.lib_two:attr3">800</item>
+ <item name="com.android.lib_two:attr3">@integer/bar</item>
</style>
</resources>
diff --git a/libs/androidfw/tests/data/libclient/R.h b/libs/androidfw/tests/data/libclient/R.h
index 43d1f9bb68e7..e21b3ebae826 100644
--- a/libs/androidfw/tests/data/libclient/R.h
+++ b/libs/androidfw/tests/data/libclient/R.h
@@ -34,6 +34,7 @@ struct R {
struct style {
enum : uint32_t {
Theme = 0x7f020000, // default
+ ThemeMultiLib = 0x7f020001, // default
};
};
diff --git a/libs/androidfw/tests/data/libclient/libclient.apk b/libs/androidfw/tests/data/libclient/libclient.apk
index 17990248e862..4b9a8833c64a 100644
--- a/libs/androidfw/tests/data/libclient/libclient.apk
+++ b/libs/androidfw/tests/data/libclient/libclient.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/libclient/res/values/values.xml b/libs/androidfw/tests/data/libclient/res/values/values.xml
index fead7c323767..a29f473e3c17 100644
--- a/libs/androidfw/tests/data/libclient/res/values/values.xml
+++ b/libs/androidfw/tests/data/libclient/res/values/values.xml
@@ -27,6 +27,12 @@
<item name="bar">@com.android.lib_one:string/foo</item>
</style>
+ <public type="style" name="ThemeMultiLib" id="0x7f020001" />
+ <style name="ThemeMultiLib" >
+ <item name="com.android.lib_one:attr1">@com.android.lib_one:string/foo</item>
+ <item name="com.android.lib_two:attr3">@com.android.lib_two:integer/bar</item>
+ </style>
+
<public type="string" name="foo_one" id="0x7f030000" />
<string name="foo_one">@com.android.lib_one:string/foo</string>
diff --git a/libs/androidfw/tests/data/loader/AndroidManifest.xml b/libs/androidfw/tests/data/loader/AndroidManifest.xml
new file mode 100644
index 000000000000..4c0bb47f59bf
--- /dev/null
+++ b/libs/androidfw/tests/data/loader/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest package="com.android.loader">
+ <application>
+ </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/loader/build b/libs/androidfw/tests/data/loader/build
new file mode 100755
index 000000000000..457ec51ffe69
--- /dev/null
+++ b/libs/androidfw/tests/data/loader/build
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -e
+
+FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
+
+rm resources.arsc
+aapt2 compile --dir res -o compiled.flata
+aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o loader.apk compiled.flata
+unzip loader.apk resources.arsc
+rm loader.apk
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/loader/res/values/public.xml b/libs/androidfw/tests/data/loader/res/values/public.xml
new file mode 100644
index 000000000000..3293229104ce
--- /dev/null
+++ b/libs/androidfw/tests/data/loader/res/values/public.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <public type="string" name="overlayable11" id="0x7f01000b" />
+</resources> \ No newline at end of file
diff --git a/libs/androidfw/tests/data/loader/res/values/values.xml b/libs/androidfw/tests/data/loader/res/values/values.xml
new file mode 100644
index 000000000000..0653536508f8
--- /dev/null
+++ b/libs/androidfw/tests/data/loader/res/values/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <string name="overlayable11">loader</string>
+</resources>
diff --git a/libs/androidfw/tests/data/loader/resources.arsc b/libs/androidfw/tests/data/loader/resources.arsc
new file mode 100644
index 000000000000..2bdb288dbcab
--- /dev/null
+++ b/libs/androidfw/tests/data/loader/resources.arsc
Binary files differ
diff --git a/libs/androidfw/tests/data/overlay/AndroidManifest.xml b/libs/androidfw/tests/data/overlay/AndroidManifest.xml
index a56ac18e900b..28a11489fae0 100644
--- a/libs/androidfw/tests/data/overlay/AndroidManifest.xml
+++ b/libs/androidfw/tests/data/overlay/AndroidManifest.xml
@@ -15,7 +15,9 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.test.basic">
- <application>
- </application>
+ package="com.android.test.overlay">
+ <overlay
+ android:targetPackage="com.android.test.basic"
+ android:targetName="OverlayableResources3"
+ android:resourcesMap="@xml/overlays"/>
</manifest>
diff --git a/libs/androidfw/tests/data/overlay/R.h b/libs/androidfw/tests/data/overlay/R.h
new file mode 100644
index 000000000000..f3dbed29d7ee
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/R.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TESTS_DATA_OVERLAY_R_H_
+#define TESTS_DATA_OVERLAY_R_H_
+
+#include <cstdint>
+
+namespace com {
+namespace android {
+namespace overlay {
+
+struct R {
+ struct string {
+ enum : uint32_t {
+ internal = 0x7f040000,
+ };
+ };
+};
+
+} // namespace overlay
+} // namespace android
+} // namespace com
+
+#endif /* TESTS_DATA_OVERLAY_R_H_ */
diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build
index 716b1bd9db64..99dfd63051a7 100755
--- a/libs/androidfw/tests/data/overlay/build
+++ b/libs/androidfw/tests/data/overlay/build
@@ -17,6 +17,15 @@
set -e
+FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
+
aapt2 compile --dir res -o compiled.flata
-aapt2 link --manifest AndroidManifest.xml -o overlay.apk compiled.flata
+aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o overlay.apk compiled.flata \
+ --no-auto-version
rm compiled.flata
+
+# Navigate back a directory so the idmap can find the overlays in the test data directory when being
+# loaded during testing.
+cd ../
+idmap2 create --target-apk-path overlayable/overlayable.apk \
+ --overlay-apk-path overlay/overlay.apk --idmap-path overlay/overlay.idmap
diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk
index d37874dcbb40..62e98662e68d 100644
--- a/libs/androidfw/tests/data/overlay/overlay.apk
+++ b/libs/androidfw/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap
new file mode 100644
index 000000000000..3759ed650033
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/overlay.idmap
Binary files differ
diff --git a/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml b/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml
new file mode 100644
index 000000000000..54dc6c09acf1
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/hello_view"
+ android:text="@android:string/yes"
+ app:max_lines="4"/> \ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlay/res/values/values.xml b/libs/androidfw/tests/data/overlay/res/values/values.xml
index 8e4417e457d1..ba018ec19e9f 100644
--- a/libs/androidfw/tests/data/overlay/res/values/values.xml
+++ b/libs/androidfw/tests/data/overlay/res/values/values.xml
@@ -13,13 +13,11 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<resources>
- <string name="test2">test2-overlay</string>
- <integer-array name="integerArray1">
- <item>10</item>
- <item>11</item>
- </integer-array>
- <public type="animator" name="unoverlaid" id="0x7fff0000" />
- <item type="animator" name="unoverlaid">@null</item>
+ <string name="overlay1">Overlay One</string>
+ <string name="overlay2">Overlay Two</string>
+ <string name="overlay3">@string/internal</string>
+ <string name="overlay4">@string/overlay2</string>
+ <string name="internal">Internal</string>
+ <attr name="max_lines" format="integer" />
</resources>
diff --git a/libs/androidfw/tests/data/overlay/res/xml/overlays.xml b/libs/androidfw/tests/data/overlay/res/xml/overlays.xml
new file mode 100644
index 000000000000..9eca2faa76f4
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/res/xml/overlays.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language language governing permissions and
+ limitations under the License.
+-->
+<overlay>
+ <!-- Overlays string/overlayable5 with the string "Overlay One". -->
+ <item target="string/overlayable5" value="@string/overlay1"/>
+
+ <!-- Overlays string/overlayable6 and string/overlayable7 with the string "Overlay Two". -->
+ <item target="string/overlayable7" value="@string/overlay2" />
+ <item target="string/overlayable6" value="@string/overlay2" />
+
+ <!-- Overlays string/overlayable8 with a reference to @string/internal. -->
+ <item target="string/overlayable8" value="@string/overlay3" />
+
+ <!-- Overlays string/overlayable9 with a reference to @string/overlay2. The reference to
+ @string/overlay2 should be rewritten to @string/overlayable7 in the target. -->
+ <item target="string/overlayable9" value="@string/overlay4" />
+
+ <!-- Overlays string/overlayable10 with the string "yes". -->
+ <item target="string/overlayable10" value="@android:string/yes" />
+
+ <!-- Overlays string/overlayable11 with the string "Hardcoded string". -->
+ <item target="string/overlayable11" value="Hardcoded string" />
+
+ <!-- Overlays string/overlayable10 with the string "yes". -->
+ <item target="integer/config_int" value="42" />
+
+ <!-- @attr/max_lines and @id/hello_view should be rewritten to @attr/max_lines and
+ @id/hello_view in the target. -->
+ <item target="layout/hello_view" value="@layout/hello_view" />
+ <item target="attr/max_lines" value="@attr/max_lines" />
+ <item target="id/hello_view" value="@id/hello_view" />
+</overlay>
+
+
diff --git a/libs/androidfw/tests/data/overlayable/R.h b/libs/androidfw/tests/data/overlayable/R.h
index e46e264da318..35125a62ff4b 100644
--- a/libs/androidfw/tests/data/overlayable/R.h
+++ b/libs/androidfw/tests/data/overlayable/R.h
@@ -31,6 +31,43 @@ struct R {
overlayable2 = 0x7f010002,
overlayable3 = 0x7f010003,
overlayable4 = 0x7f010004,
+ overlayable5 = 0x7f010005,
+ overlayable6 = 0x7f010006,
+ overlayable7 = 0x7f010007,
+ overlayable8 = 0x7f010008,
+ overlayable9 = 0x7f010009,
+ overlayable10 = 0x7f01000a,
+ overlayable11 = 0x7f01000b,
+ };
+ };
+
+ struct attr {
+ enum : uint32_t {
+ max_lines = 0x7f020000,
+ };
+ };
+
+ struct boolean {
+ enum : uint32_t {
+ config_bool = 0x7f030000,
+ };
+ };
+
+ struct id {
+ enum : uint32_t {
+ hello_view = 0x7f040000,
+ };
+ };
+
+ struct integer {
+ enum : uint32_t {
+ config_integer = 0x7f050000,
+ };
+ };
+
+ struct layout {
+ enum : uint32_t {
+ hello_view = 0x7f060000,
};
};
};
diff --git a/libs/androidfw/tests/data/overlayable/build b/libs/androidfw/tests/data/overlayable/build
index 98fdc5101160..0aa97d639c30 100755
--- a/libs/androidfw/tests/data/overlayable/build
+++ b/libs/androidfw/tests/data/overlayable/build
@@ -17,6 +17,9 @@
set -e
+FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
+
aapt2 compile --dir res -o compiled.flata
-aapt2 link --manifest AndroidManifest.xml -o overlayable.apk compiled.flata
+aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o overlayable.apk compiled.flata \
+ --no-auto-version
rm compiled.flata
diff --git a/libs/androidfw/tests/data/overlayable/overlayable.apk b/libs/androidfw/tests/data/overlayable/overlayable.apk
index 047e6afde86b..9dc9c15f68a9 100644
--- a/libs/androidfw/tests/data/overlayable/overlayable.apk
+++ b/libs/androidfw/tests/data/overlayable/overlayable.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml b/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml
new file mode 100644
index 000000000000..268118a91bff
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/hello_view"
+ android:text="None"
+ app:max_lines="0"/> \ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
index fcdbe94466c1..b3e8f7d8e84b 100644
--- a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
+++ b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
@@ -15,27 +15,41 @@
-->
<resources>
-<overlayable name="OverlayableResources1" actor="overlay://theme">
- <!-- Any overlay can overlay the value of @string/overlayable1 -->
- <item type="string" name="overlayable1" />
+ <overlayable name="OverlayableResources1" actor="overlay://theme">
+ <!-- Any overlay on the product or system partition can overlay the value of
+ @string/overlayable2 -->
+ <policy type="product|system">
+ <item type="string" name="overlayable2" />
+ </policy>
- <!-- Any overlay on the product or system partition can overlay the value of
- @string/overlayable2 -->
- <policy type="product|system">
- <item type="string" name="overlayable2" />
- </policy>
+ <!-- Any overlay can overlay the value of @string/overlayable4 -->
+ <policy type="public">
+ <item type="string" name="overlayable1" />
+ <item type="string" name="overlayable4" />
+ </policy>
+ </overlayable>
- <!-- Any overlay can overlay the value of @string/overlayable4 -->
- <policy type="public">
- <item type="string" name="overlayable4" />
- </policy>
-</overlayable>
+ <overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable">
+ <!-- Any overlay on the vendor or product partition can overlay the value of
+ @string/overlayable3 -->
+ <policy type="vendor|product">
+ <item type="string" name="overlayable3" />
+ </policy>
+ </overlayable>
-<overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable">
- <!-- Any overlay on the vendor or product partition can overlay the value of
- @string/overlayable3 -->
- <policy type="vendor|product">
- <item type="string" name="overlayable3" />
- </policy>
-</overlayable>
+ <overlayable name="OverlayableResources3">
+ <policy type="public">
+ <item type="string" name="overlayable5" />
+ <item type="string" name="overlayable6" />
+ <item type="string" name="overlayable7" />
+ <item type="string" name="overlayable8" />
+ <item type="string" name="overlayable9" />
+ <item type="string" name="overlayable10" />
+ <item type="string" name="overlayable11" />
+ <item type="integer" name="config_int" />
+ <item type="id" name="hello_view" />
+ <item type="attr" name="max_lines" />
+ <item type="layout" name="hello_view" />
+ </policy>
+ </overlayable>
</resources> \ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlayable/res/values/public.xml b/libs/androidfw/tests/data/overlayable/res/values/public.xml
index 5676d7cc64c9..042a311b2b88 100644
--- a/libs/androidfw/tests/data/overlayable/res/values/public.xml
+++ b/libs/androidfw/tests/data/overlayable/res/values/public.xml
@@ -20,4 +20,21 @@
<public type="string" name="overlayable2" id="0x7f010002" />
<public type="string" name="overlayable3" id="0x7f010003" />
<public type="string" name="overlayable4" id="0x7f010004" />
+ <public type="string" name="overlayable5" id="0x7f010005" />
+ <public type="string" name="overlayable6" id="0x7f010006" />
+ <public type="string" name="overlayable7" id="0x7f010007" />
+ <public type="string" name="overlayable8" id="0x7f010008" />
+ <public type="string" name="overlayable9" id="0x7f010009" />
+ <public type="string" name="overlayable10" id="0x7f01000a" />
+ <public type="string" name="overlayable11" id="0x7f01000b" />
+
+ <public type="attr" name="max_lines" id="0x7f020000" />
+
+ <public type="bool" name="config_bool" id="0x7f030000" />
+
+ <public type="id" name="hello_view" id="0x7f040000" />
+
+ <public type="integer" name="config_int" id="0x7f050000" />
+
+ <public type="layout" name="hello_view" id="0x7f060000" />
</resources> \ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlayable/res/values/values.xml b/libs/androidfw/tests/data/overlayable/res/values/values.xml
index a86b31282bc9..235772d35fd0 100644
--- a/libs/androidfw/tests/data/overlayable/res/values/values.xml
+++ b/libs/androidfw/tests/data/overlayable/res/values/values.xml
@@ -20,4 +20,15 @@
<string name="overlayable2">Overlayable Two</string>
<string name="overlayable3">Overlayable Three</string>
<string name="overlayable4">Overlayable Four</string>
+ <string name="overlayable5">Overlayable Five</string>
+ <string name="overlayable6">Overlayable Six</string>
+ <string name="overlayable7">Overlayable Seven</string>
+ <string name="overlayable8">Overlayable Eight</string>
+ <string name="overlayable9">Overlayable Nine</string>
+ <string name="overlayable10">Overlayable Ten</string>
+ <string name="overlayable11">Overlayable Eleven</string>
+
+ <integer name="config_int">0</integer>
+ <bool name="config_bool">false</bool>
+ <attr name="max_lines" format="integer" />
</resources>
diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h
index becb38830fb3..c0160c0f78a9 100644
--- a/libs/androidfw/tests/data/system/R.h
+++ b/libs/androidfw/tests/data/system/R.h
@@ -40,6 +40,13 @@ struct R {
number = 0x01030000, // sv
};
};
+
+ struct string {
+ enum : uint32_t {
+ no = 0x01040009,
+ yes = 0x01040013,
+ };
+ };
};
} // namespace android
diff --git a/libs/androidfw/tests/data/system/res/values/public.xml b/libs/androidfw/tests/data/system/res/values/public.xml
new file mode 100644
index 000000000000..077874d0b0fe
--- /dev/null
+++ b/libs/androidfw/tests/data/system/res/values/public.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <public type="string" name="no" id="0x01040009" />
+ <public type="string" name="yes" id="0x01040013" />
+</resources> \ No newline at end of file
diff --git a/libs/androidfw/tests/data/system/res/values/values.xml b/libs/androidfw/tests/data/system/res/values/values.xml
new file mode 100644
index 000000000000..0629c1d13795
--- /dev/null
+++ b/libs/androidfw/tests/data/system/res/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <string name="yes">yes</string>
+ <string name="no">no</string>
+</resources> \ No newline at end of file
diff --git a/libs/androidfw/tests/data/system/system.apk b/libs/androidfw/tests/data/system/system.apk
index 9045d6c4de21..1f7e00733366 100644
--- a/libs/androidfw/tests/data/system/system.apk
+++ b/libs/androidfw/tests/data/system/system.apk
Binary files differ