| /* | |
| * Copyright (C) 2016 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #include "androidfw/ApkAssets.h" | |
| #include "android-base/errors.h" | |
| #include "android-base/logging.h" | |
| #include "android-base/utf8.h" | |
| namespace android { | |
| using base::SystemErrorCodeToString; | |
| using base::unique_fd; | |
| constexpr const char* kResourcesArsc = "resources.arsc"; | |
| ApkAssets::ApkAssets(PrivateConstructorUtil, std::unique_ptr<Asset> resources_asset, | |
| std::unique_ptr<LoadedArsc> loaded_arsc, | |
| std::unique_ptr<AssetsProvider> assets, package_property_t property_flags, | |
| std::unique_ptr<Asset> idmap_asset, std::unique_ptr<LoadedIdmap> loaded_idmap) | |
| : resources_asset_(std::move(resources_asset)), | |
| loaded_arsc_(std::move(loaded_arsc)), | |
| assets_provider_(std::move(assets)), | |
| property_flags_(property_flags), | |
| idmap_asset_(std::move(idmap_asset)), | |
| loaded_idmap_(std::move(loaded_idmap)) { | |
| } | |
| ApkAssetsPtr ApkAssets::Load(const std::string& path, package_property_t flags) { | |
| return Load(ZipAssetsProvider::Create(path, flags), flags); | |
| } | |
| ApkAssetsPtr ApkAssets::LoadFromFd(base::unique_fd fd, const std::string& debug_name, | |
| package_property_t flags, off64_t offset, off64_t len) { | |
| return Load(ZipAssetsProvider::Create(std::move(fd), debug_name, offset, len), flags); | |
| } | |
| ApkAssetsPtr ApkAssets::Load(std::unique_ptr<AssetsProvider> assets, package_property_t flags) { | |
| return LoadImpl(std::move(assets), flags, nullptr /* idmap_asset */, nullptr /* loaded_idmap */); | |
| } | |
| ApkAssetsPtr ApkAssets::LoadTable(std::unique_ptr<Asset> resources_asset, | |
| std::unique_ptr<AssetsProvider> assets, | |
| package_property_t flags) { | |
| if (resources_asset == nullptr) { | |
| return {}; | |
| } | |
| return LoadImpl(std::move(resources_asset), std::move(assets), flags, nullptr /* idmap_asset */, | |
| nullptr /* loaded_idmap */); | |
| } | |
| ApkAssetsPtr ApkAssets::LoadOverlay(const std::string& idmap_path, package_property_t flags) { | |
| CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders"; | |
| auto idmap_asset = AssetsProvider::CreateAssetFromFile(idmap_path); | |
| if (idmap_asset == nullptr) { | |
| LOG(ERROR) << "failed to read IDMAP " << idmap_path; | |
| return {}; | |
| } | |
| StringPiece idmap_data(reinterpret_cast<const char*>(idmap_asset->getBuffer(true /* aligned */)), | |
| static_cast<size_t>(idmap_asset->getLength())); | |
| auto loaded_idmap = LoadedIdmap::Load(idmap_path, idmap_data); | |
| if (loaded_idmap == nullptr) { | |
| LOG(ERROR) << "failed to load IDMAP " << idmap_path; | |
| return {}; | |
| } | |
| std::string overlay_path(loaded_idmap->OverlayApkPath()); | |
| auto fd = unique_fd(base::utf8::open(overlay_path.c_str(), O_RDONLY | O_CLOEXEC)); | |
| std::unique_ptr<AssetsProvider> overlay_assets; | |
| if (IsFabricatedOverlayName(overlay_path) && IsFabricatedOverlay(fd)) { | |
| // Fabricated overlays do not contain resource definitions. All of the overlay resource values | |
| // are defined inline in the idmap. | |
| overlay_assets = EmptyAssetsProvider::Create(std::move(overlay_path)); | |
| } else { | |
| // The overlay should be an APK. | |
| overlay_assets = ZipAssetsProvider::Create(std::move(overlay_path), flags, std::move(fd)); | |
| } | |
| if (overlay_assets == nullptr) { | |
| return {}; | |
| } | |
| return LoadImpl(std::move(overlay_assets), flags | PROPERTY_OVERLAY, std::move(idmap_asset), | |
| std::move(loaded_idmap)); | |
| } | |
| ApkAssetsPtr ApkAssets::LoadImpl(std::unique_ptr<AssetsProvider> assets, | |
| package_property_t property_flags, | |
| std::unique_ptr<Asset> idmap_asset, | |
| std::unique_ptr<LoadedIdmap> loaded_idmap) { | |
| if (assets == nullptr) { | |
| return {}; | |
| } | |
| // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open. | |
| bool resources_asset_exists = false; | |
| auto resources_asset = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER, | |
| &resources_asset_exists); | |
| if (resources_asset == nullptr && resources_asset_exists) { | |
| LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << assets->GetDebugName() | |
| << "'."; | |
| return {}; | |
| } | |
| return LoadImpl(std::move(resources_asset), std::move(assets), property_flags, | |
| std::move(idmap_asset), std::move(loaded_idmap)); | |
| } | |
| ApkAssetsPtr ApkAssets::LoadImpl(std::unique_ptr<Asset> resources_asset, | |
| std::unique_ptr<AssetsProvider> assets, | |
| package_property_t property_flags, | |
| std::unique_ptr<Asset> idmap_asset, | |
| std::unique_ptr<LoadedIdmap> loaded_idmap) { | |
| if (assets == nullptr ) { | |
| return {}; | |
| } | |
| std::unique_ptr<LoadedArsc> loaded_arsc; | |
| if (resources_asset != nullptr) { | |
| const auto data = resources_asset->getIncFsBuffer(true /* aligned */); | |
| const size_t length = resources_asset->getLength(); | |
| if (!data || length == 0) { | |
| LOG(ERROR) << "Failed to read resources table in APK '" << assets->GetDebugName() << "'."; | |
| return {}; | |
| } | |
| loaded_arsc = LoadedArsc::Load(data, length, loaded_idmap.get(), property_flags); | |
| } else if (loaded_idmap != nullptr && IsFabricatedOverlay(loaded_idmap->OverlayApkPath())) { | |
| loaded_arsc = LoadedArsc::Load(loaded_idmap.get()); | |
| } else { | |
| loaded_arsc = LoadedArsc::CreateEmpty(); | |
| } | |
| if (loaded_arsc == nullptr) { | |
| LOG(ERROR) << "Failed to load resources table in APK '" << assets->GetDebugName() << "'."; | |
| return {}; | |
| } | |
| return ApkAssetsPtr::make(PrivateConstructorUtil{}, std::move(resources_asset), | |
| std::move(loaded_arsc), std::move(assets), property_flags, | |
| std::move(idmap_asset), std::move(loaded_idmap)); | |
| } | |
| std::optional<std::string_view> ApkAssets::GetPath() const { | |
| return assets_provider_->GetPath(); | |
| } | |
| const std::string& ApkAssets::GetDebugName() const { | |
| return assets_provider_->GetDebugName(); | |
| } | |
| bool ApkAssets::IsUpToDate() const { | |
| // Loaders are invalidated by the app, not the system, so assume they are up to date. | |
| return IsLoader() || ((!loaded_idmap_ || loaded_idmap_->IsUpToDate()) | |
| && assets_provider_->IsUpToDate()); | |
| } | |
| } // namespace android |