Merge "Allow non-references to be copied between AssetManagers" into qt-dev
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 9519704..1b515ad 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -1319,7 +1319,7 @@
     typedef std::map<int, int> SourceToDestinationRuntimePackageMap;
     std::map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map;
 
-    // Determine which ApkAssets are loaded in both theme AssetManagers
+    // 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];
@@ -1328,7 +1328,7 @@
       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
+        // 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();
@@ -1353,15 +1353,14 @@
             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));
+          src_to_dest_asset_cookies.insert(std::make_pair(i, j));
+          src_asset_cookie_id_map.insert(std::make_pair(i, package_map));
           break;
         }
       }
     }
 
-    // Reset the data in the destination theme
+    // Reset the data in the destination theme.
     for (size_t p = 0; p < packages_.size(); p++) {
       if (packages_[p] != nullptr) {
         packages_[p].reset();
@@ -1387,16 +1386,17 @@
             continue;
           }
 
-          // 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_ATTRIBUTE
-              || entry.value.dataType == Res_value::TYPE_REFERENCE
-              || entry.value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE
-              || entry.value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE)
-              && attribue_data != 0x0) {
+          bool is_reference = (entry.value.dataType == Res_value::TYPE_ATTRIBUTE
+                               || entry.value.dataType == Res_value::TYPE_REFERENCE
+                               || entry.value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE
+                               || entry.value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE)
+                              && entry.value.data != 0x0;
 
-            // Determine the package id of the reference in the destination AssetManager
+          // 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 attribute_data = entry.value.data;
+          if (is_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;
@@ -1408,14 +1408,28 @@
               continue;
             }
 
-            attribue_data = fix_package_id(entry.value.data, value_dest_package->second);
+            attribute_data = fix_package_id(entry.value.data, value_dest_package->second);
+          }
+
+          // Find the cookie of the value in the destination. If the source apk is not loaded in the
+          // destination, only copy resources that do not reference resources in the source.
+          ApkAssetsCookie data_dest_cookie;
+          auto value_dest_cookie = src_to_dest_asset_cookies.find(entry.cookie);
+          if (value_dest_cookie != src_to_dest_asset_cookies.end()) {
+            data_dest_cookie = value_dest_cookie->second;
+          } else {
+            if (is_reference || entry.value.dataType == Res_value::TYPE_STRING) {
+              continue;
+            } else {
+              data_dest_cookie = 0x0;
+            }
           }
 
           // The package id of the attribute needs to be rewritten to the package id of the
-          // attribute in the destination
+          // attribute in the destination.
           int attribute_dest_package_id = p;
           if (attribute_dest_package_id != 0x01) {
-            // Find the cookie of the attribute resource id
+            // Find the cookie of the attribute resource id in the source AssetManager
             FindEntryResult attribute_entry_result;
             ApkAssetsCookie attribute_cookie =
                 o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ ,
@@ -1423,7 +1437,7 @@
                                             true /* ignore_configuration */,
                                             &attribute_entry_result);
 
-            // Determine the package id of the attribute in the destination AssetManager
+            // 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;
@@ -1436,13 +1450,13 @@
             attribute_dest_package_id = attribute_dest_package->second;
           }
 
-          // Lazily instantiate the destination package
+          // 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
+          // 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)
@@ -1450,7 +1464,7 @@
             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
+            // 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)));
@@ -1460,15 +1474,9 @@
             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].cookie = data_dest_cookie;
           dest_type->entries[e].value.dataType = entry.value.dataType;
-          dest_type->entries[e].value.data = attribue_data;
+          dest_type->entries[e].value.data = attribute_data;
           dest_type->entries[e].type_spec_flags = entry.type_spec_flags;
         }
       }
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index 2c39cee..be5ecd9 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -282,48 +282,81 @@
 }
 
 TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) {
-  AssetManager2 assetmanager_one;
-  assetmanager_one.SetApkAssets({system_assets_.get(), lib_one_assets_.get(), style_assets_.get(),
+  AssetManager2 assetmanager_dst;
+  assetmanager_dst.SetApkAssets({system_assets_.get(), lib_one_assets_.get(), style_assets_.get(),
                                  libclient_assets_.get()});
 
-  AssetManager2 assetmanager_two;
-  assetmanager_two.SetApkAssets({system_assets_.get(), lib_two_assets_.get(), lib_one_assets_.get(),
+  AssetManager2 assetmanager_src;
+  assetmanager_src.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));
+  auto theme_dst = assetmanager_dst.NewTheme();
+  ASSERT_TRUE(theme_dst->ApplyStyle(app::R::style::StyleOne));
 
-  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),
+  auto theme_src = assetmanager_src.NewTheme();
+  ASSERT_TRUE(theme_src->ApplyStyle(R::style::Theme_One));
+  ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleTwo));
+  ASSERT_TRUE(theme_src->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),
+  ASSERT_TRUE(theme_src->ApplyStyle(fix_package_id(lib_two::R::style::Theme, 0x02),
                                     false /*force*/));
 
