17ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski/*
27ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski * Copyright (C) 2016 The Android Open Source Project
37ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski *
47ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
57ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski * you may not use this file except in compliance with the License.
67ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski * You may obtain a copy of the License at
77ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski *
87ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
97ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski *
107ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski * Unless required by applicable law or agreed to in writing, software
117ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
127ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski * See the License for the specific language governing permissions and
147ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski * limitations under the License.
157ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski */
167ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski
177ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski#define ATRACE_TAG ATRACE_TAG_RESOURCES
187ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski
197ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski#include "androidfw/ApkAssets.h"
207ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski
21d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski#include <algorithm>
22d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski
237ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski#include "android-base/logging.h"
24d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski#include "utils/FileMap.h"
257ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski#include "utils/Trace.h"
267ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski#include "ziparchive/zip_archive.h"
277ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski
287ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski#include "androidfw/Asset.h"
297ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski#include "androidfw/Util.h"
307ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski
317ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinskinamespace android {
327ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski
330c40524953f3d36a880f91183302a2ea5c722930Adam Lesinskistd::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) {
340c40524953f3d36a880f91183302a2ea5c722930Adam Lesinski  return ApkAssets::LoadImpl(path, system, false /*load_as_shared_library*/);
35da431a22da38f9c4085b5d71ed9a9c6122c6a5a6Adam Lesinski}
36da431a22da38f9c4085b5d71ed9a9c6122c6a5a6Adam Lesinski
370c40524953f3d36a880f91183302a2ea5c722930Adam Lesinskistd::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path,
380c40524953f3d36a880f91183302a2ea5c722930Adam Lesinski                                                                bool system) {
390c40524953f3d36a880f91183302a2ea5c722930Adam Lesinski  return ApkAssets::LoadImpl(path, system, true /*load_as_shared_library*/);
40da431a22da38f9c4085b5d71ed9a9c6122c6a5a6Adam Lesinski}
41da431a22da38f9c4085b5d71ed9a9c6122c6a5a6Adam Lesinski
420c40524953f3d36a880f91183302a2ea5c722930Adam Lesinskistd::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(const std::string& path, bool system,
430c40524953f3d36a880f91183302a2ea5c722930Adam Lesinski                                                     bool load_as_shared_library) {
44da431a22da38f9c4085b5d71ed9a9c6122c6a5a6Adam Lesinski  ATRACE_CALL();
457ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  ::ZipArchiveHandle unmanaged_handle;
467ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
477ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  if (result != 0) {
487ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski    LOG(ERROR) << ::ErrorCodeString(result);
497ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski    return {};
507ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  }
517ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski
527ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  // Wrap the handle in a unique_ptr so it gets automatically closed.
537ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets());
547ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  loaded_apk->zip_handle_.reset(unmanaged_handle);
557ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski
567ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  ::ZipString entry_name("resources.arsc");
577ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  ::ZipEntry entry;
587ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  result = ::FindEntry(loaded_apk->zip_handle_.get(), entry_name, &entry);
597ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  if (result != 0) {
607ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski    LOG(ERROR) << ::ErrorCodeString(result);
617ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski    return {};
627ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  }
637ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski
647ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  if (entry.method == kCompressDeflated) {
657ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski    LOG(WARNING) << "resources.arsc is compressed.";
667ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  }
677ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski
68d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  loaded_apk->path_ = path;
697ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  loaded_apk->resources_asset_ =
707ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski      loaded_apk->Open("resources.arsc", Asset::AccessMode::ACCESS_BUFFER);
717ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  if (loaded_apk->resources_asset_ == nullptr) {
727ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski    return {};
737ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  }
747ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski
757ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  loaded_apk->loaded_arsc_ =
767ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski      LoadedArsc::Load(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/),
770c40524953f3d36a880f91183302a2ea5c722930Adam Lesinski                       loaded_apk->resources_asset_->getLength(), system, load_as_shared_library);
787ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  if (loaded_apk->loaded_arsc_ == nullptr) {
797ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski    return {};
807ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  }
810c40524953f3d36a880f91183302a2ea5c722930Adam Lesinski
820c40524953f3d36a880f91183302a2ea5c722930Adam Lesinski  // Need to force a move for mingw32.
830c40524953f3d36a880f91183302a2ea5c722930Adam Lesinski  return std::move(loaded_apk);
847ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski}
857ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski
86d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinskistd::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const {
87d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  ATRACE_CALL();
887ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  CHECK(zip_handle_ != nullptr);
897ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski
907ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  ::ZipString name(path.c_str());
917ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  ::ZipEntry entry;
927ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  int32_t result = ::FindEntry(zip_handle_.get(), name, &entry);
937ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  if (result != 0) {
94d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    LOG(ERROR) << "No entry '" << path << "' found in APK '" << path_ << "'";
957ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski    return {};
967ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  }
977ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski
987ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  if (entry.method == kCompressDeflated) {
99d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
100d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
101d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski                     entry.compressed_length, true /*readOnly*/)) {
102d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski      LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
103d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski      return {};
104d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    }
105d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski
106d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    std::unique_ptr<Asset> asset =
107d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski        Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
108d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    if (asset == nullptr) {
1097ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski      LOG(ERROR) << "Failed to decompress '" << path << "'.";
1107ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski      return {};
1117ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski    }
112d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    return asset;
1137ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  } else {
114d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
115d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
116d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski                     entry.uncompressed_length, true /*readOnly*/)) {
117d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski      LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
1187ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski      return {};
1197ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski    }
120d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski
121d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map), mode);
122d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    if (asset == nullptr) {
123d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski      LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
124d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski      return {};
125d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    }
126d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    return asset;
1277ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski  }
128d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski}
129d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski
130d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinskibool ApkAssets::ForEachFile(const std::string& root_path,
131d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski                            const std::function<void(const StringPiece&, FileType)>& f) const {
132d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  CHECK(zip_handle_ != nullptr);
133d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski
134d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  std::string root_path_full = root_path;
135d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  if (root_path_full.back() != '/') {
136d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    root_path_full += '/';
137d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  }
138d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski
139d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  ::ZipString prefix(root_path_full.c_str());
140d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  void* cookie;
141d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  if (::StartIteration(zip_handle_.get(), &cookie, &prefix, nullptr) != 0) {
142d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    return false;
143d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  }
144d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski
145d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  ::ZipString name;
146d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  ::ZipEntry entry;
147d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski
148d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  // We need to hold back directories because many paths will contain them and we want to only
149d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  // surface one.
150d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  std::set<std::string> dirs;
151d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski
152d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  int32_t result;
153d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  while ((result = ::Next(cookie, &entry, &name)) == 0) {
154d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    StringPiece full_file_path(reinterpret_cast<const char*>(name.name), name.name_length);
155d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
156d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
157d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    if (iter != leaf_file_path.end()) {
158d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski      dirs.insert(
159d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski          leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string());
160d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    } else if (!leaf_file_path.empty()) {
161d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski      f(leaf_file_path, kFileTypeRegular);
162d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    }
163d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  }
164d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  ::EndIteration(cookie);
165d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski
166d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  // Now present the unique directories.
167d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  for (const std::string& dir : dirs) {
168d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski    f(dir, kFileTypeDirectory);
169d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  }
170d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski
171d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  // -1 is end of iteration, anything else is an error.
172d1ecd7af687bcad0f87c37fe33515ff6c5ea0f1dAdam Lesinski  return result == -1;
1737ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski}
1747ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski
1757ad1110ecd6a840fcd2895c62668828a1ca029c6Adam Lesinski}  // namespace android
176