AAPT2: Embed version of framework an app was compiled against.
This change injects the 'android:compileSdkVersion' and
'android:compileSdkVersionCodename' into an app's AndroidManifest.xml,
whose values are dervied from the version name and version code of the
framework AndroidManifest.xml that is being linked against.
Bug: 63388434
Test: make aapt2_tests
Change-Id: I7b607192ecb337307c4bcb5770e7f716c6d4c9d2
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index d2aebfd..13dd93e 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -15,6 +15,7 @@
*/
#include <sys/stat.h>
+#include <cinttypes>
#include <queue>
#include <unordered_map>
@@ -725,6 +726,30 @@
return true;
}
+static int32_t FindFrameworkAssetManagerCookie(const android::AssetManager& assets) {
+ using namespace android;
+
+ // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so
+ // we're looking for the first attribute resource in the system package.
+ const ResTable& table = assets.getResources(true);
+ Res_value val;
+ ssize_t idx = table.getResource(0x01010000, &val, true);
+ if (idx != NO_ERROR) {
+ // Try as a bag.
+ const ResTable::bag_entry* entry;
+ ssize_t cnt = table.lockBag(0x01010000, &entry);
+ if (cnt >= 0) {
+ idx = entry->stringBlock;
+ }
+ table.unlockBag(entry);
+ }
+
+ if (idx < 0) {
+ return 0;
+ }
+ return table.getTableCookie(idx);
+}
+
class LinkCommand {
public:
LinkCommand(LinkContext* context, const LinkOptions& options)
@@ -734,7 +759,65 @@
file_collection_(util::make_unique<io::FileCollection>()) {
}
+ void ExtractCompileSdkVersions(android::AssetManager* assets) {
+ using namespace android;
+
+ int32_t cookie = FindFrameworkAssetManagerCookie(*assets);
+ if (cookie == 0) {
+ // No Framework assets loaded. Not a failure.
+ return;
+ }
+
+ std::unique_ptr<Asset> manifest(
+ assets->openNonAsset(cookie, kAndroidManifestPath, Asset::AccessMode::ACCESS_BUFFER));
+ if (manifest == nullptr) {
+ // No errors.
+ return;
+ }
+
+ std::string error;
+ std::unique_ptr<xml::XmlResource> manifest_xml =
+ xml::Inflate(manifest->getBuffer(true /*wordAligned*/), manifest->getLength(), &error);
+ if (manifest_xml == nullptr) {
+ // No errors.
+ return;
+ }
+
+ xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionCode");
+ if (attr != nullptr) {
+ Maybe<std::string>& compile_sdk_version = options_.manifest_fixer_options.compile_sdk_version;
+ if (BinaryPrimitive* prim = ValueCast<BinaryPrimitive>(attr->compiled_value.get())) {
+ switch (prim->value.dataType) {
+ case Res_value::TYPE_INT_DEC:
+ compile_sdk_version = StringPrintf("%" PRId32, static_cast<int32_t>(prim->value.data));
+ break;
+ case Res_value::TYPE_INT_HEX:
+ compile_sdk_version = StringPrintf("%" PRIx32, prim->value.data);
+ break;
+ default:
+ break;
+ }
+ } else if (String* str = ValueCast<String>(attr->compiled_value.get())) {
+ compile_sdk_version = *str->value;
+ } else {
+ compile_sdk_version = attr->value;
+ }
+ }
+
+ attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionName");
+ if (attr != nullptr) {
+ Maybe<std::string>& compile_sdk_version_codename =
+ options_.manifest_fixer_options.compile_sdk_version_codename;
+ if (String* str = ValueCast<String>(attr->compiled_value.get())) {
+ compile_sdk_version_codename = *str->value;
+ } else {
+ compile_sdk_version_codename = attr->value;
+ }
+ }
+ }
+
// Creates a SymbolTable that loads symbols from the various APKs.
+ // Pre-condition: context_->GetCompilationPackage() needs to be set.
bool LoadSymbolsFromIncludePaths() {
auto asset_source = util::make_unique<AssetManagerSymbolSource>();
for (const std::string& path : options_.include_paths) {
@@ -802,6 +885,17 @@
} else if (entry.first == kAppPackageId) {
// Capture the included base feature package.
included_feature_base_ = entry.second;
+ } else if (entry.first == kFrameworkPackageId) {
+ // Try to embed which version of the framework we're compiling against.
+ // First check if we should use compileSdkVersion at all. Otherwise compilation may fail
+ // when linking our synthesized 'android:compileSdkVersion' attribute.
+ std::unique_ptr<SymbolTable::Symbol> symbol = asset_source->FindByName(
+ ResourceName("android", ResourceType::kAttr, "compileSdkVersion"));
+ if (symbol != nullptr && symbol->is_public) {
+ // The symbol is present and public, extract the android:versionName and
+ // android:versionCode from the framework AndroidManifest.xml.
+ ExtractCompileSdkVersions(asset_source->GetAssetManager());
+ }
}
}
@@ -1535,6 +1629,12 @@
context_->SetCompilationPackage(app_info.package);
}
+ // Now that the compilation package is set, load the dependencies. This will also extract
+ // the Android framework's versionCode and versionName, if they exist.
+ if (!LoadSymbolsFromIncludePaths()) {
+ return 1;
+ }
+
ManifestFixer manifest_fixer(options_.manifest_fixer_options);
if (!manifest_fixer.Consume(context_, manifest_xml.get())) {
return 1;
@@ -1563,10 +1663,6 @@
}
}
- if (!LoadSymbolsFromIncludePaths()) {
- return 1;
- }
-
TableMergerOptions table_merger_options;
table_merger_options.auto_add_overlay = options_.auto_add_overlay;
table_merger_ = util::make_unique<TableMerger>(context_, &final_table_, table_merger_options);