-  theme_one->SetTo(*theme_two);
+  theme_dst->SetTo(*theme_src);
 
   Res_value value;
   uint32_t flags;
 
-  // System resources (present in destination asset manager)
-  EXPECT_EQ(0, theme_one->GetAttribute(R::attr::foreground, &value, &flags));
+  // System resources (present in destination asset manager).
+  EXPECT_EQ(0, theme_dst->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));
+  // Check that the cookie has been rewritten to the destination values.
+  EXPECT_EQ(2, theme_dst->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,
+  // Check that the cookie and packages have been rewritten to the destination values.
+  EXPECT_EQ(1, theme_dst->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,
+  EXPECT_EQ(1, theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02), &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
+  // correct after the value of attr2 had its package id rewritten to the destination package id.
   EXPECT_EQ(700, value.data);
 }
 
+TEST_F(ThemeTest, CopyNonReferencesWhenPackagesDiffer) {
+  AssetManager2 assetmanager_dst;
+  assetmanager_dst.SetApkAssets({system_assets_.get()});
+
+  AssetManager2 assetmanager_src;
+  assetmanager_src.SetApkAssets({system_assets_.get(), style_assets_.get()});
+
+  auto theme_dst = assetmanager_dst.NewTheme();
+  auto theme_src = assetmanager_src.NewTheme();
+  ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleSeven));
+  theme_dst->SetTo(*theme_src);
+
+  Res_value value;
+  uint32_t flags;
+
+  // Allow inline resource values to be copied even if the source apk asset is not present in the
+  // destination.
+  EXPECT_EQ(0, theme_dst->GetAttribute(0x0101021b /* android:versionCode */, &value, &flags));
+
+  // Do not copy strings since the data is an index into the values string pool of the source apk
+  // asset.
+  EXPECT_EQ(-1, theme_dst->GetAttribute(0x01010001 /* android:label */, &value, &flags));
+
+  // Do not copy values that reference another resource if the resource is not present in the
+  // destination.
+  EXPECT_EQ(-1, theme_dst->GetAttribute(0x01010002 /* android:icon */, &value, &flags));
+  EXPECT_EQ(-1, theme_dst->GetAttribute(0x010100d1 /* android:tag */, &value, &flags));
+
+  // Allow @empty to and @null to be copied.
+  EXPECT_EQ(0, theme_dst->GetAttribute(0x010100d0 /* android:id */, &value, &flags));
+  EXPECT_EQ(0, theme_dst->GetAttribute(0x01010000 /* android:theme */, &value, &flags));
+}
+
 }  // namespace android
diff --git a/libs/androidfw/tests/data/styles/R.h b/libs/androidfw/tests/data/styles/R.h
index 538a847..f11486f 100644
--- a/libs/androidfw/tests/data/styles/R.h
+++ b/libs/androidfw/tests/data/styles/R.h
@@ -51,6 +51,7 @@
       StyleFour = 0x7f020003u,
       StyleFive = 0x7f020004u,
       StyleSix = 0x7f020005u,
+      StyleSeven = 0x7f020006u,
     };
   };
 };
diff --git a/libs/androidfw/tests/data/styles/build b/libs/androidfw/tests/data/styles/build
index 1ef8e6e..7b7c1f7 100755
--- a/libs/androidfw/tests/data/styles/build
+++ b/libs/androidfw/tests/data/styles/build
@@ -2,5 +2,7 @@
 
 set -e
 
+PATH_TO_FRAMEWORK_RES=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar
+
 aapt2 compile -o compiled.flata --dir res
-aapt2 link -o styles.apk --manifest AndroidManifest.xml compiled.flata
+aapt2 link -o styles.apk --manifest AndroidManifest.xml -I $PATH_TO_FRAMEWORK_RES compiled.flata
diff --git a/libs/androidfw/tests/data/styles/res/values/styles.xml b/libs/androidfw/tests/data/styles/res/values/styles.xml
index 1a23176..06774a8 100644
--- a/libs/androidfw/tests/data/styles/res/values/styles.xml
+++ b/libs/androidfw/tests/data/styles/res/values/styles.xml
@@ -79,4 +79,14 @@
         <item name="attr_three">3</item>
     </style>
 
+    <public type="style" name="StyleSeven" id="0x7f020006" />
+    <style name="StyleSeven" >
+        <item name="android:versionCode">3</item>
+        <item name="android:label">"string"</item>
+        <item name="android:icon">?attr/attr_one</item>
+        <item name="android:tag">@string/string_one</item>
+        <item name="android:id">@null</item>
+        <item name="android:theme">@empty</item>
+    </style>
+
 </resources>
diff --git a/libs/androidfw/tests/data/styles/styles.apk b/libs/androidfw/tests/data/styles/styles.apk
index cd5c7a1..92e9bf9 100644
--- a/libs/androidfw/tests/data/styles/styles.apk
+++ b/libs/androidfw/tests/data/styles/styles.apk
Binary files differ