1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "android-base/file.h"
18 #include "androidfw/ApkAssets.h"
19 #include "androidfw/AssetManager2.h"
20 #include "androidfw/ResourceTypes.h"
21 
22 #include "utils/String16.h"
23 #include "utils/String8.h"
24 
25 #include "TestHelpers.h"
26 #include "data/overlay/R.h"
27 #include "data/overlayable/R.h"
28 #include "data/system/R.h"
29 
30 using ::testing::NotNull;
31 
32 namespace overlay = com::android::overlay;
33 namespace overlayable = com::android::overlayable;
34 
35 namespace android {
36 
37 namespace {
38 
39 class IdmapTest : public ::testing::Test {
40  protected:
SetUp()41   void SetUp() override {
42     // Move to the test data directory so the idmap can locate the overlay APK.
43     original_path = base::GetExecutableDirectory();
44     chdir(GetTestDataPath().c_str());
45 
46     system_assets_ = ApkAssets::Load("system/system.apk");
47     ASSERT_NE(nullptr, system_assets_);
48 
49     overlay_assets_ = ApkAssets::LoadOverlay("overlay/overlay.idmap");
50     ASSERT_NE(nullptr, overlay_assets_);
51 
52     overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk");
53     ASSERT_NE(nullptr, overlayable_assets_);
54   }
55 
TearDown()56   void TearDown() override {
57     chdir(original_path.c_str());
58   }
59 
60  protected:
61   std::string original_path;
62   AssetManager2::ApkAssetsPtr system_assets_;
63   AssetManager2::ApkAssetsPtr overlay_assets_;
64   AssetManager2::ApkAssetsPtr overlayable_assets_;
65 };
66 
GetStringFromApkAssets(const AssetManager2 & asset_manager,const AssetManager2::SelectedValue & value)67 std::string GetStringFromApkAssets(const AssetManager2& asset_manager,
68                                    const AssetManager2::SelectedValue& value) {
69   auto op = asset_manager.StartOperation();
70   const ResStringPool* string_pool =
71       asset_manager.GetApkAssets(value.cookie)->GetLoadedArsc()->GetStringPool();
72   return GetStringFromPool(string_pool, value.data);
73 }
74 
75 }
76 
TEST_F(IdmapTest,OverlayOverridesResourceValue)77 TEST_F(IdmapTest, OverlayOverridesResourceValue) {
78   AssetManager2 asset_manager;
79   asset_manager.SetApkAssets({system_assets_, overlayable_assets_, overlay_assets_});
80 
81   auto value = asset_manager.GetResource(overlayable::R::string::overlayable5);
82   ASSERT_TRUE(value.has_value());
83   ASSERT_EQ(value->cookie, 2U);
84   ASSERT_EQ(value->type, Res_value::TYPE_STRING);
85   ASSERT_EQ("Overlay One", GetStringFromApkAssets(asset_manager, *value));
86 }
87 
TEST_F(IdmapTest,OverlayOverridesResourceValueUsingDifferentPackage)88 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingDifferentPackage) {
89   AssetManager2 asset_manager;
90   asset_manager.SetApkAssets({system_assets_, overlayable_assets_, overlay_assets_});
91 
92   auto value = asset_manager.GetResource(overlayable::R::string::overlayable10);
93   ASSERT_TRUE(value.has_value());
94   ASSERT_EQ(value->cookie, 0U);
95   ASSERT_EQ(value->type, Res_value::TYPE_STRING);
96   ASSERT_EQ("yes", GetStringFromApkAssets(asset_manager, *value));
97 }
98 
TEST_F(IdmapTest,OverlayOverridesResourceValueUsingInternalResource)99 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInternalResource) {
100   AssetManager2 asset_manager;
101   asset_manager.SetApkAssets({system_assets_, overlayable_assets_, overlay_assets_});
102 
103   auto value = asset_manager.GetResource(overlayable::R::string::overlayable8);
104   ASSERT_TRUE(value.has_value());
105   ASSERT_EQ(value->cookie, 2U);
106   ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE);
107   ASSERT_EQ(value->data, (overlay::R::string::internal & 0x00ffffffU) | (0x02U << 24));
108 }
109 
TEST_F(IdmapTest,OverlayOverridesResourceValueUsingInlineInteger)110 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineInteger) {
111   AssetManager2 asset_manager;
112   asset_manager.SetApkAssets({system_assets_, overlayable_assets_, overlay_assets_});
113 
114   auto value = asset_manager.GetResource(overlayable::R::integer::config_integer);
115   ASSERT_TRUE(value.has_value());
116   ASSERT_EQ(value->cookie, 2U);
117   ASSERT_EQ(value->type, Res_value::TYPE_INT_DEC);
118   ASSERT_EQ(value->data, 42);
119 }
120 
TEST_F(IdmapTest,OverlayOverridesResourceValueUsingInlineString)121 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineString) {
122   AssetManager2 asset_manager;
123   asset_manager.SetApkAssets({system_assets_, overlayable_assets_, overlay_assets_});
124 
125   auto value = asset_manager.GetResource(overlayable::R::string::overlayable11);
126   ASSERT_TRUE(value.has_value());
127   ASSERT_EQ(value->cookie, 2U);
128   ASSERT_EQ(value->type, Res_value::TYPE_STRING);
129   ASSERT_EQ("Hardcoded string", GetStringFromApkAssets(asset_manager, *value));
130 }
131 
TEST_F(IdmapTest,OverlayOverridesResourceValueUsingOverlayingResource)132 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingOverlayingResource) {
133   AssetManager2 asset_manager;
134   asset_manager.SetApkAssets({system_assets_, overlayable_assets_, overlay_assets_});
135 
136   auto value = asset_manager.GetResource(overlayable::R::string::overlayable9);
137   ASSERT_TRUE(value.has_value());
138   ASSERT_EQ(value->cookie, 2U);
139   ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE);
140   ASSERT_EQ(value->data, overlayable::R::string::overlayable7);
141 }
142 
TEST_F(IdmapTest,OverlayOverridesXmlParser)143 TEST_F(IdmapTest, OverlayOverridesXmlParser) {
144   AssetManager2 asset_manager;
145   asset_manager.SetApkAssets({system_assets_, overlayable_assets_, overlay_assets_});
146 
147   auto value = asset_manager.GetResource(overlayable::R::layout::hello_view);
148   ASSERT_TRUE(value.has_value());
149   ASSERT_EQ(value->cookie, 2U);
150   ASSERT_EQ(value->type, Res_value::TYPE_STRING);
151   ASSERT_EQ("res/layout/hello_view.xml", GetStringFromApkAssets(asset_manager, *value));
152 
153   auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", value->cookie,
154                                           Asset::ACCESS_RANDOM);
155   auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(value->cookie);
156   auto xml_tree = util::make_unique<ResXMLTree>(std::move(dynamic_ref_table));
157   status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), false);
158   ASSERT_EQ(err, NO_ERROR);
159 
160   while (xml_tree->next() != ResXMLParser::START_TAG) { }
161 
162   // The resource id of @id/hello_view should be rewritten to the resource id/hello_view within the
163   // target.
164   ASSERT_EQ(xml_tree->getAttributeNameResID(0), 0x010100d0 /* android:attr/id */);
165   ASSERT_EQ(xml_tree->getAttributeDataType(0), Res_value::TYPE_REFERENCE);
166   ASSERT_EQ(xml_tree->getAttributeData(0), overlayable::R::id::hello_view);
167 
168   // The resource id of @android:string/yes should not be rewritten even though it overlays
169   // string/overlayable10 in the target.
170   ASSERT_EQ(xml_tree->getAttributeNameResID(1), 0x0101014f /* android:attr/text */);
171   ASSERT_EQ(xml_tree->getAttributeDataType(1), Res_value::TYPE_REFERENCE);
172   ASSERT_EQ(xml_tree->getAttributeData(1), 0x01040013 /* android:string/yes */);
173 
174   // The resource id of the attribute within the overlay should be rewritten to the resource id of
175   // the attribute in the target.
176   ASSERT_EQ(xml_tree->getAttributeNameResID(2), overlayable::R::attr::max_lines);
177   ASSERT_EQ(xml_tree->getAttributeDataType(2), Res_value::TYPE_INT_DEC);
178   ASSERT_EQ(xml_tree->getAttributeData(2), 4);
179 }
180 
TEST_F(IdmapTest,OverlaidResourceHasSameName)181 TEST_F(IdmapTest, OverlaidResourceHasSameName) {
182   AssetManager2 asset_manager;
183   asset_manager.SetApkAssets({system_assets_, overlayable_assets_, overlay_assets_});
184 
185   auto name = asset_manager.GetResourceName(overlayable::R::string::overlayable9);
186   ASSERT_TRUE(name.has_value());
187   ASSERT_EQ("com.android.overlayable", std::string(name->package));
188   ASSERT_EQ(std::u16string(u"string"), std::u16string(name->type16));
189   ASSERT_EQ("overlayable9", std::string(name->entry));
190 }
191 
TEST_F(IdmapTest,OverlayLoaderInterop)192 TEST_F(IdmapTest, OverlayLoaderInterop) {
193   auto asset = AssetsProvider::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
194   ASSERT_THAT(asset, NotNull());
195 
196   auto loader_assets = ApkAssets::LoadTable(std::move(asset), EmptyAssetsProvider::Create(),
197       PROPERTY_LOADER);
198   AssetManager2 asset_manager;
199   asset_manager.SetApkAssets({overlayable_assets_, loader_assets, overlay_assets_});
200 
201   auto value = asset_manager.GetResource(overlayable::R::string::overlayable11);
202   ASSERT_TRUE(value.has_value());
203   ASSERT_EQ(1U, value->cookie);
204   ASSERT_EQ(Res_value::TYPE_STRING, value->type);
205   ASSERT_EQ("loader", GetStringFromApkAssets(asset_manager, *value));
206 }
207 
TEST_F(IdmapTest,OverlayAssetsIsUpToDate)208 TEST_F(IdmapTest, OverlayAssetsIsUpToDate) {
209   std::string idmap_contents;
210   ASSERT_TRUE(base::ReadFileToString("overlay/overlay.idmap", &idmap_contents));
211 
212   TemporaryFile temp_file;
213   ASSERT_TRUE(base::WriteStringToFile(idmap_contents, temp_file.path));
214 
215   auto apk_assets = ApkAssets::LoadOverlay(temp_file.path);
216   ASSERT_NE(nullptr, apk_assets);
217   ASSERT_TRUE(apk_assets->IsUpToDate());
218 
219   unlink(temp_file.path);
220   ASSERT_FALSE(apk_assets->IsUpToDate());
221   sleep(2);
222 
223   base::WriteStringToFile("hello", temp_file.path);
224   sleep(2);
225 
226   ASSERT_FALSE(apk_assets->IsUpToDate());
227 }
228 
229 }  // namespace
230