diff options
| -rw-r--r-- | core/java/android/content/res/AssetManager.java | 14 | ||||
| -rw-r--r-- | core/java/android/content/res/ResourcesImpl.java | 2 | ||||
| -rw-r--r-- | core/jni/android_util_AssetManager.cpp | 25 | ||||
| -rw-r--r-- | libs/androidfw/AssetManager2.cpp | 243 | ||||
| -rw-r--r-- | libs/androidfw/include/androidfw/AssetManager2.h | 12 | ||||
| -rw-r--r-- | libs/androidfw/tests/Theme_test.cpp | 39 | ||||
| -rw-r--r-- | libs/androidfw/tests/data/lib_two/R.h | 12 | ||||
| -rw-r--r-- | libs/androidfw/tests/data/lib_two/lib_two.apk | bin | 1106 -> 1426 bytes | |||
| -rw-r--r-- | libs/androidfw/tests/data/lib_two/res/values/values.xml | 8 |
9 files changed, 313 insertions, 42 deletions
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 0350effd84b8..5f2374995775 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -1064,6 +1064,17 @@ public final class AssetManager implements AutoCloseable { } } + @UnsupportedAppUsage + void setThemeTo(long dstThemePtr, @NonNull AssetManager srcAssetManager, long srcThemePtr) { + synchronized (this) { + ensureValidLocked(); + synchronized (srcAssetManager) { + srcAssetManager.ensureValidLocked(); + nativeThemeCopy(mObject, dstThemePtr, srcAssetManager.mObject, srcThemePtr); + } + } + } + @Override protected void finalize() throws Throwable { if (DEBUG_REFS && mNumRefs != 0) { @@ -1375,7 +1386,8 @@ public final class AssetManager implements AutoCloseable { private static native void nativeThemeDestroy(long themePtr); private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, boolean force); - static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr); + private static native void nativeThemeCopy(long dstAssetManagerPtr, long dstThemePtr, + long srcAssetManagerPtr, long srcThemePtr); static native void nativeThemeClear(long themePtr); private static native int nativeThemeGetAttributeValue(long ptr, long themePtr, @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve); diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 1d1eaf3e4286..2ad4f625ef8c 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -1336,7 +1336,7 @@ public class ResourcesImpl { void setTo(ThemeImpl other) { synchronized (mKey) { synchronized (other.mKey) { - AssetManager.nativeThemeCopy(mTheme, other.mTheme); + mAssets.setThemeTo(mTheme, other.mAssets, other.mTheme); mThemeResId = other.mThemeResId; mKey.setTo(other.getKey()); diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 830ca832fb95..b2d44e73d861 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1208,13 +1208,23 @@ static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlon // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str()); } -static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr, - jlong src_theme_ptr) { +static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_asset_manager_ptr, + jlong dst_theme_ptr, jlong src_asset_manager_ptr, jlong src_theme_ptr) { Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr); Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr); - if (!dst_theme->SetTo(*src_theme)) { - jniThrowException(env, "java/lang/IllegalArgumentException", - "Themes are from different AssetManagers"); + + if (dst_asset_manager_ptr != src_asset_manager_ptr) { + ScopedLock<AssetManager2> dst_assetmanager(AssetManagerFromLong(dst_asset_manager_ptr)); + CHECK(dst_theme->GetAssetManager() == &(*dst_assetmanager)); + (void) dst_assetmanager; + + ScopedLock <AssetManager2> src_assetmanager(AssetManagerFromLong(src_asset_manager_ptr)); + CHECK(src_theme->GetAssetManager() == &(*src_assetmanager)); + (void) src_assetmanager; + + dst_theme->SetTo(*src_theme); + } else { + dst_theme->SetTo(*src_theme); } } @@ -1255,10 +1265,11 @@ static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong Theme* theme = reinterpret_cast<Theme*>(theme_ptr); CHECK(theme->GetAssetManager() == &(*assetmanager)); (void) assetmanager; - (void) theme; (void) priority; (void) tag; (void) prefix; + + theme->Dump(); } static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/, @@ -1377,7 +1388,7 @@ static const JNINativeMethod gAssetManagerMethods[] = { {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate}, {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy}, {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle}, - {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy}, + {"nativeThemeCopy", "(JJJJ)V", (void*)NativeThemeCopy}, {"nativeThemeClear", "(J)V", (void*)NativeThemeClear}, {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I", (void*)NativeThemeGetAttributeValue}, diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 04cc5bb30ade..288ba32c47a6 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -21,6 +21,7 @@ #include <algorithm> #include <iterator> #include <set> +#include <map> #include "android-base/logging.h" #include "android-base/stringprintf.h" @@ -869,6 +870,17 @@ void AssetManager2::InvalidateCaches(uint32_t diff) { } } +uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) { + for (auto& package_group : package_groups_) { + for (auto& package2 : package_group.packages_) { + if (package2.loaded_package_ == package) { + return package_group.dynamic_ref_table.mAssignedPackageId; + } + } + } + return 0; +} + std::unique_ptr<Theme> AssetManager2::NewTheme() { return std::unique_ptr<Theme>(new Theme(this)); } @@ -1054,44 +1066,231 @@ void Theme::Clear() { } } -bool Theme::SetTo(const Theme& o) { +void Theme::SetTo(const Theme& o) { if (this == &o) { - return true; + return; } type_spec_flags_ = o.type_spec_flags_; - const bool copy_only_system = asset_manager_ != o.asset_manager_; + if (asset_manager_ == o.asset_manager_) { + // The theme comes from the same asset manager so all theme data can be copied exactly + for (size_t p = 0; p < packages_.size(); p++) { + const Package *package = o.packages_[p].get(); + if (package == nullptr) { + // The other theme doesn't have this package, clear ours. + packages_[p].reset(); + continue; + } + + if (packages_[p] == nullptr) { + // The other theme has this package, but we don't. Make one. + packages_[p].reset(new Package()); + } - for (size_t p = 0; p < packages_.size(); p++) { - const Package* package = o.packages_[p].get(); - if (package == nullptr || (copy_only_system && p != 0x01)) { - // The other theme doesn't have this package, clear ours. - packages_[p].reset(); - continue; + for (size_t t = 0; t < package->types.size(); t++) { + const ThemeType *type = package->types[t].get(); + if (type == nullptr) { + // The other theme doesn't have this type, clear ours. + packages_[p]->types[t].reset(); + continue; + } + + // Create a new type and update it to theirs. + const size_t type_alloc_size = sizeof(ThemeType) + (type->entry_count * sizeof(ThemeEntry)); + void *copied_data = malloc(type_alloc_size); + memcpy(copied_data, type, type_alloc_size); + packages_[p]->types[t].reset(reinterpret_cast<ThemeType *>(copied_data)); + } + } + } else { + std::map<ApkAssetsCookie, ApkAssetsCookie> src_to_dest_asset_cookies; + typedef std::map<int, int> SourceToDestinationRuntimePackageMap; + std::map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map; + + // Determine which ApkAssets are loaded in both theme AssetManagers + std::vector<const ApkAssets*> src_assets = o.asset_manager_->GetApkAssets(); + for (size_t i = 0; i < src_assets.size(); i++) { + const ApkAssets* src_asset = src_assets[i]; + + std::vector<const ApkAssets*> dest_assets = asset_manager_->GetApkAssets(); + for (size_t j = 0; j < dest_assets.size(); j++) { + const ApkAssets* dest_asset = dest_assets[j]; + + // Map the runtime package of the source apk asset to the destination apk asset + if (src_asset->GetPath() == dest_asset->GetPath()) { + const std::vector<std::unique_ptr<const LoadedPackage>>& src_packages = + src_asset->GetLoadedArsc()->GetPackages(); + const std::vector<std::unique_ptr<const LoadedPackage>>& dest_packages = + dest_asset->GetLoadedArsc()->GetPackages(); + + SourceToDestinationRuntimePackageMap package_map; + + // The source and destination package should have the same number of packages loaded in + // the same order. + const size_t N = src_packages.size(); + CHECK(N == dest_packages.size()) + << " LoadedArsc " << src_asset->GetPath() << " differs number of packages."; + for (size_t p = 0; p < N; p++) { + auto& src_package = src_packages[p]; + auto& dest_package = dest_packages[p]; + CHECK(src_package->GetPackageName() == dest_package->GetPackageName()) + << " Package " << src_package->GetPackageName() << " differs in load order."; + + int src_package_id = o.asset_manager_->GetAssignedPackageId(src_package.get()); + int dest_package_id = asset_manager_->GetAssignedPackageId(dest_package.get()); + package_map[src_package_id] = dest_package_id; + } + + src_to_dest_asset_cookies.insert(std::pair<ApkAssetsCookie, ApkAssetsCookie>(i, j)); + src_asset_cookie_id_map.insert( + std::pair<ApkAssetsCookie, SourceToDestinationRuntimePackageMap>(i, package_map)); + break; + } + } + } + + // Reset the data in the destination theme + for (size_t p = 0; p < packages_.size(); p++) { + if (packages_[p] != nullptr) { + packages_[p].reset(); + } + } + + for (size_t p = 0; p < packages_.size(); p++) { + const Package *package = o.packages_[p].get(); + if (package == nullptr) { + continue; + } + + for (size_t t = 0; t < package->types.size(); t++) { + const ThemeType *type = package->types[t].get(); + if (type == nullptr) { + continue; + } + + for (size_t e = 0; e < type->entry_count; e++) { + const ThemeEntry &entry = type->entries[e]; + if (entry.value.dataType == Res_value::TYPE_NULL && + entry.value.data != Res_value::DATA_NULL_EMPTY) { + continue; + } + + // The package id of the attribute needs to be rewritten to the package id of the value in + // the destination + int attribute_dest_package_id = p; + if (attribute_dest_package_id != 0x01) { + // Find the cookie of the attribute resource id + FindEntryResult attribute_entry_result; + ApkAssetsCookie attribute_cookie = + o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ , false, + &attribute_entry_result); + + // Determine the package id of the attribute in the destination AssetManager + auto attribute_package_map = src_asset_cookie_id_map.find(attribute_cookie); + if (attribute_package_map == src_asset_cookie_id_map.end()) { + continue; + } + auto attribute_dest_package = attribute_package_map->second.find( + attribute_dest_package_id); + if (attribute_dest_package == attribute_package_map->second.end()) { + continue; + } + attribute_dest_package_id = attribute_dest_package->second; + } + + // If the attribute value represents an attribute or reference, the package id of the + // value needs to be rewritten to the package id of the value in the destination + uint32_t attribue_data = entry.value.data; + if (entry.value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE + || entry.value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE + || entry.value.dataType == Res_value::TYPE_ATTRIBUTE + || entry.value.dataType == Res_value::TYPE_REFERENCE) { + + // Determine the package id of the reference in the destination AssetManager + auto value_package_map = src_asset_cookie_id_map.find(entry.cookie); + if (value_package_map == src_asset_cookie_id_map.end()) { + continue; + } + + auto value_dest_package = value_package_map->second.find( + get_package_id(entry.value.data)); + if (value_dest_package == value_package_map->second.end()) { + continue; + } + + attribue_data = fix_package_id(entry.value.data, value_dest_package->second); + } + + // Lazily instantiate the destination package + std::unique_ptr<Package>& dest_package = packages_[attribute_dest_package_id]; + if (dest_package == nullptr) { + dest_package.reset(new Package()); + } + + // Lazily instantiate and resize the destination type + util::unique_cptr<ThemeType>& dest_type = dest_package->types[t]; + if (dest_type == nullptr || dest_type->entry_count < type->entry_count) { + const size_t type_alloc_size = sizeof(ThemeType) + + (type->entry_count * sizeof(ThemeEntry)); + void* dest_data = malloc(type_alloc_size); + memset(dest_data, 0, type->entry_count * sizeof(ThemeEntry)); + + // Copy the existing destination type values if the type is resized + if (dest_type != nullptr) { + memcpy(dest_data, type, sizeof(ThemeType) + + (dest_type->entry_count * sizeof(ThemeEntry))); + } + + dest_type.reset(reinterpret_cast<ThemeType *>(dest_data)); + dest_type->entry_count = type->entry_count; + } + + // Find the cookie of the value in the destination + auto value_dest_cookie = src_to_dest_asset_cookies.find(entry.cookie); + if (value_dest_cookie == src_to_dest_asset_cookies.end()) { + continue; + } + + dest_type->entries[e].cookie = value_dest_cookie->second; + dest_type->entries[e].value.dataType = entry.value.dataType; + dest_type->entries[e].value.data = attribue_data; + dest_type->entries[e].type_spec_flags = entry.type_spec_flags; + } + } } + } +} + +void Theme::Dump() const { + base::ScopedLogSeverity _log(base::INFO); + LOG(INFO) << base::StringPrintf("Theme(this=%p, AssetManager2=%p)", this, asset_manager_); - if (packages_[p] == nullptr) { - // The other theme has this package, but we don't. Make one. - packages_[p].reset(new Package()); + for (int p = 0; p < packages_.size(); p++) { + auto& package = packages_[p]; + if (package == nullptr) { + continue; } - for (size_t t = 0; t < package->types.size(); t++) { - const ThemeType* type = package->types[t].get(); + for (int t = 0; t < package->types.size(); t++) { + auto& type = package->types[t]; if (type == nullptr) { - // The other theme doesn't have this type, clear ours. - packages_[p]->types[t].reset(); continue; } - // Create a new type and update it to theirs. - const size_t type_alloc_size = sizeof(ThemeType) + (type->entry_count * sizeof(ThemeEntry)); - void* copied_data = malloc(type_alloc_size); - memcpy(copied_data, type, type_alloc_size); - packages_[p]->types[t].reset(reinterpret_cast<ThemeType*>(copied_data)); + for (int e = 0; e < type->entry_count; e++) { + auto& entry = type->entries[e]; + if (entry.value.dataType == Res_value::TYPE_NULL && + entry.value.data != Res_value::DATA_NULL_EMPTY) { + continue; + } + + LOG(INFO) << base::StringPrintf(" entry(0x%08x)=(0x%08x) type=(0x%02x), cookie(%d)", + make_resid(p, t, e), entry.value.data, + entry.value.dataType, entry.cookie); + } } } - return true; } } // namespace android diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 2f0ee01639fe..5312b062473a 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -74,6 +74,8 @@ struct FindEntryResult; // AssetManager2 is the main entry point for accessing assets and resources. // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets. class AssetManager2 { + friend Theme; + public: struct ResourceName { const char* package = nullptr; @@ -285,6 +287,9 @@ class AssetManager2 { // been seen while traversing bag parents. const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids); + // Retrieve the assigned package id of the package if loaded into this AssetManager + uint8_t GetAssignedPackageId(const LoadedPackage* package); + // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector<const ApkAssets*> apk_assets_; @@ -355,11 +360,14 @@ class Theme { bool ApplyStyle(uint32_t resid, bool force = false); // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme. - // Returns false if the AssetManagers of the Themes were not compatible. - bool SetTo(const Theme& o); + // If `o` does not have the same AssetManager as this theme, only attributes from ApkAssets loaded + // into both AssetManagers will be copied to this theme. + void SetTo(const Theme& o); void Clear(); + void Dump() const; + inline const AssetManager2* GetAssetManager() const { return asset_manager_; } diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp index 55d53edf6a2b..2c39ceead123 100644 --- a/libs/androidfw/tests/Theme_test.cpp +++ b/libs/androidfw/tests/Theme_test.cpp @@ -21,12 +21,14 @@ #include "TestHelpers.h" #include "androidfw/ResourceUtils.h" #include "data/lib_one/R.h" +#include "data/lib_two/R.h" #include "data/libclient/R.h" #include "data/styles/R.h" #include "data/system/R.h" namespace app = com::android::app; namespace lib_one = com::android::lib_one; +namespace lib_two = com::android::lib_two; namespace libclient = com::android::libclient; namespace android { @@ -263,7 +265,7 @@ TEST_F(ThemeTest, CopyThemeSameAssetManager) { ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree)); // Copy the theme to theme_one. - ASSERT_TRUE(theme_one->SetTo(*theme_two)); + theme_one->SetTo(*theme_two); // Clear theme_two to make sure we test that there WAS a copy. theme_two->Clear(); @@ -279,12 +281,14 @@ TEST_F(ThemeTest, CopyThemeSameAssetManager) { EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); } -TEST_F(ThemeTest, OnlyCopySystemThemeWhenAssetManagersDiffer) { +TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) { AssetManager2 assetmanager_one; - assetmanager_one.SetApkAssets({system_assets_.get(), style_assets_.get()}); + assetmanager_one.SetApkAssets({system_assets_.get(), lib_one_assets_.get(), style_assets_.get(), + libclient_assets_.get()}); AssetManager2 assetmanager_two; - assetmanager_two.SetApkAssets({system_assets_.get(), style_assets_.get()}); + assetmanager_two.SetApkAssets({system_assets_.get(), lib_two_assets_.get(), lib_one_assets_.get(), + style_assets_.get()}); auto theme_one = assetmanager_one.NewTheme(); ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne)); @@ -292,17 +296,34 @@ TEST_F(ThemeTest, OnlyCopySystemThemeWhenAssetManagersDiffer) { auto theme_two = assetmanager_two.NewTheme(); ASSERT_TRUE(theme_two->ApplyStyle(R::style::Theme_One)); ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleTwo)); + ASSERT_TRUE(theme_two->ApplyStyle(fix_package_id(lib_one::R::style::Theme, 0x03), + false /*force*/)); + ASSERT_TRUE(theme_two->ApplyStyle(fix_package_id(lib_two::R::style::Theme, 0x02), + false /*force*/)); - EXPECT_TRUE(theme_one->SetTo(*theme_two)); + theme_one->SetTo(*theme_two); Res_value value; uint32_t flags; - // No app resources. - EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags)); + // System resources (present in destination asset manager) + EXPECT_EQ(0, theme_one->GetAttribute(R::attr::foreground, &value, &flags)); + + // The cookie of the style asset is 3 in the source and 2 in the destination. + // Check that the cookie has been rewritten to the destination values + EXPECT_EQ(2, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags)); + + // The cookie of the lib_one asset is 2 in the source and 1 in the destination. + // The package id of the lib_one package is 0x03 in the source and 0x02 in the destination + // Check that the cookie and packages have been rewritten to the destination values + EXPECT_EQ(1, theme_one->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02), &value, + &flags)); + EXPECT_EQ(1, theme_one->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02), &value, + &flags)); - // Only system. - EXPECT_NE(kInvalidCookie, theme_one->GetAttribute(R::attr::foreground, &value, &flags)); + // attr2 references an attribute in lib_one. Check that the resolution of the attribute value is + // correct after the value of attr2 had its package id rewritten to the destination package id + EXPECT_EQ(700, value.data); } } // namespace android diff --git a/libs/androidfw/tests/data/lib_two/R.h b/libs/androidfw/tests/data/lib_two/R.h index c04a9d3b4de0..92b9cc10e7a8 100644 --- a/libs/androidfw/tests/data/lib_two/R.h +++ b/libs/androidfw/tests/data/lib_two/R.h @@ -24,12 +24,24 @@ namespace android { namespace lib_two { struct R { + struct attr { + enum : uint32_t { + attr3 = 0x02010000, // default + }; + }; + struct string { enum : uint32_t { LibraryString = 0x02020000, // default foo = 0x02020001, // default }; }; + + struct style { + enum : uint32_t { + Theme = 0x02030000, // default + }; + }; }; } // namespace lib_two diff --git a/libs/androidfw/tests/data/lib_two/lib_two.apk b/libs/androidfw/tests/data/lib_two/lib_two.apk Binary files differindex ad44f9c21e31..486c23000276 100644 --- a/libs/androidfw/tests/data/lib_two/lib_two.apk +++ b/libs/androidfw/tests/data/lib_two/lib_two.apk diff --git a/libs/androidfw/tests/data/lib_two/res/values/values.xml b/libs/androidfw/tests/data/lib_two/res/values/values.xml index f4eea2610cab..340d14c34c5d 100644 --- a/libs/androidfw/tests/data/lib_two/res/values/values.xml +++ b/libs/androidfw/tests/data/lib_two/res/values/values.xml @@ -15,9 +15,17 @@ --> <resources> + <public type="attr" name="attr3" id="0x00010000" /> + <attr name="attr3" format="integer" /> + <public type="string" name="LibraryString" id="0x00020000" /> <string name="LibraryString">Hi from library two</string> <public type="string" name="foo" id="0x00020001" /> <string name="foo">Foo from lib_two</string> + + <public type="style" name="Theme" id="0x02030000" /> + <style name="Theme"> + <item name="com.android.lib_two:attr3">800</item> + </style> </resources> |