ApkAssets.cpp revision 03ebac8c68f9925592a172fcfd11d56f48cadaeb
1/*
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/ApkAssets.h"
20
21#include <algorithm>
22
23#include "android-base/logging.h"
24#include "utils/FileMap.h"
25#include "utils/Trace.h"
26#include "ziparchive/zip_archive.h"
27
28#include "androidfw/Asset.h"
29#include "androidfw/Util.h"
30
31namespace android {
32
33ApkAssets::ApkAssets() : zip_handle_(nullptr, ::CloseArchive) {}
34
35std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) {
36  return ApkAssets::LoadImpl(path, system, false /*load_as_shared_library*/);
37}
38
39std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path,
40                                                                bool system) {
41  return ApkAssets::LoadImpl(path, system, true /*load_as_shared_library*/);
42}
43
44std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(const std::string& path, bool system,
45                                                     bool load_as_shared_library) {
46  ATRACE_CALL();
47  ::ZipArchiveHandle unmanaged_handle;
48  int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
49  if (result != 0) {
50    LOG(ERROR) << ::ErrorCodeString(result);
51    return {};
52  }
53
54  // Wrap the handle in a unique_ptr so it gets automatically closed.
55  std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets());
56  loaded_apk->zip_handle_.reset(unmanaged_handle);
57
58  ::ZipString entry_name("resources.arsc");
59  ::ZipEntry entry;
60  result = ::FindEntry(loaded_apk->zip_handle_.get(), entry_name, &entry);
61  if (result != 0) {
62    LOG(ERROR) << ::ErrorCodeString(result);
63    return {};
64  }
65
66  if (entry.method == kCompressDeflated) {
67    LOG(WARNING) << "resources.arsc is compressed.";
68  }
69
70  loaded_apk->path_ = path;
71  loaded_apk->resources_asset_ =
72      loaded_apk->Open("resources.arsc", Asset::AccessMode::ACCESS_BUFFER);
73  if (loaded_apk->resources_asset_ == nullptr) {
74    return {};
75  }
76
77  loaded_apk->loaded_arsc_ =
78      LoadedArsc::Load(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/),
79                       loaded_apk->resources_asset_->getLength(), system, load_as_shared_library);
80  if (loaded_apk->loaded_arsc_ == nullptr) {
81    return {};
82  }
83
84  // Need to force a move for mingw32.
85  return std::move(loaded_apk);
86}
87
88std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const {
89  ATRACE_CALL();
90  CHECK(zip_handle_ != nullptr);
91
92  ::ZipString name(path.c_str());
93  ::ZipEntry entry;
94  int32_t result = ::FindEntry(zip_handle_.get(), name, &entry);
95  if (result != 0) {
96    LOG(ERROR) << "No entry '" << path << "' found in APK '" << path_ << "'";
97    return {};
98  }
99
100  if (entry.method == kCompressDeflated) {
101    std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
102    if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
103                     entry.compressed_length, true /*readOnly*/)) {
104      LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
105      return {};
106    }
107
108    std::unique_ptr<Asset> asset =
109        Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
110    if (asset == nullptr) {
111      LOG(ERROR) << "Failed to decompress '" << path << "'.";
112      return {};
113    }
114    return asset;
115  } else {
116    std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
117    if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
118                     entry.uncompressed_length, true /*readOnly*/)) {
119      LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
120      return {};
121    }
122
123    std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map), mode);
124    if (asset == nullptr) {
125      LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
126      return {};
127    }
128    return asset;
129  }
130}
131
132bool ApkAssets::ForEachFile(const std::string& root_path,
133                            const std::function<void(const StringPiece&, FileType)>& f) const {
134  CHECK(zip_handle_ != nullptr);
135
136  std::string root_path_full = root_path;
137  if (root_path_full.back() != '/') {
138    root_path_full += '/';
139  }
140
141  ::ZipString prefix(root_path_full.c_str());
142  void* cookie;
143  if (::StartIteration(zip_handle_.get(), &cookie, &prefix, nullptr) != 0) {
144    return false;
145  }
146
147  ::ZipString name;
148  ::ZipEntry entry;
149
150  // We need to hold back directories because many paths will contain them and we want to only
151  // surface one.
152  std::set<std::string> dirs;
153
154  int32_t result;
155  while ((result = ::Next(cookie, &entry, &name)) == 0) {
156    StringPiece full_file_path(reinterpret_cast<const char*>(name.name), name.name_length);
157    StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
158    auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
159    if (iter != leaf_file_path.end()) {
160      dirs.insert(
161          leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string());
162    } else if (!leaf_file_path.empty()) {
163      f(leaf_file_path, kFileTypeRegular);
164    }
165  }
166  ::EndIteration(cookie);
167
168  // Now present the unique directories.
169  for (const std::string& dir : dirs) {
170    f(dir, kFileTypeDirectory);
171  }
172
173  // -1 is end of iteration, anything else is an error.
174  return result == -1;
175}
176
177}  // namespace android
178