From c8f71aa67ea599cb80205496cb67e9e7a121299c Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Wed, 8 Feb 2017 07:03:50 -0800 Subject: [PATCH] Add ResTable_sparseTypeEntry support Benchmarks on bullhead-userdebug show that there is a negligent performance impact when using sparse entries on a 30% loaded sparse type of 1000 resources. Benchmark Time CPU Iterations ----------------------------------------------------------------------------------- BM_SparseEntryGetResourceSparseLarge 255 ns 254 ns 2751408 BM_SparseEntryGetResourceNotSparseLarge 254 ns 254 ns 2756534 Bug: 27381711 Test: make libandroidfw_tests aapt2_tests Change-Id: I051ea22f2f6b2bc3696e446adc9e2a34be18009f --- libs/androidfw/ResourceTypes.cpp | 80 +- libs/androidfw/TypeWrappers.cpp | 44 +- .../include/androidfw/ResourceTypes.h | 42 +- .../include/androidfw/TypeWrappers.h | 8 +- libs/androidfw/tests/Android.mk | 2 + libs/androidfw/tests/AssetManager2_bench.cpp | 29 +- libs/androidfw/tests/BenchmarkHelpers.cpp | 51 + libs/androidfw/tests/BenchmarkHelpers.h | 34 + libs/androidfw/tests/ResTable_test.cpp | 28 + libs/androidfw/tests/SparseEntry_bench.cpp | 64 ++ libs/androidfw/tests/TestHelpers.h | 1 + libs/androidfw/tests/data/sparse/.gitignore | 1 + .../tests/data/sparse/AndroidManifest.xml | 20 + libs/androidfw/tests/data/sparse/R.h | 53 + libs/androidfw/tests/data/sparse/build | 24 + .../tests/data/sparse/gen_strings.sh | 18 + .../tests/data/sparse/not_sparse.apk | Bin 0 -> 61971 bytes .../data/sparse/res/values-v26/strings.xml | 336 ++++++ .../data/sparse/res/values-v26/values.xml | 22 + .../tests/data/sparse/res/values/strings.xml | 1002 +++++++++++++++++ .../tests/data/sparse/res/values/values.xml | 28 + libs/androidfw/tests/data/sparse/sparse.apk | Bin 0 -> 59275 bytes tools/aapt2/LoadedApk.cpp | 4 +- tools/aapt2/SdkConstants.h | 4 +- tools/aapt2/flatten/TableFlattener.cpp | 107 +- tools/aapt2/flatten/TableFlattener.h | 16 +- tools/aapt2/flatten/TableFlattener_test.cpp | 164 ++- tools/aapt2/link/Link.cpp | 114 +- tools/aapt2/util/BigBuffer.cpp | 8 + tools/aapt2/util/BigBuffer.h | 3 + 30 files changed, 2125 insertions(+), 182 deletions(-) create mode 100644 libs/androidfw/tests/BenchmarkHelpers.cpp create mode 100644 libs/androidfw/tests/BenchmarkHelpers.h create mode 100644 libs/androidfw/tests/SparseEntry_bench.cpp create mode 100644 libs/androidfw/tests/data/sparse/.gitignore create mode 100644 libs/androidfw/tests/data/sparse/AndroidManifest.xml create mode 100644 libs/androidfw/tests/data/sparse/R.h create mode 100755 libs/androidfw/tests/data/sparse/build create mode 100755 libs/androidfw/tests/data/sparse/gen_strings.sh create mode 100644 libs/androidfw/tests/data/sparse/not_sparse.apk create mode 100644 libs/androidfw/tests/data/sparse/res/values-v26/strings.xml create mode 100644 libs/androidfw/tests/data/sparse/res/values-v26/values.xml create mode 100644 libs/androidfw/tests/data/sparse/res/values/strings.xml create mode 100644 libs/androidfw/tests/data/sparse/res/values/values.xml create mode 100644 libs/androidfw/tests/data/sparse/sparse.apk diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 763a178ed43b0..0f5b65c446863 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(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( reinterpret_cast(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(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(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; entryIndexheader.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; entryIndexflags & ResTable_type::FLAG_SPARSE) { + const ResTable_sparseTypeEntry* entry = + reinterpret_cast( + eindex + entryIndex); + entryId = dtohs(entry->idx); + // Offsets are encoded as divided by 4. + thisOffset = static_cast(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 06b40405f4040..647aa197a94db 100644 --- a/libs/androidfw/TypeWrappers.cpp +++ b/libs/androidfw/TypeWrappers.cpp @@ -16,23 +16,45 @@ #include +#include + 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(data) + dtohl(data->header.size); + const uint32_t* const entryIndices = reinterpret_cast( + reinterpret_cast(data) + dtohs(data->header.headerSize)); + if (reinterpret_cast(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(type) + dtohl(type->header.size); const uint32_t* const entryIndices = reinterpret_cast( @@ -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(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 04a5d958c6140..5c5c9e6b4f551 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; @@ -1380,6 +1396,24 @@ struct ResTable_type ResTable_config config; }; +/** + * 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 diff --git a/libs/androidfw/include/androidfw/TypeWrappers.h b/libs/androidfw/include/androidfw/TypeWrappers.h index f1daf3365c28b..5cfe54e5759dd 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 19527c51e816e..921fd147aa800 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 273290a26050a..67de741b1b66f 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& paths, } } -static void GetResourceBenchmarkOld(const std::vector& 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 0000000000000..3619b7ee83abc --- /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& 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 0000000000000..fc366642ca36b --- /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 +#include + +#include "benchmark/benchmark.h" + +#include "androidfw/ResourceTypes.h" + +namespace android { + +void GetResourceBenchmarkOld(const std::vector& 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 ad1cd2b289d67..2df41305237e1 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 0000000000000..1ebf7ce623bd4 --- /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 a11ea8416c7d2..ec78b2ae5efc8 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -19,6 +19,7 @@ #include #include +#include #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 0000000000000..52e32d4fcbf21 --- /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 0000000000000..27911b62447a9 --- /dev/null +++ b/libs/androidfw/tests/data/sparse/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/libs/androidfw/tests/data/sparse/R.h b/libs/androidfw/tests/data/sparse/R.h new file mode 100644 index 0000000000000..243e74fac65a8 --- /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 + +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 0000000000000..305593f018754 --- /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 0000000000000..e7e1d603ea4ed --- /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 "" > $OUTPUT_default +echo "" > $OUTPUT_v26 +for i in {0..999} +do + echo " $i" >> $OUTPUT_default + if [ "$(($i % 3))" -eq "0" ] + then + echo " $(($i * 10))" >> $OUTPUT_v26 + fi +done +echo "" >> $OUTPUT_default +echo "" >> $OUTPUT_v26 + diff --git a/libs/androidfw/tests/data/sparse/not_sparse.apk b/libs/androidfw/tests/data/sparse/not_sparse.apk new file mode 100644 index 0000000000000000000000000000000000000000..599a370dbfb1394d34a1300b541900c86fc9e2a7 GIT binary patch literal 61971 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 E0S$Q?O8@`> literal 0 HcmV?d00001 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 0000000000000..b6f82997d18b8 --- /dev/null +++ b/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml @@ -0,0 +1,336 @@ + + 0 + 30 + 60 + 90 + 120 + 150 + 180 + 210 + 240 + 270 + 300 + 330 + 360 + 390 + 420 + 450 + 480 + 510 + 540 + 570 + 600 + 630 + 660 + 690 + 720 + 750 + 780 + 810 + 840 + 870 + 900 + 930 + 960 + 990 + 1020 + 1050 + 1080 + 1110 + 1140 + 1170 + 1200 + 1230 + 1260 + 1290 + 1320 + 1350 + 1380 + 1410 + 1440 + 1470 + 1500 + 1530 + 1560 + 1590 + 1620 + 1650 + 1680 + 1710 + 1740 + 1770 + 1800 + 1830 + 1860 + 1890 + 1920 + 1950 + 1980 + 2010 + 2040 + 2070 + 2100 + 2130 + 2160 + 2190 + 2220 + 2250 + 2280 + 2310 + 2340 + 2370 + 2400 + 2430 + 2460 + 2490 + 2520 + 2550 + 2580 + 2610 + 2640 + 2670 + 2700 + 2730 + 2760 + 2790 + 2820 + 2850 + 2880 + 2910 + 2940 + 2970 + 3000 + 3030 + 3060 + 3090 + 3120 + 3150 + 3180 + 3210 + 3240 + 3270 + 3300 + 3330 + 3360 + 3390 + 3420 + 3450 + 3480 + 3510 + 3540 + 3570 + 3600 + 3630 + 3660 + 3690 + 3720 + 3750 + 3780 + 3810 + 3840 + 3870 + 3900 + 3930 + 3960 + 3990 + 4020 + 4050 + 4080 + 4110 + 4140 + 4170 + 4200 + 4230 + 4260 + 4290 + 4320 + 4350 + 4380 + 4410 + 4440 + 4470 + 4500 + 4530 + 4560 + 4590 + 4620 + 4650 + 4680 + 4710 + 4740 + 4770 + 4800 + 4830 + 4860 + 4890 + 4920 + 4950 + 4980 + 5010 + 5040 + 5070 + 5100 + 5130 + 5160 + 5190 + 5220 + 5250 + 5280 + 5310 + 5340 + 5370 + 5400 + 5430 + 5460 + 5490 + 5520 + 5550 + 5580 + 5610 + 5640 + 5670 + 5700 + 5730 + 5760 + 5790 + 5820 + 5850 + 5880 + 5910 + 5940 + 5970 + 6000 + 6030 + 6060 + 6090 + 6120 + 6150 + 6180 + 6210 + 6240 + 6270 + 6300 + 6330 + 6360 + 6390 + 6420 + 6450 + 6480 + 6510 + 6540 + 6570 + 6600 + 6630 + 6660 + 6690 + 6720 + 6750 + 6780 + 6810 + 6840 + 6870 + 6900 + 6930 + 6960 + 6990 + 7020 + 7050 + 7080 + 7110 + 7140 + 7170 + 7200 + 7230 + 7260 + 7290 + 7320 + 7350 + 7380 + 7410 + 7440 + 7470 + 7500 + 7530 + 7560 + 7590 + 7620 + 7650 + 7680 + 7710 + 7740 + 7770 + 7800 + 7830 + 7860 + 7890 + 7920 + 7950 + 7980 + 8010 + 8040 + 8070 + 8100 + 8130 + 8160 + 8190 + 8220 + 8250 + 8280 + 8310 + 8340 + 8370 + 8400 + 8430 + 8460 + 8490 + 8520 + 8550 + 8580 + 8610 + 8640 + 8670 + 8700 + 8730 + 8760 + 8790 + 8820 + 8850 + 8880 + 8910 + 8940 + 8970 + 9000 + 9030 + 9060 + 9090 + 9120 + 9150 + 9180 + 9210 + 9240 + 9270 + 9300 + 9330 + 9360 + 9390 + 9420 + 9450 + 9480 + 9510 + 9540 + 9570 + 9600 + 9630 + 9660 + 9690 + 9720 + 9750 + 9780 + 9810 + 9840 + 9870 + 9900 + 9930 + 9960 + 9990 + 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 0000000000000..b396ad24aa8c5 --- /dev/null +++ b/libs/androidfw/tests/data/sparse/res/values-v26/values.xml @@ -0,0 +1,22 @@ + + + + + 0 + 400 + 500 + 900 + 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 0000000000000..6ff839a167a20 --- /dev/null +++ b/libs/androidfw/tests/data/sparse/res/values/strings.xml @@ -0,0 +1,1002 @@ + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 + 100 + 101 + 102 + 103 + 104 + 105 + 106 + 107 + 108 + 109 + 110 + 111 + 112 + 113 + 114 + 115 + 116 + 117 + 118 + 119 + 120 + 121 + 122 + 123 + 124 + 125 + 126 + 127 + 128 + 129 + 130 + 131 + 132 + 133 + 134 + 135 + 136 + 137 + 138 + 139 + 140 + 141 + 142 + 143 + 144 + 145 + 146 + 147 + 148 + 149 + 150 + 151 + 152 + 153 + 154 + 155 + 156 + 157 + 158 + 159 + 160 + 161 + 162 + 163 + 164 + 165 + 166 + 167 + 168 + 169 + 170 + 171 + 172 + 173 + 174 + 175 + 176 + 177 + 178 + 179 + 180 + 181 + 182 + 183 + 184 + 185 + 186 + 187 + 188 + 189 + 190 + 191 + 192 + 193 + 194 + 195 + 196 + 197 + 198 + 199 + 200 + 201 + 202 + 203 + 204 + 205 + 206 + 207 + 208 + 209 + 210 + 211 + 212 + 213 + 214 + 215 + 216 + 217 + 218 + 219 + 220 + 221 + 222 + 223 + 224 + 225 + 226 + 227 + 228 + 229 + 230 + 231 + 232 + 233 + 234 + 235 + 236 + 237 + 238 + 239 + 240 + 241 + 242 + 243 + 244 + 245 + 246 + 247 + 248 + 249 + 250 + 251 + 252 + 253 + 254 + 255 + 256 + 257 + 258 + 259 + 260 + 261 + 262 + 263 + 264 + 265 + 266 + 267 + 268 + 269 + 270 + 271 + 272 + 273 + 274 + 275 + 276 + 277 + 278 + 279 + 280 + 281 + 282 + 283 + 284 + 285 + 286 + 287 + 288 + 289 + 290 + 291 + 292 + 293 + 294 + 295 + 296 + 297 + 298 + 299 + 300 + 301 + 302 + 303 + 304 + 305 + 306 + 307 + 308 + 309 + 310 + 311 + 312 + 313 + 314 + 315 + 316 + 317 + 318 + 319 + 320 + 321 + 322 + 323 + 324 + 325 + 326 + 327 + 328 + 329 + 330 + 331 + 332 + 333 + 334 + 335 + 336 + 337 + 338 + 339 + 340 + 341 + 342 + 343 + 344 + 345 + 346 + 347 + 348 + 349 + 350 + 351 + 352 + 353 + 354 + 355 + 356 + 357 + 358 + 359 + 360 + 361 + 362 + 363 + 364 + 365 + 366 + 367 + 368 + 369 + 370 + 371 + 372 + 373 + 374 + 375 + 376 + 377 + 378 + 379 + 380 + 381 + 382 + 383 + 384 + 385 + 386 + 387 + 388 + 389 + 390 + 391 + 392 + 393 + 394 + 395 + 396 + 397 + 398 + 399 + 400 + 401 + 402 + 403 + 404 + 405 + 406 + 407 + 408 + 409 + 410 + 411 + 412 + 413 + 414 + 415 + 416 + 417 + 418 + 419 + 420 + 421 + 422 + 423 + 424 + 425 + 426 + 427 + 428 + 429 + 430 + 431 + 432 + 433 + 434 + 435 + 436 + 437 + 438 + 439 + 440 + 441 + 442 + 443 + 444 + 445 + 446 + 447 + 448 + 449 + 450 + 451 + 452 + 453 + 454 + 455 + 456 + 457 + 458 + 459 + 460 + 461 + 462 + 463 + 464 + 465 + 466 + 467 + 468 + 469 + 470 + 471 + 472 + 473 + 474 + 475 + 476 + 477 + 478 + 479 + 480 + 481 + 482 + 483 + 484 + 485 + 486 + 487 + 488 + 489 + 490 + 491 + 492 + 493 + 494 + 495 + 496 + 497 + 498 + 499 + 500 + 501 + 502 + 503 + 504 + 505 + 506 + 507 + 508 + 509 + 510 + 511 + 512 + 513 + 514 + 515 + 516 + 517 + 518 + 519 + 520 + 521 + 522 + 523 + 524 + 525 + 526 + 527 + 528 + 529 + 530 + 531 + 532 + 533 + 534 + 535 + 536 + 537 + 538 + 539 + 540 + 541 + 542 + 543 + 544 + 545 + 546 + 547 + 548 + 549 + 550 + 551 + 552 + 553 + 554 + 555 + 556 + 557 + 558 + 559 + 560 + 561 + 562 + 563 + 564 + 565 + 566 + 567 + 568 + 569 + 570 + 571 + 572 + 573 + 574 + 575 + 576 + 577 + 578 + 579 + 580 + 581 + 582 + 583 + 584 + 585 + 586 + 587 + 588 + 589 + 590 + 591 + 592 + 593 + 594 + 595 + 596 + 597 + 598 + 599 + 600 + 601 + 602 + 603 + 604 + 605 + 606 + 607 + 608 + 609 + 610 + 611 + 612 + 613 + 614 + 615 + 616 + 617 + 618 + 619 + 620 + 621 + 622 + 623 + 624 + 625 + 626 + 627 + 628 + 629 + 630 + 631 + 632 + 633 + 634 + 635 + 636 + 637 + 638 + 639 + 640 + 641 + 642 + 643 + 644 + 645 + 646 + 647 + 648 + 649 + 650 + 651 + 652 + 653 + 654 + 655 + 656 + 657 + 658 + 659 + 660 + 661 + 662 + 663 + 664 + 665 + 666 + 667 + 668 + 669 + 670 + 671 + 672 + 673 + 674 + 675 + 676 + 677 + 678 + 679 + 680 + 681 + 682 + 683 + 684 + 685 + 686 + 687 + 688 + 689 + 690 + 691 + 692 + 693 + 694 + 695 + 696 + 697 + 698 + 699 + 700 + 701 + 702 + 703 + 704 + 705 + 706 + 707 + 708 + 709 + 710 + 711 + 712 + 713 + 714 + 715 + 716 + 717 + 718 + 719 + 720 + 721 + 722 + 723 + 724 + 725 + 726 + 727 + 728 + 729 + 730 + 731 + 732 + 733 + 734 + 735 + 736 + 737 + 738 + 739 + 740 + 741 + 742 + 743 + 744 + 745 + 746 + 747 + 748 + 749 + 750 + 751 + 752 + 753 + 754 + 755 + 756 + 757 + 758 + 759 + 760 + 761 + 762 + 763 + 764 + 765 + 766 + 767 + 768 + 769 + 770 + 771 + 772 + 773 + 774 + 775 + 776 + 777 + 778 + 779 + 780 + 781 + 782 + 783 + 784 + 785 + 786 + 787 + 788 + 789 + 790 + 791 + 792 + 793 + 794 + 795 + 796 + 797 + 798 + 799 + 800 + 801 + 802 + 803 + 804 + 805 + 806 + 807 + 808 + 809 + 810 + 811 + 812 + 813 + 814 + 815 + 816 + 817 + 818 + 819 + 820 + 821 + 822 + 823 + 824 + 825 + 826 + 827 + 828 + 829 + 830 + 831 + 832 + 833 + 834 + 835 + 836 + 837 + 838 + 839 + 840 + 841 + 842 + 843 + 844 + 845 + 846 + 847 + 848 + 849 + 850 + 851 + 852 + 853 + 854 + 855 + 856 + 857 + 858 + 859 + 860 + 861 + 862 + 863 + 864 + 865 + 866 + 867 + 868 + 869 + 870 + 871 + 872 + 873 + 874 + 875 + 876 + 877 + 878 + 879 + 880 + 881 + 882 + 883 + 884 + 885 + 886 + 887 + 888 + 889 + 890 + 891 + 892 + 893 + 894 + 895 + 896 + 897 + 898 + 899 + 900 + 901 + 902 + 903 + 904 + 905 + 906 + 907 + 908 + 909 + 910 + 911 + 912 + 913 + 914 + 915 + 916 + 917 + 918 + 919 + 920 + 921 + 922 + 923 + 924 + 925 + 926 + 927 + 928 + 929 + 930 + 931 + 932 + 933 + 934 + 935 + 936 + 937 + 938 + 939 + 940 + 941 + 942 + 943 + 944 + 945 + 946 + 947 + 948 + 949 + 950 + 951 + 952 + 953 + 954 + 955 + 956 + 957 + 958 + 959 + 960 + 961 + 962 + 963 + 964 + 965 + 966 + 967 + 968 + 969 + 970 + 971 + 972 + 973 + 974 + 975 + 976 + 977 + 978 + 979 + 980 + 981 + 982 + 983 + 984 + 985 + 986 + 987 + 988 + 989 + 990 + 991 + 992 + 993 + 994 + 995 + 996 + 997 + 998 + 999 + 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 0000000000000..5cc42d8f83455 --- /dev/null +++ b/libs/androidfw/tests/data/sparse/res/values/values.xml @@ -0,0 +1,28 @@ + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + diff --git a/libs/androidfw/tests/data/sparse/sparse.apk b/libs/androidfw/tests/data/sparse/sparse.apk new file mode 100644 index 0000000000000000000000000000000000000000..1f9bba31b0a1357b0b7f0e30903d18df18f1963d GIT binary patch literal 59275 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, + 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* entries, BigBuffer* buffer) { + bool FlattenConfig(const ResourceTableType* type, const ConfigDescription& config, + const size_t num_total_entries, std::vector* entries, + BigBuffer* buffer) { + CHECK(num_total_entries != 0); + CHECK(num_total_entries <= std::numeric_limits::max()); + ChunkWriter type_writer(buffer); ResTable_type* type_header = type_writer.StartChunk(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& a) -> uint32_t { - return std::max(max, (uint32_t)a->id.value()); - }; + std::vector offsets; + offsets.resize(num_total_entries, 0xffffffffu); - // 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(entry_count); - - CHECK((size_t)entry_count <= std::numeric_limits::max()); - memset(indices, 0xff, entry_count * sizeof(uint32_t)); - - 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(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::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(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(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); + std::sort(sorted_entries.begin(), sorted_entries.end(), cmp_ids); return sorted_entries; } @@ -443,22 +470,22 @@ class PackageFlattener { type_pool_.MakeRef(ToString(type->type)); std::vector 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> - config_to_entry_list_map; + std::map> 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 53f52c29a6a35..223aef8119c09 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 c72624066fb85..ff717423fc92d 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(); + } - std::unique_ptr data = util::Copy(buffer); - if (out_table->add(data.get(), buffer.size(), -1, true) != NO_ERROR) { + ::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; + } + + 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 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 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(&result, "android:attr/foo"); @@ -233,4 +244,119 @@ TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) { EXPECT_EQ(attr.max_int, actualAttr->max_int); } +static std::unique_ptr BuildTableWithSparseEntries( + IAaptContext* context, const ConfigDescription& sparse_config, float load) { + std::unique_ptr table = + test::ResourceTableBuilder() + .SetPackageId(context->GetCompilationPackage(), context->GetPackageId()) + .Build(); + + // Add regular entries. + int stride = static_cast(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(i)); + const auto value = + util::make_unique(Res_value::TYPE_INT_DEC, static_cast(i)); + CHECK(table->AddResource(name, resid, ConfigDescription::DefaultConfig(), "", + std::unique_ptr(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->Clone(nullptr)), + context->GetDiagnostics())); + } + } + return table; +} + +TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkO) { + std::unique_ptr 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(&sparse_table, "android:string/foo_0", + sparse_config); + ASSERT_NE(nullptr, value); + EXPECT_EQ(0u, value->value.data); + + ASSERT_EQ(nullptr, test::GetValueForConfig(&sparse_table, "android:string/foo_1", + sparse_config)); + + value = test::GetValueForConfig(&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 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 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 0501a3bb5fcad..f07e20bbc78a0 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 products; + // Flattening options. + TableFlattenerOptions table_flattener_options; + // Split APK options. TableSplitterOptions table_splitter_options; std::vector 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& 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& 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& 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& 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& 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& 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& args) { "Allows the addition of new resources in " "overlays without 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:[,[...]]", - &split_args) + .OptionalFlagList("--split", + "Split resources matching a set of configs out to a " + "Split APK.\nSyntax: path/to/output.apk:[,[...]]", + &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 ef99dca286a49..75fa78915b655 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 d23c41d4d6f0f..30452552888ec 100644 --- a/tools/aapt2/util/BigBuffer.h +++ b/tools/aapt2/util/BigBuffer.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -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);