Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d1ecd7af authored by Adam Lesinski's avatar Adam Lesinski
Browse files

AssetManager2: Various fixes

- Use FileMaps to open Assets (prevents closing of ApkAssets underlying
zip)
- Implement OpenDir and List methods
- Fix issue where DynamicRefTable wasn't properly constructed

Test: make libandroidfw_tests
Change-Id: Ib21a84e1114d028120744aa3bc1c6eb9d9399fa8
parent c535d122
Loading
Loading
Loading
Loading
+74 −15
Original line number Diff line number Diff line
@@ -18,7 +18,10 @@

#include "androidfw/ApkAssets.h"

#include <algorithm>

#include "android-base/logging.h"
#include "utils/FileMap.h"
#include "utils/Trace.h"
#include "ziparchive/zip_archive.h"

@@ -62,13 +65,13 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(const std::string& path, bo
    LOG(WARNING) << "resources.arsc is compressed.";
  }

  loaded_apk->path_ = path;
  loaded_apk->resources_asset_ =
      loaded_apk->Open("resources.arsc", Asset::AccessMode::ACCESS_BUFFER);
  if (loaded_apk->resources_asset_ == nullptr) {
    return {};
  }

  loaded_apk->path_ = path;
  loaded_apk->loaded_arsc_ =
      LoadedArsc::Load(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/),
                       loaded_apk->resources_asset_->getLength(), system, load_as_shared_library);
@@ -80,37 +83,93 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(const std::string& path, bo
  return std::move(loaded_apk);
}

std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode /*mode*/) const {
  ATRACE_NAME("ApkAssets::Open");
std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const {
  ATRACE_CALL();
  CHECK(zip_handle_ != nullptr);

  ::ZipString name(path.c_str());
  ::ZipEntry entry;
  int32_t result = ::FindEntry(zip_handle_.get(), name, &entry);
  if (result != 0) {
    LOG(ERROR) << "No entry '" << path << "' found in APK.";
    LOG(ERROR) << "No entry '" << path << "' found in APK '" << path_ << "'";
    return {};
  }

  if (entry.method == kCompressDeflated) {
    auto compressed_asset = util::make_unique<_CompressedAsset>();
    if (compressed_asset->openChunk(::GetFileDescriptor(zip_handle_.get()), entry.offset,
                                    entry.method, entry.uncompressed_length,
                                    entry.compressed_length) != NO_ERROR) {
    std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
    if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
                     entry.compressed_length, true /*readOnly*/)) {
      LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
      return {};
    }

    std::unique_ptr<Asset> asset =
        Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
    if (asset == nullptr) {
      LOG(ERROR) << "Failed to decompress '" << path << "'.";
      return {};
    }
    return std::move(compressed_asset);
    return asset;
  } else {
    auto uncompressed_asset = util::make_unique<_FileAsset>();
    if (uncompressed_asset->openChunk(path.c_str(), ::GetFileDescriptor(zip_handle_.get()),
                                      entry.offset, entry.uncompressed_length) != NO_ERROR) {
      LOG(ERROR) << "Failed to mmap '" << path << "'.";
    std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
    if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
                     entry.uncompressed_length, true /*readOnly*/)) {
      LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
      return {};
    }
    return std::move(uncompressed_asset);
  }

    std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map), mode);
    if (asset == nullptr) {
      LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
      return {};
    }
    return asset;
  }
}

bool ApkAssets::ForEachFile(const std::string& root_path,
                            const std::function<void(const StringPiece&, FileType)>& f) const {
  CHECK(zip_handle_ != nullptr);

  std::string root_path_full = root_path;
  if (root_path_full.back() != '/') {
    root_path_full += '/';
  }

  ::ZipString prefix(root_path_full.c_str());
  void* cookie;
  if (::StartIteration(zip_handle_.get(), &cookie, &prefix, nullptr) != 0) {
    return false;
  }

  ::ZipString name;
  ::ZipEntry entry;

  // We need to hold back directories because many paths will contain them and we want to only
  // surface one.
  std::set<std::string> dirs;

  int32_t result;
  while ((result = ::Next(cookie, &entry, &name)) == 0) {
    StringPiece full_file_path(reinterpret_cast<const char*>(name.name), name.name_length);
    StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
    auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
    if (iter != leaf_file_path.end()) {
      dirs.insert(
          leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string());
    } else if (!leaf_file_path.empty()) {
      f(leaf_file_path, kFileTypeRegular);
    }
  }
  ::EndIteration(cookie);

  // Now present the unique directories.
  for (const std::string& dir : dirs) {
    f(dir, kFileTypeDirectory);
  }

  // -1 is end of iteration, anything else is an error.
  return result == -1;
}

}  // namespace android
+32 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@

