diff options
author | Shane Farmer <safarmer@google.com> | 2017-09-01 14:34:22 -0700 |
---|---|---|
committer | Shane Farmer <safarmer@google.com> | 2017-10-03 16:02:38 +0000 |
commit | 3edd47264335cf609ac6e733db77522f7c959c3f (patch) | |
tree | bedc420b9d9f980ff1e2c349be4a2b39348a6209 /tools/aapt2/optimize | |
parent | 2310403031f4bb6224cdd3aeefc80585c5b5f362 (diff) |
AAPT2: Set the minSdkVersion when generating multiple APKs.
When generating multiple APKs from a configuration file, check to see if
we have filtered resource by minSdkVersion and update the manifest to
reflect this. We only want to inflate and modify the manifest file if
there is an update to be applied.
Bug: 37944703
Bug: 67005138
Test: Ran unit tests
Test: Manually split an APK and verified the manifest by dumping with
AAPT (both xmltree and badging).
Change-Id: I64a0e4889d7d9e57373369b044a091287b06cc35
Diffstat (limited to 'tools/aapt2/optimize')
-rw-r--r-- | tools/aapt2/optimize/MultiApkGenerator.cpp | 94 | ||||
-rw-r--r-- | tools/aapt2/optimize/MultiApkGenerator_test.cpp | 62 |
2 files changed, 72 insertions, 84 deletions
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp index 5ff890832371..563c46df26d1 100644 --- a/tools/aapt2/optimize/MultiApkGenerator.cpp +++ b/tools/aapt2/optimize/MultiApkGenerator.cpp @@ -22,22 +22,47 @@ #include "androidfw/StringPiece.h" #include "LoadedApk.h" +#include "ResourceUtils.h" #include "configuration/ConfigurationParser.h" #include "filter/AbiFilter.h" #include "filter/Filter.h" #include "flatten/Archive.h" +#include "flatten/XmlFlattener.h" #include "optimize/VersionCollapser.h" #include "process/IResourceTableConsumer.h" #include "split/TableSplitter.h" #include "util/Files.h" +#include "xml/XmlDom.h" namespace aapt { using ::aapt::configuration::AndroidSdk; using ::aapt::configuration::Artifact; using ::aapt::configuration::PostProcessingConfiguration; +using ::aapt::xml::XmlResource; using ::android::StringPiece; +namespace { + +Maybe<AndroidSdk> GetAndroidSdk(const Artifact& artifact, const PostProcessingConfiguration& config, + IDiagnostics* diag) { + if (!artifact.android_sdk_group) { + return {}; + } + + const std::string& group_name = artifact.android_sdk_group.value(); + auto group = config.android_sdk_groups.find(group_name); + // TODO: Remove validation when configuration parser ensures referential integrity. + if (group == config.android_sdk_groups.end()) { + diag->Error(DiagMessage() << "could not find referenced group '" << group_name << "'"); + return {}; + } + + return group->second; +} + +} // namespace + /** * Context wrapper that allows the min Android SDK value to be overridden. */ @@ -127,6 +152,43 @@ bool MultiApkGenerator::FromBaseApk(const MultiApkGeneratorOptions& options) { return false; } + std::unique_ptr<XmlResource> manifest; + + Maybe<AndroidSdk> maybe_sdk = GetAndroidSdk(artifact, config, diag); + if (maybe_sdk) { + // TODO(safarmer): Handle the rest of the Android SDK. + const AndroidSdk& android_sdk = maybe_sdk.value(); + + manifest = apk_->InflateManifest(context_); + if (!manifest) { + return false; + } + + // Make sure the first element is <manifest> with package attribute. + xml::Element* manifest_el = manifest->root.get(); + if (manifest_el == nullptr) { + return {}; + } + + if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") { + diag->Error(DiagMessage(manifest->file.source) << "root tag must be <manifest>"); + return {}; + } + + if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) { + if (xml::Attribute* min_sdk_attr = + uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) { + if (min_sdk_attr == nullptr) { + diag->Error(DiagMessage(manifest->file.source.WithLine(uses_sdk_el->line_number)) + << "missing android:minSdkVersion"); + return {}; + } + const std::string& min_sdk_str = std::to_string(android_sdk.min_sdk_version.value()); + min_sdk_attr->compiled_value = ResourceUtils::TryParseInt(min_sdk_str); + } + } + } + std::string out = options.out_dir; if (!file::mkdirs(out)) { context_->GetDiagnostics()->Warn(DiagMessage() << "could not create out dir: " << out); @@ -145,7 +207,7 @@ bool MultiApkGenerator::FromBaseApk(const MultiApkGeneratorOptions& options) { } if (!apk_->WriteToArchive(context_, table.get(), options.table_flattener_options, &filters, - writer.get())) { + writer.get(), manifest.get())) { return false; } } @@ -208,37 +270,15 @@ std::unique_ptr<ResourceTable> MultiApkGenerator::FilterTable( splits.config_filter = &axis_filter; } - if (artifact.android_sdk_group) { - const std::string& group_name = artifact.android_sdk_group.value(); - auto group = config.android_sdk_groups.find(group_name); - // TODO: Remove validation when configuration parser ensures referential integrity. - if (group == config.android_sdk_groups.end()) { - context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced group '" - << group_name << "'"); - return {}; - } - - const AndroidSdk& sdk = group->second; - if (!sdk.min_sdk_version) { - context_->GetDiagnostics()->Error(DiagMessage() - << "skipping SDK version. No min SDK: " << group_name); - return {}; - } - - ConfigDescription c; - const std::string& version = sdk.min_sdk_version.value(); - if (!ConfigDescription::Parse(version, &c)) { - context_->GetDiagnostics()->Error(DiagMessage() << "could not parse min SDK: " << version); - return {}; - } - - wrappedContext.SetMinSdkVersion(c.sdkVersion); + Maybe<AndroidSdk> sdk = GetAndroidSdk(artifact, config, context_->GetDiagnostics()); + if (sdk && sdk.value().min_sdk_version) { + wrappedContext.SetMinSdkVersion(sdk.value().min_sdk_version.value()); } std::unique_ptr<ResourceTable> table = old_table.Clone(); VersionCollapser collapser; - if (!collapser.Consume(context_, table.get())) { + if (!collapser.Consume(&wrappedContext, table.get())) { context_->GetDiagnostics()->Error(DiagMessage() << "Failed to strip versioned resources"); return {}; } diff --git a/tools/aapt2/optimize/MultiApkGenerator_test.cpp b/tools/aapt2/optimize/MultiApkGenerator_test.cpp index 23f573cd52a5..e8e6adc490e9 100644 --- a/tools/aapt2/optimize/MultiApkGenerator_test.cpp +++ b/tools/aapt2/optimize/MultiApkGenerator_test.cpp @@ -50,14 +50,6 @@ using ::testing::Return; using ::testing::Test; using ::testing::_; -/** Subclass the LoadedApk class so that we can mock the WriteToArchive method. */ -class MockApk : public LoadedApk { - public: - MockApk(std::unique_ptr<ResourceTable> table) : LoadedApk({"test.apk"}, {}, std::move(table)){}; - MOCK_METHOD5(WriteToArchive, bool(IAaptContext*, ResourceTable*, const TableFlattenerOptions&, - FilterChain*, IArchiveWriter*)); -}; - /** * Subclass the MultiApkGenerator class so that we can access the protected FilterTable method to * directly test table filter. @@ -111,54 +103,10 @@ class MultiApkGeneratorTest : public ::testing::Test { ConfigDescription v21_ = ParseConfigOrDie("v21"); }; -TEST_F(MultiApkGeneratorTest, FromBaseApk) { - std::unique_ptr<ResourceTable> table = BuildTable(); - - MockApk apk{std::move(table)}; - - EXPECT_CALL(apk, WriteToArchive(_, _, _, _, _)).Times(0); - - test::Context ctx; - PostProcessingConfiguration empty_config; - TableFlattenerOptions table_flattener_options; - - MultiApkGenerator generator{&apk, &ctx}; - EXPECT_TRUE(generator.FromBaseApk({"out", empty_config, table_flattener_options})); - - Artifact x64 = test::ArtifactBuilder() - .SetName("${basename}.x64.apk") - .SetAbiGroup("x64") - .SetLocaleGroup("en") - .SetDensityGroup("xhdpi") - .Build(); - - Artifact intel = test::ArtifactBuilder() - .SetName("${basename}.intel.apk") - .SetAbiGroup("intel") - .SetLocaleGroup("europe") - .SetDensityGroup("large") - .Build(); - - auto config = test::PostProcessingConfigurationBuilder() - .SetLocaleGroup("en", {"en"}) - .SetLocaleGroup("europe", {"en", "fr", "de", "es"}) - .SetAbiGroup("x64", {Abi::kX86_64}) - .SetAbiGroup("intel", {Abi::kX86_64, Abi::kX86}) - .SetDensityGroup("xhdpi", {"xhdpi"}) - .SetDensityGroup("large", {"xhdpi", "xxhdpi", "xxxhdpi"}) - .AddArtifact(x64) - .AddArtifact(intel) - .Build(); - - // Called once for each artifact. - EXPECT_CALL(apk, WriteToArchive(Eq(&ctx), _, _, _, _)).Times(2).WillRepeatedly(Return(true)); - EXPECT_TRUE(generator.FromBaseApk({"out", config, table_flattener_options})); -} - TEST_F(MultiApkGeneratorTest, VersionFilterNewerVersion) { std::unique_ptr<ResourceTable> table = BuildTable(); - MockApk apk{std::move(table)}; + LoadedApk apk = {{"test.apk"}, {}, std::move(table)}; std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(19).Build(); PostProcessingConfiguration empty_config; TableFlattenerOptions table_flattener_options; @@ -174,7 +122,7 @@ TEST_F(MultiApkGeneratorTest, VersionFilterNewerVersion) { .SetLocaleGroup("en", {"en"}) .SetAbiGroup("x64", {Abi::kX86_64}) .SetDensityGroup("xhdpi", {"xhdpi"}) - .SetAndroidSdk("v23", AndroidSdk::ForMinSdk("v23")) + .SetAndroidSdk("v23", AndroidSdk::ForMinSdk(23)) .AddArtifact(x64) .Build(); @@ -199,7 +147,7 @@ TEST_F(MultiApkGeneratorTest, VersionFilterNewerVersion) { TEST_F(MultiApkGeneratorTest, VersionFilterOlderVersion) { std::unique_ptr<ResourceTable> table = BuildTable(); - MockApk apk{std::move(table)}; + LoadedApk apk = {{"test.apk"}, {}, std::move(table)}; std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build(); PostProcessingConfiguration empty_config; TableFlattenerOptions table_flattener_options; @@ -215,7 +163,7 @@ TEST_F(MultiApkGeneratorTest, VersionFilterOlderVersion) { .SetLocaleGroup("en", {"en"}) .SetAbiGroup("x64", {Abi::kX86_64}) .SetDensityGroup("xhdpi", {"xhdpi"}) - .SetAndroidSdk("v4", AndroidSdk::ForMinSdk("v4")) + .SetAndroidSdk("v4", AndroidSdk::ForMinSdk(4)) .AddArtifact(x64) .Build(); @@ -238,7 +186,7 @@ TEST_F(MultiApkGeneratorTest, VersionFilterOlderVersion) { TEST_F(MultiApkGeneratorTest, VersionFilterNoVersion) { std::unique_ptr<ResourceTable> table = BuildTable(); - MockApk apk{std::move(table)}; + LoadedApk apk = {{"test.apk"}, {}, std::move(table)}; std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build(); PostProcessingConfiguration empty_config; TableFlattenerOptions table_flattener_options; |