blob: d4d9dcbafa5ba6e433e8a12408961c61b165cbac [file] [log] [blame]
Adam Lesinski7ad11102016-10-28 16:39:15 -07001/*
2 * Copyright (C) 2016 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#define ATRACE_TAG ATRACE_TAG_RESOURCES
18
19#include "androidfw/AssetManager2.h"
20
Adam Lesinski0c405242017-01-13 20:47:26 -080021#include <set>
22
Adam Lesinski7ad11102016-10-28 16:39:15 -070023#include "android-base/logging.h"
24#include "android-base/stringprintf.h"
25#include "utils/ByteOrder.h"
26#include "utils/Trace.h"
27
28#ifdef _WIN32
29#ifdef ERROR
30#undef ERROR
31#endif
32#endif
33
Adam Lesinski929d6512017-01-16 19:11:19 -080034#include "androidfw/ResourceUtils.h"
35
Adam Lesinski7ad11102016-10-28 16:39:15 -070036namespace android {
37
Adam Lesinski970bd8d2017-09-25 13:21:55 -070038AssetManager2::AssetManager2() {
39 memset(&configuration_, 0, sizeof(configuration_));
40}
Adam Lesinski7ad11102016-10-28 16:39:15 -070041
42bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets,
43 bool invalidate_caches) {
44 apk_assets_ = apk_assets;
Adam Lesinskida431a22016-12-29 16:08:16 -050045 BuildDynamicRefTable();
Adam Lesinski7ad11102016-10-28 16:39:15 -070046 if (invalidate_caches) {
47 InvalidateCaches(static_cast<uint32_t>(-1));
48 }
49 return true;
50}
51
Adam Lesinskida431a22016-12-29 16:08:16 -050052void AssetManager2::BuildDynamicRefTable() {
53 package_groups_.clear();
54 package_ids_.fill(0xff);
55
56 // 0x01 is reserved for the android package.
57 int next_package_id = 0x02;
58 const size_t apk_assets_count = apk_assets_.size();
59 for (size_t i = 0; i < apk_assets_count; i++) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -070060 const LoadedArsc* loaded_arsc = apk_assets_[i]->GetLoadedArsc();
61
62 for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) {
Adam Lesinskida431a22016-12-29 16:08:16 -050063 // Get the package ID or assign one if a shared library.
64 int package_id;
65 if (package->IsDynamic()) {
66 package_id = next_package_id++;
67 } else {
68 package_id = package->GetPackageId();
69 }
70
71 // Add the mapping for package ID to index if not present.
72 uint8_t idx = package_ids_[package_id];
73 if (idx == 0xff) {
74 package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size());
75 package_groups_.push_back({});
76 package_groups_.back().dynamic_ref_table.mAssignedPackageId = package_id;
77 }
78 PackageGroup* package_group = &package_groups_[idx];
79
80 // Add the package and to the set of packages with the same ID.
81 package_group->packages_.push_back(package.get());
82 package_group->cookies_.push_back(static_cast<ApkAssetsCookie>(i));
83
84 // Add the package name -> build time ID mappings.
85 for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) {
86 String16 package_name(entry.package_name.c_str(), entry.package_name.size());
87 package_group->dynamic_ref_table.mEntries.replaceValueFor(
88 package_name, static_cast<uint8_t>(entry.package_id));
89 }
90 }
91 }
92
93 // Now assign the runtime IDs so that we have a build-time to runtime ID map.
94 const auto package_groups_end = package_groups_.end();
95 for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) {
96 const std::string& package_name = iter->packages_[0]->GetPackageName();
97 for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) {
98 iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()),
99 iter->dynamic_ref_table.mAssignedPackageId);
100 }
101 }
102}
103
104void AssetManager2::DumpToLog() const {
105 base::ScopedLogSeverity _log(base::INFO);
106
107 std::string list;
108 for (size_t i = 0; i < package_ids_.size(); i++) {
109 if (package_ids_[i] != 0xff) {
110 base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]);
111 }
112 }
113 LOG(INFO) << "Package ID map: " << list;
114
115 for (const auto& package_group: package_groups_) {
116 list = "";
117 for (const auto& package : package_group.packages_) {
118 base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId());
119 }
120 LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list;
121 }
122}
Adam Lesinski7ad11102016-10-28 16:39:15 -0700123
124const ResStringPool* AssetManager2::GetStringPoolForCookie(ApkAssetsCookie cookie) const {
125 if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
126 return nullptr;
127 }
128 return apk_assets_[cookie]->GetLoadedArsc()->GetStringPool();
129}
130
Adam Lesinskida431a22016-12-29 16:08:16 -0500131const DynamicRefTable* AssetManager2::GetDynamicRefTableForPackage(uint32_t package_id) const {
132 if (package_id >= package_ids_.size()) {
133 return nullptr;
134 }
135
136 const size_t idx = package_ids_[package_id];
137 if (idx == 0xff) {
138 return nullptr;
139 }
140 return &package_groups_[idx].dynamic_ref_table;
141}
142
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800143const DynamicRefTable* AssetManager2::GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const {
144 for (const PackageGroup& package_group : package_groups_) {
145 for (const ApkAssetsCookie& package_cookie : package_group.cookies_) {
146 if (package_cookie == cookie) {
147 return &package_group.dynamic_ref_table;
148 }
149 }
150 }
151 return nullptr;
152}
153
Adam Lesinski7ad11102016-10-28 16:39:15 -0700154void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
155 const int diff = configuration_.diff(configuration);
156 configuration_ = configuration;
157
158 if (diff) {
159 InvalidateCaches(static_cast<uint32_t>(diff));
160 }
161}
162
Adam Lesinski0c405242017-01-13 20:47:26 -0800163std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
164 bool exclude_mipmap) {
165 ATRACE_CALL();
166 std::set<ResTable_config> configurations;
167 for (const PackageGroup& package_group : package_groups_) {
168 for (const LoadedPackage* package : package_group.packages_) {
169 if (exclude_system && package->IsSystem()) {
170 continue;
171 }
172 package->CollectConfigurations(exclude_mipmap, &configurations);
173 }
174 }
175 return configurations;
176}
177
178std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system,
179 bool merge_equivalent_languages) {
180 ATRACE_CALL();
181 std::set<std::string> locales;
182 for (const PackageGroup& package_group : package_groups_) {
183 for (const LoadedPackage* package : package_group.packages_) {
184 if (exclude_system && package->IsSystem()) {
185 continue;
186 }
187 package->CollectLocales(merge_equivalent_languages, &locales);
188 }
189 }
190 return locales;
191}
192
Adam Lesinski7ad11102016-10-28 16:39:15 -0700193std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) {
194 const std::string new_path = "assets/" + filename;
195 return OpenNonAsset(new_path, mode);
196}
197
198std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie,
199 Asset::AccessMode mode) {
200 const std::string new_path = "assets/" + filename;
201 return OpenNonAsset(new_path, cookie, mode);
202}
203
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800204std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) {
205 ATRACE_CALL();
206
207 std::string full_path = "assets/" + dirname;
208 std::unique_ptr<SortedVector<AssetDir::FileInfo>> files =
209 util::make_unique<SortedVector<AssetDir::FileInfo>>();
210
211 // Start from the back.
212 for (auto iter = apk_assets_.rbegin(); iter != apk_assets_.rend(); ++iter) {
213 const ApkAssets* apk_assets = *iter;
214
215 auto func = [&](const StringPiece& name, FileType type) {
216 AssetDir::FileInfo info;
217 info.setFileName(String8(name.data(), name.size()));
218 info.setFileType(type);
219 info.setSourceName(String8(apk_assets->GetPath().c_str()));
220 files->add(info);
221 };
222
223 if (!apk_assets->ForEachFile(full_path, func)) {
224 return {};
225 }
226 }
227
228 std::unique_ptr<AssetDir> asset_dir = util::make_unique<AssetDir>();
229 asset_dir->setFileList(files.release());
230 return asset_dir;
231}
232
Adam Lesinski7ad11102016-10-28 16:39:15 -0700233// Search in reverse because that's how we used to do it and we need to preserve behaviour.
234// This is unfortunate, because ClassLoaders delegate to the parent first, so the order
235// is inconsistent for split APKs.
236std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
237 Asset::AccessMode mode,
238 ApkAssetsCookie* out_cookie) {
239 ATRACE_CALL();
240 for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) {
241 std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode);
242 if (asset) {
243 if (out_cookie != nullptr) {
244 *out_cookie = i;
245 }
246 return asset;
247 }
248 }
249
250 if (out_cookie != nullptr) {
251 *out_cookie = kInvalidCookie;
252 }
253 return {};
254}
255
256std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
257 ApkAssetsCookie cookie, Asset::AccessMode mode) {
258 ATRACE_CALL();
259 if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
260 return {};
261 }
262 return apk_assets_[cookie]->Open(filename, mode);
263}
264
265ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
Adam Lesinskida431a22016-12-29 16:08:16 -0500266 bool stop_at_first_match, LoadedArscEntry* out_entry,
Adam Lesinski7ad11102016-10-28 16:39:15 -0700267 ResTable_config* out_selected_config,
268 uint32_t* out_flags) {
269 ATRACE_CALL();
270
271 // Might use this if density_override != 0.
272 ResTable_config density_override_config;
273
274 // Select our configuration or generate a density override configuration.
275 ResTable_config* desired_config = &configuration_;
276 if (density_override != 0 && density_override != configuration_.density) {
277 density_override_config = configuration_;
278 density_override_config.density = density_override;
279 desired_config = &density_override_config;
280 }
281
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800282 if (!is_valid_resid(resid)) {
Adam Lesinskida431a22016-12-29 16:08:16 -0500283 LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
284 return kInvalidCookie;
285 }
286
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800287 const uint32_t package_id = get_package_id(resid);
288 const uint8_t type_idx = get_type_id(resid) - 1;
289 const uint16_t entry_id = get_entry_id(resid);
290
Adam Lesinskida431a22016-12-29 16:08:16 -0500291 const uint8_t idx = package_ids_[package_id];
292 if (idx == 0xff) {
293 LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid);
294 return kInvalidCookie;
295 }
296
297 LoadedArscEntry best_entry;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700298 ResTable_config best_config;
Adam Lesinskida431a22016-12-29 16:08:16 -0500299 ApkAssetsCookie best_cookie = kInvalidCookie;
300 uint32_t cumulated_flags = 0u;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700301
Adam Lesinskida431a22016-12-29 16:08:16 -0500302 const PackageGroup& package_group = package_groups_[idx];
303 const size_t package_count = package_group.packages_.size();
304 for (size_t i = 0; i < package_count; i++) {
305 LoadedArscEntry current_entry;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700306 ResTable_config current_config;
Adam Lesinskida431a22016-12-29 16:08:16 -0500307 uint32_t current_flags = 0;
308
309 const LoadedPackage* loaded_package = package_group.packages_[i];
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800310 if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, &current_entry,
Adam Lesinskida431a22016-12-29 16:08:16 -0500311 &current_config, &current_flags)) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700312 continue;
313 }
314
Adam Lesinskida431a22016-12-29 16:08:16 -0500315 cumulated_flags |= current_flags;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700316
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700317 if (best_cookie == kInvalidCookie || current_config.isBetterThan(best_config, desired_config) ||
318 (loaded_package->IsOverlay() && current_config.compare(best_config) == 0)) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700319 best_entry = current_entry;
320 best_config = current_config;
Adam Lesinskida431a22016-12-29 16:08:16 -0500321 best_cookie = package_group.cookies_[i];
Adam Lesinski7ad11102016-10-28 16:39:15 -0700322 if (stop_at_first_match) {
323 break;
324 }
325 }
326 }
327
Adam Lesinskida431a22016-12-29 16:08:16 -0500328 if (best_cookie == kInvalidCookie) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700329 return kInvalidCookie;
330 }
331
332 *out_entry = best_entry;
Adam Lesinskida431a22016-12-29 16:08:16 -0500333 out_entry->dynamic_ref_table = &package_group.dynamic_ref_table;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700334 *out_selected_config = best_config;
335 *out_flags = cumulated_flags;
Adam Lesinskida431a22016-12-29 16:08:16 -0500336 return best_cookie;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700337}
338
339bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) {
340 ATRACE_CALL();
341
Adam Lesinskida431a22016-12-29 16:08:16 -0500342 LoadedArscEntry entry;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700343 ResTable_config config;
344 uint32_t flags = 0u;
345 ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
346 true /* stop_at_first_match */, &entry, &config, &flags);
347 if (cookie == kInvalidCookie) {
348 return false;
349 }
350
Adam Lesinskida431a22016-12-29 16:08:16 -0500351 const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid);
352 if (package == nullptr) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700353 return false;
354 }
355
Adam Lesinskida431a22016-12-29 16:08:16 -0500356 out_name->package = package->GetPackageName().data();
357 out_name->package_len = package->GetPackageName().size();
Adam Lesinski7ad11102016-10-28 16:39:15 -0700358
359 out_name->type = entry.type_string_ref.string8(&out_name->type_len);
360 out_name->type16 = nullptr;
361 if (out_name->type == nullptr) {
362 out_name->type16 = entry.type_string_ref.string16(&out_name->type_len);
363 if (out_name->type16 == nullptr) {
364 return false;
365 }
366 }
367
368 out_name->entry = entry.entry_string_ref.string8(&out_name->entry_len);
369 out_name->entry16 = nullptr;
370 if (out_name->entry == nullptr) {
371 out_name->entry16 = entry.entry_string_ref.string16(&out_name->entry_len);
372 if (out_name->entry16 == nullptr) {
373 return false;
374 }
375 }
376 return true;
377}
378
379bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) {
Adam Lesinskida431a22016-12-29 16:08:16 -0500380 LoadedArscEntry entry;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700381 ResTable_config config;
382 ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
383 false /* stop_at_first_match */, &entry, &config, out_flags);
384 return cookie != kInvalidCookie;
385}
386
387ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
388 uint16_t density_override, Res_value* out_value,
389 ResTable_config* out_selected_config,
390 uint32_t* out_flags) {
391 ATRACE_CALL();
392
Adam Lesinskida431a22016-12-29 16:08:16 -0500393 LoadedArscEntry entry;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700394 ResTable_config config;
395 uint32_t flags = 0u;
396 ApkAssetsCookie cookie =
397 FindEntry(resid, density_override, false /* stop_at_first_match */, &entry, &config, &flags);
398 if (cookie == kInvalidCookie) {
399 return kInvalidCookie;
400 }
401
402 if (dtohl(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) {
403 if (!may_be_bag) {
404 LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
Adam Lesinski0c405242017-01-13 20:47:26 -0800405 return kInvalidCookie;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700406 }
Adam Lesinski0c405242017-01-13 20:47:26 -0800407
408 // Create a reference since we can't represent this complex type as a Res_value.
409 out_value->dataType = Res_value::TYPE_REFERENCE;
410 out_value->data = resid;
411 *out_selected_config = config;
412 *out_flags = flags;
413 return cookie;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700414 }
415
416 const Res_value* device_value = reinterpret_cast<const Res_value*>(
417 reinterpret_cast<const uint8_t*>(entry.entry) + dtohs(entry.entry->size));
418 out_value->copyFrom_dtoh(*device_value);
Adam Lesinskida431a22016-12-29 16:08:16 -0500419
420 // Convert the package ID to the runtime assigned package ID.
421 entry.dynamic_ref_table->lookupResourceValue(out_value);
422
Adam Lesinski7ad11102016-10-28 16:39:15 -0700423 *out_selected_config = config;
424 *out_flags = flags;
425 return cookie;
426}
427
Adam Lesinski0c405242017-01-13 20:47:26 -0800428ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
429 ResTable_config* in_out_selected_config,
430 uint32_t* in_out_flags,
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800431 uint32_t* out_last_reference) {
Adam Lesinski0c405242017-01-13 20:47:26 -0800432 ATRACE_CALL();
433 constexpr const int kMaxIterations = 20;
434
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800435 *out_last_reference = 0u;
Adam Lesinski0c405242017-01-13 20:47:26 -0800436 for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE &&
437 in_out_value->data != 0u && iteration < kMaxIterations;
438 iteration++) {
439 if (out_last_reference != nullptr) {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800440 *out_last_reference = in_out_value->data;
Adam Lesinski0c405242017-01-13 20:47:26 -0800441 }
442 uint32_t new_flags = 0u;
443 cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/,
444 in_out_value, in_out_selected_config, &new_flags);
445 if (cookie == kInvalidCookie) {
446 return kInvalidCookie;
447 }
448 if (in_out_flags != nullptr) {
449 *in_out_flags |= new_flags;
450 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800451 if (*out_last_reference == in_out_value->data) {
Adam Lesinski0c405242017-01-13 20:47:26 -0800452 // This reference can't be resolved, so exit now and let the caller deal with it.
453 return cookie;
454 }
455 }
456 return cookie;
457}
458
Adam Lesinski7ad11102016-10-28 16:39:15 -0700459const ResolvedBag* AssetManager2::GetBag(uint32_t resid) {
460 ATRACE_CALL();
461
462 auto cached_iter = cached_bags_.find(resid);
463 if (cached_iter != cached_bags_.end()) {
464 return cached_iter->second.get();
465 }
466
Adam Lesinskida431a22016-12-29 16:08:16 -0500467 LoadedArscEntry entry;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700468 ResTable_config config;
469 uint32_t flags = 0u;
470 ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
471 false /* stop_at_first_match */, &entry, &config, &flags);
472 if (cookie == kInvalidCookie) {
473 return nullptr;
474 }
475
476 // Check that the size of the entry header is at least as big as
477 // the desired ResTable_map_entry. Also verify that the entry
478 // was intended to be a map.
479 if (dtohs(entry.entry->size) < sizeof(ResTable_map_entry) ||
480 (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) {
481 // Not a bag, nothing to do.
482 return nullptr;
483 }
484
485 const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry.entry);
486 const ResTable_map* map_entry =
487 reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size);
488 const ResTable_map* const map_entry_end = map_entry + dtohl(map->count);
489
Adam Lesinskida431a22016-12-29 16:08:16 -0500490 uint32_t parent_resid = dtohl(map->parent.ident);
491 if (parent_resid == 0) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700492 // There is no parent, meaning there is nothing to inherit and we can do a simple
493 // copy of the entries in the map.
494 const size_t entry_count = map_entry_end - map_entry;
495 util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
496 malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))};
497 ResolvedBag::Entry* new_entry = new_bag->entries;
498 for (; map_entry != map_entry_end; ++map_entry) {
Adam Lesinskida431a22016-12-29 16:08:16 -0500499 uint32_t new_key = dtohl(map_entry->name.ident);
Adam Lesinski929d6512017-01-16 19:11:19 -0800500 if (!is_internal_resid(new_key)) {
Adam Lesinskida431a22016-12-29 16:08:16 -0500501 // Attributes, arrays, etc don't have a resource id as the name. They specify
502 // other data, which would be wrong to change via a lookup.
503 if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
504 LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid);
505 return nullptr;
506 }
507 }
Adam Lesinski7ad11102016-10-28 16:39:15 -0700508 new_entry->cookie = cookie;
509 new_entry->value.copyFrom_dtoh(map_entry->value);
Adam Lesinskida431a22016-12-29 16:08:16 -0500510 new_entry->key = new_key;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700511 new_entry->key_pool = nullptr;
512 new_entry->type_pool = nullptr;
513 ++new_entry;
514 }
515 new_bag->type_spec_flags = flags;
516 new_bag->entry_count = static_cast<uint32_t>(entry_count);
517 ResolvedBag* result = new_bag.get();
518 cached_bags_[resid] = std::move(new_bag);
519 return result;
520 }
521
Adam Lesinskida431a22016-12-29 16:08:16 -0500522 // In case the parent is a dynamic reference, resolve it.
523 entry.dynamic_ref_table->lookupResourceId(&parent_resid);
524
Adam Lesinski7ad11102016-10-28 16:39:15 -0700525 // Get the parent and do a merge of the keys.
Adam Lesinskida431a22016-12-29 16:08:16 -0500526 const ResolvedBag* parent_bag = GetBag(parent_resid);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700527 if (parent_bag == nullptr) {
528 // Failed to get the parent that should exist.
Adam Lesinskida431a22016-12-29 16:08:16 -0500529 LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700530 return nullptr;
531 }
532
533 // Combine flags from the parent and our own bag.
534 flags |= parent_bag->type_spec_flags;
535
536 // Create the max possible entries we can make. Once we construct the bag,
537 // we will realloc to fit to size.
538 const size_t max_count = parent_bag->entry_count + dtohl(map->count);
George Burgess IV09b119f2017-07-25 15:00:04 -0700539 util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
540 malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))))};
Adam Lesinski7ad11102016-10-28 16:39:15 -0700541 ResolvedBag::Entry* new_entry = new_bag->entries;
542
543 const ResolvedBag::Entry* parent_entry = parent_bag->entries;
544 const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count;
545
546 // The keys are expected to be in sorted order. Merge the two bags.
547 while (map_entry != map_entry_end && parent_entry != parent_entry_end) {
Adam Lesinskida431a22016-12-29 16:08:16 -0500548 uint32_t child_key = dtohl(map_entry->name.ident);
Adam Lesinski929d6512017-01-16 19:11:19 -0800549 if (!is_internal_resid(child_key)) {
Adam Lesinskida431a22016-12-29 16:08:16 -0500550 if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) {
551 LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid);
552 return nullptr;
553 }
554 }
555
Adam Lesinski7ad11102016-10-28 16:39:15 -0700556 if (child_key <= parent_entry->key) {
557 // Use the child key if it comes before the parent
558 // or is equal to the parent (overrides).
559 new_entry->cookie = cookie;
560 new_entry->value.copyFrom_dtoh(map_entry->value);
561 new_entry->key = child_key;
562 new_entry->key_pool = nullptr;
563 new_entry->type_pool = nullptr;
564 ++map_entry;
565 } else {
566 // Take the parent entry as-is.
567 memcpy(new_entry, parent_entry, sizeof(*new_entry));
568 }
569
570 if (child_key >= parent_entry->key) {
571 // Move to the next parent entry if we used it or it was overridden.
572 ++parent_entry;
573 }
574 // Increment to the next entry to fill.
575 ++new_entry;
576 }
577
578 // Finish the child entries if they exist.
579 while (map_entry != map_entry_end) {
Adam Lesinskida431a22016-12-29 16:08:16 -0500580 uint32_t new_key = dtohl(map_entry->name.ident);
Adam Lesinski929d6512017-01-16 19:11:19 -0800581 if (!is_internal_resid(new_key)) {
Adam Lesinskida431a22016-12-29 16:08:16 -0500582 if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
583 LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid);
584 return nullptr;
585 }
586 }
Adam Lesinski7ad11102016-10-28 16:39:15 -0700587 new_entry->cookie = cookie;
588 new_entry->value.copyFrom_dtoh(map_entry->value);
Adam Lesinskida431a22016-12-29 16:08:16 -0500589 new_entry->key = new_key;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700590 new_entry->key_pool = nullptr;
591 new_entry->type_pool = nullptr;
592 ++map_entry;
593 ++new_entry;
594 }
595
596 // Finish the parent entries if they exist.
597 if (parent_entry != parent_entry_end) {
598 // Take the rest of the parent entries as-is.
599 const size_t num_entries_to_copy = parent_entry_end - parent_entry;
600 memcpy(new_entry, parent_entry, num_entries_to_copy * sizeof(*new_entry));
601 new_entry += num_entries_to_copy;
602 }
603
604 // Resize the resulting array to fit.
605 const size_t actual_count = new_entry - new_bag->entries;
606 if (actual_count != max_count) {
George Burgess IV09b119f2017-07-25 15:00:04 -0700607 new_bag.reset(reinterpret_cast<ResolvedBag*>(realloc(
608 new_bag.release(), sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry)))));
Adam Lesinski7ad11102016-10-28 16:39:15 -0700609 }
610
George Burgess IV09b119f2017-07-25 15:00:04 -0700611 new_bag->type_spec_flags = flags;
612 new_bag->entry_count = static_cast<uint32_t>(actual_count);
613 ResolvedBag* result = new_bag.get();
614 cached_bags_[resid] = std::move(new_bag);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700615 return result;
616}
617
Adam Lesinski929d6512017-01-16 19:11:19 -0800618static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) {
619 ssize_t len =
620 utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str.data()), str.size(), false);
621 if (len < 0) {
622 return false;
623 }
624 out->resize(static_cast<size_t>(len));
625 utf8_to_utf16(reinterpret_cast<const uint8_t*>(str.data()), str.size(), &*out->begin(),
626 static_cast<size_t>(len + 1));
627 return true;
628}
629
Adam Lesinski0c405242017-01-13 20:47:26 -0800630uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
631 const std::string& fallback_type,
632 const std::string& fallback_package) {
Adam Lesinski929d6512017-01-16 19:11:19 -0800633 StringPiece package_name, type, entry;
634 if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) {
635 return 0u;
636 }
637
638 if (entry.empty()) {
639 return 0u;
640 }
641
642 if (package_name.empty()) {
643 package_name = fallback_package;
644 }
645
646 if (type.empty()) {
647 type = fallback_type;
648 }
649
650 std::u16string type16;
651 if (!Utf8ToUtf16(type, &type16)) {
652 return 0u;
653 }
654
655 std::u16string entry16;
656 if (!Utf8ToUtf16(entry, &entry16)) {
657 return 0u;
658 }
659
660 const StringPiece16 kAttr16 = u"attr";
661 const static std::u16string kAttrPrivate16 = u"^attr-private";
662
663 for (const PackageGroup& package_group : package_groups_) {
664 for (const LoadedPackage* package : package_group.packages_) {
665 if (package_name != package->GetPackageName()) {
666 // All packages in the same group are expected to have the same package name.
667 break;
668 }
669
670 uint32_t resid = package->FindEntryByName(type16, entry16);
671 if (resid == 0u && kAttr16 == type16) {
672 // Private attributes in libraries (such as the framework) are sometimes encoded
673 // under the type '^attr-private' in order to leave the ID space of public 'attr'
674 // free for future additions. Check '^attr-private' for the same name.
675 resid = package->FindEntryByName(kAttrPrivate16, entry16);
676 }
677
678 if (resid != 0u) {
679 return fix_package_id(resid, package_group.dynamic_ref_table.mAssignedPackageId);
680 }
681 }
682 }
Adam Lesinski0c405242017-01-13 20:47:26 -0800683 return 0u;
684}
685
Adam Lesinski7ad11102016-10-28 16:39:15 -0700686void AssetManager2::InvalidateCaches(uint32_t diff) {
687 if (diff == 0xffffffffu) {
688 // Everything must go.
689 cached_bags_.clear();
690 return;
691 }
692
693 // Be more conservative with what gets purged. Only if the bag has other possible
694 // variations with respect to what changed (diff) should we remove it.
695 for (auto iter = cached_bags_.cbegin(); iter != cached_bags_.cend();) {
696 if (diff & iter->second->type_spec_flags) {
697 iter = cached_bags_.erase(iter);
698 } else {
699 ++iter;
700 }
701 }
702}
703
704std::unique_ptr<Theme> AssetManager2::NewTheme() { return std::unique_ptr<Theme>(new Theme(this)); }
705
706bool Theme::ApplyStyle(uint32_t resid, bool force) {
707 ATRACE_CALL();
708
709 const ResolvedBag* bag = asset_manager_->GetBag(resid);
710 if (bag == nullptr) {
711 return false;
712 }
713
714 // Merge the flags from this style.
715 type_spec_flags_ |= bag->type_spec_flags;
716
717 // On the first iteration, verify the attribute IDs and
718 // update the entry count in each type.
719 const auto bag_iter_end = end(bag);
720 for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) {
721 const uint32_t attr_resid = bag_iter->key;
722
723 // If the resource ID passed in is not a style, the key can be
724 // some other identifier that is not a resource ID.
Adam Lesinski929d6512017-01-16 19:11:19 -0800725 if (!is_valid_resid(attr_resid)) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700726 return false;
727 }
728
Adam Lesinski929d6512017-01-16 19:11:19 -0800729 const uint32_t package_idx = get_package_id(attr_resid);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700730
731 // The type ID is 1-based, so subtract 1 to get an index.
Adam Lesinski929d6512017-01-16 19:11:19 -0800732 const uint32_t type_idx = get_type_id(attr_resid) - 1;
733 const uint32_t entry_idx = get_entry_id(attr_resid);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700734
735 std::unique_ptr<Package>& package = packages_[package_idx];
736 if (package == nullptr) {
737 package.reset(new Package());
738 }
739
740 util::unique_cptr<Type>& type = package->types[type_idx];
741 if (type == nullptr) {
742 // Set the initial capacity to take up a total amount of 1024 bytes.
743 constexpr uint32_t kInitialCapacity = (1024u - sizeof(Type)) / sizeof(Entry);
744 const uint32_t initial_capacity = std::max(entry_idx, kInitialCapacity);
745 type.reset(
746 reinterpret_cast<Type*>(calloc(sizeof(Type) + (initial_capacity * sizeof(Entry)), 1)));
747 type->entry_capacity = initial_capacity;
748 }
749
750 // Set the entry_count to include this entry. We will populate
751 // and resize the array as necessary in the next pass.
752 if (entry_idx + 1 > type->entry_count) {
753 // Increase the entry count to include this.
754 type->entry_count = entry_idx + 1;
755 }
756 }
757
758 // On the second pass, we will realloc to fit the entry counts
759 // and populate the structures.
760 for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) {
761 const uint32_t attr_resid = bag_iter->key;
Adam Lesinski929d6512017-01-16 19:11:19 -0800762 const uint32_t package_idx = get_package_id(attr_resid);
763 const uint32_t type_idx = get_type_id(attr_resid) - 1;
764 const uint32_t entry_idx = get_entry_id(attr_resid);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700765 Package* package = packages_[package_idx].get();
766 util::unique_cptr<Type>& type = package->types[type_idx];
767 if (type->entry_count != type->entry_capacity) {
768 // Resize to fit the actual entries that will be included.
769 Type* type_ptr = type.release();
770 type.reset(reinterpret_cast<Type*>(
771 realloc(type_ptr, sizeof(Type) + (type_ptr->entry_count * sizeof(Entry)))));
772 if (type->entry_capacity < type->entry_count) {
773 // Clear the newly allocated memory (which does not get zero initialized).
774 // We need to do this because we |= type_spec_flags.
775 memset(type->entries + type->entry_capacity, 0,
776 sizeof(Entry) * (type->entry_count - type->entry_capacity));
777 }
778 type->entry_capacity = type->entry_count;
779 }
780 Entry& entry = type->entries[entry_idx];
781 if (force || entry.value.dataType == Res_value::TYPE_NULL) {
782 entry.cookie = bag_iter->cookie;
783 entry.type_spec_flags |= bag->type_spec_flags;
784 entry.value = bag_iter->value;
785 }
786 }
787 return true;
788}
789
790ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value,
791 uint32_t* out_flags) const {
792 constexpr const int kMaxIterations = 20;
793
794 uint32_t type_spec_flags = 0u;
795
796 for (int iterations_left = kMaxIterations; iterations_left > 0; iterations_left--) {
Adam Lesinski929d6512017-01-16 19:11:19 -0800797 if (!is_valid_resid(resid)) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700798 return kInvalidCookie;
799 }
800
Adam Lesinski929d6512017-01-16 19:11:19 -0800801 const uint32_t package_idx = get_package_id(resid);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700802
803 // Type ID is 1-based, subtract 1 to get the index.
Adam Lesinski929d6512017-01-16 19:11:19 -0800804 const uint32_t type_idx = get_type_id(resid) - 1;
805 const uint32_t entry_idx = get_entry_id(resid);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700806
807 const Package* package = packages_[package_idx].get();
808 if (package == nullptr) {
809 return kInvalidCookie;
810 }
811
812 const Type* type = package->types[type_idx].get();
813 if (type == nullptr) {
814 return kInvalidCookie;
815 }
816
817 if (entry_idx >= type->entry_count) {
818 return kInvalidCookie;
819 }
820
821 const Entry& entry = type->entries[entry_idx];
822 type_spec_flags |= entry.type_spec_flags;
823
824 switch (entry.value.dataType) {
Adam Lesinskida431a22016-12-29 16:08:16 -0500825 case Res_value::TYPE_NULL:
826 return kInvalidCookie;
827
Adam Lesinski7ad11102016-10-28 16:39:15 -0700828 case Res_value::TYPE_ATTRIBUTE:
829 resid = entry.value.data;
830 break;
831
Adam Lesinskida431a22016-12-29 16:08:16 -0500832 case Res_value::TYPE_DYNAMIC_ATTRIBUTE: {
833 // Resolve the dynamic attribute to a normal attribute
834 // (with the right package ID).
835 resid = entry.value.data;
836 const DynamicRefTable* ref_table =
837 asset_manager_->GetDynamicRefTableForPackage(package_idx);
838 if (ref_table == nullptr || ref_table->lookupResourceId(&resid) != NO_ERROR) {
839 LOG(ERROR) << base::StringPrintf("Failed to resolve dynamic attribute 0x%08x", resid);
840 return kInvalidCookie;
841 }
842 } break;
843
844 case Res_value::TYPE_DYNAMIC_REFERENCE: {
845 // Resolve the dynamic reference to a normal reference
846 // (with the right package ID).
847 out_value->dataType = Res_value::TYPE_REFERENCE;
848 out_value->data = entry.value.data;
849 const DynamicRefTable* ref_table =
850 asset_manager_->GetDynamicRefTableForPackage(package_idx);
851 if (ref_table == nullptr || ref_table->lookupResourceId(&out_value->data) != NO_ERROR) {
852 LOG(ERROR) << base::StringPrintf("Failed to resolve dynamic reference 0x%08x",
853 out_value->data);
854 return kInvalidCookie;
855 }
856
857 if (out_flags != nullptr) {
858 *out_flags = type_spec_flags;
859 }
860 return entry.cookie;
861 }
Adam Lesinski7ad11102016-10-28 16:39:15 -0700862
863 default:
864 *out_value = entry.value;
865 if (out_flags != nullptr) {
866 *out_flags = type_spec_flags;
867 }
868 return entry.cookie;
869 }
870 }
871
872 LOG(WARNING) << base::StringPrintf("Too many (%d) attribute references, stopped at: 0x%08x",
873 kMaxIterations, resid);
874 return kInvalidCookie;
875}
876
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800877ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
878 ResTable_config* in_out_selected_config,
879 uint32_t* in_out_type_spec_flags,
880 uint32_t* out_last_ref) {
881 if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) {
882 uint32_t new_flags;
883 cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags);
884 if (cookie == kInvalidCookie) {
885 return kInvalidCookie;
886 }
887
888 if (in_out_type_spec_flags != nullptr) {
889 *in_out_type_spec_flags |= new_flags;
890 }
891 }
892 return asset_manager_->ResolveReference(cookie, in_out_value, in_out_selected_config,
893 in_out_type_spec_flags, out_last_ref);
894}
895
Adam Lesinski7ad11102016-10-28 16:39:15 -0700896void Theme::Clear() {
897 type_spec_flags_ = 0u;
898 for (std::unique_ptr<Package>& package : packages_) {
899 package.reset();
900 }
901}
902
903bool Theme::SetTo(const Theme& o) {
904 if (this == &o) {
905 return true;
906 }
907
Adam Lesinski7ad11102016-10-28 16:39:15 -0700908 type_spec_flags_ = o.type_spec_flags_;
909
Adam Lesinski03ebac82017-09-25 13:10:14 -0700910 const bool copy_only_system = asset_manager_ != o.asset_manager_;
911
Adam Lesinskida431a22016-12-29 16:08:16 -0500912 for (size_t p = 0; p < packages_.size(); p++) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700913 const Package* package = o.packages_[p].get();
Adam Lesinski03ebac82017-09-25 13:10:14 -0700914 if (package == nullptr || (copy_only_system && p != 0x01)) {
915 // The other theme doesn't have this package, clear ours.
Adam Lesinski7ad11102016-10-28 16:39:15 -0700916 packages_[p].reset();
917 continue;
918 }
919
Adam Lesinski03ebac82017-09-25 13:10:14 -0700920 if (packages_[p] == nullptr) {
921 // The other theme has this package, but we don't. Make one.
922 packages_[p].reset(new Package());
923 }
924
Adam Lesinskida431a22016-12-29 16:08:16 -0500925 for (size_t t = 0; t < package->types.size(); t++) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700926 const Type* type = package->types[t].get();
927 if (type == nullptr) {
Adam Lesinski03ebac82017-09-25 13:10:14 -0700928 // The other theme doesn't have this type, clear ours.
Adam Lesinski7ad11102016-10-28 16:39:15 -0700929 packages_[p]->types[t].reset();
930 continue;
931 }
932
Adam Lesinski03ebac82017-09-25 13:10:14 -0700933 // Create a new type and update it to theirs.
Adam Lesinski7ad11102016-10-28 16:39:15 -0700934 const size_t type_alloc_size = sizeof(Type) + (type->entry_capacity * sizeof(Entry));
935 void* copied_data = malloc(type_alloc_size);
936 memcpy(copied_data, type, type_alloc_size);
937 packages_[p]->types[t].reset(reinterpret_cast<Type*>(copied_data));
938 }
939 }
940 return true;
941}
942
943} // namespace android