diff options
30 files changed, 2125 insertions, 182 deletions
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 763a178ed43b..0f5b65c44686 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -6074,6 +6074,10 @@ bool ResTable::getResourceFlags(uint32_t resID, uint32_t* outFlags) const { return true; } +static bool keyCompare(const ResTable_sparseTypeEntry& entry , uint16_t entryIdx) { + return dtohs(entry.idx) < entryIdx; +} + status_t ResTable::getEntry( const PackageGroup* packageGroup, int typeIndex, int entryIndex, const ResTable_config* config, @@ -6115,6 +6119,9 @@ status_t ResTable::getEntry( currentTypeIsOverlay = true; } + // Check that the entry idx is within range of the declared entry count (ResTable_typeSpec). + // Particular types (ResTable_type) may be encoded with sparse entries, and so their + // entryCount do not need to match. if (static_cast<size_t>(realEntryIndex) >= typeSpec->entryCount) { ALOGW("For resource 0x%08x, entry index(%d) is beyond type entryCount(%d)", Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex), @@ -6169,11 +6176,37 @@ status_t ResTable::getEntry( continue; } - // Check if there is the desired entry in this type. const uint32_t* const eindex = reinterpret_cast<const uint32_t*>( reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize)); - uint32_t thisOffset = dtohl(eindex[realEntryIndex]); + uint32_t thisOffset; + + // Check if there is the desired entry in this type. + if (thisType->flags & ResTable_type::FLAG_SPARSE) { + // This is encoded as a sparse map, so perform a binary search. + const ResTable_sparseTypeEntry* sparseIndices = + reinterpret_cast<const ResTable_sparseTypeEntry*>(eindex); + const ResTable_sparseTypeEntry* result = std::lower_bound( + sparseIndices, sparseIndices + dtohl(thisType->entryCount), realEntryIndex, + keyCompare); + if (result == sparseIndices + dtohl(thisType->entryCount) + || dtohs(result->idx) != realEntryIndex) { + // No entry found. + continue; + } + + // 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. + thisOffset = dtohs(result->offset) * 4u; + } else { + if (static_cast<uint32_t>(realEntryIndex) >= dtohl(thisType->entryCount)) { + // Entry does not exist. + continue; + } + + thisOffset = dtohl(eindex[realEntryIndex]); + } + if (thisOffset == ResTable_type::NO_ENTRY) { // There is no entry for this index and configuration. continue; @@ -6480,12 +6513,6 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, } Type* t = typeList.editItemAt(typeList.size() - 1); - if (newEntryCount != t->entryCount) { - ALOGE("ResTable_type entry count inconsistent: given %d, previously %d", - (int)newEntryCount, (int)t->entryCount); - return (mError=BAD_TYPE); - } - if (t->package != package) { ALOGE("No TypeSpec for type %d", type->id); return (mError=BAD_TYPE); @@ -7096,8 +7123,17 @@ void ResTable::print(bool inclValues) const thisConfig.copyFromDtoH(type->config); String8 configStr = thisConfig.toString(); - printf(" config %s:\n", configStr.size() > 0 + printf(" config %s", configStr.size() > 0 ? configStr.string() : "(default)"); + if (type->flags != 0u) { + printf(" flags=0x%02x", type->flags); + if (type->flags & ResTable_type::FLAG_SPARSE) { + printf(" [sparse]"); + } + } + + printf(":\n"); + size_t entryCount = dtohl(type->entryCount); uint32_t entriesStart = dtohl(type->entriesStart); if ((entriesStart&0x3) != 0) { @@ -7109,18 +7145,30 @@ void ResTable::print(bool inclValues) const printf(" NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize); continue; } - for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) { - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)type) + dtohs(type->header.headerSize)); - uint32_t thisOffset = dtohl(eindex[entryIndex]); - if (thisOffset == ResTable_type::NO_ENTRY) { - continue; + const uint32_t* const eindex = (const uint32_t*) + (((const uint8_t*)type) + dtohs(type->header.headerSize)); + for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) { + size_t entryId; + uint32_t thisOffset; + if (type->flags & ResTable_type::FLAG_SPARSE) { + const ResTable_sparseTypeEntry* entry = + reinterpret_cast<const ResTable_sparseTypeEntry*>( + eindex + entryIndex); + entryId = dtohs(entry->idx); + // Offsets are encoded as divided by 4. + thisOffset = static_cast<uint32_t>(dtohs(entry->offset)) * 4u; + } else { + entryId = entryIndex; + thisOffset = dtohl(eindex[entryIndex]); + if (thisOffset == ResTable_type::NO_ENTRY) { + continue; + } } uint32_t resID = (0xff000000 & ((packageId)<<24)) | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); + | (0x0000ffff & (entryId)); if (packageId == 0) { pg->dynamicRefTable.lookupResourceId(&resID); } diff --git a/libs/androidfw/TypeWrappers.cpp b/libs/androidfw/TypeWrappers.cpp index 06b40405f404..647aa197a94d 100644 --- a/libs/androidfw/TypeWrappers.cpp +++ b/libs/androidfw/TypeWrappers.cpp @@ -16,23 +16,45 @@ #include <androidfw/TypeWrappers.h> +#include <algorithm> + namespace android { +TypeVariant::TypeVariant(const ResTable_type* data) : data(data), mLength(dtohl(data->entryCount)) { + if (data->flags & ResTable_type::FLAG_SPARSE) { + const uint32_t entryCount = dtohl(data->entryCount); + const uintptr_t containerEnd = reinterpret_cast<uintptr_t>(data) + dtohl(data->header.size); + const uint32_t* const entryIndices = reinterpret_cast<const uint32_t*>( + reinterpret_cast<uintptr_t>(data) + dtohs(data->header.headerSize)); + if (reinterpret_cast<uintptr_t>(entryIndices) + (sizeof(uint32_t) * entryCount) + > containerEnd) { + ALOGE("Type's entry indices extend beyond its boundaries"); + mLength = 0; + } else { + mLength = ResTable_sparseTypeEntry{entryIndices[entryCount - 1]}.idx + 1; + } + } +} + TypeVariant::iterator& TypeVariant::iterator::operator++() { mIndex++; - if (mIndex > dtohl(mTypeVariant->data->entryCount)) { - mIndex = dtohl(mTypeVariant->data->entryCount); + if (mIndex > mTypeVariant->mLength) { + mIndex = mTypeVariant->mLength; } return *this; } +static bool keyCompare(uint32_t entry, uint16_t index) { + return dtohs(ResTable_sparseTypeEntry{entry}.idx) < index; +} + const ResTable_entry* TypeVariant::iterator::operator*() const { const ResTable_type* type = mTypeVariant->data; - const uint32_t entryCount = dtohl(type->entryCount); - if (mIndex >= entryCount) { + if (mIndex >= mTypeVariant->mLength) { return NULL; } + const uint32_t entryCount = dtohl(mTypeVariant->data->entryCount); const uintptr_t containerEnd = reinterpret_cast<uintptr_t>(type) + dtohl(type->header.size); const uint32_t* const entryIndices = reinterpret_cast<const uint32_t*>( @@ -42,7 +64,19 @@ const ResTable_entry* TypeVariant::iterator::operator*() const { return NULL; } - const uint32_t entryOffset = dtohl(entryIndices[mIndex]); + uint32_t entryOffset; + if (type->flags & ResTable_type::FLAG_SPARSE) { + auto iter = std::lower_bound(entryIndices, entryIndices + entryCount, mIndex, keyCompare); + if (iter == entryIndices + entryCount + || dtohs(ResTable_sparseTypeEntry{*iter}.idx) != mIndex) { + return NULL; + } + + entryOffset = static_cast<uint32_t>(dtohs(ResTable_sparseTypeEntry{*iter}.offset)) * 4u; + } else { + entryOffset = dtohl(entryIndices[mIndex]); + } + if (entryOffset == ResTable_type::NO_ENTRY) { return NULL; } diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 04a5d958c614..5c5c9e6b4f55 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -1339,12 +1339,21 @@ struct ResTable_typeSpec /** * A collection of resource entries for a particular resource data - * type. Followed by an array of uint32_t defining the resource + * type. + * + * If the flag FLAG_SPARSE is not set in `flags`, then this struct is + * followed by an array of uint32_t defining the resource * values, corresponding to the array of type strings in the * ResTable_package::typeStrings string block. Each of these hold an * index from entriesStart; a value of NO_ENTRY means that entry is * not defined. * + * If the flag FLAG_SPARSE is set in `flags`, then this struct is followed + * by an array of ResTable_sparseTypeEntry defining only the entries that + * have values for this type. Each entry is sorted by their entry ID such + * that a binary search can be performed over the entries. The ID and offset + * are encoded in a uint32_t. See ResTabe_sparseTypeEntry. + * * There may be multiple of these chunks for a particular resource type, * supply different configuration variations for the resource values of * that type. @@ -1365,10 +1374,17 @@ struct ResTable_type // resource identifier). 0 is invalid. uint8_t id; + enum { + // If set, the entry is sparse, and encodes both the entry ID and offset into each entry, + // and a binary search is used to find the key. Only available on platforms >= O. + // Mark any types that use this with a v26 qualifier to prevent runtime issues on older + // platforms. + FLAG_SPARSE = 0x01, + }; + uint8_t flags; + // Must be 0. - uint8_t res0; - // Must be 0. - uint16_t res1; + uint16_t reserved; // Number of uint32_t entry indices that follow. uint32_t entryCount; @@ -1381,6 +1397,24 @@ struct ResTable_type }; /** + * An entry in a ResTable_type with the flag `FLAG_SPARSE` set. + */ +union ResTable_sparseTypeEntry { + // Holds the raw uint32_t encoded value. Do not read this. + uint32_t entry; + struct { + // The index of the entry. + uint16_t idx; + + // The offset from ResTable_type::entriesStart, divided by 4. + uint16_t offset; + }; +}; + +static_assert(sizeof(ResTable_sparseTypeEntry) == sizeof(uint32_t), + "ResTable_sparseTypeEntry must be 4 bytes in size"); + +/** * This is the beginning of information about an entry in the resource * table. It holds the reference to the name of this entry, and is * immediately followed by one of: diff --git a/libs/androidfw/include/androidfw/TypeWrappers.h b/libs/androidfw/include/androidfw/TypeWrappers.h index f1daf3365c28..5cfe54e5759d 100644 --- a/libs/androidfw/include/androidfw/TypeWrappers.h +++ b/libs/androidfw/include/androidfw/TypeWrappers.h @@ -23,8 +23,7 @@ namespace android { struct TypeVariant { - TypeVariant(const ResTable_type* data) - : data(data) {} + TypeVariant(const ResTable_type* data); class iterator { public: @@ -72,10 +71,13 @@ struct TypeVariant { } iterator endEntries() const { - return iterator(this, dtohl(data->entryCount)); + return iterator(this, mLength); } const ResTable_type* data; + +private: + size_t mLength; }; } // namespace android diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk index 19527c51e816..921fd147aa80 100644 --- a/libs/androidfw/tests/Android.mk +++ b/libs/androidfw/tests/Android.mk @@ -45,6 +45,8 @@ testFiles := \ benchmarkFiles := \ AssetManager2_bench.cpp \ BenchMain.cpp \ + BenchmarkHelpers.cpp \ + SparseEntry_bench.cpp \ TestHelpers.cpp \ Theme_bench.cpp diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index 273290a26050..67de741b1b66 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -22,6 +22,7 @@ #include "androidfw/AssetManager2.h" #include "androidfw/ResourceTypes.h" +#include "BenchmarkHelpers.h" #include "TestHelpers.h" #include "data/basic/R.h" #include "data/libclient/R.h" @@ -112,34 +113,6 @@ static void GetResourceBenchmark(const std::vector<std::string>& paths, } } -static void GetResourceBenchmarkOld(const std::vector<std::string>& paths, - const ResTable_config* config, uint32_t resid, - benchmark::State& state) { - AssetManager assetmanager; - for (const std::string& path : paths) { - if (!assetmanager.addAssetPath(String8(path.c_str()), nullptr /* cookie */, - false /* appAsLib */, false /* isSystemAssets */)) { - state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str()); - return; - } - } - - if (config != nullptr) { - assetmanager.setConfiguration(*config); - } - - const ResTable& table = assetmanager.getResources(true); - - Res_value value; - ResTable_config selected_config; - uint32_t flags; - - while (state.KeepRunning()) { - table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, - &selected_config); - } -} - static void BM_AssetManagerGetResource(benchmark::State& state) { GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, basic::R::integer::number1, state); diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp new file mode 100644 index 000000000000..3619b7ee83ab --- /dev/null +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -0,0 +1,51 @@ +/* + * 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 "BenchmarkHelpers.h" + +#include "android-base/stringprintf.h" +#include "androidfw/AssetManager.h" + +namespace android { + +void GetResourceBenchmarkOld(const std::vector<std::string>& paths, const ResTable_config* config, + uint32_t resid, benchmark::State& state) { + AssetManager assetmanager; + for (const std::string& path : paths) { + if (!assetmanager.addAssetPath(String8(path.c_str()), nullptr /* cookie */, + false /* appAsLib */, false /* isSystemAssets */)) { + state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str()); + return; + } + } + + if (config != nullptr) { + assetmanager.setConfiguration(*config); + } + + const ResTable& table = assetmanager.getResources(true); + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + + while (state.KeepRunning()) { + table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, + &selected_config); + } +} + +} // namespace android diff --git a/libs/androidfw/tests/BenchmarkHelpers.h b/libs/androidfw/tests/BenchmarkHelpers.h new file mode 100644 index 000000000000..fc366642ca36 --- /dev/null +++ b/libs/androidfw/tests/BenchmarkHelpers.h @@ -0,0 +1,34 @@ +/* + * 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 TESTS_BENCHMARKHELPERS_H_ +#define TESTS_BENCHMARKHELPERS_H_ + +#include <string> +#include <vector> + +#include "benchmark/benchmark.h" + +#include "androidfw/ResourceTypes.h" + +namespace android { + +void GetResourceBenchmarkOld(const std::vector<std::string>& paths, const ResTable_config* config, + uint32_t resid, benchmark::State& state); + +} // namespace android + +#endif /* TESTS_BENCHMARKHELPERS_H_ */ diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp index ad1cd2b289d6..2df41305237e 100644 --- a/libs/androidfw/tests/ResTable_test.cpp +++ b/libs/androidfw/tests/ResTable_test.cpp @@ -41,6 +41,34 @@ TEST(ResTableTest, ShouldLoadSuccessfully) { ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); } +TEST(ResTableTest, ShouldLoadSparseEntriesSuccessfully) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc", + &contents)); + + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 26; + table.setParameters(&config); + + String16 name(u"com.android.sparse:integer/foo_9"); + uint32_t flags; + uint32_t resid = + table.identifierForName(name.string(), name.size(), nullptr, 0, nullptr, 0, &flags); + ASSERT_NE(0u, resid); + + Res_value val; + ResTable_config selected_config; + ASSERT_GE( + table.getResource(resid, &val, false /*mayBeBag*/, 0u /*density*/, &flags, &selected_config), + 0); + EXPECT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + EXPECT_EQ(900u, val.data); +} + TEST(ResTableTest, SimpleTypeIsRetrievedCorrectly) { std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", diff --git a/libs/androidfw/tests/SparseEntry_bench.cpp b/libs/androidfw/tests/SparseEntry_bench.cpp new file mode 100644 index 000000000000..1ebf7ce623bd --- /dev/null +++ b/libs/androidfw/tests/SparseEntry_bench.cpp @@ -0,0 +1,64 @@ +/* + * 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/AssetManager.h" +#include "androidfw/ResourceTypes.h" + +#include "BenchmarkHelpers.h" +#include "TestHelpers.h" +#include "data/sparse/R.h" + +namespace sparse = com::android::sparse; + +namespace android { + +static void BM_SparseEntryGetResourceSparseSmall(benchmark::State& state) { + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 26; + GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/sparse.apk"}, &config, + sparse::R::integer::foo_9, state); +} +BENCHMARK(BM_SparseEntryGetResourceSparseSmall); + +static void BM_SparseEntryGetResourceNotSparseSmall(benchmark::State& state) { + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 26; + GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config, + sparse::R::integer::foo_9, state); +} +BENCHMARK(BM_SparseEntryGetResourceNotSparseSmall); + +static void BM_SparseEntryGetResourceSparseLarge(benchmark::State& state) { + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 26; + GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/sparse.apk"}, &config, + sparse::R::string::foo_999, state); +} +BENCHMARK(BM_SparseEntryGetResourceSparseLarge); + +static void BM_SparseEntryGetResourceNotSparseLarge(benchmark::State& state) { + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 26; + GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config, + sparse::R::string::foo_999, state); +} +BENCHMARK(BM_SparseEntryGetResourceNotSparseLarge); + +} // namespace android diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h index a11ea8416c7d..ec78b2ae5efc 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -19,6 +19,7 @@ #include <ostream> #include <string> +#include <vector> #include "androidfw/ResourceTypes.h" #include "gtest/gtest.h" diff --git a/libs/androidfw/tests/data/sparse/.gitignore b/libs/androidfw/tests/data/sparse/.gitignore new file mode 100644 index 000000000000..52e32d4fcbf2 --- /dev/null +++ b/libs/androidfw/tests/data/sparse/.gitignore @@ -0,0 +1 @@ +*.flata diff --git a/libs/androidfw/tests/data/sparse/AndroidManifest.xml b/libs/androidfw/tests/data/sparse/AndroidManifest.xml new file mode 100644 index 000000000000..27911b62447a --- /dev/null +++ b/libs/androidfw/tests/data/sparse/AndroidManifest.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.sparse"> + <application /> +</manifest> diff --git a/libs/androidfw/tests/data/sparse/R.h b/libs/androidfw/tests/data/sparse/R.h new file mode 100644 index 000000000000..243e74fac65a --- /dev/null +++ b/libs/androidfw/tests/data/sparse/R.h @@ -0,0 +1,53 @@ +/* + * 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 TESTS_DATA_SPARSE_R_H_ +#define TESTS_DATA_SPARSE_R_H_ + +#include <cstdint> + +namespace com { +namespace android { +namespace sparse { + +struct R { + struct integer { + enum : uint32_t { + foo_0 = 0x7f010000, + foo_1 = 0x7f010000, + foo_2 = 0x7f010000, + foo_3 = 0x7f010000, + foo_4 = 0x7f010000, + foo_5 = 0x7f010000, + foo_6 = 0x7f010000, + foo_7 = 0x7f010000, + foo_8 = 0x7f010000, + foo_9 = 0x7f010000, + }; + }; + + struct string { + enum : uint32_t { + foo_999 = 0x7f0203e7, + }; + }; +}; + +} // namespace sparse +} // namespace android +} // namespace com + +#endif /* TESTS_DATA_SPARSE_R_H_ */ diff --git a/libs/androidfw/tests/data/sparse/build b/libs/androidfw/tests/data/sparse/build new file mode 100755 index 000000000000..305593f01875 --- /dev/null +++ b/libs/androidfw/tests/data/sparse/build @@ -0,0 +1,24 @@ +#!/bin/bash +# +# 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. +# + +set -e + +PATH_TO_FRAMEWORK_RES=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/android.jar + +aapt2 compile --dir res -o compiled.flata +aapt2 link --manifest AndroidManifest.xml -I $PATH_TO_FRAMEWORK_RES -o sparse.apk --enable-sparse-encoding compiled.flata +aapt2 link --manifest AndroidManifest.xml -I $PATH_TO_FRAMEWORK_RES -o not_sparse.apk compiled.flata diff --git a/libs/androidfw/tests/data/sparse/gen_strings.sh b/libs/androidfw/tests/data/sparse/gen_strings.sh new file mode 100755 index 000000000000..e7e1d603ea4e --- /dev/null +++ b/libs/androidfw/tests/data/sparse/gen_strings.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +OUTPUT_default=res/values/strings.xml +OUTPUT_v26=res/values-v26/strings.xml + +echo "<resources>" > $OUTPUT_default +echo "<resources>" > $OUTPUT_v26 +for i in {0..999} +do + echo " <string name=\"foo_$i\">$i</string>" >> $OUTPUT_default + if [ "$(($i % 3))" -eq "0" ] + then + echo " <string name=\"foo_$i\">$(($i * 10))</string>" >> $OUTPUT_v26 + fi +done +echo "</resources>" >> $OUTPUT_default +echo "</resources>" >> $OUTPUT_v26 + diff --git a/libs/androidfw/tests/data/sparse/not_sparse.apk b/libs/androidfw/tests/data/sparse/not_sparse.apk Binary files differnew file mode 100644 index 000000000000..599a370dbfb1 --- /dev/null +++ b/libs/androidfw/tests/data/sparse/not_sparse.apk diff --git a/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml b/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml new file mode 100644 index 000000000000..b6f82997d18b --- /dev/null +++ b/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml @@ -0,0 +1,336 @@ +<resources> + <string name="foo_0">0</string> + <string name="foo_3">30</string> + <string name="foo_6">60</string> + <string name="foo_9">90</string> + <string name="foo_12">120</string> + <string name="foo_15">150</string> + <string name="foo_18">180</string> + <string name="foo_21">210</string> + <string name="foo_24">240</string> + <string name="foo_27">270</string> + <string name="foo_30">300</string> + <string name="foo_33">330</string> + <string name="foo_36">360</string> + <string name="foo_39">390</string> + <string name="foo_42">420</string> + <string name="foo_45">450</string> + <string name="foo_48">480</string> + <string name="foo_51">510</string> + <string name="foo_54">540</string> + <string name="foo_57">570</string> + <string name="foo_60">600</string> + <string name="foo_63">630</string> + <string name="foo_66">660</string> + <string name="foo_69">690</string> + <string name="foo_72">720</string> + <string name="foo_75">750</string> + <string name="foo_78">780</string> + <string name="foo_81">810</string> + <string name="foo_84">840</string> + <string name="foo_87">870</string> + <string name="foo_90">900</string> + <string name="foo_93">930</string> + <string name="foo_96">960</string> + <string name="foo_99">990</string> + <string name="foo_102">1020</string> + <string name="foo_105">1050</string> + <string name="foo_108">1080</string> + <string name="foo_111">1110</string> + <string name="foo_114">1140</string> + <string name="foo_117">1170</string> + <string name="foo_120">1200</string> + <string name="foo_123">1230</string> + <string name="foo_126">1260</string> + <string name="foo_129">1290</string> + <string name="foo_132">1320</string> + <string name="foo_135">1350</string> + <string name="foo_138">1380</string> + <string name="foo_141">1410</string> + <string name="foo_144">1440</string> + <string name="foo_147">1470</string> + <string name="foo_150">1500</string> + <string name="foo_153">1530</string> + <string name="foo_156">1560</string> + <string name="foo_159">1590</string> + <string name="foo_162">1620</string> + <string name="foo_165">1650</string> + <string name="foo_168">1680</string> + <string name="foo_171">1710</string> + <string name="foo_174">1740</string> + <string name="foo_177">1770</string> + <string name="foo_180">1800</string> + <string name="foo_183">1830</string> + <string name="foo_186">1860</string> + <string name="foo_189">1890</string> + <string name="foo_192">1920</string> + <string name="foo_195">1950</string> + <string name="foo_198">1980</string> + <string name="foo_201">2010</string> + <string name="foo_204">2040</string> + <string name="foo_207">2070</string> + <string name="foo_210">2100</string> + <string name="foo_213">2130</string> + <string name="foo_216">2160</string> + <string name="foo_219">2190</string> + <string name="foo_222">2220</string> + <string name="foo_225">2250</string> + <string name="foo_228">2280</string> + <string name="foo_231">2310</string> + <string name="foo_234">2340</string> + <string name="foo_237">2370</string> + <string name="foo_240">2400</string> + <string name="foo_243">2430</string> + <string name="foo_246">2460</string> + <string name="foo_249">2490</string> + <string name="foo_252">2520</string> + <string name="foo_255">2550</string> + <string name="foo_258">2580</string> + <string name="foo_261">2610</string> + <string name="foo_264">2640</string> + <string name="foo_267">2670</string> + <string name="foo_270">2700</string> + <string name="foo_273">2730</string> + <string name="foo_276">2760</string> + <string name="foo_279">2790</string> + <string name="foo_282">2820</string> + <string name="foo_285">2850</string> + <string name="foo_288">2880</string> + <string name="foo_291">2910</string> + <string name="foo_294">2940</string> + <string name="foo_297">2970</string> + <string name="foo_300">3000</string> + <string name="foo_303">3030</string> + <string name="foo_306">3060</string> + <string name="foo_309">3090</string> + <string name="foo_312">3120</string> + <string name="foo_315">3150</string> + <string name="foo_318">3180</string> + <string name="foo_321">3210</string> + <string name="foo_324">3240</string> + <string name="foo_327">3270</string> + <string name="foo_330">3300</string> + <string name="foo_333">3330</string> + <string name="foo_336">3360</string> + <string name="foo_339">3390</string> + <string name="foo_342">3420</string> + <string name="foo_345">3450</string> + <string name="foo_348">3480</string> + <string name="foo_351">3510</string> + <string name="foo_354">3540</string> + <string name="foo_357">3570</string> + <string name="foo_360">3600</string> + <string name="foo_363">3630</string> + <string name="foo_366">3660</string> + <string name="foo_369">3690</string> + <string name="foo_372">3720</string> + <string name="foo_375">3750</string> + <string name="foo_378">3780</string> + <string name="foo_381">3810</string> + <string name="foo_384">3840</string> + <string name="foo_387">3870</string> + <string name="foo_390">3900</string> + <string name="foo_393">3930</string> + <string name="foo_396">3960</string> + <string name="foo_399">3990</string> + <string name="foo_402">4020</string> + <string name="foo_405">4050</string> + <string name="foo_408">4080</string> + <string name="foo_411">4110</string> + <string name="foo_414">4140</string> + <string name="foo_417">4170</string> + <string name="foo_420">4200</string> + <string name="foo_423">4230</string> + <string name="foo_426">4260</string> + <string name="foo_429">4290</string> + <string name="foo_432">4320</string> + <string name="foo_435">4350</string> + <string name="foo_438">4380</string> + <string name="foo_441">4410</string> + <string name="foo_444">4440</string> + <string name="foo_447">4470</string> + <string name="foo_450">4500</string> + <string name="foo_453">4530</string> + <string name="foo_456">4560</string> + <string name="foo_459">4590</string> + <string name="foo_462">4620</string> + <string name="foo_465">4650</string> + <string name="foo_468">4680</string> + <string name="foo_471">4710</string> + <string name="foo_474">4740</string> + <string name="foo_477">4770</string> + <string name="foo_480">4800</string> + <string name="foo_483">4830</string> + <string name="foo_486">4860</string> + <string name="foo_489">4890</string> + <string name="foo_492">4920</string> + <string name="foo_495">4950</string> + <string name="foo_498">4980</string> + <string name="foo_501">5010</string> + <string name="foo_504">5040</string> + <string name="foo_507">5070</string> + <string name="foo_510">5100</string> + <string name="foo_513">5130</string> + <string name="foo_516">5160</string> + <string name="foo_519">5190</string> + <string name="foo_522">5220</string> + <string name="foo_525">5250</string> + <string name="foo_528">5280</string> + <string name="foo_531">5310</string> + <string name="foo_534">5340</string> + <string name="foo_537">5370</string> + <string name="foo_540">5400</string> + <string name="foo_543">5430</string> + <string name="foo_546">5460</string> + <string name="foo_549">5490</string> + <string name="foo_552">5520</string> + <string name="foo_555">5550</string> + <string name="foo_558">5580</string> + <string name="foo_561">5610</string> + <string name="foo_564">5640</string> + <string name="foo_567">5670</string> + <string name="foo_570">5700</string> + <string name="foo_573">5730</string> + <string name="foo_576">5760</string> + <string name="foo_579">5790</string> + <string name="foo_582">5820</string> + <string name="foo_585">5850</string> + <string name="foo_588">5880</string> + <string name="foo_591">5910</string> + <string name="foo_594">5940</string> + <string name="foo_597">5970</string> + <string name="foo_600">6000</string> + <string name="foo_603">6030</string> + <string name="foo_606">6060</string> + <string name="foo_609">6090</string> + <string name="foo_612">6120</string> + <string name="foo_615">6150</string> + <string name="foo_618">6180</string> + <string name="foo_621">6210</string> + <string name="foo_624">6240</string> + <string name="foo_627">6270</string> + <string name="foo_630">6300</string> + <string name="foo_633">6330</string> + <string name="foo_636">6360</string> + <string name="foo_639">6390</string> + <string name="foo_642">6420</string> + <string name="foo_645">6450</string> + <string name="foo_648">6480</string> + <string name="foo_651">6510</string> + <string name="foo_654">6540</string> + <string name="foo_657">6570</string> + <string name="foo_660">6600</string> + <string name="foo_663">6630</string> + <string name="foo_666">6660</string> + <string name="foo_669">6690</string> + <string name="foo_672">6720</string> + <string name="foo_675">6750</string> + <string name="foo_678">6780</string> + <string name="foo_681">6810</string> + <string name="foo_684">6840</string> + <string name="foo_687">6870</string> + <string name="foo_690">6900</string> + <string name="foo_693">6930</string> + <string name="foo_696">6960</string> + <string name="foo_699">6990</string> + <string name="foo_702">7020</string> + <string name="foo_705">7050</string> + <string name="foo_708">7080</string> + <string name="foo_711">7110</string> + <string name="foo_714">7140</string> + <string name="foo_717">7170</string> + <string name="foo_720">7200</string> + <string name="foo_723">7230</string> + <string name="foo_726">7260</string> + <string name="foo_729">7290</string> + <string name="foo_732">7320</string> + <string name="foo_735">7350</string> + <string name="foo_738">7380</string> + <string name="foo_741">7410</string> + <string name="foo_744">7440</string> + <string name="foo_747">7470</string> + <string name="foo_750">7500</string> + <string name="foo_753">7530</string> + <string name="foo_756">7560</string> + <string name="foo_759">7590</string> + <string name="foo_762">7620</string> + <string name="foo_765">7650</string> + <string name="foo_768">7680</string> + <string name="foo_771">7710</string> + <string name="foo_774">7740</string> + <string name="foo_777">7770</string> + <string name="foo_780">7800</string> + <string name="foo_783">7830</string> + <string name="foo_786">7860</string> + <string name="foo_789">7890</string> + <string name="foo_792">7920</string> + <string name="foo_795">7950</string> + <string name="foo_798">7980</string> + <string name="foo_801">8010</string> + <string name="foo_804">8040</string> + <string name="foo_807">8070</string> + <string name="foo_810">8100</string> + <string name="foo_813">8130</string> + <string name="foo_816">8160</string> + <string name="foo_819">8190</string> + <string name="foo_822">8220</string> + <string name="foo_825">8250</string> + <string name="foo_828">8280</string> + <string name="foo_831">8310</string> + <string name="foo_834">8340</string> + <string name="foo_837">8370</string> + <string name="foo_840">8400</string> + <string name="foo_843">8430</string> + <string name="foo_846">8460</string> + <string name="foo_849">8490</string> + <string name="foo_852">8520</string> + <string name="foo_855">8550</string> + <string name="foo_858">8580</string> + <string name="foo_861">8610</string> + <string name="foo_864">8640</string> + <string name="foo_867">8670</string> + <string name="foo_870">8700</string> + <string name="foo_873">8730</string> + <string name="foo_876">8760</string> + <string name="foo_879">8790</string> + <string name="foo_882">8820</string> + <string name="foo_885">8850</string> + <string name="foo_888">8880</string> + <string name="foo_891">8910</string> + <string name="foo_894">8940</string> + <string name="foo_897">8970</string> + <string name="foo_900">9000</string> + <string name="foo_903">9030</string> + <string name="foo_906">9060</string> + <string name="foo_909">9090</string> + <string name="foo_912">9120</string> + <string name="foo_915">9150</string> + <string name="foo_918">9180</string> + <string name="foo_921">9210</string> + <string name="foo_924">9240</string> + <string name="foo_927">9270</string> + <string name="foo_930">9300</string> + <string name="foo_933">9330</string> + <string name="foo_936">9360</string> + <string name="foo_939">9390</string> + <string name="foo_942">9420</string> + <string name="foo_945">9450</string> + <string name="foo_948">9480</string> + <string name="foo_951">9510</string> + <string name="foo_954">9540</string> + <string name="foo_957">9570</string> + <string name="foo_960">9600</string> + <string name="foo_963">9630</string> + <string name="foo_966">9660</string> + <string name="foo_969">9690</string> + <string name="foo_972">9720</string> + <string name="foo_975">9750</string> + <string name="foo_978">9780</string> + <string name="foo_981">9810</string> + <string name="foo_984">9840</string> + <string name="foo_987">9870</string> + <string name="foo_990">9900</string> + <string name="foo_993">9930</string> + <string name="foo_996">9960</string> + <string name="foo_999">9990</string> +</resources> diff --git a/libs/androidfw/tests/data/sparse/res/values-v26/values.xml b/libs/androidfw/tests/data/sparse/res/values-v26/values.xml new file mode 100644 index 000000000000..b396ad24aa8c --- /dev/null +++ b/libs/androidfw/tests/data/sparse/res/values-v26/values.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<resources> + <integer name="foo_0">0</integer> + <integer name="foo_4">400</integer> + <integer name="foo_5">500</integer> + <integer name="foo_9">900</integer> +</resources> diff --git a/libs/androidfw/tests/data/sparse/res/values/strings.xml b/libs/androidfw/tests/data/sparse/res/values/strings.xml new file mode 100644 index 000000000000..6ff839a167a2 --- /dev/null +++ b/libs/androidfw/tests/data/sparse/res/values/strings.xml @@ -0,0 +1,1002 @@ +<resources> + <string name="foo_0">0</string> + <string name="foo_1">1</string> + <string name="foo_2">2</string> + <string name="foo_3">3</string> + <string name="foo_4">4</string> + <string name="foo_5">5</string> + <string name="foo_6">6</string> + <string name="foo_7">7</string> + <string name="foo_8">8</string> + <string name="foo_9">9</string> + <string name="foo_10">10</string> + <string name="foo_11">11</string> + <string name="foo_12">12</string> + <string name="foo_13">13</string> + <string name="foo_14">14</string> + <string name="foo_15">15</string> + <string name="foo_16">16</string> + <string name="foo_17">17</string> + <string name="foo_18">18</string> + <string name="foo_19">19</string> + <string name="foo_20">20</string> + <string name="foo_21">21</string> + <string name="foo_22">22</string> + <string name="foo_23">23</string> + <string name="foo_24">24</string> + <string name="foo_25">25</string> + <string name="foo_26">26</string> + <string name="foo_27">27</string> + <string name="foo_28">28</string> + <string name="foo_29">29</string> + <string name="foo_30">30</string> + <string name="foo_31">31</string> + <string name="foo_32">32</string> + <string name="foo_33">33</string> + <string name="foo_34">34</string> + <string name="foo_35">35</string> + <string name="foo_36">36</string> + <string name="foo_37">37</string> + <string name="foo_38">38</string> + <string name="foo_39">39</string> + <string name="foo_40">40</string> + <string name="foo_41">41</string> + <string name="foo_42">42</string> + <string name="foo_43">43</string> + <string name="foo_44">44</string> + <string name="foo_45">45</string> + <string name="foo_46">46</string> + <string name="foo_47">47</string> + <string name="foo_48">48</string> + <string name="foo_49">49</string> + <string name="foo_50">50</string> + <string name="foo_51">51</string> + <string name="foo_52">52</string> + <string name="foo_53">53</string> + <string name="foo_54">54</string> + <string name="foo_55">55</string> + <string name="foo_56">56</string> + <string name="foo_57">57</string> + <string name="foo_58">58</string> + <string name="foo_59">59</string> + <string name="foo_60">60</string> + <string name="foo_61">61</string> + <string name="foo_62">62</string> + <string name="foo_63">63</string> + <string name="foo_64">64</string> + <string name="foo_65">65</string> + <string name="foo_66">66</string> + <string name="foo_67">67</string> + <string name="foo_68">68</string> + <string name="foo_69">69</string> + <string name="foo_70">70</string> + <string name="foo_71">71</string> + <string name="foo_72">72</string> + <string name="foo_73">73</string> + <string name="foo_74">74</string> + <string name="foo_75">75</string> + <string name="foo_76">76</string> + <string name="foo_77">77</string> + <string name="foo_78">78</string> + <string name="foo_79">79</string> + <string name="foo_80">80</string> + <string name="foo_81">81</string> + <string name="foo_82">82</string> + <string name="foo_83">83</string> + <string name="foo_84">84</string> + <string name="foo_85">85</string> + <string name="foo_86">86</string> + <string name="foo_87">87</string> + <string name="foo_88">88</string> + <string name="foo_89">89</string> + <string name="foo_90">90</string> + <string name="foo_91">91</string> + <string name="foo_92">92</string> + <string name="foo_93">93</string> + <string name="foo_94">94</string> + <string name="foo_95">95</string> + <string name="foo_96">96</string> + <string name="foo_97">97</string> + <string name="foo_98">98</string> + <string name="foo_99">99</string> + <string name="foo_100">100</string> + <string name="foo_101">101</string> + <string name="foo_102">102</string> + <string name="foo_103">103</string> + <string name="foo_104">104</string> + <string name="foo_105">105</string> + <string name="foo_106">106</string> + <string name="foo_107">107</string> + <string name="foo_108">108</string> + <string name="foo_109">109</string> + <string name="foo_110">110</string> + <string name="foo_111">111</string> + <string name="foo_112">112</string> + <string name="foo_113">113</string> + <string name="foo_114">114</string> + <string name="foo_115">115</string> + <string name="foo_116">116</string> + <string name="foo_117">117</string> + <string name="foo_118">118</string> + <string name="foo_119">119</string> + <string name="foo_120">120</string> + <string name="foo_121">121</string> + <string name="foo_122">122</string> + <string name="foo_123">123</string> + <string name="foo_124">124</string> + <string name="foo_125">125</string> + <string name="foo_126">126</string> + <string name="foo_127">127</string> + <string name="foo_128">128</string> + <string name="foo_129">129</string> + <string name="foo_130">130</string> + <string name="foo_131">131</string> + <string name="foo_132">132</string> + <string name="foo_133">133</string> + <string name="foo_134">134</string> + <string name="foo_135">135</string> + <string name="foo_136">136</string> + <string name="foo_137">137</string> + <string name="foo_138">138</string> + <string name="foo_139">139</string> + <string name="foo_140">140</string> + <string name="foo_141">141</string> + <string name="foo_142">142</string> + <string name="foo_143">143</string> + <string name="foo_144">144</string> + <string name="foo_145">145</string> + <string name="foo_146">146</string> + <string name="foo_147">147</string> + <string name="foo_148">148</string> + <string name="foo_149">149</string> + <string name="foo_150">150</string> + <string name="foo_151">151</string> + <string name="foo_152">152</string> + <string name="foo_153">153</string> + <string name="foo_154">154</string> + <string name="foo_155">155</string> + <string name="foo_156">156</string> + <string name="foo_157">157</string> + <string name="foo_158">158</string> + <string name="foo_159">159</string> + <string name="foo_160">160</string> + <string name="foo_161">161</string> + <string name="foo_162">162</string> + <string name="foo_163">163</string> + <string name="foo_164">164</string> + <string name="foo_165">165</string> + <string name="foo_166">166</string> + <string name="foo_167">167</string> + <string name="foo_168">168</string> + <string name="foo_169">169</string> + <string name="foo_170">170</string> + <string name="foo_171">171</string> + <string name="foo_172">172</string> + <string name="foo_173">173</string> + <string name="foo_174">174</string> + <string name="foo_175">175</string> + <string name="foo_176">176</string> + <string name="foo_177">177</string> + <string name="foo_178">178</string> + <string name="foo_179">179</string> + <string name="foo_180">180</string> + <string name="foo_181">181</string> + <string name="foo_182">182</string> + <string name="foo_183">183</string> + <string name="foo_184">184</string> + <string name="foo_185">185</string> + <string name="foo_186">186</string> + <string name="foo_187">187</string> + <string name="foo_188">188</string> + <string name="foo_189">189</string> + <string name="foo_190">190</string> + <string name="foo_191">191</string> + <string name="foo_192">192</string> + <string name="foo_193">193</string> + <string name="foo_194">194</string> + <string name="foo_195">195</string> + <string name="foo_196">196</string> + <string name="foo_197">197</string> + <string name="foo_198">198</string> + <string name="foo_199">199</string> + <string name="foo_200">200</string> + <string name="foo_201">201</string> + <string name="foo_202">202</string> + <string name="foo_203">203</string> + <string name="foo_204">204</string> + <string name="foo_205">205</string> + <string name="foo_206">206</string> + <string name="foo_207">207</string> + <string name="foo_208">208</string> + <string name="foo_209">209</string> + <string name="foo_210">210</string> + <string name="foo_211">211</string> + <string name="foo_212">212</string> + <string name="foo_213">213</string> + <string name="foo_214">214</string> + <string name="foo_215">215</string> + <string name="foo_216">216</string> + <string name="foo_217">217</string> + <string name="foo_218">218</string> + <string name="foo_219">219</string> + <string name="foo_220">220</string> + <string name="foo_221">221</string> + <string name="foo_222">222</string> + <string name="foo_223">223</string> + <string name="foo_224">224</string> + <string name="foo_225">225</string> + <string name="foo_226">226</string> + <string name="foo_227">227</string> + <string name="foo_228">228</string> + <string name="foo_229">229</string> + <string name="foo_230">230</string> + <string name="foo_231">231</string> + <string name="foo_232">232</string> + <string name="foo_233">233</string> + <string name="foo_234">234</string> + <string name="foo_235">235</string> + <string name="foo_236">236</string> + <string name="foo_237">237</string> + <string name="foo_238">238</string> + <string name="foo_239">239</string> + <string name="foo_240">240</string> + <string name="foo_241">241</string> + <string name="foo_242">242</string> + <string name="foo_243">243</string> + <string name="foo_244">244</string> + <string name="foo_245">245</string> + <string name="foo_246">246</string> + <string name="foo_247">247</string> + <string name="foo_248">248</string> + <string name="foo_249">249</string> + <string name="foo_250">250</string> + <string name="foo_251">251</string> + <string name="foo_252">252</string> + <string name="foo_253">253</string> + <string name="foo_254">254</string> + <string name="foo_255">255</string> + <string name="foo_256">256</string> + <string name="foo_257">257</string> + <string name="foo_258">258</string> + <string name="foo_259">259</string> + <string name="foo_260">260</string> + <string name="foo_261">261</string> + <string name="foo_262">262</string> + <string name="foo_263">263</string> + <string name="foo_264">264</string> + <string name="foo_265">265</string> + <string name="foo_266">266</string> + <string name="foo_267">267</string> + <string name="foo_268">268</string> + <string name="foo_269">269</string> + <string name="foo_270">270</string> + <string name="foo_271">271</string> + <string name="foo_272">272</string> + <string name="foo_273">273</string> + <string name="foo_274">274</string> + <string name="foo_275">275</string> + <string name="foo_276">276</string> + <string name="foo_277">277</string> + <string name="foo_278">278</string> + <string name="foo_279">279</string> + <string name="foo_280">280</string> + <string name="foo_281">281</string> + <string name="foo_282">282</string> + <string name="foo_283">283</string> + <string name="foo_284">284</string> + <string name="foo_285">285</string> + <string name="foo_286">286</string> + <string name="foo_287">287</string> + <string name="foo_288">288</string> + <string name="foo_289">289</string> + <string name="foo_290">290</string> + <string name="foo_291">291</string> + <string name="foo_292">292</string> + <string name="foo_293">293</string> + <string name="foo_294">294</string> + <string name="foo_295">295</string> + <string name="foo_296">296</string> + <string name="foo_297">297</string> + <string name="foo_298">298</string> + <string name="foo_299">299</string> + <string name="foo_300">300</string> + <string name="foo_301">301</string> + <string name="foo_302">302</string> + <string name="foo_303">303</string> + <string name="foo_304">304</string> + <string name="foo_305">305</string> + <string name="foo_306">306</string> + <string name="foo_307">307</string> + <string name="foo_308">308</string> + <string name="foo_309">309</string> + <string name="foo_310">310</string> + <string name="foo_311">311</string> + <string name="foo_312">312</string> + <string name="foo_313">313</string> + <string name="foo_314">314</string> + <string name="foo_315">315</string> + <string name="foo_316">316</string> + <string name="foo_317">317</string> + <string name="foo_318">318</string> + <string name="foo_319">319</string> + <string name="foo_320">320</string> + <string name="foo_321">321</string> + <string name="foo_322">322</string> + <string name="foo_323">323</string> + <string name="foo_324">324</string> + <string name="foo_325">325</string> + <string name="foo_326">326</string> + <string name="foo_327">327</string> + <string name="foo_328">328</string> + <string name="foo_329">329</string> + <string name="foo_330">330</string> + <string name="foo_331">331</string> + <string name="foo_332">332</string> + <string name="foo_333">333</string> + <string name="foo_334">334</string> + <string name="foo_335">335</string> + <string name="foo_336">336</string> + <string name="foo_337">337</string> + <string name="foo_338">338</string> + <string name="foo_339">339</string> + <string name="foo_340">340</string> + <string name="foo_341">341</string> + <string name="foo_342">342</string> + <string name="foo_343">343</string> + <string name="foo_344">344</string> + <string name="foo_345">345</string> + <string name="foo_346">346</string> + <string name="foo_347">347</string> + <string name="foo_348">348</string> + <string name="foo_349">349</string> + <string name="foo_350">350</string> + <string name="foo_351">351</string> + <string name="foo_352">352</string> + <string name="foo_353">353</string> + <string name="foo_354">354</string> + <string name="foo_355">355</string> + <string name="foo_356">356</string> + <string name="foo_357">357</string> + <string name="foo_358">358</string> + <string name="foo_359">359</string> + <string name="foo_360">360</string> + <string name="foo_361">361</string> + <string name="foo_362">362</string> + <string name="foo_363">363</string> + <string name="foo_364">364</string> + <string name="foo_365">365</string> + <string name="foo_366">366</string> + <string name="foo_367">367</string> + <string name="foo_368">368</string> + <string name="foo_369">369</string> + <string name="foo_370">370</string> + <string name="foo_371">371</string> + <string name="foo_372">372</string> + <string name="foo_373">373</string> + <string name="foo_374">374</string> + <string name="foo_375">375</string> + <string name="foo_376">376</string> + <string name="foo_377">377</string> + <string name="foo_378">378</string> + <string name="foo_379">379</string> + <string name="foo_380">380</string> + <string name="foo_381">381</string> + <string name="foo_382">382</string> + <string name="foo_383">383</string> + <string name="foo_384">384</string> + <string name="foo_385">385</string> + <string name="foo_386">386</string> + <string name="foo_387">387</string> + <string name="foo_388">388</string> + <string name="foo_389">389</string> + <string name="foo_390">390</string> + <string name="foo_391">391</string> + <string name="foo_392">392</string> + <string name="foo_393">393</string> + <string name="foo_394">394</string> + <string name="foo_395">395</string> + <string name="foo_396">396</string> + <string name="foo_397">397</string> + <string name="foo_398">398</string> + <string name="foo_399">399</string> + <string name="foo_400">400</string> + <string name="foo_401">401</string> + <string name="foo_402">402</string> + <string name="foo_403">403</string> + <string name="foo_404">404</string> + <string name="foo_405">405</string> + <string name="foo_406">406</string> + <string name="foo_407">407</string> + <string name="foo_408">408</string> + <string name="foo_409">409</string> + <string name="foo_410">410</string> + <string name="foo_411">411</string> + <string name="foo_412">412</string> + <string name="foo_413">413</string> + <string name="foo_414">414</string> + <string name="foo_415">415</string> + <string name="foo_416">416</string> + <string name="foo_417">417</string> + <string name="foo_418">418</string> + <string name="foo_419">419</string> + <string name="foo_420">420</string> + <string name="foo_421">421</string> + <string name="foo_422">422</string> + <string name="foo_423">423</string> + <string name="foo_424">424</string> + <string name="foo_425">425</string> + <string name="foo_426">426</string> + <string name="foo_427">427</string> + <string name="foo_428">428</string> + <string name="foo_429">429</string> + <string name="foo_430">430</string> + <string name="foo_431">431</string> + <string name="foo_432">432</string> + <string name="foo_433">433</string> + <string name="foo_434">434</string> + <string name="foo_435">435</string> + <string name="foo_436">436</string> + <string name="foo_437">437</string> + <string name="foo_438">438</string> + <string name="foo_439">439</string> + <string name="foo_440">440</string> + <string name="foo_441">441</string> + <string name="foo_442">442</string> + <string name="foo_443">443</string> + <string name="foo_444">444</string> + <string name="foo_445">445</string> + <string name="foo_446">446</string> + <string name="foo_447">447</string> + <string name="foo_448">448</string> + <string name="foo_449">449</string> + <string name="foo_450">450</string> + <string name="foo_451">451</string> + <string name="foo_452">452</string> + <string name="foo_453">453</string> + <string name="foo_454">454</string> + <string name="foo_455">455</string> + <string name="foo_456">456</string> + <string name="foo_457">457</string> + <string name="foo_458">458</string> + <string name="foo_459">459</string> + <string name="foo_460">460</string> + <string name="foo_461">461</string> + <string name="foo_462">462</string> + <string name="foo_463">463</string> + <string name="foo_464">464</string> + <string name="foo_465">465</string> + <string name="foo_466">466</string> + <string name="foo_467">467</string> + <string name="foo_468">468</string> + <string name="foo_469">469</string> + <string name="foo_470">470</string> + <string name="foo_471">471</string> + <string name="foo_472">472</string> + <string name="foo_473">473</string> + <string name="foo_474">474</string> + <string name="foo_475">475</string> + <string name="foo_476">476</string> + <string name="foo_477">477</string> + <string name="foo_478">478</string> + <string name="foo_479">479</string> + <string name="foo_480">480</string> + <string name="foo_481">481</string> + <string name="foo_482">482</string> + <string name="foo_483">483</string> + <string name="foo_484">484</string> + <string name="foo_485">485</string> + <string name="foo_486">486</string> + <string name="foo_487">487</string> + <string name="foo_488">488</string> + <string name="foo_489">489</string> + <string name="foo_490">490</string> + <string name="foo_491">491</string> + <string name="foo_492">492</string> + <string name="foo_493">493</string> + <string name="foo_494">494</string> + <string name="foo_495">495</string> + <string name="foo_496">496</string> + <string name="foo_497">497</string> + <string name="foo_498">498</string> + <string name="foo_499">499</string> + <string name="foo_500">500</string> + <string name="foo_501">501</string> + <string name="foo_502">502</string> + <string name="foo_503">503</string> + <string name="foo_504">504</string> + <string name="foo_505">505</string> + <string name="foo_506">506</string> + <string name="foo_507">507</string> + <string name="foo_508">508</string> + <string name="foo_509">509</string> + <string name="foo_510">510</string> + <string name="foo_511">511</string> + <string name="foo_512">512</string> + <string name="foo_513">513</string> + <string name="foo_514">514</string> + <string name="foo_515">515</string> + <string name="foo_516">516</string> + <string name="foo_517">517</string> + <string name="foo_518">518</string> + <string name="foo_519">519</string> + <string name="foo_520">520</string> + <string name="foo_521">521</string> + <string name="foo_522">522</string> + <string name="foo_523">523</string> + <string name="foo_524">524</string> + <string name="foo_525">525</string> + <string name="foo_526">526</string> + <string name="foo_527">527</string> + <string name="foo_528">528</string> + <string name="foo_529">529</string> + <string name="foo_530">530</string> + <string name="foo_531">531</string> + <string name="foo_532">532</string> + <string name="foo_533">533</string> + <string name="foo_534">534</string> + <string name="foo_535">535</string> + <string name="foo_536">536</string> + <string name="foo_537">537</string> + <string name="foo_538">538</string> + <string name="foo_539">539</string> + <string name="foo_540">540</string> + <string name="foo_541">541</string> + <string name="foo_542">542</string> + <string name="foo_543">543</string> + <string name="foo_544">544</string> + <string name="foo_545">545</string> + <string name="foo_546">546</string> + <string name="foo_547">547</string> + <string name="foo_548">548</string> + <string name="foo_549">549</string> + <string name="foo_550">550</string> + <string name="foo_551">551</string> + <string name="foo_552">552</string> + <string name="foo_553">553</string> + <string name="foo_554">554</string> + <string name="foo_555">555</string> + <string name="foo_556">556</string> + <string name="foo_557">557</string> + <string name="foo_558">558</string> + <string name="foo_559">559</string> + <string name="foo_560">560</string> + <string name="foo_561">561</string> + <string name="foo_562">562</string> + <string name="foo_563">563</string> + <string name="foo_564">564</string> + <string name="foo_565">565</string> + <string name="foo_566">566</string> + <string name="foo_567">567</string> + <string name="foo_568">568</string> + <string name="foo_569">569</string> + <string name="foo_570">570</string> + <string name="foo_571">571</string> + <string name="foo_572">572</string> + <string name="foo_573">573</string> + <string name="foo_574">574</string> + <string name="foo_575">575</string> + <string name="foo_576">576</string> + <string name="foo_577">577</string> + <string name="foo_578">578</string> + <string name="foo_579">579</string> + <string name="foo_580">580</string> + <string name="foo_581">581</string> + <string name="foo_582">582</string> + <string name="foo_583">583</string> + <string name="foo_584">584</string> + <string name="foo_585">585</string> + <string name="foo_586">586</string> + <string name="foo_587">587</string> + <string name="foo_588">588</string> + <string name="foo_589">589</string> + <string name="foo_590">590</string> + <string name="foo_591">591</string> + <string name="foo_592">592</string> + <string name="foo_593">593</string> + <string name="foo_594">594</string> + <string name="foo_595">595</string> + <string name="foo_596">596</string> + <string name="foo_597">597</string> + <string name="foo_598">598</string> + <string name="foo_599">599</string> + <string name="foo_600">600</string> + <string name="foo_601">601</string> + <string name="foo_602">602</string> + <string name="foo_603">603</string> + <string name="foo_604">604</string> + <string name="foo_605">605</string> + <string name="foo_606">606</string> + <string name="foo_607">607</string> + <string name="foo_608">608</string> + <string name="foo_609">609</string> + <string name="foo_610">610</string> + <string name="foo_611">611</string> + <string name="foo_612">612</string> + <string name="foo_613">613</string> + <string name="foo_614">614</string> + <string name="foo_615">615</string> + <string name="foo_616">616</string> + <string name="foo_617">617</string> + <string name="foo_618">618</string> + <string name="foo_619">619</string> + <string name="foo_620">620</string> + <string name="foo_621">621</string> + <string name="foo_622">622</string> + <string name="foo_623">623</string> + <string name="foo_624">624</string> + <string name="foo_625">625</string> + <string name="foo_626">626</string> + <string name="foo_627">627</string> + <string name="foo_628">628</string> + <string name="foo_629">629</string> + <string name="foo_630">630</string> + <string name="foo_631">631</string> + <string name="foo_632">632</string> + <string name="foo_633">633</string> + <string name="foo_634">634</string> + <string name="foo_635">635</string> + <string name="foo_636">636</string> + <string name="foo_637">637</string> + <string name="foo_638">638</string> + <string name="foo_639">639</string> + <string name="foo_640">640</string> + <string name="foo_641">641</string> + <string name="foo_642">642</string> + <string name="foo_643">643</string> + <string name="foo_644">644</string> + <string name="foo_645">645</string> + <string name="foo_646">646</string> + <string name="foo_647">647</string> + <string name="foo_648">648</string> + <string name="foo_649">649</string> + <string name="foo_650">650</string> + <string name="foo_651">651</string> + <string name="foo_652">652</string> + <string name="foo_653">653</string> + <string name="foo_654">654</string> + <string name="foo_655">655</string> + <string name="foo_656">656</string> + <string name="foo_657">657</string> + <string name="foo_658">658</string> + <string name="foo_659">659</string> + <string name="foo_660">660</string> + <string name="foo_661">661</string> + <string name="foo_662">662</string> + <string name="foo_663">663</string> + <string name="foo_664">664</string> + <string name="foo_665">665</string> + <string name="foo_666">666</string> + <string name="foo_667">667</string> + <string name="foo_668">668</string> + <string name="foo_669">669</string> + <string name="foo_670">670</string> + <string name="foo_671">671</string> + <string name="foo_672">672</string> + <string name="foo_673">673</string> + <string name="foo_674">674</string> + <string name="foo_675">675</string> + <string name="foo_676">676</string> + <string name="foo_677">677</string> + <string name="foo_678">678</string> + <string name="foo_679">679</string> + <string name="foo_680">680</string> + <string name="foo_681">681</string> + <string name="foo_682">682</string> + <string name="foo_683">683</string> + <string name="foo_684">684</string> + <string name="foo_685">685</string> + <string name="foo_686">686</string> + <string name="foo_687">687</string> + <string name="foo_688">688</string> + <string name="foo_689">689</string> + <string name="foo_690">690</string> + <string name="foo_691">691</string> + <string name="foo_692">692</string> + <string name="foo_693">693</string> + <string name="foo_694">694</string> + <string name="foo_695">695</string> + <string name="foo_696">696</string> + <string name="foo_697">697</string> + <string name="foo_698">698</string> + <string name="foo_699">699</string> + <string name="foo_700">700</string> + <string name="foo_701">701</string> + <string name="foo_702">702</string> + <string name="foo_703">703</string> + <string name="foo_704">704</string> + <string name="foo_705">705</string> + <string name="foo_706">706</string> + <string name="foo_707">707</string> + <string name="foo_708">708</string> + <string name="foo_709">709</string> + <string name="foo_710">710</string> + <string name="foo_711">711</string> + <string name="foo_712">712</string> + <string name="foo_713">713</string> + <string name="foo_714">714</string> + <string name="foo_715">715</string> + <string name="foo_716">716</string> + <string name="foo_717">717</string> + <string name="foo_718">718</string> + <string name="foo_719">719</string> + <string name="foo_720">720</string> + <string name="foo_721">721</string> + <string name="foo_722">722</string> + <string name="foo_723">723</string> + <string name="foo_724">724</string> + <string name="foo_725">725</string> + <string name="foo_726">726</string> + <string name="foo_727">727</string> + <string name="foo_728">728</string> + <string name="foo_729">729</string> + <string name="foo_730">730</string> + <string name="foo_731">731</string> + <string name="foo_732">732</string> + <string name="foo_733">733</string> + <string name="foo_734">734</string> + <string name="foo_735">735</string> + <string name="foo_736">736</string> + <string name="foo_737">737</string> + <string name="foo_738">738</string> + <string name="foo_739">739</string> + <string name="foo_740">740</string> + <string name="foo_741">741</string> + <string name="foo_742">742</string> + <string name="foo_743">743</string> + <string name="foo_744">744</string> + <string name="foo_745">745</string> + <string name="foo_746">746</string> + <string name="foo_747">747</string> + <string name="foo_748">748</string> + <string name="foo_749">749</string> + <string name="foo_750">750</string> + <string name="foo_751">751</string> + <string name="foo_752">752</string> + <string name="foo_753">753</string> + <string name="foo_754">754</string> + <string name="foo_755">755</string> + <string name="foo_756">756</string> + <string name="foo_757">757</string> + <string name="foo_758">758</string> + <string name="foo_759">759</string> + <string name="foo_760">760</string> + <string name="foo_761">761</string> + <string name="foo_762">762</string> + <string name="foo_763">763</string> + <string name="foo_764">764</string> + <string name="foo_765">765</string> + <string name="foo_766">766</string> + <string name="foo_767">767</string> + <string name="foo_768">768</string> + <string name="foo_769">769</string> + <string name="foo_770">770</string> + <string name="foo_771">771</string> + <string name="foo_772">772</string> + <string name="foo_773">773</string> + <string name="foo_774">774</string> + <string name="foo_775">775</string> + <string name="foo_776">776</string> + <string name="foo_777">777</string> + <string name="foo_778">778</string> + <string name="foo_779">779</string> + <string name="foo_780">780</string> + <string name="foo_781">781</string> + <string name="foo_782">782</string> + <string name="foo_783">783</string> + <string name="foo_784">784</string> + <string name="foo_785">785</string> + <string name="foo_786">786</string> + <string name="foo_787">787</string> + <string name="foo_788">788</string> + <string name="foo_789">789</string> + <string name="foo_790">790</string> + <string name="foo_791">791</string> + <string name="foo_792">792</string> + <string name="foo_793">793</string> + <string name="foo_794">794</string> + <string name="foo_795">795</string> + <string name="foo_796">796</string> + <string name="foo_797">797</string> + <string name="foo_798">798</string> + <string name="foo_799">799</string> + <string name="foo_800">800</string> + <string name="foo_801">801</string> + <string name="foo_802">802</string> + <string name="foo_803">803</string> + <string name="foo_804">804</string> + <string name="foo_805">805</string> + <string name="foo_806">806</string> + <string name="foo_807">807</string> + <string name="foo_808">808</string> + <string name="foo_809">809</string> + <string name="foo_810">810</string> + <string name="foo_811">811</string> + <string name="foo_812">812</string> + <string name="foo_813">813</string> + <string name="foo_814">814</string> + <string name="foo_815">815</string> + <string name="foo_816">816</string> + <string name="foo_817">817</string> + <string name="foo_818">818</string> + <string name="foo_819">819</string> + <string name="foo_820">820</string> + <string name="foo_821">821</string> + <string name="foo_822">822</string> + <string name="foo_823">823</string> + <string name="foo_824">824</string> + <string name="foo_825">825</string> + <string name="foo_826">826</string> + <string name="foo_827">827</string> + <string name="foo_828">828</string> + <string name="foo_829">829</string> + <string name="foo_830">830</string> + <string name="foo_831">831</string> + <string name="foo_832">832</string> + <string name="foo_833">833</string> + <string name="foo_834">834</string> + <string name="foo_835">835</string> + <string name="foo_836">836</string> + <string name="foo_837">837</string> + <string name="foo_838">838</string> + <string name="foo_839">839</string> + <string name="foo_840">840</string> + <string name="foo_841">841</string> + <string name="foo_842">842</string> + <string name="foo_843">843</string> + <string name="foo_844">844</string> + <string name="foo_845">845</string> + <string name="foo_846">846</string> + <string name="foo_847">847</string> + <string name="foo_848">848</string> + <string name="foo_849">849</string> + <string name="foo_850">850</string> + <string name="foo_851">851</string> + <string name="foo_852">852</string> + <string name="foo_853">853</string> + <string name="foo_854">854</string> + <string name="foo_855">855</string> + <string name="foo_856">856</string> + <string name="foo_857">857</string> + <string name="foo_858">858</string> + <string name="foo_859">859</string> + <string name="foo_860">860</string> + <string name="foo_861">861</string> + <string name="foo_862">862</string> + <string name="foo_863">863</string> + <string name="foo_864">864</string> + <string name="foo_865">865</string> + <string name="foo_866">866</string> + <string name="foo_867">867</string> + <string name="foo_868">868</string> + <string name="foo_869">869</string> + <string name="foo_870">870</string> + <string name="foo_871">871</string> + <string name="foo_872">872</string> + <string name="foo_873">873</string> + <string name="foo_874">874</string> + <string name="foo_875">875</string> + <string name="foo_876">876</string> + <string name="foo_877">877</string> + <string name="foo_878">878</string> + <string name="foo_879">879</string> + <string name="foo_880">880</string> + <string name="foo_881">881</string> + <string name="foo_882">882</string> + <string name="foo_883">883</string> + <string name="foo_884">884</string> + <string name="foo_885">885</string> + <string name="foo_886">886</string> + <string name="foo_887">887</string> + <string name="foo_888">888</string> + <string name="foo_889">889</string> + <string name="foo_890">890</string> + <string name="foo_891">891</string> + <string name="foo_892">892</string> + <string name="foo_893">893</string> + <string name="foo_894">894</string> + <string name="foo_895">895</string> + <string name="foo_896">896</string> + <string name="foo_897">897</string> + <string name="foo_898">898</string> + <string name="foo_899">899</string> + <string name="foo_900">900</string> + <string name="foo_901">901</string> + <string name="foo_902">902</string> + <string name="foo_903">903</string> + <string name="foo_904">904</string> + <string name="foo_905">905</string> + <string name="foo_906">906</string> + <string name="foo_907">907</string> + <string name="foo_908">908</string> + <string name="foo_909">909</string> + <string name="foo_910">910</string> + <string name="foo_911">911</string> + <string name="foo_912">912</string> + <string name="foo_913">913</string> + <string name="foo_914">914</string> + <string name="foo_915">915</string> + <string name="foo_916">916</string> + <string name="foo_917">917</string> + <string name="foo_918">918</string> + <string name="foo_919">919</string> + <string name="foo_920">920</string> + <string name="foo_921">921</string> + <string name="foo_922">922</string> + <string name="foo_923">923</string> + <string name="foo_924">924</string> + <string name="foo_925">925</string> + <string name="foo_926">926</string> + <string name="foo_927">927</string> + <string name="foo_928">928</string> + <string name="foo_929">929</string> + <string name="foo_930">930</string> + <string name="foo_931">931</string> + <string name="foo_932">932</string> + <string name="foo_933">933</string> + <string name="foo_934">934</string> + <string name="foo_935">935</string> + <string name="foo_936">936</string> + <string name="foo_937">937</string> + <string name="foo_938">938</string> + <string name="foo_939">939</string> + <string name="foo_940">940</string> + <string name="foo_941">941</string> + <string name="foo_942">942</string> + <string name="foo_943">943</string> + <string name="foo_944">944</string> + <string name="foo_945">945</string> + <string name="foo_946">946</string> + <string name="foo_947">947</string> + <string name="foo_948">948</string> + <string name="foo_949">949</string> + <string name="foo_950">950</string> + <string name="foo_951">951</string> + <string name="foo_952">952</string> + <string name="foo_953">953</string> + <string name="foo_954">954</string> + <string name="foo_955">955</string> + <string name="foo_956">956</string> + <string name="foo_957">957</string> + <string name="foo_958">958</string> + <string name="foo_959">959</string> + <string name="foo_960">960</string> + <string name="foo_961">961</string> + <string name="foo_962">962</string> + <string name="foo_963">963</string> + <string name="foo_964">964</string> + <string name="foo_965">965</string> + <string name="foo_966">966</string> + <string name="foo_967">967</string> + <string name="foo_968">968</string> + <string name="foo_969">969</string> + <string name="foo_970">970</string> + <string name="foo_971">971</string> + <string name="foo_972">972</string> + <string name="foo_973">973</string> + <string name="foo_974">974</string> + <string name="foo_975">975</string> + <string name="foo_976">976</string> + <string name="foo_977">977</string> + <string name="foo_978">978</string> + <string name="foo_979">979</string> + <string name="foo_980">980</string> + <string name="foo_981">981</string> + <string name="foo_982">982</string> + <string name="foo_983">983</string> + <string name="foo_984">984</string> + <string name="foo_985">985</string> + <string name="foo_986">986</string> + <string name="foo_987">987</string> + <string name="foo_988">988</string> + <string name="foo_989">989</string> + <string name="foo_990">990</string> + <string name="foo_991">991</string> + <string name="foo_992">992</string> + <string name="foo_993">993</string> + <string name="foo_994">994</string> + <string name="foo_995">995</string> + <string name="foo_996">996</string> + <string name="foo_997">997</string> + <string name="foo_998">998</string> + <string name="foo_999">999</string> +</resources> diff --git a/libs/androidfw/tests/data/sparse/res/values/values.xml b/libs/androidfw/tests/data/sparse/res/values/values.xml new file mode 100644 index 000000000000..5cc42d8f8345 --- /dev/null +++ b/libs/androidfw/tests/data/sparse/res/values/values.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<resources> + <integer name="foo_0">0</integer> + <integer name="foo_1">1</integer> + <integer name="foo_2">2</integer> + <integer name="foo_3">3</integer> + <integer name="foo_4">4</integer> + <integer name="foo_5">5</integer> + <integer name="foo_6">6</integer> + <integer name="foo_7">7</integer> + <integer name="foo_8">8</integer> + <integer name="foo_9">9</integer> +</resources> diff --git a/libs/androidfw/tests/data/sparse/sparse.apk b/libs/androidfw/tests/data/sparse/sparse.apk Binary files differnew file mode 100644 index 000000000000..1f9bba31b0a1 --- /dev/null +++ b/libs/androidfw/tests/data/sparse/sparse.apk diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp index 3d7bd9479ad8..407550b112dc 100644 --- a/tools/aapt2/LoadedApk.cpp +++ b/tools/aapt2/LoadedApk.cpp @@ -94,7 +94,9 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, IArchiveWriter* writer) { // The resource table needs to be reserialized since it might have changed. if (path == "resources.arsc") { BigBuffer buffer = BigBuffer(1024); - TableFlattener flattener(&buffer); + // TODO(adamlesinski): How to determine if there were sparse entries (and if to encode + // with sparse entries) b/35389232. + TableFlattener flattener({}, &buffer); if (!flattener.Consume(context, table_.get())) { return false; } diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h index 98ba94badfed..c2ee2524c5e8 100644 --- a/tools/aapt2/SdkConstants.h +++ b/tools/aapt2/SdkConstants.h @@ -25,7 +25,7 @@ namespace aapt { -enum { +enum : int { SDK_CUPCAKE = 3, SDK_DONUT = 4, SDK_ECLAIR = 5, @@ -49,7 +49,7 @@ enum { SDK_MARSHMALLOW = 23, SDK_NOUGAT = 24, SDK_NOUGAT_MR1 = 25, - SDK_O = 26, // STOPSHIP Replace with real version + SDK_O = 26, // STOPSHIP Replace with real version }; size_t FindAttributeSdkLevel(const ResourceId& id); diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp index 19d030ec4a25..697b07fa414d 100644 --- a/tools/aapt2/flatten/TableFlattener.cpp +++ b/tools/aapt2/flatten/TableFlattener.cpp @@ -26,6 +26,7 @@ #include "ResourceTable.h" #include "ResourceValues.h" +#include "SdkConstants.h" #include "ValueVisitor.h" #include "flatten/ChunkWriter.h" #include "flatten/ResourceTypeExtensions.h" @@ -216,8 +217,11 @@ class MapFlattenVisitor : public RawValueVisitor { class PackageFlattener { public: - PackageFlattener(IDiagnostics* diag, ResourceTablePackage* package) - : diag_(diag), package_(package) {} + PackageFlattener(IAaptContext* context, ResourceTablePackage* package, bool use_sparse_entries) + : context_(context), + diag_(context->GetDiagnostics()), + package_(package), + use_sparse_entries_(use_sparse_entries) {} bool FlattenPackage(BigBuffer* buffer) { ChunkWriter pkg_writer(buffer); @@ -298,9 +302,12 @@ class PackageFlattener { return true; } - bool FlattenConfig(const ResourceTableType* type, - const ConfigDescription& config, - std::vector<FlatEntry>* entries, BigBuffer* buffer) { + bool FlattenConfig(const ResourceTableType* type, const ConfigDescription& config, + const size_t num_total_entries, std::vector<FlatEntry>* entries, + BigBuffer* buffer) { + CHECK(num_total_entries != 0); + CHECK(num_total_entries <= std::numeric_limits<uint16_t>::max()); + ChunkWriter type_writer(buffer); ResTable_type* type_header = type_writer.StartChunk<ResTable_type>(RES_TABLE_TYPE_TYPE); @@ -308,39 +315,60 @@ class PackageFlattener { type_header->config = config; type_header->config.swapHtoD(); - auto max_accum = [](uint32_t max, - const std::unique_ptr<ResourceEntry>& a) -> uint32_t { - return std::max(max, (uint32_t)a->id.value()); - }; - - // Find the largest entry ID. That is how many entries we will have. - const uint32_t entry_count = - std::accumulate(type->entries.begin(), type->entries.end(), 0, - max_accum) + - 1; - - type_header->entryCount = util::HostToDevice32(entry_count); - uint32_t* indices = type_writer.NextBlock<uint32_t>(entry_count); - - CHECK((size_t)entry_count <= std::numeric_limits<uint16_t>::max()); - memset(indices, 0xff, entry_count * sizeof(uint32_t)); + std::vector<uint32_t> offsets; + offsets.resize(num_total_entries, 0xffffffffu); - type_header->entriesStart = util::HostToDevice32(type_writer.size()); - - const size_t entry_start = type_writer.buffer()->size(); + BigBuffer values_buffer(512); for (FlatEntry& flat_entry : *entries) { - CHECK(flat_entry.entry->id.value() < entry_count); - indices[flat_entry.entry->id.value()] = - util::HostToDevice32(type_writer.buffer()->size() - entry_start); - if (!FlattenValue(&flat_entry, type_writer.buffer())) { + CHECK(static_cast<size_t>(flat_entry.entry->id.value()) < num_total_entries); + offsets[flat_entry.entry->id.value()] = values_buffer.size(); + if (!FlattenValue(&flat_entry, &values_buffer)) { diag_->Error(DiagMessage() << "failed to flatten resource '" - << ResourceNameRef(package_->name, type->type, - flat_entry.entry->name) + << ResourceNameRef(package_->name, type->type, flat_entry.entry->name) << "' for configuration '" << config << "'"); return false; } } + + bool sparse_encode = use_sparse_entries_; + + // Only sparse encode if the entries will be read on platforms O+. + sparse_encode = + sparse_encode && (context_->GetMinSdkVersion() >= SDK_O || config.sdkVersion >= SDK_O); + + // Only sparse encode if the offsets are representable in 2 bytes. + sparse_encode = + sparse_encode && (values_buffer.size() / 4u) <= std::numeric_limits<uint16_t>::max(); + + // Only sparse encode if the ratio of populated entries to total entries is below some + // threshold. + sparse_encode = + sparse_encode && ((100 * entries->size()) / num_total_entries) < kSparseEncodingThreshold; + + if (sparse_encode) { + type_header->entryCount = util::HostToDevice32(entries->size()); + type_header->flags |= ResTable_type::FLAG_SPARSE; + ResTable_sparseTypeEntry* indices = + type_writer.NextBlock<ResTable_sparseTypeEntry>(entries->size()); + for (size_t i = 0; i < num_total_entries; i++) { + if (offsets[i] != ResTable_type::NO_ENTRY) { + CHECK((offsets[i] & 0x03) == 0); + indices->idx = util::HostToDevice16(i); + indices->offset = util::HostToDevice16(offsets[i] / 4u); + indices++; + } + } + } else { + type_header->entryCount = util::HostToDevice32(num_total_entries); + uint32_t* indices = type_writer.NextBlock<uint32_t>(num_total_entries); + for (size_t i = 0; i < num_total_entries; i++) { + indices[i] = util::HostToDevice32(offsets[i]); + } + } + + type_header->entriesStart = util::HostToDevice32(type_writer.size()); + type_writer.buffer()->AppendBuffer(std::move(values_buffer)); type_writer.Finish(); return true; } @@ -370,8 +398,7 @@ class PackageFlattener { CHECK(bool(entry->id)) << "entry must have an ID set"; sorted_entries.push_back(entry.get()); } - std::sort(sorted_entries.begin(), sorted_entries.end(), - cmp_ids<ResourceEntry>); + std::sort(sorted_entries.begin(), sorted_entries.end(), cmp_ids<ResourceEntry>); return sorted_entries; } @@ -443,22 +470,22 @@ class PackageFlattener { type_pool_.MakeRef(ToString(type->type)); std::vector<ResourceEntry*> sorted_entries = CollectAndSortEntries(type); - if (!FlattenTypeSpec(type, &sorted_entries, buffer)) { return false; } + // Since the entries are sorted by ID, the last ID will be the largest. + const size_t num_entries = sorted_entries.back()->id.value() + 1; + // The binary resource table lists resource entries for each // configuration. // We store them inverted, where a resource entry lists the values for // each // configuration available. Here we reverse this to match the binary // table. - std::map<ConfigDescription, std::vector<FlatEntry>> - config_to_entry_list_map; + std::map<ConfigDescription, std::vector<FlatEntry>> config_to_entry_list_map; for (ResourceEntry* entry : sorted_entries) { - const uint32_t key_index = - (uint32_t)key_pool_.MakeRef(entry->name).index(); + const uint32_t key_index = (uint32_t)key_pool_.MakeRef(entry->name).index(); // Group values by configuration. for (auto& config_value : entry->values) { @@ -469,7 +496,7 @@ class PackageFlattener { // Flatten a configuration value. for (auto& entry : config_to_entry_list_map) { - if (!FlattenConfig(type, entry.first, &entry.second, buffer)) { + if (!FlattenConfig(type, entry.first, num_entries, &entry.second, buffer)) { return false; } } @@ -477,8 +504,10 @@ class PackageFlattener { return true; } + IAaptContext* context_; IDiagnostics* diag_; ResourceTablePackage* package_; + bool use_sparse_entries_; StringPool type_pool_; StringPool key_pool_; }; @@ -513,7 +542,7 @@ bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) { // Flatten each package. for (auto& package : table->packages) { - PackageFlattener flattener(context->GetDiagnostics(), package.get()); + PackageFlattener flattener(context, package.get(), options_.use_sparse_entries); if (!flattener.FlattenPackage(&package_buffer)) { return false; } diff --git a/tools/aapt2/flatten/TableFlattener.h b/tools/aapt2/flatten/TableFlattener.h index 53f52c29a6a3..223aef8119c0 100644 --- a/tools/aapt2/flatten/TableFlattener.h +++ b/tools/aapt2/flatten/TableFlattener.h @@ -25,15 +25,29 @@ namespace aapt { +// The percentage of used entries for a type for which using a sparse encoding is +// preferred. +constexpr const size_t kSparseEncodingThreshold = 60; + +struct TableFlattenerOptions { + // When true, types for configurations with a sparse set of entries are encoded + // as a sparse map of entry ID and offset to actual data. + // This is only available on platforms O+ and will only be respected when + // minSdk is O+. + bool use_sparse_entries = false; +}; + class TableFlattener : public IResourceTableConsumer { public: - explicit TableFlattener(BigBuffer* buffer) : buffer_(buffer) {} + explicit TableFlattener(const TableFlattenerOptions& options, BigBuffer* buffer) + : options_(options), buffer_(buffer) {} bool Consume(IAaptContext* context, ResourceTable* table) override; private: DISALLOW_COPY_AND_ASSIGN(TableFlattener); + TableFlattenerOptions options_; BigBuffer* buffer_; }; diff --git a/tools/aapt2/flatten/TableFlattener_test.cpp b/tools/aapt2/flatten/TableFlattener_test.cpp index c72624066fb8..ff717423fc92 100644 --- a/tools/aapt2/flatten/TableFlattener_test.cpp +++ b/tools/aapt2/flatten/TableFlattener_test.cpp @@ -16,7 +16,10 @@ #include "flatten/TableFlattener.h" +#include "android-base/stringprintf.h" + #include "ResourceUtils.h" +#include "SdkConstants.h" #include "test/Test.h" #include "unflatten/BinaryResourceParser.h" #include "util/Util.h" @@ -34,32 +37,40 @@ class TableFlattenerTest : public ::testing::Test { .Build(); } - ::testing::AssertionResult Flatten(ResourceTable* table, - ResTable* out_table) { + ::testing::AssertionResult Flatten(IAaptContext* context, const TableFlattenerOptions& options, + ResourceTable* table, std::string* out_content) { BigBuffer buffer(1024); - TableFlattener flattener(&buffer); - if (!flattener.Consume(context_.get(), table)) { + TableFlattener flattener(options, &buffer); + if (!flattener.Consume(context, table)) { return ::testing::AssertionFailure() << "failed to flatten ResourceTable"; } + *out_content = buffer.to_string(); + return ::testing::AssertionSuccess(); + } + + ::testing::AssertionResult Flatten(IAaptContext* context, const TableFlattenerOptions& options, + ResourceTable* table, ResTable* out_table) { + std::string content; + auto result = Flatten(context, options, table, &content); + if (!result) { + return result; + } - std::unique_ptr<uint8_t[]> data = util::Copy(buffer); - if (out_table->add(data.get(), buffer.size(), -1, true) != NO_ERROR) { + if (out_table->add(content.data(), content.size(), -1, true) != NO_ERROR) { return ::testing::AssertionFailure() << "flattened ResTable is corrupt"; } return ::testing::AssertionSuccess(); } - ::testing::AssertionResult Flatten(ResourceTable* table, - ResourceTable* out_table) { - BigBuffer buffer(1024); - TableFlattener flattener(&buffer); - if (!flattener.Consume(context_.get(), table)) { - return ::testing::AssertionFailure() << "failed to flatten ResourceTable"; + ::testing::AssertionResult Flatten(IAaptContext* context, const TableFlattenerOptions options, + ResourceTable* table, ResourceTable* out_table) { + std::string content; + auto result = Flatten(context, options, table, &content); + if (!result) { + return result; } - std::unique_ptr<uint8_t[]> data = util::Copy(buffer); - BinaryResourceParser parser(context_.get(), out_table, {}, data.get(), - buffer.size()); + BinaryResourceParser parser(context, out_table, {}, content.data(), content.size()); if (!parser.Parse()) { return ::testing::AssertionFailure() << "flattened ResTable is corrupt"; } @@ -127,7 +138,7 @@ class TableFlattenerTest : public ::testing::Test { return ::testing::AssertionSuccess(); } - private: + protected: std::unique_ptr<IAaptContext> context_; }; @@ -153,7 +164,7 @@ TEST_F(TableFlattenerTest, FlattenFullyLinkedTable) { .Build(); ResTable res_table; - ASSERT_TRUE(Flatten(table.get(), &res_table)); + ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table)); EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020000), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); @@ -200,7 +211,7 @@ TEST_F(TableFlattenerTest, FlattenEntriesWithGapsInIds) { .Build(); ResTable res_table; - ASSERT_TRUE(Flatten(table.get(), &res_table)); + ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table)); EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020001), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); @@ -222,7 +233,7 @@ TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) { .Build(); ResourceTable result; - ASSERT_TRUE(Flatten(table.get(), &result)); + ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &result)); Attribute* actualAttr = test::GetValue<Attribute>(&result, "android:attr/foo"); @@ -233,4 +244,119 @@ TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) { EXPECT_EQ(attr.max_int, actualAttr->max_int); } +static std::unique_ptr<ResourceTable> BuildTableWithSparseEntries( + IAaptContext* context, const ConfigDescription& sparse_config, float load) { + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .SetPackageId(context->GetCompilationPackage(), context->GetPackageId()) + .Build(); + + // Add regular entries. + int stride = static_cast<int>(1.0f / load); + for (int i = 0; i < 100; i++) { + const ResourceName name = test::ParseNameOrDie( + base::StringPrintf("%s:string/foo_%d", context->GetCompilationPackage().data(), i)); + const ResourceId resid(context->GetPackageId(), 0x02, static_cast<uint16_t>(i)); + const auto value = + util::make_unique<BinaryPrimitive>(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(i)); + CHECK(table->AddResource(name, resid, ConfigDescription::DefaultConfig(), "", + std::unique_ptr<Value>(value->Clone(nullptr)), + context->GetDiagnostics())); + + // Every few entries, write out a sparse_config value. This will give us the desired load. + if (i % stride == 0) { + CHECK(table->AddResource(name, resid, sparse_config, "", + std::unique_ptr<Value>(value->Clone(nullptr)), + context->GetDiagnostics())); + } + } + return table; +} + +TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkO) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder() + .SetCompilationPackage("android") + .SetPackageId(0x01) + .SetMinSdkVersion(SDK_O) + .Build(); + + const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB"); + auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f); + + TableFlattenerOptions options; + options.use_sparse_entries = true; + + std::string no_sparse_contents; + ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents)); + + std::string sparse_contents; + ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents)); + + EXPECT_GT(no_sparse_contents.size(), sparse_contents.size()); + + // Attempt to parse the sparse contents. + + ResourceTable sparse_table; + BinaryResourceParser parser(context.get(), &sparse_table, Source("test.arsc"), + sparse_contents.data(), sparse_contents.size()); + ASSERT_TRUE(parser.Parse()); + + auto value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_0", + sparse_config); + ASSERT_NE(nullptr, value); + EXPECT_EQ(0u, value->value.data); + + ASSERT_EQ(nullptr, test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1", + sparse_config)); + + value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4", + sparse_config); + ASSERT_NE(nullptr, value); + EXPECT_EQ(4u, value->value.data); +} + +TEST_F(TableFlattenerTest, FlattenSparseEntryWithConfigSdkVersionO) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder() + .SetCompilationPackage("android") + .SetPackageId(0x01) + .SetMinSdkVersion(SDK_LOLLIPOP) + .Build(); + + const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB-v26"); + auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f); + + TableFlattenerOptions options; + options.use_sparse_entries = true; + + std::string no_sparse_contents; + ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents)); + + std::string sparse_contents; + ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents)); + + EXPECT_GT(no_sparse_contents.size(), sparse_contents.size()); +} + +TEST_F(TableFlattenerTest, DoNotUseSparseEntryForDenseConfig) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder() + .SetCompilationPackage("android") + .SetPackageId(0x01) + .SetMinSdkVersion(SDK_O) + .Build(); + + const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB"); + auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.80f); + + TableFlattenerOptions options; + options.use_sparse_entries = true; + + std::string no_sparse_contents; + ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents)); + + std::string sparse_contents; + ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents)); + + EXPECT_EQ(no_sparse_contents.size(), sparse_contents.size()); +} + } // namespace aapt diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp index 0501a3bb5fca..f07e20bbc78a 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -96,6 +96,9 @@ struct LinkOptions { // Products to use/filter on. std::unordered_set<std::string> products; + // Flattening options. + TableFlattenerOptions table_flattener_options; + // Split APK options. TableSplitterOptions table_splitter_options; std::vector<SplitConstraints> split_constraints; @@ -874,7 +877,7 @@ class LinkCommand { bool FlattenTable(ResourceTable* table, IArchiveWriter* writer) { BigBuffer buffer(1024); - TableFlattener flattener(&buffer); + TableFlattener flattener(options_.table_flattener_options, &buffer); if (!flattener.Consume(context_, table)) { return false; } @@ -1870,24 +1873,19 @@ int Link(const std::vector<StringPiece>& args) { .RequiredFlag("-o", "Output path", &options.output_path) .RequiredFlag("--manifest", "Path to the Android manifest to build", &options.manifest_path) - .OptionalFlagList("-I", "Adds an Android APK to link against", - &options.include_paths) - .OptionalFlagList( - "-R", - "Compilation unit to link, using `overlay` semantics.\n" - "The last conflicting resource given takes precedence.", - &overlay_arg_list) + .OptionalFlagList("-I", "Adds an Android APK to link against", &options.include_paths) + .OptionalFlagList("-R", + "Compilation unit to link, using `overlay` semantics.\n" + "The last conflicting resource given takes precedence.", + &overlay_arg_list) .OptionalFlag("--java", "Directory in which to generate R.java", &options.generate_java_class_path) - .OptionalFlag("--proguard", - "Output file for generated Proguard rules", + .OptionalFlag("--proguard", "Output file for generated Proguard rules", &options.generate_proguard_rules_path) - .OptionalFlag( - "--proguard-main-dex", - "Output file for generated Proguard rules for the main dex", - &options.generate_main_dex_proguard_rules_path) - .OptionalSwitch("--no-auto-version", - "Disables automatic style and layout SDK versioning", + .OptionalFlag("--proguard-main-dex", + "Output file for generated Proguard rules for the main dex", + &options.generate_main_dex_proguard_rules_path) + .OptionalSwitch("--no-auto-version", "Disables automatic style and layout SDK versioning", &options.no_auto_version) .OptionalSwitch("--no-version-vectors", "Disables automatic versioning of vector drawables. " @@ -1903,25 +1901,22 @@ int Link(const std::vector<StringPiece>& args) { "Disables automatic deduping of resources with\n" "identical values across compatible configurations.", &options.no_resource_deduping) - .OptionalSwitch( - "-x", - "Legacy flag that specifies to use the package identifier 0x01", - &legacy_x_flag) - .OptionalSwitch("-z", - "Require localization of strings marked 'suggested'", + .OptionalSwitch("--enable-sparse-encoding", + "Enables encoding sparse entries using a binary search tree.\n" + "This decreases APK size at the cost of resource retrieval performance.", + &options.table_flattener_options.use_sparse_entries) + .OptionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01", + &legacy_x_flag) + .OptionalSwitch("-z", "Require localization of strings marked 'suggested'", &require_localization) - .OptionalFlag( - "-c", - "Comma separated list of configurations to include. The default\n" - "is all configurations", - &configs) - .OptionalFlag( - "--preferred-density", - "Selects the closest matching density and strips out all others.", - &preferred_density) - .OptionalFlag("--product", - "Comma separated list of product names to keep", - &product_list) + .OptionalFlag("-c", + "Comma separated list of configurations to include. The default\n" + "is all configurations", + &configs) + .OptionalFlag("--preferred-density", + "Selects the closest matching density and strips out all others.", + &preferred_density) + .OptionalFlag("--product", "Comma separated list of product names to keep", &product_list) .OptionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified " "by -o", @@ -1935,11 +1930,10 @@ int Link(const std::vector<StringPiece>& args) { "Default minimum SDK version to use for " "AndroidManifest.xml", &options.manifest_fixer_options.min_sdk_version_default) - .OptionalFlag( - "--target-sdk-version", - "Default target SDK version to use for " - "AndroidManifest.xml", - &options.manifest_fixer_options.target_sdk_version_default) + .OptionalFlag("--target-sdk-version", + "Default target SDK version to use for " + "AndroidManifest.xml", + &options.manifest_fixer_options.target_sdk_version_default) .OptionalFlag("--version-code", "Version code (integer) to inject into the " "AndroidManifest.xml if none is present", @@ -1948,8 +1942,7 @@ int Link(const std::vector<StringPiece>& args) { "Version name to inject into the AndroidManifest.xml " "if none is present", &options.manifest_fixer_options.version_name_default) - .OptionalSwitch("--static-lib", "Generate a static Android library", - &options.static_lib) + .OptionalSwitch("--static-lib", "Generate a static Android library", &options.static_lib) .OptionalSwitch("--no-static-lib-packages", "Merge all library resources under the app's package", &options.no_static_lib_packages) @@ -1957,14 +1950,12 @@ int Link(const std::vector<StringPiece>& args) { "Generates R.java without the final modifier.\n" "This is implied when --static-lib is specified.", &options.generate_non_final_ids) - .OptionalFlag("--stable-ids", - "File containing a list of name to ID mapping.", + .OptionalFlag("--stable-ids", "File containing a list of name to ID mapping.", &stable_id_file_path) - .OptionalFlag( - "--emit-ids", - "Emit a file at the given path with a list of name to ID\n" - "mappings, suitable for use with --stable-ids.", - &options.resource_id_map_path) + .OptionalFlag("--emit-ids", + "Emit a file at the given path with a list of name to ID\n" + "mappings, suitable for use with --stable-ids.", + &options.resource_id_map_path) .OptionalFlag("--private-symbols", "Package name to use when generating R.java for " "private symbols.\n" @@ -1972,8 +1963,7 @@ int Link(const std::vector<StringPiece>& args) { "the application's " "package name", &options.private_symbols) - .OptionalFlag("--custom-package", - "Custom Java package under which to generate R.java", + .OptionalFlag("--custom-package", "Custom Java package under which to generate R.java", &options.custom_java_package) .OptionalFlagList("--extra-packages", "Generate the same R.java but with different " @@ -1987,23 +1977,19 @@ int Link(const std::vector<StringPiece>& args) { "Allows the addition of new resources in " "overlays without <add-resource> tags", &options.auto_add_overlay) - .OptionalFlag("--rename-manifest-package", - "Renames the package in AndroidManifest.xml", + .OptionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml", &options.manifest_fixer_options.rename_manifest_package) - .OptionalFlag( - "--rename-instrumentation-target-package", - "Changes the name of the target package for instrumentation. " - "Most useful " - "when used\nin conjunction with --rename-manifest-package", - &options.manifest_fixer_options - .rename_instrumentation_target_package) + .OptionalFlag("--rename-instrumentation-target-package", + "Changes the name of the target package for instrumentation. " + "Most useful " + "when used\nin conjunction with --rename-manifest-package", + &options.manifest_fixer_options.rename_instrumentation_target_package) .OptionalFlagList("-0", "File extensions not to compress", &options.extensions_to_not_compress) - .OptionalFlagList( - "--split", - "Split resources matching a set of configs out to a " - "Split APK.\nSyntax: path/to/output.apk:<config>[,<config>[...]]", - &split_args) + .OptionalFlagList("--split", + "Split resources matching a set of configs out to a " + "Split APK.\nSyntax: path/to/output.apk:<config>[,<config>[...]]", + &split_args) .OptionalSwitch("-v", "Enables verbose logging", &verbose); if (!flags.Parse("aapt2 link", args, &std::cerr)) { diff --git a/tools/aapt2/util/BigBuffer.cpp b/tools/aapt2/util/BigBuffer.cpp index ef99dca286a4..75fa78915b65 100644 --- a/tools/aapt2/util/BigBuffer.cpp +++ b/tools/aapt2/util/BigBuffer.cpp @@ -76,4 +76,12 @@ void* BigBuffer::NextBlock(size_t* out_size) { return blocks_.back().buffer.get(); } +std::string BigBuffer::to_string() const { + std::string result; + for (const Block& block : blocks_) { + result.append(block.buffer.get(), block.buffer.get() + block.size); + } + return result; +} + } // namespace aapt diff --git a/tools/aapt2/util/BigBuffer.h b/tools/aapt2/util/BigBuffer.h index d23c41d4d6f0..30452552888e 100644 --- a/tools/aapt2/util/BigBuffer.h +++ b/tools/aapt2/util/BigBuffer.h @@ -19,6 +19,7 @@ #include <cstring> #include <memory> +#include <string> #include <type_traits> #include <vector> @@ -116,6 +117,8 @@ class BigBuffer { const_iterator begin() const; const_iterator end() const; + std::string to_string() const; + private: DISALLOW_COPY_AND_ASSIGN(BigBuffer); |