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 <chrono>
18 #include <thread>
19
20 #include "android-base/file.h"
21 #include "androidfw/ApkAssets.h"
22 #include "androidfw/AssetManager2.h"
23 #include "androidfw/ResourceTypes.h"
24
25 #include "utils/String16.h"
26 #include "utils/String8.h"
27
28 #include "TestHelpers.h"
29 #include "data/overlay/R.h"
30 #include "data/overlayable/R.h"
31 #include "data/system/R.h"
32
33 using namespace std::chrono_literals;
34 using ::testing::NotNull;
35
36 namespace overlay = com::android::overlay;
37 namespace overlayable = com::android::overlayable;
38
39 namespace android {
40
41 namespace {
42
43 class IdmapTest : public ::testing::Test {
44 protected:
SetUp()45 void SetUp() override {
46 // Move to the test data directory so the idmap can locate the overlay APK.
47 original_path = base::GetExecutableDirectory();
48 chdir(GetTestDataPath().c_str());
49
50 system_assets_ = ApkAssets::Load("system/system.apk");
51 ASSERT_NE(nullptr, system_assets_);
52
53 overlay_assets_ = ApkAssets::LoadOverlay("overlay/overlay.idmap");
54 ASSERT_NE(nullptr, overlay_assets_);
55
56 overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk");
57 ASSERT_NE(nullptr, overlayable_assets_);
58 }
59
TearDown()60 void TearDown() override {
61 chdir(original_path.c_str());
62 }
63
64 protected:
65 std::string original_path;
66 AssetManager2::ApkAssetsPtr system_assets_;
67 AssetManager2::ApkAssetsPtr overlay_assets_;
68 AssetManager2::ApkAssetsPtr overlayable_assets_;
69 };
70
GetStringFromApkAssets(const AssetManager2 & asset_manager,const AssetManager2::SelectedValue & value)71 std::string GetStringFromApkAssets(const AssetManager2& asset_manager,
72 const AssetManager2::SelectedValue& value) {
73 auto op = asset_manager.StartOperation();
74 const ResStringPool* string_pool =
75 asset_manager.GetApkAssets(value.cookie)->GetLoadedArsc()->GetStringPool();
76 return GetStringFromPool(string_pool, value.data);
77 }
78
79 }
80
TEST_F(IdmapTest,OverlayOverridesResourceValue)81 TEST_F(IdmapTest, OverlayOverridesResourceValue) {
82 AssetManager2 asset_manager;
83 asset_manager.SetApkAssets({system_assets_, overlayable_assets_, overlay_assets_});
84
85 auto value = asset_manager.GetResource(overlayable::R::string::overlayable5);
86 ASSERT_TRUE(value.has_value());
87 ASSERT_EQ(value->cookie, 2U);
88 ASSERT_EQ(value->type, Res_value::TYPE_STRING);
89 ASSERT_EQ("Overlay One", GetStringFromApkAssets(asset_manager, *value));
90 }
91
TEST_F(IdmapTest,OverlayOverridesResourceValueUsingDifferentPackage)92 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingDifferentPackage) {
93 AssetManager2 asset_manager;
94 asset_manager.SetApkAssets({system_assets_, overlayable_assets_, overlay_assets_});
95
96 auto value = asset_manager.GetResource(overlayable::R::string::overlayable10);
97 ASSERT_TRUE(value.has_value());
98 ASSERT_EQ(value->cookie, 0U);
99 ASSERT_EQ(value->type, Res_value::TYPE_STRING);
100 ASSERT_EQ("yes", GetStringFromApkAssets(asset_manager, *value));
101 }
102
TEST_F(IdmapTest,OverlayOverridesResourceValueUsingInternalResource)103 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInternalResource) {
104 AssetManager2 asset_manager;
105 asset_manager.SetApkAssets({system_assets_, overlayable_assets_, overlay_assets_});
106
107 auto value = asset_manager.GetResource(overlayable::R::string::overlayable8);
108 ASSERT_TRUE(value.has_value());
109 ASSERT_EQ(value->cookie, 2U);
110 ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE);
111 ASSERT_EQ(value->data, (overlay::R::string::internal & 0x00ffffffU) | (0x02U << 24));
112 }
113
TEST_F(IdmapTest,OverlayOverridesResourceValueUsingInlineInteger)114 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineInteger) {
115 AssetManager2 asset_manager;
116 asset_manager.SetApkAssets({system_assets_, overlayable_assets_, overlay_assets_});
117
118 auto value = asset_manager.GetResource(overlayable::R::integer::config_integer);
119 ASSERT_TRUE(value.has_value());
120 ASSERT_EQ(value->cookie, 2U);
121 ASSERT_EQ(value->type, Res_value::TYPE_INT_DEC);
122 ASSERT_EQ(value->data, 42);
123 }
124
TEST_F(IdmapTest,OverlayOverridesResourceValueUsingInlineString)125 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineString) {
126 AssetManager2 asset_manager;
127 asset_manager.SetApkAssets({system_assets_, overlayable_assets_, overlay_assets_});
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_, overlayable_assets_, overlay_assets_});
139
140 auto value = asset_manager.GetResource(overlayable::R::string::overlayable9);
141 ASSERT_TRUE(value.has_value());
142 ASSERT_EQ(value->cookie, 2U);
143 ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE);
144 ASSERT_EQ(value->data, overlayable::R::string::overlayable7);
145 }
146
TEST_F(IdmapTest,OverlayOverridesXmlParser)147 TEST_F(IdmapTest, OverlayOverridesXmlParser) {
148 AssetManager2 asset_manager;
149 asset_manager.SetApkAssets({system_assets_, overlayable_assets_, overlay_assets_});
150
151 auto value = asset_manager.GetResource(overlayable::R::layout::hello_view);
152 ASSERT_TRUE(value.has_value());
153 ASSERT_EQ(value->cookie, 2U);
154 ASSERT_EQ(value->type, Res_value::TYPE_STRING);
155 ASSERT_EQ("res/layout/hello_view.xml", GetStringFromApkAssets(asset_manager, *value));
156
157 auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", value->cookie,
158 Asset::ACCESS_RANDOM);
159 auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(value->cookie);
160 auto xml_tree = util::make_unique<ResXMLTree>(std::move(dynamic_ref_table));
161 status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), false);
162 ASSERT_EQ(err, NO_ERROR);
163
164 while (xml_tree->next() != ResXMLParser::START_TAG) { }
165
166 // The resource id of @id/hello_view should be rewritten to the resource id/hello_view within the
167 // target.
168 ASSERT_EQ(xml_tree->getAttributeNameResID(0), 0x010100d0 /* android:attr/id */);
169 ASSERT_EQ(xml_tree->getAttributeDataType(0), Res_value::TYPE_REFERENCE);
170 ASSERT_EQ(xml_tree->getAttributeData(0), overlayable::R::id::hello_view);
171
172 // The resource id of @android:string/yes should not be rewritten even though it overlays
173 // string/overlayable10 in the target.
174 ASSERT_EQ(xml_tree->getAttributeNameResID(1), 0x0101014f /* android:attr/text */);
175 ASSERT_EQ(xml_tree->getAttributeDataType(1), Res_value::TYPE_REFERENCE);
176 ASSERT_EQ(xml_tree->getAttributeData(1), 0x01040013 /* android:string/yes */);
177
178 // The resource id of the attribute within the overlay should be rewritten to the resource id of
179 // the attribute in the target.
180 ASSERT_EQ(xml_tree->getAttributeNameResID(2), overlayable::R::attr::max_lines);
181 ASSERT_EQ(xml_tree->getAttributeDataType(2), Res_value::TYPE_INT_DEC);
182 ASSERT_EQ(xml_tree->getAttributeData(2), 4);
183 }
184
TEST_F(IdmapTest,OverlaidResourceHasSameName)185 TEST_F(IdmapTest, OverlaidResourceHasSameName) {
186 AssetManager2 asset_manager;
187 asset_manager.SetApkAssets({system_assets_, overlayable_assets_, overlay_assets_});
188
189 auto name = asset_manager.GetResourceName(overlayable::R::string::overlayable9);
190 ASSERT_TRUE(name.has_value());
191 ASSERT_EQ("com.android.overlayable", std::string(name->package));
192 ASSERT_EQ(std::u16string(u"string"), std::u16string(name->type16));
193 ASSERT_EQ("overlayable9", std::string(name->entry));
194 }
195
TEST_F(IdmapTest,OverlayLoaderInterop)196 TEST_F(IdmapTest, OverlayLoaderInterop) {
197 auto asset = AssetsProvider::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
198 ASSERT_THAT(asset, NotNull());
199
200 auto loader_assets = ApkAssets::LoadTable(std::move(asset), EmptyAssetsProvider::Create(),
201 PROPERTY_LOADER);
202 AssetManager2 asset_manager;
203 asset_manager.SetApkAssets({overlayable_assets_, loader_assets, overlay_assets_});
204
205 auto value = asset_manager.GetResource(overlayable::R::string::overlayable11);
206 ASSERT_TRUE(value.has_value());
207 ASSERT_EQ(1U, value->cookie);
208 ASSERT_EQ(Res_value::TYPE_STRING, value->type);
209 ASSERT_EQ("loader", GetStringFromApkAssets(asset_manager, *value));
210 }
211
TEST_F(IdmapTest,OverlayAssetsIsUpToDate)212 TEST_F(IdmapTest, OverlayAssetsIsUpToDate) {
213 std::string idmap_contents;
214 ASSERT_TRUE(base::ReadFileToString("overlay/overlay.idmap", &idmap_contents));
215
216 TemporaryFile temp_file;
217 ASSERT_TRUE(base::WriteStringToFile(idmap_contents, temp_file.path));
218
219 auto apk_assets = ApkAssets::LoadOverlay(temp_file.path);
220 ASSERT_NE(nullptr, apk_assets);
221 ASSERT_TRUE(apk_assets->IsOverlay());
222 ASSERT_EQ(UpToDate::True, apk_assets->IsUpToDate());
223
224 unlink(temp_file.path);
225 ASSERT_EQ(UpToDate::False, apk_assets->IsUpToDate());
226
227 const auto sleep_duration =
228 std::chrono::nanoseconds(std::max(kModDateResolutionNs, 1'000'000ull));
229 std::this_thread::sleep_for(sleep_duration);
230
231 base::WriteStringToFile("hello", temp_file.path);
232 std::this_thread::sleep_for(sleep_duration);
233
234 ASSERT_EQ(UpToDate::False, apk_assets->IsUpToDate());
235 }
236
TEST(IdmapTestUpToDate,Combine)237 TEST(IdmapTestUpToDate, Combine) {
238 ASSERT_EQ(UpToDate::False, combine(UpToDate::False, [] {
239 ADD_FAILURE(); // Shouldn't get called at all.
240 return UpToDate::False;
241 }));
242
243 ASSERT_EQ(UpToDate::False, combine(UpToDate::True, [] { return UpToDate::False; }));
244
245 ASSERT_EQ(UpToDate::True, combine(UpToDate::True, [] { return UpToDate::True; }));
246 ASSERT_EQ(UpToDate::True, combine(UpToDate::True, [] { return UpToDate::Always; }));
247 ASSERT_EQ(UpToDate::True, combine(UpToDate::Always, [] { return UpToDate::True; }));
248
249 ASSERT_EQ(UpToDate::Always, combine(UpToDate::Always, [] { return UpToDate::Always; }));
250 }
251
TEST(IdmapTestUpToDate,FromBool)252 TEST(IdmapTestUpToDate, FromBool) {
253 ASSERT_EQ(UpToDate::False, fromBool(false));
254 ASSERT_EQ(UpToDate::True, fromBool(true));
255 }
256
257 } // namespace
258