diff options
| author | 2017-01-16 19:11:19 -0800 | |
|---|---|---|
| committer | 2017-02-08 06:04:52 -0800 | |
| commit | 929d6517dfd338f0d481dbe6587643d5aef27ec6 (patch) | |
| tree | b80a93b93946bd4e982b9f4abd97a4c3aa43feb3 | |
| parent | c270de85cc0c398d9ce165592908d2740219a708 (diff) | |
AssetManager2: Add GetResourceId
Add ability to lookup a resource by name.
Test: make libandroidfw_tests
Change-Id: I262ba5ce4c9892458226fbdb44cf21f9877fb92d
| -rw-r--r-- | libs/androidfw/Android.bp | 1 | ||||
| -rw-r--r-- | libs/androidfw/AssetManager2.cpp | 101 | ||||
| -rw-r--r-- | libs/androidfw/LoadedArsc.cpp | 48 | ||||
| -rw-r--r-- | libs/androidfw/ResourceUtils.cpp | 48 | ||||
| -rw-r--r-- | libs/androidfw/include/androidfw/LoadedArsc.h | 7 | ||||
| -rw-r--r-- | libs/androidfw/include/androidfw/ResourceUtils.h | 60 | ||||
| -rw-r--r-- | libs/androidfw/include/androidfw/StringPiece.h | 10 | ||||
| -rw-r--r-- | libs/androidfw/include/androidfw/Util.h | 23 | ||||
| -rw-r--r-- | libs/androidfw/tests/Android.mk | 1 | ||||
| -rw-r--r-- | libs/androidfw/tests/AssetManager2_test.cpp | 9 | ||||
| -rw-r--r-- | libs/androidfw/tests/ResourceUtils_test.cpp | 49 | ||||
| -rw-r--r-- | libs/androidfw/tests/Theme_test.cpp | 7 | ||||
| -rw-r--r-- | tools/aapt2/ResourceUtils.cpp | 36 | ||||
| -rw-r--r-- | tools/aapt2/ResourceUtils.h | 12 |
14 files changed, 316 insertions, 96 deletions
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index ecf6bd4a0cf1..e764034ccd1c 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -36,6 +36,7 @@ cc_library { "misc.cpp", "ObbFile.cpp", "ResourceTypes.cpp", + "ResourceUtils.cpp", "StreamingZipInflater.cpp", "TypeWrappers.cpp", "Util.cpp", diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 542a125f018b..ef0c96736868 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -31,6 +31,8 @@ #endif #endif +#include "androidfw/ResourceUtils.h" + namespace android { AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } @@ -235,9 +237,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri desired_config = &density_override_config; } - const uint32_t package_id = util::get_package_id(resid); - const uint8_t type_id = util::get_type_id(resid); - const uint16_t entry_id = util::get_entry_id(resid); + const uint32_t package_id = get_package_id(resid); + const uint8_t type_id = get_type_id(resid); + const uint16_t entry_id = get_entry_id(resid); if (type_id == 0) { LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); @@ -452,7 +454,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { ResolvedBag::Entry* new_entry = new_bag->entries; for (; map_entry != map_entry_end; ++map_entry) { uint32_t new_key = dtohl(map_entry->name.ident); - if (!util::is_internal_resid(new_key)) { + 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) { @@ -501,7 +503,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // The keys are expected to be in sorted order. Merge the two bags. while (map_entry != map_entry_end && parent_entry != parent_entry_end) { uint32_t child_key = dtohl(map_entry->name.ident); - if (!util::is_internal_resid(child_key)) { + if (!is_internal_resid(child_key)) { if (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; @@ -533,7 +535,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Finish the child entries if they exist. while (map_entry != map_entry_end) { uint32_t new_key = dtohl(map_entry->name.ident); - if (!util::is_internal_resid(new_key)) { + if (!is_internal_resid(new_key)) { if (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; @@ -571,12 +573,71 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { return result; } +static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { + ssize_t len = + utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str.data()), str.size(), false); + if (len < 0) { + return false; + } + out->resize(static_cast<size_t>(len)); + utf8_to_utf16(reinterpret_cast<const uint8_t*>(str.data()), str.size(), &*out->begin(), + static_cast<size_t>(len + 1)); + return true; +} + uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const std::string& fallback_type, const std::string& fallback_package) { - (void)resource_name; - (void)fallback_type; - (void)fallback_package; + StringPiece package_name, type, entry; + if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { + return 0u; + } + + if (entry.empty()) { + return 0u; + } + + if (package_name.empty()) { + package_name = fallback_package; + } + + if (type.empty()) { + type = fallback_type; + } + + std::u16string type16; + if (!Utf8ToUtf16(type, &type16)) { + return 0u; + } + + std::u16string entry16; + if (!Utf8ToUtf16(entry, &entry16)) { + return 0u; + } + + const StringPiece16 kAttr16 = u"attr"; + const static std::u16string kAttrPrivate16 = u"^attr-private"; + + for (const PackageGroup& package_group : package_groups_) { + for (const LoadedPackage* package : package_group.packages_) { + if (package_name != package->GetPackageName()) { + // All packages in the same group are expected to have the same package name. + break; + } + + uint32_t resid = package->FindEntryByName(type16, entry16); + if (resid == 0u && 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); + } + } + } return 0u; } @@ -619,15 +680,15 @@ bool Theme::ApplyStyle(uint32_t resid, bool force) { // If the resource ID passed in is not a style, the key can be // some other identifier that is not a resource ID. - if (!util::is_valid_resid(attr_resid)) { + if (!is_valid_resid(attr_resid)) { return false; } - const uint32_t package_idx = util::get_package_id(attr_resid); + const uint32_t package_idx = get_package_id(attr_resid); // The type ID is 1-based, so subtract 1 to get an index. - const uint32_t type_idx = util::get_type_id(attr_resid) - 1; - const uint32_t entry_idx = util::get_entry_id(attr_resid); + const uint32_t type_idx = get_type_id(attr_resid) - 1; + const uint32_t entry_idx = get_entry_id(attr_resid); std::unique_ptr<Package>& package = packages_[package_idx]; if (package == nullptr) { @@ -656,9 +717,9 @@ bool Theme::ApplyStyle(uint32_t resid, bool force) { // and populate the structures. for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) { const uint32_t attr_resid = bag_iter->key; - const uint32_t package_idx = util::get_package_id(attr_resid); - const uint32_t type_idx = util::get_type_id(attr_resid) - 1; - const uint32_t entry_idx = util::get_entry_id(attr_resid); + const uint32_t package_idx = get_package_id(attr_resid); + const uint32_t type_idx = get_type_id(attr_resid) - 1; + const uint32_t entry_idx = get_entry_id(attr_resid); Package* package = packages_[package_idx].get(); util::unique_cptr<Type>& type = package->types[type_idx]; if (type->entry_count != type->entry_capacity) { @@ -691,15 +752,15 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, uint32_t type_spec_flags = 0u; for (int iterations_left = kMaxIterations; iterations_left > 0; iterations_left--) { - if (!util::is_valid_resid(resid)) { + if (!is_valid_resid(resid)) { return kInvalidCookie; } - const uint32_t package_idx = util::get_package_id(resid); + const uint32_t package_idx = get_package_id(resid); // Type ID is 1-based, subtract 1 to get the index. - const uint32_t type_idx = util::get_type_id(resid) - 1; - const uint32_t entry_idx = util::get_entry_id(resid); + const uint32_t type_idx = get_type_id(resid) - 1; + const uint32_t entry_idx = get_entry_id(resid); const Package* package = packages_[package_idx].get(); if (package == nullptr) { diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index cb589ec41034..db72f487d52b 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -34,6 +34,7 @@ #include "androidfw/ByteBucketArray.h" #include "androidfw/Chunk.h" +#include "androidfw/ResourceUtils.h" #include "androidfw/Util.h" using android::base::StringPrintf; @@ -181,9 +182,9 @@ bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, LoadedArscEntry* out_entry, ResTable_config* out_selected_config, uint32_t* out_flags) const { ATRACE_CALL(); - const uint8_t package_id = util::get_package_id(resid); - const uint8_t type_id = util::get_type_id(resid); - const uint16_t entry_id = util::get_entry_id(resid); + const uint8_t package_id = get_package_id(resid); + const uint8_t type_id = get_type_id(resid); + const uint16_t entry_id = get_entry_id(resid); if (type_id == 0) { LOG(ERROR) << "Invalid ID 0x" << std::hex << resid << std::dec << "."; @@ -200,7 +201,7 @@ bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, } const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { - const uint8_t package_id = util::get_package_id(resid); + const uint8_t package_id = get_package_id(resid); for (const auto& loaded_package : packages_) { if (loaded_package->package_id_ == package_id) { return loaded_package.get(); @@ -372,6 +373,45 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* 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; + } + + ssize_t key_idx = key_string_pool_.indexOfString(entry_name.data(), entry_name.size()); + if (key_idx < 0) { + return 0u; + } + + const TypeSpec* type_spec = type_specs_[type_idx].get(); + if (type_spec == nullptr) { + return 0u; + } + + for (size_t ti = 0; ti < type_spec->type_count; ti++) { + const Type* type = &type_spec->types[ti]; + size_t entry_count = dtohl(type->type->entryCount); + for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { + const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>( + reinterpret_cast<const uint8_t*>(type->type) + dtohs(type->type->header.headerSize)); + const uint32_t offset = dtohl(entry_offsets[entry_idx]); + if (offset != ResTable_type::NO_ENTRY) { + const ResTable_entry* entry = + reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type->type) + + dtohl(type->type->entriesStart) + offset); + if (dtohl(entry->key.index) == static_cast<uint32_t>(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 0u; +} + std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) { ATRACE_CALL(); std::unique_ptr<LoadedPackage> loaded_package{new LoadedPackage()}; diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp new file mode 100644 index 000000000000..1aa6cf6da28d --- /dev/null +++ b/libs/androidfw/ResourceUtils.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "androidfw/ResourceUtils.h" + +namespace android { + +bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, StringPiece* out_type, + StringPiece* out_entry) { + *out_package = ""; + *out_type = ""; + bool has_package_separator = false; + bool has_type_separator = false; + const char* start = str.data(); + const char* end = start + str.size(); + const char* current = start; + while (current != end) { + if (out_type->size() == 0 && *current == '/') { + has_type_separator = true; + out_type->assign(start, current - start); + start = current + 1; + } else if (out_package->size() == 0 && *current == ':') { + has_package_separator = true; + out_package->assign(start, current - start); + start = current + 1; + } + current++; + } + out_entry->assign(start, end - start); + + return !(has_package_separator && out_package->empty()) && + !(has_type_separator && out_type->empty()); +} + +} // namespace android diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 91a7cb7f45f9..f30b158084eb 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -101,6 +101,13 @@ class LoadedPackage { // before being inserted into the set. This may cause some equivalent locales to de-dupe. void CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const; + // Finds the entry with the specified type name and entry name. The names are in UTF-16 because + // the underlying ResStringPool API expects this. For now this is acceptable, but since + // 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; + private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h new file mode 100644 index 000000000000..6bf7c2438797 --- /dev/null +++ b/libs/androidfw/include/androidfw/ResourceUtils.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2017 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_RESOURCEUTILS_H +#define ANDROIDFW_RESOURCEUTILS_H + +#include "androidfw/StringPiece.h" + +namespace android { + +// Extracts the package, type, and name from a string of the format: [[package:]type/]name +// Validation must be performed on each extracted piece. +// Returns false if there was a syntax error. +bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, StringPiece* out_type, + StringPiece* out_entry); + +inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) { + return resid | (static_cast<uint32_t>(package_id) << 24); +} + +inline uint8_t get_package_id(uint32_t resid) { + return static_cast<uint8_t>((resid >> 24) & 0x000000ffu); +} + +// The type ID is 1-based, so if the returned value is 0 it is invalid. +inline uint8_t get_type_id(uint32_t resid) { + return static_cast<uint8_t>((resid >> 16) & 0x000000ffu); +} + +inline uint16_t get_entry_id(uint32_t resid) { return static_cast<uint16_t>(resid & 0x0000ffffu); } + +inline bool is_internal_resid(uint32_t resid) { + return (resid & 0xffff0000u) != 0 && (resid & 0x00ff0000u) == 0; +} + +inline bool is_valid_resid(uint32_t resid) { + return (resid & 0x00ff0000u) != 0 && (resid & 0xff000000u) != 0; +} + +inline uint32_t make_resid(uint8_t package_id, uint8_t type_id, uint16_t entry_id) { + return (static_cast<uint32_t>(package_id) << 24) | (static_cast<uint32_t>(type_id) << 16) | + entry_id; +} + +} // namespace android + +#endif /* ANDROIDFW_RESOURCEUTILS_H */ diff --git a/libs/androidfw/include/androidfw/StringPiece.h b/libs/androidfw/include/androidfw/StringPiece.h index c9effd1a5112..8f6824b5322c 100644 --- a/libs/androidfw/include/androidfw/StringPiece.h +++ b/libs/androidfw/include/androidfw/StringPiece.h @@ -257,6 +257,16 @@ inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>: return data_ + length_; } +template <typename TChar> +inline bool operator==(const TChar* lhs, const BasicStringPiece<TChar>& rhs) { + return BasicStringPiece<TChar>(lhs) == rhs; +} + +template <typename TChar> +inline bool operator!=(const TChar* lhs, const BasicStringPiece<TChar>& rhs) { + return BasicStringPiece<TChar>(lhs) != rhs; +} + inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char>& str) { return out.write(str.data(), str.size()); } diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h index 96b42bf45a08..3950cf240a9e 100644 --- a/libs/androidfw/include/androidfw/Util.h +++ b/libs/androidfw/include/androidfw/Util.h @@ -106,29 +106,6 @@ class unique_cptr { pointer ptr_; }; -inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) { - return resid | (static_cast<uint32_t>(package_id) << 24); -} - -inline uint8_t get_package_id(uint32_t resid) { - return static_cast<uint8_t>((resid >> 24) & 0x000000ffu); -} - -// The type ID is 1-based, so if the returned value is 0 it is invalid. -inline uint8_t get_type_id(uint32_t resid) { - return static_cast<uint8_t>((resid >> 16) & 0x000000ffu); -} - -inline uint16_t get_entry_id(uint32_t resid) { return static_cast<uint16_t>(resid & 0x0000ffffu); } - -inline bool is_internal_resid(uint32_t resid) { - return (resid & 0xffff0000u) != 0 && (resid & 0x00ff0000u) == 0; -} - -inline bool is_valid_resid(uint32_t resid) { - return (resid & 0x00ff0000u) != 0 && (resid & 0xff000000u) != 0; -} - void ReadUtf16StringFromDevice(const uint16_t* src, size_t len, std::string* out); } // namespace util diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk index 650f8138366b..19527c51e816 100644 --- a/libs/androidfw/tests/Android.mk +++ b/libs/androidfw/tests/Android.mk @@ -32,6 +32,7 @@ testFiles := \ ConfigLocale_test.cpp \ Idmap_test.cpp \ LoadedArsc_test.cpp \ + ResourceUtils_test.cpp \ ResTable_test.cpp \ Split_test.cpp \ StringPiece_test.cpp \ diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 557d8d40dfc2..78fbb0f85f68 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -20,6 +20,7 @@ #include "android-base/logging.h" #include "TestHelpers.h" +#include "androidfw/ResourceUtils.h" #include "data/appaslib/R.h" #include "data/basic/R.h" #include "data/lib_one/R.h" @@ -194,11 +195,11 @@ TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) { ResTable_config selected_config; uint32_t flags; ApkAssetsCookie cookie = assetmanager.GetResource( - util::fix_package_id(appaslib::R::integer::number1, 0x02), false /*may_be_bag*/, + 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(util::fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data); + EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data); } TEST_F(AssetManager2Test, FindsBagResourceFromSingleApkAssets) { @@ -238,9 +239,9 @@ TEST_F(AssetManager2Test, FindsBagResourceFromSharedLibrary) { // First two attributes come from lib_one. EXPECT_EQ(1, bag->entries[0].cookie); - EXPECT_EQ(0x03, util::get_package_id(bag->entries[0].key)); + EXPECT_EQ(0x03, get_package_id(bag->entries[0].key)); EXPECT_EQ(1, bag->entries[1].cookie); - EXPECT_EQ(0x03, util::get_package_id(bag->entries[1].key)); + EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); } TEST_F(AssetManager2Test, MergesStylesWithParentFromSingleApkAssets) { diff --git a/libs/androidfw/tests/ResourceUtils_test.cpp b/libs/androidfw/tests/ResourceUtils_test.cpp new file mode 100644 index 000000000000..b64a884fb8fb --- /dev/null +++ b/libs/androidfw/tests/ResourceUtils_test.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "androidfw/ResourceUtils.h" + +#include "TestHelpers.h" + +namespace android { + +TEST(ResourceUtilsTest, ExtractResourceName) { + StringPiece package, type, entry; + ASSERT_TRUE(ExtractResourceName("android:string/foo", &package, &type, &entry)); + EXPECT_EQ("android", package); + EXPECT_EQ("string", type); + EXPECT_EQ("foo", entry); + + ASSERT_TRUE(ExtractResourceName("string/foo", &package, &type, &entry)); + EXPECT_EQ("", package); + EXPECT_EQ("string", type); + EXPECT_EQ("foo", entry); + + ASSERT_TRUE(ExtractResourceName("foo", &package, &type, &entry)); + EXPECT_EQ("", package); + EXPECT_EQ("", type); + EXPECT_EQ("foo", entry); + + ASSERT_TRUE(ExtractResourceName("android:foo", &package, &type, &entry)); + EXPECT_EQ("android", package); + EXPECT_EQ("", type); + EXPECT_EQ("foo", entry); + + EXPECT_FALSE(ExtractResourceName(":string/foo", &package, &type, &entry)); + EXPECT_FALSE(ExtractResourceName("/foo", &package, &type, &entry)); +} + +} // namespace android diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp index daed28b01a4f..dfff9c00922c 100644 --- a/libs/androidfw/tests/Theme_test.cpp +++ b/libs/androidfw/tests/Theme_test.cpp @@ -19,6 +19,7 @@ #include "android-base/logging.h" #include "TestHelpers.h" +#include "androidfw/ResourceUtils.h" #include "data/lib_one/R.h" #include "data/libclient/R.h" #include "data/styles/R.h" @@ -215,9 +216,9 @@ TEST_F(ThemeTest, ResolveDynamicAttributesAndReferencesToSharedLibrary) { EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); // lib_one is assigned package ID 0x03. - EXPECT_EQ(3u, util::get_package_id(value.data)); - EXPECT_EQ(util::get_type_id(lib_one::R::string::foo), util::get_type_id(value.data)); - EXPECT_EQ(util::get_entry_id(lib_one::R::string::foo), util::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) { diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index 11239673272e..150dc58290d4 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -19,6 +19,7 @@ #include <sstream> #include "androidfw/ResourceTypes.h" +#include "androidfw/ResourceUtils.h" #include "NameMangler.h" #include "SdkConstants.h" @@ -69,31 +70,6 @@ Maybe<ResourceName> ToResourceName( return name_out; } -bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, - StringPiece* out_type, StringPiece* out_entry) { - bool has_package_separator = false; - bool has_type_separator = false; - const char* start = str.data(); - const char* end = start + str.size(); - const char* current = start; - while (current != end) { - if (out_type->size() == 0 && *current == '/') { - has_type_separator = true; - out_type->assign(start, current - start); - start = current + 1; - } else if (out_package->size() == 0 && *current == ':') { - has_package_separator = true; - out_package->assign(start, current - start); - start = current + 1; - } - current++; - } - out_entry->assign(start, end - start); - - return !(has_package_separator && out_package->empty()) && - !(has_type_separator && out_type->empty()); -} - bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_ref, bool* out_private) { if (str.empty()) { @@ -110,8 +86,8 @@ bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_ref, StringPiece package; StringPiece type; StringPiece entry; - if (!ExtractResourceName(str.substr(offset, str.size() - offset), &package, - &type, &entry)) { + if (!android::ExtractResourceName(str.substr(offset, str.size() - offset), &package, &type, + &entry)) { return false; } @@ -197,8 +173,8 @@ bool ParseAttributeReference(const StringPiece& str, ResourceNameRef* out_ref) { StringPiece package; StringPiece type; StringPiece entry; - if (!ExtractResourceName(trimmed_str.substr(1, trimmed_str.size() - 1), - &package, &type, &entry)) { + if (!android::ExtractResourceName(trimmed_str.substr(1, trimmed_str.size() - 1), &package, + &type, &entry)) { return false; } @@ -258,7 +234,7 @@ Maybe<Reference> ParseStyleParentReference(const StringPiece& str, ref.type = ResourceType::kStyle; StringPiece type_str; - ExtractResourceName(name, &ref.package, &type_str, &ref.entry); + android::ExtractResourceName(name, &ref.package, &type_str, &ref.entry); if (!type_str.empty()) { // If we have a type, make sure it is a Style. const ResourceType* parsed_type = ParseResourceType(type_str); diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h index bd3a8e3e0bc8..59b78f4b3f33 100644 --- a/tools/aapt2/ResourceUtils.h +++ b/tools/aapt2/ResourceUtils.h @@ -29,18 +29,6 @@ namespace aapt { namespace ResourceUtils { -/* - * Extracts the package, type, and name from a string of the format: - * - * [package:]type/name - * - * where the package can be empty. Validation must be performed on each - * individual extracted piece to verify that the pieces are valid. - * Returns false if there was no package but a ':' was present. - */ -bool ExtractResourceName(const android::StringPiece& str, android::StringPiece* out_package, - android::StringPiece* out_type, android::StringPiece* out_entry); - /** * Returns true if the string was parsed as a resource name * ([*][package:]type/name), with |