Add <staging-public-group-final> to aapt2
To allow apps that compiled against a pre-release SDK to continue
working for a period of time after API finalization, a new tag,
<staging-public-group-final>, has been added to aapt2.
When finalizing the framework resource API, converting
<staging-public-group> tags to <staging-public-group-final> will
cause aapt2 to generate the resource table so that there is a resource
entry for the old non-finalized (staged) resource ID and another entry
for the finalized resource ID of newly finalized resources. This allows
an application that compiled against the pre-release SDK to continue
resolving resources using pre-release resource IDs.
All references to pre-release resource IDs will be rewritten to their
finalized resource IDs through the information stored in the new staged
alias chunk. This allows applications compiled against
<staging-public-group> resources to use the newly finalized
resource ID without re-compilation.
When an application is re-compiled against the SDK with
<staging-public-group-final> tags, the application will use the
finalized resource IDs.
This change limits the use of the alias chunk to the framework for S.
Bug: 183411356
Test: aapt2_test
Change-Id: Iba1c3033c3c2f32de8e4a19b58d3921c971092c4
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 7e45f95..7d061fb 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -221,6 +221,16 @@
for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) {
iter2->dynamic_ref_table->addMapping(String16(package_name.c_str(), package_name.size()),
iter->dynamic_ref_table->mAssignedPackageId);
+
+ // Add the alias resources to the dynamic reference table of every package group. Since
+ // staging aliases can only be defined by the framework package (which is not a shared
+ // library), the compile-time package id of the framework is the same across all packages
+ // that compile against the framework.
+ for (const auto& package : iter2->packages_) {
+ for (const auto& entry : package.loaded_package_->GetAliasResourceIdMap()) {
+ iter->dynamic_ref_table->addAlias(entry.first, entry.second);
+ }
+ }
}
}
}
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index cb620cc..d17c328 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -41,6 +41,7 @@
namespace android {
+constexpr const static int kFrameworkPackageId = 0x01;
constexpr const static int kAppPackageId = 0x7f;
namespace {
@@ -675,6 +676,42 @@
}
} break;
+ case RES_TABLE_STAGED_ALIAS_TYPE: {
+ if (loaded_package->package_id_ != kFrameworkPackageId) {
+ LOG(WARNING) << "Alias chunk ignored for non-framework package '"
+ << loaded_package->package_name_ << "'";
+ break;
+ }
+
+ std::unordered_set<uint32_t> finalized_ids;
+ const auto lib_alias = child_chunk.header<ResTable_staged_alias_header>();
+ if (!lib_alias) {
+ return {};
+ }
+ const auto entry_begin = child_chunk.data_ptr().convert<ResTable_staged_alias_entry>();
+ const auto entry_end = entry_begin + dtohl(lib_alias->count);
+ for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) {
+ if (!entry_iter) {
+ return {};
+ }
+ auto finalized_id = dtohl(entry_iter->finalizedResId);
+ if (!finalized_ids.insert(finalized_id).second) {
+ LOG(ERROR) << StringPrintf("Repeated finalized resource id '%08x' in staged aliases.",
+ finalized_id);
+ return {};
+ }
+
+ auto staged_id = dtohl(entry_iter->stagedResId);
+ auto [_, success] = loaded_package->alias_id_map_.insert(std::make_pair(staged_id,
+ finalized_id));
+ if (!success) {
+ LOG(ERROR) << StringPrintf("Repeated staged resource id '%08x' in staged aliases.",
+ staged_id);
+ return {};
+ }
+ }
+ } break;
+
default:
LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
break;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 30500ab..cae2d0b 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -7079,6 +7079,10 @@
mLookupTable[buildPackageId] = runtimePackageId;
}
+void DynamicRefTable::addAlias(uint32_t stagedId, uint32_t finalizedId) {
+ mAliasId[stagedId] = finalizedId;
+}
+
status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
uint32_t res = *resId;
size_t packageId = Res_GETPACKAGE(res) + 1;
@@ -7088,8 +7092,16 @@
return NO_ERROR;
}
- if (packageId == APP_PACKAGE_ID && !mAppAsLib) {
- // No lookup needs to be done, app package IDs are absolute.
+ auto alias_id = mAliasId.find(res);
+ if (alias_id != mAliasId.end()) {
+ // Rewrite the resource id to its alias resource id. Since the alias resource id is a
+ // compile-time id, it still needs to be resolved further.
+ res = alias_id->second;
+ }
+
+ if (packageId == SYS_PACKAGE_ID || (packageId == APP_PACKAGE_ID && !mAppAsLib)) {
+ // No lookup needs to be done, app and framework package IDs are absolute.
+ *resId = res;
return NO_ERROR;
}
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 3b222c5..9bbdede 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -17,6 +17,7 @@
#ifndef LOADEDARSC_H_
#define LOADEDARSC_H_
+#include <map>
#include <memory>
#include <set>
#include <vector>
@@ -171,51 +172,51 @@
incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset);
// Returns the string pool where type names are stored.
- inline const ResStringPool* GetTypeStringPool() const {
+ const ResStringPool* GetTypeStringPool() const {
return &type_string_pool_;
}
// Returns the string pool where the names of resource entries are stored.
- inline const ResStringPool* GetKeyStringPool() const {
+ const ResStringPool* GetKeyStringPool() const {
return &key_string_pool_;
}
- inline const std::string& GetPackageName() const {
+ const std::string& GetPackageName() const {
return package_name_;
}
- inline int GetPackageId() const {
+ int GetPackageId() const {
return package_id_;
}
// Returns true if this package is dynamic (shared library) and needs to have an ID assigned.
- inline bool IsDynamic() const {
+ bool IsDynamic() const {
return (property_flags_ & PROPERTY_DYNAMIC) != 0;
}
// Returns true if this package is a Runtime Resource Overlay.
- inline bool IsOverlay() const {
+ bool IsOverlay() const {
return (property_flags_ & PROPERTY_OVERLAY) != 0;
}
// Returns true if this package originates from a system provided resource.
- inline bool IsSystem() const {
+ bool IsSystem() const {
return (property_flags_ & PROPERTY_SYSTEM) != 0;
}
// Returns true if this package is a custom loader and should behave like an overlay.
- inline bool IsCustomLoader() const {
+ bool IsCustomLoader() const {
return (property_flags_ & PROPERTY_LOADER) != 0;
}
- inline package_property_t GetPropertyFlags() const {
+ package_property_t GetPropertyFlags() const {
return property_flags_;
}
// Returns the map of package name to package ID used in this LoadedPackage. At runtime, a
// package could have been assigned a different package ID than what this LoadedPackage was
// compiled with. AssetManager rewrites the package IDs so that they are compatible at runtime.
- inline const std::vector<DynamicPackageEntry>& GetDynamicPackageMap() const {
+ const std::vector<DynamicPackageEntry>& GetDynamicPackageMap() const {
return dynamic_package_map_;
}
@@ -270,6 +271,10 @@
return overlayable_map_;
}
+ const std::map<uint32_t, uint32_t>& GetAliasResourceIdMap() const {
+ return alias_id_map_;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
@@ -287,6 +292,7 @@
ByteBucketArray<uint32_t> resource_ids_;
std::vector<DynamicPackageEntry> dynamic_package_map_;
std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;
+ std::map<uint32_t, uint32_t> alias_id_map_;
// A map of overlayable name to actor
std::unordered_map<std::string, std::string> overlayable_map_;
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 17c1404..3d66244 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -39,6 +39,7 @@
#include <android/configuration.h>
#include <array>
+#include <map>
#include <memory>
namespace android {
@@ -229,30 +230,31 @@
};
enum {
- RES_NULL_TYPE = 0x0000,
- RES_STRING_POOL_TYPE = 0x0001,
- RES_TABLE_TYPE = 0x0002,
- RES_XML_TYPE = 0x0003,
+ RES_NULL_TYPE = 0x0000,
+ RES_STRING_POOL_TYPE = 0x0001,
+ RES_TABLE_TYPE = 0x0002,
+ RES_XML_TYPE = 0x0003,
// Chunk types in RES_XML_TYPE
- RES_XML_FIRST_CHUNK_TYPE = 0x0100,
- RES_XML_START_NAMESPACE_TYPE= 0x0100,
- RES_XML_END_NAMESPACE_TYPE = 0x0101,
- RES_XML_START_ELEMENT_TYPE = 0x0102,
- RES_XML_END_ELEMENT_TYPE = 0x0103,
- RES_XML_CDATA_TYPE = 0x0104,
- RES_XML_LAST_CHUNK_TYPE = 0x017f,
+ RES_XML_FIRST_CHUNK_TYPE = 0x0100,
+ RES_XML_START_NAMESPACE_TYPE = 0x0100,
+ RES_XML_END_NAMESPACE_TYPE = 0x0101,
+ RES_XML_START_ELEMENT_TYPE = 0x0102,
+ RES_XML_END_ELEMENT_TYPE = 0x0103,
+ RES_XML_CDATA_TYPE = 0x0104,
+ RES_XML_LAST_CHUNK_TYPE = 0x017f,
// This contains a uint32_t array mapping strings in the string
// pool back to resource identifiers. It is optional.
- RES_XML_RESOURCE_MAP_TYPE = 0x0180,
+ RES_XML_RESOURCE_MAP_TYPE = 0x0180,
// Chunk types in RES_TABLE_TYPE
- RES_TABLE_PACKAGE_TYPE = 0x0200,
- RES_TABLE_TYPE_TYPE = 0x0201,
- RES_TABLE_TYPE_SPEC_TYPE = 0x0202,
- RES_TABLE_LIBRARY_TYPE = 0x0203,
- RES_TABLE_OVERLAYABLE_TYPE = 0x0204,
+ RES_TABLE_PACKAGE_TYPE = 0x0200,
+ RES_TABLE_TYPE_TYPE = 0x0201,
+ RES_TABLE_TYPE_SPEC_TYPE = 0x0202,
+ RES_TABLE_LIBRARY_TYPE = 0x0203,
+ RES_TABLE_OVERLAYABLE_TYPE = 0x0204,
RES_TABLE_OVERLAYABLE_POLICY_TYPE = 0x0205,
+ RES_TABLE_STAGED_ALIAS_TYPE = 0x0206,
};
/**
@@ -1639,6 +1641,29 @@
};
/**
+ * A map that allows rewriting staged (non-finalized) resource ids to their finalized counterparts.
+ */
+struct ResTable_staged_alias_header
+{
+ struct ResChunk_header header;
+
+ // The number of ResTable_staged_alias_entry that follow this header.
+ uint32_t count;
+};
+
+/**
+ * Maps the staged (non-finalized) resource id to its finalized resource id.
+ */
+struct ResTable_staged_alias_entry
+{
+ // The compile-time staged resource id to rewrite.
+ uint32_t stagedResId;
+
+ // The compile-time finalized resource id to which the staged resource id should be rewritten.
+ uint32_t finalizedResId;
+};
+
+/**
* Specifies the set of resources that are explicitly allowed to be overlaid by RROs.
*/
struct ResTable_overlayable_header
@@ -1751,6 +1776,8 @@
void addMapping(uint8_t buildPackageId, uint8_t runtimePackageId);
+ void addAlias(uint32_t stagedId, uint32_t finalizedId);
+
// Returns whether or not the value must be looked up.
bool requiresLookup(const Res_value* value) const;
@@ -1768,6 +1795,7 @@
uint8_t mLookupTable[256];
KeyedVector<String16, uint8_t> mEntries;
bool mAppAsLib;
+ std::map<uint32_t, uint32_t> mAliasId;
};
bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue);