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 std::unique_ptr<const ApkAssets> system_assets_;
63 std::unique_ptr<const ApkAssets> overlay_assets_;
64 std::unique_ptr<const ApkAssets> 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 assets = asset_manager.GetApkAssets();
70 const ResStringPool* string_pool = assets[value.cookie]->GetLoadedArsc()->GetStringPool();
71 return GetStringFromPool(string_pool, value.data);
72 }
73
74 }
75
TEST_F(IdmapTest,OverlayOverridesResourceValue)76 TEST_F(IdmapTest, OverlayOverridesResourceValue) {
77 AssetManager2 asset_manager;
78 asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
79 overlay_assets_.get()});
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_.get(), overlayable_assets_.get(),
91 overlay_assets_.get()});
92
93 auto value = asset_manager.GetResource(overlayable::R::string::overlayable10);
94 ASSERT_TRUE(value.has_value());
95 ASSERT_EQ(value->cookie, 0U);
96 ASSERT_EQ(value->type, Res_value::TYPE_STRING);
97 ASSERT_EQ("yes", GetStringFromApkAssets(asset_manager, *value));
98 }
99
TEST_F(IdmapTest,OverlayOverridesResourceValueUsingInternalResource)100 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInternalResource) {
101 AssetManager2 asset_manager;
102 asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
103 overlay_assets_.get()});
104
105 auto value = asset_manager.GetResource(overlayable::R::string::overlayable8);
106 ASSERT_TRUE(value.has_value());
107 ASSERT_EQ(value->cookie, 2U);
108 ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE);
109 ASSERT_EQ(value->data, (overlay::R::string::internal & 0x00ffffffU) | (0x02U << 24));
110 }
111
TEST_F(IdmapTest,OverlayOverridesResourceValueUsingInlineInteger)112 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineInteger) {
113 AssetManager2 asset_manager;
114 asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
115 overlay_assets_.get()});
116
117 auto value = asset_manager.GetResource(overlayable::R::integer::config_integer);
118 ASSERT_TRUE(value.has_value());
119 ASSERT_EQ(value->cookie, 2U);
120 ASSERT_EQ(value->type, Res_value::TYPE_INT_DEC);
121 ASSERT_EQ(value->data, 42);
122 }
123
TEST_F(IdmapTest,OverlayOverridesResourceValueUsingInlineString)124 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineString) {
125 AssetManager2 asset_manager;
126 asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
127 overlay_assets_.get()});
128
129 auto value = asset_manager.GetResource(overlayable::R::string::overlayable11);
130 ASSERT_TRUE(value.has_value());
131 ASSERT_EQ(value->cookie, 2U);
132 ASSERT_EQ(value->type, Res_value::TYPE_STRING);
133 ASSERT_EQ("Hardcoded string", GetStringFromApkAssets(asset_manager, *value));
134 }
135
TEST_F(IdmapTest,OverlayOverridesResourceValueUsingOverlayingResource)136 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingOverlayingResource) {
137 AssetManager2 asset_manager;
138 asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
139 overlay_assets_.get()});
140
141 auto value = asset_manager.GetResource(overlayable::R::string::overlayable9);
142 ASSERT_TRUE(value.has_value());
143 ASSERT_EQ(value->cookie, 2U);
144 ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE);
145 ASSERT_EQ(value->data, overlayable::R::string::overlayable7);
146 }
147
TEST_F(IdmapTest,OverlayOverridesXmlParser)148 TEST_F(IdmapTest, OverlayOverridesXmlParser) {
149 AssetManager2 asset_manager;
150 asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
151 overlay_assets_.get()});
152
153 auto value = asset_manager.GetResource(overlayable::R::layout::hello_view);
154 ASSERT_TRUE(value.has_value());
155 ASSERT_EQ(value->cookie, 2U);
156 ASSERT_EQ(value->type, Res_value::TYPE_STRING);
157 ASSERT_EQ("res/layout/hello_view.xml", GetStringFromApkAssets(asset_manager, *value));
158
159 auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", value->cookie,
160 Asset::ACCESS_RANDOM);
161 auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(value->cookie);
162 auto xml_tree = util::make_unique<ResXMLTree>(std::move(dynamic_ref_table));
163 status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), false);
164 ASSERT_EQ(err, NO_ERROR);
165
166 while (xml_tree->next() != ResXMLParser::START_TAG) { }
167
168 // The resource id of @id/hello_view should be rewritten to the resource id/hello_view within the
169 // target.
170 ASSERT_EQ(xml_tree->getAttributeNameResID(0), 0x010100d0 /* android:attr/id */);
171 ASSERT_EQ(xml_tree->getAttributeDataType(0), Res_value::TYPE_REFERENCE);
172 ASSERT_EQ(xml_tree->getAttributeData(0), overlayable::R::id::hello_view);
173
174 // The resource id of @android:string/yes should not be rewritten even though it overlays
175 // string/overlayable10 in the target.
176 ASSERT_EQ(xml_tree->getAttributeNameResID(1), 0x0101014f /* android:attr/text */);
177 ASSERT_EQ(xml_tree->getAttributeDataType(1), Res_value::TYPE_REFERENCE);
178 ASSERT_EQ(xml_tree->getAttributeData(1), 0x01040013 /* android:string/yes */);
179
180 // The resource id of the attribute within the overlay should be rewritten to the resource id of
181 // the attribute in the target.
182 ASSERT_EQ(xml_tree->getAttributeNameResID(2), overlayable::R::attr::max_lines);
183 ASSERT_EQ(xml_tree->getAttributeDataType(2), Res_value::TYPE_INT_DEC);
184 ASSERT_EQ(xml_tree->getAttributeData(2), 4);
185 }
186
TEST_F(IdmapTest,OverlaidResourceHasSameName)187 TEST_F(IdmapTest, OverlaidResourceHasSameName) {
188 AssetManager2 asset_manager;
189 asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
190 overlay_assets_.get()});
191
192 auto name = asset_manager.GetResourceName(overlayable::R::string::overlayable9);
193 ASSERT_TRUE(name.has_value());
194 ASSERT_EQ("com.android.overlayable", std::string(name->package));
195 ASSERT_EQ(std::u16string(u"string"), std::u16string(name->type16));
196 ASSERT_EQ("overlayable9", std::string(name->entry));
197 }
198
TEST_F(IdmapTest,OverlayLoaderInterop)199 TEST_F(IdmapTest, OverlayLoaderInterop) {
200 auto asset = AssetsProvider::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
201 ASSERT_THAT(asset, NotNull());
202
203 auto loader_assets = ApkAssets::LoadTable(std::move(asset), EmptyAssetsProvider::Create(),
204 PROPERTY_LOADER);
205 AssetManager2 asset_manager;
206 asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(),
207 overlay_assets_.get()});
208
209 auto value = asset_manager.GetResource(overlayable::R::string::overlayable11);
210 ASSERT_TRUE(value.has_value());
211 ASSERT_EQ(1U, value->cookie);
212 ASSERT_EQ(Res_value::TYPE_STRING, value->type);
213 ASSERT_EQ("loader", GetStringFromApkAssets(asset_manager, *value));
214 }
215
TEST_F(IdmapTest,OverlayAssetsIsUpToDate)216 TEST_F(IdmapTest, OverlayAssetsIsUpToDate) {
217 std::string idmap_contents;
218 ASSERT_TRUE(base::ReadFileToString("overlay/overlay.idmap", &idmap_contents));
219
220 TemporaryFile temp_file;
221 ASSERT_TRUE(base::WriteStringToFile(idmap_contents, temp_file.path));
222
223 auto apk_assets = ApkAssets::LoadOverlay(temp_file.path);
224 ASSERT_NE(nullptr, apk_assets);
225 ASSERT_TRUE(apk_assets->IsUpToDate());
226
227 unlink(temp_file.path);
228 ASSERT_FALSE(apk_assets->IsUpToDate());
229 sleep(2);
230
231 base::WriteStringToFile("hello", temp_file.path);
232 sleep(2);
233
234 ASSERT_FALSE(apk_assets->IsUpToDate());
235 }
236
237 } // namespace
238