• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "androidfw/ResourceTypes.h"
18 
19 #include <codecvt>
20 #include <locale>
21 #include <string>
22 
23 #include "utils/String16.h"
24 #include "utils/String8.h"
25 
26 #include "TestHelpers.h"
27 #include "data/basic/R.h"
28 #include "data/lib_one/R.h"
29 
30 namespace basic = com::android::basic;
31 namespace lib = com::android::lib_one;
32 
33 namespace android {
34 
TEST(ResTableTest,ShouldLoadSuccessfully)35 TEST(ResTableTest, ShouldLoadSuccessfully) {
36   std::string contents;
37   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
38                                       "resources.arsc", &contents));
39 
40   ResTable table;
41   ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
42 }
43 
TEST(ResTableTest,ShouldLoadSparseEntriesSuccessfully)44 TEST(ResTableTest, ShouldLoadSparseEntriesSuccessfully) {
45   std::string contents;
46   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc",
47                                       &contents));
48 
49   ResTable table;
50   ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
51 
52   ResTable_config config;
53   memset(&config, 0, sizeof(config));
54   config.sdkVersion = 26;
55   table.setParameters(&config);
56 
57   String16 name(u"com.android.sparse:integer/foo_9");
58   uint32_t flags;
59   uint32_t resid =
60       table.identifierForName(name.string(), name.size(), nullptr, 0, nullptr, 0, &flags);
61   ASSERT_NE(0u, resid);
62 
63   Res_value val;
64   ResTable_config selected_config;
65   ASSERT_GE(
66       table.getResource(resid, &val, false /*mayBeBag*/, 0u /*density*/, &flags, &selected_config),
67       0);
68   EXPECT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
69   EXPECT_EQ(900u, val.data);
70 }
71 
TEST(ResTableTest,SimpleTypeIsRetrievedCorrectly)72 TEST(ResTableTest, SimpleTypeIsRetrievedCorrectly) {
73   std::string contents;
74   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
75                                       "resources.arsc", &contents));
76 
77   ResTable table;
78   ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
79 
80   EXPECT_TRUE(IsStringEqual(table, basic::R::string::test1, "test1"));
81 }
82 
TEST(ResTableTest,ResourceNameIsResolved)83 TEST(ResTableTest, ResourceNameIsResolved) {
84   std::string contents;
85   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
86                                       "resources.arsc", &contents));
87 
88   ResTable table;
89   ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
90 
91   String16 defPackage("com.android.basic");
92   String16 testName("@string/test1");
93   uint32_t resID =
94       table.identifierForName(testName.string(), testName.size(), 0, 0,
95                               defPackage.string(), defPackage.size());
96   ASSERT_NE(uint32_t(0x00000000), resID);
97   ASSERT_EQ(basic::R::string::test1, resID);
98 }
99 
TEST(ResTableTest,NoParentThemeIsAppliedCorrectly)100 TEST(ResTableTest, NoParentThemeIsAppliedCorrectly) {
101   std::string contents;
102   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
103                                       "resources.arsc", &contents));
104 
105   ResTable table;
106   ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
107 
108   ResTable::Theme theme(table);
109   ASSERT_EQ(NO_ERROR, theme.applyStyle(basic::R::style::Theme1));
110 
111   Res_value val;
112   uint32_t specFlags = 0;
113   ssize_t index = theme.getAttribute(basic::R::attr::attr1, &val, &specFlags);
114   ASSERT_GE(index, 0);
115   ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
116   ASSERT_EQ(uint32_t(100), val.data);
117 
118   index = theme.getAttribute(basic::R::attr::attr2, &val, &specFlags);
119   ASSERT_GE(index, 0);
120   ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
121   ASSERT_EQ(basic::R::integer::number1, val.data);
122 }
123 
TEST(ResTableTest,ParentThemeIsAppliedCorrectly)124 TEST(ResTableTest, ParentThemeIsAppliedCorrectly) {
125   std::string contents;
126   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
127                                       "resources.arsc", &contents));
128 
129   ResTable table;
130   ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
131 
132   ResTable::Theme theme(table);
133   ASSERT_EQ(NO_ERROR, theme.applyStyle(basic::R::style::Theme2));
134 
135   Res_value val;
136   uint32_t specFlags = 0;
137   ssize_t index = theme.getAttribute(basic::R::attr::attr1, &val, &specFlags);
138   ASSERT_GE(index, 0);
139   ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
140   ASSERT_EQ(uint32_t(300), val.data);
141 
142   index = theme.getAttribute(basic::R::attr::attr2, &val, &specFlags);
143   ASSERT_GE(index, 0);
144   ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
145   ASSERT_EQ(basic::R::integer::number1, val.data);
146 }
147 
TEST(ResTableTest,LibraryThemeIsAppliedCorrectly)148 TEST(ResTableTest, LibraryThemeIsAppliedCorrectly) {
149   std::string contents;
150   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk",
151                                       "resources.arsc", &contents));
152 
153   ResTable table;
154   ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
155 
156   ResTable::Theme theme(table);
157   ASSERT_EQ(NO_ERROR, theme.applyStyle(lib::R::style::Theme));
158 
159   Res_value val;
160   uint32_t specFlags = 0;
161   ssize_t index = theme.getAttribute(lib::R::attr::attr1, &val, &specFlags);
162   ASSERT_GE(index, 0);
163   ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
164   ASSERT_EQ(uint32_t(700), val.data);
165 
166   index = theme.getAttribute(lib::R::attr::attr2, &val, &specFlags);
167   ASSERT_GE(index, 0);
168   ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
169   ASSERT_EQ(uint32_t(700), val.data);
170 }
171 
TEST(ResTableTest,ReferenceToBagIsNotResolved)172 TEST(ResTableTest, ReferenceToBagIsNotResolved) {
173   std::string contents;
174   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
175                                       "resources.arsc", &contents));
176 
177   ResTable table;
178   ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
179 
180   Res_value val;
181   ssize_t block =
182       table.getResource(basic::R::integer::number2, &val, MAY_NOT_BE_BAG);
183   ASSERT_GE(block, 0);
184   ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
185   ASSERT_EQ(basic::R::array::integerArray1, val.data);
186 
187   ssize_t newBlock = table.resolveReference(&val, block);
188   EXPECT_EQ(block, newBlock);
189   EXPECT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
190   EXPECT_EQ(basic::R::array::integerArray1, val.data);
191 }
192 
TEST(ResTableTest,ResourcesStillAccessibleAfterParameterChange)193 TEST(ResTableTest, ResourcesStillAccessibleAfterParameterChange) {
194   std::string contents;
195   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
196                                       "resources.arsc", &contents));
197 
198   ResTable table;
199   ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
200 
201   Res_value val;
202   ssize_t block =
203       table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG);
204   ASSERT_GE(block, 0);
205   ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
206 
207   const ResTable::bag_entry* entry;
208   ssize_t count = table.lockBag(basic::R::array::integerArray1, &entry);
209   ASSERT_GE(count, 0);
210   table.unlockBag(entry);
211 
212   ResTable_config param;
213   memset(&param, 0, sizeof(param));
214   param.density = 320;
215   table.setParameters(&param);
216 
217   block = table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG);
218   ASSERT_GE(block, 0);
219   ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
220 
221   count = table.lockBag(basic::R::array::integerArray1, &entry);
222   ASSERT_GE(count, 0);
223   table.unlockBag(entry);
224 }
225 
TEST(ResTableTest,ResourceIsOverridenWithBetterConfig)226 TEST(ResTableTest, ResourceIsOverridenWithBetterConfig) {
227   std::string contents;
228   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
229                                       "resources.arsc", &contents));
230 
231   ResTable table;
232   ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
233 
234   Res_value val;
235   ssize_t block =
236       table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG);
237   ASSERT_GE(block, 0);
238   ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
239   ASSERT_EQ(uint32_t(200), val.data);
240 
241   ResTable_config param;
242   memset(&param, 0, sizeof(param));
243   param.language[0] = 's';
244   param.language[1] = 'v';
245   param.country[0] = 'S';
246   param.country[1] = 'E';
247   table.setParameters(&param);
248 
249   block = table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG);
250   ASSERT_GE(block, 0);
251   ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
252   ASSERT_EQ(uint32_t(400), val.data);
253 }
254 
TEST(ResTableTest,emptyTableHasSensibleDefaults)255 TEST(ResTableTest, emptyTableHasSensibleDefaults) {
256   const int32_t assetCookie = 1;
257 
258   ResTable table;
259   ASSERT_EQ(NO_ERROR, table.addEmpty(assetCookie));
260 
261   // Adding an empty table gives us one table!
262   ASSERT_EQ(uint32_t(1), table.getTableCount());
263 
264   // Adding an empty table doesn't mean we get packages.
265   ASSERT_EQ(uint32_t(0), table.getBasePackageCount());
266 
267   Res_value val;
268   ASSERT_LT(table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG),
269             0);
270 }
271 
testU16StringToInt(const char16_t * str,uint32_t expectedValue,bool expectSuccess,bool expectHex)272 void testU16StringToInt(const char16_t* str, uint32_t expectedValue,
273                         bool expectSuccess, bool expectHex) {
274   size_t len = std::char_traits<char16_t>::length(str);
275 
276   // Gtest can't print UTF-16 strings, so we have to convert to UTF-8 :(
277   std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
278   std::string s = convert.to_bytes(std::u16string(str, len));
279 
280   Res_value out = {};
281   ASSERT_EQ(expectSuccess, U16StringToInt(str, len, &out)) << "Failed with "
282                                                            << s;
283 
284   if (!expectSuccess) {
285     ASSERT_EQ(out.TYPE_NULL, out.dataType) << "Failed with " << s;
286     return;
287   }
288 
289   if (expectHex) {
290     ASSERT_EQ(out.TYPE_INT_HEX, out.dataType) << "Failed with " << s;
291   } else {
292     ASSERT_EQ(out.TYPE_INT_DEC, out.dataType) << "Failed with " << s;
293   }
294 
295   ASSERT_EQ(expectedValue, out.data) << "Failed with " << s;
296 }
297 
TEST(ResTableTest,U16StringToInt)298 TEST(ResTableTest, U16StringToInt) {
299   testU16StringToInt(u"", 0U, false, false);
300   testU16StringToInt(u"    ", 0U, false, false);
301   testU16StringToInt(u"\t\n", 0U, false, false);
302 
303   testU16StringToInt(u"abcd", 0U, false, false);
304   testU16StringToInt(u"10abcd", 0U, false, false);
305   testU16StringToInt(u"42 42", 0U, false, false);
306   testU16StringToInt(u"- 42", 0U, false, false);
307   testU16StringToInt(u"-", 0U, false, false);
308 
309   testU16StringToInt(u"0x", 0U, false, true);
310   testU16StringToInt(u"0xnope", 0U, false, true);
311   testU16StringToInt(u"0X42", 0U, false, true);
312   testU16StringToInt(u"0x42 0x42", 0U, false, true);
313   testU16StringToInt(u"-0x0", 0U, false, true);
314   testU16StringToInt(u"-0x42", 0U, false, true);
315   testU16StringToInt(u"- 0x42", 0U, false, true);
316 
317   // Note that u" 42" would pass. This preserves the old behavior, but it may
318   // not be desired.
319   testU16StringToInt(u"42 ", 0U, false, false);
320   testU16StringToInt(u"0x42 ", 0U, false, true);
321 
322   // Decimal cases.
323   testU16StringToInt(u"0", 0U, true, false);
324   testU16StringToInt(u"-0", 0U, true, false);
325   testU16StringToInt(u"42", 42U, true, false);
326   testU16StringToInt(u" 42", 42U, true, false);
327   testU16StringToInt(u"-42", static_cast<uint32_t>(-42), true, false);
328   testU16StringToInt(u" -42", static_cast<uint32_t>(-42), true, false);
329   testU16StringToInt(u"042", 42U, true, false);
330   testU16StringToInt(u"-042", static_cast<uint32_t>(-42), true, false);
331 
332   // Hex cases.
333   testU16StringToInt(u"0x0", 0x0, true, true);
334   testU16StringToInt(u"0x42", 0x42, true, true);
335   testU16StringToInt(u" 0x42", 0x42, true, true);
336 
337   // Just before overflow cases:
338   testU16StringToInt(u"2147483647", INT_MAX, true, false);
339   testU16StringToInt(u"-2147483648", static_cast<uint32_t>(INT_MIN), true,
340                      false);
341   testU16StringToInt(u"0xffffffff", UINT_MAX, true, true);
342 
343   // Overflow cases:
344   testU16StringToInt(u"2147483648", 0U, false, false);
345   testU16StringToInt(u"-2147483649", 0U, false, false);
346   testU16StringToInt(u"0x1ffffffff", 0U, false, true);
347 }
348 
TEST(ResTableTest,ShareButDontModifyResTable)349 TEST(ResTableTest, ShareButDontModifyResTable) {
350   std::string contents;
351   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
352                                       "resources.arsc", &contents));
353 
354   ResTable sharedTable;
355   ASSERT_EQ(NO_ERROR, sharedTable.add(contents.data(), contents.size()));
356 
357   ResTable_config param;
358   memset(&param, 0, sizeof(param));
359   param.language[0] = 'v';
360   param.language[1] = 's';
361   sharedTable.setParameters(&param);
362 
363   // Check that we get the default value for @integer:number1
364   Res_value val;
365   ssize_t block =
366       sharedTable.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG);
367   ASSERT_GE(block, 0);
368   ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
369   ASSERT_EQ(uint32_t(600), val.data);
370 
371   // Create a new table that shares the entries of the shared table.
372   ResTable table;
373   ASSERT_EQ(NO_ERROR, table.add(&sharedTable, false));
374 
375   // Set a new configuration on the new table.
376   memset(&param, 0, sizeof(param));
377   param.language[0] = 's';
378   param.language[1] = 'v';
379   param.country[0] = 'S';
380   param.country[1] = 'E';
381   table.setParameters(&param);
382 
383   // Check that we get a new value in the new table.
384   block = table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG);
385   ASSERT_GE(block, 0);
386   ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
387   ASSERT_EQ(uint32_t(400), val.data);
388 
389   // Check that we still get the old value in the shared table.
390   block =
391       sharedTable.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG);
392   ASSERT_GE(block, 0);
393   ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
394   ASSERT_EQ(uint32_t(600), val.data);
395 }
396 
TEST(ResTableTest,GetConfigurationsReturnsUniqueList)397 TEST(ResTableTest, GetConfigurationsReturnsUniqueList) {
398   std::string contents;
399   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
400                                       "resources.arsc", &contents));
401 
402   std::string system_contents;
403   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/system/system.apk",
404                                       "resources.arsc", &system_contents));
405 
406   ResTable table;
407   ASSERT_EQ(NO_ERROR,
408             table.add(system_contents.data(), system_contents.size()));
409   ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
410 
411   ResTable_config configSv;
412   memset(&configSv, 0, sizeof(configSv));
413   configSv.language[0] = 's';
414   configSv.language[1] = 'v';
415 
416   Vector<ResTable_config> configs;
417   table.getConfigurations(&configs);
418 
419   EXPECT_EQ(1, std::count(configs.begin(), configs.end(), configSv));
420 
421   Vector<String8> locales;
422   table.getLocales(&locales);
423 
424   EXPECT_EQ(1, std::count(locales.begin(), locales.end(), String8("sv")));
425 }
426 
TEST(ResTableTest,TruncatedEncodeLength)427 TEST(ResTableTest, TruncatedEncodeLength) {
428   std::string contents;
429   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/length_decode/length_decode_valid.apk",
430                                       "resources.arsc", &contents));
431 
432   ResTable table;
433   ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
434 
435   Res_value val;
436   ssize_t block = table.getResource(0x7f010001, &val, MAY_NOT_BE_BAG);
437   ASSERT_GE(block, 0);
438   ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
439 
440   const ResStringPool* pool = table.getTableStringBlock(block);
441   ASSERT_TRUE(pool != NULL);
442   ASSERT_LT(val.data, pool->size());
443 
444   // Make sure a string with a truncated length is read to its correct length
445   auto target_str8 = pool->string8At(val.data);
446   ASSERT_TRUE(target_str8.has_value());
447   ASSERT_EQ(size_t(40076), String8(target_str8->data(), target_str8->size()).size());
448   ASSERT_EQ(target_str8->data()[40075], ']');
449 
450   auto target_str16 = pool->stringAt(val.data);
451   ASSERT_TRUE(target_str16.has_value());
452   ASSERT_EQ(size_t(40076), String16(target_str16->data(), target_str16->size()).size());
453   ASSERT_EQ(target_str8->data()[40075], (char16_t) ']');
454 
455   // Load an edited apk with the null terminator removed from the end of the
456   // string
457   std::string invalid_contents;
458   ASSERT_TRUE(ReadFileFromZipToString(
459       GetTestDataPath() + "/length_decode/length_decode_invalid.apk", "resources.arsc",
460       &invalid_contents));
461   ResTable invalid_table;
462   ASSERT_EQ(NO_ERROR, invalid_table.add(invalid_contents.data(), invalid_contents.size()));
463 
464   Res_value invalid_val;
465   ssize_t invalid_block = invalid_table.getResource(0x7f010001, &invalid_val, MAY_NOT_BE_BAG);
466   ASSERT_GE(invalid_block, 0);
467   ASSERT_EQ(Res_value::TYPE_STRING, invalid_val.dataType);
468 
469   const ResStringPool* invalid_pool = invalid_table.getTableStringBlock(invalid_block);
470   ASSERT_TRUE(invalid_pool != NULL);
471   ASSERT_LT(invalid_val.data, invalid_pool->size());
472 
473   // Make sure a string with a truncated length that is not null terminated errors
474   // and does not return the string
475   ASSERT_FALSE(invalid_pool->string8At(invalid_val.data).has_value());
476   ASSERT_FALSE(invalid_pool->stringAt(invalid_val.data).has_value());
477 }
478 
479 }  // namespace android
480