blob: b21528b95178fc9ce1fc992c419bc4c9abfa488d [file] [log] [blame]
Mårten Kongstad02751232018-04-27 13:16:32 +02001/*
2 * Copyright (C) 2018 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 <algorithm>
18#include <iostream>
19#include <iterator>
20#include <limits>
21#include <map>
22#include <memory>
23#include <set>
24#include <string>
25#include <utility>
26#include <vector>
27
28#include "android-base/macros.h"
29#include "android-base/stringprintf.h"
30#include "androidfw/AssetManager2.h"
31#include "utils/String16.h"
32#include "utils/String8.h"
33
34#include "idmap2/Idmap.h"
35#include "idmap2/ResourceUtils.h"
Mårten Kongstad0f763112018-11-19 14:14:37 +010036#include "idmap2/Result.h"
Mårten Kongstad02751232018-04-27 13:16:32 +020037#include "idmap2/ZipFile.h"
38
39namespace android {
40namespace idmap2 {
41
Mårten Kongstad744ccfe2018-12-20 14:56:14 +010042namespace {
43
Mårten Kongstad02751232018-04-27 13:16:32 +020044#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
45
46#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
47
48struct MatchingResources {
49 void Add(ResourceId target_resid, ResourceId overlay_resid) {
50 TypeId target_typeid = EXTRACT_TYPE(target_resid);
51 if (map.find(target_typeid) == map.end()) {
52 map.emplace(target_typeid, std::set<std::pair<ResourceId, ResourceId>>());
53 }
54 map[target_typeid].insert(std::make_pair(target_resid, overlay_resid));
55 }
56
57 // target type id -> set { pair { overlay entry id, overlay entry id } }
58 std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>> map;
59};
60
Mårten Kongstad744ccfe2018-12-20 14:56:14 +010061bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) {
Mårten Kongstad02751232018-04-27 13:16:32 +020062 uint16_t value;
63 if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint16_t))) {
64 *out = dtohl(value);
65 return true;
66 }
67 return false;
68}
69
Mårten Kongstad744ccfe2018-12-20 14:56:14 +010070bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) {
Mårten Kongstad02751232018-04-27 13:16:32 +020071 uint32_t value;
72 if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) {
73 *out = dtohl(value);
74 return true;
75 }
76 return false;
77}
78
79// a string is encoded as a kIdmapStringLength char array; the array is always null-terminated
Mårten Kongstad744ccfe2018-12-20 14:56:14 +010080bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength]) {
Mårten Kongstad02751232018-04-27 13:16:32 +020081 char buf[kIdmapStringLength];
82 memset(buf, 0, sizeof(buf));
83 if (!stream.read(buf, sizeof(buf))) {
84 return false;
85 }
86 if (buf[sizeof(buf) - 1] != '\0') {
87 return false;
88 }
89 memcpy(out, buf, sizeof(buf));
90 return true;
91}
92
Mårten Kongstad744ccfe2018-12-20 14:56:14 +010093ResourceId NameToResid(const AssetManager2& am, const std::string& name) {
Mårten Kongstad02751232018-04-27 13:16:32 +020094 return am.GetResourceId(name);
95}
96
97// TODO(martenkongstad): scan for package name instead of assuming package at index 0
98//
99// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
100// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
101// this assumption tends to work out. That said, the correct thing to do is to scan
102// resources.arsc for a package with a given name as read from the package manifest instead of
103// relying on a hard-coded index. This however requires storing the package name in the idmap
104// header, which in turn requires incrementing the idmap version. Because the initial version of
105// idmap2 is compatible with idmap, this will have to wait for now.
Mårten Kongstad744ccfe2018-12-20 14:56:14 +0100106const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
Mårten Kongstad02751232018-04-27 13:16:32 +0200107 const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
108 if (packages.empty()) {
109 return nullptr;
110 }
111 int id = packages[0]->GetPackageId();
112 return loaded_arsc.GetPackageById(id);
113}
114
Mårten Kongstad744ccfe2018-12-20 14:56:14 +0100115} // namespace
116
Mårten Kongstad02751232018-04-27 13:16:32 +0200117std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) {
118 std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader());
119
120 if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_) ||
121 !Read32(stream, &idmap_header->target_crc_) || !Read32(stream, &idmap_header->overlay_crc_) ||
122 !ReadString(stream, idmap_header->target_path_) ||
123 !ReadString(stream, idmap_header->overlay_path_)) {
124 return nullptr;
125 }
126
127 return std::move(idmap_header);
128}
129
130bool IdmapHeader::IsUpToDate(std::ostream& out_error) const {
131 if (magic_ != kIdmapMagic) {
132 out_error << base::StringPrintf("error: bad magic: actual 0x%08x, expected 0x%08x", magic_,
133 kIdmapMagic)
134 << std::endl;
135 return false;
136 }
137
138 if (version_ != kIdmapCurrentVersion) {
139 out_error << base::StringPrintf("error: bad version: actual 0x%08x, expected 0x%08x", version_,
140 kIdmapCurrentVersion)
141 << std::endl;
142 return false;
143 }
144
145 const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_path_);
146 if (!target_zip) {
147 out_error << "error: failed to open target " << target_path_ << std::endl;
148 return false;
149 }
150
Mårten Kongstad0f763112018-11-19 14:14:37 +0100151 Result<uint32_t> target_crc = target_zip->Crc("resources.arsc");
152 if (!target_crc) {
Mårten Kongstad02751232018-04-27 13:16:32 +0200153 out_error << "error: failed to get target crc" << std::endl;
154 return false;
155 }
156
Mårten Kongstad0f763112018-11-19 14:14:37 +0100157 if (target_crc_ != *target_crc) {
Mårten Kongstad02751232018-04-27 13:16:32 +0200158 out_error << base::StringPrintf(
159 "error: bad target crc: idmap version 0x%08x, file system version 0x%08x",
Mårten Kongstad0f763112018-11-19 14:14:37 +0100160 target_crc_, *target_crc)
Mårten Kongstad02751232018-04-27 13:16:32 +0200161 << std::endl;
162 return false;
163 }
164
165 const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_path_);
166 if (!overlay_zip) {
167 out_error << "error: failed to open overlay " << overlay_path_ << std::endl;
168 return false;
169 }
170
Mårten Kongstad0f763112018-11-19 14:14:37 +0100171 Result<uint32_t> overlay_crc = overlay_zip->Crc("resources.arsc");
172 if (!overlay_crc) {
Mårten Kongstad02751232018-04-27 13:16:32 +0200173 out_error << "error: failed to get overlay crc" << std::endl;
174 return false;
175 }
176
Mårten Kongstad0f763112018-11-19 14:14:37 +0100177 if (overlay_crc_ != *overlay_crc) {
Mårten Kongstad02751232018-04-27 13:16:32 +0200178 out_error << base::StringPrintf(
179 "error: bad overlay crc: idmap version 0x%08x, file system version 0x%08x",
Mårten Kongstad0f763112018-11-19 14:14:37 +0100180 overlay_crc_, *overlay_crc)
Mårten Kongstad02751232018-04-27 13:16:32 +0200181 << std::endl;
182 return false;
183 }
184
185 return true;
186}
187
188std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) {
189 std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header());
190
191 uint16_t target_package_id16;
192 if (!Read16(stream, &target_package_id16) || !Read16(stream, &idmap_data_header->type_count_)) {
193 return nullptr;
194 }
195 idmap_data_header->target_package_id_ = target_package_id16;
196
197 return std::move(idmap_data_header);
198}
199
200std::unique_ptr<const IdmapData::TypeEntry> IdmapData::TypeEntry::FromBinaryStream(
201 std::istream& stream) {
202 std::unique_ptr<IdmapData::TypeEntry> data(new IdmapData::TypeEntry());
203
204 uint16_t target_type16, overlay_type16, entry_count;
205 if (!Read16(stream, &target_type16) || !Read16(stream, &overlay_type16) ||
206 !Read16(stream, &entry_count) || !Read16(stream, &data->entry_offset_)) {
207 return nullptr;
208 }
209 data->target_type_id_ = target_type16;
210 data->overlay_type_id_ = overlay_type16;
211 for (uint16_t i = 0; i < entry_count; i++) {
212 ResourceId resid;
213 if (!Read32(stream, &resid)) {
214 return nullptr;
215 }
216 data->entries_.push_back(resid);
217 }
218
219 return std::move(data);
220}
221
222std::unique_ptr<const IdmapData> IdmapData::FromBinaryStream(std::istream& stream) {
223 std::unique_ptr<IdmapData> data(new IdmapData());
224 data->header_ = IdmapData::Header::FromBinaryStream(stream);
225 if (!data->header_) {
226 return nullptr;
227 }
228 for (size_t type_count = 0; type_count < data->header_->GetTypeCount(); type_count++) {
229 std::unique_ptr<const TypeEntry> type = IdmapData::TypeEntry::FromBinaryStream(stream);
230 if (!type) {
231 return nullptr;
232 }
233 data->type_entries_.push_back(std::move(type));
234 }
235 return std::move(data);
236}
237
238std::string Idmap::CanonicalIdmapPathFor(const std::string& absolute_dir,
239 const std::string& absolute_apk_path) {
240 assert(absolute_dir.size() > 0 && absolute_dir[0] == "/");
241 assert(absolute_apk_path.size() > 0 && absolute_apk_path[0] == "/");
242 std::string copy(++absolute_apk_path.cbegin(), absolute_apk_path.cend());
243 replace(copy.begin(), copy.end(), '/', '@');
244 return absolute_dir + "/" + copy + "@idmap";
245}
246
247std::unique_ptr<const Idmap> Idmap::FromBinaryStream(std::istream& stream,
248 std::ostream& out_error) {
249 std::unique_ptr<Idmap> idmap(new Idmap());
250
251 idmap->header_ = IdmapHeader::FromBinaryStream(stream);
252 if (!idmap->header_) {
253 out_error << "error: failed to parse idmap header" << std::endl;
254 return nullptr;
255 }
256
257 // idmap version 0x01 does not specify the number of data blocks that follow
258 // the idmap header; assume exactly one data block
259 for (int i = 0; i < 1; i++) {
260 std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream);
261 if (!data) {
262 out_error << "error: failed to parse data block " << i << std::endl;
263 return nullptr;
264 }
265 idmap->data_.push_back(std::move(data));
266 }
267
268 return std::move(idmap);
269}
270
271std::unique_ptr<const Idmap> Idmap::FromApkAssets(const std::string& target_apk_path,
272 const ApkAssets& target_apk_assets,
273 const std::string& overlay_apk_path,
274 const ApkAssets& overlay_apk_assets,
275 std::ostream& out_error) {
276 AssetManager2 target_asset_manager;
277 if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true, false)) {
278 out_error << "error: failed to create target asset manager" << std::endl;
279 return nullptr;
280 }
281
282 AssetManager2 overlay_asset_manager;
283 if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true, false)) {
284 out_error << "error: failed to create overlay asset manager" << std::endl;
285 return nullptr;
286 }
287
288 const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
289 if (!target_arsc) {
290 out_error << "error: failed to load target resources.arsc" << std::endl;
291 return nullptr;
292 }
293
294 const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
295 if (!overlay_arsc) {
296 out_error << "error: failed to load overlay resources.arsc" << std::endl;
297 return nullptr;
298 }
299
300 const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
301 if (!target_pkg) {
302 out_error << "error: failed to load target package from resources.arsc" << std::endl;
303 return nullptr;
304 }
305
306 const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
307 if (!overlay_pkg) {
308 out_error << "error: failed to load overlay package from resources.arsc" << std::endl;
309 return nullptr;
310 }
311
312 const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_apk_path);
313 if (!target_zip) {
314 out_error << "error: failed to open target as zip" << std::endl;
315 return nullptr;
316 }
317
318 const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_apk_path);
319 if (!overlay_zip) {
320 out_error << "error: failed to open overlay as zip" << std::endl;
321 return nullptr;
322 }
323
324 std::unique_ptr<IdmapHeader> header(new IdmapHeader());
325 header->magic_ = kIdmapMagic;
326 header->version_ = kIdmapCurrentVersion;
Mårten Kongstad0f763112018-11-19 14:14:37 +0100327
328 Result<uint32_t> crc = target_zip->Crc("resources.arsc");
329 if (!crc) {
Mårten Kongstad02751232018-04-27 13:16:32 +0200330 out_error << "error: failed to get zip crc for target" << std::endl;
331 return nullptr;
332 }
Mårten Kongstad0f763112018-11-19 14:14:37 +0100333 header->target_crc_ = *crc;
334
335 crc = overlay_zip->Crc("resources.arsc");
336 if (!crc) {
Mårten Kongstad02751232018-04-27 13:16:32 +0200337 out_error << "error: failed to get zip crc for overlay" << std::endl;
338 return nullptr;
339 }
Mårten Kongstad0f763112018-11-19 14:14:37 +0100340 header->overlay_crc_ = *crc;
Mårten Kongstad02751232018-04-27 13:16:32 +0200341
342 if (target_apk_path.size() > sizeof(header->target_path_)) {
343 out_error << "error: target apk path \"" << target_apk_path << "\" longer that maximum size "
344 << sizeof(header->target_path_) << std::endl;
345 return nullptr;
346 }
347 memset(header->target_path_, 0, sizeof(header->target_path_));
348 memcpy(header->target_path_, target_apk_path.data(), target_apk_path.size());
349
350 if (overlay_apk_path.size() > sizeof(header->overlay_path_)) {
351 out_error << "error: overlay apk path \"" << overlay_apk_path << "\" longer that maximum size "
352 << sizeof(header->overlay_path_) << std::endl;
353 return nullptr;
354 }
355 memset(header->overlay_path_, 0, sizeof(header->overlay_path_));
356 memcpy(header->overlay_path_, overlay_apk_path.data(), overlay_apk_path.size());
357
358 std::unique_ptr<Idmap> idmap(new Idmap());
359 idmap->header_ = std::move(header);
360
361 // find the resources that exist in both packages
362 MatchingResources matching_resources;
363 const auto end = overlay_pkg->end();
364 for (auto iter = overlay_pkg->begin(); iter != end; ++iter) {
365 const ResourceId overlay_resid = *iter;
Mårten Kongstad0f763112018-11-19 14:14:37 +0100366 Result<std::string> name = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
367 if (!name) {
Mårten Kongstad02751232018-04-27 13:16:32 +0200368 continue;
369 }
370 // prepend "<package>:" to turn name into "<package>:<type>/<name>"
Mårten Kongstad0f763112018-11-19 14:14:37 +0100371 const std::string full_name =
372 base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name->c_str());
373 const ResourceId target_resid = NameToResid(target_asset_manager, full_name);
Mårten Kongstad02751232018-04-27 13:16:32 +0200374 if (target_resid == 0) {
375 continue;
376 }
377 matching_resources.Add(target_resid, overlay_resid);
378 }
379
380 // encode idmap data
381 std::unique_ptr<IdmapData> data(new IdmapData());
382 const auto types_end = matching_resources.map.cend();
383 for (auto ti = matching_resources.map.cbegin(); ti != types_end; ++ti) {
384 auto ei = ti->second.cbegin();
385 std::unique_ptr<IdmapData::TypeEntry> type(new IdmapData::TypeEntry());
386 type->target_type_id_ = EXTRACT_TYPE(ei->first);
387 type->overlay_type_id_ = EXTRACT_TYPE(ei->second);
388 type->entry_offset_ = EXTRACT_ENTRY(ei->first);
389 EntryId last_target_entry = kNoEntry;
390 for (; ei != ti->second.cend(); ++ei) {
391 if (last_target_entry != kNoEntry) {
392 int count = EXTRACT_ENTRY(ei->first) - last_target_entry - 1;
393 type->entries_.insert(type->entries_.end(), count, kNoEntry);
394 }
395 type->entries_.push_back(EXTRACT_ENTRY(ei->second));
396 last_target_entry = EXTRACT_ENTRY(ei->first);
397 }
398 data->type_entries_.push_back(std::move(type));
399 }
400
401 std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
402 data_header->target_package_id_ = target_pkg->GetPackageId();
403 data_header->type_count_ = data->type_entries_.size();
404 data->header_ = std::move(data_header);
405
406 idmap->data_.push_back(std::move(data));
407
408 return std::move(idmap);
409}
410
411void IdmapHeader::accept(Visitor* v) const {
412 assert(v != nullptr);
413 v->visit(*this);
414}
415
416void IdmapData::Header::accept(Visitor* v) const {
417 assert(v != nullptr);
418 v->visit(*this);
419}
420
421void IdmapData::TypeEntry::accept(Visitor* v) const {
422 assert(v != nullptr);
423 v->visit(*this);
424}
425
426void IdmapData::accept(Visitor* v) const {
427 assert(v != nullptr);
428 v->visit(*this);
429 header_->accept(v);
430 auto end = type_entries_.cend();
431 for (auto iter = type_entries_.cbegin(); iter != end; ++iter) {
432 (*iter)->accept(v);
433 }
434}
435
436void Idmap::accept(Visitor* v) const {
437 assert(v != nullptr);
438 v->visit(*this);
439 header_->accept(v);
440 auto end = data_.cend();
441 for (auto iter = data_.cbegin(); iter != end; ++iter) {
442 (*iter)->accept(v);
443 }
444}
445
446} // namespace idmap2
447} // namespace android