#include <androidfw/Asset.h>
#include <androidfw/StreamingZipInflater.h>
#include <androidfw/Util.h>
#include <androidfw/ZipFileRO.h>
#include <androidfw/ZipUtils.h>
#include <utils/Atomic.h>
@@ -298,6 +299,22 @@ Asset::Asset(void)
    return pAsset;
}

/*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,
    AccessMode mode)
{
    std::unique_ptr<_FileAsset> pAsset = util::make_unique<_FileAsset>();

    status_t result = pAsset->openChunk(dataMap.get());
    if (result != NO_ERROR) {
        return NULL;
    }

    // We succeeded, so relinquish control of dataMap
    (void) dataMap.release();
    pAsset->mAccessMode = mode;
    return std::move(pAsset);
}

/*
 * Create a new Asset from compressed data in a memory mapping.
 */
@@ -316,6 +333,21 @@ Asset::Asset(void)
    return pAsset;
}

/*static*/ std::unique_ptr<Asset> Asset::createFromCompressedMap(std::unique_ptr<FileMap> dataMap,
    size_t uncompressedLen, AccessMode mode)
{
  std::unique_ptr<_CompressedAsset> pAsset = util::make_unique<_CompressedAsset>();

  status_t result = pAsset->openChunk(dataMap.get(), uncompressedLen);
  if (result != NO_ERROR) {
      return NULL;
  }

  // We succeeded, so relinquish control of dataMap
  (void) dataMap.release();
  pAsset->mAccessMode = mode;
  return std::move(pAsset);
}

