blob: 542a125f018be16ac096c1b888fd671f6a2487a7 [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
34namespace android {
35
36AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); }
37
38bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets,
39 bool invalidate_caches) {
40 apk_assets_ = apk_assets;
Adam Lesinskida431a22016-12-29 16:08:16 -050041 BuildDynamicRefTable();
Adam Lesinski7ad11102016-10-28 16:39:15 -070042 if (invalidate_caches) {
43 InvalidateCaches(static_cast<uint32_t>(-1));
44 }
45 return true;
46}
47
Adam Lesinskida431a22016-12-29 16:08:16 -050048void AssetManager2::BuildDynamicRefTable() {
49 package_groups_.clear();
50 package_ids_.fill(0xff);
51
52 // 0x01 is reserved for the android package.
53 int next_package_id = 0x02;
54 const size_t apk_assets_count = apk_assets_.size();
55 for (size_t i = 0; i < apk_assets_count; i++) {
56 const ApkAssets* apk_asset = apk_assets_[i];
57 for (const std::unique_ptr<const LoadedPackage>& package :
58 apk_asset->GetLoadedArsc()->GetPackages()) {
59 // Get the package ID or assign one if a shared library.
60 int package_id;
61 if (package->IsDynamic()) {
62 package_id = next_package_id++;
63 } else {
64 package_id = package->GetPackageId();
65 }
66
67 // Add the mapping for package ID to index if not present.
68 uint8_t idx = package_ids_[package_id];
69 if (idx == 0xff) {
70 package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size());
71 package_groups_.push_back({});
72 package_groups_.back().dynamic_ref_table.mAssignedPackageId = package_id;
73 }
74 PackageGroup* package_group = &package_groups_[idx];
75
76 // Add the package and to the set of packages with the same ID.
77 package_group->packages_.push_back(package.get());
78 package_group->cookies_.push_back(static_cast<ApkAssetsCookie>(i));
79
80 // Add the package name -> build time ID mappings.
81 for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) {
82 String16 package_name(entry.package_name.c_str(), entry.package_name.size());
83 package_group->dynamic_ref_table.mEntries.replaceValueFor(
84 package_name, static_cast<uint8_t>(entry.package_id));
85 }
86 }
87 }
88
89 // Now assign the runtime IDs so that we have a build-time to runtime ID map.
90 const auto package_groups_end = package_groups_.end();
91 for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) {
92 const std::string& package_name = iter->packages_[0]->GetPackageName();
93 for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) {
94 iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()),
95 iter->dynamic_ref_table.mAssignedPackageId);
96 }
97 }
98}
99
100void AssetManager2::DumpToLog() const {
101 base::ScopedLogSeverity _log(base::INFO);
102
103 std::string list;
104 for (size_t i = 0; i < package_ids_.size(); i++) {
105 if (package_ids_[i] != 0xff) {
106 base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]);
107 }
108 }
109 LOG(INFO) << "Package ID map: " << list;
110
111 for (const auto& package_group: package_groups_) {
112 list = "";
113 for (const auto& package : package_group.packages_) {
114 base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId());
115 }
116 LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list;
117 }
118}
Adam Lesinski7ad11102016-10-28 16:39:15 -0700119
120const ResStringPool* AssetManager2::GetStringPoolForCookie(ApkAssetsCookie cookie) const {
121 if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
122 return nullptr;
123 }
124 return apk_assets_[cookie]->GetLoadedArsc()->GetStringPool();
125}
126
Adam Lesinskida431a22016-12-29 16:08:16 -0500127const DynamicRefTable* AssetManager2::GetDynamicRefTableForPackage(uint32_t package_id) const {
128 if (package_id >= package_ids_.size()) {
129 return nullptr;
130 }
131
132 const size_t idx = package_ids_[package_id];
133 if (idx == 0xff) {
134 return nullptr;
135 }
136 return &package_groups_[idx].dynamic_ref_table;
137}
138
Adam Lesinski7ad11102016-10-28 16:39:15 -0700139void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
140 const int diff = configuration_.diff(configuration);
141 configuration_ = configuration;
142
143 if (diff) {
144 InvalidateCaches(static_cast<uint32_t>(diff));
145 }
146}
147
Adam Lesinski0c405242017-01-13 20:47:26 -0800148std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
149 bool exclude_mipmap) {
150 ATRACE_CALL();
151 std::set<ResTable_config> configurations;
152 for (const PackageGroup& package_group : package_groups_) {
153 for (const LoadedPackage* package : package_group.packages_) {
154 if (exclude_system && package->IsSystem()) {
155 continue;
156 }
157 package->CollectConfigurations(exclude_mipmap, &configurations);
158 }
159 }
160 return configurations;
161}
162
163std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system,
164 bool merge_equivalent_languages) {
165 ATRACE_CALL();
166 std::set<std::string> locales;
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->CollectLocales(merge_equivalent_languages, &locales);
173 }
174 }
175 return locales;
176}
177
Adam Lesinski7ad11102016-10-28 16:39:15 -0700178std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) {
179 const std::string new_path = "assets/" + filename;
180 return OpenNonAsset(new_path, mode);
181}
182
183std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie,
184 Asset::AccessMode mode) {
185 const std::string new_path = "assets/" + filename;
186 return OpenNonAsset(new_path, cookie, mode);
187}
188
189// Search in reverse because that's how we used to do it and we need to preserve behaviour.
190// This is unfortunate, because ClassLoaders delegate to the parent first, so the order
191// is inconsistent for split APKs.
192std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
193 Asset::AccessMode mode,
194 ApkAssetsCookie* out_cookie) {
195 ATRACE_CALL();
196 for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) {
197 std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode);
198 if (asset) {
199 if (out_cookie != nullptr) {
200 *out_cookie = i;
201 }
202 return asset;
203 }
204 }
205
206 if (out_cookie != nullptr) {
207 *out_cookie = kInvalidCookie;
208 }
209 return {};
210}
211
212std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
213 ApkAssetsCookie cookie, Asset::AccessMode mode) {
214 ATRACE_CALL();
215 if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
216 return {};
217 }
218 return apk_assets_[cookie]->Open(filename, mode);
219}
220
221ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
Adam Lesinskida431a22016-12-29 16:08:16 -0500222 bool stop_at_first_match, LoadedArscEntry* out_entry,
Adam Lesinski7ad11102016-10-28 16:39:15 -0700223 ResTable_config* out_selected_config,
224 uint32_t* out_flags) {
225 ATRACE_CALL();
226
227 // Might use this if density_override != 0.
228 ResTable_config density_override_config;
229
230 // Select our configuration or generate a density override configuration.
231 ResTable_config* desired_config = &configuration_;
232 if (density_override != 0 && density_override != configuration_.density) {
233 density_override_config = configuration_;
234 density_override_config.density = density_override;
235 desired_config = &density_override_config;
236 }
237
Adam Lesinskida431a22016-12-29 16:08:16 -0500238 const uint32_t package_id = util::get_package_id(resid);
239 const uint8_t type_id = util::get_type_id(resid);
240 const uint16_t entry_id = util::get_entry_id(resid);
241
242 if (type_id == 0) {
243 LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
244 return kInvalidCookie;
245 }
246
247 const uint8_t idx = package_ids_[package_id];
248 if (idx == 0xff) {
249 LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid);
250 return kInvalidCookie;
251 }
252
253 LoadedArscEntry best_entry;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700254 ResTable_config best_config;
Adam Lesinskida431a22016-12-29 16:08:16 -0500255 ApkAssetsCookie best_cookie = kInvalidCookie;
256 uint32_t cumulated_flags = 0u;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700257
Adam Lesinskida431a22016-12-29 16:08:16 -0500258 const PackageGroup& package_group = package_groups_[idx];
259 const size_t package_count = package_group.packages_.size();
260 for (size_t i = 0; i < package_count; i++) {
261 LoadedArscEntry current_entry;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700262 ResTable_config current_config;
Adam Lesinskida431a22016-12-29 16:08:16 -0500263 uint32_t current_flags = 0;
264
265 const LoadedPackage* loaded_package = package_group.packages_[i];
266 if (!loaded_package->FindEntry(type_id - 1, entry_id, *desired_config, &current_entry,
267 &current_config, &current_flags)) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700268 continue;
269 }
270
Adam Lesinskida431a22016-12-29 16:08:16 -0500271 cumulated_flags |= current_flags;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700272
Adam Lesinskida431a22016-12-29 16:08:16 -0500273 if (best_cookie == kInvalidCookie || current_config.isBetterThan(best_config, desired_config)) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700274 best_entry = current_entry;
275 best_config = current_config;
Adam Lesinskida431a22016-12-29 16:08:16 -0500276 best_cookie = package_group.cookies_[i];
Adam Lesinski7ad11102016-10-28 16:39:15 -0700277 if (stop_at_first_match) {
278 break;
279 }
280 }
281 }
282
Adam Lesinskida431a22016-12-29 16:08:16 -0500283 if (best_cookie == kInvalidCookie) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700284 return kInvalidCookie;
285 }
286
287 *out_entry = best_entry;
Adam Lesinskida431a22016-12-29 16:08:16 -0500288 out_entry->dynamic_ref_table = &package_group.dynamic_ref_table;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700289 *out_selected_config = best_config;
290 *out_flags = cumulated_flags;
Adam Lesinskida431a22016-12-29 16:08:16 -0500291 return best_cookie;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700292}
293
294bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) {
295 ATRACE_CALL();
296
Adam Lesinskida431a22016-12-29 16:08:16 -0500297 LoadedArscEntry entry;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700298 ResTable_config config;
299 uint32_t flags = 0u;
300 ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
301 true /* stop_at_first_match */, &entry, &config, &flags);
302 if (cookie == kInvalidCookie) {
303 return false;
304 }
305
Adam Lesinskida431a22016-12-29 16:08:16 -0500306 const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid);
307 if (package == nullptr) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700308 return false;
309 }
310
Adam Lesinskida431a22016-12-29 16:08:16 -0500311 out_name->package = package->GetPackageName().data();
312 out_name->package_len = package->GetPackageName().size();
Adam Lesinski7ad11102016-10-28 16:39:15 -0700313
314 out_name->type = entry.type_string_ref.string8(&out_name->type_len);
315 out_name->type16 = nullptr;
316 if (out_name->type == nullptr) {
317 out_name->type16 = entry.type_string_ref.string16(&out_name->type_len);
318 if (out_name->type16 == nullptr) {
319 return false;
320 }
321 }
322
323 out_name->entry = entry.entry_string_ref.string8(&out_name->entry_len);
324 out_name->entry16 = nullptr;
325 if (out_name->entry == nullptr) {
326 out_name->entry16 = entry.entry_string_ref.string16(&out_name->entry_len);
327 if (out_name->entry16 == nullptr) {
328 return false;
329 }
330 }
331 return true;
332}
333
334bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) {
Adam Lesinskida431a22016-12-29 16:08:16 -0500335 LoadedArscEntry entry;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700336 ResTable_config config;
337 ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
338 false /* stop_at_first_match */, &entry, &config, out_flags);
339 return cookie != kInvalidCookie;
340}
341
342ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
343 uint16_t density_override, Res_value* out_value,
344 ResTable_config* out_selected_config,
345 uint32_t* out_flags) {
346 ATRACE_CALL();
347
Adam Lesinskida431a22016-12-29 16:08:16 -0500348 LoadedArscEntry entry;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700349 ResTable_config config;
350 uint32_t flags = 0u;
351 ApkAssetsCookie cookie =
352 FindEntry(resid, density_override, false /* stop_at_first_match */, &entry, &config, &flags);
353 if (cookie == kInvalidCookie) {
354 return kInvalidCookie;
355 }
356
357 if (dtohl(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) {
358 if (!may_be_bag) {
359 LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
Adam Lesinski0c405242017-01-13 20:47:26 -0800360 return kInvalidCookie;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700361 }
Adam Lesinski0c405242017-01-13 20:47:26 -0800362
363 // Create a reference since we can't represent this complex type as a Res_value.
364 out_value->dataType = Res_value::TYPE_REFERENCE;
365 out_value->data = resid;
366 *out_selected_config = config;
367 *out_flags = flags;
368 return cookie;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700369 }
370
371 const Res_value* device_value = reinterpret_cast<const Res_value*>(
372 reinterpret_cast<const uint8_t*>(entry.entry) + dtohs(entry.entry->size));
373 out_value->copyFrom_dtoh(*device_value);
Adam Lesinskida431a22016-12-29 16:08:16 -0500374
375 // Convert the package ID to the runtime assigned package ID.
376 entry.dynamic_ref_table->lookupResourceValue(out_value);
377
Adam Lesinski7ad11102016-10-28 16:39:15 -0700378 *out_selected_config = config;
379 *out_flags = flags;
380 return cookie;
381}
382
Adam Lesinski0c405242017-01-13 20:47:26 -0800383ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
384 ResTable_config* in_out_selected_config,
385 uint32_t* in_out_flags,
386 ResTable_ref* out_last_reference) {
387 ATRACE_CALL();
388 constexpr const int kMaxIterations = 20;
389
390 out_last_reference->ident = 0u;
391 for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE &&
392 in_out_value->data != 0u && iteration < kMaxIterations;
393 iteration++) {
394 if (out_last_reference != nullptr) {
395 out_last_reference->ident = in_out_value->data;
396 }
397 uint32_t new_flags = 0u;
398 cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/,
399 in_out_value, in_out_selected_config, &new_flags);
400 if (cookie == kInvalidCookie) {
401 return kInvalidCookie;
402 }
403 if (in_out_flags != nullptr) {
404 *in_out_flags |= new_flags;
405 }
406 if (out_last_reference->ident == in_out_value->data) {
407 // This reference can't be resolved, so exit now and let the caller deal with it.
408 return cookie;
409 }
410 }
411 return cookie;
412}
413
Adam Lesinski7ad11102016-10-28 16:39:15 -0700414const ResolvedBag* AssetManager2::GetBag(uint32_t resid) {
415 ATRACE_CALL();
416
417 auto cached_iter = cached_bags_.find(resid);
418 if (cached_iter != cached_bags_.end()) {
419 return cached_iter->second.get();
420 }
421
Adam Lesinskida431a22016-12-29 16:08:16 -0500422 LoadedArscEntry entry;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700423 ResTable_config config;
424 uint32_t flags = 0u;
425 ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
426 false /* stop_at_first_match */, &entry, &config, &flags);
427 if (cookie == kInvalidCookie) {
428 return nullptr;
429 }
430
431 // Check that the size of the entry header is at least as big as
432 // the desired ResTable_map_entry. Also verify that the entry
433 // was intended to be a map.
434 if (dtohs(entry.entry->size) < sizeof(ResTable_map_entry) ||
435 (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) {
436 // Not a bag, nothing to do.
437 return nullptr;
438 }
439
440 const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry.entry);
441 const ResTable_map* map_entry =
442 reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size);
443 const ResTable_map* const map_entry_end = map_entry + dtohl(map->count);
444
Adam Lesinskida431a22016-12-29 16:08:16 -0500445 uint32_t parent_resid = dtohl(map->parent.ident);
446 if (parent_resid == 0) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700447 // There is no parent, meaning there is nothing to inherit and we can do a simple
448 // copy of the entries in the map.
449 const size_t entry_count = map_entry_end - map_entry;
450 util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
451 malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))};
452 ResolvedBag::Entry* new_entry = new_bag->entries;
453 for (; map_entry != map_entry_end; ++map_entry) {
Adam Lesinskida431a22016-12-29 16:08:16 -0500454 uint32_t new_key = dtohl(map_entry->name.ident);
455 if (!util::is_internal_resid(new_key)) {
456 // Attributes, arrays, etc don't have a resource id as the name. They specify
457 // other data, which would be wrong to change via a lookup.
458 if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
459 LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid);
460 return nullptr;
461 }
462 }
Adam Lesinski7ad11102016-10-28 16:39:15 -0700463 new_entry->cookie = cookie;
464 new_entry->value.copyFrom_dtoh(map_entry->value);
Adam Lesinskida431a22016-12-29 16:08:16 -0500465 new_entry->key = new_key;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700466 new_entry->key_pool = nullptr;
467 new_entry->type_pool = nullptr;
468 ++new_entry;
469 }
470 new_bag->type_spec_flags = flags;
471 new_bag->entry_count = static_cast<uint32_t>(entry_count);
472 ResolvedBag* result = new_bag.get();
473 cached_bags_[resid] = std::move(new_bag);
474 return result;
475 }
476
Adam Lesinskida431a22016-12-29 16:08:16 -0500477 // In case the parent is a dynamic reference, resolve it.
478 entry.dynamic_ref_table->lookupResourceId(&parent_resid);
479
Adam Lesinski7ad11102016-10-28 16:39:15 -0700480 // Get the parent and do a merge of the keys.
Adam Lesinskida431a22016-12-29 16:08:16 -0500481 const ResolvedBag* parent_bag = GetBag(parent_resid);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700482 if (parent_bag == nullptr) {
483 // Failed to get the parent that should exist.
Adam Lesinskida431a22016-12-29 16:08:16 -0500484 LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700485 return nullptr;
486 }
487
488 // Combine flags from the parent and our own bag.
489 flags |= parent_bag->type_spec_flags;
490
491 // Create the max possible entries we can make. Once we construct the bag,
492 // we will realloc to fit to size.
493 const size_t max_count = parent_bag->entry_count + dtohl(map->count);
494 ResolvedBag* new_bag = reinterpret_cast<ResolvedBag*>(
495 malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))));
496 ResolvedBag::Entry* new_entry = new_bag->entries;
497
498 const ResolvedBag::Entry* parent_entry = parent_bag->entries;
499 const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count;
500
501 // The keys are expected to be in sorted order. Merge the two bags.
502 while (map_entry != map_entry_end && parent_entry != parent_entry_end) {
Adam Lesinskida431a22016-12-29 16:08:16 -0500503 uint32_t child_key = dtohl(map_entry->name.ident);
504 if (!util::is_internal_resid(child_key)) {
505 if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) {
506 LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid);
507 return nullptr;
508 }
509 }
510
Adam Lesinski7ad11102016-10-28 16:39:15 -0700511 if (child_key <= parent_entry->key) {
512 // Use the child key if it comes before the parent
513 // or is equal to the parent (overrides).
514 new_entry->cookie = cookie;
515 new_entry->value.copyFrom_dtoh(map_entry->value);
516 new_entry->key = child_key;
517 new_entry->key_pool = nullptr;
518 new_entry->type_pool = nullptr;
519 ++map_entry;
520 } else {
521 // Take the parent entry as-is.
522 memcpy(new_entry, parent_entry, sizeof(*new_entry));
523 }
524
525 if (child_key >= parent_entry->key) {
526 // Move to the next parent entry if we used it or it was overridden.
527 ++parent_entry;
528 }
529 // Increment to the next entry to fill.
530 ++new_entry;
531 }
532
533 // Finish the child entries if they exist.
534 while (map_entry != map_entry_end) {
Adam Lesinskida431a22016-12-29 16:08:16 -0500535 uint32_t new_key = dtohl(map_entry->name.ident);
536 if (!util::is_internal_resid(new_key)) {
537 if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
538 LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid);
539 return nullptr;
540 }
541 }
Adam Lesinski7ad11102016-10-28 16:39:15 -0700542 new_entry->cookie = cookie;
543 new_entry->value.copyFrom_dtoh(map_entry->value);
Adam Lesinskida431a22016-12-29 16:08:16 -0500544 new_entry->key = new_key;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700545 new_entry->key_pool = nullptr;
546 new_entry->type_pool = nullptr;
547 ++map_entry;
548 ++new_entry;
549 }
550
551 // Finish the parent entries if they exist.
552 if (parent_entry != parent_entry_end) {
553 // Take the rest of the parent entries as-is.
554 const size_t num_entries_to_copy = parent_entry_end - parent_entry;
555 memcpy(new_entry, parent_entry, num_entries_to_copy * sizeof(*new_entry));
556 new_entry += num_entries_to_copy;
557 }
558
559 // Resize the resulting array to fit.
560 const size_t actual_count = new_entry - new_bag->entries;
561 if (actual_count != max_count) {
562 new_bag = reinterpret_cast<ResolvedBag*>(
563 realloc(new_bag, sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry))));
564 }
565
566 util::unique_cptr<ResolvedBag> final_bag{new_bag};
567 final_bag->type_spec_flags = flags;
568 final_bag->entry_count = static_cast<uint32_t>(actual_count);
569 ResolvedBag* result = final_bag.get();
570 cached_bags_[resid] = std::move(final_bag);
571 return result;
572}
573
Adam Lesinski0c405242017-01-13 20:47:26 -0800574uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
575 const std::string& fallback_type,
576 const std::string& fallback_package) {
577 (void)resource_name;
578 (void)fallback_type;
579 (void)fallback_package;
580 return 0u;
581}
582
Adam Lesinski7ad11102016-10-28 16:39:15 -0700583void AssetManager2::InvalidateCaches(uint32_t diff) {
584 if (diff == 0xffffffffu) {
585 // Everything must go.
586 cached_bags_.clear();
587 return;
588 }
589
590 // Be more conservative with what gets purged. Only if the bag has other possible
591 // variations with respect to what changed (diff) should we remove it.
592 for (auto iter = cached_bags_.cbegin(); iter != cached_bags_.cend();) {
593 if (diff & iter->second->type_spec_flags) {
594 iter = cached_bags_.erase(iter);
595 } else {
596 ++iter;
597 }
598 }
599}
600
601std::unique_ptr<Theme> AssetManager2::NewTheme() { return std::unique_ptr<Theme>(new Theme(this)); }
602
603bool Theme::ApplyStyle(uint32_t resid, bool force) {
604 ATRACE_CALL();
605
606 const ResolvedBag* bag = asset_manager_->GetBag(resid);
607 if (bag == nullptr) {
608 return false;
609 }
610
611 // Merge the flags from this style.
612 type_spec_flags_ |= bag->type_spec_flags;
613
614 // On the first iteration, verify the attribute IDs and
615 // update the entry count in each type.
616 const auto bag_iter_end = end(bag);
617 for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) {
618 const uint32_t attr_resid = bag_iter->key;
619
620 // If the resource ID passed in is not a style, the key can be
621 // some other identifier that is not a resource ID.
622 if (!util::is_valid_resid(attr_resid)) {
623 return false;
624 }
625
626 const uint32_t package_idx = util::get_package_id(attr_resid);
627
628 // The type ID is 1-based, so subtract 1 to get an index.
629 const uint32_t type_idx = util::get_type_id(attr_resid) - 1;
630 const uint32_t entry_idx = util::get_entry_id(attr_resid);
631
632 std::unique_ptr<Package>& package = packages_[package_idx];
633 if (package == nullptr) {
634 package.reset(new Package());
635 }
636
637 util::unique_cptr<Type>& type = package->types[type_idx];
638 if (type == nullptr) {
639 // Set the initial capacity to take up a total amount of 1024 bytes.
640 constexpr uint32_t kInitialCapacity = (1024u - sizeof(Type)) / sizeof(Entry);
641 const uint32_t initial_capacity = std::max(entry_idx, kInitialCapacity);
642 type.reset(
643 reinterpret_cast<Type*>(calloc(sizeof(Type) + (initial_capacity * sizeof(Entry)), 1)));
644 type->entry_capacity = initial_capacity;
645 }
646
647 // Set the entry_count to include this entry. We will populate
648 // and resize the array as necessary in the next pass.
649 if (entry_idx + 1 > type->entry_count) {
650 // Increase the entry count to include this.
651 type->entry_count = entry_idx + 1;
652 }
653 }
654
655 // On the second pass, we will realloc to fit the entry counts
656 // and populate the structures.
657 for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) {
658 const uint32_t attr_resid = bag_iter->key;
659 const uint32_t package_idx = util::get_package_id(attr_resid);
660 const uint32_t type_idx = util::get_type_id(attr_resid) - 1;
661 const uint32_t entry_idx = util::get_entry_id(attr_resid);
662 Package* package = packages_[package_idx].get();
663 util::unique_cptr<Type>& type = package->types[type_idx];
664 if (type->entry_count != type->entry_capacity) {
665 // Resize to fit the actual entries that will be included.
666 Type* type_ptr = type.release();
667 type.reset(reinterpret_cast<Type*>(
668 realloc(type_ptr, sizeof(Type) + (type_ptr->entry_count * sizeof(Entry)))));
669 if (type->entry_capacity < type->entry_count) {
670 // Clear the newly allocated memory (which does not get zero initialized).
671 // We need to do this because we |= type_spec_flags.
672 memset(type->entries + type->entry_capacity, 0,
673 sizeof(Entry) * (type->entry_count - type->entry_capacity));
674 }
675 type->entry_capacity = type->entry_count;
676 }
677 Entry& entry = type->entries[entry_idx];
678 if (force || entry.value.dataType == Res_value::TYPE_NULL) {
679 entry.cookie = bag_iter->cookie;
680 entry.type_spec_flags |= bag->type_spec_flags;
681 entry.value = bag_iter->value;
682 }
683 }
684 return true;
685}
686
687ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value,
688 uint32_t* out_flags) const {
689 constexpr const int kMaxIterations = 20;
690
691 uint32_t type_spec_flags = 0u;
692
693 for (int iterations_left = kMaxIterations; iterations_left > 0; iterations_left--) {
694 if (!util::is_valid_resid(resid)) {
695 return kInvalidCookie;
696 }
697
698 const uint32_t package_idx = util::get_package_id(resid);
699
700 // Type ID is 1-based, subtract 1 to get the index.
701 const uint32_t type_idx = util::get_type_id(resid) - 1;
702 const uint32_t entry_idx = util::get_entry_id(resid);
703
704 const Package* package = packages_[package_idx].get();
705 if (package == nullptr) {
706 return kInvalidCookie;
707 }
708
709 const Type* type = package->types[type_idx].get();
710 if (type == nullptr) {
711 return kInvalidCookie;
712 }
713
714 if (entry_idx >= type->entry_count) {
715 return kInvalidCookie;
716 }
717
718 const Entry& entry = type->entries[entry_idx];
719 type_spec_flags |= entry.type_spec_flags;
720
721 switch (entry.value.dataType) {
Adam Lesinskida431a22016-12-29 16:08:16 -0500722 case Res_value::TYPE_NULL:
723 return kInvalidCookie;
724
Adam Lesinski7ad11102016-10-28 16:39:15 -0700725 case Res_value::TYPE_ATTRIBUTE:
726 resid = entry.value.data;
727 break;
728
Adam Lesinskida431a22016-12-29 16:08:16 -0500729 case Res_value::TYPE_DYNAMIC_ATTRIBUTE: {
730 // Resolve the dynamic attribute to a normal attribute
731 // (with the right package ID).
732 resid = entry.value.data;
733 const DynamicRefTable* ref_table =
734 asset_manager_->GetDynamicRefTableForPackage(package_idx);
735 if (ref_table == nullptr || ref_table->lookupResourceId(&resid) != NO_ERROR) {
736 LOG(ERROR) << base::StringPrintf("Failed to resolve dynamic attribute 0x%08x", resid);
737 return kInvalidCookie;
738 }
739 } break;
740
741 case Res_value::TYPE_DYNAMIC_REFERENCE: {
742 // Resolve the dynamic reference to a normal reference
743 // (with the right package ID).
744 out_value->dataType = Res_value::TYPE_REFERENCE;
745 out_value->data = entry.value.data;
746 const DynamicRefTable* ref_table =
747 asset_manager_->GetDynamicRefTableForPackage(package_idx);
748 if (ref_table == nullptr || ref_table->lookupResourceId(&out_value->data) != NO_ERROR) {
749 LOG(ERROR) << base::StringPrintf("Failed to resolve dynamic reference 0x%08x",
750 out_value->data);
751 return kInvalidCookie;
752 }
753
754 if (out_flags != nullptr) {
755 *out_flags = type_spec_flags;
756 }
757 return entry.cookie;
758 }
Adam Lesinski7ad11102016-10-28 16:39:15 -0700759
760 default:
761 *out_value = entry.value;
762 if (out_flags != nullptr) {
763 *out_flags = type_spec_flags;
764 }
765 return entry.cookie;
766 }
767 }
768
769 LOG(WARNING) << base::StringPrintf("Too many (%d) attribute references, stopped at: 0x%08x",
770 kMaxIterations, resid);
771 return kInvalidCookie;
772}
773
774void Theme::Clear() {
775 type_spec_flags_ = 0u;
776 for (std::unique_ptr<Package>& package : packages_) {
777 package.reset();
778 }
779}
780
781bool Theme::SetTo(const Theme& o) {
782 if (this == &o) {
783 return true;
784 }
785
786 if (asset_manager_ != o.asset_manager_) {
787 return false;
788 }
789
790 type_spec_flags_ = o.type_spec_flags_;
791
Adam Lesinskida431a22016-12-29 16:08:16 -0500792 for (size_t p = 0; p < packages_.size(); p++) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700793 const Package* package = o.packages_[p].get();
794 if (package == nullptr) {
795 packages_[p].reset();
796 continue;
797 }
798
Adam Lesinskida431a22016-12-29 16:08:16 -0500799 for (size_t t = 0; t < package->types.size(); t++) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700800 const Type* type = package->types[t].get();
801 if (type == nullptr) {
802 packages_[p]->types[t].reset();
803 continue;
804 }
805
806 const size_t type_alloc_size = sizeof(Type) + (type->entry_capacity * sizeof(Entry));
807 void* copied_data = malloc(type_alloc_size);
808 memcpy(copied_data, type, type_alloc_size);
809 packages_[p]->types[t].reset(reinterpret_cast<Type*>(copied_data));
810 }
811 }
812 return true;
813}
814
815} // namespace android