From 6532483950eb0e75a5caf9645cae5ef4ac02fffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= Date: Thu, 11 Jun 2020 15:06:51 +0200 Subject: [PATCH 1/5] idmap2: remove the 'scan' command The 'scan' command has been replaced by 'create-multiple'. Remove the unused code. Also remove unused functions and #includes from FileUtils, and the obsolete JNI plumbing. Test: atest idmap2_tests OverlayDeviceTests OverlayHostTests Change-Id: Iae073c13ce64b5db48f22f7e723bc8c0c5fcd2c9 Merged-In: Iae073c13ce64b5db48f22f7e723bc8c0c5fcd2c9 --- cmds/idmap2/Android.bp | 1 - cmds/idmap2/idmap2/Commands.h | 1 - cmds/idmap2/idmap2/Main.cpp | 6 +- cmds/idmap2/idmap2/Scan.cpp | 257 ------------------ cmds/idmap2/include/idmap2/FileUtils.h | 14 - cmds/idmap2/libidmap2/FileUtils.cpp | 60 ---- cmds/idmap2/tests/FileUtilsTests.cpp | 57 ---- cmds/idmap2/tests/Idmap2BinaryTests.cpp | 125 --------- cmds/idmap2/valgrind.sh | 1 - .../android/content/res/AssetManager.java | 1 - core/jni/android_util_AssetManager.cpp | 86 ------ 11 files changed, 4 insertions(+), 605 deletions(-) delete mode 100644 cmds/idmap2/idmap2/Scan.cpp diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index 878cef94b6743..e21a6b288fb3c 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -181,7 +181,6 @@ cc_binary { "idmap2/Dump.cpp", "idmap2/Lookup.cpp", "idmap2/Main.cpp", - "idmap2/Scan.cpp", ], target: { android: { diff --git a/cmds/idmap2/idmap2/Commands.h b/cmds/idmap2/idmap2/Commands.h index 69eea8d262d27..4099671066a2c 100644 --- a/cmds/idmap2/idmap2/Commands.h +++ b/cmds/idmap2/idmap2/Commands.h @@ -26,6 +26,5 @@ android::idmap2::Result Create(const std::vector CreateMultiple(const std::vector& args); android::idmap2::Result Dump(const std::vector& args); android::idmap2::Result Lookup(const std::vector& args); -android::idmap2::Result Scan(const std::vector& args); #endif // IDMAP2_IDMAP2_COMMANDS_H_ diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp index fb093f0f22a42..aa6d0e76698f5 100644 --- a/cmds/idmap2/idmap2/Main.cpp +++ b/cmds/idmap2/idmap2/Main.cpp @@ -53,8 +53,10 @@ void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) { int main(int argc, char** argv) { SYSTRACE << "main"; const NameToFunctionMap commands = { - {"create", Create}, {"create-multiple", CreateMultiple}, {"dump", Dump}, {"lookup", Lookup}, - {"scan", Scan}, + {"create", Create}, + {"create-multiple", CreateMultiple}, + {"dump", Dump}, + {"lookup", Lookup}, }; if (argc <= 1) { PrintUsage(commands, std::cerr); diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp deleted file mode 100644 index 36250450cc74e..0000000000000 --- a/cmds/idmap2/idmap2/Scan.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/* - * 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. - */ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "Commands.h" -#include "android-base/properties.h" -#include "idmap2/CommandLineOptions.h" -#include "idmap2/CommandUtils.h" -#include "idmap2/FileUtils.h" -#include "idmap2/Idmap.h" -#include "idmap2/Policies.h" -#include "idmap2/PolicyUtils.h" -#include "idmap2/ResourceUtils.h" -#include "idmap2/Result.h" -#include "idmap2/SysTrace.h" -#include "idmap2/XmlParser.h" - -using android::idmap2::CommandLineOptions; -using android::idmap2::Error; -using android::idmap2::Idmap; -using android::idmap2::Result; -using android::idmap2::Unit; -using android::idmap2::policy::kPolicyOdm; -using android::idmap2::policy::kPolicyOem; -using android::idmap2::policy::kPolicyProduct; -using android::idmap2::policy::kPolicyPublic; -using android::idmap2::policy::kPolicySystem; -using android::idmap2::policy::kPolicyVendor; -using android::idmap2::utils::ExtractOverlayManifestInfo; -using android::idmap2::utils::FindFiles; -using android::idmap2::utils::OverlayManifestInfo; -using android::idmap2::utils::PoliciesToBitmaskResult; - -using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask; - -namespace { - -struct InputOverlay { - bool operator<(InputOverlay const& rhs) const { - return priority < rhs.priority || (priority == rhs.priority && apk_path < rhs.apk_path); - } - - std::string apk_path; // NOLINT(misc-non-private-member-variables-in-classes) - std::string idmap_path; // NOLINT(misc-non-private-member-variables-in-classes) - int priority; // NOLINT(misc-non-private-member-variables-in-classes) - std::vector policies; // NOLINT(misc-non-private-member-variables-in-classes) - bool ignore_overlayable; // NOLINT(misc-non-private-member-variables-in-classes) -}; - -bool VendorIsQOrLater() { - constexpr int kQSdkVersion = 29; - constexpr int kBase = 10; - std::string version_prop = android::base::GetProperty("ro.vndk.version", "29"); - int version = strtol(version_prop.data(), nullptr, kBase); - - // If the string cannot be parsed, it is a development sdk codename. - return version >= kQSdkVersion || version == 0; -} - -Result>> FindApkFiles(const std::vector& dirs, - bool recursive) { - SYSTRACE << "FindApkFiles " << dirs << " " << recursive; - const auto predicate = [](unsigned char type, const std::string& path) -> bool { - static constexpr size_t kExtLen = 4; // strlen(".apk") - return type == DT_REG && path.size() > kExtLen && - path.compare(path.size() - kExtLen, kExtLen, ".apk") == 0; - }; - // pass apk paths through a set to filter out duplicates - std::set paths; - for (const auto& dir : dirs) { - const auto apk_paths = FindFiles(dir, recursive, predicate); - if (!apk_paths) { - return Error("failed to open directory %s", dir.c_str()); - } - paths.insert(apk_paths->cbegin(), apk_paths->cend()); - } - return std::make_unique>(paths.cbegin(), paths.cend()); -} - -std::vector PoliciesForPath(const std::string& apk_path) { - // clang-format off - static const std::vector> values = { - {"/odm/", kPolicyOdm}, - {"/oem/", kPolicyOem}, - {"/product/", kPolicyProduct}, - {"/system/", kPolicySystem}, - {"/system_ext/", kPolicySystem}, - {"/vendor/", kPolicyVendor}, - }; - // clang-format on - - std::vector fulfilled_policies = {kPolicyPublic}; - for (auto const& pair : values) { - if (apk_path.compare(0, pair.first.size(), pair.first) == 0) { - fulfilled_policies.emplace_back(pair.second); - break; - } - } - - return fulfilled_policies; -} - -} // namespace - -Result Scan(const std::vector& args) { - SYSTRACE << "Scan " << args; - std::vector input_directories; - std::string target_package_name; - std::string target_apk_path; - std::string output_directory; - std::vector override_policies; - bool recursive = false; - - const CommandLineOptions opts = - CommandLineOptions("idmap2 scan") - .MandatoryOption("--input-directory", "directory containing overlay apks to scan", - &input_directories) - .OptionalFlag("--recursive", "also scan subfolders of overlay-directory", &recursive) - .MandatoryOption("--target-package-name", "package name of target package", - &target_package_name) - .MandatoryOption("--target-apk-path", "path to target apk", &target_apk_path) - .MandatoryOption("--output-directory", - "directory in which to write artifacts (idmap files and overlays.list)", - &output_directory) - .OptionalOption( - "--override-policy", - "input: an overlayable policy this overlay fulfills " - "(if none or supplied, the overlays will not have their policies overriden", - &override_policies); - const auto opts_ok = opts.Parse(args); - if (!opts_ok) { - return opts_ok.GetError(); - } - - const auto apk_paths = FindApkFiles(input_directories, recursive); - if (!apk_paths) { - return Error(apk_paths.GetError(), "failed to find apk files"); - } - - std::vector interesting_apks; - for (const std::string& path : **apk_paths) { - Result overlay_info = - ExtractOverlayManifestInfo(path, /* assert_overlay */ false); - if (!overlay_info) { - return overlay_info.GetError(); - } - - if (!overlay_info->is_static) { - continue; - } - - if (overlay_info->target_package.empty() || - overlay_info->target_package != target_package_name) { - continue; - } - - if (overlay_info->priority < 0) { - continue; - } - - // Note that conditional property enablement/exclusion only applies if - // the attribute is present. In its absence, all overlays are presumed enabled. - if (!overlay_info->requiredSystemPropertyName.empty() && - !overlay_info->requiredSystemPropertyValue.empty()) { - // if property set & equal to value, then include overlay - otherwise skip - if (android::base::GetProperty(overlay_info->requiredSystemPropertyName, "") != - overlay_info->requiredSystemPropertyValue) { - continue; - } - } - - std::vector fulfilled_policies; - if (!override_policies.empty()) { - fulfilled_policies = override_policies; - } else { - fulfilled_policies = PoliciesForPath(path); - } - - bool ignore_overlayable = false; - if (std::find(fulfilled_policies.begin(), fulfilled_policies.end(), kPolicyVendor) != - fulfilled_policies.end() && - !VendorIsQOrLater()) { - // If the overlay is on a pre-Q vendor partition, do not enforce overlayable - // restrictions on this overlay because the pre-Q platform has no understanding of - // overlayable. - ignore_overlayable = true; - } - - std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, path); - - // Sort the static overlays in ascending priority order - InputOverlay input{path, idmap_path, overlay_info->priority, fulfilled_policies, - ignore_overlayable}; - interesting_apks.insert( - std::lower_bound(interesting_apks.begin(), interesting_apks.end(), input), input); - } - - std::stringstream stream; - for (const auto& overlay : interesting_apks) { - const auto policy_bitmask = PoliciesToBitmaskResult(overlay.policies); - if (!policy_bitmask) { - LOG(WARNING) << "failed to create idmap for overlay apk path \"" << overlay.apk_path - << "\": " << policy_bitmask.GetErrorMessage(); - continue; - } - - if (!Verify(overlay.idmap_path, target_apk_path, overlay.apk_path, *policy_bitmask, - !overlay.ignore_overlayable)) { - std::vector create_args = {"--target-apk-path", target_apk_path, - "--overlay-apk-path", overlay.apk_path, - "--idmap-path", overlay.idmap_path}; - if (overlay.ignore_overlayable) { - create_args.emplace_back("--ignore-overlayable"); - } - - for (const std::string& policy : overlay.policies) { - create_args.emplace_back("--policy"); - create_args.emplace_back(policy); - } - - const auto create_ok = Create(create_args); - if (!create_ok) { - LOG(WARNING) << "failed to create idmap for overlay apk path \"" << overlay.apk_path - << "\": " << create_ok.GetError().GetMessage(); - continue; - } - } - - stream << overlay.idmap_path << std::endl; - } - - std::cout << stream.str(); - - return Unit{}; -} diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h index 3f03236d5e1ab..c4e0e1fd8ef09 100644 --- a/cmds/idmap2/include/idmap2/FileUtils.h +++ b/cmds/idmap2/include/idmap2/FileUtils.h @@ -17,27 +17,13 @@ #ifndef IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_ #define IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_ -#include - -#include -#include #include -#include namespace android::idmap2::utils { constexpr const char* kIdmapCacheDir = "/data/resource-cache"; constexpr const mode_t kIdmapFilePermissionMask = 0133; // u=rw,g=r,o=r -typedef std::function - FindFilesPredicate; -std::unique_ptr> FindFiles(const std::string& root, bool recurse, - const FindFilesPredicate& predicate); - -std::unique_ptr ReadFile(int fd); - -std::unique_ptr ReadFile(const std::string& path); - bool UidHasWriteAccessToPath(uid_t uid, const std::string& path); } // namespace android::idmap2::utils diff --git a/cmds/idmap2/libidmap2/FileUtils.cpp b/cmds/idmap2/libidmap2/FileUtils.cpp index 3e8e32989a094..3af1f70ebe39b 100644 --- a/cmds/idmap2/libidmap2/FileUtils.cpp +++ b/cmds/idmap2/libidmap2/FileUtils.cpp @@ -16,19 +16,7 @@ #include "idmap2/FileUtils.h" -#include -#include -#include - -#include -#include -#include -#include -#include -#include #include -#include -#include #include "android-base/file.h" #include "android-base/macros.h" @@ -37,54 +25,6 @@ namespace android::idmap2::utils { -std::unique_ptr> FindFiles(const std::string& root, bool recurse, - const FindFilesPredicate& predicate) { - DIR* dir = opendir(root.c_str()); - if (dir == nullptr) { - return nullptr; - } - std::unique_ptr> vector(new std::vector()); - struct dirent* dirent; - while ((dirent = readdir(dir)) != nullptr) { - const std::string path = root + "/" + dirent->d_name; - if (predicate(dirent->d_type, path)) { - vector->push_back(path); - } - if (recurse && dirent->d_type == DT_DIR && strcmp(dirent->d_name, ".") != 0 && - strcmp(dirent->d_name, "..") != 0) { - auto sub_vector = FindFiles(path, recurse, predicate); - if (!sub_vector) { - closedir(dir); - return nullptr; - } - vector->insert(vector->end(), sub_vector->begin(), sub_vector->end()); - } - } - closedir(dir); - - return vector; -} - -std::unique_ptr ReadFile(const std::string& path) { - std::unique_ptr str(new std::string()); - std::ifstream fin(path); - str->append({std::istreambuf_iterator(fin), std::istreambuf_iterator()}); - fin.close(); - return str; -} - -std::unique_ptr ReadFile(int fd) { - static constexpr const size_t kBufSize = 1024; - - std::unique_ptr str(new std::string()); - char buf[kBufSize]; - ssize_t r; - while ((r = read(fd, buf, sizeof(buf))) > 0) { - str->append(buf, r); - } - return r == 0 ? std::move(str) : nullptr; -} - #ifdef __ANDROID__ bool UidHasWriteAccessToPath(uid_t uid, const std::string& path) { // resolve symlinks and relative paths; the directories must exist diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp index 8af4037be954f..5750ca1f49c5b 100644 --- a/cmds/idmap2/tests/FileUtilsTests.cpp +++ b/cmds/idmap2/tests/FileUtilsTests.cpp @@ -14,73 +14,16 @@ * limitations under the License. */ -#include -#include - -#include #include #include "TestHelpers.h" -#include "android-base/macros.h" #include "android-base/stringprintf.h" -#include "gmock/gmock.h" #include "gtest/gtest.h" #include "idmap2/FileUtils.h" #include "private/android_filesystem_config.h" -using ::testing::NotNull; - namespace android::idmap2::utils { -TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) { - const auto& root = GetTestDataPath(); - auto v = utils::FindFiles(root, false, - [](unsigned char type ATTRIBUTE_UNUSED, - const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; }); - ASSERT_THAT(v, NotNull()); - ASSERT_EQ(v->size(), 7U); - ASSERT_EQ(std::set(v->begin(), v->end()), std::set({ - root + "/.", - root + "/..", - root + "/overlay", - root + "/target", - root + "/signature-overlay", - root + "/system-overlay", - root + "/system-overlay-invalid", - })); -} - -TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) { - const auto& root = GetTestDataPath(); - auto v = utils::FindFiles(root, true, [](unsigned char type, const std::string& path) -> bool { - return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0; - }); - ASSERT_THAT(v, NotNull()); - ASSERT_EQ(v->size(), 11U); - ASSERT_EQ(std::set(v->begin(), v->end()), - std::set( - {root + "/target/target.apk", root + "/target/target-no-overlayable.apk", - root + "/overlay/overlay.apk", root + "/overlay/overlay-no-name.apk", - root + "/overlay/overlay-no-name-static.apk", root + "/overlay/overlay-shared.apk", - root + "/overlay/overlay-static-1.apk", root + "/overlay/overlay-static-2.apk", - root + "/signature-overlay/signature-overlay.apk", - root + "/system-overlay/system-overlay.apk", - root + "/system-overlay-invalid/system-overlay-invalid.apk"})); -} - -TEST(FileUtilsTests, ReadFile) { - int pipefd[2]; - ASSERT_EQ(pipe2(pipefd, O_CLOEXEC), 0); - - ASSERT_EQ(write(pipefd[1], "foobar", 6), 6); - close(pipefd[1]); - - auto data = ReadFile(pipefd[0]); - ASSERT_THAT(data, NotNull()); - ASSERT_EQ(*data, "foobar"); - close(pipefd[0]); -} - #ifdef __ANDROID__ TEST(FileUtilsTests, UidHasWriteAccessToPath) { constexpr const char* tmp_path = "/data/local/tmp/test@idmap"; diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp index eba102da07631..e7e9e4cf5091c 100644 --- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp +++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp @@ -159,131 +159,6 @@ TEST_F(Idmap2BinaryTests, Dump) { unlink(GetIdmapPath().c_str()); } -TEST_F(Idmap2BinaryTests, Scan) { - SKIP_TEST_IF_CANT_EXEC_IDMAP2; - - const std::string overlay_static_no_name_apk_path = - GetTestDataPath() + "/overlay/overlay-no-name-static.apk"; - const std::string overlay_static_1_apk_path = GetTestDataPath() + "/overlay/overlay-static-1.apk"; - const std::string overlay_static_2_apk_path = GetTestDataPath() + "/overlay/overlay-static-2.apk"; - const std::string idmap_static_no_name_path = - Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_no_name_apk_path); - const std::string idmap_static_1_path = - Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_1_apk_path); - const std::string idmap_static_2_path = - Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_2_apk_path); - - // single input directory, recursive - // clang-format off - auto result = ExecuteBinary({"idmap2", - "scan", - "--input-directory", GetTestDataPath(), - "--recursive", - "--target-package-name", "test.target", - "--target-apk-path", GetTargetApkPath(), - "--output-directory", GetTempDirPath(), - "--override-policy", "public"}); - // clang-format on - ASSERT_THAT(result, NotNull()); - ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; - std::stringstream expected; - expected << idmap_static_no_name_path << std::endl; - expected << idmap_static_1_path << std::endl; - expected << idmap_static_2_path << std::endl; - ASSERT_EQ(result->stdout, expected.str()); - - auto idmap_static_no_name_raw_string = utils::ReadFile(idmap_static_no_name_path); - auto idmap_static_no_name_raw_stream = std::istringstream(*idmap_static_no_name_raw_string); - auto idmap_static_no_name = Idmap::FromBinaryStream(idmap_static_no_name_raw_stream); - ASSERT_TRUE(idmap_static_no_name); - ASSERT_IDMAP(**idmap_static_no_name, GetTargetApkPath(), overlay_static_no_name_apk_path); - - auto idmap_static_1_raw_string = utils::ReadFile(idmap_static_1_path); - auto idmap_static_1_raw_stream = std::istringstream(*idmap_static_1_raw_string); - auto idmap_static_1 = Idmap::FromBinaryStream(idmap_static_1_raw_stream); - ASSERT_TRUE(idmap_static_1); - ASSERT_IDMAP(**idmap_static_1, GetTargetApkPath(), overlay_static_1_apk_path); - - auto idmap_static_2_raw_string = utils::ReadFile(idmap_static_2_path); - auto idmap_static_2_raw_stream = std::istringstream(*idmap_static_2_raw_string); - auto idmap_static_2 = Idmap::FromBinaryStream(idmap_static_2_raw_stream); - ASSERT_TRUE(idmap_static_2); - ASSERT_IDMAP(**idmap_static_2, GetTargetApkPath(), overlay_static_2_apk_path); - - unlink(idmap_static_no_name_path.c_str()); - unlink(idmap_static_2_path.c_str()); - unlink(idmap_static_1_path.c_str()); - - // multiple input directories, non-recursive - // clang-format off - result = ExecuteBinary({"idmap2", - "scan", - "--input-directory", GetTestDataPath() + "/target", - "--input-directory", GetTestDataPath() + "/overlay", - "--target-package-name", "test.target", - "--target-apk-path", GetTargetApkPath(), - "--output-directory", GetTempDirPath(), - "--override-policy", "public"}); - // clang-format on - ASSERT_THAT(result, NotNull()); - ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; - ASSERT_EQ(result->stdout, expected.str()); - unlink(idmap_static_no_name_path.c_str()); - unlink(idmap_static_2_path.c_str()); - unlink(idmap_static_1_path.c_str()); - - // the same input directory given twice, but no duplicate entries - // clang-format off - result = ExecuteBinary({"idmap2", - "scan", - "--input-directory", GetTestDataPath(), - "--input-directory", GetTestDataPath(), - "--recursive", - "--target-package-name", "test.target", - "--target-apk-path", GetTargetApkPath(), - "--output-directory", GetTempDirPath(), - "--override-policy", "public"}); - // clang-format on - ASSERT_THAT(result, NotNull()); - ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; - ASSERT_EQ(result->stdout, expected.str()); - unlink(idmap_static_no_name_path.c_str()); - unlink(idmap_static_2_path.c_str()); - unlink(idmap_static_1_path.c_str()); - - // no APKs in input-directory: ok, but no output - // clang-format off - result = ExecuteBinary({"idmap2", - "scan", - "--input-directory", GetTempDirPath(), - "--target-package-name", "test.target", - "--target-apk-path", GetTargetApkPath(), - "--output-directory", GetTempDirPath(), - "--override-policy", "public"}); - // clang-format on - ASSERT_THAT(result, NotNull()); - ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; - ASSERT_EQ(result->stdout, ""); - - // the signature idmap failing to generate should not cause scanning to fail - // clang-format off - result = ExecuteBinary({"idmap2", - "scan", - "--input-directory", GetTestDataPath(), - "--recursive", - "--target-package-name", "test.target", - "--target-apk-path", GetTargetApkPath(), - "--output-directory", GetTempDirPath(), - "--override-policy", "public"}); - // clang-format on - ASSERT_THAT(result, NotNull()); - ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; - ASSERT_EQ(result->stdout, expected.str()); - unlink(idmap_static_no_name_path.c_str()); - unlink(idmap_static_2_path.c_str()); - unlink(idmap_static_1_path.c_str()); -} - TEST_F(Idmap2BinaryTests, Lookup) { SKIP_TEST_IF_CANT_EXEC_IDMAP2; diff --git a/cmds/idmap2/valgrind.sh b/cmds/idmap2/valgrind.sh index b4ebab0c7ffec..0fcb4a1497998 100755 --- a/cmds/idmap2/valgrind.sh +++ b/cmds/idmap2/valgrind.sh @@ -53,7 +53,6 @@ valgrind="valgrind --error-exitcode=1 -q --track-origins=yes --leak-check=full" _eval "idmap2 create" "$valgrind idmap2 create --policy public --target-apk-path $target_path --overlay-apk-path $overlay_path --idmap-path $idmap_path" _eval "idmap2 dump" "$valgrind idmap2 dump --idmap-path $idmap_path" _eval "idmap2 lookup" "$valgrind idmap2 lookup --idmap-path $idmap_path --config '' --resid test.target:string/str1" -_eval "idmap2 scan" "$valgrind idmap2 scan --input-directory ${prefix}/tests/data/overlay --recursive --target-package-name test.target --target-apk-path $target_path --output-directory /tmp --override-policy public" _eval "idmap2 verify" "$valgrind idmap2 verify --idmap-path $idmap_path" _eval "idmap2_tests" "$valgrind $ANDROID_HOST_OUT/nativetest64/idmap2_tests/idmap2_tests" exit $errors diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 36986dea02a13..13433dc7979f4 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -1574,7 +1574,6 @@ public final class AssetManager implements AutoCloseable { private static native long nativeAssetGetLength(long assetPtr); private static native long nativeAssetGetRemainingLength(long assetPtr); - private static native String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid(); private static native @Nullable Map nativeGetOverlayableMap(long ptr, @NonNull String packageName); private static native @Nullable String nativeGetOverlayablesToString(long ptr, diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index e6881b3995a89..efca33aaf5206 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -39,7 +39,6 @@ #include "androidfw/AssetManager2.h" #include "androidfw/AttributeResolution.h" #include "androidfw/MutexGuard.h" -#include "androidfw/PosixUtils.h" #include "androidfw/ResourceTypes.h" #include "androidfw/ResourceUtils.h" @@ -58,7 +57,6 @@ extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap); extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap); using ::android::base::StringPrintf; -using ::android::util::ExecuteBinary; namespace android { @@ -114,88 +112,6 @@ constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) return cookie > 0 ? static_cast(cookie - 1) : kInvalidCookie; } -static jobjectArray NativeCreateIdmapsForStaticOverlaysTargetingAndroid(JNIEnv* env, - jclass /*clazz*/) { - // --input-directory can be given multiple times, but idmap2 expects the directory to exist - std::vector input_dirs; - struct stat st; - if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) { - input_dirs.push_back(AssetManager::VENDOR_OVERLAY_DIR); - } - - if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { - input_dirs.push_back(AssetManager::PRODUCT_OVERLAY_DIR); - } - - if (stat(AssetManager::SYSTEM_EXT_OVERLAY_DIR, &st) == 0) { - input_dirs.push_back(AssetManager::SYSTEM_EXT_OVERLAY_DIR); - } - - if (stat(AssetManager::ODM_OVERLAY_DIR, &st) == 0) { - input_dirs.push_back(AssetManager::ODM_OVERLAY_DIR); - } - - if (stat(AssetManager::OEM_OVERLAY_DIR, &st) == 0) { - input_dirs.push_back(AssetManager::OEM_OVERLAY_DIR); - } - - if (input_dirs.empty()) { - LOG(WARNING) << "no directories for idmap2 to scan"; - return env->NewObjectArray(0, g_stringClass, nullptr); - } - - if (access("/system/bin/idmap2", X_OK) == -1) { - PLOG(WARNING) << "unable to execute idmap2"; - return nullptr; - } - - std::vector argv{"/system/bin/idmap2", - "scan", - "--recursive", - "--target-package-name", "android", - "--target-apk-path", "/system/framework/framework-res.apk", - "--output-directory", "/data/resource-cache"}; - - for (const auto& dir : input_dirs) { - argv.push_back("--input-directory"); - argv.push_back(dir); - } - - const auto result = ExecuteBinary(argv); - - if (!result) { - LOG(ERROR) << "failed to execute idmap2"; - return nullptr; - } - - if (result->status != 0) { - LOG(ERROR) << "idmap2: " << result->stderr; - return nullptr; - } - - std::vector idmap_paths; - std::istringstream input(result->stdout); - std::string path; - while (std::getline(input, path)) { - idmap_paths.push_back(path); - } - - jobjectArray array = env->NewObjectArray(idmap_paths.size(), g_stringClass, nullptr); - if (array == nullptr) { - return nullptr; - } - for (size_t i = 0; i < idmap_paths.size(); i++) { - const std::string path = idmap_paths[i]; - jstring java_string = env->NewStringUTF(path.c_str()); - if (env->ExceptionCheck()) { - return nullptr; - } - env->SetObjectArrayElement(array, i, java_string); - env->DeleteLocalRef(java_string); - } - return array; -} - static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref, uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) { env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType); @@ -1563,8 +1479,6 @@ static const JNINativeMethod gAssetManagerMethods[] = { {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength}, // System/idmap related methods. - {"nativeCreateIdmapsForStaticOverlaysTargetingAndroid", "()[Ljava/lang/String;", - (void*)NativeCreateIdmapsForStaticOverlaysTargetingAndroid}, {"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;", (void*)NativeGetOverlayableMap}, {"nativeGetOverlayablesToString", "(JLjava/lang/String;)Ljava/lang/String;", From ec7e7f5622e3444a3003db20ddfd8f5745971fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= Date: Tue, 16 Jun 2020 14:22:02 +0200 Subject: [PATCH 2/5] idmap2: remove call to obsolete 'idmap2 verify' from valgrind.sh The 'verify' command was removed as part of a707013b78c. Update valgrind.sh accordingly. Test: frameworks/base/cmds/idmap2/valgrind.sh Change-Id: I571cb46bb2ca09a244dc7b609a8bb0ffe01a6e2a Merged-In: I571cb46bb2ca09a244dc7b609a8bb0ffe01a6e2a --- cmds/idmap2/valgrind.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/cmds/idmap2/valgrind.sh b/cmds/idmap2/valgrind.sh index 0fcb4a1497998..84daeecdb21ed 100755 --- a/cmds/idmap2/valgrind.sh +++ b/cmds/idmap2/valgrind.sh @@ -53,6 +53,5 @@ valgrind="valgrind --error-exitcode=1 -q --track-origins=yes --leak-check=full" _eval "idmap2 create" "$valgrind idmap2 create --policy public --target-apk-path $target_path --overlay-apk-path $overlay_path --idmap-path $idmap_path" _eval "idmap2 dump" "$valgrind idmap2 dump --idmap-path $idmap_path" _eval "idmap2 lookup" "$valgrind idmap2 lookup --idmap-path $idmap_path --config '' --resid test.target:string/str1" -_eval "idmap2 verify" "$valgrind idmap2 verify --idmap-path $idmap_path" _eval "idmap2_tests" "$valgrind $ANDROID_HOST_OUT/nativetest64/idmap2_tests/idmap2_tests" exit $errors From 80094e39f90801c44cd80ab0f98df505828ea1f3 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Mon, 16 Nov 2020 23:08:18 +0000 Subject: [PATCH 3/5] Revert^2 "libandroidfw hardening for IncFs" 55ef6167a2c235bd88c7216238b2001b46795b79 Change-Id: I02d4890d181655dfd0a14c188468db512559d27b Merged-In: I02d4890d181655dfd0a14c188468db512559d27b --- cmds/idmap2/idmap2/Lookup.cpp | 71 +- cmds/idmap2/include/idmap2/ResourceMapping.h | 3 +- cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp | 5 +- cmds/idmap2/libidmap2/ResourceMapping.cpp | 54 +- cmds/idmap2/libidmap2/ResourceUtils.cpp | 16 +- cmds/idmap2/libidmap2/XmlParser.cpp | 11 +- core/jni/android_content_res_ApkAssets.cpp | 14 +- core/jni/android_util_AssetManager.cpp | 417 +++++------ core/jni/android_util_AssetManager_private.h | 54 ++ core/jni/android_util_StringBlock.cpp | 99 +-- libs/androidfw/Android.bp | 10 +- libs/androidfw/ApkAssets.cpp | 90 ++- libs/androidfw/Asset.cpp | 174 ++--- libs/androidfw/AssetManager.cpp | 16 +- libs/androidfw/AssetManager2.cpp | 667 ++++++++++-------- libs/androidfw/AttributeResolution.cpp | 457 ++++++------ libs/androidfw/ChunkIterator.cpp | 27 +- libs/androidfw/Idmap.cpp | 12 +- libs/androidfw/LoadedArsc.cpp | 341 +++++---- libs/androidfw/ResourceTypes.cpp | 490 ++++++++----- libs/androidfw/ResourceUtils.cpp | 75 +- libs/androidfw/StreamingZipInflater.cpp | 6 +- libs/androidfw/ZipFileRO.cpp | 23 + libs/androidfw/ZipUtils.cpp | 21 +- .../resourcefile_fuzzer.cpp | 5 +- libs/androidfw/include/androidfw/Asset.h | 106 ++- .../include/androidfw/AssetManager2.h | 258 ++++--- .../include/androidfw/AttributeResolution.h | 24 +- libs/androidfw/include/androidfw/Chunk.h | 23 +- libs/androidfw/include/androidfw/Errors.h | 47 ++ libs/androidfw/include/androidfw/Idmap.h | 4 +- libs/androidfw/include/androidfw/LoadedArsc.h | 37 +- .../include/androidfw/ResourceTypes.h | 68 +- .../include/androidfw/ResourceUtils.h | 9 +- .../include/androidfw/StreamingZipInflater.h | 6 +- libs/androidfw/include/androidfw/Util.h | 8 +- libs/androidfw/include/androidfw/ZipFileRO.h | 27 +- libs/androidfw/include/androidfw/ZipUtils.h | 6 +- libs/androidfw/tests/AssetManager2_bench.cpp | 10 +- libs/androidfw/tests/AssetManager2_test.cpp | 459 ++++++------ .../tests/AttributeResolution_bench.cpp | 19 +- .../tests/AttributeResolution_test.cpp | 18 +- libs/androidfw/tests/BenchmarkHelpers.cpp | 10 +- libs/androidfw/tests/CommonHelpers.cpp | 5 +- libs/androidfw/tests/Idmap_test.cpp | 149 ++-- libs/androidfw/tests/LoadedArsc_test.cpp | 63 +- libs/androidfw/tests/ResTable_test.cpp | 26 +- libs/androidfw/tests/TestHelpers.cpp | 12 +- libs/androidfw/tests/Theme_bench.cpp | 5 +- libs/androidfw/tests/Theme_test.cpp | 270 ++++--- tools/aapt/Resource.cpp | 2 +- tools/aapt/ResourceTable.cpp | 4 +- tools/aapt/StringPool.cpp | 8 +- tools/aapt2/Debug.cpp | 8 +- tools/aapt2/ResourceUtils.cpp | 6 +- tools/aapt2/StringPool_test.cpp | 40 +- tools/aapt2/cmd/Compile_test.cpp | 1 - tools/aapt2/cmd/Link.cpp | 24 +- .../format/binary/TableFlattener_test.cpp | 30 +- tools/aapt2/process/SymbolTable.cpp | 51 +- tools/aapt2/util/Util.cpp | 12 +- tools/split-select/Main.cpp | 18 +- 62 files changed, 2640 insertions(+), 2391 deletions(-) create mode 100644 core/jni/android_util_AssetManager_private.h create mode 100644 libs/androidfw/include/androidfw/Errors.h diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp index c44170928992e..8323d0ba24158 100644 --- a/cmds/idmap2/idmap2/Lookup.cpp +++ b/cmds/idmap2/idmap2/Lookup.cpp @@ -45,11 +45,8 @@ using android::ApkAssets; using android::ApkAssetsCookie; using android::AssetManager2; using android::ConfigDescription; -using android::is_valid_resid; -using android::kInvalidCookie; using android::Res_value; using android::ResStringPool; -using android::ResTable_config; using android::StringPiece16; using android::base::StringPrintf; using android::idmap2::CommandLineOptions; @@ -59,7 +56,6 @@ using android::idmap2::ResourceId; using android::idmap2::Result; using android::idmap2::Unit; using android::idmap2::utils::ExtractOverlayManifestInfo; -using android::util::Utf16ToUtf8; namespace { @@ -69,25 +65,23 @@ Result WARN_UNUSED ParseResReference(const AssetManager2& am, const // first, try to parse as a hex number char* endptr = nullptr; - ResourceId resid; - resid = strtol(res.c_str(), &endptr, kBaseHex); + const ResourceId parsed_resid = strtol(res.c_str(), &endptr, kBaseHex); if (*endptr == '\0') { - return resid; + return parsed_resid; } // next, try to parse as a package:type/name string - resid = am.GetResourceId(res, "", fallback_package); - if (is_valid_resid(resid)) { - return resid; + if (auto resid = am.GetResourceId(res, "", fallback_package)) { + return *resid; } // end of the road: res could not be parsed return Error("failed to obtain resource id for %s", res.c_str()); } -void PrintValue(AssetManager2* const am, const Res_value& value, const ApkAssetsCookie& cookie, +void PrintValue(AssetManager2* const am, const AssetManager2::SelectedValue& value, std::string* const out) { - switch (value.dataType) { + switch (value.type) { case Res_value::TYPE_INT_DEC: out->append(StringPrintf("%d", value.data)); break; @@ -98,30 +92,21 @@ void PrintValue(AssetManager2* const am, const Res_value& value, const ApkAssets out->append(value.data != 0 ? "true" : "false"); break; case Res_value::TYPE_STRING: { - const ResStringPool* pool = am->GetStringPoolForCookie(cookie); + const ResStringPool* pool = am->GetStringPoolForCookie(value.cookie); out->append("\""); - size_t len; - if (pool->isUTF8()) { - const char* str = pool->string8At(value.data, &len); - out->append(str, len); - } else { - const char16_t* str16 = pool->stringAt(value.data, &len); - out->append(Utf16ToUtf8(StringPiece16(str16, len))); + if (auto str = pool->string8ObjectAt(value.data)) { + out->append(*str); } - out->append("\""); } break; default: - out->append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data)); + out->append(StringPrintf("dataType=0x%02x data=0x%08x", value.type, value.data)); break; } } Result WARN_UNUSED GetValue(AssetManager2* const am, ResourceId resid) { - Res_value value; - ResTable_config config; - uint32_t flags; - ApkAssetsCookie cookie = am->GetResource(resid, true, 0, &value, &config, &flags); - if (cookie == kInvalidCookie) { + auto value = am->GetResource(resid); + if (!value.has_value()) { return Error("no resource 0x%08x in asset manager", resid); } @@ -129,41 +114,35 @@ Result WARN_UNUSED GetValue(AssetManager2* const am, ResourceId res // TODO(martenkongstad): use optional parameter GetResource(..., std::string* // stacktrace = NULL) instead - out.append(StringPrintf("cookie=%d ", cookie)); + out.append(StringPrintf("cookie=%d ", value->cookie)); out.append("config='"); - out.append(config.toString().c_str()); + out.append(value->config.toString().c_str()); out.append("' value="); - if (value.dataType == Res_value::TYPE_REFERENCE) { - const android::ResolvedBag* bag = am->GetBag(static_cast(value.data)); - if (bag == nullptr) { - out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data)); + if (value->type == Res_value::TYPE_REFERENCE) { + auto bag_result = am->GetBag(static_cast(value->data)); + if (!bag_result.has_value()) { + out.append(StringPrintf("dataType=0x%02x data=0x%08x", value->type, value->data)); return out; } + out.append("["); - Res_value bag_val; - ResTable_config selected_config; - uint32_t flags; - uint32_t ref; - ApkAssetsCookie bag_cookie; + const android::ResolvedBag* bag = bag_result.value(); for (size_t i = 0; i < bag->entry_count; ++i) { - const android::ResolvedBag::Entry& entry = bag->entries[i]; - bag_val = entry.value; - bag_cookie = am->ResolveReference(entry.cookie, &bag_val, &selected_config, &flags, &ref); - if (bag_cookie == kInvalidCookie) { - out.append( - StringPrintf("Error: dataType=0x%02x data=0x%08x", bag_val.dataType, bag_val.data)); + AssetManager2::SelectedValue entry(bag, bag->entries[i]); + if (am->ResolveReference(entry).has_value()) { + out.append(StringPrintf("Error: dataType=0x%02x data=0x%08x", entry.type, entry.data)); continue; } - PrintValue(am, bag_val, bag_cookie, &out); + PrintValue(am, entry, &out); if (i != bag->entry_count - 1) { out.append(", "); } } out.append("]"); } else { - PrintValue(am, value, cookie, &out); + PrintValue(am, *value, &out); } return out; diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h index 0a58ec43d8ff7..f66916ccb78c1 100644 --- a/cmds/idmap2/include/idmap2/ResourceMapping.h +++ b/cmds/idmap2/include/idmap2/ResourceMapping.h @@ -117,7 +117,8 @@ class ResourceMapping { static Result CreateResourceMappingLegacy(const AssetManager2* target_am, const AssetManager2* overlay_am, const LoadedPackage* target_package, - const LoadedPackage* overlay_package); + const LoadedPackage* overlay_package, + LogInfo& log_info); // Removes resources that do not pass policy or overlayable checks of the target package. void FilterOverlayableResources(const AssetManager2* target_am, diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp index a93202a64d318..3037a791328e3 100644 --- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp @@ -100,10 +100,9 @@ void PrettyPrintVisitor::visit(const IdmapData& data) { stream_ << TAB << base::StringPrintf("0x%08x -> ", target_entry.target_id) << utils::DataTypeToString(target_entry.value.data_type); - size_t unused; if (target_entry.value.data_type == Res_value::TYPE_STRING) { - auto str = string_pool.stringAt(target_entry.value.data_value - string_pool_offset, &unused); - stream_ << " \"" << StringPiece16(str) << "\""; + auto str = string_pool.stringAt(target_entry.value.data_value - string_pool_offset); + stream_ << " \"" << str.value_or(StringPiece16(u"")) << "\""; } else { stream_ << " " << base::StringPrintf("0x%08x", target_entry.value.data_value); } diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp index 31f1c16ba5a68..d777cbfa9a14c 100644 --- a/cmds/idmap2/libidmap2/ResourceMapping.cpp +++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp @@ -71,9 +71,10 @@ Result CheckOverlayable(const LoadedPackage& target_package, if (!target_package.DefinesOverlayable()) { return (sDefaultPolicies & fulfilled_policies) != 0 ? Result({}) - : Error("overlay must be preinstalled, signed with the same signature as the target," - " or signed with the same signature as the package referenced through" - " ."); + : Error( + "overlay must be preinstalled, signed with the same signature as the target," + " or signed with the same signature as the package referenced through" + " ."); } const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource); @@ -119,32 +120,25 @@ const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) { Result> OpenNonAssetFromResource(const ResourceId& resource_id, const AssetManager2& asset_manager) { - Res_value value{}; - ResTable_config selected_config{}; - uint32_t flags; - auto cookie = - asset_manager.GetResource(resource_id, /* may_be_bag */ false, - /* density_override */ 0U, &value, &selected_config, &flags); - if (cookie == kInvalidCookie) { + auto value = asset_manager.GetResource(resource_id); + if (!value.has_value()) { return Error("failed to find resource for id 0x%08x", resource_id); } - if (value.dataType != Res_value::TYPE_STRING) { + if (value->type != Res_value::TYPE_STRING) { return Error("resource for is 0x%08x is not a file", resource_id); } - auto string_pool = asset_manager.GetStringPoolForCookie(cookie); - size_t len; - auto file_path16 = string_pool->stringAt(value.data, &len); - if (file_path16 == nullptr) { - return Error("failed to find string for index %d", value.data); + auto string_pool = asset_manager.GetStringPoolForCookie(value->cookie); + auto file = string_pool->string8ObjectAt(value->data); + if (!file.has_value()) { + return Error("failed to find string for index %d", value->data); } // Load the overlay resource mappings from the file specified using android:resourcesMap. - auto file_path = String8(String16(file_path16)); - auto asset = asset_manager.OpenNonAsset(file_path.c_str(), Asset::AccessMode::ACCESS_BUFFER); + auto asset = asset_manager.OpenNonAsset(file->c_str(), Asset::AccessMode::ACCESS_BUFFER); if (asset == nullptr) { - return Error("file \"%s\" not found", file_path.c_str()); + return Error("file \"%s\" not found", file->c_str()); } return asset; @@ -190,16 +184,16 @@ Result ResourceMapping::CreateResourceMapping(const AssetManage return Error(R"( tag missing expected attribute "value")"); } - ResourceId target_id = + auto target_id_result = target_am->GetResourceId(*target_resource, "", target_package->GetPackageName()); - if (target_id == 0U) { + if (!target_id_result.has_value()) { log_info.Warning(LogMessage() << "failed to find resource \"" << *target_resource << "\" in target resources"); continue; } // Retrieve the compile-time resource id of the target resource. - target_id = REWRITE_PACKAGE(target_id, target_package_id); + uint32_t target_id = REWRITE_PACKAGE(*target_id_result, target_package_id); if (overlay_resource->dataType == Res_value::TYPE_STRING) { overlay_resource->data += string_pool_offset; @@ -220,7 +214,7 @@ Result ResourceMapping::CreateResourceMapping(const AssetManage Result ResourceMapping::CreateResourceMappingLegacy( const AssetManager2* target_am, const AssetManager2* overlay_am, - const LoadedPackage* target_package, const LoadedPackage* overlay_package) { + const LoadedPackage* target_package, const LoadedPackage* overlay_package, LogInfo& log_info) { ResourceMapping resource_mapping; const uint8_t target_package_id = target_package->GetPackageId(); const auto end = overlay_package->end(); @@ -234,13 +228,15 @@ Result ResourceMapping::CreateResourceMappingLegacy( // Find the resource with the same type and entry name within the target package. const std::string full_name = base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str()); - ResourceId target_resource = target_am->GetResourceId(full_name); - if (target_resource == 0U) { + auto target_resource_result = target_am->GetResourceId(full_name); + if (!target_resource_result.has_value()) { + log_info.Warning(LogMessage() << "failed to find resource \"" << full_name + << "\" in target resources"); continue; } // Retrieve the compile-time resource id of the target resource. - target_resource = REWRITE_PACKAGE(target_resource, target_package_id); + ResourceId target_resource = REWRITE_PACKAGE(*target_resource_result, target_package_id); resource_mapping.AddMapping(target_resource, overlay_resid, false /* rewrite_overlay_reference */); } @@ -347,7 +343,9 @@ Result ResourceMapping::FromApkAssets(const ApkAssets& target_a auto& string_pool = (*parser)->get_strings(); string_pool_data_length = string_pool.bytes(); string_pool_data.reset(new uint8_t[string_pool_data_length]); - memcpy(string_pool_data.get(), string_pool.data(), string_pool_data_length); + + // Overlays should not be incrementally installed, so calling unsafe_ptr is fine here. + memcpy(string_pool_data.get(), string_pool.data().unsafe_ptr(), string_pool_data_length); // Offset string indices by the size of the overlay resource table string pool. string_pool_offset = overlay_arsc->GetStringPool()->size(); @@ -358,7 +356,7 @@ Result ResourceMapping::FromApkAssets(const ApkAssets& target_a // If no file is specified using android:resourcesMap, it is assumed that the overlay only // defines resources intended to override target resources of the same type and name. resource_mapping = CreateResourceMappingLegacy(&target_asset_manager, &overlay_asset_manager, - target_pkg, overlay_pkg); + target_pkg, overlay_pkg, log_info); } if (!resource_mapping) { diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp index 98d026bc70dc6..e817140238ae5 100644 --- a/cmds/idmap2/libidmap2/ResourceUtils.cpp +++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp @@ -72,21 +72,21 @@ StringPiece DataTypeToString(uint8_t data_type) { } Result ResToTypeEntryName(const AssetManager2& am, uint32_t resid) { - AssetManager2::ResourceName name; - if (!am.GetResourceName(resid, &name)) { + const auto name = am.GetResourceName(resid); + if (!name.has_value()) { return Error("no resource 0x%08x in asset manager", resid); } std::string out; - if (name.type != nullptr) { - out.append(name.type, name.type_len); + if (name->type != nullptr) { + out.append(name->type, name->type_len); } else { - out += Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); + out += Utf16ToUtf8(StringPiece16(name->type16, name->type_len)); } out.append("/"); - if (name.entry != nullptr) { - out.append(name.entry, name.entry_len); + if (name->entry != nullptr) { + out.append(name->entry, name->entry_len); } else { - out += Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); + out += Utf16ToUtf8(StringPiece16(name->entry16, name->entry_len)); } return out; } diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp index 526a560907aa0..7c55b64566f20 100644 --- a/cmds/idmap2/libidmap2/XmlParser.cpp +++ b/cmds/idmap2/libidmap2/XmlParser.cpp @@ -98,18 +98,19 @@ Result XmlParser::Node::GetAttributeStringValue(const std::string& switch ((*value).dataType) { case Res_value::TYPE_STRING: { - size_t len; - const String16 value16(parser_.getStrings().stringAt((*value).data, &len)); - return std::string(String8(value16).c_str()); + if (auto str = parser_.getStrings().string8ObjectAt((*value).data)) { + return std::string(str->string()); + } + break; } case Res_value::TYPE_INT_DEC: case Res_value::TYPE_INT_HEX: case Res_value::TYPE_INT_BOOLEAN: { return std::to_string((*value).data); } - default: - return Error(R"(Failed to convert attribute "%s" value to a string)", name.c_str()); } + + return Error(R"(Failed to convert attribute "%s" value to a string)", name.c_str()); } Result XmlParser::Node::GetAttributeValue(const std::string& name) const { diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp index fbdd4060d7f26..444bb668dc578 100644 --- a/core/jni/android_content_res_ApkAssets.cpp +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -24,6 +24,7 @@ #include "utils/misc.h" #include "utils/Trace.h" +#include "android_util_AssetManager_private.h" #include "core_jni_helpers.h" #include "jni.h" #include "nativehelper/ScopedUtfChars.h" @@ -347,12 +348,17 @@ static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring fil return 0; } + const auto buffer = asset->getIncFsBuffer(true /* aligned */); + const size_t length = asset->getLength(); + if (!buffer.convert().verify(length)) { + jniThrowException(env, kResourcesNotFound, kIOErrorMessage); + return 0; + } + // DynamicRefTable is only needed when looking up resource references. Opening an XML file // directly from an ApkAssets has no notion of proper resource references. - std::unique_ptr xml_tree = util::make_unique(nullptr /*dynamicRefTable*/); - status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); - asset.reset(); - + auto xml_tree = util::make_unique(nullptr /*dynamicRefTable*/); + status_t err = xml_tree->setTo(buffer.unsafe_ptr(), length, true); if (err != NO_ERROR) { jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); return 0; diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index efca33aaf5206..b2c69a0aeafdb 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -42,6 +42,7 @@ #include "androidfw/ResourceTypes.h" #include "androidfw/ResourceUtils.h" +#include "android_util_AssetManager_private.h" #include "core_jni_helpers.h" #include "jni.h" #include "nativehelper/JNIPlatformHelp.h" @@ -112,19 +113,17 @@ constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) return cookie > 0 ? static_cast(cookie - 1) : kInvalidCookie; } -static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref, - uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) { - env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType); +static jint CopyValue(JNIEnv* env, const AssetManager2::SelectedValue& value, + jobject out_typed_value) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.type); env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie, - ApkAssetsCookieToJavaCookie(cookie)); + ApkAssetsCookieToJavaCookie(value.cookie)); env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data); env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr); - env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref); - env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags); - if (config != nullptr) { - env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density); - } - return static_cast(ApkAssetsCookieToJavaCookie(cookie)); + env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, value.resid); + env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, value.flags); + env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, value.config.density); + return static_cast(ApkAssetsCookieToJavaCookie(value.cookie)); } // ---------------------------------------------------------------------------- @@ -569,15 +568,15 @@ static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint return 0; } - // May be nullptr. - std::shared_ptr dynamic_ref_table = - assetmanager->GetDynamicRefTableForCookie(cookie); - - std::unique_ptr xml_tree = util::make_unique( - std::move(dynamic_ref_table)); - status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); - asset.reset(); + const incfs::map_ptr buffer = asset->getIncFsBuffer(true /* aligned */); + const size_t length = asset->getLength(); + if (!buffer.convert().verify(length)) { + jniThrowException(env, kResourcesNotFound, kIOErrorMessage); + return 0; + } + auto xml_tree = util::make_unique(assetmanager->GetDynamicRefTableForCookie(cookie)); + status_t err = xml_tree->setTo(buffer.unsafe_ptr(), length, true); if (err != NO_ERROR) { jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); return 0; @@ -606,15 +605,15 @@ static jlong NativeOpenXmlAssetFd(JNIEnv* env, jobject /*clazz*/, jlong ptr, int ScopedLock assetmanager(AssetManagerFromLong(ptr)); ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); - // May be nullptr. - std::shared_ptr dynamic_ref_table = - assetmanager->GetDynamicRefTableForCookie(cookie); - - std::unique_ptr xml_tree = util::make_unique( - std::move(dynamic_ref_table)); - status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); - asset.reset(); + const incfs::map_ptr buffer = asset->getIncFsBuffer(true /* aligned */); + const size_t length = asset->getLength(); + if (!buffer.convert().verify(length)) { + jniThrowException(env, kResourcesNotFound, kIOErrorMessage); + return 0; + } + auto xml_tree = util::make_unique(assetmanager->GetDynamicRefTableForCookie(cookie)); + status_t err = xml_tree->setTo(buffer.unsafe_ptr(), length, true); if (err != NO_ERROR) { jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); return 0; @@ -626,67 +625,62 @@ static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jin jshort density, jobject typed_value, jboolean resolve_references) { ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - ApkAssetsCookie cookie = - assetmanager->GetResource(static_cast(resid), false /*may_be_bag*/, - static_cast(density), &value, &selected_config, &flags); - if (cookie == kInvalidCookie) { + auto value = assetmanager->GetResource(static_cast(resid), false /*may_be_bag*/, + static_cast(density)); + if (!value.has_value()) { + ThrowIfIOError(env, value); return ApkAssetsCookieToJavaCookie(kInvalidCookie); } - uint32_t ref = static_cast(resid); if (resolve_references) { - cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { + auto result = assetmanager->ResolveReference(value.value()); + if (!result.has_value()) { + ThrowIfIOError(env, result); return ApkAssetsCookieToJavaCookie(kInvalidCookie); } } - return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value); + return CopyValue(env, *value, typed_value); } static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, jint bag_entry_id, jobject typed_value) { ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { + auto bag = assetmanager->GetBag(static_cast(resid)); + if (!bag.has_value()) { + ThrowIfIOError(env, bag); return ApkAssetsCookieToJavaCookie(kInvalidCookie); } - uint32_t type_spec_flags = bag->type_spec_flags; - ApkAssetsCookie cookie = kInvalidCookie; - const Res_value* bag_value = nullptr; - for (const ResolvedBag::Entry& entry : bag) { - if (entry.key == static_cast(bag_entry_id)) { - cookie = entry.cookie; - bag_value = &entry.value; + // The legacy would find the last entry with the target bag entry id + using reverse_bag_iterator = std::reverse_iterator; + const auto rbegin = reverse_bag_iterator(end(*bag)); + const auto rend = reverse_bag_iterator(begin(*bag)); + auto entry = std::find_if(rbegin, rend, [bag_entry_id](auto&& e) { + return e.key == static_cast(bag_entry_id); + }); - // Keep searching (the old implementation did that). - } - } - - if (cookie == kInvalidCookie) { + if (entry == rend) { return ApkAssetsCookieToJavaCookie(kInvalidCookie); } - Res_value value = *bag_value; - uint32_t ref = static_cast(resid); - ResTable_config selected_config; - cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref); - if (cookie == kInvalidCookie) { + AssetManager2::SelectedValue attr_value(*bag, *entry); + auto result = assetmanager->ResolveReference(attr_value); + if (!result.has_value()) { + ThrowIfIOError(env, result); return ApkAssetsCookieToJavaCookie(kInvalidCookie); } - return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value); + return CopyValue(env, attr_value, typed_value); } static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { + auto bag_result = assetmanager->GetBag(static_cast(resid)); + if (!bag_result.has_value()) { + ThrowIfIOError(env, bag_result); return nullptr; } + const ResolvedBag* bag = *bag_result; jintArray array = env->NewIntArray(bag->entry_count); if (env->ExceptionCheck()) { return nullptr; @@ -702,42 +696,47 @@ static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong p static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { + auto bag_result = assetmanager->GetBag(static_cast(resid)); + if (!bag_result.has_value()) { + ThrowIfIOError(env, bag_result); return nullptr; } + const ResolvedBag* bag = *bag_result; jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr); if (array == nullptr) { return nullptr; } for (uint32_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - // Resolve any references to their final value. - Res_value value = entry.value; - ResTable_config selected_config; - uint32_t flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { + AssetManager2::SelectedValue attr_value(bag, bag->entries[i]); + auto result = assetmanager->ResolveReference(attr_value); + if (!result.has_value()) { + ThrowIfIOError(env, result); return nullptr; } - if (value.dataType == Res_value::TYPE_STRING) { - const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie]; + if (attr_value.type == Res_value::TYPE_STRING) { + const ApkAssets* apk_assets = assetmanager->GetApkAssets()[attr_value.cookie]; const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool(); jstring java_string = nullptr; - size_t str_len; - const char* str_utf8 = pool->string8At(value.data, &str_len); - if (str_utf8 != nullptr) { - java_string = env->NewStringUTF(str_utf8); + auto str_utf8 = pool->string8At(attr_value.data); + if (UNLIKELY(ThrowIfIOError(env, str_utf8))) { + return nullptr; + } + + if (str_utf8.has_value()) { + java_string = env->NewStringUTF(str_utf8->data()); } else { - const char16_t* str_utf16 = pool->stringAt(value.data, &str_len); - java_string = env->NewString(reinterpret_cast(str_utf16), str_len); + auto str_utf16 = pool->stringAt(attr_value.data); + if (!str_utf16.has_value()) { + ThrowIfIOError(env, str_utf16); + return nullptr; + } + java_string = env->NewString(reinterpret_cast(str_utf16->data()), + str_utf16->size()); } // Check for errors creating the strings (if malformed or no memory). @@ -758,11 +757,13 @@ static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { + auto bag_result = assetmanager->GetBag(static_cast(resid)); + if (!bag_result.has_value()) { + ThrowIfIOError(env, bag_result); return nullptr; } + const ResolvedBag* bag = *bag_result; jintArray array = env->NewIntArray(bag->entry_count * 2); if (array == nullptr) { return nullptr; @@ -774,24 +775,20 @@ static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, } for (size_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - Res_value value = entry.value; - ResTable_config selected_config; - uint32_t flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { + AssetManager2::SelectedValue attr_value(bag, bag->entries[i]); + auto result = assetmanager->ResolveReference(attr_value); + if (!result.has_value()) { env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + ThrowIfIOError(env, result); return nullptr; } jint string_index = -1; - if (value.dataType == Res_value::TYPE_STRING) { - string_index = static_cast(value.data); + if (attr_value.type == Res_value::TYPE_STRING) { + string_index = static_cast(attr_value.data); } - buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie); + buffer[i * 2] = ApkAssetsCookieToJavaCookie(attr_value.cookie); buffer[(i * 2) + 1] = string_index; } env->ReleasePrimitiveArrayCritical(array, buffer, 0); @@ -800,11 +797,13 @@ static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { + auto bag_result = assetmanager->GetBag(static_cast(resid)); + if (!bag_result.has_value()) { + ThrowIfIOError(env, bag_result); return nullptr; } + const ResolvedBag* bag = *bag_result; jintArray array = env->NewIntArray(bag->entry_count); if (array == nullptr) { return nullptr; @@ -816,40 +815,39 @@ static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong } for (size_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - Res_value value = entry.value; - ResTable_config selected_config; - uint32_t flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + AssetManager2::SelectedValue attr_value(bag, bag->entries[i]); + auto result = assetmanager->ResolveReference(attr_value); + if (!result.has_value()) { + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + ThrowIfIOError(env, result); return nullptr; } - if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) { - buffer[i] = static_cast(value.data); + if (attr_value.type >= Res_value::TYPE_FIRST_INT && + attr_value.type <= Res_value::TYPE_LAST_INT) { + buffer[i] = static_cast(attr_value.data); } } env->ReleasePrimitiveArrayCritical(array, buffer, 0); return array; } -static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return -1; - } - return static_cast(bag->entry_count); +static jint NativeGetResourceArraySize(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + auto bag = assetmanager->GetBag(static_cast(resid)); + if (!bag.has_value()) { + ThrowIfIOError(env, bag); + return -1; + } + return static_cast((*bag)->entry_count); } static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, jintArray out_data) { ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { + auto bag_result = assetmanager->GetBag(static_cast(resid)); + if (!bag_result.has_value()) { + ThrowIfIOError(env, bag_result); return -1; } @@ -858,8 +856,10 @@ static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jin return -1; } + const ResolvedBag* bag = *bag_result; if (static_cast(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough"); + jniThrowException(env, "java/lang/IllegalArgumentException", + "Input array is not large enough"); return -1; } @@ -870,31 +870,26 @@ static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jin jint* cursor = buffer; for (size_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - Res_value value = entry.value; - ResTable_config selected_config; - selected_config.density = 0; - uint32_t flags = bag->type_spec_flags; - uint32_t ref = 0; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { + AssetManager2::SelectedValue attr_value(bag, bag->entries[i]); + auto result = assetmanager->ResolveReference(attr_value); + if (!result.has_value()) { env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT); + ThrowIfIOError(env, bag_result); return -1; } // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; + if (attr_value.type == Res_value::TYPE_REFERENCE && attr_value.data == 0) { + attr_value.type = Res_value::TYPE_NULL; + attr_value.data = Res_value::DATA_NULL_UNDEFINED; } - cursor[STYLE_TYPE] = static_cast(value.dataType); - cursor[STYLE_DATA] = static_cast(value.data); - cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); - cursor[STYLE_RESOURCE_ID] = static_cast(ref); - cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast(flags); - cursor[STYLE_DENSITY] = static_cast(selected_config.density); + cursor[STYLE_TYPE] = static_cast(attr_value.type); + cursor[STYLE_DATA] = static_cast(attr_value.data); + cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(attr_value.cookie); + cursor[STYLE_RESOURCE_ID] = static_cast(attr_value.resid); + cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast(attr_value.flags); + cursor[STYLE_DENSITY] = static_cast(attr_value.config.density); cursor += STYLE_NUM_ENTRIES; } env->ReleasePrimitiveArrayCritical(out_data, buffer, 0); @@ -922,60 +917,71 @@ static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr CHECK(package_utf8.c_str() != nullptr); package = package_utf8.c_str(); } + ScopedLock assetmanager(AssetManagerFromLong(ptr)); - return static_cast(assetmanager->GetResourceId(name_utf8.c_str(), type, package)); + auto resid = assetmanager->GetResourceId(name_utf8.c_str(), type, package); + if (!resid.has_value()) { + ThrowIfIOError(env, resid); + return 0; + } + + return static_cast(*resid); } static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { ScopedLock assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + auto name = assetmanager->GetResourceName(static_cast(resid)); + if (!name.has_value()) { + ThrowIfIOError(env, name); return nullptr; } - std::string result = ToFormattedResourceString(&name); + const std::string result = ToFormattedResourceString(name.value()); return env->NewStringUTF(result.c_str()); } static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { ScopedLock assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + auto name = assetmanager->GetResourceName(static_cast(resid)); + if (!name.has_value()) { + ThrowIfIOError(env, name); return nullptr; } - if (name.package != nullptr) { - return env->NewStringUTF(name.package); + if (name->package != nullptr) { + return env->NewStringUTF(name->package); } return nullptr; } static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { ScopedLock assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + auto name = assetmanager->GetResourceName(static_cast(resid)); + if (!name.has_value()) { + ThrowIfIOError(env, name); return nullptr; } - if (name.type != nullptr) { - return env->NewStringUTF(name.type); - } else if (name.type16 != nullptr) { - return env->NewString(reinterpret_cast(name.type16), name.type_len); + if (name->type != nullptr) { + return env->NewStringUTF(name->type); + } else if (name->type16 != nullptr) { + return env->NewString(reinterpret_cast(name->type16), name->type_len); } return nullptr; } static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { ScopedLock assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + auto name = assetmanager->GetResourceName(static_cast(resid)); + if (!name.has_value()) { + ThrowIfIOError(env, name); return nullptr; } - if (name.entry != nullptr) { - return env->NewStringUTF(name.entry); - } else if (name.entry16 != nullptr) { - return env->NewString(reinterpret_cast(name.entry16), name.entry_len); + if (name->entry != nullptr) { + return env->NewStringUTF(name->entry); + } else if (name->entry16 != nullptr) { + return env->NewString(reinterpret_cast(name->entry16), name->entry_len); } return nullptr; } @@ -1039,17 +1045,21 @@ static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::set configurations = - assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/); + auto configurations = assetmanager->GetResourceConfigurations(true /*exclude_system*/, + false /*exclude_mipmap*/); + if (!configurations.has_value()) { + ThrowIfIOError(env, configurations); + return nullptr; + } jobjectArray array = - env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr); + env->NewObjectArray(configurations->size(), gConfigurationOffsets.classObject, nullptr); if (array == nullptr) { return nullptr; } size_t idx = 0; - for (const ResTable_config& configuration : configurations) { + for (const ResTable_config& configuration : *configurations) { jobject java_configuration = ConstructConfigurationObject(env, configuration); if (java_configuration == nullptr) { return nullptr; @@ -1072,13 +1082,10 @@ static jintArray NativeAttributeResolutionStack( (void) assetmanager; // Load default style from attribute, if specified... - uint32_t def_style_flags = 0u; if (def_style_attr != 0) { - Res_value value; - if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { - if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_resid = value.data; - } + auto value = theme->GetAttribute(def_style_attr); + if (value.has_value() && value->type == Res_value::TYPE_REFERENCE) { + def_style_resid = value->data; } } @@ -1119,10 +1126,11 @@ static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong the return; } - ApplyStyle(theme, xml_parser, static_cast(def_style_attr), - static_cast(def_style_resid), reinterpret_cast(attrs), attrs_len, - out_values, out_indices); + auto result = ApplyStyle(theme, xml_parser, static_cast(def_style_attr), + static_cast(def_style_resid), + reinterpret_cast(attrs), attrs_len, out_values, out_indices); env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + ThrowIfIOError(env, result); } static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, @@ -1183,11 +1191,12 @@ static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlo Theme* theme = reinterpret_cast(theme_ptr); CHECK(theme->GetAssetManager() == &(*assetmanager)); (void) assetmanager; - - bool result = ResolveAttrs( - theme, static_cast(def_style_attr), static_cast(def_style_resid), - reinterpret_cast(values), values_len, reinterpret_cast(attrs), - attrs_len, reinterpret_cast(out_values), reinterpret_cast(out_indices)); + auto result = + ResolveAttrs(theme, static_cast(def_style_attr), + static_cast(def_style_resid), reinterpret_cast(values), + values_len, reinterpret_cast(attrs), attrs_len, + reinterpret_cast(out_values), + reinterpret_cast(out_indices)); if (out_indices != nullptr) { env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); } @@ -1196,8 +1205,13 @@ static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlo if (values != nullptr) { env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); } + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return result ? JNI_TRUE : JNI_FALSE; + if (!result.has_value()) { + ThrowIfIOError(env, result); + return JNI_FALSE; + } + return JNI_TRUE; } static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, @@ -1238,18 +1252,22 @@ static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong pt ScopedLock assetmanager(AssetManagerFromLong(ptr)); ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); - - bool result = RetrieveAttributes(assetmanager.get(), xml_parser, - reinterpret_cast(attrs), attrs_len, - reinterpret_cast(out_values), - reinterpret_cast(out_indices)); + auto result = + RetrieveAttributes(assetmanager.get(), xml_parser, reinterpret_cast(attrs), + attrs_len, reinterpret_cast(out_values), + reinterpret_cast(out_indices)); if (out_indices != nullptr) { env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return result ? JNI_TRUE : JNI_FALSE; + if (!result.has_value()) { + ThrowIfIOError(env, result); + return JNI_FALSE; + } + return JNI_TRUE; } static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { @@ -1268,7 +1286,9 @@ static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlon Theme* theme = reinterpret_cast(theme_ptr); CHECK(theme->GetAssetManager() == &(*assetmanager)); (void) assetmanager; - theme->ApplyStyle(static_cast(resid), force); + + auto result = theme->ApplyStyle(static_cast(resid), force); + ThrowIfIOError(env, result); // TODO(adamlesinski): Consider surfacing exception when result is failure. // CTS currently expects no exceptions from this method. @@ -1281,19 +1301,22 @@ static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_asset_manag Theme* dst_theme = reinterpret_cast(dst_theme_ptr); Theme* src_theme = reinterpret_cast(src_theme_ptr); + ScopedLock src_assetmanager(AssetManagerFromLong(src_asset_manager_ptr)); + CHECK(src_theme->GetAssetManager() == &(*src_assetmanager)); + (void) src_assetmanager; + if (dst_asset_manager_ptr != src_asset_manager_ptr) { ScopedLock dst_assetmanager(AssetManagerFromLong(dst_asset_manager_ptr)); CHECK(dst_theme->GetAssetManager() == &(*dst_assetmanager)); (void) dst_assetmanager; - ScopedLock src_assetmanager(AssetManagerFromLong(src_asset_manager_ptr)); - CHECK(src_theme->GetAssetManager() == &(*src_assetmanager)); - (void) src_assetmanager; - - dst_theme->SetTo(*src_theme); - } else { - dst_theme->SetTo(*src_theme); + auto result = dst_theme->SetTo(*src_theme); + ThrowIfIOError(env, result); + return; } + + auto result = dst_theme->SetTo(*src_theme); + ThrowIfIOError(env, result); } static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { @@ -1308,23 +1331,21 @@ static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong pt CHECK(theme->GetAssetManager() == &(*assetmanager)); (void) assetmanager; - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie = theme->GetAttribute(static_cast(resid), &value, &flags); - if (cookie == kInvalidCookie) { + auto value = theme->GetAttribute(static_cast(resid)); + if (!value.has_value()) { return ApkAssetsCookieToJavaCookie(kInvalidCookie); } - uint32_t ref = 0u; - if (resolve_references) { - ResTable_config selected_config; - cookie = - theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } + if (!resolve_references) { + return CopyValue(env, *value, typed_value); } - return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value); + + auto result = theme->GetAssetManager()->ResolveReference(*value); + if (!result.has_value()) { + ThrowIfIOError(env, result); + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + return CopyValue(env, *value, typed_value); } static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr, diff --git a/core/jni/android_util_AssetManager_private.h b/core/jni/android_util_AssetManager_private.h new file mode 100644 index 0000000000000..153509b98db50 --- /dev/null +++ b/core/jni/android_util_AssetManager_private.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 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 ANDROID_UTIL_ASSETMANAGER_PRIVATE_H +#define ANDROID_UTIL_ASSETMANAGER_PRIVATE_H + +#include + +#include +#include + +#include "core_jni_helpers.h" +#include "jni.h" +#include "nativehelper/JNIHelp.h" + +namespace android { + +constexpr const char* kResourcesNotFound = "android/content/res/Resources$NotFoundException"; +constexpr const static char* kIOErrorMessage = "failed to read resources.arsc data"; + +template +static bool ThrowIfIOError(JNIEnv* env, const base::expected& result) { + if constexpr (std::is_same::value) { + if (IsIOError(result)) { + jniThrowException(env, kResourcesNotFound, kIOErrorMessage); + return true; + } + return false; + } else { + if (!result.has_value()) { + static_assert(std::is_same::value, "Unknown result error type"); + jniThrowException(env, kResourcesNotFound, kIOErrorMessage); + return true; + } + return false; + } +} + +} // namespace android + +#endif //ANDROID_UTIL_ASSETMANAGER_PRIVATE_H diff --git a/core/jni/android_util_StringBlock.cpp b/core/jni/android_util_StringBlock.cpp index 760f9e36ed3d8..45f6b72b37272 100644 --- a/core/jni/android_util_StringBlock.cpp +++ b/core/jni/android_util_StringBlock.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "StringBlock" +#include "android_util_AssetManager_private.h" #include "jni.h" #include #include @@ -31,10 +32,8 @@ namespace android { // ---------------------------------------------------------------------------- -static jlong android_content_StringBlock_nativeCreate(JNIEnv* env, jobject clazz, - jbyteArray bArray, - jint off, jint len) -{ +static jlong android_content_StringBlock_nativeCreate(JNIEnv* env, jobject clazz, jbyteArray bArray, + jint off, jint len) { if (bArray == NULL) { jniThrowNullPointerException(env, NULL); return 0; @@ -59,9 +58,7 @@ static jlong android_content_StringBlock_nativeCreate(JNIEnv* env, jobject clazz return reinterpret_cast(osb); } -static jint android_content_StringBlock_nativeGetSize(JNIEnv* env, jobject clazz, - jlong token) -{ +static jint android_content_StringBlock_nativeGetSize(JNIEnv* env, jobject clazz, jlong token) { ResStringPool* osb = reinterpret_cast(token); if (osb == NULL) { jniThrowNullPointerException(env, NULL); @@ -71,76 +68,84 @@ static jint android_content_StringBlock_nativeGetSize(JNIEnv* env, jobject clazz return osb->size(); } -static jstring android_content_StringBlock_nativeGetString(JNIEnv* env, jobject clazz, - jlong token, jint idx) -{ +static jstring android_content_StringBlock_nativeGetString(JNIEnv* env, jobject clazz, jlong token, + jint idx) { ResStringPool* osb = reinterpret_cast(token); if (osb == NULL) { jniThrowNullPointerException(env, NULL); return 0; } - size_t len; - const char* str8 = osb->string8At(idx, &len); - if (str8 != NULL) { - return env->NewStringUTF(str8); + auto str8 = osb->string8At(idx); + if (UNLIKELY(ThrowIfIOError(env, str8))) { + return 0; + } else if (str8.has_value()) { + return env->NewStringUTF(str8->data()); } - const char16_t* str = osb->stringAt(idx, &len); - if (str == NULL) { + auto str = osb->stringAt(idx); + if (UNLIKELY(ThrowIfIOError(env, str))) { + return 0; + } else if (UNLIKELY(!str.has_value())) { jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL); return 0; } - return env->NewString((const jchar*)str, len); + return env->NewString((const jchar*)str->data(), str->size()); } -static jintArray android_content_StringBlock_nativeGetStyle(JNIEnv* env, jobject clazz, - jlong token, jint idx) -{ +static jintArray android_content_StringBlock_nativeGetStyle(JNIEnv* env, jobject clazz, jlong token, + jint idx) { ResStringPool* osb = reinterpret_cast(token); if (osb == NULL) { jniThrowNullPointerException(env, NULL); return NULL; } - const ResStringPool_span* spans = osb->styleAt(idx); - if (spans == NULL) { + auto spans = osb->styleAt(idx); + if (!spans.has_value()) { + ThrowIfIOError(env, spans); return NULL; } - const ResStringPool_span* pos = spans; - int num = 0; - while (pos->name.index != ResStringPool_span::END) { - num++; - pos++; - } + jintArray array; + { + int num = 0; + auto pos = *spans; + while (true) { + if (UNLIKELY(!pos)) { + jniThrowException(env, kResourcesNotFound, kIOErrorMessage); + return NULL; + } + if (pos->name.index == ResStringPool_span::END) { + break; + } + num++; + pos++; + } - if (num == 0) { - return NULL; - } + if (num == 0) { + return NULL; + } - jintArray array = env->NewIntArray((num*sizeof(ResStringPool_span))/sizeof(jint)); - if (array == NULL) { // NewIntArray already threw OutOfMemoryError. - return NULL; + array = env->NewIntArray((num * sizeof(ResStringPool_span)) / sizeof(jint)); + if (array == NULL) { // NewIntArray already threw OutOfMemoryError. + return NULL; + } } - - num = 0; - static const int numInts = sizeof(ResStringPool_span)/sizeof(jint); - while (spans->name.index != ResStringPool_span::END) { - env->SetIntArrayRegion(array, - num*numInts, numInts, - (jint*)spans); - spans++; - num++; + { + int num = 0; + static const int numInts = sizeof(ResStringPool_span) / sizeof(jint); + while ((*spans)->name.index != ResStringPool_span::END) { + env->SetIntArrayRegion(array, num * numInts, numInts, (jint*)spans->unsafe_ptr()); + (*spans)++; + num++; + } } - return array; } -static void android_content_StringBlock_nativeDestroy(JNIEnv* env, jobject clazz, - jlong token) -{ +static void android_content_StringBlock_nativeDestroy(JNIEnv* env, jobject clazz, jlong token) { ResStringPool* osb = reinterpret_cast(token); if (osb == NULL) { jniThrowNullPointerException(env, NULL); diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index aa34edf487fe4..6a7df94331f36 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -61,6 +61,9 @@ cc_library { ], export_include_dirs: ["include"], export_shared_lib_headers: ["libz"], + static_libs: ["libincfs-utils"], + whole_static_libs: ["libincfs-utils"], + export_static_lib_headers: ["libincfs-utils"], target: { android: { srcs: [ @@ -69,13 +72,14 @@ cc_library { "CursorWindow.cpp", ], shared_libs: [ - "libziparchive", "libbase", "libbinder", "liblog", "libcutils", + "libincfs", "libutils", "libz", + "libziparchive", ], static: { enabled: false, @@ -86,11 +90,11 @@ cc_library { enabled: false, }, static_libs: [ - "libziparchive", "libbase", - "liblog", "libcutils", + "liblog", "libutils", + "libziparchive", ], shared_libs: [ "libz", diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index e15b42d46f536..cb56a5172a455 100755 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -25,13 +25,11 @@ #include "android-base/unique_fd.h" #include "android-base/utf8.h" #include "utils/Compat.h" -#include "utils/FileMap.h" #include "ziparchive/zip_archive.h" #include "androidfw/Asset.h" #include "androidfw/Idmap.h" #include "androidfw/misc.h" -#include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" namespace android { @@ -161,50 +159,46 @@ class ZipAssetsProvider : public AssetsProvider { } const int fd = ::GetFileDescriptor(zip_handle_.get()); - const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get()); + const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get()); + incfs::IncFsFileMap asset_map; if (entry.method == kCompressDeflated) { - std::unique_ptr map = util::make_unique(); - if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.compressed_length, - true /*readOnly*/)) { + if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length, GetPath())) { LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; return {}; } std::unique_ptr asset = - Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode); + Asset::createFromCompressedMap(std::move(asset_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 map = util::make_unique(); - 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::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; } + + if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length, GetPath())) { + 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 {}; + } + } + + auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd)); + if (asset == nullptr) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; + return {}; + } + return asset; } private: @@ -446,8 +440,8 @@ std::unique_ptr ApkAssets::CreateAssetFromFd(base::unique_fd fd, } } - std::unique_ptr file_map = util::make_unique(); - if (!file_map->create(path, fd, offset, static_cast(length), true /*readOnly*/)) { + incfs::IncFsFileMap file_map; + if (!file_map.Create(fd, offset, static_cast(length), path)) { LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': " << SystemErrorCodeToString(errno); return {}; @@ -456,8 +450,8 @@ std::unique_ptr ApkAssets::CreateAssetFromFd(base::unique_fd fd, // 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); + Asset::AccessMode::ACCESS_RANDOM, + (path) ? base::unique_fd(-1) : std::move(fd)); } std::unique_ptr ApkAssets::LoadImpl( @@ -493,15 +487,14 @@ std::unique_ptr ApkAssets::LoadImpl( loaded_apk->idmap_asset_ = std::move(idmap_asset); loaded_apk->loaded_idmap_ = std::move(idmap); - const StringPiece data( - reinterpret_cast(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), - loaded_apk->resources_asset_->getLength()); - if (data.data() == nullptr || data.empty()) { + const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */); + const size_t length = loaded_apk->resources_asset_->getLength(); + if (!data || length == 0) { LOG(ERROR) << "Failed to read '" << kResourcesArsc << "' data in APK '" << path << "'."; return {}; } - loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(), + loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, loaded_apk->loaded_idmap_.get(), property_flags); if (!loaded_apk->loaded_arsc_) { LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'."; @@ -525,15 +518,15 @@ std::unique_ptr ApkAssets::LoadTableImpl( new ApkAssets(std::move(assets), path, last_mod_time, property_flags)); loaded_apk->resources_asset_ = std::move(resources_asset); - const StringPiece data( - reinterpret_cast(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), - loaded_apk->resources_asset_->getLength()); - if (data.data() == nullptr || data.empty()) { + const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */); + const size_t length = loaded_apk->resources_asset_->getLength(); + if (!data || length == 0) { LOG(ERROR) << "Failed to read resources table data in '" << path << "'."; return {}; } - loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags); + loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, nullptr /* loaded_idmap */, + property_flags); if (loaded_apk->loaded_arsc_ == nullptr) { LOG(ERROR) << "Failed to read resources table in '" << path << "'."; return {}; @@ -550,7 +543,6 @@ bool ApkAssets::IsUpToDate() const { } return (!loaded_idmap_ || loaded_idmap_->IsUpToDate()) && last_mod_time_ == getFileModDate(path_.c_str()); - } } // namespace android diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index cd30c184d5a47..4fbe4a3efbddb 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -298,34 +298,18 @@ Asset::Asset(void) /* * Create a new Asset from a memory mapping. */ -/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, AccessMode mode) +/*static*/ std::unique_ptr Asset::createFromUncompressedMap(incfs::IncFsFileMap&& dataMap, + AccessMode mode, + base::unique_fd fd) { - _FileAsset* pAsset; - status_t result; + auto pAsset = util::make_unique<_FileAsset>(); - pAsset = new _FileAsset; - result = pAsset->openChunk(dataMap, base::unique_fd(-1)); - if (result != NO_ERROR) { - delete pAsset; - return NULL; - } - - pAsset->mAccessMode = mode; - return pAsset; -} - -/*static*/ std::unique_ptr Asset::createFromUncompressedMap(std::unique_ptr dataMap, - base::unique_fd fd, AccessMode mode) -{ - std::unique_ptr<_FileAsset> pAsset = util::make_unique<_FileAsset>(); - - status_t result = pAsset->openChunk(dataMap.get(), std::move(fd)); + status_t result = pAsset->openChunk(std::move(dataMap), std::move(fd)); if (result != NO_ERROR) { return NULL; } // We succeeded, so relinquish control of dataMap - (void) dataMap.release(); pAsset->mAccessMode = mode; return std::move(pAsset); } @@ -333,35 +317,18 @@ Asset::Asset(void) /* * Create a new Asset from compressed data in a memory mapping. */ -/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap, - size_t uncompressedLen, AccessMode mode) +/*static*/ std::unique_ptr Asset::createFromCompressedMap(incfs::IncFsFileMap&& dataMap, + size_t uncompressedLen, + AccessMode mode) { - _CompressedAsset* pAsset; - status_t result; + auto pAsset = util::make_unique<_CompressedAsset>(); - pAsset = new _CompressedAsset; - result = pAsset->openChunk(dataMap, uncompressedLen); - if (result != NO_ERROR) { - delete pAsset; - return NULL; - } - - pAsset->mAccessMode = mode; - return pAsset; -} - -/*static*/ std::unique_ptr Asset::createFromCompressedMap(std::unique_ptr dataMap, - size_t uncompressedLen, AccessMode mode) -{ - std::unique_ptr<_CompressedAsset> pAsset = util::make_unique<_CompressedAsset>(); - - status_t result = pAsset->openChunk(dataMap.get(), uncompressedLen); + status_t result = pAsset->openChunk(std::move(dataMap), uncompressedLen); if (result != NO_ERROR) { return NULL; } // We succeeded, so relinquish control of dataMap - (void) dataMap.release(); pAsset->mAccessMode = mode; return std::move(pAsset); } @@ -414,7 +381,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), mFd(-1), mMap(NULL), mBuf(NULL) + : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), 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 @@ -441,7 +408,7 @@ _FileAsset::~_FileAsset(void) status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length) { assert(mFp == NULL); // no reopen - assert(mMap == NULL); + assert(!mMap.has_value()); assert(fd >= 0); assert(offset >= 0); @@ -484,15 +451,15 @@ 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, base::unique_fd fd) +status_t _FileAsset::openChunk(incfs::IncFsFileMap&& dataMap, base::unique_fd fd) { assert(mFp == NULL); // no reopen - assert(mMap == NULL); + assert(!mMap.has_value()); assert(dataMap != NULL); - mMap = dataMap; + mMap = std::move(dataMap); mStart = -1; // not used - mLength = dataMap->getDataLength(); + mLength = mMap->length(); mFd = std::move(fd); assert(mOffset == 0); @@ -528,10 +495,15 @@ ssize_t _FileAsset::read(void* buf, size_t count) if (!count) return 0; - if (mMap != NULL) { + if (mMap.has_value()) { /* copy from mapped area */ //printf("map read\n"); - memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count); + const auto readPos = mMap->data().offset(mOffset).convert(); + if (!readPos.verify(count)) { + return -1; + } + + memcpy(buf, readPos.unsafe_ptr(), count); actual = count; } else if (mBuf != NULL) { /* copy from buffer */ @@ -594,10 +566,6 @@ off64_t _FileAsset::seek(off64_t offset, int whence) */ void _FileAsset::close(void) { - if (mMap != NULL) { - delete mMap; - mMap = NULL; - } if (mBuf != NULL) { delete[] mBuf; mBuf = NULL; @@ -624,16 +592,21 @@ void _FileAsset::close(void) * level and we'd be using a different object, but we didn't, so we * deal with it here. */ -const void* _FileAsset::getBuffer(bool wordAligned) +const void* _FileAsset::getBuffer(bool aligned) +{ + return getIncFsBuffer(aligned).unsafe_ptr(); +} + +incfs::map_ptr _FileAsset::getIncFsBuffer(bool aligned) { /* subsequent requests just use what we did previously */ if (mBuf != NULL) return mBuf; - if (mMap != NULL) { - if (!wordAligned) { - return mMap->getDataPtr(); + if (mMap.has_value()) { + if (!aligned) { + return mMap->data(); } - return ensureAlignment(mMap); + return ensureAlignment(*mMap); } assert(mFp != NULL); @@ -671,47 +644,44 @@ const void* _FileAsset::getBuffer(bool wordAligned) mBuf = buf; return mBuf; } else { - FileMap* map; - - map = new FileMap; - if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) { - delete map; + incfs::IncFsFileMap map; + if (!map.Create(fileno(mFp), mStart, mLength, NULL /* file_name */ )) { return NULL; } ALOGV(" getBuffer: mapped\n"); - mMap = map; - if (!wordAligned) { - return mMap->getDataPtr(); + mMap = std::move(map); + if (!aligned) { + return mMap->data(); } - return ensureAlignment(mMap); + return ensureAlignment(*mMap); } } int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const { - if (mMap != NULL) { + if (mMap.has_value()) { 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; + *outStart = mMap->offset(); + *outLength = mMap->length(); + 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(); + const char* fname = mMap->file_name(); if (fname == NULL) { fname = mFileName; } if (fname == NULL) { return -1; } - *outStart = mMap->getDataOffset(); - *outLength = mMap->getDataLength(); + *outStart = mMap->offset(); + *outLength = mMap->length(); return open(fname, O_RDONLY | O_BINARY); } if (mFileName == NULL) { @@ -722,16 +692,21 @@ int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const return open(mFileName, O_RDONLY | O_BINARY); } -const void* _FileAsset::ensureAlignment(FileMap* map) +incfs::map_ptr _FileAsset::ensureAlignment(const incfs::IncFsFileMap& map) { - void* data = map->getDataPtr(); - if ((((size_t)data)&0x3) == 0) { + const auto data = map.data(); + if (util::IsFourByteAligned(data)) { // We can return this directly if it is aligned on a word // boundary. ALOGV("Returning aligned FileAsset %p (%s).", this, getAssetSource()); return data; } + + if (!data.convert().verify(mLength)) { + return NULL; + } + // If not aligned on a word boundary, then we need to copy it into // our own buffer. ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this, @@ -741,7 +716,8 @@ const void* _FileAsset::ensureAlignment(FileMap* map) ALOGE("alloc of %ld bytes failed\n", (long) mLength); return NULL; } - memcpy(buf, data, mLength); + + memcpy(buf, data.unsafe_ptr(), mLength); mBuf = buf; return buf; } @@ -757,7 +733,7 @@ const void* _FileAsset::ensureAlignment(FileMap* map) */ _CompressedAsset::_CompressedAsset(void) : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0), - mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL) + mFd(-1), mZipInflater(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 @@ -786,7 +762,7 @@ status_t _CompressedAsset::openChunk(int fd, off64_t offset, int compressionMethod, size_t uncompressedLen, size_t compressedLen) { assert(mFd < 0); // no re-open - assert(mMap == NULL); + assert(!mMap.has_value()); assert(fd >= 0); assert(offset >= 0); assert(compressedLen > 0); @@ -815,20 +791,20 @@ status_t _CompressedAsset::openChunk(int fd, off64_t offset, * * Nothing is expanded until the first read call. */ -status_t _CompressedAsset::openChunk(FileMap* dataMap, size_t uncompressedLen) +status_t _CompressedAsset::openChunk(incfs::IncFsFileMap&& dataMap, size_t uncompressedLen) { assert(mFd < 0); // no re-open - assert(mMap == NULL); + assert(!mMap.has_value()); assert(dataMap != NULL); - mMap = dataMap; + mMap = std::move(dataMap); mStart = -1; // not used - mCompressedLen = dataMap->getDataLength(); + mCompressedLen = mMap->length(); mUncompressedLen = uncompressedLen; assert(mOffset == 0); if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) { - mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen); + mZipInflater = new StreamingZipInflater(&(*mMap), uncompressedLen); } return NO_ERROR; } @@ -901,11 +877,6 @@ off64_t _CompressedAsset::seek(off64_t offset, int whence) */ void _CompressedAsset::close(void) { - if (mMap != NULL) { - delete mMap; - mMap = NULL; - } - delete[] mBuf; mBuf = NULL; @@ -940,8 +911,8 @@ const void* _CompressedAsset::getBuffer(bool) goto bail; } - if (mMap != NULL) { - if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf, + if (mMap.has_value()) { + if (!ZipUtils::inflateToBuffer(mMap->data(), buf, mUncompressedLen, mCompressedLen)) goto bail; } else { @@ -976,3 +947,6 @@ bail: return mBuf; } +incfs::map_ptr _CompressedAsset::getIncFsBuffer(bool aligned) { + return incfs::map_ptr(getBuffer(aligned)); +} diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index f7c83371f79c3..fb2b57193b836 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -917,7 +917,7 @@ Asset* AssetManager::openAssetFromFileLocked(const String8& pathName, Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, const ZipEntryRO entry, AccessMode mode, const String8& entryName) { - Asset* pAsset = NULL; + std::unique_ptr pAsset; // TODO: look for previously-created shared memory slice? uint16_t method; @@ -932,28 +932,28 @@ Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, return NULL; } - FileMap* dataMap = pZipFile->createEntryFileMap(entry); - if (dataMap == NULL) { + std::optional dataMap = pZipFile->createEntryIncFsFileMap(entry); + if (!dataMap.has_value()) { ALOGW("create map from entry failed\n"); return NULL; } if (method == ZipFileRO::kCompressStored) { - pAsset = Asset::createFromUncompressedMap(dataMap, mode); + pAsset = Asset::createFromUncompressedMap(std::move(*dataMap), mode); ALOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(), - dataMap->getFileName(), mode, pAsset); + dataMap->file_name(), mode, pAsset.get()); } else { - pAsset = Asset::createFromCompressedMap(dataMap, + pAsset = Asset::createFromCompressedMap(std::move(*dataMap), static_cast(uncompressedLen), mode); ALOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(), - dataMap->getFileName(), mode, pAsset); + dataMap->file_name(), mode, pAsset.get()); } if (pAsset == NULL) { /* unexpected */ ALOGW("create from segment failed\n"); } - return pAsset; + return pAsset.release(); } /* diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 99dd3134ff8a6..3e54dc67db763 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -38,9 +38,43 @@ namespace android { +namespace { + +using EntryValue = std::variant>; + +base::expected GetEntryValue( + incfs::verified_map_ptr table_entry) { + const uint16_t entry_size = dtohs(table_entry->size); + + // Check if the entry represents a bag value. + if (entry_size >= sizeof(ResTable_map_entry) && + (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX)) { + const auto map_entry = table_entry.convert(); + if (!map_entry) { + return base::unexpected(IOError::PAGES_MISSING); + } + return map_entry.verified(); + } + + // The entry represents a non-bag value. + const auto entry_value = table_entry.offset(entry_size).convert(); + if (!entry_value) { + return base::unexpected(IOError::PAGES_MISSING); + } + Res_value value; + value.copyFrom_dtoh(entry_value.value()); + return value; +} + +} // namespace + struct FindEntryResult { - // A pointer to the value of the resource table entry. - std::variant entry; + // The cookie representing the ApkAssets in which the value resides. + ApkAssetsCookie cookie; + + // The value of the resource table entry. Either an android::Res_value for non-bag types or an + // incfs::verified_map_ptr for bag types. + EntryValue entry; // The configuration for which the resulting entry was defined. This is already swapped to host // endianness. @@ -265,7 +299,7 @@ const std::unordered_map* } const PackageGroup& package_group = package_groups_[idx]; - if (package_group.packages_.size() == 0) { + if (package_group.packages_.empty()) { return nullptr; } @@ -310,14 +344,14 @@ bool AssetManager2::GetOverlayablesToString(const android::StringPiece& 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)) { + auto res_name = GetResourceName(*it); + if (!res_name.has_value()) { ANDROID_LOG(ERROR) << base::StringPrintf( "Unable to retrieve name of overlayable resource 0x%08x", *it); return false; } - const std::string name = ToFormattedResourceString(&res_name); + 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)); @@ -365,8 +399,8 @@ std::set AssetManager2::GetNonSystemOverlayPaths() const { return non_system_overlays; } -std::set AssetManager2::GetResourceConfigurations(bool exclude_system, - bool exclude_mipmap) const { +base::expected, IOError> AssetManager2::GetResourceConfigurations( + bool exclude_system, bool exclude_mipmap) const { ATRACE_NAME("AssetManager::GetResourceConfigurations"); const auto non_system_overlays = (exclude_system) ? GetNonSystemOverlayPaths() : std::set(); @@ -386,7 +420,10 @@ std::set AssetManager2::GetResourceConfigurations(bool exclude_ continue; } - package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); + auto result = package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); + if (UNLIKELY(!result.has_value())) { + return base::unexpected(result.error()); + } } } return configurations; @@ -501,11 +538,11 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, 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_) { +base::expected AssetManager2::FindEntry( + uint32_t resid, uint16_t density_override, bool stop_at_first_match, + bool ignore_configuration) const { + const bool logging_enabled = resource_resolution_logging_enabled_; + if (UNLIKELY(logging_enabled)) { // Clear the last logged resource resolution. ResetResourceResolution(); last_resolution_.resid = resid; @@ -523,94 +560,96 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri } // Retrieve the package group from the package id of the resource id. - if (!is_valid_resid(resid)) { + if (UNLIKELY(!is_valid_resid(resid))) { LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); - return kInvalidCookie; + return base::unexpected(std::nullopt); } 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); uint8_t package_idx = package_ids_[package_id]; - if (package_idx == 0xff) { + if (UNLIKELY(package_idx == 0xff)) { ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); - return kInvalidCookie; + return base::unexpected(std::nullopt); } const PackageGroup& package_group = package_groups_[package_idx]; - 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; + auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config, + stop_at_first_match, ignore_configuration); + if (UNLIKELY(!result.has_value())) { + return base::unexpected(result.error()); } - if (!apk_assets_[cookie]->IsLoader()) { + if (!stop_at_first_match && !ignore_configuration && !apk_assets_[result->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; - } else if (overlay_entry.IsInlineValue()) { + } + if (overlay_entry.IsInlineValue()) { // The target resource is overlaid by an inline value not represented by a resource. - out_entry->entry = overlay_entry.GetInlineValue(); - out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); - cookie = id_map.cookie; + result->entry = overlay_entry.GetInlineValue(); + result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); + result->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)) { + auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override, + false /* stop_at_first_match */, + false /* ignore_configuration */); + if (UNLIKELY(IsIOError(overlay_result))) { + return base::unexpected(overlay_result.error()); + } + if (!overlay_result.has_value()) { continue; } - if (!overlay_result.config.isBetterThan(out_entry->config, desired_config) - && overlay_result.config.compare(out_entry->config) != 0) { + if (!overlay_result->config.isBetterThan(result->config, desired_config) + && overlay_result->config.compare(result->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 = 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_) { + result->cookie = overlay_result->cookie; + result->entry = overlay_result->entry; + result->config = overlay_result->config; + result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); + + if (UNLIKELY(logging_enabled)) { last_resolution_.steps.push_back( - Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(), - overlay_result.package_name}); + 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; + if (UNLIKELY(logging_enabled)) { + last_resolution_.cookie = result->cookie; + last_resolution_.type_string_ref = result->type_string_ref; + last_resolution_.entry_string_ref = result->entry_string_ref; } - return cookie; + return result; } -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 { +base::expected 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) const { + const bool logging_enabled = resource_resolution_logging_enabled_; ApkAssetsCookie best_cookie = kInvalidCookie; const LoadedPackage* best_package = nullptr; - const ResTable_type* best_type = nullptr; + incfs::verified_map_ptr best_type; const ResTable_config* best_config = nullptr; ResTable_config best_config_copy; - uint32_t best_offset = 0u; - uint32_t type_flags = 0u; + uint32_t best_offset = 0U; + uint32_t type_flags = 0U; - Resolution::Step::Type resolution_type = Resolution::Step::Type::NO_ENTRY; + auto resolution_type = Resolution::Step::Type::NO_ENTRY; std::vector resolution_steps; // If desired_config is the same as the set configuration, then we can use our filtered list @@ -630,17 +669,20 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro continue; } + auto entry_flags = type_spec->GetFlagsForEntryIndex(entry_idx); + if (UNLIKELY(!entry_flags)) { + return base::unexpected(entry_flags.error()); + } + type_flags |= entry_flags.value(); + // 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]; - const std::vector& candidate_configs = filtered_group.configurations; - const size_t type_count = candidate_configs.size(); - for (uint32_t i = 0; i < type_count; i++) { - const ResTable_config& this_config = candidate_configs[i]; + for (const auto& type_config : filtered_group.type_configs) { + const ResTable_config& this_config = type_config.config; // We can skip calling ResTable_config::match() because we know that all candidate // configurations that do NOT match have been filtered-out. @@ -652,7 +694,7 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro } else if (package_is_loader && this_config.compare(*best_config) == 0) { resolution_type = Resolution::Step::Type::OVERLAID_LOADER; } else { - if (resource_resolution_logging_enabled_) { + if (UNLIKELY(logging_enabled)) { resolution_type = (package_is_loader) ? Resolution::Step::Type::SKIPPED_LOADER : Resolution::Step::Type::SKIPPED; resolution_steps.push_back(Resolution::Step{resolution_type, @@ -664,10 +706,13 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro // 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, entry_idx); - if (offset == ResTable_type::NO_ENTRY) { - if (resource_resolution_logging_enabled_) { + const auto& type = type_config.type; + const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx); + if (UNLIKELY(IsIOError(offset))) { + return base::unexpected(offset.error()); + } + if (!offset.has_value()) { + if (UNLIKELY(logging_enabled)) { if (package_is_loader) { resolution_type = Resolution::Step::Type::NO_ENTRY_LOADER; } else { @@ -684,9 +729,9 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro best_package = loaded_package; best_type = type; best_config = &this_config; - best_offset = offset; + best_offset = offset.value(); - if (resource_resolution_logging_enabled_) { + if (UNLIKELY(logging_enabled)) { last_resolution_.steps.push_back(Resolution::Step{resolution_type, this_config.toString(), &loaded_package->GetPackageName()}); @@ -700,10 +745,11 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro // ResTable_config, we must copy it. const auto iter_end = type_spec->types + type_spec->type_count; for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config this_config{}; + const incfs::verified_map_ptr& type = *iter; + ResTable_config this_config{}; if (!ignore_configuration) { - this_config.copyFromDtoH((*iter)->config); + this_config.copyFromDtoH(type->config); if (!this_config.match(desired_config)) { continue; } @@ -722,24 +768,27 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro // 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, entry_idx); - if (offset == ResTable_type::NO_ENTRY) { + const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx); + if (UNLIKELY(IsIOError(offset))) { + return base::unexpected(offset.error()); + } + if (!offset.has_value()) { continue; } best_cookie = cookie; best_package = loaded_package; - best_type = *iter; + best_type = type; best_config_copy = this_config; best_config = &best_config_copy; - best_offset = offset; + best_offset = offset.value(); - if (ignore_configuration) { + if (stop_at_first_match) { // Any configuration will suffice, so break. break; } - if (resource_resolution_logging_enabled_) { + if (UNLIKELY(logging_enabled)) { last_resolution_.steps.push_back(Resolution::Step{resolution_type, this_config.toString(), &loaded_package->GetPackageName()}); @@ -749,36 +798,35 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro } if (UNLIKELY(best_cookie == kInvalidCookie)) { - return kInvalidCookie; + return base::unexpected(std::nullopt); } - const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset); - if (UNLIKELY(best_entry == nullptr)) { - return kInvalidCookie; + auto best_entry_result = LoadedPackage::GetEntryFromOffset(best_type, best_offset); + if (!best_entry_result.has_value()) { + return base::unexpected(best_entry_result.error()); } - const uint16_t entry_size = dtohs(best_entry->size); - if (entry_size >= sizeof(ResTable_map_entry) && - (dtohs(best_entry->flags) & ResTable_entry::FLAG_COMPLEX)) { - // The entry represents a bag/map. - out_entry->entry = reinterpret_cast(best_entry); - } else { - // The entry represents a value. - Res_value value; - value.copyFrom_dtoh(*reinterpret_cast( - reinterpret_cast(best_entry) + entry_size)); - out_entry->entry = value; + const incfs::map_ptr best_entry = *best_entry_result; + if (!best_entry) { + return base::unexpected(IOError::PAGES_MISSING); } - 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.get(); + const auto entry = GetEntryValue(best_entry.verified()); + if (!entry.has_value()) { + return base::unexpected(entry.error()); + } - return best_cookie; + return FindEntryResult{ + .cookie = best_cookie, + .entry = *entry, + .config = *best_config, + .type_flags = type_flags, + .package_name = &best_package->GetPackageName(), + .type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1), + .entry_string_ref = StringPoolRef(best_package->GetKeyStringPool(), + best_entry->key.index), + .dynamic_ref_table = package_group.dynamic_ref_table.get(), + }; } void AssetManager2::ResetResourceResolution() const { @@ -799,30 +847,28 @@ void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) { std::string AssetManager2::GetLastResourceResolution() const { if (!resource_resolution_logging_enabled_) { LOG(ERROR) << "Must enable resource resolution logging before getting path."; - return std::string(); + return {}; } auto cookie = last_resolution_.cookie; if (cookie == kInvalidCookie) { LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path."; - return std::string(); + return {}; } uint32_t resid = last_resolution_.resid; std::vector& steps = last_resolution_.steps; - - ResourceName resource_name; std::string resource_name_string; const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); if (package != nullptr) { - ToResourceName(last_resolution_.type_string_ref, - last_resolution_.entry_string_ref, - package->GetPackageName(), - &resource_name); - resource_name_string = ToFormattedResourceString(&resource_name); + auto resource_name = ToResourceName(last_resolution_.type_string_ref, + last_resolution_.entry_string_ref, + package->GetPackageName()); + resource_name_string = resource_name.has_value() ? + ToFormattedResourceString(resource_name.value()) : ""; } std::stringstream log_stream; @@ -875,200 +921,186 @@ std::string AssetManager2::GetLastResourceResolution() const { return log_stream.str(); } -bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { - FindEntryResult entry; - ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, - true /* stop_at_first_match */, - true /* ignore_configuration */, &entry); - if (cookie == kInvalidCookie) { - return false; +base::expected AssetManager2::GetResourceName( + uint32_t resid) const { + auto result = FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, + true /* ignore_configuration */); + if (!result.has_value()) { + return base::unexpected(result.error()); } - return ToResourceName(entry.type_string_ref, - entry.entry_string_ref, - *entry.package_name, - out_name); + return ToResourceName(result->type_string_ref, + result->entry_string_ref, + *result->package_name); } -bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { - FindEntryResult entry; - ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, - false /* stop_at_first_match */, - true /* ignore_configuration */, &entry); - if (cookie != kInvalidCookie) { - *out_flags = entry.type_flags; - return true; - } - return false; -} - -ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, - uint16_t density_override, Res_value* out_value, - ResTable_config* out_selected_config, - uint32_t* out_flags) const { - FindEntryResult entry; - ApkAssetsCookie cookie = FindEntry(resid, density_override, false /* stop_at_first_match */, - false /* ignore_configuration */, &entry); - if (cookie == kInvalidCookie) { - return kInvalidCookie; +base::expected AssetManager2::GetResource( + uint32_t resid, bool may_be_bag, uint16_t density_override) const { + auto result = FindEntry(resid, density_override, false /* stop_at_first_match */, + false /* ignore_configuration */); + if (!result.has_value()) { + return base::unexpected(result.error()); } - auto result_map_entry = std::get_if(&entry.entry); + auto result_map_entry = std::get_if>(&result->entry); if (result_map_entry != nullptr) { if (!may_be_bag) { LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid); - return kInvalidCookie; + return base::unexpected(std::nullopt); } // Create a reference since we can't represent this complex type as a Res_value. - out_value->dataType = Res_value::TYPE_REFERENCE; - out_value->data = resid; - *out_selected_config = entry.config; - *out_flags = entry.type_flags; - return cookie; + return SelectedValue(Res_value::TYPE_REFERENCE, resid, result->cookie, result->type_flags, + resid, result->config); } // Convert the package ID to the runtime assigned package ID. - *out_value = std::get(entry.entry); - entry.dynamic_ref_table->lookupResourceValue(out_value); + Res_value value = std::get(result->entry); + result->dynamic_ref_table->lookupResourceValue(&value); - *out_selected_config = entry.config; - *out_flags = entry.type_flags; - return cookie; + return SelectedValue(value.dataType, value.data, result->cookie, result->type_flags, + resid, result->config); } -ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, - ResTable_config* in_out_selected_config, - uint32_t* in_out_flags, - uint32_t* out_last_reference) const { - constexpr const int kMaxIterations = 20; - - for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && - in_out_value->data != 0u && iteration < kMaxIterations; - iteration++) { - *out_last_reference = in_out_value->data; - uint32_t new_flags = 0u; - cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/, - in_out_value, in_out_selected_config, &new_flags); - if (cookie == kInvalidCookie) { - return kInvalidCookie; - } - if (in_out_flags != nullptr) { - *in_out_flags |= new_flags; - } - if (*out_last_reference == in_out_value->data) { - // This reference can't be resolved, so exit now and let the caller deal with it. - return cookie; - } +base::expected AssetManager2::ResolveReference( + AssetManager2::SelectedValue& value) const { + if (value.type != Res_value::TYPE_REFERENCE || value.data == 0U) { + // Not a reference. Nothing to do. + return {}; + } + + uint32_t combined_flags = value.flags; + uint32_t resolve_resid = value.data; + constexpr const uint32_t kMaxIterations = 20; + for (uint32_t i = 0U;; i++) { + auto result = GetResource(resolve_resid, true /*may_be_bag*/); + if (!result.has_value()) { + return base::unexpected(result.error()); + } + + if (result->type != Res_value::TYPE_REFERENCE || + result->data == Res_value::DATA_NULL_UNDEFINED || + result->data == resolve_resid || i == kMaxIterations) { + // This reference can't be resolved, so exit now and let the caller deal with it. + value = *result; + value.flags |= combined_flags; + return {}; + } + + combined_flags |= result->flags; + resolve_resid = result->data; } - return cookie; } -const std::vector AssetManager2::GetBagResIdStack(uint32_t resid) { +const std::vector AssetManager2::GetBagResIdStack(uint32_t resid) const { auto cached_iter = cached_bag_resid_stacks_.find(resid); if (cached_iter != cached_bag_resid_stacks_.end()) { return cached_iter->second; - } else { - auto found_resids = std::vector(); - GetBag(resid, found_resids); - // Cache style stacks if they are not already cached. - cached_bag_resid_stacks_[resid] = found_resids; - return found_resids; } + + std::vector found_resids; + GetBag(resid, found_resids); + cached_bag_resid_stacks_.emplace(resid, found_resids); + return found_resids; } -const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { - auto found_resids = std::vector(); - auto bag = GetBag(resid, found_resids); +base::expected AssetManager2::ResolveBag( + AssetManager2::SelectedValue& value) const { + if (UNLIKELY(value.type != Res_value::TYPE_REFERENCE)) { + return base::unexpected(std::nullopt); + } - // Cache style stacks if they are not already cached. - auto cached_iter = cached_bag_resid_stacks_.find(resid); - if (cached_iter == cached_bag_resid_stacks_.end()) { - cached_bag_resid_stacks_[resid] = found_resids; + auto bag = GetBag(value.data); + if (bag.has_value()) { + value.flags |= (*bag)->type_spec_flags; } return bag; } -static bool compare_bag_entries(const ResolvedBag::Entry& entry1, - const ResolvedBag::Entry& entry2) { - return entry1.key < entry2.key; +base::expected AssetManager2::GetBag(uint32_t resid) const { + std::vector found_resids; + const auto bag = GetBag(resid, found_resids); + cached_bag_resid_stacks_.emplace(resid, found_resids); + return bag; } -const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& child_resids) { - auto cached_iter = cached_bags_.find(resid); - if (cached_iter != cached_bags_.end()) { +base::expected AssetManager2::GetBag( + uint32_t resid, std::vector& child_resids) const { + if (auto cached_iter = cached_bags_.find(resid); cached_iter != cached_bags_.end()) { return cached_iter->second.get(); } - FindEntryResult entry; - ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, - false /* stop_at_first_match */, - false /* ignore_configuration */, - &entry); - if (cookie == kInvalidCookie) { - return nullptr; + auto entry = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, + false /* ignore_configuration */); + if (!entry.has_value()) { + return base::unexpected(entry.error()); } - auto result_map_entry = std::get_if(&entry.entry); - if (result_map_entry == nullptr) { + auto entry_map = std::get_if>(&entry->entry); + if (entry_map == nullptr) { // Not a bag, nothing to do. - return nullptr; + return base::unexpected(std::nullopt); } - auto map = reinterpret_cast(*result_map_entry); - auto map_entry = reinterpret_cast( - reinterpret_cast(map) + map->size); - const ResTable_map* const map_entry_end = map_entry + dtohl(map->count); + auto map = *entry_map; + auto map_entry = map.offset(dtohs(map->size)).convert(); + const auto map_entry_end = map_entry + dtohl(map->count); // Keep track of ids that have already been seen to prevent infinite loops caused by circular - // dependencies between bags + // dependencies between bags. child_resids.push_back(resid); uint32_t parent_resid = dtohl(map->parent.ident); - if (parent_resid == 0U || std::find(child_resids.begin(), child_resids.end(), parent_resid) - != child_resids.end()) { - // 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. + if (parent_resid == 0U || + std::find(child_resids.begin(), child_resids.end(), parent_resid) != child_resids.end()) { + // There is no parent or a circular parental 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 new_bag{reinterpret_cast( 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) { + for (auto new_entry = new_bag->entries; map_entry != map_entry_end; ++map_entry) { + if (UNLIKELY(!map_entry)) { + return base::unexpected(IOError::PAGES_MISSING); + } + uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { // Attributes, arrays, etc don't have a resource id as the name. They specify // other data, which would be wrong to change via a lookup. - if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { + if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR)) { LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); - return nullptr; + return base::unexpected(std::nullopt); } } - new_entry->cookie = cookie; + + new_entry->cookie = entry->cookie; new_entry->key = new_key; new_entry->key_pool = nullptr; new_entry->type_pool = nullptr; new_entry->style = resid; new_entry->value.copyFrom_dtoh(map_entry->value); - status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value); - if (err != NO_ERROR) { + status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value); + if (UNLIKELY(err != NO_ERROR)) { LOG(ERROR) << base::StringPrintf( "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType, new_entry->value.data, new_key); - return nullptr; + return base::unexpected(std::nullopt); } + 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); + std::sort(new_bag->entries, new_bag->entries + entry_count, + [](auto&& lhs, auto&& rhs) { return lhs.key < rhs.key; }); } - new_bag->type_spec_flags = entry.type_flags; + new_bag->type_spec_flags = entry->type_flags; new_bag->entry_count = static_cast(entry_count); ResolvedBag* result = new_bag.get(); cached_bags_[resid] = std::move(new_bag); @@ -1076,54 +1108,58 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& } // In case the parent is a dynamic reference, resolve it. - entry.dynamic_ref_table->lookupResourceId(&parent_resid); + entry->dynamic_ref_table->lookupResourceId(&parent_resid); // Get the parent and do a merge of the keys. - const ResolvedBag* parent_bag = GetBag(parent_resid, child_resids); - if (parent_bag == nullptr) { + const auto parent_bag = GetBag(parent_resid, child_resids); + if (UNLIKELY(!parent_bag.has_value())) { // Failed to get the parent that should exist. LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid); - return nullptr; + return base::unexpected(parent_bag.error()); } // Create the max possible entries we can make. Once we construct the bag, // we will realloc to fit to size. - const size_t max_count = parent_bag->entry_count + dtohl(map->count); + const size_t max_count = (*parent_bag)->entry_count + dtohl(map->count); util::unique_cptr new_bag{reinterpret_cast( malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))))}; ResolvedBag::Entry* new_entry = new_bag->entries; - const ResolvedBag::Entry* parent_entry = parent_bag->entries; - const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count; + const ResolvedBag::Entry* parent_entry = (*parent_bag)->entries; + 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) { + if (UNLIKELY(!map_entry)) { + return base::unexpected(IOError::PAGES_MISSING); + } + uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { - if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) { + if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR)) { LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid); - return nullptr; + return base::unexpected(std::nullopt); } } if (child_key <= parent_entry->key) { // Use the child key if it comes before the parent // or is equal to the parent (overrides). - new_entry->cookie = cookie; + new_entry->cookie = entry->cookie; new_entry->key = child_key; new_entry->key_pool = nullptr; new_entry->type_pool = nullptr; new_entry->value.copyFrom_dtoh(map_entry->value); new_entry->style = resid; - status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value); - if (err != NO_ERROR) { + status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value); + if (UNLIKELY(err != NO_ERROR)) { LOG(ERROR) << base::StringPrintf( "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType, new_entry->value.data, child_key); - return nullptr; + return base::unexpected(std::nullopt); } ++map_entry; } else { @@ -1143,25 +1179,29 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& // Finish the child entries if they exist. while (map_entry != map_entry_end) { + if (UNLIKELY(!map_entry)) { + return base::unexpected(IOError::PAGES_MISSING); + } + uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { - if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { + if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR)) { LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); - return nullptr; + return base::unexpected(std::nullopt); } } - new_entry->cookie = cookie; + new_entry->cookie = entry->cookie; new_entry->key = new_key; new_entry->key_pool = nullptr; new_entry->type_pool = nullptr; new_entry->value.copyFrom_dtoh(map_entry->value); new_entry->style = resid; - status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value); - if (err != NO_ERROR) { + status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value); + if (UNLIKELY(err != NO_ERROR)) { LOG(ERROR) << base::StringPrintf("Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType, new_entry->value.data, new_key); - return nullptr; + return base::unexpected(std::nullopt); } sort_entries = sort_entries || (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key)); @@ -1185,11 +1225,12 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& } if (sort_entries) { - std::sort(new_bag->entries, new_bag->entries + actual_count, compare_bag_entries); + std::sort(new_bag->entries, new_bag->entries + actual_count, + [](auto&& lhs, auto&& rhs) { return lhs.key < rhs.key; }); } // Combine flags from the parent and our own bag. - new_bag->type_spec_flags = entry.type_flags | parent_bag->type_spec_flags; + new_bag->type_spec_flags = entry->type_flags | (*parent_bag)->type_spec_flags; new_bag->entry_count = static_cast(actual_count); ResolvedBag* result = new_bag.get(); cached_bags_[resid] = std::move(new_bag); @@ -1208,16 +1249,16 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { return true; } -uint32_t AssetManager2::GetResourceId(const std::string& resource_name, - const std::string& fallback_type, - const std::string& fallback_package) const { +base::expected AssetManager2::GetResourceId( + const std::string& resource_name, const std::string& fallback_type, + const std::string& fallback_package) const { StringPiece package_name, type, entry; if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { - return 0u; + return base::unexpected(std::nullopt); } if (entry.empty()) { - return 0u; + return base::unexpected(std::nullopt); } if (package_name.empty()) { @@ -1230,12 +1271,12 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, std::u16string type16; if (!Utf8ToUtf16(type, &type16)) { - return 0u; + return base::unexpected(std::nullopt); } std::u16string entry16; if (!Utf8ToUtf16(entry, &entry16)) { - return 0u; + return base::unexpected(std::nullopt); } const StringPiece16 kAttr16 = u"attr"; @@ -1249,20 +1290,24 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, break; } - uint32_t resid = package->FindEntryByName(type16, entry16); - if (resid == 0u && kAttr16 == type16) { + base::expected resid = package->FindEntryByName(type16, entry16); + if (UNLIKELY(IsIOError(resid))) { + return base::unexpected(resid.error()); + } + + if (!resid.has_value() && kAttr16 == type16) { // Private attributes in libraries (such as the framework) are sometimes encoded // under the type '^attr-private' in order to leave the ID space of public 'attr' // free for future additions. Check '^attr-private' for the same name. resid = package->FindEntryByName(kAttrPrivate16, entry16); } - if (resid != 0u) { - return fix_package_id(resid, package_group.dynamic_ref_table->mAssignedPackageId); + if (resid.has_value()) { + return fix_package_id(*resid, package_group.dynamic_ref_table->mAssignedPackageId); } } } - return 0u; + return base::unexpected(std::nullopt); } void AssetManager2::RebuildFilterList(bool filter_incompatible_configs) { @@ -1282,8 +1327,7 @@ void AssetManager2::RebuildFilterList(bool filter_incompatible_configs) { ResTable_config this_config; this_config.copyFromDtoH((*iter)->config); if (!filter_incompatible_configs || this_config.match(configuration_)) { - group.configurations.push_back(this_config); - group.types.push_back(*iter); + group.type_configs.push_back(TypeConfig{*iter, this_config}); } } }); @@ -1354,16 +1398,16 @@ struct Theme::Package { std::array, kTypeCount> types; }; -bool Theme::ApplyStyle(uint32_t resid, bool force) { +base::expected Theme::ApplyStyle(uint32_t resid, bool force) { ATRACE_NAME("Theme::ApplyStyle"); - const ResolvedBag* bag = asset_manager_->GetBag(resid); - if (bag == nullptr) { - return false; + auto bag = asset_manager_->GetBag(resid); + if (!bag.has_value()) { + return base::unexpected(bag.error()); } // Merge the flags from this style. - type_spec_flags_ |= bag->type_spec_flags; + type_spec_flags_ |= (*bag)->type_spec_flags; int last_type_idx = -1; int last_package_idx = -1; @@ -1373,14 +1417,14 @@ bool Theme::ApplyStyle(uint32_t resid, bool force) { // Iterate backwards, because each bag is sorted in ascending key ID order, meaning we will only // need to perform one resize per type. using reverse_bag_iterator = std::reverse_iterator; - const auto bag_iter_end = reverse_bag_iterator(begin(bag)); - for (auto bag_iter = reverse_bag_iterator(end(bag)); bag_iter != bag_iter_end; ++bag_iter) { - const uint32_t attr_resid = bag_iter->key; + const auto rbegin = reverse_bag_iterator(begin(*bag)); + for (auto it = reverse_bag_iterator(end(*bag)); it != rbegin; ++it) { + const uint32_t attr_resid = it->key; // If the resource ID passed in is not a style, the key can be some other identifier that is not // a resource ID. We should fail fast instead of operating with strange resource IDs. if (!is_valid_resid(attr_resid)) { - return false; + return base::unexpected(std::nullopt); } // We don't use the 0-based index for the type so that we can avoid doing ID validation @@ -1428,20 +1472,18 @@ bool Theme::ApplyStyle(uint32_t resid, bool force) { ThemeEntry& entry = last_type->entries[entry_idx]; if (force || (entry.value.dataType == Res_value::TYPE_NULL && entry.value.data != Res_value::DATA_NULL_EMPTY)) { - entry.cookie = bag_iter->cookie; - entry.type_spec_flags |= bag->type_spec_flags; - entry.value = bag_iter->value; + entry.cookie = it->cookie; + entry.type_spec_flags |= (*bag)->type_spec_flags; + entry.value = it->value; } } - return true; + return {}; } -ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, - uint32_t* out_flags) const { +std::optional Theme::GetAttribute(uint32_t resid) const { + int cnt = 20; - uint32_t type_spec_flags = 0u; - do { const int package_idx = get_package_id(resid); const Package* package = packages_[package_idx].get(); @@ -1461,43 +1503,42 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, resid = entry.value.data; continue; } - return kInvalidCookie; + return std::nullopt; } // @null is different than @empty. if (entry.value.dataType == Res_value::TYPE_NULL && entry.value.data != Res_value::DATA_NULL_EMPTY) { - return kInvalidCookie; + return std::nullopt; } - *out_value = entry.value; - *out_flags = type_spec_flags; - return entry.cookie; + return AssetManager2::SelectedValue(entry.value.dataType, entry.value.data, entry.cookie, + type_spec_flags, 0U /* resid */, {} /* config */); } } } break; } while (true); - return kInvalidCookie; + return std::nullopt; } -ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, - ResTable_config* in_out_selected_config, - uint32_t* in_out_type_spec_flags, - uint32_t* out_last_ref) const { - if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) { - uint32_t new_flags; - cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags); - if (cookie == kInvalidCookie) { - return kInvalidCookie; - } - - if (in_out_type_spec_flags != nullptr) { - *in_out_type_spec_flags |= new_flags; - } +base::expected Theme::ResolveAttributeReference( + AssetManager2::SelectedValue& value) const { + if (value.type != Res_value::TYPE_ATTRIBUTE) { + return asset_manager_->ResolveReference(value); } - return asset_manager_->ResolveReference(cookie, in_out_value, in_out_selected_config, - in_out_type_spec_flags, out_last_ref); + + std::optional result = GetAttribute(value.data); + if (!result.has_value()) { + return base::unexpected(std::nullopt); + } + + auto resolve_result = asset_manager_->ResolveReference(*result); + if (resolve_result.has_value()) { + result->flags |= value.flags; + value = *result; + } + return resolve_result; } void Theme::Clear() { @@ -1507,9 +1548,9 @@ void Theme::Clear() { } } -void Theme::SetTo(const Theme& o) { +base::expected Theme::SetTo(const Theme& o) { if (this == &o) { - return; + return {}; } type_spec_flags_ = o.type_spec_flags_; @@ -1560,10 +1601,8 @@ void Theme::SetTo(const Theme& o) { // Map the runtime package of the source apk asset to the destination apk asset. if (src_asset->GetPath() == dest_asset->GetPath()) { - const std::vector>& src_packages = - src_asset->GetLoadedArsc()->GetPackages(); - const std::vector>& dest_packages = - dest_asset->GetLoadedArsc()->GetPackages(); + const auto& src_packages = src_asset->GetLoadedArsc()->GetPackages(); + const auto& dest_packages = dest_asset->GetLoadedArsc()->GetPackages(); SourceToDestinationRuntimePackageMap package_map; @@ -1660,15 +1699,20 @@ void Theme::SetTo(const Theme& o) { int attribute_dest_package_id = p; if (attribute_dest_package_id != 0x01) { // Find the cookie of the attribute resource id in the source AssetManager - FindEntryResult attribute_entry_result; - ApkAssetsCookie attribute_cookie = + base::expected attribute_entry_result = o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ , true /* stop_at_first_match */, - true /* ignore_configuration */, - &attribute_entry_result); + true /* ignore_configuration */); + if (UNLIKELY(IsIOError(attribute_entry_result))) { + return base::unexpected(GetIOError(attribute_entry_result.error())); + } + if (!attribute_entry_result.has_value()) { + continue; + } // Determine the package id of the attribute in the destination AssetManager. - auto attribute_package_map = src_asset_cookie_id_map.find(attribute_cookie); + auto attribute_package_map = src_asset_cookie_id_map.find( + attribute_entry_result->cookie); if (attribute_package_map == src_asset_cookie_id_map.end()) { continue; } @@ -1712,6 +1756,7 @@ void Theme::SetTo(const Theme& o) { } } } + return {}; } void Theme::Dump() const { diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index e62fb614e1950..71919fdbb7361 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -24,9 +24,12 @@ #include "androidfw/AttributeFinder.h" constexpr bool kDebugStyles = false; +#define DEBUG_LOG(...) do { if (kDebugStyles) { ALOGI(__VA_ARGS__); } } while(0) namespace android { +namespace { + // Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { return cookie != kInvalidCookie ? static_cast(cookie + 1) : static_cast(-1); @@ -61,136 +64,149 @@ class BagAttributeFinder } }; -bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, - uint32_t* src_values, size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { - if (kDebugStyles) { - ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, - def_style_attr, def_style_res); - } - - AssetManager2* assetmanager = theme->GetAssetManager(); - ResTable_config config; - Res_value value; - - int indices_idx = 0; - - // Load default style from attribute, if specified... - uint32_t def_style_flags = 0u; - if (def_style_attr != 0) { - Res_value value; - if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { - if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_res = value.data; +base::expected GetStyleBag(Theme* theme, + uint32_t theme_attribute_resid, + uint32_t fallback_resid, + uint32_t* out_theme_flags) { + // Load the style from the attribute if specified. + if (theme_attribute_resid != 0U) { + std::optional value = theme->GetAttribute(theme_attribute_resid); + if (value.has_value()) { + *out_theme_flags |= value->flags; + auto result = theme->GetAssetManager()->ResolveBag(*value); + if (result.has_value() || IsIOError(result)) { + return result; } } } - // Retrieve the default style bag, if requested. - const ResolvedBag* default_style_bag = nullptr; - if (def_style_res != 0) { - default_style_bag = assetmanager->GetBag(def_style_res); - if (default_style_bag != nullptr) { - def_style_flags |= default_style_bag->type_spec_flags; + // Fallback to loading the style from the resource id if specified. + if (fallback_resid != 0U) { + return theme->GetAssetManager()->GetBag(fallback_resid); + } + + return base::unexpected(std::nullopt); +} + +base::expected GetXmlStyleBag(Theme* theme, + ResXMLParser* xml_parser, + uint32_t* out_theme_flags) { + if (xml_parser == nullptr) { + return base::unexpected(std::nullopt); + } + + // Retrieve the style resource ID associated with the current XML tag's style attribute. + Res_value value; + const ssize_t idx = xml_parser->indexOfStyle(); + if (idx < 0 || xml_parser->getAttributeValue(idx, &value) < 0) { + return base::unexpected(std::nullopt); + } + + if (value.dataType == Res_value::TYPE_ATTRIBUTE) { + // Resolve the attribute with out theme. + if (std::optional result = theme->GetAttribute(value.data)) { + *out_theme_flags |= result->flags; + return theme->GetAssetManager()->ResolveBag(*result); } } - BagAttributeFinder def_style_attr_finder(default_style_bag); + if (value.dataType == Res_value::TYPE_REFERENCE) { + return theme->GetAssetManager()->GetBag(value.data); + } + + return base::unexpected(std::nullopt); +} + +} // namespace + +base::expected ResolveAttrs(Theme* theme, uint32_t def_style_attr, + uint32_t def_style_res, uint32_t* src_values, + size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, + uint32_t* out_indices) { + DEBUG_LOG("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, + def_style_res); + + int indices_idx = 0; + const AssetManager2* assetmanager = theme->GetAssetManager(); + + // Load default style from attribute or resource id, if specified... + uint32_t def_style_theme_flags = 0U; + const auto default_style_bag = GetStyleBag(theme, def_style_attr, def_style_res, + &def_style_theme_flags); + if (UNLIKELY(IsIOError(default_style_bag))) { + return base::unexpected(GetIOError(default_style_bag.error())); + } + + BagAttributeFinder def_style_attr_finder(default_style_bag.value_or(nullptr)); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - - if (kDebugStyles) { - ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); - } - - ApkAssetsCookie cookie = kInvalidCookie; - uint32_t type_set_flags = 0; - - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - config.density = 0; + DEBUG_LOG("RETRIEVING ATTR 0x%08x...", cur_ident); // Try to find a value for this attribute... we prioritize values // coming from, first XML attributes, then XML style, then default // style, and finally the theme. // Retrieve the current input value if available. + AssetManager2::SelectedValue value{}; if (src_values_length > 0 && src_values[ii] != 0) { - value.dataType = Res_value::TYPE_ATTRIBUTE; + value.type = Res_value::TYPE_ATTRIBUTE; value.data = src_values[ii]; - if (kDebugStyles) { - ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data); - } + DEBUG_LOG("-> From values: type=0x%x, data=0x%08x", value.type, value.data); } else { const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident); if (entry != def_style_attr_finder.end()) { - cookie = entry->cookie; - type_set_flags = def_style_flags; - value = entry->value; - if (kDebugStyles) { - ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); - } + value = AssetManager2::SelectedValue(*default_style_bag, *entry); + value.flags |= def_style_theme_flags; + DEBUG_LOG("-> From def style: type=0x%x, data=0x%08x", value.type, value.data); } } - uint32_t resid = 0; - if (value.dataType != Res_value::TYPE_NULL) { + if (value.type != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } - if (kDebugStyles) { - ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); + const auto result = theme->ResolveAttributeReference(value); + if (UNLIKELY(IsIOError(result))) { + return base::unexpected(GetIOError(result.error())); } + DEBUG_LOG("-> Resolved attr: type=0x%x, data=0x%08x", value.type, value.data); } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); - if (new_cookie != kInvalidCookie) { - if (kDebugStyles) { - ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); - } - new_cookie = - assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } - if (kDebugStyles) { - ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); + if (auto attr_value = theme->GetAttribute(cur_ident)) { + value = *attr_value; + DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data); + + const auto result = assetmanager->ResolveReference(value); + if (UNLIKELY(IsIOError(result))) { + return base::unexpected(GetIOError(result.error())); } + DEBUG_LOG("-> Resolved theme: type=0x%x, data=0x%08x", value.type, value.data); } } // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - if (kDebugStyles) { - ALOGI("-> Setting to @null!"); - } - value.dataType = Res_value::TYPE_NULL; + if (value.type == Res_value::TYPE_REFERENCE && value.data == 0) { + DEBUG_LOG("-> Setting to @null!"); + value.type = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + value.cookie = kInvalidCookie; } - if (kDebugStyles) { - ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data); - } + DEBUG_LOG("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.type, value.data); // Write the final value back to Java. - out_values[STYLE_TYPE] = value.dataType; + out_values[STYLE_TYPE] = value.type; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); - out_values[STYLE_RESOURCE_ID] = resid; - out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; - out_values[STYLE_DENSITY] = config.density; + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie); + out_values[STYLE_RESOURCE_ID] = value.resid; + out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags; + out_values[STYLE_DENSITY] = value.config.density; if (out_indices != nullptr && - (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) { - indices_idx++; - out_indices[indices_idx] = ii; + (value.type != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) { + out_indices[++indices_idx] = ii; } out_values += STYLE_NUM_ENTRIES; @@ -199,93 +215,46 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, if (out_indices != nullptr) { out_indices[0] = indices_idx; } - return true; + return {}; } -void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, - uint32_t* out_values, uint32_t* out_indices) { - if (kDebugStyles) { - ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, - def_style_attr, def_style_resid, xml_parser); - } - - AssetManager2* assetmanager = theme->GetAssetManager(); - ResTable_config config; - Res_value value; +base::expected ApplyStyle(Theme* theme, ResXMLParser* xml_parser, + uint32_t def_style_attr, + uint32_t def_style_resid, + const uint32_t* attrs, size_t attrs_length, + uint32_t* out_values, uint32_t* out_indices) { + DEBUG_LOG("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, + def_style_attr, def_style_resid, xml_parser); int indices_idx = 0; + const AssetManager2* assetmanager = theme->GetAssetManager(); // Load default style from attribute, if specified... - uint32_t def_style_flags = 0u; - if (def_style_attr != 0) { - Res_value value; - if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { - if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_resid = value.data; - } - } + uint32_t def_style_theme_flags = 0U; + const auto default_style_bag = GetStyleBag(theme, def_style_attr, def_style_resid, + &def_style_theme_flags); + if (IsIOError(default_style_bag)) { + return base::unexpected(GetIOError(default_style_bag.error())); } // Retrieve the style resource ID associated with the current XML tag's style attribute. - uint32_t style_resid = 0u; - uint32_t style_flags = 0u; - if (xml_parser != nullptr) { - ssize_t idx = xml_parser->indexOfStyle(); - if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { - if (value.dataType == value.TYPE_ATTRIBUTE) { - // Resolve the attribute with out theme. - if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) { - value.dataType = Res_value::TYPE_NULL; - } - } - - if (value.dataType == value.TYPE_REFERENCE) { - style_resid = value.data; - } - } + uint32_t xml_style_theme_flags = 0U; + const auto xml_style_bag = GetXmlStyleBag(theme, xml_parser, &def_style_theme_flags); + if (IsIOError(xml_style_bag)) { + return base::unexpected(GetIOError(xml_style_bag.error())); } - // Retrieve the default style bag, if requested. - const ResolvedBag* default_style_bag = nullptr; - if (def_style_resid != 0) { - default_style_bag = assetmanager->GetBag(def_style_resid); - if (default_style_bag != nullptr) { - def_style_flags |= default_style_bag->type_spec_flags; - } - } - - BagAttributeFinder def_style_attr_finder(default_style_bag); - - // Retrieve the style class bag, if requested. - const ResolvedBag* xml_style_bag = nullptr; - if (style_resid != 0) { - xml_style_bag = assetmanager->GetBag(style_resid); - if (xml_style_bag != nullptr) { - style_flags |= xml_style_bag->type_spec_flags; - } - } - - BagAttributeFinder xml_style_attr_finder(xml_style_bag); - - // Retrieve the XML attributes, if requested. + BagAttributeFinder def_style_attr_finder(default_style_bag.value_or(nullptr)); + BagAttributeFinder xml_style_attr_finder(xml_style_bag.value_or(nullptr)); XmlAttributeFinder xml_attr_finder(xml_parser); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; + DEBUG_LOG("RETRIEVING ATTR 0x%08x...", cur_ident); - if (kDebugStyles) { - ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); - } - - ApkAssetsCookie cookie = kInvalidCookie; - uint32_t type_set_flags = 0u; - - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - config.density = 0; + AssetManager2::SelectedValue value{}; uint32_t value_source_resid = 0; // Try to find a value for this attribute... we prioritize values @@ -296,178 +265,152 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); if (xml_attr_idx != xml_attr_finder.end()) { // We found the attribute we were looking for. - xml_parser->getAttributeValue(xml_attr_idx, &value); - if (kDebugStyles) { - ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, value.data); - } + Res_value attribute_value; + xml_parser->getAttributeValue(xml_attr_idx, &attribute_value); + value.type = attribute_value.dataType; + value.data = attribute_value.data; value_source_resid = xml_parser->getSourceResourceId(); + DEBUG_LOG("-> From XML: type=0x%x, data=0x%08x", value.type, value.data); } - if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { + if (value.type == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the style class values looking for the requested attribute. const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); if (entry != xml_style_attr_finder.end()) { - // We found the attribute we were looking for. - cookie = entry->cookie; - type_set_flags = style_flags; - value = entry->value; + value = AssetManager2::SelectedValue(*xml_style_bag, *entry); + value.flags |= xml_style_theme_flags; value_source_resid = entry->style; - if (kDebugStyles) { - ALOGI("-> From style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data, - entry->style); - } + DEBUG_LOG("-> From style: type=0x%x, data=0x%08x, style=0x%08x", value.type, value.data, + value_source_resid); } } - if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { + if (value.type == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the default style values looking for the requested attribute. const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); if (entry != def_style_attr_finder.end()) { - // We found the attribute we were looking for. - cookie = entry->cookie; - type_set_flags = def_style_flags; - value = entry->value; - if (kDebugStyles) { - ALOGI("-> From def style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data, - entry->style); - } + value = AssetManager2::SelectedValue(*default_style_bag, *entry); + value.flags |= def_style_theme_flags; value_source_resid = entry->style; + DEBUG_LOG("-> From def style: type=0x%x, data=0x%08x, style=0x%08x", value.type, value.data, + entry->style); } } - uint32_t resid = 0u; - if (value.dataType != Res_value::TYPE_NULL) { + if (value.type != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } - - if (kDebugStyles) { - ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); + auto result = theme->ResolveAttributeReference(value); + if (UNLIKELY(IsIOError(result))) { + return base::unexpected(GetIOError(result.error())); } + DEBUG_LOG("-> Resolved attr: type=0x%x, data=0x%08x", value.type, value.data); } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); - // TODO: set value_source_resid for the style in the theme that was used. - if (new_cookie != kInvalidCookie) { - if (kDebugStyles) { - ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); - } - new_cookie = - assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + if (auto attr_value = theme->GetAttribute(cur_ident)) { + value = *attr_value; + DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data); - if (kDebugStyles) { - ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); + auto result = assetmanager->ResolveReference(value); + if (UNLIKELY(IsIOError(result))) { + return base::unexpected(GetIOError(result.error())); } + DEBUG_LOG("-> Resolved theme: type=0x%x, data=0x%08x", value.type, value.data); + // TODO: set value_source_resid for the style in the theme that was used. } } // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - if (kDebugStyles) { - ALOGI("-> Setting to @null!"); - } - value.dataType = Res_value::TYPE_NULL; + if (value.type == Res_value::TYPE_REFERENCE && value.data == 0U) { + DEBUG_LOG("-> Setting to @null!"); + value.type = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + value.cookie = kInvalidCookie; } - if (kDebugStyles) { - ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data); - } + DEBUG_LOG("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.type, value.data); // Write the final value back to Java. - out_values[STYLE_TYPE] = value.dataType; + out_values[STYLE_TYPE] = value.type; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); - out_values[STYLE_RESOURCE_ID] = resid; - out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; - out_values[STYLE_DENSITY] = config.density; + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie); + out_values[STYLE_RESOURCE_ID] = value.resid; + out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags; + out_values[STYLE_DENSITY] = value.config.density; out_values[STYLE_SOURCE_RESOURCE_ID] = value_source_resid; - if (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) { - indices_idx++; - + if (value.type != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) { // out_indices must NOT be nullptr. - out_indices[indices_idx] = ii; + out_indices[++indices_idx] = ii; } out_values += STYLE_NUM_ENTRIES; } // out_indices must NOT be nullptr. out_indices[0] = indices_idx; + return {}; } -bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { - ResTable_config config; - Res_value value; - +base::expected RetrieveAttributes(AssetManager2* assetmanager, + ResXMLParser* xml_parser, + uint32_t* attrs, + size_t attrs_length, + uint32_t* out_values, + uint32_t* out_indices) { int indices_idx = 0; // Retrieve the XML attributes, if requested. - const size_t xml_attr_count = xml_parser->getAttributeCount(); size_t ix = 0; + const size_t xml_attr_count = xml_parser->getAttributeCount(); uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - ApkAssetsCookie cookie = kInvalidCookie; - uint32_t type_set_flags = 0u; - - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - config.density = 0; + AssetManager2::SelectedValue value{}; // Try to find a value for this attribute... // Skip through XML attributes until the end or the next possible match. while (ix < xml_attr_count && cur_ident > cur_xml_attr) { - ix++; - cur_xml_attr = xml_parser->getAttributeNameResID(ix); - } - // Retrieve the current XML attribute if it matches, and step to next. - if (ix < xml_attr_count && cur_ident == cur_xml_attr) { - xml_parser->getAttributeValue(ix, &value); - ix++; - cur_xml_attr = xml_parser->getAttributeNameResID(ix); + cur_xml_attr = xml_parser->getAttributeNameResID(++ix); } - uint32_t resid = 0u; - if (value.dataType != Res_value::TYPE_NULL) { + // Retrieve the current XML attribute if it matches, and step to next. + if (ix < xml_attr_count && cur_ident == cur_xml_attr) { + Res_value attribute_value; + xml_parser->getAttributeValue(ix, &attribute_value); + value.type = attribute_value.dataType; + value.data = attribute_value.data; + cur_xml_attr = xml_parser->getAttributeNameResID(++ix); + } + + if (value.type != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; + auto result = assetmanager->ResolveReference(value); + if (UNLIKELY(IsIOError(result))) { + return base::unexpected(GetIOError(result.error())); } } // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - value.dataType = Res_value::TYPE_NULL; + if (value.type == Res_value::TYPE_REFERENCE && value.data == 0U) { + value.type = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + value.cookie = kInvalidCookie; } // Write the final value back to Java. - out_values[STYLE_TYPE] = value.dataType; + out_values[STYLE_TYPE] = value.type; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); - out_values[STYLE_RESOURCE_ID] = resid; - out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; - out_values[STYLE_DENSITY] = config.density; + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie); + out_values[STYLE_RESOURCE_ID] = value.resid; + out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags; + out_values[STYLE_DENSITY] = value.config.density; if (out_indices != nullptr && - (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) { - indices_idx++; - out_indices[indices_idx] = ii; + (value.type != Res_value::TYPE_NULL || + value.data == Res_value::DATA_NULL_EMPTY)) { + out_indices[++indices_idx] = ii; } out_values += STYLE_NUM_ENTRIES; @@ -476,7 +419,7 @@ bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, u if (out_indices != nullptr) { out_indices[0] = indices_idx; } - return true; + return {}; } } // namespace android diff --git a/libs/androidfw/ChunkIterator.cpp b/libs/androidfw/ChunkIterator.cpp index 8fc3219680559..25c8aa64e492a 100644 --- a/libs/androidfw/ChunkIterator.cpp +++ b/libs/androidfw/ChunkIterator.cpp @@ -15,6 +15,7 @@ */ #include "androidfw/Chunk.h" +#include "androidfw/Util.h" #include "android-base/logging.h" @@ -23,11 +24,11 @@ namespace android { Chunk ChunkIterator::Next() { CHECK(len_ != 0) << "called Next() after last chunk"; - const ResChunk_header* this_chunk = next_chunk_; + const incfs::map_ptr this_chunk = next_chunk_; + CHECK((bool) this_chunk) << "Next() called without verifying next chunk"; // We've already checked the values of this_chunk, so safely increment. - next_chunk_ = reinterpret_cast( - reinterpret_cast(this_chunk) + dtohl(this_chunk->size)); + next_chunk_ = this_chunk.offset(dtohl(this_chunk->size)).convert(); len_ -= dtohl(this_chunk->size); if (len_ != 0) { @@ -36,7 +37,7 @@ Chunk ChunkIterator::Next() { VerifyNextChunk(); } } - return Chunk(this_chunk); + return Chunk(this_chunk.verified()); } // TODO(b/111401637) remove this and have full resource file verification @@ -47,6 +48,13 @@ bool ChunkIterator::VerifyNextChunkNonFatal() { last_error_was_fatal_ = false; return false; } + + if (!next_chunk_) { + last_error_ = "failed to read chunk from data"; + last_error_was_fatal_ = false; + return false; + } + const size_t size = dtohl(next_chunk_->size); if (size > len_) { last_error_ = "chunk size is bigger than given data"; @@ -58,12 +66,10 @@ bool ChunkIterator::VerifyNextChunkNonFatal() { // Returns false if there was an error. bool ChunkIterator::VerifyNextChunk() { - const uintptr_t header_start = reinterpret_cast(next_chunk_); - // This data must be 4-byte aligned, since we directly // access 32-bit words, which must be aligned on // certain architectures. - if (header_start & 0x03) { + if (!util::IsFourByteAligned(next_chunk_)) { last_error_ = "header not aligned on 4-byte boundary"; return false; } @@ -73,6 +79,11 @@ bool ChunkIterator::VerifyNextChunk() { return false; } + if (!next_chunk_) { + last_error_ = "failed to read chunk from data"; + return false; + } + const size_t header_size = dtohs(next_chunk_->headerSize); const size_t size = dtohl(next_chunk_->size); if (header_size < sizeof(ResChunk_header)) { @@ -90,7 +101,7 @@ bool ChunkIterator::VerifyNextChunk() { return false; } - if ((size | header_size) & 0x03) { + if ((size | header_size) & 0x03U) { last_error_ = "header sizes are not aligned on 4-byte boundary"; return false; } diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index 4e03ce5d95843..a613095141432 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -52,22 +52,22 @@ OverlayStringPool::~OverlayStringPool() { uninit(); } -const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const { +base::expected OverlayStringPool::stringAt(size_t idx) 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); + return idmap_string_pool_->stringAt(idx - offset); } - return ResStringPool::stringAt(idx, outLen); + return ResStringPool::stringAt(idx); } -const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const { +base::expected OverlayStringPool::string8At(size_t idx) 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); + return idmap_string_pool_->string8At(idx - offset); } - return ResStringPool::string8At(idx, outLen); + return ResStringPool::string8At(idx); } size_t OverlayStringPool::size() const { diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 70bb441f94cb5..2fc3b05011c29 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -38,7 +38,7 @@ #include "androidfw/ResourceUtils.h" #include "androidfw/Util.h" -using ::android::base::StringPrintf; +using android::base::StringPrintf; namespace android { @@ -51,17 +51,17 @@ namespace { // the Type structs. class TypeSpecPtrBuilder { public: - explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header) + explicit TypeSpecPtrBuilder(incfs::verified_map_ptr header) : header_(header) { } - void AddType(const ResTable_type* type) { + void AddType(incfs::verified_map_ptr type) { types_.push_back(type); } TypeSpecPtr Build() { // Check for overflow. - using ElementType = const ResTable_type*; + using ElementType = incfs::verified_map_ptr; if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(ElementType) < types_.size()) { return {}; @@ -77,8 +77,8 @@ class TypeSpecPtrBuilder { private: DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder); - const ResTable_typeSpec* header_; - std::vector types_; + incfs::verified_map_ptr header_; + std::vector> types_; }; } // namespace @@ -88,7 +88,7 @@ LoadedPackage::~LoadedPackage() = default; // Precondition: The header passed in has already been verified, so reading any fields and trusting // the ResChunk_header is safe. -static bool VerifyResTableType(const ResTable_type* header) { +static bool VerifyResTableType(incfs::map_ptr header) { if (header->id == 0) { LOG(ERROR) << "RES_TABLE_TYPE_TYPE has invalid ID 0."; return false; @@ -115,89 +115,99 @@ static bool VerifyResTableType(const ResTable_type* header) { return false; } - if (entries_offset & 0x03) { + if (entries_offset & 0x03U) { LOG(ERROR) << "RES_TABLE_TYPE_TYPE entries start at unaligned address."; return false; } return true; } -static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) { +static base::expected VerifyResTableEntry( + incfs::verified_map_ptr type, uint32_t entry_offset) { // Check that the offset is aligned. - if (entry_offset & 0x03) { + if (UNLIKELY(entry_offset & 0x03U)) { LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned."; - return false; + return base::unexpected(std::nullopt); } // Check that the offset doesn't overflow. - if (entry_offset > std::numeric_limits::max() - dtohl(type->entriesStart)) { + if (UNLIKELY(entry_offset > std::numeric_limits::max() - dtohl(type->entriesStart))) { // Overflow in offset. LOG(ERROR) << "Entry at offset " << entry_offset << " is too large."; - return false; + return base::unexpected(std::nullopt); } const size_t chunk_size = dtohl(type->header.size); entry_offset += dtohl(type->entriesStart); - if (entry_offset > chunk_size - sizeof(ResTable_entry)) { + if (UNLIKELY(entry_offset > chunk_size - sizeof(ResTable_entry))) { LOG(ERROR) << "Entry at offset " << entry_offset << " is too large. No room for ResTable_entry."; - return false; + return base::unexpected(std::nullopt); } - const ResTable_entry* entry = reinterpret_cast( - reinterpret_cast(type) + entry_offset); + auto entry = type.offset(entry_offset).convert(); + if (UNLIKELY(!entry)) { + return base::unexpected(IOError::PAGES_MISSING); + } const size_t entry_size = dtohs(entry->size); - if (entry_size < sizeof(*entry)) { + if (UNLIKELY(entry_size < sizeof(entry.value()))) { LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too small."; - return false; + return base::unexpected(std::nullopt); } - if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { + if (UNLIKELY(entry_size > chunk_size || entry_offset > chunk_size - entry_size)) { LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too large."; - return false; + return base::unexpected(std::nullopt); } if (entry_size < sizeof(ResTable_map_entry)) { // There needs to be room for one Res_value struct. - if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) { + if (UNLIKELY(entry_offset + entry_size > chunk_size - sizeof(Res_value))) { LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset << " for type " << (int)type->id << "."; - return false; + return base::unexpected(std::nullopt); + } + + auto value = entry.offset(entry_size).convert(); + if (UNLIKELY(!value)) { + return base::unexpected(IOError::PAGES_MISSING); } - const Res_value* value = - reinterpret_cast(reinterpret_cast(entry) + entry_size); const size_t value_size = dtohs(value->size); - if (value_size < sizeof(Res_value)) { + if (UNLIKELY(value_size < sizeof(Res_value))) { LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small."; - return false; + return base::unexpected(std::nullopt); } - if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { + if (UNLIKELY(value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size)) { LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset << " is too large."; - return false; + return base::unexpected(std::nullopt); } } else { - const ResTable_map_entry* map = reinterpret_cast(entry); + auto map = entry.convert(); + if (UNLIKELY(!map)) { + return base::unexpected(IOError::PAGES_MISSING); + } + const size_t map_entry_count = dtohl(map->count); size_t map_entries_start = entry_offset + entry_size; - if (map_entries_start & 0x03) { + if (UNLIKELY(map_entries_start & 0x03U)) { LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset."; - return false; + return base::unexpected(std::nullopt); } // Each entry is sizeof(ResTable_map) big. - if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) { + if (UNLIKELY(map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map)))) { LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << "."; - return false; + return base::unexpected(std::nullopt); } } - return true; + return {}; } LoadedPackage::iterator::iterator(const LoadedPackage* lp, size_t ti, size_t ei) @@ -233,99 +243,125 @@ uint32_t LoadedPackage::iterator::operator*() const { entryIndex_); } -const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk, - uint16_t entry_index) { - uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index); - if (entry_offset == ResTable_type::NO_ENTRY) { - return nullptr; +base::expected, NullOrIOError> LoadedPackage::GetEntry( + incfs::verified_map_ptr type_chunk, uint16_t entry_index) { + base::expected entry_offset = GetEntryOffset(type_chunk, entry_index); + if (UNLIKELY(!entry_offset.has_value())) { + return base::unexpected(entry_offset.error()); } - return GetEntryFromOffset(type_chunk, entry_offset); + return GetEntryFromOffset(type_chunk, entry_offset.value()); } -uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) { +base::expected LoadedPackage::GetEntryOffset( + incfs::verified_map_ptr type_chunk, uint16_t entry_index) { // The configuration matches and is better than the previous selection. // Find the entry value if it exists for this configuration. const size_t entry_count = dtohl(type_chunk->entryCount); const size_t offsets_offset = dtohs(type_chunk->header.headerSize); // Check if there is the desired entry in this type. - if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { // This is encoded as a sparse map, so perform a binary search. - const ResTable_sparseTypeEntry* sparse_indices = - reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; - const ResTable_sparseTypeEntry* result = - std::lower_bound(sparse_indices, sparse_indices_end, entry_index, - [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { - return dtohs(entry.idx) < entry_idx; - }); + bool error = false; + auto sparse_indices = type_chunk.offset(offsets_offset) + .convert().iterator(); + auto sparse_indices_end = sparse_indices + entry_count; + auto result = std::lower_bound(sparse_indices, sparse_indices_end, entry_index, + [&error](const incfs::map_ptr& entry, + uint16_t entry_idx) { + if (UNLIKELY(!entry)) { + return error = true; + } + return dtohs(entry->idx) < entry_idx; + }); - if (result == sparse_indices_end || dtohs(result->idx) != entry_index) { + if (result == sparse_indices_end) { // No entry found. - return ResTable_type::NO_ENTRY; + return base::unexpected(std::nullopt); + } + + const incfs::verified_map_ptr entry = (*result).verified(); + if (dtohs(entry->idx) != entry_index) { + if (error) { + return base::unexpected(IOError::PAGES_MISSING); + } + return base::unexpected(std::nullopt); } // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as // the real offset divided by 4. - return uint32_t{dtohs(result->offset)} * 4u; + return uint32_t{dtohs(entry->offset)} * 4u; } // This type is encoded as a dense array. if (entry_index >= entry_count) { // This entry cannot be here. - return ResTable_type::NO_ENTRY; + return base::unexpected(std::nullopt); } - const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - return dtohl(entry_offsets[entry_index]); -} - -const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk, - uint32_t offset) { - if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) { - return nullptr; + const auto entry_offset_ptr = type_chunk.offset(offsets_offset).convert() + entry_index; + if (UNLIKELY(!entry_offset_ptr)) { + return base::unexpected(IOError::PAGES_MISSING); } - return reinterpret_cast(reinterpret_cast(type_chunk) + - offset + dtohl(type_chunk->entriesStart)); + + const uint32_t value = dtohl(entry_offset_ptr.value()); + if (value == ResTable_type::NO_ENTRY) { + return base::unexpected(std::nullopt); + } + + return value; } -void LoadedPackage::CollectConfigurations(bool exclude_mipmap, - std::set* out_configs) const { - const static std::u16string kMipMap = u"mipmap"; +base::expected, NullOrIOError> LoadedPackage::GetEntryFromOffset( + incfs::verified_map_ptr type_chunk, uint32_t offset) { + auto valid = VerifyResTableEntry(type_chunk, offset); + if (UNLIKELY(!valid.has_value())) { + return base::unexpected(valid.error()); + } + return type_chunk.offset(offset + dtohl(type_chunk->entriesStart)).convert(); +} + +base::expected LoadedPackage::CollectConfigurations( + bool exclude_mipmap, std::set* out_configs) const { const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { const TypeSpecPtr& type_spec = type_specs_[i]; - if (type_spec != nullptr) { - if (exclude_mipmap) { - const int type_idx = type_spec->type_spec->id - 1; - size_t type_name_len; - const char16_t* type_name16 = type_string_pool_.stringAt(type_idx, &type_name_len); - if (type_name16 != nullptr) { - if (kMipMap.compare(0, std::u16string::npos, type_name16, type_name_len) == 0) { - // This is a mipmap type, skip collection. - continue; - } - } - const char* type_name = type_string_pool_.string8At(type_idx, &type_name_len); - if (type_name != nullptr) { - if (strncmp(type_name, "mipmap", type_name_len) == 0) { - // This is a mipmap type, skip collection. - continue; - } + if (type_spec == nullptr) { + continue; + } + if (exclude_mipmap) { + const int type_idx = type_spec->type_spec->id - 1; + const auto type_name16 = type_string_pool_.stringAt(type_idx); + if (UNLIKELY(IsIOError(type_name16))) { + return base::unexpected(GetIOError(type_name16.error())); + } + if (type_name16.has_value()) { + if (strncmp16(type_name16->data(), u"mipmap", type_name16->size()) == 0) { + // This is a mipmap type, skip collection. + continue; } } - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config config; - config.copyFromDtoH((*iter)->config); - out_configs->insert(config); + const auto type_name = type_string_pool_.string8At(type_idx); + if (UNLIKELY(IsIOError(type_name))) { + return base::unexpected(GetIOError(type_name.error())); + } + if (type_name.has_value()) { + if (strncmp(type_name->data(), "mipmap", type_name->size()) == 0) { + // This is a mipmap type, skip collection. + continue; + } } } + + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config config; + config.copyFromDtoH((*iter)->config); + out_configs->insert(config); + } } + return {}; } void LoadedPackage::CollectLocales(bool canonicalize, std::set* out_locales) const { @@ -348,43 +384,53 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set* out } } -uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, - const std::u16string& entry_name) const { - ssize_t type_idx = type_string_pool_.indexOfString(type_name.data(), type_name.size()); - if (type_idx < 0) { - return 0u; +base::expected LoadedPackage::FindEntryByName( + const std::u16string& type_name, const std::u16string& entry_name) const { + const base::expected type_idx = type_string_pool_.indexOfString( + type_name.data(), type_name.size()); + if (!type_idx.has_value()) { + return base::unexpected(type_idx.error()); } - ssize_t key_idx = key_string_pool_.indexOfString(entry_name.data(), entry_name.size()); - if (key_idx < 0) { - return 0u; + const base::expected key_idx = key_string_pool_.indexOfString( + entry_name.data(), entry_name.size()); + if (!key_idx.has_value()) { + return base::unexpected(key_idx.error()); } - const TypeSpec* type_spec = type_specs_[type_idx].get(); + const TypeSpec* type_spec = type_specs_[*type_idx].get(); if (type_spec == nullptr) { - return 0u; + return base::unexpected(std::nullopt); } const auto iter_end = type_spec->types + type_spec->type_count; for (auto iter = type_spec->types; iter != iter_end; ++iter) { - const ResTable_type* type = *iter; + const incfs::verified_map_ptr& type = *iter; + size_t entry_count = dtohl(type->entryCount); for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { - const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type) + dtohs(type->header.headerSize)); - const uint32_t offset = dtohl(entry_offsets[entry_idx]); + auto entry_offset_ptr = type.offset(dtohs(type->header.headerSize)).convert() + + entry_idx; + if (!entry_offset_ptr) { + return base::unexpected(IOError::PAGES_MISSING); + } + + auto offset = dtohl(entry_offset_ptr.value()); if (offset != ResTable_type::NO_ENTRY) { - const ResTable_entry* entry = reinterpret_cast( - reinterpret_cast(type) + dtohl(type->entriesStart) + offset); - if (dtohl(entry->key.index) == static_cast(key_idx)) { + auto entry = type.offset(dtohl(type->entriesStart) + offset).convert(); + if (!entry) { + return base::unexpected(IOError::PAGES_MISSING); + } + + if (dtohl(entry->key.index) == static_cast(*key_idx)) { // The package ID will be overridden by the caller (due to runtime assignment of package // IDs for shared libraries). - return make_resid(0x00, type_idx + type_id_offset_ + 1, entry_idx); + return make_resid(0x00, *type_idx + type_id_offset_ + 1, entry_idx); } } } } - return 0u; + return base::unexpected(std::nullopt); } const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { @@ -405,8 +451,8 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, // was added. constexpr size_t kMinPackageSize = sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset); - const ResTable_package* header = chunk.header(); - if (header == nullptr) { + const incfs::map_ptr header = chunk.header(); + if (!header) { LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE too small."; return {}; } @@ -453,10 +499,13 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, const Chunk child_chunk = iter.Next(); switch (child_chunk.type()) { case RES_STRING_POOL_TYPE: { - const uintptr_t pool_address = - reinterpret_cast(child_chunk.header()); - const uintptr_t header_address = reinterpret_cast(header); - if (pool_address == header_address + dtohl(header->typeStrings)) { + const auto pool_address = child_chunk.header(); + if (!pool_address) { + LOG(ERROR) << "RES_STRING_POOL_TYPE is incomplete due to incremental installation."; + return {}; + } + + if (pool_address == header.offset(dtohl(header->typeStrings)).convert()) { // This string pool is the type string pool. status_t err = loaded_package->type_string_pool_.setTo( child_chunk.header(), child_chunk.size()); @@ -464,7 +513,8 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, LOG(ERROR) << "RES_STRING_POOL_TYPE for types corrupt."; return {}; } - } else if (pool_address == header_address + dtohl(header->keyStrings)) { + } else if (pool_address == header.offset(dtohl(header->keyStrings)) + .convert()) { // This string pool is the key string pool. status_t err = loaded_package->key_string_pool_.setTo( child_chunk.header(), child_chunk.size()); @@ -478,8 +528,8 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_TYPE_SPEC_TYPE: { - const ResTable_typeSpec* type_spec = child_chunk.header(); - if (type_spec == nullptr) { + const auto type_spec = child_chunk.header(); + if (!type_spec) { LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small."; return {}; } @@ -514,7 +564,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, std::unique_ptr& builder_ptr = type_builder_map[type_spec->id - 1]; if (builder_ptr == nullptr) { - builder_ptr = util::make_unique(type_spec); + builder_ptr = util::make_unique(type_spec.verified()); loaded_package->resource_ids_.set(type_spec->id, entry_count); } else { LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x", @@ -523,8 +573,8 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_TYPE_TYPE: { - const ResTable_type* type = child_chunk.header(); - if (type == nullptr) { + const auto type = child_chunk.header(); + if (!type) { LOG(ERROR) << "RES_TABLE_TYPE_TYPE too small."; return {}; } @@ -536,7 +586,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, // Type chunks must be preceded by their TypeSpec chunks. std::unique_ptr& builder_ptr = type_builder_map[type->id - 1]; if (builder_ptr != nullptr) { - builder_ptr->AddType(type); + builder_ptr->AddType(type.verified()); } else { LOG(ERROR) << StringPrintf( "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.", @@ -546,8 +596,8 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_LIBRARY_TYPE: { - const ResTable_lib_header* lib = child_chunk.header(); - if (lib == nullptr) { + const auto lib = child_chunk.header(); + if (!lib) { LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small."; return {}; } @@ -559,10 +609,13 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, loaded_package->dynamic_package_map_.reserve(dtohl(lib->count)); - const ResTable_lib_entry* const entry_begin = - reinterpret_cast(child_chunk.data_ptr()); - const ResTable_lib_entry* const entry_end = entry_begin + dtohl(lib->count); + const auto entry_begin = child_chunk.data_ptr().convert(); + const auto entry_end = entry_begin + dtohl(lib->count); for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) { + if (!entry_iter) { + return {}; + } + std::string package_name; util::ReadUtf16StringFromDevice(entry_iter->packageName, arraysize(entry_iter->packageName), &package_name); @@ -580,17 +633,16 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_OVERLAYABLE_TYPE: { - const ResTable_overlayable_header* header = - child_chunk.header(); - if (header == nullptr) { + const auto overlayable = child_chunk.header(); + if (!overlayable) { LOG(ERROR) << "RES_TABLE_OVERLAYABLE_TYPE too small."; return {}; } std::string name; - util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &name); + util::ReadUtf16StringFromDevice(overlayable->name, arraysize(overlayable->name), &name); std::string actor; - util::ReadUtf16StringFromDevice(header->actor, arraysize(header->actor), &actor); + util::ReadUtf16StringFromDevice(overlayable->actor, arraysize(overlayable->actor), &actor); if (loaded_package->overlayable_map_.find(name) != loaded_package->overlayable_map_.end()) { @@ -606,9 +658,9 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, switch (overlayable_child_chunk.type()) { case RES_TABLE_OVERLAYABLE_POLICY_TYPE: { - const ResTable_overlayable_policy_header* policy_header = + const auto policy_header = overlayable_child_chunk.header(); - if (policy_header == nullptr) { + if (!policy_header) { LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small."; return {}; } @@ -621,10 +673,12 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, // Retrieve all the resource ids belonging to this policy chunk std::unordered_set ids; - const auto ids_begin = - reinterpret_cast(overlayable_child_chunk.data_ptr()); + const auto ids_begin = overlayable_child_chunk.data_ptr().convert(); const auto ids_end = ids_begin + dtohl(policy_header->entry_count); for (auto id_iter = ids_begin; id_iter != ids_end; ++id_iter) { + if (!id_iter) { + return {}; + } ids.insert(dtohl(id_iter->ident)); } @@ -633,7 +687,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, overlayable_info.name = name; overlayable_info.actor = actor; overlayable_info.policy_flags = policy_header->policy_flags; - loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids)); + loaded_package->overlayable_infos_.emplace_back(overlayable_info, ids); loaded_package->defines_overlayable_ = true; break; } @@ -683,8 +737,8 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, package_property_t property_flags) { - const ResTable_header* header = chunk.header(); - if (header == nullptr) { + incfs::map_ptr header = chunk.header(); + if (!header) { LOG(ERROR) << "RES_TABLE_TYPE too small."; return false; } @@ -747,7 +801,8 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, return true; } -std::unique_ptr LoadedArsc::Load(const StringPiece& data, +std::unique_ptr LoadedArsc::Load(incfs::map_ptr data, + const size_t length, const LoadedIdmap* loaded_idmap, const package_property_t property_flags) { ATRACE_NAME("LoadedArsc::Load"); @@ -755,7 +810,7 @@ std::unique_ptr LoadedArsc::Load(const StringPiece& data, // Not using make_unique because the constructor is private. std::unique_ptr loaded_arsc(new LoadedArsc()); - ChunkIterator iter(data.data(), data.size()); + ChunkIterator iter(data, length); while (iter.HasNext()) { const Chunk chunk = iter.Next(); switch (chunk.type()) { diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index dfb4009b07e28..4010e4e103170 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -104,22 +104,26 @@ static void strcpy16_dtoh(char16_t* dst, const uint16_t* src, size_t avail) *dst = 0; } -static status_t validate_chunk(const ResChunk_header* chunk, +static status_t validate_chunk(const incfs::map_ptr& chunk, size_t minSize, - const uint8_t* dataEnd, + const incfs::map_ptr dataEnd, const char* name) { + if (!chunk) { + return BAD_TYPE; + } + const uint16_t headerSize = dtohs(chunk->headerSize); const uint32_t size = dtohl(chunk->size); if (headerSize >= minSize) { if (headerSize <= size) { if (((headerSize|size)&0x3) == 0) { - if ((size_t)size <= (size_t)(dataEnd-((const uint8_t*)chunk))) { + if ((size_t)size <= (size_t)(dataEnd-chunk.convert())) { return NO_ERROR; } ALOGW("%s data size 0x%x extends beyond resource end %p.", - name, size, (void*)(dataEnd-((const uint8_t*)chunk))); + name, size, (void*)(dataEnd-chunk.convert())); return BAD_TYPE; } ALOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.", @@ -450,7 +454,7 @@ void ResStringPool::setToEmpty() mHeader = (const ResStringPool_header*) header; } -status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) +status_t ResStringPool::setTo(incfs::map_ptr data, size_t size, bool copyData) { if (!data || !size) { return (mError=BAD_TYPE); @@ -467,8 +471,8 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) // The data is at least as big as a ResChunk_header, so we can safely validate the other // header fields. // `data + size` is safe because the source of `size` comes from the kernel/filesystem. - if (validate_chunk(reinterpret_cast(data), sizeof(ResStringPool_header), - reinterpret_cast(data) + size, + const auto chunk_header = data.convert(); + if (validate_chunk(chunk_header, sizeof(ResStringPool_header), data.convert() + size, "ResStringPool_header") != NO_ERROR) { ALOGW("Bad string block: malformed block dimensions"); return (mError=BAD_TYPE); @@ -481,16 +485,25 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) if (mOwnedData == NULL) { return (mError=NO_MEMORY); } - memcpy(mOwnedData, data, size); + + if (!data.convert().verify(size)) { + return (mError=NO_MEMORY); + } + + memcpy(mOwnedData, data.unsafe_ptr(), size); data = mOwnedData; } // The size has been checked, so it is safe to read the data in the ResStringPool_header // data structure. - mHeader = (const ResStringPool_header*)data; + const auto header = data.convert(); + if (!header) { + return (mError=BAD_TYPE); + } + mHeader = header.verified(); if (notDeviceEndian) { - ResStringPool_header* h = const_cast(mHeader); + ResStringPool_header* h = const_cast(mHeader.unsafe_ptr()); h->header.headerSize = dtohs(mHeader->header.headerSize); h->header.type = dtohs(mHeader->header.type); h->header.size = dtohl(mHeader->header.size); @@ -508,8 +521,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) return (mError=BAD_TYPE); } mSize = mHeader->header.size; - mEntries = (const uint32_t*) - (((const uint8_t*)data)+mHeader->header.headerSize); + mEntries = data.offset(mHeader->header.headerSize).convert(); if (mHeader->stringCount > 0) { if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow? @@ -536,9 +548,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) return (mError=BAD_TYPE); } - mStrings = (const void*) - (((const uint8_t*)data) + mHeader->stringsStart); - + mStrings = data.offset(mHeader->stringsStart).convert(); if (mHeader->styleCount == 0) { mStringPoolSize = (mSize - mHeader->stringsStart) / charSize; } else { @@ -560,31 +570,37 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) // check invariant: stringCount > 0 requires a string pool to exist if (mStringPoolSize == 0) { - ALOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount); + ALOGW("Bad string block: stringCount is %d but pool size is 0\n", + (int)mHeader->stringCount); return (mError=BAD_TYPE); } if (notDeviceEndian) { size_t i; - uint32_t* e = const_cast(mEntries); + auto e = const_cast(mEntries.unsafe_ptr()); for (i=0; istringCount; i++) { - e[i] = dtohl(mEntries[i]); + e[i] = dtohl(e[i]); } if (!(mHeader->flags&ResStringPool_header::UTF8_FLAG)) { - const uint16_t* strings = (const uint16_t*)mStrings; - uint16_t* s = const_cast(strings); + uint16_t* s = const_cast(mStrings.convert().unsafe_ptr()); for (i=0; iflags&ResStringPool_header::UTF8_FLAG && - ((uint8_t*)mStrings)[mStringPoolSize-1] != 0) || - (!(mHeader->flags&ResStringPool_header::UTF8_FLAG) && - ((uint16_t*)mStrings)[mStringPoolSize-1] != 0)) { - ALOGW("Bad string block: last string is not 0-terminated\n"); - return (mError=BAD_TYPE); + if (mHeader->flags&ResStringPool_header::UTF8_FLAG) { + auto end = mStrings.convert() + (mStringPoolSize-1); + if (!end || end.value() != 0) { + ALOGW("Bad string block: last string is not 0-terminated\n"); + return (mError=BAD_TYPE); + } + } else { + auto end = mStrings.convert() + (mStringPoolSize-1); + if (!end || end.value() != 0) { + ALOGW("Bad string block: last string is not 0-terminated\n"); + return (mError=BAD_TYPE); + } } } else { mStrings = NULL; @@ -599,14 +615,13 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) return (mError=BAD_TYPE); } - if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) { + if ((mEntryStyles.convert() - mHeader.convert()) > (int)size) { ALOGW("Bad string block: entry of %d styles extends past data size %d\n", - (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader), + (int)(mEntryStyles.convert()-mHeader.convert()), (int)size); return (mError=BAD_TYPE); } - mStyles = (const uint32_t*) - (((const uint8_t*)data)+mHeader->stylesStart); + mStyles = data.offset(mHeader->stylesStart).convert(); if (mHeader->stylesStart >= mHeader->header.size) { ALOGW("Bad string block: style pool starts %d, after total size %d\n", (int)mHeader->stylesStart, (int)mHeader->header.size); @@ -617,13 +632,13 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) if (notDeviceEndian) { size_t i; - uint32_t* e = const_cast(mEntryStyles); + uint32_t* e = const_cast(mEntryStyles.unsafe_ptr()); for (i=0; istyleCount; i++) { - e[i] = dtohl(mEntryStyles[i]); + e[i] = dtohl(e[i]); } - uint32_t* s = const_cast(mStyles); + uint32_t* s = const_cast(mStyles.unsafe_ptr()); for (i=0; istringCount; x++) { if (mCache[x] != NULL) { free(mCache[x]); @@ -679,15 +695,21 @@ void ResStringPool::uninit() * data encoded. In that case, drop the high bit of the first character and * add it together with the next character. */ -static inline size_t -decodeLength(const uint16_t** str) +static inline base::expected decodeLength(incfs::map_ptr* str) { - size_t len = **str; - if ((len & 0x8000) != 0) { - (*str)++; - len = ((len & 0x7FFF) << 16) | **str; + if (UNLIKELY(!*str)) { + return base::unexpected(IOError::PAGES_MISSING); } - (*str)++; + + size_t len = str->value(); + if ((len & 0x8000U) != 0) { + ++(*str); + if (UNLIKELY(!*str)) { + return base::unexpected(IOError::PAGES_MISSING); + } + len = ((len & 0x7FFFU) << 16U) | str->value(); + } + ++(*str); return len; } @@ -701,82 +723,119 @@ decodeLength(const uint16_t** str) * data encoded. In that case, drop the high bit of the first character and * add it together with the next character. */ -static inline size_t -decodeLength(const uint8_t** str) +static inline base::expected decodeLength(incfs::map_ptr* str) { - size_t len = **str; - if ((len & 0x80) != 0) { - (*str)++; - len = ((len & 0x7F) << 8) | **str; + if (UNLIKELY(!*str)) { + return base::unexpected(IOError::PAGES_MISSING); } - (*str)++; + + size_t len = str->value(); + if ((len & 0x80U) != 0) { + ++(*str); + if (UNLIKELY(!*str)) { + return base::unexpected(IOError::PAGES_MISSING); + } + len = ((len & 0x7FU) << 8U) | str->value(); + } + ++(*str); return len; } -const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const +base::expected ResStringPool::stringAt(size_t idx) const { if (mError == NO_ERROR && idx < mHeader->stringCount) { const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0; - const uint32_t off = mEntries[idx]/(isUTF8?sizeof(uint8_t):sizeof(uint16_t)); + auto offPtr = mEntries + idx; + if (UNLIKELY(!offPtr)) { + return base::unexpected(IOError::PAGES_MISSING); + } + + const uint32_t off = (offPtr.value())/(isUTF8?sizeof(uint8_t):sizeof(uint16_t)); if (off < (mStringPoolSize-1)) { if (!isUTF8) { - const uint16_t* strings = (uint16_t*)mStrings; - const uint16_t* str = strings+off; + auto strings = mStrings.convert(); + auto str = strings+off; + + const base::expected u16len = decodeLength(&str); + if (UNLIKELY(!u16len.has_value())) { + return base::unexpected(u16len.error()); + } - *u16len = decodeLength(&str); if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) { // Reject malformed (non null-terminated) strings - if (str[*u16len] != 0x0000) { - ALOGW("Bad string block: string #%d is not null-terminated", - (int)idx); - return NULL; + const auto nullAddress = str + (*u16len); + if (UNLIKELY(!nullAddress)) { + return base::unexpected(IOError::PAGES_MISSING); } - return reinterpret_cast(str); + + if (nullAddress.value() != 0x0000) { + ALOGW("Bad string block: string #%d is not null-terminated", (int)idx); + return base::unexpected(std::nullopt); + } + + if (UNLIKELY(!str.verify(*u16len + 1U))) { + return base::unexpected(IOError::PAGES_MISSING); + } + + return StringPiece16(reinterpret_cast(str.unsafe_ptr()), + *u16len); } else { ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", (int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize); } } else { - const uint8_t* strings = (uint8_t*)mStrings; - const uint8_t* u8str = strings+off; + auto strings = mStrings.convert(); + auto u8str = strings+off; - *u16len = decodeLength(&u8str); - size_t u8len = decodeLength(&u8str); + base::expected u16len = decodeLength(&u8str); + if (UNLIKELY(!u16len.has_value())) { + return base::unexpected(u16len.error()); + } + + const base::expected u8len = decodeLength(&u8str); + if (UNLIKELY(!u8len.has_value())) { + return base::unexpected(u8len.error()); + } // encLen must be less than 0x7FFF due to encoding. - if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) { + if ((uint32_t)(u8str+*u8len-strings) < mStringPoolSize) { AutoMutex lock(mDecodeLock); if (mCache != NULL && mCache[idx] != NULL) { - return mCache[idx]; + return StringPiece16(mCache[idx], *u16len); } // Retrieve the actual length of the utf8 string if the // encoded length was truncated - if (stringDecodeAt(idx, u8str, u8len, &u8len) == NULL) { - return NULL; + auto decodedString = stringDecodeAt(idx, u8str, *u8len); + if (!decodedString.has_value()) { + return base::unexpected(decodedString.error()); } // Since AAPT truncated lengths longer than 0x7FFF, check // that the bits that remain after truncation at least match // the bits of the actual length - ssize_t actualLen = utf8_to_utf16_length(u8str, u8len); - if (actualLen < 0 || ((size_t)actualLen & 0x7FFF) != *u16len) { + ssize_t actualLen = utf8_to_utf16_length( + reinterpret_cast(decodedString->data()), + decodedString->size()); + + if (actualLen < 0 || ((size_t)actualLen & 0x7FFFU) != *u16len) { ALOGW("Bad string block: string #%lld decoded length is not correct " "%lld vs %llu\n", (long long)idx, (long long)actualLen, (long long)*u16len); - return NULL; + return base::unexpected(std::nullopt); } - *u16len = (size_t) actualLen; - char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t)); + u16len = (size_t) actualLen; + auto u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t)); if (!u16str) { ALOGW("No memory when trying to allocate decode cache for string #%d\n", (int)idx); - return NULL; + return base::unexpected(std::nullopt); } - utf8_to_utf16(u8str, u8len, u16str, *u16len + 1); + utf8_to_utf16(reinterpret_cast(decodedString->data()), + decodedString->size(), u16str, *u16len + 1); if (mCache == NULL) { #ifndef __ANDROID__ @@ -793,19 +852,19 @@ const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const if (mCache == NULL) { ALOGW("No memory trying to allocate decode cache table of %d bytes\n", (int)(mHeader->stringCount*sizeof(char16_t**))); - return NULL; + return base::unexpected(std::nullopt); } } if (kDebugStringPoolNoisy) { - ALOGI("Caching UTF8 string: %s", u8str); + ALOGI("Caching UTF8 string: %s", u8str.unsafe_ptr()); } mCache[idx] = u16str; - return u16str; + return StringPiece16(u16str, *u16len); } else { ALOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n", - (long long)idx, (long long)(u8str+u8len-strings), + (long long)idx, (long long)(u8str+*u8len-strings), (long long)mStringPoolSize); } } @@ -815,33 +874,43 @@ const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const (int)(mStringPoolSize*sizeof(uint16_t))); } } - return NULL; + return base::unexpected(std::nullopt); } -const char* ResStringPool::string8At(size_t idx, size_t* outLen) const +base::expected ResStringPool::string8At(size_t idx) const { if (mError == NO_ERROR && idx < mHeader->stringCount) { if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) == 0) { - return NULL; + return base::unexpected(std::nullopt); } - const uint32_t off = mEntries[idx]/sizeof(char); + + auto offPtr = mEntries + idx; + if (UNLIKELY(!offPtr)) { + return base::unexpected(IOError::PAGES_MISSING); + } + + const uint32_t off = (offPtr.value())/sizeof(char); if (off < (mStringPoolSize-1)) { - const uint8_t* strings = (uint8_t*)mStrings; - const uint8_t* str = strings+off; + auto strings = mStrings.convert(); + auto str = strings+off; // Decode the UTF-16 length. This is not used if we're not // converting to UTF-16 from UTF-8. - decodeLength(&str); + const base::expected u16len = decodeLength(&str); + if (UNLIKELY(!u16len)) { + return base::unexpected(u16len.error()); + } - const size_t encLen = decodeLength(&str); - *outLen = encLen; - - if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { - return stringDecodeAt(idx, str, encLen, outLen); + const base::expected u8len = decodeLength(&str); + if (UNLIKELY(!u8len)) { + return base::unexpected(u8len.error()); + } + if ((uint32_t)(str+*u8len-strings) < mStringPoolSize) { + return stringDecodeAt(idx, str, *u8len); } else { ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); + (int)idx, (int)(str+*u8len-strings), (int)mStringPoolSize); } } else { ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n", @@ -849,7 +918,7 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const (int)(mStringPoolSize*sizeof(uint16_t))); } } - return NULL; + return base::unexpected(std::nullopt); } /** @@ -859,74 +928,93 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const * bits. Strings that exceed the maximum encode length are not placed into * StringPools in AAPT2. **/ -const char* ResStringPool::stringDecodeAt(size_t idx, const uint8_t* str, - const size_t encLen, size_t* outLen) const { - const uint8_t* strings = (uint8_t*)mStrings; - +base::expected ResStringPool::stringDecodeAt( + size_t idx, incfs::map_ptr str, size_t encLen) const +{ + const auto strings = mStrings.convert(); size_t i = 0, end = encLen; while ((uint32_t)(str+end-strings) < mStringPoolSize) { - if (str[end] == 0x00) { + const auto nullAddress = str + end; + if (UNLIKELY(!nullAddress)) { + return base::unexpected(IOError::PAGES_MISSING); + } + + if (nullAddress.value() == 0x00) { if (i != 0) { ALOGW("Bad string block: string #%d is truncated (actual length is %d)", (int)idx, (int)end); } - *outLen = end; - return (const char*)str; + if (UNLIKELY(!str.verify(end + 1U))) { + return base::unexpected(IOError::PAGES_MISSING); + } + + return StringPiece((const char*) str.unsafe_ptr(), end); } end = (++i << (sizeof(uint8_t) * 8 * 2 - 1)) | encLen; } // Reject malformed (non null-terminated) strings - ALOGW("Bad string block: string #%d is not null-terminated", - (int)idx); - return NULL; + ALOGW("Bad string block: string #%d is not null-terminated", (int)idx); + return base::unexpected(std::nullopt); } -const String8 ResStringPool::string8ObjectAt(size_t idx) const +base::expected ResStringPool::string8ObjectAt(size_t idx) const { - size_t len; - const char *str = string8At(idx, &len); - if (str != NULL) { - return String8(str, len); + const base::expected str = string8At(idx); + if (UNLIKELY(IsIOError(str))) { + return base::unexpected(GetIOError(str.error())); + } + if (str.has_value()) { + return String8(str->data(), str->size()); } - const char16_t *str16 = stringAt(idx, &len); - if (str16 != NULL) { - return String8(str16, len); + const base::expected str16 = stringAt(idx); + if (UNLIKELY(IsIOError(str16))) { + return base::unexpected(GetIOError(str16.error())); } + if (str16.has_value()) { + return String8(str16->data(), str16->size()); + } + return String8(); } -const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const +base::expected, NullOrIOError> ResStringPool::styleAt( + const ResStringPool_ref& ref) const { return styleAt(ref.index); } -const ResStringPool_span* ResStringPool::styleAt(size_t idx) const +base::expected, NullOrIOError> ResStringPool::styleAt( + size_t idx) const { if (mError == NO_ERROR && idx < mHeader->styleCount) { - const uint32_t off = (mEntryStyles[idx]/sizeof(uint32_t)); + auto offPtr = mEntryStyles + idx; + if (UNLIKELY(!offPtr)) { + return base::unexpected(IOError::PAGES_MISSING); + } + + const uint32_t off = ((offPtr.value())/sizeof(uint32_t)); if (off < mStylePoolSize) { - return (const ResStringPool_span*)(mStyles+off); + return (mStyles+off).convert(); } else { ALOGW("Bad string block: style #%d entry is at %d, past end at %d\n", (int)idx, (int)(off*sizeof(uint32_t)), (int)(mStylePoolSize*sizeof(uint32_t))); } } - return NULL; + return base::unexpected(std::nullopt); } -ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const +base::expected ResStringPool::indexOfString(const char16_t* str, + size_t strLen) const { if (mError != NO_ERROR) { - return mError; + return base::unexpected(std::nullopt); } - size_t len; - if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) { if (kDebugStringPoolNoisy) { ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string()); @@ -948,17 +1036,19 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const ssize_t mid; while (l <= h) { mid = l + (h - l)/2; - const uint8_t* s = (const uint8_t*)string8At(mid, &len); - int c; - if (s != NULL) { - char16_t* end = utf8_to_utf16(s, len, convBuffer, convBufferLen); + int c = -1; + const base::expected s = string8At(mid); + if (UNLIKELY(IsIOError(s))) { + return base::unexpected(s.error()); + } + if (s.has_value()) { + char16_t* end = utf8_to_utf16(reinterpret_cast(s->data()), + s->size(), convBuffer, convBufferLen); c = strzcmp16(convBuffer, end-convBuffer, str, strLen); - } else { - c = -1; } if (kDebugStringPoolNoisy) { ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", - (const char*)s, c, (int)l, (int)mid, (int)h); + s->data(), c, (int)l, (int)mid, (int)h); } if (c == 0) { if (kDebugStringPoolNoisy) { @@ -981,15 +1071,21 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const String8 str8(str, strLen); const size_t str8Len = str8.size(); for (int i=mHeader->stringCount-1; i>=0; i--) { - const char* s = string8At(i, &len); - if (kDebugStringPoolNoisy) { - ALOGI("Looking at %s, i=%d\n", String8(s).string(), i); + const base::expected s = string8At(i); + if (UNLIKELY(IsIOError(s))) { + return base::unexpected(s.error()); } - if (s && str8Len == len && memcmp(s, str8.string(), str8Len) == 0) { + if (s.has_value()) { if (kDebugStringPoolNoisy) { - ALOGI("MATCH!"); + ALOGI("Looking at %s, i=%d\n", s->data(), i); + } + if (str8Len == s->size() + && memcmp(s->data(), str8.string(), str8Len) == 0) { + if (kDebugStringPoolNoisy) { + ALOGI("MATCH!"); + } + return i; } - return i; } } } @@ -1007,11 +1103,14 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const ssize_t mid; while (l <= h) { mid = l + (h - l)/2; - const char16_t* s = stringAt(mid, &len); - int c = s ? strzcmp16(s, len, str, strLen) : -1; + const base::expected s = stringAt(mid); + if (UNLIKELY(IsIOError(s))) { + return base::unexpected(s.error()); + } + int c = s.has_value() ? strzcmp16(s->data(), s->size(), str, strLen) : -1; if (kDebugStringPoolNoisy) { ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", - String8(s).string(), c, (int)l, (int)mid, (int)h); + String8(s->data(), s->size()).string(), c, (int)l, (int)mid, (int)h); } if (c == 0) { if (kDebugStringPoolNoisy) { @@ -1030,11 +1129,15 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const // span tags; since those always appear at the end of the string // block, start searching at the back. for (int i=mHeader->stringCount-1; i>=0; i--) { - const char16_t* s = stringAt(i, &len); - if (kDebugStringPoolNoisy) { - ALOGI("Looking at %s, i=%d\n", String8(s).string(), i); + const base::expected s = stringAt(i); + if (UNLIKELY(IsIOError(s))) { + return base::unexpected(s.error()); } - if (s && strLen == len && strzcmp16(s, len, str, strLen) == 0) { + if (kDebugStringPoolNoisy) { + ALOGI("Looking at %s, i=%d\n", String8(s->data(), s->size()).string(), i); + } + if (s.has_value() && strLen == s->size() && + strzcmp16(s->data(), s->size(), str, strLen) == 0) { if (kDebugStringPoolNoisy) { ALOGI("MATCH!"); } @@ -1043,8 +1146,7 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const } } } - - return NAME_NOT_FOUND; + return base::unexpected(std::nullopt); } size_t ResStringPool::size() const @@ -1062,9 +1164,10 @@ size_t ResStringPool::bytes() const return (mError == NO_ERROR) ? mHeader->header.size : 0; } -const void* ResStringPool::data() const +incfs::map_ptr ResStringPool::data() const { - return mHeader; + + return mHeader.unsafe_ptr(); } bool ResStringPool::isSorted() const @@ -1121,7 +1224,7 @@ int32_t ResXMLParser::getCommentID() const const char16_t* ResXMLParser::getComment(size_t* outLen) const { int32_t id = getCommentID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } uint32_t ResXMLParser::getLineNumber() const @@ -1140,7 +1243,7 @@ int32_t ResXMLParser::getTextID() const const char16_t* ResXMLParser::getText(size_t* outLen) const { int32_t id = getTextID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } ssize_t ResXMLParser::getTextValue(Res_value* outValue) const @@ -1164,7 +1267,7 @@ const char16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const { int32_t id = getNamespacePrefixID(); //printf("prefix=%d event=%p\n", id, mEventCode); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } int32_t ResXMLParser::getNamespaceUriID() const @@ -1179,7 +1282,7 @@ const char16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const { int32_t id = getNamespaceUriID(); //printf("uri=%d event=%p\n", id, mEventCode); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } int32_t ResXMLParser::getElementNamespaceID() const @@ -1196,7 +1299,7 @@ int32_t ResXMLParser::getElementNamespaceID() const const char16_t* ResXMLParser::getElementNamespace(size_t* outLen) const { int32_t id = getElementNamespaceID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } int32_t ResXMLParser::getElementNameID() const @@ -1213,7 +1316,7 @@ int32_t ResXMLParser::getElementNameID() const const char16_t* ResXMLParser::getElementName(size_t* outLen) const { int32_t id = getElementNameID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } size_t ResXMLParser::getAttributeCount() const @@ -1246,7 +1349,7 @@ const char16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) if (kDebugXMLNoisy) { printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id); } - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) const @@ -1256,7 +1359,7 @@ const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) con if (kDebugXMLNoisy) { printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id); } - return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.string8At(id), outLen) : NULL; } int32_t ResXMLParser::getAttributeNameID(size_t idx) const @@ -1281,7 +1384,7 @@ const char16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const if (kDebugXMLNoisy) { printf("getAttributeName 0x%zx=0x%x\n", idx, id); } - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const @@ -1291,7 +1394,7 @@ const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const if (kDebugXMLNoisy) { printf("getAttributeName 0x%zx=0x%x\n", idx, id); } - return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.string8At(id), outLen) : NULL; } uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const @@ -1328,7 +1431,7 @@ const char16_t* ResXMLParser::getAttributeStringValue(size_t idx, size_t* outLen if (kDebugXMLNoisy) { printf("getAttributeValue 0x%zx=0x%x\n", idx, id); } - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } int32_t ResXMLParser::getAttributeDataType(size_t idx) const @@ -3596,9 +3699,10 @@ struct ResTable::PackageGroup ssize_t findType16(const char16_t* type, size_t len) const { const size_t N = packages.size(); for (size_t i = 0; i < N; i++) { - ssize_t index = packages[i]->typeStrings.indexOfString(type, len); - if (index >= 0) { - return index + packages[i]->typeIdOffset; + const base::expected index = + packages[i]->typeStrings.indexOfString(type, len); + if (index.has_value()) { + return *index + packages[i]->typeIdOffset; } } return -1; @@ -4304,21 +4408,21 @@ bool ResTable::getResourceName(uint32_t resID, bool allowUtf8, resource_name* ou outName->package = grp->name.string(); outName->packageLen = grp->name.size(); if (allowUtf8) { - outName->type8 = entry.typeStr.string8(&outName->typeLen); - outName->name8 = entry.keyStr.string8(&outName->nameLen); + outName->type8 = UnpackOptionalString(entry.typeStr.string8(), &outName->typeLen); + outName->name8 = UnpackOptionalString(entry.keyStr.string8(), &outName->nameLen); } else { outName->type8 = NULL; outName->name8 = NULL; } if (outName->type8 == NULL) { - outName->type = entry.typeStr.string16(&outName->typeLen); + outName->type = UnpackOptionalString(entry.typeStr.string16(), &outName->typeLen); // If we have a bad index for some reason, we should abort. if (outName->type == NULL) { return false; } } if (outName->name8 == NULL) { - outName->name = entry.keyStr.string16(&outName->nameLen); + outName->name = UnpackOptionalString(entry.keyStr.string16(), &outName->nameLen); // If we have a bad index for some reason, we should abort. if (outName->name == NULL) { return false; @@ -4406,7 +4510,8 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag entry.package->header->index, outValue->dataType, outValue->dataType == Res_value::TYPE_STRING ? - String8(entry.package->header->values.stringAt(outValue->data, &len)).string() : + String8(UnpackOptionalString( + entry.package->header->values.stringAt(outValue->data), &len)).string() : "", outValue->data); } @@ -4462,7 +4567,8 @@ const char16_t* ResTable::valueToString( return NULL; } if (value->dataType == value->TYPE_STRING) { - return getTableStringBlock(stringBlock)->stringAt(value->data, outLen); + return UnpackOptionalString(getTableStringBlock(stringBlock)->stringAt(value->data), + outLen); } // XXX do int to string conversions. return NULL; @@ -4978,15 +5084,13 @@ nope: size_t targetTypeLen = typeLen; do { - ssize_t ti = group->packages[pi]->typeStrings.indexOfString( - targetType, targetTypeLen); - if (ti < 0) { + auto ti = group->packages[pi]->typeStrings.indexOfString(targetType, targetTypeLen); + if (!ti.has_value()) { continue; } - ti += group->packages[pi]->typeIdOffset; - - const uint32_t identifier = findEntry(group, ti, name, nameLen, + *ti += group->packages[pi]->typeIdOffset; + const uint32_t identifier = findEntry(group, *ti, name, nameLen, outTypeSpecFlags); if (identifier != 0) { if (fakePublic && outTypeSpecFlags) { @@ -5009,8 +5113,9 @@ uint32_t ResTable::findEntry(const PackageGroup* group, ssize_t typeIndex, const const size_t typeCount = typeList.size(); for (size_t i = 0; i < typeCount; i++) { const Type* t = typeList[i]; - const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen); - if (ei < 0) { + const base::expected ei = + t->package->keyStrings.indexOfString(name, nameLen); + if (!ei.has_value()) { continue; } @@ -5025,7 +5130,7 @@ uint32_t ResTable::findEntry(const PackageGroup* group, ssize_t typeIndex, const continue; } - if (dtohl(entry->key.index) == (size_t) ei) { + if (dtohl(entry->key.index) == (size_t) *ei) { uint32_t resId = Res_MAKEID(group->id - 1, typeIndex, iter.index()); if (outTypeSpecFlags) { Entry result; @@ -6187,8 +6292,9 @@ void ResTable::forEachConfiguration(bool ignoreMipmap, bool ignoreAndroidPackage for (size_t k = 0; k < numTypes; k++) { const Type* type = typeList[k]; const ResStringPool& typeStrings = type->package->typeStrings; - if (ignoreMipmap && typeStrings.string8ObjectAt( - type->typeSpec->id - 1) == "mipmap") { + const base::expected typeStr = typeStrings.string8ObjectAt( + type->typeSpec->id - 1); + if (ignoreMipmap && typeStr.has_value() && *typeStr == "mipmap") { continue; } @@ -6244,24 +6350,18 @@ void ResTable::getLocales(Vector* locales, bool includeSystemLocales, StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index) : mPool(pool), mIndex(index) {} -const char* StringPoolRef::string8(size_t* outLen) const { - if (mPool != NULL) { - return mPool->string8At(mIndex, outLen); +base::expected StringPoolRef::string8() const { + if (LIKELY(mPool != NULL)) { + return mPool->string8At(mIndex); } - if (outLen != NULL) { - *outLen = 0; - } - return NULL; + return base::unexpected(std::nullopt); } -const char16_t* StringPoolRef::string16(size_t* outLen) const { - if (mPool != NULL) { - return mPool->stringAt(mIndex, outLen); +base::expected StringPoolRef::string16() const { + if (LIKELY(mPool != NULL)) { + return mPool->stringAt(mIndex); } - if (outLen != NULL) { - *outLen = 0; - } - return NULL; + return base::unexpected(std::nullopt); } bool ResTable::getResourceFlags(uint32_t resID, uint32_t* outFlags) const { @@ -7380,13 +7480,13 @@ void ResTable::print_value(const Package* pkg, const Res_value& value) const printf("(dynamic attribute) 0x%08x\n", value.data); } else if (value.dataType == Res_value::TYPE_STRING) { size_t len; - const char* str8 = pkg->header->values.string8At( - value.data, &len); + const char* str8 = UnpackOptionalString(pkg->header->values.string8At( + value.data), &len); if (str8 != NULL) { printf("(string8) \"%s\"\n", normalizeForOutput(str8).string()); } else { - const char16_t* str16 = pkg->header->values.stringAt( - value.data, &len); + const char16_t* str16 = UnpackOptionalString(pkg->header->values.stringAt( + value.data), &len); if (str16 != NULL) { printf("(string16) \"%s\"\n", normalizeForOutput(String8(str16, len).string()).string()); diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp index c63dff8f9104b..a34aa72392504 100644 --- a/libs/androidfw/ResourceUtils.cpp +++ b/libs/androidfw/ResourceUtils.cpp @@ -48,61 +48,76 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin !(has_type_separator && out_type->empty()); } -bool ToResourceName(const StringPoolRef& type_string_ref, - const StringPoolRef& entry_string_ref, - const StringPiece& package_name, - AssetManager2::ResourceName* out_name) { - out_name->package = package_name.data(); - out_name->package_len = package_name.size(); +base::expected ToResourceName( + const StringPoolRef& type_string_ref, const StringPoolRef& entry_string_ref, + const StringPiece& package_name) { + AssetManager2::ResourceName name{ + .package = package_name.data(), + .package_len = package_name.size(), + }; - out_name->type = type_string_ref.string8(&out_name->type_len); - out_name->type16 = nullptr; - if (out_name->type == nullptr) { - out_name->type16 = type_string_ref.string16(&out_name->type_len); - if (out_name->type16 == nullptr) { - return false; + if (base::expected type_str = type_string_ref.string8()) { + name.type = type_str->data(); + name.type_len = type_str->size(); + } else if (UNLIKELY(IsIOError(type_str))) { + return base::unexpected(type_str.error()); + } + + if (name.type == nullptr) { + if (base::expected type16_str = type_string_ref.string16()) { + name.type16 = type16_str->data(); + name.type_len = type16_str->size(); + } else if (!type16_str.has_value()) { + return base::unexpected(type16_str.error()); } } - out_name->entry = entry_string_ref.string8(&out_name->entry_len); - out_name->entry16 = nullptr; - if (out_name->entry == nullptr) { - out_name->entry16 = entry_string_ref.string16(&out_name->entry_len); - if (out_name->entry16 == nullptr) { - return false; + if (base::expected entry_str = entry_string_ref.string8()) { + name.entry = entry_str->data(); + name.entry_len = entry_str->size(); + } else if (UNLIKELY(IsIOError(entry_str))) { + return base::unexpected(entry_str.error()); + } + + if (name.entry == nullptr) { + if (base::expected entry16_str = entry_string_ref.string16()) { + name.entry16 = entry16_str->data(); + name.entry_len = entry16_str->size(); + } else if (!entry16_str.has_value()) { + return base::unexpected(entry16_str.error()); } } - return true; + return name; } -std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name) { +std::string ToFormattedResourceString(const AssetManager2::ResourceName& resource_name) { std::string result; - if (resource_name->package != nullptr) { - result.append(resource_name->package, resource_name->package_len); + if (resource_name.package != nullptr) { + result.append(resource_name.package, resource_name.package_len); } - if (resource_name->type != nullptr || resource_name->type16 != nullptr) { + if (resource_name.type != nullptr || resource_name.type16 != nullptr) { if (!result.empty()) { result += ":"; } - if (resource_name->type != nullptr) { - result.append(resource_name->type, resource_name->type_len); + if (resource_name.type != nullptr) { + result.append(resource_name.type, resource_name.type_len); } else { - result += util::Utf16ToUtf8(StringPiece16(resource_name->type16, resource_name->type_len)); + result += util::Utf16ToUtf8(StringPiece16(resource_name.type16, resource_name.type_len)); } } - if (resource_name->entry != nullptr || resource_name->entry16 != nullptr) { + if (resource_name.entry != nullptr || resource_name.entry16 != nullptr) { if (!result.empty()) { result += "/"; } - if (resource_name->entry != nullptr) { - result.append(resource_name->entry, resource_name->entry_len); + if (resource_name.entry != nullptr) { + result.append(resource_name.entry, resource_name.entry_len); } else { - result += util::Utf16ToUtf8(StringPiece16(resource_name->entry16, resource_name->entry_len)); + result += util::Utf16ToUtf8(StringPiece16(resource_name.entry16, resource_name.entry_len)); } } diff --git a/libs/androidfw/StreamingZipInflater.cpp b/libs/androidfw/StreamingZipInflater.cpp index b39b5f0b8b36e..1c5e5d44c8459 100644 --- a/libs/androidfw/StreamingZipInflater.cpp +++ b/libs/androidfw/StreamingZipInflater.cpp @@ -70,13 +70,13 @@ StreamingZipInflater::StreamingZipInflater(int fd, off64_t compDataStart, /* * Streaming access to compressed data held in an mmapped region of memory */ -StreamingZipInflater::StreamingZipInflater(FileMap* dataMap, size_t uncompSize) { +StreamingZipInflater::StreamingZipInflater(const incfs::IncFsFileMap* dataMap, size_t uncompSize) { mFd = -1; mDataMap = dataMap; mOutTotalSize = uncompSize; - mInTotalSize = dataMap->getDataLength(); + mInTotalSize = dataMap->length(); - mInBuf = (uint8_t*) dataMap->getDataPtr(); + mInBuf = (uint8_t*) dataMap->unsafe_data(); // IncFs safety handled in zlib. mInBufSize = mInTotalSize; mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE; diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp index e77ac3df474c8..52e7a70521a1e 100644 --- a/libs/androidfw/ZipFileRO.cpp +++ b/libs/androidfw/ZipFileRO.cpp @@ -232,6 +232,29 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const return newMap; } +/* + * Create a new incfs::IncFsFileMap object that spans the data in "entry". + */ +std::optional ZipFileRO::createEntryIncFsFileMap(ZipEntryRO entry) const +{ + const _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); + const ZipEntry& ze = zipEntry->entry; + int fd = GetFileDescriptor(mHandle); + size_t actualLen = 0; + + if (ze.method == kCompressStored) { + actualLen = ze.uncompressed_length; + } else { + actualLen = ze.compressed_length; + } + + incfs::IncFsFileMap newMap; + if (!newMap.Create(fd, ze.offset, actualLen, mFileName)) { + return std::nullopt; + } + return std::move(newMap); +} + /* * Uncompress an entry, in its entirety, into the provided output buffer. * diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp index 568e3b63d67f3..58fc5bbbab5ec 100644 --- a/libs/androidfw/ZipUtils.cpp +++ b/libs/androidfw/ZipUtils.cpp @@ -40,7 +40,7 @@ class FileReader : public zip_archive::Reader { explicit FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) { } - bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const { + bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override { // Data is usually requested sequentially, so this helps avoid pointless // fseeks every time we perform a read. There's an impedence mismatch // here because the original API was designed around pread and pwrite. @@ -71,7 +71,7 @@ class FdReader : public zip_archive::Reader { explicit FdReader(int fd) : mFd(fd) { } - bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const { + bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override { return android::base::ReadFullyAtOffset(mFd, buf, len, offset); } @@ -81,22 +81,27 @@ class FdReader : public zip_archive::Reader { class BufferReader : public zip_archive::Reader { public: - BufferReader(const void* input, size_t inputSize) : Reader(), - mInput(reinterpret_cast(input)), + BufferReader(incfs::map_ptr input, size_t inputSize) : Reader(), + mInput(input.convert()), mInputSize(inputSize) { } - bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const { + bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override { if (mInputSize < len || offset > mInputSize - len) { return false; } - memcpy(buf, mInput + offset, len); + const incfs::map_ptr pos = mInput.offset(offset); + if (!pos.verify(len)) { + return false; + } + + memcpy(buf, pos.unsafe_ptr(), len); return true; } private: - const uint8_t* mInput; + const incfs::map_ptr mInput; const size_t mInputSize; }; @@ -138,7 +143,7 @@ class BufferWriter : public zip_archive::Writer { return (zip_archive::Inflate(reader, compressedLen, uncompressedLen, &writer, nullptr) == 0); } -/*static*/ bool ZipUtils::inflateToBuffer(const void* in, void* buf, +/*static*/ bool ZipUtils::inflateToBuffer(incfs::map_ptr in, void* buf, long uncompressedLen, long compressedLen) { BufferReader reader(in, compressedLen); diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp index 96d44ab8e45ca..5309ab2b6e201 100644 --- a/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp +++ b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp @@ -31,9 +31,6 @@ using android::LoadedArsc; using android::StringPiece; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - - std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(reinterpret_cast(data), size)); - + std::unique_ptr loaded_arsc = LoadedArsc::Load(data, size); return 0; } \ No newline at end of file diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h index 298509eb37a1c..80bae20f34197 100644 --- a/libs/androidfw/include/androidfw/Asset.h +++ b/libs/androidfw/include/androidfw/Asset.h @@ -23,18 +23,18 @@ #include #include - #include +#include #include +#include + #include #include #include namespace android { -class FileMap; - /* * Instances of this class provide read-only operations on a byte stream. * @@ -49,6 +49,8 @@ class FileMap; class Asset { public: virtual ~Asset(void) = default; + Asset(const Asset& src) = delete; + Asset& operator=(const Asset& src) = delete; static int32_t getGlobalCount(); static String8 getAssetAllocations(); @@ -87,8 +89,19 @@ public: /* * Get a pointer to a buffer with the entire contents of the file. + * If `aligned` is true, the buffer data will be aligned to a 4-byte boundary. + * + * Use this function if the asset can never reside on IncFs. */ - virtual const void* getBuffer(bool wordAligned) = 0; + virtual const void* getBuffer(bool aligned) = 0; + + /* + * Get a incfs::map_ptr to a buffer with the entire contents of the file. + * If `aligned` is true, the buffer data will be aligned to a 4-byte boundary. + * + * Use this function if the asset can potentially reside on IncFs. + */ + virtual incfs::map_ptr getIncFsBuffer(bool aligned) = 0; /* * Get the total amount of data that can be read. @@ -152,10 +165,6 @@ protected: AccessMode getAccessMode(void) const { return mAccessMode; } private: - /* these operations are not implemented */ - Asset(const Asset& src); - Asset& operator=(const Asset& src); - /* AssetManager needs access to our "create" functions */ friend class AssetManager; friend class ApkAssets; @@ -169,8 +178,7 @@ private: /* * Create the asset from a named, compressed file on disk (e.g. ".gz"). */ - static Asset* createFromCompressedFile(const char* fileName, - AccessMode mode); + static Asset* createFromCompressedFile(const char* fileName, AccessMode mode); #if 0 /* @@ -200,31 +208,21 @@ private: /* * Create the asset from a memory-mapped file segment. * - * The asset takes ownership of the FileMap. + * The asset takes ownership of the incfs::IncFsFileMap and the file descriptor "fd". The + * file descriptor is used to request new file descriptors using "openFileDescriptor". */ - 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 createFromUncompressedMap(std::unique_ptr dataMap, - base::unique_fd fd, AccessMode mode); + static std::unique_ptr createFromUncompressedMap(incfs::IncFsFileMap&& dataMap, + AccessMode mode, + base::unique_fd fd = {}); /* * Create the asset from a memory-mapped file segment with compressed * data. * - * The asset takes ownership of the FileMap. + * The asset takes ownership of the incfs::IncFsFileMap. */ - static Asset* createFromCompressedMap(FileMap* dataMap, - size_t uncompressedLen, AccessMode mode); - - static std::unique_ptr createFromCompressedMap(std::unique_ptr dataMap, - size_t uncompressedLen, AccessMode mode); - + static std::unique_ptr createFromCompressedMap(incfs::IncFsFileMap&& dataMap, + size_t uncompressedLen, AccessMode mode); /* * Create from a reference-counted chunk of shared memory. @@ -252,7 +250,7 @@ private: class _FileAsset : public Asset { public: _FileAsset(void); - virtual ~_FileAsset(void); + ~_FileAsset(void) override; /* * Use a piece of an already-open file. @@ -266,21 +264,24 @@ public: * * On success, the object takes ownership of "dataMap" and "fd". */ - status_t openChunk(FileMap* dataMap, base::unique_fd fd); + status_t openChunk(incfs::IncFsFileMap&& dataMap, base::unique_fd fd); /* * Standard Asset interfaces. */ - virtual ssize_t read(void* buf, size_t count); - virtual off64_t seek(off64_t offset, int whence); - virtual void close(void); - virtual const void* getBuffer(bool wordAligned); - virtual off64_t getLength(void) const { return mLength; } - virtual off64_t getRemainingLength(void) const { return mLength-mOffset; } - virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const; - virtual bool isAllocated(void) const { return mBuf != NULL; } + ssize_t read(void* buf, size_t count) override; + off64_t seek(off64_t offset, int whence) override; + void close(void) override; + const void* getBuffer(bool aligned) override; + incfs::map_ptr getIncFsBuffer(bool aligned) override; + off64_t getLength(void) const override { return mLength; } + off64_t getRemainingLength(void) const override { return mLength-mOffset; } + int openFileDescriptor(off64_t* outStart, off64_t* outLength) const override; + bool isAllocated(void) const override { return mBuf != NULL; } private: + incfs::map_ptr ensureAlignment(const incfs::IncFsFileMap& map); + 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 @@ -295,10 +296,8 @@ private: */ enum { kReadVsMapThreshold = 4096 }; - FileMap* mMap; // for memory map - unsigned char* mBuf; // for read - - const void* ensureAlignment(FileMap* map); + unsigned char* mBuf; // for read + std::optional mMap; // for memory map }; @@ -323,7 +322,7 @@ public: * * On success, the object takes ownership of "fd". */ - status_t openChunk(FileMap* dataMap, size_t uncompressedLen); + status_t openChunk(incfs::IncFsFileMap&& dataMap, size_t uncompressedLen); /* * Standard Asset interfaces. @@ -331,24 +330,23 @@ public: virtual ssize_t read(void* buf, size_t count); virtual off64_t seek(off64_t offset, int whence); virtual void close(void); - virtual const void* getBuffer(bool wordAligned); + virtual const void* getBuffer(bool aligned); + virtual incfs::map_ptr getIncFsBuffer(bool aligned); virtual off64_t getLength(void) const { return mUncompressedLen; } virtual off64_t getRemainingLength(void) const { return mUncompressedLen-mOffset; } virtual int openFileDescriptor(off64_t* /* outStart */, off64_t* /* outLength */) const { return -1; } virtual bool isAllocated(void) const { return mBuf != NULL; } private: - off64_t mStart; // offset to start of compressed data - off64_t mCompressedLen; // length of the compressed data - off64_t mUncompressedLen; // length of the uncompressed data - off64_t mOffset; // current offset, 0 == start of uncomp data + off64_t mStart; // offset to start of compressed data + off64_t mCompressedLen; // length of the compressed data + off64_t mUncompressedLen; // length of the uncompressed data + off64_t mOffset; // current offset, 0 == start of uncomp data + int mFd; // for file input - FileMap* mMap; // for memory-mapped input - int mFd; // for file input - - class StreamingZipInflater* mZipInflater; // for streaming large compressed assets - - unsigned char* mBuf; // for getBuffer() + class StreamingZipInflater* mZipInflater; // for streaming large compressed assets + unsigned char* mBuf; // for getBuffer() + std::optional mMap; // for memory-mapped input }; // need: shared mmap version? diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 30ef25c6a5169..4e993b0838a2b 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -131,8 +131,8 @@ class AssetManager2 { bool GetOverlayablesToString(const android::StringPiece& package_name, std::string* out) const; - const std::unordered_map* - GetOverlayableMapForPackage(uint32_t package_id) const; + const std::unordered_map* GetOverlayableMapForPackage( + uint32_t package_id) const; // Returns whether the resources.arsc of any loaded apk assets is allocated in RAM (not mmapped). bool ContainsAllocatedTable() const; @@ -145,14 +145,16 @@ class AssetManager2 { return configuration_; } - // Returns all configurations for which there are resources defined. This includes resource - // configurations in all the ApkAssets set for this AssetManager. + // Returns all configurations for which there are resources defined, or an I/O error if reading + // resource data failed. + // + // This includes resource configurations in all the ApkAssets set for this AssetManager. // If `exclude_system` is set to true, resource configurations from system APKs // ('android' package, other libraries) will be excluded from the list. // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap' // will be excluded from the list. - std::set GetResourceConfigurations(bool exclude_system = false, - bool exclude_mipmap = false) const; + base::expected, IOError> GetResourceConfigurations( + bool exclude_system = false, bool exclude_mipmap = false) const; // Returns all the locales for which there are resources defined. This includes resource // locales in all the ApkAssets set for this AssetManager. @@ -194,53 +196,106 @@ class AssetManager2 { std::unique_ptr OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie, Asset::AccessMode mode) const; - // Populates the `out_name` parameter with resource name information. - // Utf8 strings are preferred, and only if they are unavailable are - // the Utf16 variants populated. - // Returns false if the resource was not found or the name was missing/corrupt. - bool GetResourceName(uint32_t resid, ResourceName* out_name) const; - - // Populates `out_flags` with the bitmask of configuration axis that this resource varies with. - // See ResTable_config for the list of configuration axis. - // Returns false if the resource was not found. - bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const; + // Returns the resource name of the specified resource ID. + // + // Utf8 strings are preferred, and only if they are unavailable are the Utf16 variants populated. + // + // Returns a null error if the name is missing/corrupt, or an I/O error if reading resource data + // failed. + base::expected GetResourceName(uint32_t resid) const; // Finds the resource ID assigned to `resource_name`. + // // `resource_name` must be of the form '[package:][type/]entry'. // If no package is specified in `resource_name`, then `fallback_package` is used as the package. // If no type is specified in `resource_name`, then `fallback_type` is used as the type. - // Returns 0x0 if no resource by that name was found. - uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {}, - const std::string& fallback_package = {}) const; - - // Retrieves the best matching resource with ID `resid`. The resource value is filled into - // `out_value` and the configuration for the selected value is populated in `out_selected_config`. - // `out_flags` holds the same flags as retrieved with GetResourceFlags(). - // If `density_override` is non-zero, the configuration to match against is overridden with that - // density. // - // Returns a valid cookie if the resource was found. If the resource was not found, or if the - // resource was a map/bag type, then kInvalidCookie is returned. If `may_be_bag` is false, - // this function logs if the resource was a map/bag type before returning kInvalidCookie. - ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, - Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) const; + // Returns a null error if no resource by that name was found, or an I/O error if reading resource + // data failed. + base::expected GetResourceId( + const std::string& resource_name, const std::string& fallback_type = {}, + const std::string& fallback_package = {}) const; - // Resolves the resource reference in `in_out_value` if the data type is - // Res_value::TYPE_REFERENCE. - // `cookie` is the ApkAssetsCookie of the reference in `in_out_value`. - // `in_out_value` is the reference to resolve. The result is placed back into this object. - // `in_out_flags` is the type spec flags returned from calls to GetResource() or - // GetResourceFlags(). Configuration flags of the values pointed to by the reference - // are OR'd together with `in_out_flags`. - // `in_out_config` is populated with the configuration for which the resolved value was defined. - // `out_last_reference` is populated with the last reference ID before resolving to an actual - // value. This is only initialized if the passed in `in_out_value` is a reference. - // Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if - // it was not found. - ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, - ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) const; + struct SelectedValue { + friend AssetManager2; + friend Theme; + SelectedValue() = default; + SelectedValue(const ResolvedBag* bag, const ResolvedBag::Entry& entry) : + cookie(entry.cookie), data(entry.value.data), type(entry.value.dataType), + flags(bag->type_spec_flags), resid(0U), config({}) {}; + + // The cookie representing the ApkAssets in which the value resides. + ApkAssetsCookie cookie = kInvalidCookie; + + // The data for this value, as interpreted according to `type`. + Res_value::data_type data; + + // Type of the data value. + uint8_t type; + + // The bitmask of configuration axis that this resource varies with. + // See ResTable_config::CONFIG_*. + uint32_t flags; + + // The resource ID from which this value was resolved. + uint32_t resid; + + // The configuration for which the resolved value was defined. + ResTable_config config; + + private: + SelectedValue(uint8_t value_type, Res_value::data_type value_data, ApkAssetsCookie cookie, + uint32_t type_flags, uint32_t resid, const ResTable_config& config) : + cookie(cookie), data(value_data), type(value_type), flags(type_flags), + resid(resid), config(config) {}; + }; + + // Retrieves the best matching resource value with ID `resid`. + // + // If `may_be_bag` is false, this function logs if the resource was a map/bag type and returns a + // null result. If `density_override` is non-zero, the configuration to match against is + // overridden with that density. + // + // Returns a null error if a best match could not be found, or an I/O error if reading resource + // data failed. + base::expected GetResource(uint32_t resid, bool may_be_bag = false, + uint16_t density_override = 0U) const; + + // Resolves the resource referenced in `value` if the type is Res_value::TYPE_REFERENCE. + // + // If the data type is not Res_value::TYPE_REFERENCE, no work is done. Configuration flags of the + // values pointed to by the reference are OR'd into `value.flags`. + // + // Returns a null error if the resource could not be resolved, or an I/O error if reading + // resource data failed. + base::expected ResolveReference(SelectedValue& value) const; + + // Retrieves the best matching bag/map resource with ID `resid`. + // + // This method will resolve all parent references for this bag and merge keys with the child. + // To iterate over the keys, use the following idiom: + // + // base::expected bag = asset_manager->GetBag(id); + // if (bag.has_value()) { + // for (auto iter = begin(*bag); iter != end(*bag); ++iter) { + // ... + // } + // } + // + // Returns a null error if a best match could not be found, or an I/O error if reading resource + // data failed. + base::expected GetBag(uint32_t resid) const; + + // Retrieves the best matching bag/map resource of the resource referenced in `value`. + // + // If `value.type` is not Res_value::TYPE_REFERENCE, a null result is returned. + // Configuration flags of the bag pointed to by the reference are OR'd into `value.flags`. + // + // Returns a null error if a best match could not be found, or an I/O error if reading resource + // data failed. + base::expected ResolveBag(SelectedValue& value) const; + + const std::vector GetBagResIdStack(uint32_t resid) const; // Resets the resource resolution structures in preparation for the next resource retrieval. void ResetResourceResolution() const; @@ -252,20 +307,6 @@ class AssetManager2 { // resolved yet. std::string GetLastResourceResolution() const; - const std::vector GetBagResIdStack(uint32_t resid); - - // Retrieves the best matching bag/map resource with ID `resid`. - // This method will resolve all parent references for this bag and merge keys with the child. - // To iterate over the keys, use the following idiom: - // - // const AssetManager2::ResolvedBag* bag = asset_manager->GetBag(id); - // if (bag != nullptr) { - // for (auto iter = begin(bag); iter != end(bag); ++iter) { - // ... - // } - // } - const ResolvedBag* GetBag(uint32_t resid); - // Creates a new Theme from this AssetManager. std::unique_ptr NewTheme(); @@ -286,11 +327,15 @@ class AssetManager2 { private: DISALLOW_COPY_AND_ASSIGN(AssetManager2); + struct TypeConfig { + incfs::verified_map_ptr type; + ResTable_config config; + }; + // A collection of configurations and their associated ResTable_type that match the current // AssetManager configuration. struct FilteredConfigGroup { - std::vector configurations; - std::vector types; + std::vector type_configs; }; // Represents an single package. @@ -331,9 +376,7 @@ class AssetManager2 { }; // 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 - // the ApkAssets in which the entry was found. + // Res_value, or a complex map/bag type. Returns a null result if a best entry cannot be found. // // `density_override` overrides the density of the current configuration when doing a search. // @@ -347,13 +390,15 @@ class AssetManager2 { // // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. - ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, - bool ignore_configuration, FindEntryResult* out_entry) const; + base::expected FindEntry(uint32_t resid, + uint16_t density_override, + bool stop_at_first_match, + bool ignore_configuration) 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; + base::expected 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) const; // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. @@ -372,7 +417,8 @@ class AssetManager2 { // 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& child_resids); + base::expected GetBag( + uint32_t resid, std::vector& child_resids) const; // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. @@ -394,11 +440,11 @@ class AssetManager2 { // Cached set of bags. These are cached because they can inherit keys from parent bags, // which involves some calculation. - std::unordered_map> cached_bags_; + mutable std::unordered_map> cached_bags_; // Cached set of bag resid stacks for each bag. These are cached because they might be requested // a number of times for each view during View inspection. - std::unordered_map> cached_bag_resid_stacks_; + mutable std::unordered_map> cached_bag_resid_stacks_; // Whether or not to save resource resolution steps bool resource_resolution_logging_enabled_ = false; @@ -455,55 +501,53 @@ class Theme { public: ~Theme(); - // Applies the style identified by `resid` to this theme. This can be called - // multiple times with different styles. By default, any theme attributes that - // are already defined before this call are not overridden. If `force` is set - // to true, this behavior is changed and all theme attributes from the style at - // `resid` are applied. - // Returns false if the style failed to apply. - bool ApplyStyle(uint32_t resid, bool force = false); + // Applies the style identified by `resid` to this theme. + // + // This can be called multiple times with different styles. By default, any theme attributes that + // are already defined before this call are not overridden. If `force` is set to true, this + // behavior is changed and all theme attributes from the style at `resid` are applied. + // + // Returns a null error if the style could not be applied, or an I/O error if reading resource + // data failed. + base::expected ApplyStyle(uint32_t resid, bool force = false); - // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme. - // If `o` does not have the same AssetManager as this theme, only attributes from ApkAssets loaded - // into both AssetManagers will be copied to this theme. - void SetTo(const Theme& o); + // Sets this Theme to be a copy of `other` if `other` has the same AssetManager as this Theme. + // + // If `other` does not have the same AssetManager as this theme, only attributes from ApkAssets + // loaded into both AssetManagers will be copied to this theme. + // + // Returns an I/O error if reading resource data failed. + base::expected SetTo(const Theme& other); void Clear(); - void Dump() const; + // Retrieves the value of attribute ID `resid` in the theme. + // + // NOTE: This function does not do reference traversal. If you want to follow references to other + // resources to get the "real" value to use, you need to call ResolveReference() after this + // function. + std::optional GetAttribute(uint32_t resid) const; - inline const AssetManager2* GetAssetManager() const { + // This is like AssetManager2::ResolveReference(), but also takes care of resolving attribute + // references to the theme. + base::expected ResolveAttributeReference( + AssetManager2::SelectedValue& value) const; + + AssetManager2* GetAssetManager() { return asset_manager_; } - inline AssetManager2* GetAssetManager() { + const AssetManager2* GetAssetManager() const { return asset_manager_; } // Returns a bit mask of configuration changes that will impact this // theme (and thus require completely reloading it). - inline uint32_t GetChangingConfigurations() const { + uint32_t GetChangingConfigurations() const { return type_spec_flags_; } - // Retrieve a value in the theme. If the theme defines this value, returns an asset cookie - // indicating which ApkAssets it came from and populates `out_value` with the value. - // `out_flags` is populated with a bitmask of the configuration axis with which the resource - // varies. - // - // If the attribute is not found, returns kInvalidCookie. - // - // NOTE: This function does not do reference traversal. If you want to follow references to other - // resources to get the "real" value to use, you need to call ResolveReference() after this - // function. - ApkAssetsCookie GetAttribute(uint32_t resid, Res_value* out_value, uint32_t* out_flags) const; - - // This is like AssetManager2::ResolveReference(), but also takes - // care of resolving attribute references to the theme. - ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, - ResTable_config* in_out_selected_config = nullptr, - uint32_t* in_out_type_spec_flags = nullptr, - uint32_t* out_last_ref = nullptr) const; + void Dump() const; private: DISALLOW_COPY_AND_ASSIGN(Theme); diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index d71aad29d917a..1a69a309d3659 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -45,20 +45,28 @@ enum { // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid, - uint32_t* src_values, size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); +base::expected ResolveAttrs(Theme* theme, uint32_t def_style_attr, + uint32_t def_style_resid, uint32_t* src_values, + size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, + uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` is NOT optional and must NOT be nullptr. -void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, - uint32_t* out_values, uint32_t* out_indices); +base::expected ApplyStyle(Theme* theme, ResXMLParser* xml_parser, + uint32_t def_style_attr, + uint32_t def_style_resid, + const uint32_t* attrs, size_t attrs_length, + uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); +base::expected RetrieveAttributes(AssetManager2* assetmanager, + ResXMLParser* xml_parser, + uint32_t* attrs, + size_t attrs_length, + uint32_t* out_values, + uint32_t* out_indices); } // namespace android diff --git a/libs/androidfw/include/androidfw/Chunk.h b/libs/androidfw/include/androidfw/Chunk.h index a0f23433c676c..f1c43b298e535 100644 --- a/libs/androidfw/include/androidfw/Chunk.h +++ b/libs/androidfw/include/androidfw/Chunk.h @@ -36,7 +36,7 @@ namespace android { // of the chunk. class Chunk { public: - explicit Chunk(const ResChunk_header* chunk) : device_chunk_(chunk) {} + explicit Chunk(incfs::verified_map_ptr chunk) : device_chunk_(chunk) {} // Returns the type of the chunk. Caller need not worry about endianness. inline int type() const { return dtohs(device_chunk_->type); } @@ -49,21 +49,18 @@ class Chunk { inline size_t header_size() const { return dtohs(device_chunk_->headerSize); } template - inline const T* header() const { - if (header_size() >= MinSize) { - return reinterpret_cast(device_chunk_); - } - return nullptr; + inline incfs::map_ptr header() const { + return (header_size() >= MinSize) ? device_chunk_.convert() : nullptr; } - inline const void* data_ptr() const { - return reinterpret_cast(device_chunk_) + header_size(); + inline incfs::map_ptr data_ptr() const { + return device_chunk_.offset(header_size()); } inline size_t data_size() const { return size() - header_size(); } private: - const ResChunk_header* device_chunk_; + const incfs::verified_map_ptr device_chunk_; }; // Provides a Java style iterator over an array of ResChunk_header's. @@ -84,11 +81,11 @@ class Chunk { // class ChunkIterator { public: - ChunkIterator(const void* data, size_t len) - : next_chunk_(reinterpret_cast(data)), + ChunkIterator(incfs::map_ptr data, size_t len) + : next_chunk_(data.convert()), len_(len), last_error_(nullptr) { - CHECK(next_chunk_ != nullptr) << "data can't be nullptr"; + CHECK((bool) next_chunk_) << "data can't be null"; if (len_ != 0) { VerifyNextChunk(); } @@ -113,7 +110,7 @@ class ChunkIterator { // Returns false if there was an error. For legacy purposes. bool VerifyNextChunkNonFatal(); - const ResChunk_header* next_chunk_; + incfs::map_ptr next_chunk_; size_t len_; const char* last_error_; bool last_error_was_fatal_ = true; diff --git a/libs/androidfw/include/androidfw/Errors.h b/libs/androidfw/include/androidfw/Errors.h new file mode 100644 index 0000000000000..948162d104808 --- /dev/null +++ b/libs/androidfw/include/androidfw/Errors.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 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_ERRORS_H_ +#define ANDROIDFW_ERRORS_H_ + +#include +#include + +#include + +namespace android { + +enum class IOError { + // Used when reading a file residing on an IncFs file-system times out. + PAGES_MISSING = -1, +}; + +// Represents an absent result or an I/O error. +using NullOrIOError = std::variant; + +// Checks whether the result holds an unexpected I/O error. +template +static inline bool IsIOError(const base::expected result) { + return !result.has_value() && std::holds_alternative(result.error()); +} + +static inline IOError GetIOError(const NullOrIOError& error) { + return std::get(error); +} + +} // namespace android + +#endif //ANDROIDFW_ERRORS_H_ diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index ab0f47f025d20..fdab03ba2de44 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -40,8 +40,8 @@ class IdmapResMap; 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; + base::expected stringAt(size_t idx) const override; + base::expected string8At(size_t idx) const override; size_t size() const override; explicit OverlayStringPool(const LoadedIdmap* loaded_idmap); diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 89ff9f52125df..17d97a2a2e73d 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -23,7 +23,8 @@ #include #include -#include "android-base/macros.h" +#include +#include #include "androidfw/ByteBucketArray.h" #include "androidfw/Chunk.h" @@ -49,7 +50,7 @@ struct TypeSpec { // Pointer to the mmapped data where flags are kept. // Flags denote whether the resource entry is public // and under which configurations it varies. - const ResTable_typeSpec* type_spec; + incfs::verified_map_ptr type_spec; // The number of types that follow this struct. // There is a type for each configuration that entries are defined for. @@ -57,15 +58,17 @@ struct TypeSpec { // Trick to easily access a variable number of Type structs // proceeding this struct, and to ensure their alignment. - const ResTable_type* types[0]; + incfs::verified_map_ptr types[0]; - inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const { + base::expected GetFlagsForEntryIndex(uint16_t entry_index) const { if (entry_index >= dtohl(type_spec->entryCount)) { - return 0u; + return 0U; } - - const uint32_t* flags = reinterpret_cast(type_spec + 1); - return flags[entry_index]; + const auto entry_flags_ptr = ((type_spec + 1).convert() + entry_index); + if (!entry_flags_ptr) { + return base::unexpected(IOError::PAGES_MISSING); + } + return entry_flags_ptr.value(); } }; @@ -161,13 +164,17 @@ class LoadedPackage { // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. // Returns a partial resource ID, with the package ID left as 0x00. The caller is responsible // for patching the correct package ID to the resource ID. - uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; + base::expected FindEntryByName(const std::u16string& type_name, + const std::u16string& entry_name) const; - static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index); + static base::expected, NullOrIOError> GetEntry( + incfs::verified_map_ptr type_chunk, uint16_t entry_index); - static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index); + static base::expected GetEntryOffset( + incfs::verified_map_ptr type_chunk, uint16_t entry_index); - static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset); + static base::expected, NullOrIOError> GetEntryFromOffset( + incfs::verified_map_ptr type_chunk, uint32_t offset); // Returns the string pool where type names are stored. inline const ResStringPool* GetTypeStringPool() const { @@ -220,7 +227,8 @@ class LoadedPackage { // Populates a set of ResTable_config structs, possibly excluding configurations defined for // the mipmap type. - void CollectConfigurations(bool exclude_mipmap, std::set* out_configs) const; + base::expected CollectConfigurations( + bool exclude_mipmap, std::set* out_configs) const; // Populates a set of strings representing locales. // If `canonicalize` is set to true, each locale is transformed into its canonical format @@ -300,7 +308,8 @@ class LoadedArsc { // If `load_as_shared_library` is set to true, the application package (0x7f) is treated // as a shared library (0x00). When loaded into an AssetManager, the package will be assigned an // ID. - static std::unique_ptr Load(const StringPiece& data, + static std::unique_ptr Load(incfs::map_ptr data, + size_t length, const LoadedIdmap* loaded_idmap = nullptr, package_property_t property_flags = 0U); diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 04ba78b6705db..fb5f864731891 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -20,7 +20,10 @@ #ifndef _LIBS_UTILS_RESOURCE_TYPES_H #define _LIBS_UTILS_RESOURCE_TYPES_H +#include + #include +#include #include #include #include @@ -497,7 +500,7 @@ public: virtual ~ResStringPool(); void setToEmpty(); - status_t setTo(const void* data, size_t size, bool copyData=false); + status_t setTo(incfs::map_ptr data, size_t size, bool copyData=false); status_t getError() const; @@ -505,48 +508,49 @@ public: // Return string entry as UTF16; if the pool is UTF8, the string will // be converted before returning. - inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const { - return stringAt(ref.index, outLen); + inline base::expected stringAt( + const ResStringPool_ref& ref) const { + return stringAt(ref.index); } - virtual const char16_t* stringAt(size_t idx, size_t* outLen) const; + virtual base::expected stringAt(size_t idx) const; // Note: returns null if the string pool is not UTF8. - virtual const char* string8At(size_t idx, size_t* outLen) const; + virtual base::expected string8At(size_t idx) const; // Return string whether the pool is UTF8 or UTF16. Does not allow you // to distinguish null. - const String8 string8ObjectAt(size_t idx) const; + base::expected string8ObjectAt(size_t idx) const; - const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const; - const ResStringPool_span* styleAt(size_t idx) const; + base::expected, NullOrIOError> styleAt( + const ResStringPool_ref& ref) const; + base::expected, NullOrIOError> styleAt(size_t idx) const; - ssize_t indexOfString(const char16_t* str, size_t strLen) const; + base::expected indexOfString(const char16_t* str, size_t strLen) const; virtual size_t size() const; size_t styleCount() const; size_t bytes() const; - const void* data() const; - + incfs::map_ptr data() const; bool isSorted() const; bool isUTF8() const; private: - status_t mError; - void* mOwnedData; - const ResStringPool_header* mHeader; - size_t mSize; - mutable Mutex mDecodeLock; - const uint32_t* mEntries; - const uint32_t* mEntryStyles; - const void* mStrings; - char16_t mutable** mCache; - uint32_t mStringPoolSize; // number of uint16_t - const uint32_t* mStyles; - uint32_t mStylePoolSize; // number of uint32_t + status_t mError; + void* mOwnedData; + incfs::verified_map_ptr mHeader; + size_t mSize; + mutable Mutex mDecodeLock; + incfs::map_ptr mEntries; + incfs::map_ptr mEntryStyles; + incfs::map_ptr mStrings; + char16_t mutable** mCache; + uint32_t mStringPoolSize; // number of uint16_t + incfs::map_ptr mStyles; + uint32_t mStylePoolSize; // number of uint32_t - const char* stringDecodeAt(size_t idx, const uint8_t* str, const size_t encLen, - size_t* outLen) const; + base::expected stringDecodeAt( + size_t idx, incfs::map_ptr str, size_t encLen) const; }; /** @@ -558,8 +562,8 @@ public: StringPoolRef() = default; StringPoolRef(const ResStringPool* pool, uint32_t index); - const char* string8(size_t* outLen) const; - const char16_t* string16(size_t* outLen) const; + base::expected string8() const; + base::expected string16() const; private: const ResStringPool* mPool = nullptr; @@ -1797,6 +1801,16 @@ private: bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue); +template +static const TChar* UnpackOptionalString(base::expected, E>&& result, + size_t* outLen) { + if (result.has_value()) { + *outLen = result->size(); + return result->data(); + } + return NULL; +} + /** * Convenience class for accessing data in a ResTable resource. */ diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h index e649940cdde1f..bd1c44033b881 100644 --- a/libs/androidfw/include/androidfw/ResourceUtils.h +++ b/libs/androidfw/include/androidfw/ResourceUtils.h @@ -30,13 +30,12 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin // Convert a type_string_ref, entry_string_ref, and package to AssetManager2::ResourceName. // Useful for getting resource name without re-running AssetManager2::FindEntry searches. -bool ToResourceName(const StringPoolRef& type_string_ref, - const StringPoolRef& entry_string_ref, - const StringPiece& package_name, - AssetManager2::ResourceName* out_name); +base::expected ToResourceName( + const StringPoolRef& type_string_ref, const StringPoolRef& entry_string_ref, + const StringPiece& package_name); // Formats a ResourceName to "package:type/entry_name". -std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name); +std::string ToFormattedResourceString(const AssetManager2::ResourceName& resource_name); inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) { return (resid & 0x00ffffffu) | (static_cast(package_id) << 24); diff --git a/libs/androidfw/include/androidfw/StreamingZipInflater.h b/libs/androidfw/include/androidfw/StreamingZipInflater.h index 3ace5d5a83cf1..472b794b911c2 100644 --- a/libs/androidfw/include/androidfw/StreamingZipInflater.h +++ b/libs/androidfw/include/androidfw/StreamingZipInflater.h @@ -19,6 +19,8 @@ #include #include + +#include #include #include @@ -34,7 +36,7 @@ public: StreamingZipInflater(int fd, off64_t compDataStart, size_t uncompSize, size_t compSize); // Flavor that gets the compressed data from an in-memory buffer - StreamingZipInflater(class FileMap* dataMap, size_t uncompSize); + StreamingZipInflater(const incfs::IncFsFileMap* dataMap, size_t uncompSize); ~StreamingZipInflater(); @@ -54,7 +56,7 @@ private: // where to find the uncompressed data int mFd; off64_t mInFileStart; // where the compressed data lives in the file - class FileMap* mDataMap; + const incfs::IncFsFileMap* mDataMap; z_stream mInflateState; bool mStreamNeedsInit; diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h index 9a3646b49db87..aceeeccccb611 100644 --- a/libs/androidfw/include/androidfw/Util.h +++ b/libs/androidfw/include/androidfw/Util.h @@ -22,7 +22,8 @@ #include #include -#include "android-base/macros.h" +#include +#include #include "androidfw/StringPiece.h" @@ -126,6 +127,11 @@ std::string Utf16ToUtf8(const StringPiece16& utf16); std::vector SplitAndLowercase(const android::StringPiece& str, char sep); +template +bool IsFourByteAligned(const incfs::map_ptr& data) { + return ((size_t)data.unsafe_ptr() & 0x3U) == 0; +} + } // namespace util } // namespace android diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h index c221e3b7aeae0..10f6d0655bf4a 100644 --- a/libs/androidfw/include/androidfw/ZipFileRO.h +++ b/libs/androidfw/include/androidfw/ZipFileRO.h @@ -30,17 +30,20 @@ #ifndef __LIBS_ZIPFILERO_H #define __LIBS_ZIPFILERO_H -#include -#include -#include -#include - +#include #include #include #include #include #include +#include + +#include +#include +#include +#include + struct ZipArchive; typedef ZipArchive* ZipArchiveHandle; @@ -136,13 +139,25 @@ public: uint32_t* pCrc32) const; /* - * Create a new FileMap object that maps a subset of the archive. For + * Create a new FileMap object that maps a subset of the archive. For * an uncompressed entry this effectively provides a pointer to the * actual data, for a compressed entry this provides the input buffer * for inflate(). + * + * Use this function if the archive can never reside on IncFs. */ FileMap* createEntryFileMap(ZipEntryRO entry) const; + /* + * Create a new incfs::IncFsFileMap object that maps a subset of the archive. For + * an uncompressed entry this effectively provides a pointer to the + * actual data, for a compressed entry this provides the input buffer + * for inflate(). + * + * Use this function if the archive can potentially reside on IncFs. + */ + std::optional createEntryIncFsFileMap(ZipEntryRO entry) const; + /* * Uncompress the data into a buffer. Depending on the compression * format, this is either an "inflate" operation or a memcpy. diff --git a/libs/androidfw/include/androidfw/ZipUtils.h b/libs/androidfw/include/androidfw/ZipUtils.h index 4d35e992cc89b..dbfec34fda89a 100644 --- a/libs/androidfw/include/androidfw/ZipUtils.h +++ b/libs/androidfw/include/androidfw/ZipUtils.h @@ -25,6 +25,8 @@ #include #include +#include "util/map_ptr.h" + namespace android { /* @@ -40,8 +42,8 @@ public: long compressedLen); static bool inflateToBuffer(int fd, void* buf, long uncompressedLen, long compressedLen); - static bool inflateToBuffer(const void *in, void* buf, long uncompressedLen, - long compressedLen); + static bool inflateToBuffer(incfs::map_ptr in, void* buf, + long uncompressedLen, long compressedLen); /* * Someday we might want to make this generic and handle bzip2 ".bz2" diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index 437e147729648..c7ae618991b9f 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -139,9 +139,13 @@ static void BM_AssetManagerGetBag(benchmark::State& state) { assets.SetApkAssets({apk.get()}); while (state.KeepRunning()) { - const ResolvedBag* bag = assets.GetBag(app::R::style::StyleTwo); - const auto bag_end = end(bag); - for (auto iter = begin(bag); iter != bag_end; ++iter) { + auto bag = assets.GetBag(app::R::style::StyleTwo); + if (!bag.has_value()) { + state.SkipWithError("Failed to load get bag"); + return; + } + const auto bag_end = end(*bag); + for (auto iter = begin(*bag); iter != bag_end; ++iter) { uint32_t key = iter->key; Res_value value = iter->value; benchmark::DoNotOptimize(key); diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 8c255d16fe1f3..3638ce1f92d72 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -108,24 +108,18 @@ TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) { assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_.get()}); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, - 0 /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + auto value = assetmanager.GetResource(basic::R::string::test1); + ASSERT_TRUE(value.has_value()); // Came from our ApkAssets. - EXPECT_EQ(0, cookie); + EXPECT_EQ(0, value->cookie); // It is the default config. - EXPECT_EQ(0, selected_config.language[0]); - EXPECT_EQ(0, selected_config.language[1]); + EXPECT_EQ(0, value->config.language[0]); + EXPECT_EQ(0, value->config.language[1]); // It is a string. - EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); + EXPECT_EQ(Res_value::TYPE_STRING, value->type); } TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) { @@ -138,24 +132,18 @@ TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) { assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()}); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, - 0 /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + auto value = assetmanager.GetResource(basic::R::string::test1); + ASSERT_TRUE(value.has_value()); // Came from our de_fr ApkAssets. - EXPECT_EQ(1, cookie); + EXPECT_EQ(1, value->cookie); // The configuration is German. - EXPECT_EQ('d', selected_config.language[0]); - EXPECT_EQ('e', selected_config.language[1]); + EXPECT_EQ('d', value->config.language[0]); + EXPECT_EQ('e', value->config.language[1]); // It is a string. - EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); + EXPECT_EQ(Res_value::TYPE_STRING, value->type); } TEST_F(AssetManager2Test, FindsResourceFromSharedLibrary) { @@ -166,44 +154,35 @@ TEST_F(AssetManager2Test, FindsResourceFromSharedLibrary) { assetmanager.SetApkAssets( {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - - ApkAssetsCookie cookie = - assetmanager.GetResource(libclient::R::string::foo_one, false /*may_be_bag*/, - 0 /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + auto value = assetmanager.GetResource(libclient::R::string::foo_one); + ASSERT_TRUE(value.has_value()); // Reference comes from libclient. - EXPECT_EQ(2, cookie); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); + EXPECT_EQ(2, value->cookie); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); // Lookup the reference. - cookie = assetmanager.GetResource(value.data, false /* may_be_bag */, 0 /* density_override*/, - &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(1, cookie); - EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); + value = assetmanager.GetResource(value->data); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(1, value->cookie); + EXPECT_EQ(Res_value::TYPE_STRING, value->type); EXPECT_EQ(std::string("Foo from lib_one"), - GetStringFromPool(assetmanager.GetStringPoolForCookie(cookie), value.data)); + GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie), value->data)); - cookie = assetmanager.GetResource(libclient::R::string::foo_two, false /*may_be_bag*/, - 0 /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + value = assetmanager.GetResource(libclient::R::string::foo_two); + ASSERT_TRUE(value.has_value()); // Reference comes from libclient. - EXPECT_EQ(2, cookie); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); + EXPECT_EQ(2, value->cookie); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); // Lookup the reference. - cookie = assetmanager.GetResource(value.data, false /* may_be_bag */, 0 /* density_override*/, - &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(0, cookie); - EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); + value = assetmanager.GetResource(value->data); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(0, value->cookie); + EXPECT_EQ(Res_value::TYPE_STRING, value->type); EXPECT_EQ(std::string("Foo from lib_two"), - GetStringFromPool(assetmanager.GetStringPoolForCookie(cookie), value.data)); + GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie), value->data)); } TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) { @@ -211,16 +190,10 @@ TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) { assetmanager.SetApkAssets({appaslib_assets_.get()}); // The appaslib package will have been assigned the package ID 0x02. - - Res_value value; - ResTable_config selected_config; - uint32_t flags; - ApkAssetsCookie cookie = assetmanager.GetResource( - fix_package_id(appaslib::R::integer::number1, 0x02), false /*may_be_bag*/, - 0u /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); - EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data); + auto value = assetmanager.GetResource(fix_package_id(appaslib::R::integer::number1, 0x02)); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); + EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value->data); } TEST_F(AssetManager2Test, AssignsOverlayPackageIdLast) { @@ -238,40 +211,40 @@ TEST_F(AssetManager2Test, AssignsOverlayPackageIdLast) { return assetmanager.GetAssignedPackageId(apkAssets->GetLoadedArsc()->GetPackages()[0].get()); }; - ASSERT_EQ(get_first_package_id(overlayable_assets_.get()), 0x7f); - ASSERT_EQ(get_first_package_id(overlay_assets_.get()), 0x03); - ASSERT_EQ(get_first_package_id(lib_one_assets_.get()), 0x02); + ASSERT_EQ(0x7f, get_first_package_id(overlayable_assets_.get())); + ASSERT_EQ(0x03, get_first_package_id(overlay_assets_.get())); + ASSERT_EQ(0x02, get_first_package_id(lib_one_assets_.get())); } TEST_F(AssetManager2Test, GetSharedLibraryResourceName) { AssetManager2 assetmanager; assetmanager.SetApkAssets({lib_one_assets_.get()}); - AssetManager2::ResourceName name; - ASSERT_TRUE(assetmanager.GetResourceName(lib_one::R::string::foo, &name)); - std::string formatted_name = ToFormattedResourceString(&name); - ASSERT_EQ(formatted_name, "com.android.lib_one:string/foo"); + auto name = assetmanager.GetResourceName(lib_one::R::string::foo); + ASSERT_TRUE(name.has_value()); + ASSERT_EQ("com.android.lib_one:string/foo", ToFormattedResourceString(*name)); } TEST_F(AssetManager2Test, FindsBagResourceFromSingleApkAssets) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()}); - const ResolvedBag* bag = assetmanager.GetBag(basic::R::array::integerArray1); - ASSERT_NE(nullptr, bag); - ASSERT_EQ(3u, bag->entry_count); + auto bag = assetmanager.GetBag(basic::R::array::integerArray1); + ASSERT_TRUE(bag.has_value()); - EXPECT_EQ(static_cast(Res_value::TYPE_INT_DEC), bag->entries[0].value.dataType); - EXPECT_EQ(1u, bag->entries[0].value.data); - EXPECT_EQ(0, bag->entries[0].cookie); + ASSERT_EQ(3u, (*bag)->entry_count); - EXPECT_EQ(static_cast(Res_value::TYPE_INT_DEC), bag->entries[1].value.dataType); - EXPECT_EQ(2u, bag->entries[1].value.data); - EXPECT_EQ(0, bag->entries[1].cookie); + EXPECT_EQ(static_cast(Res_value::TYPE_INT_DEC), (*bag)->entries[0].value.dataType); + EXPECT_EQ(1u, (*bag)->entries[0].value.data); + EXPECT_EQ(0, (*bag)->entries[0].cookie); - EXPECT_EQ(static_cast(Res_value::TYPE_INT_DEC), bag->entries[2].value.dataType); - EXPECT_EQ(3u, bag->entries[2].value.data); - EXPECT_EQ(0, bag->entries[2].cookie); + EXPECT_EQ(static_cast(Res_value::TYPE_INT_DEC), (*bag)->entries[1].value.dataType); + EXPECT_EQ(2u, (*bag)->entries[1].value.data); + EXPECT_EQ(0, (*bag)->entries[1].cookie); + + EXPECT_EQ(static_cast(Res_value::TYPE_INT_DEC), (*bag)->entries[2].value.dataType); + EXPECT_EQ(3u, (*bag)->entries[2].value.data); + EXPECT_EQ(0, (*bag)->entries[2].cookie); } TEST_F(AssetManager2Test, FindsBagResourceFromMultipleApkAssets) {} @@ -284,15 +257,16 @@ TEST_F(AssetManager2Test, FindsBagResourceFromSharedLibrary) { assetmanager.SetApkAssets( {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); - const ResolvedBag* bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03)); - ASSERT_NE(nullptr, bag); - ASSERT_GE(bag->entry_count, 2u); + auto bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03)); + ASSERT_TRUE(bag.has_value()); + + ASSERT_GE((*bag)->entry_count, 2u); // First two attributes come from lib_one. - EXPECT_EQ(1, bag->entries[0].cookie); - EXPECT_EQ(0x03, get_package_id(bag->entries[0].key)); - EXPECT_EQ(1, bag->entries[1].cookie); - EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); + EXPECT_EQ(1, (*bag)->entries[0].cookie); + EXPECT_EQ(0x03, get_package_id((*bag)->entries[0].key)); + EXPECT_EQ(1, (*bag)->entries[1].cookie); + EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key)); } TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) { @@ -303,17 +277,17 @@ TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) { 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); + auto bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib); + ASSERT_TRUE(bag.has_value()); + 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)); + 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)); + EXPECT_EQ(2, (*bag)->entries[1].cookie); + EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key)); } TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) { @@ -324,79 +298,79 @@ TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) { assetmanager.SetApkAssets( {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); - const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::Theme); - ASSERT_NE(nullptr, bag); - ASSERT_GE(bag->entry_count, 2u); + auto bag = assetmanager.GetBag(libclient::R::style::Theme); + ASSERT_TRUE(bag.has_value()); + ASSERT_GE((*bag)->entry_count, 2u); // First two attributes come from lib_one. - EXPECT_EQ(1, bag->entries[0].cookie); - EXPECT_EQ(0x03, get_package_id(bag->entries[0].key)); - EXPECT_EQ(1, bag->entries[1].cookie); - EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); + EXPECT_EQ(1, (*bag)->entries[0].cookie); + EXPECT_EQ(0x03, get_package_id((*bag)->entries[0].key)); + EXPECT_EQ(1, (*bag)->entries[1].cookie); + EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key)); } TEST_F(AssetManager2Test, MergesStylesWithParentFromSingleApkAssets) { AssetManager2 assetmanager; assetmanager.SetApkAssets({style_assets_.get()}); - const ResolvedBag* bag_one = assetmanager.GetBag(app::R::style::StyleOne); - ASSERT_NE(nullptr, bag_one); - ASSERT_EQ(2u, bag_one->entry_count); + auto bag_one = assetmanager.GetBag(app::R::style::StyleOne); + ASSERT_TRUE(bag_one.has_value()); + ASSERT_EQ(2u, (*bag_one)->entry_count); - EXPECT_EQ(app::R::attr::attr_one, bag_one->entries[0].key); - EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[0].value.dataType); - EXPECT_EQ(1u, bag_one->entries[0].value.data); - EXPECT_EQ(0, bag_one->entries[0].cookie); + EXPECT_EQ(app::R::attr::attr_one, (*bag_one)->entries[0].key); + EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_one)->entries[0].value.dataType); + EXPECT_EQ(1u, (*bag_one)->entries[0].value.data); + EXPECT_EQ(0, (*bag_one)->entries[0].cookie); - EXPECT_EQ(app::R::attr::attr_two, bag_one->entries[1].key); - EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[1].value.dataType); - EXPECT_EQ(2u, bag_one->entries[1].value.data); - EXPECT_EQ(0, bag_one->entries[1].cookie); + EXPECT_EQ(app::R::attr::attr_two, (*bag_one)->entries[1].key); + EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_one)->entries[1].value.dataType); + EXPECT_EQ(2u, (*bag_one)->entries[1].value.data); + EXPECT_EQ(0, (*bag_one)->entries[1].cookie); - const ResolvedBag* bag_two = assetmanager.GetBag(app::R::style::StyleTwo); - ASSERT_NE(nullptr, bag_two); - ASSERT_EQ(6u, bag_two->entry_count); + auto bag_two = assetmanager.GetBag(app::R::style::StyleTwo); + ASSERT_TRUE(bag_two.has_value()); + ASSERT_EQ(6u, (*bag_two)->entry_count); // attr_one is inherited from StyleOne. - EXPECT_EQ(app::R::attr::attr_one, bag_two->entries[0].key); - EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[0].value.dataType); - EXPECT_EQ(1u, bag_two->entries[0].value.data); - EXPECT_EQ(0, bag_two->entries[0].cookie); - EXPECT_EQ(app::R::style::StyleOne, bag_two->entries[0].style); + EXPECT_EQ(app::R::attr::attr_one, (*bag_two)->entries[0].key); + EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_two)->entries[0].value.dataType); + EXPECT_EQ(1u, (*bag_two)->entries[0].value.data); + EXPECT_EQ(0, (*bag_two)->entries[0].cookie); + EXPECT_EQ(app::R::style::StyleOne, (*bag_two)->entries[0].style); // attr_two should be overridden from StyleOne by StyleTwo. - EXPECT_EQ(app::R::attr::attr_two, bag_two->entries[1].key); - EXPECT_EQ(Res_value::TYPE_STRING, bag_two->entries[1].value.dataType); - EXPECT_EQ(0, bag_two->entries[1].cookie); - EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[1].style); + EXPECT_EQ(app::R::attr::attr_two, (*bag_two)->entries[1].key); + EXPECT_EQ(Res_value::TYPE_STRING, (*bag_two)->entries[1].value.dataType); + EXPECT_EQ(0, (*bag_two)->entries[1].cookie); + EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[1].style); EXPECT_EQ(std::string("string"), GetStringFromPool(assetmanager.GetStringPoolForCookie(0), - bag_two->entries[1].value.data)); + (*bag_two)->entries[1].value.data)); // The rest are new attributes. - EXPECT_EQ(app::R::attr::attr_three, bag_two->entries[2].key); - EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, bag_two->entries[2].value.dataType); - EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[2].value.data); - EXPECT_EQ(0, bag_two->entries[2].cookie); - EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[2].style); + EXPECT_EQ(app::R::attr::attr_three, (*bag_two)->entries[2].key); + EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, (*bag_two)->entries[2].value.dataType); + EXPECT_EQ(app::R::attr::attr_indirect, (*bag_two)->entries[2].value.data); + EXPECT_EQ(0, (*bag_two)->entries[2].cookie); + EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[2].style); - EXPECT_EQ(app::R::attr::attr_five, bag_two->entries[3].key); - EXPECT_EQ(Res_value::TYPE_REFERENCE, bag_two->entries[3].value.dataType); - EXPECT_EQ(app::R::string::string_one, bag_two->entries[3].value.data); - EXPECT_EQ(0, bag_two->entries[3].cookie); - EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[3].style); + EXPECT_EQ(app::R::attr::attr_five, (*bag_two)->entries[3].key); + EXPECT_EQ(Res_value::TYPE_REFERENCE, (*bag_two)->entries[3].value.dataType); + EXPECT_EQ(app::R::string::string_one, (*bag_two)->entries[3].value.data); + EXPECT_EQ(0, (*bag_two)->entries[3].cookie); + EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[3].style); - EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[4].key); - EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[4].value.dataType); - EXPECT_EQ(3u, bag_two->entries[4].value.data); - EXPECT_EQ(0, bag_two->entries[4].cookie); - EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[4].style); + EXPECT_EQ(app::R::attr::attr_indirect, (*bag_two)->entries[4].key); + EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_two)->entries[4].value.dataType); + EXPECT_EQ(3u, (*bag_two)->entries[4].value.data); + EXPECT_EQ(0, (*bag_two)->entries[4].cookie); + EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[4].style); - EXPECT_EQ(app::R::attr::attr_empty, bag_two->entries[5].key); - EXPECT_EQ(Res_value::TYPE_NULL, bag_two->entries[5].value.dataType); - EXPECT_EQ(Res_value::DATA_NULL_EMPTY, bag_two->entries[5].value.data); - EXPECT_EQ(0, bag_two->entries[5].cookie); - EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[5].style); + EXPECT_EQ(app::R::attr::attr_empty, (*bag_two)->entries[5].key); + EXPECT_EQ(Res_value::TYPE_NULL, (*bag_two)->entries[5].value.dataType); + EXPECT_EQ(Res_value::DATA_NULL_EMPTY, (*bag_two)->entries[5].value.data); + EXPECT_EQ(0, (*bag_two)->entries[5].cookie); + EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[5].style); } TEST_F(AssetManager2Test, MergeStylesCircularDependency) { @@ -405,55 +379,41 @@ TEST_F(AssetManager2Test, MergeStylesCircularDependency) { // GetBag should stop traversing the parents of styles when a circular // dependency is detected - const ResolvedBag* bag_one = assetmanager.GetBag(app::R::style::StyleFour); - ASSERT_NE(nullptr, bag_one); - ASSERT_EQ(3u, bag_one->entry_count); + auto bag = assetmanager.GetBag(app::R::style::StyleFour); + ASSERT_TRUE(bag.has_value()); + ASSERT_EQ(3u, (*bag)->entry_count); } TEST_F(AssetManager2Test, ResolveReferenceToResource) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()}); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::integer::ref1, false /*may_be_bag*/, - 0u /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + auto value = assetmanager.GetResource(basic::R::integer::ref1); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); + EXPECT_EQ(basic::R::integer::ref2, value->data); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); - EXPECT_EQ(basic::R::integer::ref2, value.data); - - uint32_t last_ref = 0u; - cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(12000u, value.data); - EXPECT_EQ(basic::R::integer::ref2, last_ref); + auto result = assetmanager.ResolveReference(*value); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(12000u, value->data); + EXPECT_EQ(basic::R::integer::ref2, value->resid); } TEST_F(AssetManager2Test, ResolveReferenceToBag) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()}); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::integer::number2, true /*may_be_bag*/, - 0u /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + auto value = assetmanager.GetResource(basic::R::integer::number2, true /*may_be_bag*/); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); + EXPECT_EQ(basic::R::array::integerArray1, value->data); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); - EXPECT_EQ(basic::R::array::integerArray1, value.data); - - uint32_t last_ref = 0u; - cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); - EXPECT_EQ(basic::R::array::integerArray1, value.data); - EXPECT_EQ(basic::R::array::integerArray1, last_ref); + auto result = assetmanager.ResolveReference(*value); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); + EXPECT_EQ(basic::R::array::integerArray1, value->data); + EXPECT_EQ(basic::R::array::integerArray1, value->resid); } TEST_F(AssetManager2Test, ResolveDeepIdReference) { @@ -461,31 +421,25 @@ TEST_F(AssetManager2Test, ResolveDeepIdReference) { assetmanager.SetApkAssets({basic_assets_.get()}); // Set up the resource ids - const uint32_t high_ref = assetmanager - .GetResourceId("@id/high_ref", "values", "com.android.basic"); - ASSERT_NE(high_ref, 0u); - const uint32_t middle_ref = assetmanager - .GetResourceId("@id/middle_ref", "values", "com.android.basic"); - ASSERT_NE(middle_ref, 0u); - const uint32_t low_ref = assetmanager - .GetResourceId("@id/low_ref", "values", "com.android.basic"); - ASSERT_NE(low_ref, 0u); + auto high_ref = assetmanager.GetResourceId("@id/high_ref", "values", "com.android.basic"); + ASSERT_TRUE(high_ref.has_value()); + + auto middle_ref = assetmanager.GetResourceId("@id/middle_ref", "values", "com.android.basic"); + ASSERT_TRUE(middle_ref.has_value()); + + auto low_ref = assetmanager.GetResourceId("@id/low_ref", "values", "com.android.basic"); + ASSERT_TRUE(low_ref.has_value()); // Retrieve the most shallow resource - Res_value value; - ResTable_config config; - uint32_t flags; - ApkAssetsCookie cookie = assetmanager.GetResource(high_ref, false /*may_be_bag*/, - 0 /*density_override*/, - &value, &config, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); - EXPECT_EQ(middle_ref, value.data); + auto value = assetmanager.GetResource(*high_ref); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); + EXPECT_EQ(*middle_ref, value->data);; // Check that resolving the reference resolves to the deepest id - uint32_t last_ref = high_ref; - assetmanager.ResolveReference(cookie, &value, &config, &flags, &last_ref); - EXPECT_EQ(last_ref, low_ref); + auto result = assetmanager.ResolveReference(*value); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(*low_ref, value->resid); } TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) { @@ -495,16 +449,16 @@ TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) ResTable_config selected_config; memset(&selected_config, 0, sizeof(selected_config)); - uint32_t flags = 0u; + // Create some kind of value that is NOT a reference. + AssetManager2::SelectedValue value{}; + value.cookie = 1; + value.type = Res_value::TYPE_STRING; + value.resid = basic::R::string::test1; - // Create some kind of Res_value that is NOT a reference. - Res_value value; - value.dataType = Res_value::TYPE_STRING; - value.data = 0; - - uint32_t last_ref = basic::R::string::test1; - EXPECT_EQ(1, assetmanager.ResolveReference(1, &value, &selected_config, &flags, &last_ref)); - EXPECT_EQ(basic::R::string::test1, last_ref); + auto result = assetmanager.ResolveReference(value); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(1, value.cookie); + EXPECT_EQ(basic::R::string::test1, value.resid); } static bool IsConfigurationPresent(const std::set& configurations, @@ -516,43 +470,45 @@ TEST_F(AssetManager2Test, GetResourceConfigurations) { AssetManager2 assetmanager; assetmanager.SetApkAssets({system_assets_.get(), basic_de_fr_assets_.get()}); - std::set configurations = assetmanager.GetResourceConfigurations(); + auto configurations = assetmanager.GetResourceConfigurations(); + ASSERT_TRUE(configurations.has_value()); // We expect the locale sv from the system assets, and de and fr from basic_de_fr assets. // And one extra for the default configuration. - EXPECT_EQ(4u, configurations.size()); + EXPECT_EQ(4u, configurations->size()); ResTable_config expected_config; memset(&expected_config, 0, sizeof(expected_config)); expected_config.language[0] = 's'; expected_config.language[1] = 'v'; - EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); + EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config)); expected_config.language[0] = 'd'; expected_config.language[1] = 'e'; - EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); + EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config)); expected_config.language[0] = 'f'; expected_config.language[1] = 'r'; - EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); + EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config)); // Take out the system assets. configurations = assetmanager.GetResourceConfigurations(true /* exclude_system */); + ASSERT_TRUE(configurations.has_value()); // We expect de and fr from basic_de_fr assets. - EXPECT_EQ(2u, configurations.size()); + EXPECT_EQ(2u, configurations->size()); expected_config.language[0] = 's'; expected_config.language[1] = 'v'; - EXPECT_FALSE(IsConfigurationPresent(configurations, expected_config)); + EXPECT_FALSE(IsConfigurationPresent(*configurations, expected_config)); expected_config.language[0] = 'd'; expected_config.language[1] = 'e'; - EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); + EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config)); expected_config.language[0] = 'f'; expected_config.language[1] = 'r'; - EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); + EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config)); } TEST_F(AssetManager2Test, GetResourceLocales) { @@ -578,12 +534,17 @@ TEST_F(AssetManager2Test, GetResourceId) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()}); - EXPECT_EQ(basic::R::layout::main, - assetmanager.GetResourceId("com.android.basic:layout/main", "", "")); - EXPECT_EQ(basic::R::layout::main, - assetmanager.GetResourceId("layout/main", "", "com.android.basic")); - EXPECT_EQ(basic::R::layout::main, - assetmanager.GetResourceId("main", "layout", "com.android.basic")); + auto resid = assetmanager.GetResourceId("com.android.basic:layout/main", "", ""); + ASSERT_TRUE(resid.has_value()); + EXPECT_EQ(basic::R::layout::main, *resid); + + resid = assetmanager.GetResourceId("layout/main", "", "com.android.basic"); + ASSERT_TRUE(resid.has_value()); + EXPECT_EQ(basic::R::layout::main, *resid); + + resid = assetmanager.GetResourceId("main", "layout", "com.android.basic"); + ASSERT_TRUE(resid.has_value()); + EXPECT_EQ(basic::R::layout::main, *resid); } TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) { @@ -658,14 +619,8 @@ TEST_F(AssetManager2Test, GetLastPathWithoutEnablingReturnsEmpty) { assetmanager.SetApkAssets({basic_assets_.get()}); assetmanager.SetResourceResolutionLoggingEnabled(false); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, - 0 /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + auto value = assetmanager.GetResource(basic::R::string::test1); + ASSERT_TRUE(value.has_value()); auto result = assetmanager.GetLastResourceResolution(); EXPECT_EQ("", result); @@ -693,17 +648,12 @@ TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) { assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_.get()}); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, - 0 /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + auto value = assetmanager.GetResource(basic::R::string::test1); + ASSERT_TRUE(value.has_value()); auto result = assetmanager.GetLastResourceResolution(); - EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic", result); + EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n" + "\tFor config -de\n\tFound initial: com.android.basic", result); } TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) { @@ -717,17 +667,14 @@ TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) { assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()}); - Res_value value = Res_value(); - ResTable_config selected_config; - uint32_t flags; - - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, - 0 /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + auto value = assetmanager.GetResource(basic::R::string::test1); + ASSERT_TRUE(value.has_value()); auto result = assetmanager.GetLastResourceResolution(); - EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic\n\tFound better: com.android.basic -de", result); + EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n" + "\tFor config -de\n" + "\tFound initial: com.android.basic\n" + "\tFound better: com.android.basic -de", result); } TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) { @@ -739,14 +686,8 @@ TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) { assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_.get()}); - Res_value value = Res_value(); - ResTable_config selected_config; - uint32_t flags; - - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, - 0 /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + auto value = assetmanager.GetResource(basic::R::string::test1); + ASSERT_TRUE(value.has_value()); auto resultEnabled = assetmanager.GetLastResourceResolution(); ASSERT_NE("", resultEnabled); diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp index fa300c50218aa..ddd8ab820cb10 100644 --- a/libs/androidfw/tests/AttributeResolution_bench.cpp +++ b/libs/androidfw/tests/AttributeResolution_bench.cpp @@ -108,27 +108,20 @@ static void BM_ApplyStyleFramework(benchmark::State& state) { device_config.screenHeightDp = 1024; device_config.sdkVersion = 27; - Res_value value; - ResTable_config config; - uint32_t flags = 0u; - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::layout::layoutt, false /*may_be_bag*/, - 0u /*density_override*/, &value, &config, &flags); - if (cookie == kInvalidCookie) { + auto value = assetmanager.GetResource(basic::R::layout::layoutt); + if (!value.has_value()) { state.SkipWithError("failed to find R.layout.layout"); return; } - size_t len = 0u; - const char* layout_path = - assetmanager.GetStringPoolForCookie(cookie)->string8At(value.data, &len); - if (layout_path == nullptr || len == 0u) { + auto layout_path = assetmanager.GetStringPoolForCookie(value->cookie)->string8At(value->data); + if (!layout_path.has_value()) { state.SkipWithError("failed to lookup layout path"); return; } - std::unique_ptr asset = assetmanager.OpenNonAsset( - StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER); + std::unique_ptr asset = assetmanager.OpenNonAsset(layout_path->to_string(), value->cookie, + Asset::ACCESS_BUFFER); if (asset == nullptr) { state.SkipWithError("failed to load layout"); return; diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index 24361b5817f41..bb9129ad01c83 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -77,9 +77,9 @@ TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) { {fix_package_id(R::attr::attr_one, 0x02), fix_package_id(R::attr::attr_two, 0x02)}}; std::array values; std::array indices; - ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/, - fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(), values.data(), - indices.data()); + ASSERT_TRUE(ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/, + fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(), + values.data(), indices.data()).has_value()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; @@ -102,7 +102,7 @@ TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) { TEST_F(AttributeResolutionTest, Theme) { std::unique_ptr theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo).has_value()); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_empty}}; @@ -110,7 +110,7 @@ TEST_F(AttributeResolutionTest, Theme) { ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/, nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), - attrs.size(), values.data(), nullptr /*out_indices*/)); + attrs.size(), values.data(), nullptr /*out_indices*/).has_value()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; @@ -162,7 +162,7 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { std::array values; ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(), - values.data(), nullptr /*out_indices*/)); + values.data(), nullptr /*out_indices*/).has_value()); uint32_t* values_cursor = values.data(); EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); @@ -207,15 +207,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { std::unique_ptr theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo).has_value()); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}}; std::array values; std::array indices; - ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), - attrs.size(), values.data(), indices.data()); + ASSERT_TRUE(ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, + attrs.data(), attrs.size(), values.data(), indices.data()).has_value()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index faddfe599af48..0fa0573bcbb80 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -71,15 +71,9 @@ void GetResourceBenchmark(const std::vector& paths, const ResTable_ assetmanager.SetConfiguration(*config); } - Res_value value; - ResTable_config selected_config; - uint32_t flags; - uint32_t last_id = 0u; - while (state.KeepRunning()) { - ApkAssetsCookie cookie = assetmanager.GetResource( - resid, false /* may_be_bag */, 0u /* density_override */, &value, &selected_config, &flags); - assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_id); + auto value = assetmanager.GetResource(resid); + assetmanager.ResolveReference(*value); } } diff --git a/libs/androidfw/tests/CommonHelpers.cpp b/libs/androidfw/tests/CommonHelpers.cpp index faa5350f9ecc6..3396729536a4c 100644 --- a/libs/androidfw/tests/CommonHelpers.cpp +++ b/libs/androidfw/tests/CommonHelpers.cpp @@ -58,8 +58,9 @@ const std::string& GetTestDataPath() { } std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx) { - String8 str = pool->string8ObjectAt(idx); - return std::string(str.string(), str.length()); + auto str = pool->string8ObjectAt(idx); + CHECK(str.has_value()) << "failed to find string entry"; + return std::string(str->string(), str->length()); } } // namespace android diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp index 7aa0dbbafab3a..3f0c7cbc8ffc4 100644 --- a/libs/androidfw/tests/Idmap_test.cpp +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -62,10 +62,10 @@ class IdmapTest : public ::testing::Test { std::unique_ptr overlayable_assets_; }; -std::string GetStringFromApkAssets(const AssetManager2& asset_manager, const Res_value& value, - ApkAssetsCookie cookie) { +std::string GetStringFromApkAssets(const AssetManager2& asset_manager, + const AssetManager2::SelectedValue& value) { auto assets = asset_manager.GetApkAssets(); - const ResStringPool* string_pool = assets[cookie]->GetLoadedArsc()->GetStringPool(); + const ResStringPool* string_pool = assets[value.cookie]->GetLoadedArsc()->GetStringPool(); return GetStringFromPool(string_pool, value.data); } @@ -75,117 +75,88 @@ TEST_F(IdmapTest, OverlayOverridesResourceValue) { 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::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"); + + auto value = asset_manager.GetResource(overlayable::R::string::overlayable5); + ASSERT_TRUE(value.has_value()); + ASSERT_EQ(value->cookie, 2U); + ASSERT_EQ(value->type, Res_value::TYPE_STRING); + ASSERT_EQ("Overlay One", GetStringFromApkAssets(asset_manager, *value)); } 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"); + + auto value = asset_manager.GetResource(overlayable::R::string::overlayable10); + ASSERT_TRUE(value.has_value()); + ASSERT_EQ(value->cookie, 0U); + ASSERT_EQ(value->type, Res_value::TYPE_STRING); + ASSERT_EQ("yes", GetStringFromApkAssets(asset_manager, *value)); } 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)); + + auto value = asset_manager.GetResource(overlayable::R::string::overlayable8); + ASSERT_TRUE(value.has_value()); + ASSERT_EQ(value->cookie, 2U); + ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE); + ASSERT_EQ(value->data, (overlay::R::string::internal & 0x00ffffffU) | (0x02U << 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); + + auto value = asset_manager.GetResource(overlayable::R::integer::config_integer); + ASSERT_TRUE(value.has_value()); + ASSERT_EQ(value->cookie, 2U); + ASSERT_EQ(value->type, Res_value::TYPE_INT_DEC); + ASSERT_EQ(value->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"); + auto value = asset_manager.GetResource(overlayable::R::string::overlayable11); + ASSERT_TRUE(value.has_value()); + ASSERT_EQ(value->cookie, 2U); + ASSERT_EQ(value->type, Res_value::TYPE_STRING); + ASSERT_EQ("Hardcoded string", GetStringFromApkAssets(asset_manager, *value)); } 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); + + auto value = asset_manager.GetResource(overlayable::R::string::overlayable9); + ASSERT_TRUE(value.has_value()); + ASSERT_EQ(value->cookie, 2U); + ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE); + ASSERT_EQ(value->data, overlayable::R::string::overlayable7); } 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, + auto value = asset_manager.GetResource(overlayable::R::layout::hello_view); + ASSERT_TRUE(value.has_value()); + ASSERT_EQ(value->cookie, 2U); + ASSERT_EQ(value->type, Res_value::TYPE_STRING); + ASSERT_EQ("res/layout/hello_view.xml", GetStringFromApkAssets(asset_manager, *value)); + + auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", value->cookie, Asset::ACCESS_RANDOM); - auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(cookie); + auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(value->cookie); auto xml_tree = util::make_unique(std::move(dynamic_ref_table)); status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), false); ASSERT_EQ(err, NO_ERROR); @@ -216,32 +187,24 @@ TEST_F(IdmapTest, OverlaidResourceHasSameName) { 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"); + auto name = asset_manager.GetResourceName(overlayable::R::string::overlayable9); + ASSERT_TRUE(name.has_value()); + ASSERT_EQ("com.android.overlayable", std::string(name->package)); + ASSERT_EQ(std::u16string(u"string"), std::u16string(name->type16)); + ASSERT_EQ("overlayable9", std::string(name->entry)); } TEST_F(IdmapTest, OverlayLoaderInterop) { - std::string contents; auto loader_assets = ApkAssets::LoadTable("loader/resources.arsc", PROPERTY_LOADER); - AssetManager2 asset_manager; asset_manager.SetApkAssets({overlayable_assets_.get(), loader_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); - 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"); + auto value = asset_manager.GetResource(overlayable::R::string::overlayable11); + ASSERT_TRUE(value.has_value()); + ASSERT_EQ(1U, value->cookie); + ASSERT_EQ(Res_value::TYPE_STRING, value->type); + ASSERT_EQ("loader", GetStringFromApkAssets(asset_manager, *value)); } TEST_F(IdmapTest, OverlayAssetsIsUpToDate) { diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 2d69dfe4f4295..63574110a817c 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -50,7 +50,8 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + auto loaded_arsc = LoadedArsc::Load(reinterpret_cast(contents.data()), + contents.length()); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = @@ -66,9 +67,8 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { 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()); + auto type = type_spec->types[0]; + ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value()); } TEST(LoadedArscTest, LoadSparseEntryApp) { @@ -76,7 +76,8 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = @@ -90,9 +91,8 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { 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()); + auto type = type_spec->types[0]; + ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value()); } TEST(LoadedArscTest, LoadSharedLibrary) { @@ -100,7 +100,8 @@ TEST(LoadedArscTest, LoadSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); @@ -120,7 +121,8 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/libclient/libclient.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); @@ -145,8 +147,10 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, PROPERTY_DYNAMIC); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length(), + nullptr /* loaded_idmap */, + PROPERTY_DYNAMIC); ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); @@ -159,7 +163,8 @@ TEST(LoadedArscTest, LoadFeatureSplit) { std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = @@ -172,15 +177,12 @@ TEST(LoadedArscTest, LoadFeatureSplit) { const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); ASSERT_THAT(type_spec, NotNull()); ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); - size_t len; - const char16_t* type_name16 = - package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len); - ASSERT_THAT(type_name16, NotNull()); - EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string")); + auto type_name16 = package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1); + ASSERT_TRUE(type_name16.has_value()); + EXPECT_THAT(util::Utf16ToUtf8(*type_name16), StrEq("string")); - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); + ASSERT_TRUE(LoadedPackage::GetEntry(type_spec->types[0], entry_index).has_value()); } // AAPT(2) generates resource tables with chunks in a certain order. The rule is that @@ -205,7 +207,8 @@ TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) { ReadFileFromZipToString(GetTestDataPath() + "/out_of_order_types/out_of_order_types.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_THAT(loaded_arsc, NotNull()); ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); @@ -215,12 +218,10 @@ TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) { const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0); ASSERT_THAT(type_spec, NotNull()); ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); type_spec = package->GetTypeSpecByTypeIndex(1); ASSERT_THAT(type_spec, NotNull()); ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); } TEST(LoadedArscTest, LoadOverlayable) { @@ -228,7 +229,8 @@ TEST(LoadedArscTest, LoadOverlayable) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = loaded_arsc->GetPackageById( @@ -272,7 +274,8 @@ TEST(LoadedArscTest, ResourceIdentifierIterator) { ASSERT_TRUE( ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_NE(nullptr, loaded_arsc); const std::vector>& packages = loaded_arsc->GetPackages(); @@ -320,7 +323,8 @@ TEST(LoadedArscTest, GetOverlayableMap) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_NE(nullptr, loaded_arsc); const std::vector>& packages = loaded_arsc->GetPackages(); @@ -345,7 +349,7 @@ TEST(LoadedArscTest, LoadCustomLoader) { asset->getLength()); std::unique_ptr loaded_arsc = - LoadedArsc::Load(data, nullptr, PROPERTY_LOADER); + LoadedArsc::Load(data.data(), data.length(), nullptr, PROPERTY_LOADER); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = @@ -361,9 +365,8 @@ TEST(LoadedArscTest, LoadCustomLoader) { 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()); + auto type = type_spec->types[0]; + ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value()); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp index 326474e16e5d3..9aeb00c47e63f 100644 --- a/libs/androidfw/tests/ResTable_test.cpp +++ b/libs/androidfw/tests/ResTable_test.cpp @@ -442,22 +442,22 @@ TEST(ResTableTest, TruncatedEncodeLength) { ASSERT_LT(val.data, pool->size()); // Make sure a string with a truncated length is read to its correct length - size_t str_len; - const char* target_str8 = pool->string8At(val.data, &str_len); - ASSERT_TRUE(target_str8 != NULL); - ASSERT_EQ(size_t(40076), String8(target_str8, str_len).size()); - ASSERT_EQ(target_str8[40075], ']'); + auto target_str8 = pool->string8At(val.data); + ASSERT_TRUE(target_str8.has_value()); + ASSERT_EQ(size_t(40076), String8(target_str8->data(), target_str8->size()).size()); + ASSERT_EQ(target_str8->data()[40075], ']'); - const char16_t* target_str16 = pool->stringAt(val.data, &str_len); - ASSERT_TRUE(target_str16 != NULL); - ASSERT_EQ(size_t(40076), String16(target_str16, str_len).size()); - ASSERT_EQ(target_str8[40075], (char16_t) ']'); + auto target_str16 = pool->stringAt(val.data); + ASSERT_TRUE(target_str16.has_value()); + ASSERT_EQ(size_t(40076), String16(target_str16->data(), target_str16->size()).size()); + ASSERT_EQ(target_str8->data()[40075], (char16_t) ']'); // Load an edited apk with the null terminator removed from the end of the // string std::string invalid_contents; - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/length_decode/length_decode_invalid.apk", - "resources.arsc", &invalid_contents)); + ASSERT_TRUE(ReadFileFromZipToString( + GetTestDataPath() + "/length_decode/length_decode_invalid.apk", "resources.arsc", + &invalid_contents)); ResTable invalid_table; ASSERT_EQ(NO_ERROR, invalid_table.add(invalid_contents.data(), invalid_contents.size())); @@ -472,8 +472,8 @@ TEST(ResTableTest, TruncatedEncodeLength) { // Make sure a string with a truncated length that is not null terminated errors // and does not return the string - ASSERT_TRUE(invalid_pool->string8At(invalid_val.data, &str_len) == NULL); - ASSERT_TRUE(invalid_pool->stringAt(invalid_val.data, &str_len) == NULL); + ASSERT_FALSE(invalid_pool->string8At(invalid_val.data).has_value()); + ASSERT_FALSE(invalid_pool->stringAt(invalid_val.data).has_value()); } } // namespace android diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp index a81bb6ffab06b..10c0a4fc8316d 100644 --- a/libs/androidfw/tests/TestHelpers.cpp +++ b/libs/androidfw/tests/TestHelpers.cpp @@ -73,11 +73,15 @@ AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id, return AssertionFailure() << "table has no string pool for block " << block; } - const String8 actual_str = pool->string8ObjectAt(val.data); - if (String8(expected_str) != actual_str) { - return AssertionFailure() << actual_str.string(); + auto actual_str = pool->string8ObjectAt(val.data); + if (!actual_str.has_value()) { + return AssertionFailure() << "could not find string entry"; } - return AssertionSuccess() << actual_str.string(); + + if (String8(expected_str) != *actual_str) { + return AssertionFailure() << actual_str->string(); + } + return AssertionSuccess() << actual_str->string(); } } // namespace android diff --git a/libs/androidfw/tests/Theme_bench.cpp b/libs/androidfw/tests/Theme_bench.cpp index 594c39eb682f3..f3d60bbe4f156 100644 --- a/libs/androidfw/tests/Theme_bench.cpp +++ b/libs/androidfw/tests/Theme_bench.cpp @@ -70,11 +70,8 @@ static void BM_ThemeGetAttribute(benchmark::State& state) { auto theme = assets.NewTheme(); theme->ApplyStyle(kStyleId, false /* force */); - Res_value value; - uint32_t flags; - while (state.KeepRunning()) { - theme->GetAttribute(kAttrId, &value, &flags); + theme->GetAttribute(kAttrId); } } BENCHMARK(BM_ThemeGetAttribute); diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp index 16b9c75982fbc..f658735da5154 100644 --- a/libs/androidfw/tests/Theme_test.cpp +++ b/libs/androidfw/tests/Theme_test.cpp @@ -67,10 +67,7 @@ TEST_F(ThemeTest, EmptyTheme) { std::unique_ptr theme = assetmanager.NewTheme(); EXPECT_EQ(0u, theme->GetChangingConfigurations()); EXPECT_EQ(&assetmanager, theme->GetAssetManager()); - - Res_value value; - uint32_t flags; - EXPECT_EQ(kInvalidCookie, theme->GetAttribute(app::R::attr::attr_one, &value, &flags)); + EXPECT_FALSE(theme->GetAttribute(app::R::attr::attr_one).has_value()); } TEST_F(ThemeTest, SingleThemeNoParent) { @@ -78,23 +75,19 @@ TEST_F(ThemeTest, SingleThemeNoParent) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne)); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne).has_value()); - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie; + auto value = theme->GetAttribute(app::R::attr::attr_one); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(1u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); - cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(1u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); - - cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(2u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + value = theme->GetAttribute(app::R::attr::attr_two); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(2u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); } TEST_F(ThemeTest, SingleThemeWithParent) { @@ -102,32 +95,28 @@ TEST_F(ThemeTest, SingleThemeWithParent) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo)); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value()); - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie; + auto value = theme->GetAttribute(app::R::attr::attr_one); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(1u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); - cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(1u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); - - cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); - EXPECT_EQ(0, cookie); + value = theme->GetAttribute(app::R::attr::attr_two); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_STRING, value->type); + EXPECT_EQ(0, value->cookie); EXPECT_EQ(std::string("string"), - GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value.data)); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value->data)); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); // This attribute should point to an attr_indirect, so the result should be 3. - cookie = theme->GetAttribute(app::R::attr::attr_three, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(3u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + value = theme->GetAttribute(app::R::attr::attr_three); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(3u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); } TEST_F(ThemeTest, TryToUseBadResourceId) { @@ -135,11 +124,8 @@ TEST_F(ThemeTest, TryToUseBadResourceId) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo)); - - Res_value value; - uint32_t flags; - ASSERT_EQ(kInvalidCookie, theme->GetAttribute(0x7f000001, &value, &flags)); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value()); + ASSERT_FALSE(theme->GetAttribute(0x7f000001)); } TEST_F(ThemeTest, MultipleThemesOverlaidNotForce) { @@ -147,33 +133,29 @@ TEST_F(ThemeTest, MultipleThemesOverlaidNotForce) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo)); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree)); - - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie; + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value()); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree).has_value()); // attr_one is still here from the base. - cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(1u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + auto value = theme->GetAttribute(app::R::attr::attr_one); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(1u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); // check for the new attr_six - cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(6u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + value = theme->GetAttribute(app::R::attr::attr_six); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(6u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); // check for the old attr_five (force=true was not used). - cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); - EXPECT_EQ(app::R::string::string_one, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + value = theme->GetAttribute(app::R::attr::attr_five); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); + EXPECT_EQ(app::R::string::string_one, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); } TEST_F(ThemeTest, MultipleThemesOverlaidForced) { @@ -181,33 +163,29 @@ TEST_F(ThemeTest, MultipleThemesOverlaidForced) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo)); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree, true /* force */)); - - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie; + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value()); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree, true /* force */).has_value()); // attr_one is still here from the base. - cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(1u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + auto value = theme->GetAttribute(app::R::attr::attr_one); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(1u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); // check for the new attr_six - cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(6u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + value = theme->GetAttribute(app::R::attr::attr_six); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(6u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); // check for the new attr_five (force=true was used). - cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(5u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + value = theme->GetAttribute(app::R::attr::attr_five); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(5u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); } TEST_F(ThemeTest, ResolveDynamicAttributesAndReferencesToSharedLibrary) { @@ -216,28 +194,24 @@ TEST_F(ThemeTest, ResolveDynamicAttributesAndReferencesToSharedLibrary) { {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); std::unique_ptr theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(libclient::R::style::Theme, false /*force*/)); - - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie; + ASSERT_TRUE(theme->ApplyStyle(libclient::R::style::Theme, false /*force*/).has_value()); // The attribute should be resolved to the final value. - cookie = theme->GetAttribute(libclient::R::attr::foo, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(700u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + auto value = theme->GetAttribute(libclient::R::attr::foo); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(700u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); // The reference should be resolved to a TYPE_REFERENCE. - cookie = theme->GetAttribute(libclient::R::attr::bar, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); + value = theme->GetAttribute(libclient::R::attr::bar); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); // lib_one is assigned package ID 0x03. - EXPECT_EQ(3u, get_package_id(value.data)); - EXPECT_EQ(get_type_id(lib_one::R::string::foo), get_type_id(value.data)); - EXPECT_EQ(get_entry_id(lib_one::R::string::foo), get_entry_id(value.data)); + EXPECT_EQ(3u, get_package_id(value->data)); + EXPECT_EQ(get_type_id(lib_one::R::string::foo), get_type_id(value->data)); + EXPECT_EQ(get_entry_id(lib_one::R::string::foo), get_entry_id(value->data)); } TEST_F(ThemeTest, CopyThemeSameAssetManager) { @@ -245,24 +219,20 @@ TEST_F(ThemeTest, CopyThemeSameAssetManager) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr theme_one = assetmanager.NewTheme(); - ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne)); - - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie; + ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne).has_value()); // attr_one is still here from the base. - cookie = theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(1u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + auto value = theme_one->GetAttribute(app::R::attr::attr_one); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(1u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); // attr_six is not here. - EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags)); + ASSERT_FALSE(theme_one->GetAttribute(app::R::attr::attr_six).has_value()); std::unique_ptr theme_two = assetmanager.NewTheme(); - ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree)); + ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree).has_value()); // Copy the theme to theme_one. theme_one->SetTo(*theme_two); @@ -271,14 +241,14 @@ TEST_F(ThemeTest, CopyThemeSameAssetManager) { theme_two->Clear(); // attr_one is now not here. - EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags)); + ASSERT_FALSE(theme_one->GetAttribute(app::R::attr::attr_one).has_value()); // attr_six is now here because it was copied. - cookie = theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(6u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + value = theme_one->GetAttribute(app::R::attr::attr_six); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(6u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); } TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) { @@ -291,39 +261,43 @@ TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) { style_assets_.get()}); auto theme_dst = assetmanager_dst.NewTheme(); - ASSERT_TRUE(theme_dst->ApplyStyle(app::R::style::StyleOne)); + ASSERT_TRUE(theme_dst->ApplyStyle(app::R::style::StyleOne).has_value()); auto theme_src = assetmanager_src.NewTheme(); - ASSERT_TRUE(theme_src->ApplyStyle(R::style::Theme_One)); - ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleTwo)); + ASSERT_TRUE(theme_src->ApplyStyle(R::style::Theme_One).has_value()); + ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleTwo).has_value()); ASSERT_TRUE(theme_src->ApplyStyle(fix_package_id(lib_one::R::style::Theme, 0x03), - false /*force*/)); + false /*force*/).has_value()); ASSERT_TRUE(theme_src->ApplyStyle(fix_package_id(lib_two::R::style::Theme, 0x02), - false /*force*/)); + false /*force*/).has_value()); theme_dst->SetTo(*theme_src); - Res_value value; - uint32_t flags; - // System resources (present in destination asset manager). - EXPECT_EQ(0, theme_dst->GetAttribute(R::attr::foreground, &value, &flags)); + auto value = theme_dst->GetAttribute(R::attr::foreground); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(0, value->cookie); // The cookie of the style asset is 3 in the source and 2 in the destination. // Check that the cookie has been rewritten to the destination values. - EXPECT_EQ(2, theme_dst->GetAttribute(app::R::attr::attr_one, &value, &flags)); + value = theme_dst->GetAttribute(app::R::attr::attr_one); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(2, value->cookie); // The cookie of the lib_one asset is 2 in the source and 1 in the destination. // The package id of the lib_one package is 0x03 in the source and 0x02 in the destination // Check that the cookie and packages have been rewritten to the destination values. - EXPECT_EQ(1, theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02), &value, - &flags)); - EXPECT_EQ(1, theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02), &value, - &flags)); + value = theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02)); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(1, value->cookie); + + value = theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02)); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(1, value->cookie); // attr2 references an attribute in lib_one. Check that the resolution of the attribute value is // correct after the value of attr2 had its package id rewritten to the destination package id. - EXPECT_EQ(700, value.data); + EXPECT_EQ(700, value->data); } TEST_F(ThemeTest, CopyNonReferencesWhenPackagesDiffer) { @@ -335,28 +309,32 @@ TEST_F(ThemeTest, CopyNonReferencesWhenPackagesDiffer) { auto theme_dst = assetmanager_dst.NewTheme(); auto theme_src = assetmanager_src.NewTheme(); - ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleSeven)); + ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleSeven).has_value()); theme_dst->SetTo(*theme_src); - Res_value value; - uint32_t flags; - // Allow inline resource values to be copied even if the source apk asset is not present in the // destination. - EXPECT_EQ(0, theme_dst->GetAttribute(0x0101021b /* android:versionCode */, &value, &flags)); + auto value = theme_dst->GetAttribute(0x0101021b /* android:versionCode */); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(0, value->cookie); // Do not copy strings since the data is an index into the values string pool of the source apk // asset. - EXPECT_EQ(-1, theme_dst->GetAttribute(0x01010001 /* android:label */, &value, &flags)); + EXPECT_FALSE(theme_dst->GetAttribute(0x01010001 /* android:label */).has_value()); // Do not copy values that reference another resource if the resource is not present in the // destination. - EXPECT_EQ(-1, theme_dst->GetAttribute(0x01010002 /* android:icon */, &value, &flags)); - EXPECT_EQ(-1, theme_dst->GetAttribute(0x010100d1 /* android:tag */, &value, &flags)); + EXPECT_FALSE(theme_dst->GetAttribute(0x01010002 /* android:icon */).has_value()); + EXPECT_FALSE(theme_dst->GetAttribute(0x010100d1 /* android:tag */).has_value()); // Allow @empty to and @null to be copied. - EXPECT_EQ(0, theme_dst->GetAttribute(0x010100d0 /* android:id */, &value, &flags)); - EXPECT_EQ(0, theme_dst->GetAttribute(0x01010000 /* android:theme */, &value, &flags)); + value = theme_dst->GetAttribute(0x010100d0 /* android:id */); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(0, value->cookie); + + value = theme_dst->GetAttribute(0x01010000 /* android:theme */); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(0, value->cookie); } } // namespace android diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index ab6dced5b67d6..dd3ebdbdea09a 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -519,7 +519,7 @@ static int validateAttr(const String8& path, const ResTable& table, String8(parser.getElementName(&len)).string(), attr); return ATTR_NOT_FOUND; } - if ((str=pool->stringAt(value.data, &len)) == NULL) { + if ((str = UnpackOptionalString(pool->stringAt(value.data), &len)) == NULL) { fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n", path.string(), parser.getLineNumber(), String8(parser.getElementName(&len)).string(), attr); diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index d02f44edaa4cc..257e96b6e51a0 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -3066,7 +3066,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& for (size_t ti=0; tigetTypeStrings().stringAt(ti, &len)); + String16 typeName(UnpackOptionalString(p->getTypeStrings().stringAt(ti), &len)); sp t = p->getTypes().valueFor(typeName); LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16(""), "Type name %s not found", @@ -4169,7 +4169,7 @@ status_t ResourceTable::Package::setStrings(const sp& data, const size_t N = strings->size(); for (size_t i=0; iadd(String16(strings->stringAt(i, &len)), i); + mappings->add(String16(UnpackOptionalString(strings->stringAt(i), &len)), i); } } return err; diff --git a/tools/aapt/StringPool.cpp b/tools/aapt/StringPool.cpp index 37b61bfdffbd6..6cacd32eb91d5 100644 --- a/tools/aapt/StringPool.cpp +++ b/tools/aapt/StringPool.cpp @@ -52,9 +52,9 @@ void printStringPool(const ResStringPool* pool) for (size_t i=0; iisUTF8()) { - uniqueStrings.add(pool->string8At(i, &len)); + uniqueStrings.add(UnpackOptionalString(pool->string8At(i), &len)); } else { - uniqueStrings.add(pool->stringAt(i, &len)); + uniqueStrings.add(UnpackOptionalString(pool->stringAt(i), &len)); } } @@ -66,8 +66,8 @@ void printStringPool(const ResStringPool* pool) const size_t NS = pool->size(); for (size_t s=0; sstring8ObjectAt(s); - printf("String #" ZD ": %s\n", (ZD_TYPE) s, str.string()); + auto str = pool->string8ObjectAt(s); + printf("String #" ZD ": %s\n", (ZD_TYPE) s, (str.has_value() ? str->string() : "")); } } diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp index 439f231193dfb..82da24959521c 100644 --- a/tools/aapt2/Debug.cpp +++ b/tools/aapt2/Debug.cpp @@ -436,9 +436,9 @@ void Debug::DumpResStringPool(const android::ResStringPool* pool, text::Printer* for (size_t i=0; iisUTF8()) { - uniqueStrings.add(pool->string8At(i, &len)); + uniqueStrings.add(UnpackOptionalString(pool->string8At(i), &len)); } else { - uniqueStrings.add(pool->stringAt(i, &len)); + uniqueStrings.add(UnpackOptionalString(pool->stringAt(i), &len)); } } @@ -450,8 +450,8 @@ void Debug::DumpResStringPool(const android::ResStringPool* pool, text::Printer* const size_t NS = pool->size(); for (size_t s=0; sstring8ObjectAt(s); - printer->Print(StringPrintf("String #%zd : %s\n", s, str.string())); + auto str = pool->string8ObjectAt(s); + printer->Print(StringPrintf("String #%zd : %s\n", s, str.has_value() ? str->string() : "")); } } diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index 469128b1e50b1..f26e995aa4f91 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -751,10 +751,12 @@ std::unique_ptr ParseBinaryResValue(const ResourceType& type, const Config switch (res_value.dataType) { case android::Res_value::TYPE_STRING: { const std::string str = util::GetString(src_pool, data); - const android::ResStringPool_span* spans = src_pool.styleAt(data); + auto spans_result = src_pool.styleAt(data); // Check if the string has a valid style associated with it. - if (spans != nullptr && spans->name.index != android::ResStringPool_span::END) { + if (spans_result.has_value() && + (*spans_result)->name.index != android::ResStringPool_span::END) { + const android::ResStringPool_span* spans = spans_result->unsafe_ptr(); StyleString style_str = {str}; while (spans->name.index != android::ResStringPool_span::END) { style_str.spans.push_back(Span{util::GetString(src_pool, spans->name.index), diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp index 9a7238b584ba9..6e5200bca44c4 100644 --- a/tools/aapt2/StringPool_test.cpp +++ b/tools/aapt2/StringPool_test.cpp @@ -223,11 +223,11 @@ TEST(StringPoolTest, FlattenOddCharactersUtf16) { std::unique_ptr data = util::Copy(buffer); ResStringPool test; ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR); - size_t len = 0; - const char16_t* str = test.stringAt(0, &len); - EXPECT_THAT(len, Eq(1u)); - EXPECT_THAT(str, Pointee(Eq(u'\u093f'))); - EXPECT_THAT(str[1], Eq(0u)); + auto str = test.stringAt(0); + ASSERT_TRUE(str.has_value()); + EXPECT_THAT(str->size(), Eq(1u)); + EXPECT_THAT(str->data(), Pointee(Eq(u'\u093f'))); + EXPECT_THAT(str->data()[1], Eq(0u)); } constexpr const char* sLongString = @@ -278,14 +278,15 @@ TEST(StringPoolTest, Flatten) { EXPECT_THAT(util::GetString(test, 3), Eq(sLongString)); EXPECT_THAT(util::GetString16(test, 3), Eq(util::Utf8ToUtf16(sLongString))); - size_t len; - EXPECT_TRUE(test.stringAt(4, &len) != nullptr || test.string8At(4, &len) != nullptr); + EXPECT_TRUE(test.stringAt(4).has_value() || test.string8At(4).has_value()); EXPECT_THAT(util::GetString(test, 0), Eq("style")); EXPECT_THAT(util::GetString16(test, 0), Eq(u"style")); - const ResStringPool_span* span = test.styleAt(0); - ASSERT_THAT(span, NotNull()); + auto span_result = test.styleAt(0); + ASSERT_TRUE(span_result.has_value()); + + const ResStringPool_span* span = span_result->unsafe_ptr(); EXPECT_THAT(util::GetString(test, span->name.index), Eq("b")); EXPECT_THAT(util::GetString16(test, span->name.index), Eq(u"b")); EXPECT_THAT(span->firstChar, Eq(0u)); @@ -318,16 +319,17 @@ TEST(StringPoolTest, ModifiedUTF8) { // Check that the codepoints are encoded using two three-byte surrogate pairs ResStringPool test; ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR); - size_t len; - const char* str = test.string8At(0, &len); - ASSERT_THAT(str, NotNull()); - EXPECT_THAT(std::string(str, len), Eq("\xED\xA0\x81\xED\xB0\x80")); - str = test.string8At(1, &len); - ASSERT_THAT(str, NotNull()); - EXPECT_THAT(std::string(str, len), Eq("foo \xED\xA0\x81\xED\xB0\xB7 bar")); - str = test.string8At(2, &len); - ASSERT_THAT(str, NotNull()); - EXPECT_THAT(std::string(str, len), Eq("\xED\xA0\x81\xED\xB0\x80\xED\xA0\x81\xED\xB0\xB7")); + auto str = test.string8At(0); + ASSERT_TRUE(str.has_value()); + EXPECT_THAT(str->to_string(), Eq("\xED\xA0\x81\xED\xB0\x80")); + + str = test.string8At(1); + ASSERT_TRUE(str.has_value()); + EXPECT_THAT(str->to_string(), Eq("foo \xED\xA0\x81\xED\xB0\xB7 bar")); + + str = test.string8At(2); + ASSERT_TRUE(str.has_value()); + EXPECT_THAT(str->to_string(), Eq("\xED\xA0\x81\xED\xB0\x80\xED\xA0\x81\xED\xB0\xB7")); // Check that retrieving the strings returns the original UTF-8 character bytes EXPECT_THAT(util::GetString(test, 0), Eq("\xF0\x90\x90\x80")); diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp index fb786a31360ec..ae9f7926cf09e 100644 --- a/tools/aapt2/cmd/Compile_test.cpp +++ b/tools/aapt2/cmd/Compile_test.cpp @@ -252,5 +252,4 @@ TEST_F(CompilerTest, DoNotTranslateTest) { AssertTranslations(this, "donottranslate", expected_not_translatable); AssertTranslations(this, "donottranslate_foo", expected_not_translatable); } - } // namespace aapt diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index fd12d02434faa..faabe461648a3 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -730,22 +730,6 @@ static bool LoadStableIdMap(IDiagnostics* diag, const std::string& path, return true; } -static android::ApkAssetsCookie FindFrameworkAssetManagerCookie( - const android::AssetManager2& assets) { - using namespace android; - - // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so - // we're looking for the first attribute resource in the system package. - Res_value val{}; - ResTable_config config{}; - uint32_t type_spec_flags; - ApkAssetsCookie idx = assets.GetResource(0x01010000, true /** may_be_bag */, - 0 /** density_override */, &val, &config, - &type_spec_flags); - - return idx; -} - class Linker { public: Linker(LinkContext* context, const LinkOptions& options) @@ -758,8 +742,12 @@ class Linker { void ExtractCompileSdkVersions(android::AssetManager2* assets) { using namespace android; - android::ApkAssetsCookie cookie = FindFrameworkAssetManagerCookie(*assets); - if (cookie == android::kInvalidCookie) { + // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so + // we're looking for the first attribute resource in the system package. + android::ApkAssetsCookie cookie; + if (auto value = assets->GetResource(0x01010000, true /** may_be_bag */); value.has_value()) { + cookie = value->cookie; + } else { // No Framework assets loaded. Not a failure. return; } diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp index 6932baf76c75c..f8b8a1c4e35be 100644 --- a/tools/aapt2/format/binary/TableFlattener_test.cpp +++ b/tools/aapt2/format/binary/TableFlattener_test.cpp @@ -189,16 +189,16 @@ TEST_F(TableFlattenerTest, FlattenFullyLinkedTable) { ResTable_config::CONFIG_VERSION)); std::u16string foo_str = u"foo"; - ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size()); - ASSERT_GE(idx, 0); + auto idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size()); + ASSERT_TRUE(idx.has_value()); EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {}, - Res_value::TYPE_STRING, (uint32_t)idx, 0u)); + Res_value::TYPE_STRING, (uint32_t)*idx, 0u)); std::u16string bar_path = u"res/layout/bar.xml"; idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size()); - ASSERT_GE(idx, 0); + ASSERT_TRUE(idx.has_value()); EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/bar", ResourceId(0x7f050000), {}, - Res_value::TYPE_STRING, (uint32_t)idx, 0u)); + Res_value::TYPE_STRING, (uint32_t)*idx, 0u)); } TEST_F(TableFlattenerTest, FlattenEntriesWithGapsInIds) { @@ -603,16 +603,16 @@ TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoNameCollapseExemptionsSucce 2u, ResTable_config::CONFIG_VERSION)); std::u16string foo_str = u"foo"; - ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size()); - ASSERT_GE(idx, 0); + auto idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size()); + ASSERT_TRUE(idx.has_value()); EXPECT_TRUE(Exists(&res_table, "com.app.test:string/0_resource_name_obfuscated", - ResourceId(0x7f040000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u)); + ResourceId(0x7f040000), {}, Res_value::TYPE_STRING, (uint32_t)*idx, 0u)); std::u16string bar_path = u"res/layout/bar.xml"; idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size()); - ASSERT_GE(idx, 0); + ASSERT_TRUE(idx.has_value()); EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated", - ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u)); + ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)*idx, 0u)); } TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithNameCollapseExemptionsSucceeds) { @@ -659,16 +659,16 @@ TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithNameCollapseExemptionsSuc 2u, ResTable_config::CONFIG_VERSION)); std::u16string foo_str = u"foo"; - ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size()); - ASSERT_GE(idx, 0); + auto idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size()); + ASSERT_TRUE(idx.has_value()); EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {}, - Res_value::TYPE_STRING, (uint32_t)idx, 0u)); + Res_value::TYPE_STRING, (uint32_t)*idx, 0u)); std::u16string bar_path = u"res/layout/bar.xml"; idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size()); - ASSERT_GE(idx, 0); + ASSERT_TRUE(idx.has_value()); EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated", - ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u)); + ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)*idx, 0u)); } TEST_F(TableFlattenerTest, FlattenOverlayable) { diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp index 897fa80ffedb4..ad716c78d65d5 100644 --- a/tools/aapt2/process/SymbolTable.cpp +++ b/tools/aapt2/process/SymbolTable.cpp @@ -265,21 +265,22 @@ bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId, static std::unique_ptr LookupAttributeInTable( android::AssetManager2& am, ResourceId id) { + using namespace android; if (am.GetApkAssets().empty()) { return {}; } - const android::ResolvedBag* bag = am.GetBag(id.id); - if (bag == nullptr) { + auto bag_result = am.GetBag(id.id); + if (!bag_result.has_value()) { return nullptr; } // We found a resource. std::unique_ptr s = util::make_unique(id); - + const ResolvedBag* bag = *bag_result; const size_t count = bag->entry_count; for (uint32_t i = 0; i < count; i++) { - if (bag->entries[i].key == android::ResTable_map::ATTR_TYPE) { + if (bag->entries[i].key == ResTable_map::ATTR_TYPE) { s->attribute = std::make_shared(bag->entries[i].value.data); break; } @@ -287,25 +288,25 @@ static std::unique_ptr LookupAttributeInTable( if (s->attribute) { for (size_t i = 0; i < count; i++) { - const android::ResolvedBag::Entry& map_entry = bag->entries[i]; + const ResolvedBag::Entry& map_entry = bag->entries[i]; if (Res_INTERNALID(map_entry.key)) { switch (map_entry.key) { - case android::ResTable_map::ATTR_MIN: + case ResTable_map::ATTR_MIN: s->attribute->min_int = static_cast(map_entry.value.data); break; - case android::ResTable_map::ATTR_MAX: + case ResTable_map::ATTR_MAX: s->attribute->max_int = static_cast(map_entry.value.data); break; } continue; } - android::AssetManager2::ResourceName name; - if (!am.GetResourceName(map_entry.key, &name)) { + auto name = am.GetResourceName(map_entry.key); + if (!name.has_value()) { return nullptr; } - Maybe parsed_name = ResourceUtils::ToResourceName(name); + Maybe parsed_name = ResourceUtils::ToResourceName(*name); if (!parsed_name) { return nullptr; } @@ -328,7 +329,7 @@ std::unique_ptr AssetManagerSymbolSource::FindByName( bool found = false; ResourceId res_id = 0; - uint32_t type_spec_flags; + uint32_t type_spec_flags = 0; ResourceName real_name; // There can be mangled resources embedded within other packages. Here we will @@ -340,8 +341,19 @@ std::unique_ptr AssetManagerSymbolSource::FindByName( real_name.package = package_name; } - res_id = asset_manager_.GetResourceId(real_name.to_string()); - if (res_id.is_valid_static() && asset_manager_.GetResourceFlags(res_id.id, &type_spec_flags)) { + auto real_res_id = asset_manager_.GetResourceId(real_name.to_string()); + if (!real_res_id.has_value()) { + return true; + } + + res_id.id = *real_res_id; + if (!res_id.is_valid_static()) { + return true; + } + + auto value = asset_manager_.GetResource(res_id.id, true /* may_be_bag */); + if (value.has_value()) { + type_spec_flags = value->flags; found = true; return false; } @@ -371,11 +383,11 @@ std::unique_ptr AssetManagerSymbolSource::FindByName( static Maybe GetResourceName(android::AssetManager2& am, ResourceId id) { - android::AssetManager2::ResourceName name; - if (!am.GetResourceName(id.id, &name)) { + auto name = am.GetResourceName(id.id); + if (!name.has_value()) { return {}; } - return ResourceUtils::ToResourceName(name); + return ResourceUtils::ToResourceName(*name); } std::unique_ptr AssetManagerSymbolSource::FindById( @@ -394,9 +406,8 @@ std::unique_ptr AssetManagerSymbolSource::FindById( return {}; } - - uint32_t type_spec_flags = 0; - if (!asset_manager_.GetResourceFlags(id.id, &type_spec_flags)) { + auto value = asset_manager_.GetResource(id.id, true /* may_be_bag */); + if (!value.has_value()) { return {}; } @@ -411,7 +422,7 @@ std::unique_ptr AssetManagerSymbolSource::FindById( } if (s) { - s->is_public = (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0; + s->is_public = (value->flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0; return s; } return {}; diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp index 37ce65e4fe5b1..ef33c3463a810 100644 --- a/tools/aapt2/util/Util.cpp +++ b/tools/aapt2/util/Util.cpp @@ -531,19 +531,15 @@ bool ExtractResFilePathParts(const StringPiece& path, StringPiece* out_prefix, } StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx) { - size_t len; - const char16_t* str = pool.stringAt(idx, &len); - if (str != nullptr) { - return StringPiece16(str, len); + if (auto str = pool.stringAt(idx)) { + return *str; } return StringPiece16(); } std::string GetString(const android::ResStringPool& pool, size_t idx) { - size_t len; - const char* str = pool.string8At(idx, &len); - if (str != nullptr) { - return ModifiedUtf8ToUtf8(std::string(str, len)); + if (auto str = pool.string8At(idx)) { + return ModifiedUtf8ToUtf8(str->to_string()); } return Utf16ToUtf8(GetString16(pool, idx)); } diff --git a/tools/split-select/Main.cpp b/tools/split-select/Main.cpp index d3eb012a80e98..e6966db0aa004 100644 --- a/tools/split-select/Main.cpp +++ b/tools/split-select/Main.cpp @@ -182,14 +182,18 @@ static bool getAppInfo(const String8& path, AppInfo& outInfo) { if (type >= Res_value::TYPE_FIRST_INT && type <= Res_value::TYPE_LAST_INT) { outInfo.minSdkVersion = xml.getAttributeData(idx); } else if (type == Res_value::TYPE_STRING) { - String8 minSdk8(xml.getStrings().string8ObjectAt(idx)); - char* endPtr; - int minSdk = strtol(minSdk8.string(), &endPtr, 10); - if (endPtr != minSdk8.string() + minSdk8.size()) { - fprintf(stderr, "warning: failed to parse android:minSdkVersion '%s'\n", - minSdk8.string()); + auto minSdk8 = xml.getStrings().string8ObjectAt(idx); + if (!minSdk8.has_value()) { + fprintf(stderr, "warning: failed to retrieve android:minSdkVersion.\n"); } else { - outInfo.minSdkVersion = minSdk; + char *endPtr; + int minSdk = strtol(minSdk8->string(), &endPtr, 10); + if (endPtr != minSdk8->string() + minSdk8->size()) { + fprintf(stderr, "warning: failed to parse android:minSdkVersion '%s'\n", + minSdk8->string()); + } else { + outInfo.minSdkVersion = minSdk; + } } } else { fprintf(stderr, "warning: unrecognized value for android:minSdkVersion.\n"); From e7ab62723ac8bc1c95405353e7f625956b1dfbe3 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Fri, 13 Nov 2020 18:06:15 -0800 Subject: [PATCH 4/5] Set resource id correctly when resolve fails If for some reason the resource id cannot be resolved to a value (there is no configuration that matches the AssetManager configuration or some error occurs), set the resource id of the SelectedValue to the resource id that could not be resolved. This was the behavior before the AssetManager IncFs hardening refactor. Bug: 173203252 Test: atest com.google.android.config.pts.PreinstalledAppsTestCase Test: Chrome icon appears on launcher Change-Id: Iad1760c0e246da1a4bf64d1c2ec60bb08da32d06 Merged-In: Iad1760c0e246da1a4bf64d1c2ec60bb08da32d06 --- libs/androidfw/AssetManager2.cpp | 10 ++++-- libs/androidfw/AttributeResolution.cpp | 4 +-- libs/androidfw/tests/AssetManager2_test.cpp | 38 +++++++++++++++++++-- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 3e54dc67db763..8bab73cd15f09 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -975,19 +975,23 @@ base::expected AssetManager2::ResolveReference( for (uint32_t i = 0U;; i++) { auto result = GetResource(resolve_resid, true /*may_be_bag*/); if (!result.has_value()) { + value.resid = resolve_resid; return base::unexpected(result.error()); } + // If resource resolution fails, the value should be set to the last reference that was able to + // be resolved successfully. + value = *result; + value.flags |= combined_flags; + if (result->type != Res_value::TYPE_REFERENCE || result->data == Res_value::DATA_NULL_UNDEFINED || result->data == resolve_resid || i == kMaxIterations) { // This reference can't be resolved, so exit now and let the caller deal with it. - value = *result; - value.flags |= combined_flags; return {}; } - combined_flags |= result->flags; + combined_flags = result->flags; resolve_resid = result->data; } } diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index 71919fdbb7361..d07b452ad40ec 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -265,7 +265,7 @@ base::expected ApplyStyle(Theme* theme, ResXMLParser* x const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); if (xml_attr_idx != xml_attr_finder.end()) { // We found the attribute we were looking for. - Res_value attribute_value; + Res_value attribute_value{}; xml_parser->getAttributeValue(xml_attr_idx, &attribute_value); value.type = attribute_value.dataType; value.data = attribute_value.data; @@ -377,7 +377,7 @@ base::expected RetrieveAttributes(AssetManager2* assetm // Retrieve the current XML attribute if it matches, and step to next. if (ix < xml_attr_count && cur_ident == cur_xml_attr) { - Res_value attribute_value; + Res_value attribute_value{}; xml_parser->getAttributeValue(ix, &attribute_value); value.type = attribute_value.dataType; value.data = attribute_value.data; diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 3638ce1f92d72..0f5afd45546cd 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -446,9 +446,6 @@ TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()}); - ResTable_config selected_config; - memset(&selected_config, 0, sizeof(selected_config)); - // Create some kind of value that is NOT a reference. AssetManager2::SelectedValue value{}; value.cookie = 1; @@ -461,6 +458,41 @@ TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) EXPECT_EQ(basic::R::string::test1, value.resid); } +TEST_F(AssetManager2Test, ResolveReferenceMissingResource) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({basic_assets_.get()}); + + const uint32_t kMissingResId = 0x8001ffff; + AssetManager2::SelectedValue value{}; + value.type = Res_value::TYPE_REFERENCE; + value.data = kMissingResId; + + auto result = assetmanager.ResolveReference(value); + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value.type); + EXPECT_EQ(kMissingResId, value.data); + EXPECT_EQ(kMissingResId, value.resid); + EXPECT_EQ(-1, value.cookie); + EXPECT_EQ(0, value.flags); +} + +TEST_F(AssetManager2Test, ResolveReferenceMissingResourceLib) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({libclient_assets_.get()}); + + AssetManager2::SelectedValue value{}; + value.type = Res_value::TYPE_REFERENCE; + value.data = libclient::R::string::foo_one; + + auto result = assetmanager.ResolveReference(value); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(Res_value::TYPE_DYNAMIC_REFERENCE, value.type); + EXPECT_EQ(lib_one::R::string::foo, value.data); + EXPECT_EQ(libclient::R::string::foo_one, value.resid); + EXPECT_EQ(0, value.cookie); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value.flags); +} + static bool IsConfigurationPresent(const std::set& configurations, const ResTable_config& configuration) { return configurations.count(configuration) > 0; From a45506e6f6619f59ce1ae94b20ad377b86966be0 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Mon, 16 Nov 2020 23:08:18 +0000 Subject: [PATCH 5/5] Revert^2 "Cache resolved theme values" 6ca48473e533a8b89abac6294a0bb8130b8c8c89 Change-Id: Icb295186b85e1edcdcebc1d746f7ff0d6ef66829 Merged-In: Icb295186b85e1edcdcebc1d746f7ff0d6ef66829 --- libs/androidfw/AssetManager2.cpp | 28 ++++++++++++++--- libs/androidfw/AttributeResolution.cpp | 7 ++--- .../include/androidfw/AssetManager2.h | 12 ++++--- libs/androidfw/tests/AssetManager2_test.cpp | 31 +++++++++++++++++++ 4 files changed, 66 insertions(+), 12 deletions(-) diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 8bab73cd15f09..a545b3d5e1341 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -963,14 +963,25 @@ base::expected AssetManager2::GetRe } base::expected AssetManager2::ResolveReference( - AssetManager2::SelectedValue& value) const { + AssetManager2::SelectedValue& value, bool cache_value) const { if (value.type != Res_value::TYPE_REFERENCE || value.data == 0U) { // Not a reference. Nothing to do. return {}; } - uint32_t combined_flags = value.flags; - uint32_t resolve_resid = value.data; + const uint32_t original_flags = value.flags; + const uint32_t original_resid = value.data; + if (cache_value) { + auto cached_value = cached_resolved_values_.find(value.data); + if (cached_value != cached_resolved_values_.end()) { + value = cached_value->second; + value.flags |= original_flags; + return {}; + } + } + + uint32_t combined_flags = 0U; + uint32_t resolve_resid = original_resid; constexpr const uint32_t kMaxIterations = 20; for (uint32_t i = 0U;; i++) { auto result = GetResource(resolve_resid, true /*may_be_bag*/); @@ -988,6 +999,13 @@ base::expected AssetManager2::ResolveReference( result->data == Res_value::DATA_NULL_UNDEFINED || result->data == resolve_resid || i == kMaxIterations) { // This reference can't be resolved, so exit now and let the caller deal with it. + if (cache_value) { + cached_resolved_values_[original_resid] = value; + } + + // Above value is cached without original_flags to ensure they don't get included in future + // queries that hit the cache + value.flags |= original_flags; return {}; } @@ -1357,6 +1375,8 @@ void AssetManager2::InvalidateCaches(uint32_t diff) { ++iter; } } + + cached_resolved_values_.clear(); } uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const { @@ -1537,7 +1557,7 @@ base::expected Theme::ResolveAttributeReference( return base::unexpected(std::nullopt); } - auto resolve_result = asset_manager_->ResolveReference(*result); + auto resolve_result = asset_manager_->ResolveReference(*result, true /* cache_value */); if (resolve_result.has_value()) { result->flags |= value.flags; value = *result; diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index d07b452ad40ec..c188712e58774 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -39,8 +39,7 @@ class XmlAttributeFinder : public BackTrackingAttributeFinder { public: explicit XmlAttributeFinder(const ResXMLParser* parser) - : BackTrackingAttributeFinder( - 0, parser != nullptr ? parser->getAttributeCount() : 0), + : BackTrackingAttributeFinder(0, parser != nullptr ? parser->getAttributeCount() : 0), parser_(parser) {} inline uint32_t GetAttribute(size_t index) const { @@ -178,7 +177,7 @@ base::expected ResolveAttrs(Theme* theme, uint32_t def_ value = *attr_value; DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data); - const auto result = assetmanager->ResolveReference(value); + const auto result = assetmanager->ResolveReference(value, true /* cache_value */); if (UNLIKELY(IsIOError(result))) { return base::unexpected(GetIOError(result.error())); } @@ -310,7 +309,7 @@ base::expected ApplyStyle(Theme* theme, ResXMLParser* x value = *attr_value; DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data); - auto result = assetmanager->ResolveReference(value); + auto result = assetmanager->ResolveReference(value, true /* cache_value */); if (UNLIKELY(IsIOError(result))) { return base::unexpected(GetIOError(result.error())); } diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 4e993b0838a2b..a92694c94b9f8 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -264,11 +264,14 @@ class AssetManager2 { // Resolves the resource referenced in `value` if the type is Res_value::TYPE_REFERENCE. // // If the data type is not Res_value::TYPE_REFERENCE, no work is done. Configuration flags of the - // values pointed to by the reference are OR'd into `value.flags`. + // values pointed to by the reference are OR'd into `value.flags`. If `cache_value` is true, then + // the resolved value will be cached and used when attempting to resolve the resource id specified + // in `value`. // // Returns a null error if the resource could not be resolved, or an I/O error if reading // resource data failed. - base::expected ResolveReference(SelectedValue& value) const; + base::expected ResolveReference(SelectedValue& value, + bool cache_value = false) const; // Retrieves the best matching bag/map resource with ID `resid`. // @@ -446,13 +449,14 @@ class AssetManager2 { // a number of times for each view during View inspection. mutable std::unordered_map> cached_bag_resid_stacks_; + // Cached set of resolved resource values. + mutable std::unordered_map cached_resolved_values_; + // Whether or not to save resource resolution steps bool resource_resolution_logging_enabled_ = false; struct Resolution { - struct Step { - enum class Type { INITIAL, BETTER_MATCH, diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 0f5afd45546cd..471b0ee1e7e99 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -458,6 +458,37 @@ TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) EXPECT_EQ(basic::R::string::test1, value.resid); } +TEST_F(AssetManager2Test, ResolveReferenceMissingResourceDoNotCacheFlags) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({basic_assets_.get()}); + { + AssetManager2::SelectedValue value{}; + value.data = basic::R::string::test1; + value.type = Res_value::TYPE_REFERENCE; + value.flags = ResTable_config::CONFIG_KEYBOARD; + + auto result = assetmanager.ResolveReference(value); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(Res_value::TYPE_STRING, value.type); + EXPECT_EQ(0, value.cookie); + EXPECT_EQ(basic::R::string::test1, value.resid); + EXPECT_EQ(ResTable_typeSpec::SPEC_PUBLIC | ResTable_config::CONFIG_KEYBOARD, value.flags); + } + { + AssetManager2::SelectedValue value{}; + value.data = basic::R::string::test1; + value.type = Res_value::TYPE_REFERENCE; + value.flags = ResTable_config::CONFIG_COLOR_MODE; + + auto result = assetmanager.ResolveReference(value); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(Res_value::TYPE_STRING, value.type); + EXPECT_EQ(0, value.cookie); + EXPECT_EQ(basic::R::string::test1, value.resid); + EXPECT_EQ(ResTable_typeSpec::SPEC_PUBLIC | ResTable_config::CONFIG_COLOR_MODE, value.flags); + } +} + TEST_F(AssetManager2Test, ResolveReferenceMissingResource) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()});