/*
 * Do generic seek() housekeeping.  Pass in the offset/whence values from
+69 −10
Original line number Diff line number Diff line
@@ -138,6 +138,17 @@ const DynamicRefTable* AssetManager2::GetDynamicRefTableForPackage(uint32_t pack
  return &package_groups_[idx].dynamic_ref_table;
}

const DynamicRefTable* AssetManager2::GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const {
  for (const PackageGroup& package_group : package_groups_) {
    for (const ApkAssetsCookie& package_cookie : package_group.cookies_) {
      if (package_cookie == cookie) {
        return &package_group.dynamic_ref_table;
      }
    }
  }
  return nullptr;
}

void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
  const int diff = configuration_.diff(configuration);
  configuration_ = configuration;
@@ -188,6 +199,35 @@ std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, ApkAsset
  return OpenNonAsset(new_path, cookie, mode);
}

std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) {
  ATRACE_CALL();

  std::string full_path = "assets/" + dirname;
  std::unique_ptr<SortedVector<AssetDir::FileInfo>> files =
      util::make_unique<SortedVector<AssetDir::FileInfo>>();

  // Start from the back.
  for (auto iter = apk_assets_.rbegin(); iter != apk_assets_.rend(); ++iter) {
    const ApkAssets* apk_assets = *iter;

    auto func = [&](const StringPiece& name, FileType type) {
      AssetDir::FileInfo info;
      info.setFileName(String8(name.data(), name.size()));
      info.setFileType(type);
      info.setSourceName(String8(apk_assets->GetPath().c_str()));
      files->add(info);
    };

    if (!apk_assets->ForEachFile(full_path, func)) {
      return {};
    }
  }

  std::unique_ptr<AssetDir> asset_dir = util::make_unique<AssetDir>();
  asset_dir->setFileList(files.release());
  return asset_dir;
}

// Search in reverse because that's how we used to do it and we need to preserve behaviour.
// This is unfortunate, because ClassLoaders delegate to the parent first, so the order
// is inconsistent for split APKs.
@@ -237,15 +277,15 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
    desired_config = &density_override_config;
  }

  const uint32_t package_id = get_package_id(resid);
  const uint8_t type_id = get_type_id(resid);
  const uint16_t entry_id = get_entry_id(resid);

  if (type_id == 0) {
  if (!is_valid_resid(resid)) {
    LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
    return kInvalidCookie;
  }

  const uint32_t package_id = get_package_id(resid);
  const uint8_t type_idx = get_type_id(resid) - 1;
  const uint16_t entry_id = get_entry_id(resid);

  const uint8_t idx = package_ids_[package_id];
  if (idx == 0xff) {
    LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid);
@@ -265,7 +305,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
    uint32_t current_flags = 0;

    const LoadedPackage* loaded_package = package_group.packages_[i];
    if (!loaded_package->FindEntry(type_id - 1, entry_id, *desired_config, &current_entry,
    if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, &current_entry,
                                   &current_config, &current_flags)) {
      continue;
    }
@@ -385,16 +425,16 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
                                                ResTable_config* in_out_selected_config,
                                                uint32_t* in_out_flags,
                                                ResTable_ref* out_last_reference) {
                                                uint32_t* out_last_reference) {
  ATRACE_CALL();
  constexpr const int kMaxIterations = 20;

  out_last_reference->ident = 0u;
  *out_last_reference = 0u;
  for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE &&
                              in_out_value->data != 0u && iteration < kMaxIterations;
       iteration++) {
    if (out_last_reference != nullptr) {
      out_last_reference->ident = in_out_value->data;
      *out_last_reference = in_out_value->data;
    }
    uint32_t new_flags = 0u;
    cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/,
@@ -405,7 +445,7 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu
    if (in_out_flags != nullptr) {
      *in_out_flags |= new_flags;
    }
    if (out_last_reference->ident == in_out_value->data) {
    if (*out_last_reference == in_out_value->data) {
      // This reference can't be resolved, so exit now and let the caller deal with it.
      return cookie;
    }
@@ -832,6 +872,25 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value,
  return kInvalidCookie;
}

ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
                                                 ResTable_config* in_out_selected_config,
                                                 uint32_t* in_out_type_spec_flags,
                                                 uint32_t* out_last_ref) {
  if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) {
    uint32_t new_flags;
    cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags);
    if (cookie == kInvalidCookie) {
      return kInvalidCookie;
    }

    if (in_out_type_spec_flags != nullptr) {
      *in_out_type_spec_flags |= new_flags;
    }
  }
  return asset_manager_->ResolveReference(cookie, in_out_value, in_out_selected_config,
                                          in_out_type_spec_flags, out_last_ref);
}

void Theme::Clear() {
  type_spec_flags_ = 0u;
  for (std::unique_ptr<Package>& package : packages_) {
+4 −2
Original line number Diff line number Diff line
@@ -6532,6 +6532,8 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
    return NO_ERROR;
}

DynamicRefTable::DynamicRefTable() : DynamicRefTable(0, false) {}

DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib)
    : mAssignedPackageId(packageId)
    , mAppAsLib(appAsLib)
@@ -6637,11 +6639,11 @@ status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
    // Do a proper lookup.
    uint8_t translatedId = mLookupTable[packageId];
    if (translatedId == 0) {
        ALOGV("DynamicRefTable(0x%02x): No mapping for build-time package ID 0x%02x.",
        ALOGW("DynamicRefTable(0x%02x): No mapping for build-time package ID 0x%02x.",
                (uint8_t)mAssignedPackageId, (uint8_t)packageId);
        for (size_t i = 0; i < 256; i++) {
            if (mLookupTable[i] != 0) {
                ALOGV("e[0x%02x] -> 0x%02x", (uint8_t)i, mLookupTable[i]);
                ALOGW("e[0x%02x] -> 0x%02x", (uint8_t)i, mLookupTable[i]);
            }
        }
        return UNKNOWN_ERROR;
+26 −0
Original line number Diff line number Diff line
@@ -41,5 +41,31 @@ void ReadUtf16StringFromDevice(const uint16_t* src, size_t len, std::string* out
  }
}

std::u16string Utf8ToUtf16(const StringPiece& utf8) {
  ssize_t utf16_length =
      utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
  if (utf16_length <= 0) {
    return {};
  }

  std::u16string utf16;
  utf16.resize(utf16_length);
  utf8_to_utf16(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length(), &*utf16.begin(),
                utf16_length + 1);
  return utf16;
}

std::string Utf16ToUtf8(const StringPiece16& utf16) {
  ssize_t utf8_length = utf16_to_utf8_length(utf16.data(), utf16.length());
  if (utf8_length <= 0) {
    return {};
  }

  std::string utf8;
  utf8.resize(utf8_length);
  utf16_to_utf8(utf16.data(), utf16.length(), &*utf8.begin(), utf8_length + 1);
  return utf8;
}

} // namespace util
} // namespace android
Loading