Revert^2 "libandroidfw hardening for IncFs"

55ef6167a2c235bd88c7216238b2001b46795b79

Change-Id: I02d4890d181655dfd0a14c188468db512559d27b
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index a3fcf90..8330363 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -61,6 +61,9 @@
     ],
     export_include_dirs: ["include"],
     export_shared_lib_headers: ["libz"],
+    static_libs: ["libincfs-utils"],
+    whole_static_libs: ["libincfs-utils"],
+    export_static_lib_headers: ["libincfs-utils"],
     target: {
         android: {
             srcs: [
@@ -69,13 +72,14 @@
                 "CursorWindow.cpp",
             ],
             shared_libs: [
-                "libziparchive",
                 "libbase",
                 "libbinder",
                 "liblog",
                 "libcutils",
+                "libincfs",
                 "libutils",
                 "libz",
+                "libziparchive",
             ],
             static: {
                 enabled: false,
@@ -86,11 +90,11 @@
                 enabled: false,
             },
             static_libs: [
-                "libziparchive",
                 "libbase",
-                "liblog",
                 "libcutils",
+                "liblog",
                 "libutils",
+                "libziparchive",
             ],
             shared_libs: [
                 "libz",
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index e15b42d..cb56a51 100755
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -25,13 +25,11 @@
 #include "android-base/unique_fd.h"
 #include "android-base/utf8.h"
 #include "utils/Compat.h"
-#include "utils/FileMap.h"
 #include "ziparchive/zip_archive.h"
 
 #include "androidfw/Asset.h"
 #include "androidfw/Idmap.h"
 #include "androidfw/misc.h"
-#include "androidfw/ResourceTypes.h"
 #include "androidfw/Util.h"
 
 namespace android {
@@ -161,50 +159,46 @@
     }
 
     const int fd = ::GetFileDescriptor(zip_handle_.get());
-     const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get());
+    const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get());
+    incfs::IncFsFileMap asset_map;
     if (entry.method == kCompressDeflated) {
-      std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
-      if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.compressed_length,
-                       true /*readOnly*/)) {
+      if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length, GetPath())) {
         LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
         return {};
       }
 
       std::unique_ptr<Asset> asset =
-          Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
+          Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode);
       if (asset == nullptr) {
         LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << friendly_name_ << "'";
         return {};
       }
       return asset;
-    } else {
-      std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
-      if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.uncompressed_length,
-                       true /*readOnly*/)) {
-        LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
-        return {};
-      }
-
-      unique_fd ufd;
-      if (!GetPath()) {
-        // If the `path` is not set, create a new `fd` for the new Asset to own in order to create
-        // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used
-        // to create new file descriptors.
-        ufd = unique_fd(dup(fd));
-        if (!ufd.ok()) {
-          LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'";
-          return {};
-        }
-      }
-
-      std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map),
-          std::move(ufd), mode);
-      if (asset == nullptr) {
-        LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
-        return {};
-      }
-      return asset;
     }
+
+    if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length, GetPath())) {
+      LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
+      return {};
+    }
+
+    unique_fd ufd;
+    if (!GetPath()) {
+      // If the `path` is not set, create a new `fd` for the new Asset to own in order to create
+      // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used
+      // to create new file descriptors.
+      ufd = unique_fd(dup(fd));
+      if (!ufd.ok()) {
+        LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'";
+        return {};
+      }
+    }
+
+    auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd));
+    if (asset == nullptr) {
+      LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
+      return {};
+    }
+    return asset;
   }
 
  private:
@@ -446,8 +440,8 @@
     }
   }
 
-  std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>();
-  if (!file_map->create(path, fd, offset, static_cast<size_t>(length), true /*readOnly*/)) {
+  incfs::IncFsFileMap file_map;
+  if (!file_map.Create(fd, offset, static_cast<size_t>(length), path)) {
     LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': "
                << SystemErrorCodeToString(errno);
     return {};
@@ -456,8 +450,8 @@
   // If `path` is set, do not pass ownership of the `fd` to the new Asset since
   // Asset::openFileDescriptor can use `path` to create new file descriptors.
   return Asset::createFromUncompressedMap(std::move(file_map),
-                                          (path) ? base::unique_fd(-1) : std::move(fd),
-                                          Asset::AccessMode::ACCESS_RANDOM);
+                                          Asset::AccessMode::ACCESS_RANDOM,
+                                          (path) ? base::unique_fd(-1) : std::move(fd));
 }
 
 std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
@@ -493,15 +487,14 @@
   loaded_apk->idmap_asset_ = std::move(idmap_asset);
   loaded_apk->loaded_idmap_ = std::move(idmap);
 
-  const StringPiece data(
-      reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
-      loaded_apk->resources_asset_->getLength());
-  if (data.data() == nullptr || data.empty()) {
+  const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */);
+  const size_t length = loaded_apk->resources_asset_->getLength();
+  if (!data || length == 0) {
     LOG(ERROR) << "Failed to read '" << kResourcesArsc << "' data in APK '" << path << "'.";
     return {};
   }
 
-  loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(),
+  loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, loaded_apk->loaded_idmap_.get(),
                                               property_flags);
   if (!loaded_apk->loaded_arsc_) {
     LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
@@ -525,15 +518,15 @@
       new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
   loaded_apk->resources_asset_ = std::move(resources_asset);
 
-  const StringPiece data(
-      reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
-      loaded_apk->resources_asset_->getLength());
-  if (data.data() == nullptr || data.empty()) {
+  const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */);
+  const size_t length = loaded_apk->resources_asset_->getLength();
+  if (!data || length == 0) {
     LOG(ERROR) << "Failed to read resources table data in '" << path << "'.";
     return {};
   }
 
-  loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags);
+  loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, nullptr /* loaded_idmap */,
+                                              property_flags);
   if (loaded_apk->loaded_arsc_ == nullptr) {
     LOG(ERROR) << "Failed to read resources table in '" << path << "'.";
     return {};
@@ -550,7 +543,6 @@
   }
   return (!loaded_idmap_ || loaded_idmap_->IsUpToDate()) &&
       last_mod_time_ == getFileModDate(path_.c_str());
-
 }
 
 }  // namespace android
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index cd30c18..4fbe4a3 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -298,34 +298,18 @@
 /*
  * Create a new Asset from a memory mapping.
  */
-/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, AccessMode mode)
+/*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(incfs::IncFsFileMap&& dataMap,
+                                                                   AccessMode mode,
+                                                                   base::unique_fd fd)
 {
-    _FileAsset* pAsset;
-    status_t result;
+    auto pAsset = util::make_unique<_FileAsset>();
 
-    pAsset = new _FileAsset;
-    result = pAsset->openChunk(dataMap, base::unique_fd(-1));
-    if (result != NO_ERROR) {
-        delete pAsset;
-        return NULL;
-    }
-
-    pAsset->mAccessMode = mode;
-    return pAsset;
-}
-
-/*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,
-    base::unique_fd fd, AccessMode mode)
-{
-    std::unique_ptr<_FileAsset> pAsset = util::make_unique<_FileAsset>();
-
-    status_t result = pAsset->openChunk(dataMap.get(), std::move(fd));
+    status_t result = pAsset->openChunk(std::move(dataMap), std::move(fd));
     if (result != NO_ERROR) {
         return NULL;
     }
 
     // We succeeded, so relinquish control of dataMap
-    (void) dataMap.release();
     pAsset->mAccessMode = mode;
     return std::move(pAsset);
 }
@@ -333,35 +317,18 @@
 /*
  * Create a new Asset from compressed data in a memory mapping.
  */
-/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap,
-    size_t uncompressedLen, AccessMode mode)
+/*static*/ std::unique_ptr<Asset> Asset::createFromCompressedMap(incfs::IncFsFileMap&& dataMap,
+                                                                 size_t uncompressedLen,
+                                                                 AccessMode mode)
 {
-    _CompressedAsset* pAsset;
-    status_t result;
+  auto pAsset = util::make_unique<_CompressedAsset>();
 
-    pAsset = new _CompressedAsset;
-    result = pAsset->openChunk(dataMap, uncompressedLen);
-    if (result != NO_ERROR) {
-        delete pAsset;
-        return NULL;
-    }
-
-    pAsset->mAccessMode = mode;
-    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);
+  status_t result = pAsset->openChunk(std::move(dataMap), uncompressedLen);
   if (result != NO_ERROR) {
       return NULL;
   }
 
   // We succeeded, so relinquish control of dataMap
-  (void) dataMap.release();
   pAsset->mAccessMode = mode;
   return std::move(pAsset);
 }
@@ -414,7 +381,7 @@
  * Constructor.
  */
 _FileAsset::_FileAsset(void)
-    : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mMap(NULL), mBuf(NULL)
+    : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mBuf(NULL)
 {
     // Register the Asset with the global list here after it is fully constructed and its
     // vtable pointer points to this concrete type. b/31113965
@@ -441,7 +408,7 @@
 status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length)
 {
     assert(mFp == NULL);    // no reopen
-    assert(mMap == NULL);
+    assert(!mMap.has_value());
     assert(fd >= 0);
     assert(offset >= 0);
 
@@ -484,15 +451,15 @@
 /*
  * Create the chunk from the map.
  */
-status_t _FileAsset::openChunk(FileMap* dataMap, base::unique_fd fd)
+status_t _FileAsset::openChunk(incfs::IncFsFileMap&& dataMap, base::unique_fd fd)
 {
     assert(mFp == NULL);    // no reopen
-    assert(mMap == NULL);
+    assert(!mMap.has_value());
     assert(dataMap != NULL);
 
-    mMap = dataMap;
+    mMap = std::move(dataMap);
     mStart = -1;            // not used
-    mLength = dataMap->getDataLength();
+    mLength = mMap->length();
     mFd = std::move(fd);
     assert(mOffset == 0);
 
@@ -528,10 +495,15 @@
     if (!count)
         return 0;
 
-    if (mMap != NULL) {
+    if (mMap.has_value()) {
         /* copy from mapped area */
         //printf("map read\n");
-        memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count);
+        const auto readPos = mMap->data().offset(mOffset).convert<char>();
+        if (!readPos.verify(count)) {
+            return -1;
+        }
+
+        memcpy(buf, readPos.unsafe_ptr(), count);
         actual = count;
     } else if (mBuf != NULL) {
         /* copy from buffer */
@@ -594,10 +566,6 @@
  */
 void _FileAsset::close(void)
 {
-    if (mMap != NULL) {
-        delete mMap;
-        mMap = NULL;
-    }
     if (mBuf != NULL) {
         delete[] mBuf;
         mBuf = NULL;
@@ -624,16 +592,21 @@
  * level and we'd be using a different object, but we didn't, so we
  * deal with it here.
  */
-const void* _FileAsset::getBuffer(bool wordAligned)
+const void* _FileAsset::getBuffer(bool aligned)
+{
+    return getIncFsBuffer(aligned).unsafe_ptr();
+}
+
+incfs::map_ptr<void> _FileAsset::getIncFsBuffer(bool aligned)
 {
     /* subsequent requests just use what we did previously */
     if (mBuf != NULL)
         return mBuf;
-    if (mMap != NULL) {
-        if (!wordAligned) {
-            return  mMap->getDataPtr();
+    if (mMap.has_value()) {
+        if (!aligned) {
+            return mMap->data();
         }
-        return ensureAlignment(mMap);
+        return ensureAlignment(*mMap);
     }
 
     assert(mFp != NULL);
@@ -671,47 +644,44 @@
         mBuf = buf;
         return mBuf;
     } else {
-        FileMap* map;
-
-        map = new FileMap;
-        if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) {
-            delete map;
+        incfs::IncFsFileMap map;
+        if (!map.Create(fileno(mFp), mStart, mLength, NULL /* file_name */ )) {
             return NULL;
         }
 
         ALOGV(" getBuffer: mapped\n");
 
-        mMap = map;
-        if (!wordAligned) {
-            return  mMap->getDataPtr();
+        mMap = std::move(map);
+        if (!aligned) {
+            return mMap->data();
         }
-        return ensureAlignment(mMap);
+        return ensureAlignment(*mMap);
     }
 }
 
 int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
 {
-    if (mMap != NULL) {
+    if (mMap.has_value()) {
         if (mFd.ok()) {
-          *outStart = mMap->getDataOffset();
-          *outLength = mMap->getDataLength();
-          const int fd = dup(mFd);
-          if (fd < 0) {
-            ALOGE("Unable to dup fd (%d).", mFd.get());
-            return -1;
-          }
-          lseek64(fd, 0, SEEK_SET);
-          return fd;
+            *outStart = mMap->offset();
+            *outLength = mMap->length();
+            const int fd = dup(mFd);
+            if (fd < 0) {
+                ALOGE("Unable to dup fd (%d).", mFd.get());
+                return -1;
+            }
+            lseek64(fd, 0, SEEK_SET);
+            return fd;
         }
-        const char* fname = mMap->getFileName();
+        const char* fname = mMap->file_name();
         if (fname == NULL) {
             fname = mFileName;
         }
         if (fname == NULL) {
             return -1;
         }
-        *outStart = mMap->getDataOffset();
-        *outLength = mMap->getDataLength();
+        *outStart = mMap->offset();
+        *outLength = mMap->length();
         return open(fname, O_RDONLY | O_BINARY);
     }
     if (mFileName == NULL) {
@@ -722,16 +692,21 @@
     return open(mFileName, O_RDONLY | O_BINARY);
 }
 
-const void* _FileAsset::ensureAlignment(FileMap* map)
+incfs::map_ptr<void> _FileAsset::ensureAlignment(const incfs::IncFsFileMap& map)
 {
-    void* data = map->getDataPtr();
-    if ((((size_t)data)&0x3) == 0) {
+    const auto data = map.data();
+    if (util::IsFourByteAligned(data)) {
         // We can return this directly if it is aligned on a word
         // boundary.
         ALOGV("Returning aligned FileAsset %p (%s).", this,
                 getAssetSource());
         return data;
     }
+
+     if (!data.convert<uint8_t>().verify(mLength)) {
+        return NULL;
+    }
+
     // If not aligned on a word boundary, then we need to copy it into
     // our own buffer.
     ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
@@ -741,7 +716,8 @@
         ALOGE("alloc of %ld bytes failed\n", (long) mLength);
         return NULL;
     }
-    memcpy(buf, data, mLength);
+
+    memcpy(buf, data.unsafe_ptr(), mLength);
     mBuf = buf;
     return buf;
 }
@@ -757,7 +733,7 @@
  */
 _CompressedAsset::_CompressedAsset(void)
     : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
-      mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
+      mFd(-1), mZipInflater(NULL), mBuf(NULL)
 {
     // Register the Asset with the global list here after it is fully constructed and its
     // vtable pointer points to this concrete type. b/31113965
@@ -786,7 +762,7 @@
     int compressionMethod, size_t uncompressedLen, size_t compressedLen)
 {
     assert(mFd < 0);        // no re-open
-    assert(mMap == NULL);
+    assert(!mMap.has_value());
     assert(fd >= 0);
     assert(offset >= 0);
     assert(compressedLen > 0);
@@ -815,20 +791,20 @@
  *
  * Nothing is expanded until the first read call.
  */
-status_t _CompressedAsset::openChunk(FileMap* dataMap, size_t uncompressedLen)
+status_t _CompressedAsset::openChunk(incfs::IncFsFileMap&& dataMap, size_t uncompressedLen)
 {
     assert(mFd < 0);        // no re-open
-    assert(mMap == NULL);
+    assert(!mMap.has_value());
     assert(dataMap != NULL);
 
-    mMap = dataMap;
+    mMap = std::move(dataMap);
     mStart = -1;        // not used
-    mCompressedLen = dataMap->getDataLength();
+    mCompressedLen = mMap->length();
     mUncompressedLen = uncompressedLen;
     assert(mOffset == 0);
 
     if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
-        mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen);
+        mZipInflater = new StreamingZipInflater(&(*mMap), uncompressedLen);
     }
     return NO_ERROR;
 }
@@ -901,11 +877,6 @@
  */
 void _CompressedAsset::close(void)
 {
-    if (mMap != NULL) {
-        delete mMap;
-        mMap = NULL;
-    }
-
     delete[] mBuf;
     mBuf = NULL;
 
@@ -940,8 +911,8 @@
         goto bail;
     }
 
-    if (mMap != NULL) {
-        if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf,
+    if (mMap.has_value()) {
+        if (!ZipUtils::inflateToBuffer(mMap->data(), buf,
                 mUncompressedLen, mCompressedLen))
             goto bail;
     } else {
@@ -976,3 +947,6 @@
     return mBuf;
 }
 
+incfs::map_ptr<void> _CompressedAsset::getIncFsBuffer(bool aligned) {
+    return incfs::map_ptr<void>(getBuffer(aligned));
+}
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index f7c8337..fb2b571 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -917,7 +917,7 @@
 Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile,
     const ZipEntryRO entry, AccessMode mode, const String8& entryName)
 {
-    Asset* pAsset = NULL;
+    std::unique_ptr<Asset> pAsset;
 
     // TODO: look for previously-created shared memory slice?
     uint16_t method;
@@ -932,28 +932,28 @@
         return NULL;
     }
 
-    FileMap* dataMap = pZipFile->createEntryFileMap(entry);
-    if (dataMap == NULL) {
+    std::optional<incfs::IncFsFileMap> dataMap = pZipFile->createEntryIncFsFileMap(entry);
+    if (!dataMap.has_value()) {
         ALOGW("create map from entry failed\n");
         return NULL;
     }
 
     if (method == ZipFileRO::kCompressStored) {
-        pAsset = Asset::createFromUncompressedMap(dataMap, mode);
+        pAsset = Asset::createFromUncompressedMap(std::move(*dataMap), mode);
         ALOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(),
-                dataMap->getFileName(), mode, pAsset);
+                dataMap->file_name(), mode, pAsset.get());
     } else {
-        pAsset = Asset::createFromCompressedMap(dataMap,
+        pAsset = Asset::createFromCompressedMap(std::move(*dataMap),
             static_cast<size_t>(uncompressedLen), mode);
         ALOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(),
-                dataMap->getFileName(), mode, pAsset);
+                dataMap->file_name(), mode, pAsset.get());
     }
     if (pAsset == NULL) {
         /* unexpected */
         ALOGW("create from segment failed\n");
     }
 
-    return pAsset;
+    return pAsset.release();
 }
 
 /*
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 99dd313..3e54dc6 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -38,9 +38,43 @@
 
 namespace android {
 
+namespace {
+
+using EntryValue = std::variant<Res_value, incfs::verified_map_ptr<ResTable_map_entry>>;
+
+base::expected<EntryValue, IOError> GetEntryValue(
+    incfs::verified_map_ptr<ResTable_entry> table_entry) {
+  const uint16_t entry_size = dtohs(table_entry->size);
+
+  // Check if the entry represents a bag value.
+  if (entry_size >= sizeof(ResTable_map_entry) &&
+      (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX)) {
+    const auto map_entry = table_entry.convert<ResTable_map_entry>();
+    if (!map_entry) {
+      return base::unexpected(IOError::PAGES_MISSING);
+    }
+    return map_entry.verified();
+  }
+
+  // The entry represents a non-bag value.
+  const auto entry_value = table_entry.offset(entry_size).convert<Res_value>();
+  if (!entry_value) {
+    return base::unexpected(IOError::PAGES_MISSING);
+  }
+  Res_value value;
+  value.copyFrom_dtoh(entry_value.value());
+  return value;
+}
+
+} // namespace
+
 struct FindEntryResult {
-  // A pointer to the value of the resource table entry.
-  std::variant<Res_value, const ResTable_map_entry*> entry;
+  // The cookie representing the ApkAssets in which the value resides.
+  ApkAssetsCookie cookie;
+
+  // The value of the resource table entry. Either an android::Res_value for non-bag types or an
+  // incfs::verified_map_ptr<ResTable_map_entry> for bag types.
+  EntryValue entry;
 
   // The configuration for which the resulting entry was defined. This is already swapped to host
   // endianness.
@@ -265,7 +299,7 @@
   }
 
   const PackageGroup& package_group = package_groups_[idx];
-  if (package_group.packages_.size() == 0) {
+  if (package_group.packages_.empty()) {
     return nullptr;
   }
 
@@ -310,14 +344,14 @@
     for (auto it = loaded_package->begin(); it != loaded_package->end(); it++) {
       const OverlayableInfo* info = loaded_package->GetOverlayableInfo(*it);
       if (info != nullptr) {
-        ResourceName res_name;
-        if (!GetResourceName(*it, &res_name)) {
+        auto res_name = GetResourceName(*it);
+        if (!res_name.has_value()) {
           ANDROID_LOG(ERROR) << base::StringPrintf(
               "Unable to retrieve name of overlayable resource 0x%08x", *it);
           return false;
         }
 
-        const std::string name = ToFormattedResourceString(&res_name);
+        const std::string name = ToFormattedResourceString(*res_name);
         output.append(base::StringPrintf(
             "resource='%s' overlayable='%s' actor='%s' policy='0x%08x'\n",
             name.c_str(), info->name.c_str(), info->actor.c_str(), info->policy_flags));
@@ -365,8 +399,8 @@
   return non_system_overlays;
 }
 
-std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
-                                                                   bool exclude_mipmap) const {
+base::expected<std::set<ResTable_config>, IOError> AssetManager2::GetResourceConfigurations(
+    bool exclude_system, bool exclude_mipmap) const {
   ATRACE_NAME("AssetManager::GetResourceConfigurations");
   const auto non_system_overlays =
       (exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>();
@@ -386,7 +420,10 @@
         continue;
       }
 
-      package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations);
+      auto result = package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations);
+      if (UNLIKELY(!result.has_value())) {
+        return base::unexpected(result.error());
+      }
     }
   }
   return configurations;
@@ -501,11 +538,11 @@
   return apk_assets_[cookie]->GetAssetsProvider()->Open(filename, mode);
 }
 
-ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
-                                         bool /*stop_at_first_match*/,
-                                         bool ignore_configuration,
-                                         FindEntryResult* out_entry) const {
-  if (resource_resolution_logging_enabled_) {
+base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
+    uint32_t resid, uint16_t density_override, bool stop_at_first_match,
+    bool ignore_configuration) const {
+  const bool logging_enabled = resource_resolution_logging_enabled_;
+  if (UNLIKELY(logging_enabled)) {
     // Clear the last logged resource resolution.
     ResetResourceResolution();
     last_resolution_.resid = resid;
@@ -523,94 +560,96 @@
   }
 
   // Retrieve the package group from the package id of the resource id.
-  if (!is_valid_resid(resid)) {
+  if (UNLIKELY(!is_valid_resid(resid))) {
     LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
-    return kInvalidCookie;
+    return base::unexpected(std::nullopt);
   }
 
   const uint32_t package_id = get_package_id(resid);
   const uint8_t type_idx = get_type_id(resid) - 1;
   const uint16_t entry_idx = get_entry_id(resid);
   uint8_t package_idx = package_ids_[package_id];
-  if (package_idx == 0xff) {
+  if (UNLIKELY(package_idx == 0xff)) {
     ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.",
                                              package_id, resid);
-    return kInvalidCookie;
+    return base::unexpected(std::nullopt);
   }
 
   const PackageGroup& package_group = package_groups_[package_idx];
-  ApkAssetsCookie cookie = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
-                                             false /* stop_at_first_match */,
-                                             ignore_configuration, out_entry);
-  if (UNLIKELY(cookie == kInvalidCookie)) {
-    return kInvalidCookie;
+  auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
+                                 stop_at_first_match, ignore_configuration);
+  if (UNLIKELY(!result.has_value())) {
+    return base::unexpected(result.error());
   }
 
-  if (!apk_assets_[cookie]->IsLoader()) {
+  if (!stop_at_first_match && !ignore_configuration && !apk_assets_[result->cookie]->IsLoader()) {
     for (const auto& id_map : package_group.overlays_) {
       auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
       if (!overlay_entry) {
         // No id map entry exists for this target resource.
         continue;
-      } else if (overlay_entry.IsInlineValue()) {
+      }
+      if (overlay_entry.IsInlineValue()) {
         // The target resource is overlaid by an inline value not represented by a resource.
-        out_entry->entry = overlay_entry.GetInlineValue();
-        out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
-        cookie = id_map.cookie;
+        result->entry = overlay_entry.GetInlineValue();
+        result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
+        result->cookie = id_map.cookie;
         continue;
       }
 
-      FindEntryResult overlay_result;
-      ApkAssetsCookie overlay_cookie = FindEntry(overlay_entry.GetResourceId(), density_override,
-                                                 false /* stop_at_first_match */,
-                                                 ignore_configuration, &overlay_result);
-      if (UNLIKELY(overlay_cookie == kInvalidCookie)) {
+      auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override,
+                                      false /* stop_at_first_match */,
+                                      false /* ignore_configuration */);
+      if (UNLIKELY(IsIOError(overlay_result))) {
+        return base::unexpected(overlay_result.error());
+      }
+      if (!overlay_result.has_value()) {
         continue;
       }
 
-      if (!overlay_result.config.isBetterThan(out_entry->config, desired_config)
-          && overlay_result.config.compare(out_entry->config) != 0) {
+      if (!overlay_result->config.isBetterThan(result->config, desired_config)
+          && overlay_result->config.compare(result->config) != 0) {
         // The configuration of the entry for the overlay must be equal to or better than the target
         // configuration to be chosen as the better value.
         continue;
       }
 
-      cookie = overlay_cookie;
-      out_entry->entry = overlay_result.entry;
-      out_entry->config = overlay_result.config;
-      out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
-      if (resource_resolution_logging_enabled_) {
+      result->cookie = overlay_result->cookie;
+      result->entry = overlay_result->entry;
+      result->config = overlay_result->config;
+      result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
+
+      if (UNLIKELY(logging_enabled)) {
         last_resolution_.steps.push_back(
-            Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(),
-                             overlay_result.package_name});
+            Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->config.toString(),
+                             overlay_result->package_name});
       }
     }
   }
 
-  if (resource_resolution_logging_enabled_) {
-    last_resolution_.cookie = cookie;
-    last_resolution_.type_string_ref = out_entry->type_string_ref;
-    last_resolution_.entry_string_ref = out_entry->entry_string_ref;
+  if (UNLIKELY(logging_enabled)) {
+    last_resolution_.cookie = result->cookie;
+    last_resolution_.type_string_ref = result->type_string_ref;
+    last_resolution_.entry_string_ref = result->entry_string_ref;
   }
 
-  return cookie;
+  return result;
 }
 
-ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_group,
-                                                 uint8_t type_idx, uint16_t entry_idx,
-                                                 const ResTable_config& desired_config,
-                                                 bool /*stop_at_first_match*/,
-                                                 bool ignore_configuration,
-                                                 FindEntryResult* out_entry) const {
+base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
+    const PackageGroup& package_group, uint8_t type_idx, uint16_t entry_idx,
+    const ResTable_config& desired_config, bool stop_at_first_match,
+    bool ignore_configuration) const {
+  const bool logging_enabled = resource_resolution_logging_enabled_;
   ApkAssetsCookie best_cookie = kInvalidCookie;
   const LoadedPackage* best_package = nullptr;
-  const ResTable_type* best_type = nullptr;
+  incfs::verified_map_ptr<ResTable_type> best_type;
   const ResTable_config* best_config = nullptr;
   ResTable_config best_config_copy;
-  uint32_t best_offset = 0u;
-  uint32_t type_flags = 0u;
+  uint32_t best_offset = 0U;
+  uint32_t type_flags = 0U;
 
-  Resolution::Step::Type resolution_type = Resolution::Step::Type::NO_ENTRY;
+  auto resolution_type = Resolution::Step::Type::NO_ENTRY;
   std::vector<Resolution::Step> resolution_steps;
 
   // If desired_config is the same as the set configuration, then we can use our filtered list
@@ -630,17 +669,20 @@
       continue;
     }
 
+    auto entry_flags = type_spec->GetFlagsForEntryIndex(entry_idx);
+    if (UNLIKELY(!entry_flags)) {
+      return base::unexpected(entry_flags.error());
+    }
+    type_flags |= entry_flags.value();
+
     // If the package is an overlay or custom loader,
     // then even configurations that are the same MUST be chosen.
     const bool package_is_loader = loaded_package->IsCustomLoader();
-    type_flags |= type_spec->GetFlagsForEntryIndex(entry_idx);
 
     if (use_fast_path) {
       const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
-      const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations;
-      const size_t type_count = candidate_configs.size();
-      for (uint32_t i = 0; i < type_count; i++) {
-        const ResTable_config& this_config = candidate_configs[i];
+      for (const auto& type_config : filtered_group.type_configs) {
+        const ResTable_config& this_config = type_config.config;
 
         // We can skip calling ResTable_config::match() because we know that all candidate
         // configurations that do NOT match have been filtered-out.
@@ -652,7 +694,7 @@
         } else if (package_is_loader && this_config.compare(*best_config) == 0) {
           resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
         } else {
-          if (resource_resolution_logging_enabled_) {
+          if (UNLIKELY(logging_enabled)) {
             resolution_type = (package_is_loader) ? Resolution::Step::Type::SKIPPED_LOADER
                                                   : Resolution::Step::Type::SKIPPED;
             resolution_steps.push_back(Resolution::Step{resolution_type,
@@ -664,10 +706,13 @@
 
         // The configuration matches and is better than the previous selection.
         // Find the entry value if it exists for this configuration.
-        const ResTable_type* type = filtered_group.types[i];
-        const uint32_t offset = LoadedPackage::GetEntryOffset(type, entry_idx);
-        if (offset == ResTable_type::NO_ENTRY) {
-          if (resource_resolution_logging_enabled_) {
+        const auto& type = type_config.type;
+        const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx);
+        if (UNLIKELY(IsIOError(offset))) {
+          return base::unexpected(offset.error());
+        }
+        if (!offset.has_value()) {
+          if (UNLIKELY(logging_enabled)) {
             if (package_is_loader) {
               resolution_type = Resolution::Step::Type::NO_ENTRY_LOADER;
             } else {
@@ -684,9 +729,9 @@
         best_package = loaded_package;
         best_type = type;
         best_config = &this_config;
-        best_offset = offset;
+        best_offset = offset.value();
 
-        if (resource_resolution_logging_enabled_) {
+        if (UNLIKELY(logging_enabled)) {
           last_resolution_.steps.push_back(Resolution::Step{resolution_type,
                                                             this_config.toString(),
                                                             &loaded_package->GetPackageName()});
@@ -700,10 +745,11 @@
       // ResTable_config, we must copy it.
       const auto iter_end = type_spec->types + type_spec->type_count;
       for (auto iter = type_spec->types; iter != iter_end; ++iter) {
-        ResTable_config this_config{};
+        const incfs::verified_map_ptr<ResTable_type>& type = *iter;
 
+        ResTable_config this_config{};
         if (!ignore_configuration) {
-          this_config.copyFromDtoH((*iter)->config);
+          this_config.copyFromDtoH(type->config);
           if (!this_config.match(desired_config)) {
             continue;
           }
@@ -722,24 +768,27 @@
 
         // The configuration matches and is better than the previous selection.
         // Find the entry value if it exists for this configuration.
-        const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, entry_idx);
-        if (offset == ResTable_type::NO_ENTRY) {
+        const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx);
+        if (UNLIKELY(IsIOError(offset))) {
+          return base::unexpected(offset.error());
+        }
+        if (!offset.has_value()) {
           continue;
         }
 
         best_cookie = cookie;
         best_package = loaded_package;
-        best_type = *iter;
+        best_type = type;
         best_config_copy = this_config;
         best_config = &best_config_copy;
-        best_offset = offset;
+        best_offset = offset.value();
 
-        if (ignore_configuration) {
+        if (stop_at_first_match) {
           // Any configuration will suffice, so break.
           break;
         }
 
-        if (resource_resolution_logging_enabled_) {
+        if (UNLIKELY(logging_enabled)) {
           last_resolution_.steps.push_back(Resolution::Step{resolution_type,
                                                             this_config.toString(),
                                                             &loaded_package->GetPackageName()});
@@ -749,36 +798,35 @@
   }
 
   if (UNLIKELY(best_cookie == kInvalidCookie)) {
-    return kInvalidCookie;
+    return base::unexpected(std::nullopt);
   }
 
-  const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
-  if (UNLIKELY(best_entry == nullptr)) {
-    return kInvalidCookie;
+  auto best_entry_result = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
+  if (!best_entry_result.has_value()) {
+    return base::unexpected(best_entry_result.error());
   }
 
-  const uint16_t entry_size = dtohs(best_entry->size);
-  if (entry_size >= sizeof(ResTable_map_entry) &&
-      (dtohs(best_entry->flags) & ResTable_entry::FLAG_COMPLEX)) {
-    // The entry represents a bag/map.
-    out_entry->entry = reinterpret_cast<const ResTable_map_entry*>(best_entry);
-  } else {
-    // The entry represents a value.
-    Res_value value;
-    value.copyFrom_dtoh(*reinterpret_cast<const Res_value*>(
-        reinterpret_cast<const uint8_t*>(best_entry) + entry_size));
-    out_entry->entry = value;
+  const incfs::map_ptr<ResTable_entry> best_entry = *best_entry_result;
+  if (!best_entry) {
+    return base::unexpected(IOError::PAGES_MISSING);
   }
 
-  out_entry->config = *best_config;
-  out_entry->type_flags = type_flags;
-  out_entry->package_name = &best_package->GetPackageName();
-  out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
-  out_entry->entry_string_ref =
-          StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
-  out_entry->dynamic_ref_table = package_group.dynamic_ref_table.get();
+  const auto entry = GetEntryValue(best_entry.verified());
+  if (!entry.has_value()) {
+    return base::unexpected(entry.error());
+  }
 
-  return best_cookie;
+  return FindEntryResult{
+    .cookie = best_cookie,
+    .entry = *entry,
+    .config = *best_config,
+    .type_flags = type_flags,
+    .package_name = &best_package->GetPackageName(),
+    .type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1),
+    .entry_string_ref = StringPoolRef(best_package->GetKeyStringPool(),
+                                      best_entry->key.index),
+    .dynamic_ref_table = package_group.dynamic_ref_table.get(),
+  };
 }
 
 void AssetManager2::ResetResourceResolution() const {
@@ -799,30 +847,28 @@
 std::string AssetManager2::GetLastResourceResolution() const {
   if (!resource_resolution_logging_enabled_) {
     LOG(ERROR) << "Must enable resource resolution logging before getting path.";
-    return std::string();
+    return {};
   }
 
   auto cookie = last_resolution_.cookie;
   if (cookie == kInvalidCookie) {
     LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path.";
-    return std::string();
+    return {};
   }
 
   uint32_t resid = last_resolution_.resid;
   std::vector<Resolution::Step>& steps = last_resolution_.steps;
-
-  ResourceName resource_name;
   std::string resource_name_string;
 
   const LoadedPackage* package =
           apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
 
   if (package != nullptr) {
-    ToResourceName(last_resolution_.type_string_ref,
-                   last_resolution_.entry_string_ref,
-                   package->GetPackageName(),
-                   &resource_name);
-    resource_name_string = ToFormattedResourceString(&resource_name);
+    auto resource_name = ToResourceName(last_resolution_.type_string_ref,
+                                        last_resolution_.entry_string_ref,
+                                        package->GetPackageName());
+    resource_name_string = resource_name.has_value() ?
+        ToFormattedResourceString(resource_name.value()) : "<unknown>";
   }
 
   std::stringstream log_stream;
@@ -875,200 +921,186 @@
   return log_stream.str();
 }
 
-bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const {
-  FindEntryResult entry;
-  ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
-                                     true /* stop_at_first_match */,
-                                     true /* ignore_configuration */, &entry);
-  if (cookie == kInvalidCookie) {
-    return false;
+base::expected<AssetManager2::ResourceName, NullOrIOError> AssetManager2::GetResourceName(
+    uint32_t resid) const {
+  auto result = FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */,
+                          true /* ignore_configuration */);
+  if (!result.has_value()) {
+    return base::unexpected(result.error());
   }
 
-  return ToResourceName(entry.type_string_ref,
-                        entry.entry_string_ref,
-                        *entry.package_name,
-                        out_name);
+  return ToResourceName(result->type_string_ref,
+                        result->entry_string_ref,
+                        *result->package_name);
 }
 
-bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const {
-  FindEntryResult entry;
-  ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
-                                     false /* stop_at_first_match */,
-                                     true /* ignore_configuration */, &entry);
-  if (cookie != kInvalidCookie) {
-    *out_flags = entry.type_flags;
-    return true;
-  }
-  return false;
-}
-
-ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
-                                           uint16_t density_override, Res_value* out_value,
-                                           ResTable_config* out_selected_config,
-                                           uint32_t* out_flags) const {
-  FindEntryResult entry;
-  ApkAssetsCookie cookie = FindEntry(resid, density_override, false /* stop_at_first_match */,
-                                     false /* ignore_configuration */, &entry);
-  if (cookie == kInvalidCookie) {
-    return kInvalidCookie;
+base::expected<AssetManager2::SelectedValue, NullOrIOError> AssetManager2::GetResource(
+    uint32_t resid, bool may_be_bag, uint16_t density_override) const {
+  auto result = FindEntry(resid, density_override, false /* stop_at_first_match */,
+                          false /* ignore_configuration */);
+  if (!result.has_value()) {
+    return base::unexpected(result.error());
   }
 
-  auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry);
+  auto result_map_entry = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&result->entry);
   if (result_map_entry != nullptr) {
     if (!may_be_bag) {
       LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
-      return kInvalidCookie;
+      return base::unexpected(std::nullopt);
     }
 
     // Create a reference since we can't represent this complex type as a Res_value.
-    out_value->dataType = Res_value::TYPE_REFERENCE;
-    out_value->data = resid;
-    *out_selected_config = entry.config;
-    *out_flags = entry.type_flags;
-    return cookie;
+    return SelectedValue(Res_value::TYPE_REFERENCE, resid, result->cookie, result->type_flags,
+                         resid, result->config);
   }
 
   // Convert the package ID to the runtime assigned package ID.
-  *out_value = std::get<Res_value>(entry.entry);
-  entry.dynamic_ref_table->lookupResourceValue(out_value);
+  Res_value value = std::get<Res_value>(result->entry);
+  result->dynamic_ref_table->lookupResourceValue(&value);
 
-  *out_selected_config = entry.config;
-  *out_flags = entry.type_flags;
-  return cookie;
+  return SelectedValue(value.dataType, value.data, result->cookie, result->type_flags,
+                       resid, result->config);
 }
 
-ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
-                                                ResTable_config* in_out_selected_config,
-                                                uint32_t* in_out_flags,
-                                                uint32_t* out_last_reference) const {
-  constexpr const int kMaxIterations = 20;
-
-  for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE &&
-                              in_out_value->data != 0u && iteration < kMaxIterations;
-       iteration++) {
-    *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*/,
-                         in_out_value, in_out_selected_config, &new_flags);
-    if (cookie == kInvalidCookie) {
-      return kInvalidCookie;
-    }
-    if (in_out_flags != nullptr) {
-      *in_out_flags |= new_flags;
-    }
-    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;
-    }
+base::expected<std::monostate, NullOrIOError> AssetManager2::ResolveReference(
+    AssetManager2::SelectedValue& value) const {
+  if (value.type != Res_value::TYPE_REFERENCE || value.data == 0U) {
+    // Not a reference. Nothing to do.
+    return {};
   }
-  return cookie;
+
+  uint32_t combined_flags = value.flags;
+  uint32_t resolve_resid = value.data;
+  constexpr const uint32_t kMaxIterations = 20;
+  for (uint32_t i = 0U;; i++) {
+    auto result = GetResource(resolve_resid, true /*may_be_bag*/);
+    if (!result.has_value()) {
+      return base::unexpected(result.error());
+    }
+
+    if (result->type != Res_value::TYPE_REFERENCE ||
+        result->data == Res_value::DATA_NULL_UNDEFINED ||
+        result->data == resolve_resid || i == kMaxIterations) {
+      // This reference can't be resolved, so exit now and let the caller deal with it.
+      value = *result;
+      value.flags |= combined_flags;
+      return {};
+    }
+
+    combined_flags |= result->flags;
+    resolve_resid = result->data;
+  }
 }
 
-const std::vector<uint32_t> AssetManager2::GetBagResIdStack(uint32_t resid) {
+const std::vector<uint32_t> AssetManager2::GetBagResIdStack(uint32_t resid) const {
   auto cached_iter = cached_bag_resid_stacks_.find(resid);
   if (cached_iter != cached_bag_resid_stacks_.end()) {
     return cached_iter->second;
-  } else {
-    auto found_resids = std::vector<uint32_t>();
-    GetBag(resid, found_resids);
-    // Cache style stacks if they are not already cached.
-    cached_bag_resid_stacks_[resid] = found_resids;
-    return found_resids;
   }
+
+  std::vector<uint32_t> found_resids;
+  GetBag(resid, found_resids);
+  cached_bag_resid_stacks_.emplace(resid, found_resids);
+  return found_resids;
 }
 
-const ResolvedBag* AssetManager2::GetBag(uint32_t resid) {
-  auto found_resids = std::vector<uint32_t>();
-  auto bag = GetBag(resid, found_resids);
+base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::ResolveBag(
+    AssetManager2::SelectedValue& value) const {
+  if (UNLIKELY(value.type != Res_value::TYPE_REFERENCE)) {
+    return base::unexpected(std::nullopt);
+  }
 
-  // Cache style stacks if they are not already cached.
-  auto cached_iter = cached_bag_resid_stacks_.find(resid);
-  if (cached_iter == cached_bag_resid_stacks_.end()) {
-    cached_bag_resid_stacks_[resid] = found_resids;
+  auto bag = GetBag(value.data);
+  if (bag.has_value()) {
+    value.flags |= (*bag)->type_spec_flags;
   }
   return bag;
 }
 
-static bool compare_bag_entries(const ResolvedBag::Entry& entry1,
-    const ResolvedBag::Entry& entry2) {
-  return entry1.key < entry2.key;
+base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(uint32_t resid) const {
+  std::vector<uint32_t> found_resids;
+  const auto bag = GetBag(resid, found_resids);
+  cached_bag_resid_stacks_.emplace(resid, found_resids);
+  return bag;
 }
 
-const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& child_resids) {
-  auto cached_iter = cached_bags_.find(resid);
-  if (cached_iter != cached_bags_.end()) {
+base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(
+    uint32_t resid, std::vector<uint32_t>& child_resids) const {
+  if (auto cached_iter = cached_bags_.find(resid); cached_iter != cached_bags_.end()) {
     return cached_iter->second.get();
   }
 
-  FindEntryResult entry;
-  ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
-                                     false /* stop_at_first_match */,
-                                     false /* ignore_configuration */,
-                                     &entry);
-  if (cookie == kInvalidCookie) {
-    return nullptr;
+  auto entry = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */,
+                         false /* ignore_configuration */);
+  if (!entry.has_value()) {
+    return base::unexpected(entry.error());
   }
 
-  auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry);
-  if (result_map_entry == nullptr) {
+  auto entry_map = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&entry->entry);
+  if (entry_map == nullptr) {
     // Not a bag, nothing to do.
-    return nullptr;
+    return base::unexpected(std::nullopt);
   }
 
-  auto map = reinterpret_cast<const ResTable_map_entry*>(*result_map_entry);
-  auto map_entry = reinterpret_cast<const ResTable_map*>(
-      reinterpret_cast<const uint8_t*>(map) + map->size);
-  const ResTable_map* const map_entry_end = map_entry + dtohl(map->count);
+  auto map = *entry_map;
+  auto map_entry = map.offset(dtohs(map->size)).convert<ResTable_map>();
+  const auto map_entry_end = map_entry + dtohl(map->count);
 
   // Keep track of ids that have already been seen to prevent infinite loops caused by circular
-  // dependencies between bags
+  // dependencies between bags.
   child_resids.push_back(resid);
 
   uint32_t parent_resid = dtohl(map->parent.ident);
-  if (parent_resid == 0U || std::find(child_resids.begin(), child_resids.end(), parent_resid)
-      != child_resids.end()) {
-    // There is no parent or a circular dependency exist, meaning there is nothing to inherit and
-    // we can do a simple copy of the entries in the map.
+  if (parent_resid == 0U ||
+      std::find(child_resids.begin(), child_resids.end(), parent_resid) != child_resids.end()) {
+    // There is no parent or a circular parental dependency exist, meaning there is nothing to
+    // inherit and we can do a simple copy of the entries in the map.
     const size_t entry_count = map_entry_end - map_entry;
     util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
         malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))};
 
     bool sort_entries = false;
-    ResolvedBag::Entry* new_entry = new_bag->entries;
-    for (; map_entry != map_entry_end; ++map_entry) {
+    for (auto new_entry = new_bag->entries; map_entry != map_entry_end; ++map_entry) {
+      if (UNLIKELY(!map_entry)) {
+        return base::unexpected(IOError::PAGES_MISSING);
+      }
+
       uint32_t new_key = dtohl(map_entry->name.ident);
       if (!is_internal_resid(new_key)) {
         // Attributes, arrays, etc don't have a resource id as the name. They specify
         // other data, which would be wrong to change via a lookup.
-        if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
+        if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR)) {
           LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key,
                                            resid);
-          return nullptr;
+          return base::unexpected(std::nullopt);
         }
       }
-      new_entry->cookie = cookie;
+
+      new_entry->cookie = entry->cookie;
       new_entry->key = new_key;
       new_entry->key_pool = nullptr;
       new_entry->type_pool = nullptr;
       new_entry->style = resid;
       new_entry->value.copyFrom_dtoh(map_entry->value);
-      status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value);
-      if (err != NO_ERROR) {
+      status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value);
+      if (UNLIKELY(err != NO_ERROR)) {
         LOG(ERROR) << base::StringPrintf(
             "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType,
             new_entry->value.data, new_key);
-        return nullptr;
+        return base::unexpected(std::nullopt);
       }
+
       sort_entries = sort_entries ||
           (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
       ++new_entry;
     }
 
     if (sort_entries) {
-      std::sort(new_bag->entries, new_bag->entries + entry_count, compare_bag_entries);
+      std::sort(new_bag->entries, new_bag->entries + entry_count,
+                [](auto&& lhs, auto&& rhs) { return lhs.key < rhs.key; });
     }
 
-    new_bag->type_spec_flags = entry.type_flags;
+    new_bag->type_spec_flags = entry->type_flags;
     new_bag->entry_count = static_cast<uint32_t>(entry_count);
     ResolvedBag* result = new_bag.get();
     cached_bags_[resid] = std::move(new_bag);
@@ -1076,54 +1108,58 @@
   }
 
   // In case the parent is a dynamic reference, resolve it.
-  entry.dynamic_ref_table->lookupResourceId(&parent_resid);
+  entry->dynamic_ref_table->lookupResourceId(&parent_resid);
 
   // Get the parent and do a merge of the keys.
-  const ResolvedBag* parent_bag = GetBag(parent_resid, child_resids);
-  if (parent_bag == nullptr) {
+  const auto parent_bag = GetBag(parent_resid, child_resids);
+  if (UNLIKELY(!parent_bag.has_value())) {
     // Failed to get the parent that should exist.
     LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid,
                                      resid);
-    return nullptr;
+    return base::unexpected(parent_bag.error());
   }
 
   // Create the max possible entries we can make. Once we construct the bag,
   // we will realloc to fit to size.
-  const size_t max_count = parent_bag->entry_count + dtohl(map->count);
+  const size_t max_count = (*parent_bag)->entry_count + dtohl(map->count);
   util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
       malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))))};
   ResolvedBag::Entry* new_entry = new_bag->entries;
 
-  const ResolvedBag::Entry* parent_entry = parent_bag->entries;
-  const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count;
+  const ResolvedBag::Entry* parent_entry = (*parent_bag)->entries;
+  const ResolvedBag::Entry* const parent_entry_end = parent_entry + (*parent_bag)->entry_count;
 
   // The keys are expected to be in sorted order. Merge the two bags.
   bool sort_entries = false;
   while (map_entry != map_entry_end && parent_entry != parent_entry_end) {
+    if (UNLIKELY(!map_entry)) {
+      return base::unexpected(IOError::PAGES_MISSING);
+    }
+
     uint32_t child_key = dtohl(map_entry->name.ident);
     if (!is_internal_resid(child_key)) {
-      if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) {
+      if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR)) {
         LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key,
                                          resid);
-        return nullptr;
+        return base::unexpected(std::nullopt);
       }
     }
 
     if (child_key <= parent_entry->key) {
       // Use the child key if it comes before the parent
       // or is equal to the parent (overrides).
-      new_entry->cookie = cookie;
+      new_entry->cookie = entry->cookie;
       new_entry->key = child_key;
       new_entry->key_pool = nullptr;
       new_entry->type_pool = nullptr;
       new_entry->value.copyFrom_dtoh(map_entry->value);
       new_entry->style = resid;
-      status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value);
-      if (err != NO_ERROR) {
+      status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value);
+      if (UNLIKELY(err != NO_ERROR)) {
         LOG(ERROR) << base::StringPrintf(
             "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType,
             new_entry->value.data, child_key);
-        return nullptr;
+        return base::unexpected(std::nullopt);
       }
       ++map_entry;
     } else {
@@ -1143,25 +1179,29 @@
 
   // Finish the child entries if they exist.
   while (map_entry != map_entry_end) {
+    if (UNLIKELY(!map_entry)) {
+      return base::unexpected(IOError::PAGES_MISSING);
+    }
+
     uint32_t new_key = dtohl(map_entry->name.ident);
     if (!is_internal_resid(new_key)) {
-      if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
+      if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR)) {
         LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key,
                                          resid);
-        return nullptr;
+        return base::unexpected(std::nullopt);
       }
     }
-    new_entry->cookie = cookie;
+    new_entry->cookie = entry->cookie;
     new_entry->key = new_key;
     new_entry->key_pool = nullptr;
     new_entry->type_pool = nullptr;
     new_entry->value.copyFrom_dtoh(map_entry->value);
     new_entry->style = resid;
-    status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value);
-    if (err != NO_ERROR) {
+    status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value);
+    if (UNLIKELY(err != NO_ERROR)) {
       LOG(ERROR) << base::StringPrintf("Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.",
                                        new_entry->value.dataType, new_entry->value.data, new_key);
-      return nullptr;
+      return base::unexpected(std::nullopt);
     }
     sort_entries = sort_entries ||
         (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
@@ -1185,11 +1225,12 @@
   }
 
   if (sort_entries) {
-    std::sort(new_bag->entries, new_bag->entries + actual_count, compare_bag_entries);
+    std::sort(new_bag->entries, new_bag->entries + actual_count,
+              [](auto&& lhs, auto&& rhs) { return lhs.key < rhs.key; });
   }
 
   // Combine flags from the parent and our own bag.
-  new_bag->type_spec_flags = entry.type_flags | parent_bag->type_spec_flags;
+  new_bag->type_spec_flags = entry->type_flags | (*parent_bag)->type_spec_flags;
   new_bag->entry_count = static_cast<uint32_t>(actual_count);
   ResolvedBag* result = new_bag.get();
   cached_bags_[resid] = std::move(new_bag);
@@ -1208,16 +1249,16 @@
   return true;
 }
 
-uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
-                                      const std::string& fallback_type,
-                                      const std::string& fallback_package) const {
+base::expected<uint32_t, NullOrIOError> AssetManager2::GetResourceId(
+    const std::string& resource_name, const std::string& fallback_type,
+    const std::string& fallback_package) const {
   StringPiece package_name, type, entry;
   if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) {
-    return 0u;
+    return base::unexpected(std::nullopt);
   }
 
   if (entry.empty()) {
-    return 0u;
+    return base::unexpected(std::nullopt);
   }
 
   if (package_name.empty()) {
@@ -1230,12 +1271,12 @@
 
   std::u16string type16;
   if (!Utf8ToUtf16(type, &type16)) {
-    return 0u;
+    return base::unexpected(std::nullopt);
   }
 
   std::u16string entry16;
   if (!Utf8ToUtf16(entry, &entry16)) {
-    return 0u;
+    return base::unexpected(std::nullopt);
   }
 
   const StringPiece16 kAttr16 = u"attr";
@@ -1249,20 +1290,24 @@
         break;
       }
 
-      uint32_t resid = package->FindEntryByName(type16, entry16);
-      if (resid == 0u && kAttr16 == type16) {
+      base::expected<uint32_t, NullOrIOError> resid = package->FindEntryByName(type16, entry16);
+      if (UNLIKELY(IsIOError(resid))) {
+         return base::unexpected(resid.error());
+       }
+
+      if (!resid.has_value() && kAttr16 == type16) {
         // Private attributes in libraries (such as the framework) are sometimes encoded
         // under the type '^attr-private' in order to leave the ID space of public 'attr'
         // free for future additions. Check '^attr-private' for the same name.
         resid = package->FindEntryByName(kAttrPrivate16, entry16);
       }
 
-      if (resid != 0u) {
-        return fix_package_id(resid, package_group.dynamic_ref_table->mAssignedPackageId);
+      if (resid.has_value()) {
+        return fix_package_id(*resid, package_group.dynamic_ref_table->mAssignedPackageId);
       }
     }
   }
-  return 0u;
+  return base::unexpected(std::nullopt);
 }
 
 void AssetManager2::RebuildFilterList(bool filter_incompatible_configs) {
@@ -1282,8 +1327,7 @@
           ResTable_config this_config;
           this_config.copyFromDtoH((*iter)->config);
           if (!filter_incompatible_configs || this_config.match(configuration_)) {
-            group.configurations.push_back(this_config);
-            group.types.push_back(*iter);
+            group.type_configs.push_back(TypeConfig{*iter, this_config});
           }
         }
       });
@@ -1354,16 +1398,16 @@
   std::array<util::unique_cptr<ThemeType>, kTypeCount> types;
 };
 
-bool Theme::ApplyStyle(uint32_t resid, bool force) {
+base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid, bool force) {
   ATRACE_NAME("Theme::ApplyStyle");
 
-  const ResolvedBag* bag = asset_manager_->GetBag(resid);
-  if (bag == nullptr) {
-    return false;
+  auto bag = asset_manager_->GetBag(resid);
+  if (!bag.has_value()) {
+    return base::unexpected(bag.error());
   }
 
   // Merge the flags from this style.
-  type_spec_flags_ |= bag->type_spec_flags;
+  type_spec_flags_ |= (*bag)->type_spec_flags;
 
   int last_type_idx = -1;
   int last_package_idx = -1;
@@ -1373,14 +1417,14 @@
   // Iterate backwards, because each bag is sorted in ascending key ID order, meaning we will only
   // need to perform one resize per type.
   using reverse_bag_iterator = std::reverse_iterator<const ResolvedBag::Entry*>;
-  const auto bag_iter_end = reverse_bag_iterator(begin(bag));
-  for (auto bag_iter = reverse_bag_iterator(end(bag)); bag_iter != bag_iter_end; ++bag_iter) {
-    const uint32_t attr_resid = bag_iter->key;
+  const auto rbegin = reverse_bag_iterator(begin(*bag));
+  for (auto it = reverse_bag_iterator(end(*bag)); it != rbegin; ++it) {
+    const uint32_t attr_resid = it->key;
 
     // If the resource ID passed in is not a style, the key can be some other identifier that is not
     // a resource ID. We should fail fast instead of operating with strange resource IDs.
     if (!is_valid_resid(attr_resid)) {
-      return false;
+      return base::unexpected(std::nullopt);
     }
 
     // We don't use the 0-based index for the type so that we can avoid doing ID validation
@@ -1428,20 +1472,18 @@
     ThemeEntry& entry = last_type->entries[entry_idx];
     if (force || (entry.value.dataType == Res_value::TYPE_NULL &&
                   entry.value.data != Res_value::DATA_NULL_EMPTY)) {
-      entry.cookie = bag_iter->cookie;
-      entry.type_spec_flags |= bag->type_spec_flags;
-      entry.value = bag_iter->value;
+      entry.cookie = it->cookie;
+      entry.type_spec_flags |= (*bag)->type_spec_flags;
+      entry.value = it->value;
     }
   }
-  return true;
+  return {};
 }
 
-ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value,
-                                    uint32_t* out_flags) const {
+std::optional<AssetManager2::SelectedValue> Theme::GetAttribute(uint32_t resid) const {
+
   int cnt = 20;
-
   uint32_t type_spec_flags = 0u;
-
   do {
     const int package_idx = get_package_id(resid);
     const Package* package = packages_[package_idx].get();
@@ -1461,43 +1503,42 @@
               resid = entry.value.data;
               continue;
             }
-            return kInvalidCookie;
+            return std::nullopt;
           }
 
           // @null is different than @empty.
           if (entry.value.dataType == Res_value::TYPE_NULL &&
               entry.value.data != Res_value::DATA_NULL_EMPTY) {
-            return kInvalidCookie;
+            return std::nullopt;
           }
 
-          *out_value = entry.value;
-          *out_flags = type_spec_flags;
-          return entry.cookie;
+          return AssetManager2::SelectedValue(entry.value.dataType, entry.value.data, entry.cookie,
+                                              type_spec_flags, 0U /* resid */, {} /* config */);
         }
       }
     }
     break;
   } while (true);
-  return kInvalidCookie;
+  return std::nullopt;
 }
 
-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) const {
-  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;
-    }
+base::expected<std::monostate, NullOrIOError> Theme::ResolveAttributeReference(
+      AssetManager2::SelectedValue& value) const {
+  if (value.type != Res_value::TYPE_ATTRIBUTE) {
+    return asset_manager_->ResolveReference(value);
   }
-  return asset_manager_->ResolveReference(cookie, in_out_value, in_out_selected_config,
-                                          in_out_type_spec_flags, out_last_ref);
+
+  std::optional<AssetManager2::SelectedValue> result = GetAttribute(value.data);
+  if (!result.has_value()) {
+    return base::unexpected(std::nullopt);
+  }
+
+  auto resolve_result = asset_manager_->ResolveReference(*result);
+  if (resolve_result.has_value()) {
+    result->flags |= value.flags;
+    value = *result;
+  }
+  return resolve_result;
 }
 
 void Theme::Clear() {
@@ -1507,9 +1548,9 @@
   }
 }
 
-void Theme::SetTo(const Theme& o) {
+base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) {
   if (this == &o) {
-    return;
+    return {};
   }
 
   type_spec_flags_ = o.type_spec_flags_;
@@ -1560,10 +1601,8 @@
 
         // Map the runtime package of the source apk asset to the destination apk asset.
         if (src_asset->GetPath() == dest_asset->GetPath()) {
-          const std::vector<std::unique_ptr<const LoadedPackage>>& src_packages =
-              src_asset->GetLoadedArsc()->GetPackages();
-          const std::vector<std::unique_ptr<const LoadedPackage>>& dest_packages =
-              dest_asset->GetLoadedArsc()->GetPackages();
+          const auto& src_packages = src_asset->GetLoadedArsc()->GetPackages();
+          const auto& dest_packages = dest_asset->GetLoadedArsc()->GetPackages();
 
           SourceToDestinationRuntimePackageMap package_map;
 
@@ -1660,15 +1699,20 @@
           int attribute_dest_package_id = p;
           if (attribute_dest_package_id != 0x01) {
             // Find the cookie of the attribute resource id in the source AssetManager
-            FindEntryResult attribute_entry_result;
-            ApkAssetsCookie attribute_cookie =
+            base::expected<FindEntryResult, NullOrIOError> attribute_entry_result =
                 o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ ,
                                             true /* stop_at_first_match */,
-                                            true /* ignore_configuration */,
-                                            &attribute_entry_result);
+                                            true /* ignore_configuration */);
+            if (UNLIKELY(IsIOError(attribute_entry_result))) {
+              return base::unexpected(GetIOError(attribute_entry_result.error()));
+            }
+            if (!attribute_entry_result.has_value()) {
+              continue;
+            }
 
             // Determine the package id of the attribute in the destination AssetManager.
-            auto attribute_package_map = src_asset_cookie_id_map.find(attribute_cookie);
+            auto attribute_package_map = src_asset_cookie_id_map.find(
+                attribute_entry_result->cookie);
             if (attribute_package_map == src_asset_cookie_id_map.end()) {
               continue;
             }
@@ -1712,6 +1756,7 @@
       }
     }
   }
+  return {};
 }
 
 void Theme::Dump() const {
diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
index e62fb61..71919fd 100644
--- a/libs/androidfw/AttributeResolution.cpp
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -24,9 +24,12 @@
 #include "androidfw/AttributeFinder.h"
 
 constexpr bool kDebugStyles = false;
+#define DEBUG_LOG(...) do { if (kDebugStyles) { ALOGI(__VA_ARGS__); } } while(0)
 
 namespace android {
 
+namespace {
+
 // Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0.
 static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) {
   return cookie != kInvalidCookie ? static_cast<uint32_t>(cookie + 1) : static_cast<uint32_t>(-1);
@@ -61,136 +64,149 @@
   }
 };
 
-bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
-                  uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
-                  size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
-  if (kDebugStyles) {
-    ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme,
-          def_style_attr, def_style_res);
-  }
-
-  AssetManager2* assetmanager = theme->GetAssetManager();
-  ResTable_config config;
-  Res_value value;
-
-  int indices_idx = 0;
-
-  // Load default style from attribute, if specified...
-  uint32_t def_style_flags = 0u;
-  if (def_style_attr != 0) {
-    Res_value value;
-    if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
-      if (value.dataType == Res_value::TYPE_REFERENCE) {
-        def_style_res = value.data;
+base::expected<const ResolvedBag*, NullOrIOError> GetStyleBag(Theme* theme,
+                                                              uint32_t theme_attribute_resid,
+                                                              uint32_t fallback_resid,
+                                                              uint32_t* out_theme_flags) {
+  // Load the style from the attribute if specified.
+  if (theme_attribute_resid != 0U) {
+    std::optional<AssetManager2::SelectedValue> value = theme->GetAttribute(theme_attribute_resid);
+    if (value.has_value()) {
+      *out_theme_flags |= value->flags;
+      auto result = theme->GetAssetManager()->ResolveBag(*value);
+      if (result.has_value() || IsIOError(result)) {
+        return result;
       }
     }
   }
 
-  // Retrieve the default style bag, if requested.
-  const ResolvedBag* default_style_bag = nullptr;
-  if (def_style_res != 0) {
-    default_style_bag = assetmanager->GetBag(def_style_res);
-    if (default_style_bag != nullptr) {
-      def_style_flags |= default_style_bag->type_spec_flags;
+  // Fallback to loading the style from the resource id if specified.
+  if (fallback_resid != 0U) {
+    return theme->GetAssetManager()->GetBag(fallback_resid);
+  }
+
+  return base::unexpected(std::nullopt);
+}
+
+base::expected<const ResolvedBag*, NullOrIOError> GetXmlStyleBag(Theme* theme,
+                                                                 ResXMLParser* xml_parser,
+                                                                 uint32_t* out_theme_flags) {
+  if (xml_parser == nullptr) {
+    return base::unexpected(std::nullopt);
+  }
+
+  // Retrieve the style resource ID associated with the current XML tag's style attribute.
+  Res_value value;
+  const ssize_t idx = xml_parser->indexOfStyle();
+  if (idx < 0 || xml_parser->getAttributeValue(idx, &value) < 0) {
+    return base::unexpected(std::nullopt);
+  }
+
+  if (value.dataType == Res_value::TYPE_ATTRIBUTE) {
+    // Resolve the attribute with out theme.
+    if (std::optional<AssetManager2::SelectedValue> result = theme->GetAttribute(value.data)) {
+      *out_theme_flags |= result->flags;
+      return theme->GetAssetManager()->ResolveBag(*result);
     }
   }
 
-  BagAttributeFinder def_style_attr_finder(default_style_bag);
+  if (value.dataType == Res_value::TYPE_REFERENCE) {
+    return theme->GetAssetManager()->GetBag(value.data);
+  }
+
+  return base::unexpected(std::nullopt);
+}
+
+} // namespace
+
+base::expected<std::monostate, IOError> ResolveAttrs(Theme* theme, uint32_t def_style_attr,
+                                                     uint32_t def_style_res, uint32_t* src_values,
+                                                     size_t src_values_length, uint32_t* attrs,
+                                                     size_t attrs_length, uint32_t* out_values,
+                                                     uint32_t* out_indices) {
+  DEBUG_LOG("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr,
+            def_style_res);
+
+  int indices_idx = 0;
+  const AssetManager2* assetmanager = theme->GetAssetManager();
+
+  // Load default style from attribute or resource id, if specified...
+  uint32_t def_style_theme_flags = 0U;
+  const auto default_style_bag = GetStyleBag(theme, def_style_attr, def_style_res,
+                                             &def_style_theme_flags);
+  if (UNLIKELY(IsIOError(default_style_bag))) {
+    return base::unexpected(GetIOError(default_style_bag.error()));
+  }
+
+  BagAttributeFinder def_style_attr_finder(default_style_bag.value_or(nullptr));
 
   // Now iterate through all of the attributes that the client has requested,
   // filling in each with whatever data we can find.
   for (size_t ii = 0; ii < attrs_length; ii++) {
     const uint32_t cur_ident = attrs[ii];
-
-    if (kDebugStyles) {
-      ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
-    }
-
-    ApkAssetsCookie cookie = kInvalidCookie;
-    uint32_t type_set_flags = 0;
-
-    value.dataType = Res_value::TYPE_NULL;
-    value.data = Res_value::DATA_NULL_UNDEFINED;
-    config.density = 0;
+    DEBUG_LOG("RETRIEVING ATTR 0x%08x...", cur_ident);
 
     // Try to find a value for this attribute...  we prioritize values
     // coming from, first XML attributes, then XML style, then default
     // style, and finally the theme.
 
     // Retrieve the current input value if available.
+    AssetManager2::SelectedValue value{};
     if (src_values_length > 0 && src_values[ii] != 0) {
-      value.dataType = Res_value::TYPE_ATTRIBUTE;
+      value.type = Res_value::TYPE_ATTRIBUTE;
       value.data = src_values[ii];
-      if (kDebugStyles) {
-        ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data);
-      }
+      DEBUG_LOG("-> From values: type=0x%x, data=0x%08x", value.type, value.data);
     } else {
       const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident);
       if (entry != def_style_attr_finder.end()) {
-        cookie = entry->cookie;
-        type_set_flags = def_style_flags;
-        value = entry->value;
-        if (kDebugStyles) {
-          ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
-        }
+        value = AssetManager2::SelectedValue(*default_style_bag, *entry);
+        value.flags |= def_style_theme_flags;
+        DEBUG_LOG("-> From def style: type=0x%x, data=0x%08x", value.type, value.data);
       }
     }
 
-    uint32_t resid = 0;
-    if (value.dataType != Res_value::TYPE_NULL) {
+    if (value.type != Res_value::TYPE_NULL) {
       // Take care of resolving the found resource to its final value.
-      ApkAssetsCookie new_cookie =
-          theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid);
-      if (new_cookie != kInvalidCookie) {
-        cookie = new_cookie;
+      const auto result = theme->ResolveAttributeReference(value);
+      if (UNLIKELY(IsIOError(result))) {
+        return base::unexpected(GetIOError(result.error()));
       }
-      if (kDebugStyles) {
-        ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
-      }
+      DEBUG_LOG("-> Resolved attr: type=0x%x, data=0x%08x", value.type, value.data);
     } else if (value.data != Res_value::DATA_NULL_EMPTY) {
       // If we still don't have a value for this attribute, try to find it in the theme!
-      ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags);
-      if (new_cookie != kInvalidCookie) {
-        if (kDebugStyles) {
-          ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
+      if (auto attr_value = theme->GetAttribute(cur_ident)) {
+        value = *attr_value;
+        DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data);
+
+        const auto result = assetmanager->ResolveReference(value);
+        if (UNLIKELY(IsIOError(result))) {
+          return base::unexpected(GetIOError(result.error()));
         }
-        new_cookie =
-            assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid);
-        if (new_cookie != kInvalidCookie) {
-          cookie = new_cookie;
-        }
-        if (kDebugStyles) {
-          ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
-        }
+        DEBUG_LOG("-> Resolved theme: type=0x%x, data=0x%08x", value.type, value.data);
       }
     }
 
     // Deal with the special @null value -- it turns back to TYPE_NULL.
-    if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
-      if (kDebugStyles) {
-        ALOGI("-> Setting to @null!");
-      }
-      value.dataType = Res_value::TYPE_NULL;
+    if (value.type == Res_value::TYPE_REFERENCE && value.data == 0) {
+      DEBUG_LOG("-> Setting to @null!");
+      value.type = Res_value::TYPE_NULL;
       value.data = Res_value::DATA_NULL_UNDEFINED;
-      cookie = kInvalidCookie;
+      value.cookie = kInvalidCookie;
     }
 
-    if (kDebugStyles) {
-      ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
-    }
+    DEBUG_LOG("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.type, value.data);
 
     // Write the final value back to Java.
-    out_values[STYLE_TYPE] = value.dataType;
+    out_values[STYLE_TYPE] = value.type;
     out_values[STYLE_DATA] = value.data;
-    out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
-    out_values[STYLE_RESOURCE_ID] = resid;
-    out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
-    out_values[STYLE_DENSITY] = config.density;
+    out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie);
+    out_values[STYLE_RESOURCE_ID] = value.resid;
+    out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags;
+    out_values[STYLE_DENSITY] = value.config.density;
 
     if (out_indices != nullptr &&
-        (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) {
-      indices_idx++;
-      out_indices[indices_idx] = ii;
+        (value.type != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) {
+      out_indices[++indices_idx] = ii;
     }
 
     out_values += STYLE_NUM_ENTRIES;
@@ -199,93 +215,46 @@
   if (out_indices != nullptr) {
     out_indices[0] = indices_idx;
   }
-  return true;
+  return {};
 }
 
-void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
-                uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length,
-                uint32_t* out_values, uint32_t* out_indices) {
-  if (kDebugStyles) {
-    ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme,
-          def_style_attr, def_style_resid, xml_parser);
-  }
-
-  AssetManager2* assetmanager = theme->GetAssetManager();
-  ResTable_config config;
-  Res_value value;
+base::expected<std::monostate, IOError> ApplyStyle(Theme* theme, ResXMLParser* xml_parser,
+                                                   uint32_t def_style_attr,
+                                                   uint32_t def_style_resid,
+                                                   const uint32_t* attrs, size_t attrs_length,
+                                                   uint32_t* out_values, uint32_t* out_indices) {
+  DEBUG_LOG("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme,
+            def_style_attr, def_style_resid, xml_parser);
 
   int indices_idx = 0;
+  const AssetManager2* assetmanager = theme->GetAssetManager();
 
   // Load default style from attribute, if specified...
-  uint32_t def_style_flags = 0u;
-  if (def_style_attr != 0) {
-    Res_value value;
-    if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
-      if (value.dataType == Res_value::TYPE_REFERENCE) {
-        def_style_resid = value.data;
-      }
-    }
+  uint32_t def_style_theme_flags = 0U;
+  const auto default_style_bag = GetStyleBag(theme, def_style_attr, def_style_resid,
+                                             &def_style_theme_flags);
+  if (IsIOError(default_style_bag)) {
+    return base::unexpected(GetIOError(default_style_bag.error()));
   }
 
   // Retrieve the style resource ID associated with the current XML tag's style attribute.
-  uint32_t style_resid = 0u;
-  uint32_t style_flags = 0u;
-  if (xml_parser != nullptr) {
-    ssize_t idx = xml_parser->indexOfStyle();
-    if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) {
-      if (value.dataType == value.TYPE_ATTRIBUTE) {
-        // Resolve the attribute with out theme.
-        if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) {
-          value.dataType = Res_value::TYPE_NULL;
-        }
-      }
-
-      if (value.dataType == value.TYPE_REFERENCE) {
-        style_resid = value.data;
-      }
-    }
+  uint32_t xml_style_theme_flags = 0U;
+  const auto xml_style_bag = GetXmlStyleBag(theme, xml_parser, &def_style_theme_flags);
+  if (IsIOError(xml_style_bag)) {
+    return base::unexpected(GetIOError(xml_style_bag.error()));
   }
 
-  // Retrieve the default style bag, if requested.
-  const ResolvedBag* default_style_bag = nullptr;
-  if (def_style_resid != 0) {
-    default_style_bag = assetmanager->GetBag(def_style_resid);
-    if (default_style_bag != nullptr) {
-      def_style_flags |= default_style_bag->type_spec_flags;
-    }
-  }
-
-  BagAttributeFinder def_style_attr_finder(default_style_bag);
-
-  // Retrieve the style class bag, if requested.
-  const ResolvedBag* xml_style_bag = nullptr;
-  if (style_resid != 0) {
-    xml_style_bag = assetmanager->GetBag(style_resid);
-    if (xml_style_bag != nullptr) {
-      style_flags |= xml_style_bag->type_spec_flags;
-    }
-  }
-
-  BagAttributeFinder xml_style_attr_finder(xml_style_bag);
-
-  // Retrieve the XML attributes, if requested.
+  BagAttributeFinder def_style_attr_finder(default_style_bag.value_or(nullptr));
+  BagAttributeFinder xml_style_attr_finder(xml_style_bag.value_or(nullptr));
   XmlAttributeFinder xml_attr_finder(xml_parser);
 
   // Now iterate through all of the attributes that the client has requested,
   // filling in each with whatever data we can find.
   for (size_t ii = 0; ii < attrs_length; ii++) {
     const uint32_t cur_ident = attrs[ii];
+    DEBUG_LOG("RETRIEVING ATTR 0x%08x...", cur_ident);
 
-    if (kDebugStyles) {
-      ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
-    }
-
-    ApkAssetsCookie cookie = kInvalidCookie;
-    uint32_t type_set_flags = 0u;
-
-    value.dataType = Res_value::TYPE_NULL;
-    value.data = Res_value::DATA_NULL_UNDEFINED;
-    config.density = 0;
+    AssetManager2::SelectedValue value{};
     uint32_t value_source_resid = 0;
 
     // Try to find a value for this attribute...  we prioritize values
@@ -296,178 +265,152 @@
     const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident);
     if (xml_attr_idx != xml_attr_finder.end()) {
       // We found the attribute we were looking for.
-      xml_parser->getAttributeValue(xml_attr_idx, &value);
-      if (kDebugStyles) {
-        ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, value.data);
-      }
+      Res_value attribute_value;
+      xml_parser->getAttributeValue(xml_attr_idx, &attribute_value);
+      value.type = attribute_value.dataType;
+      value.data = attribute_value.data;
       value_source_resid = xml_parser->getSourceResourceId();
+      DEBUG_LOG("-> From XML: type=0x%x, data=0x%08x", value.type, value.data);
     }
 
-    if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
+    if (value.type == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
       // Walk through the style class values looking for the requested attribute.
       const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident);
       if (entry != xml_style_attr_finder.end()) {
-        // We found the attribute we were looking for.
-        cookie = entry->cookie;
-        type_set_flags = style_flags;
-        value = entry->value;
+        value = AssetManager2::SelectedValue(*xml_style_bag, *entry);
+        value.flags |= xml_style_theme_flags;
         value_source_resid = entry->style;
-        if (kDebugStyles) {
-          ALOGI("-> From style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data,
-              entry->style);
-        }
+        DEBUG_LOG("-> From style: type=0x%x, data=0x%08x, style=0x%08x", value.type, value.data,
+                  value_source_resid);
       }
     }
 
-    if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
+    if (value.type == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
       // Walk through the default style values looking for the requested attribute.
       const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident);
       if (entry != def_style_attr_finder.end()) {
-        // We found the attribute we were looking for.
-        cookie = entry->cookie;
-        type_set_flags = def_style_flags;
-        value = entry->value;
-        if (kDebugStyles) {
-          ALOGI("-> From def style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data,
-              entry->style);
-        }
+        value = AssetManager2::SelectedValue(*default_style_bag, *entry);
+        value.flags |= def_style_theme_flags;
         value_source_resid = entry->style;
+        DEBUG_LOG("-> From def style: type=0x%x, data=0x%08x, style=0x%08x", value.type, value.data,
+                  entry->style);
       }
     }
 
-    uint32_t resid = 0u;
-    if (value.dataType != Res_value::TYPE_NULL) {
+    if (value.type != Res_value::TYPE_NULL) {
       // Take care of resolving the found resource to its final value.
-      ApkAssetsCookie new_cookie =
-          theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid);
-      if (new_cookie != kInvalidCookie) {
-        cookie = new_cookie;
+      auto result = theme->ResolveAttributeReference(value);
+      if (UNLIKELY(IsIOError(result))) {
+        return base::unexpected(GetIOError(result.error()));
       }
-
-      if (kDebugStyles) {
-        ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
-      }
+      DEBUG_LOG("-> Resolved attr: type=0x%x, data=0x%08x", value.type, value.data);
     } else if (value.data != Res_value::DATA_NULL_EMPTY) {
       // If we still don't have a value for this attribute, try to find it in the theme!
-      ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags);
-      // TODO: set value_source_resid for the style in the theme that was used.
-      if (new_cookie != kInvalidCookie) {
-        if (kDebugStyles) {
-          ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
-        }
-        new_cookie =
-            assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid);
-        if (new_cookie != kInvalidCookie) {
-          cookie = new_cookie;
-        }
+      if (auto attr_value = theme->GetAttribute(cur_ident)) {
+        value = *attr_value;
+        DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data);
 
-        if (kDebugStyles) {
-          ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
+        auto result = assetmanager->ResolveReference(value);
+        if (UNLIKELY(IsIOError(result))) {
+          return base::unexpected(GetIOError(result.error()));
         }
+        DEBUG_LOG("-> Resolved theme: type=0x%x, data=0x%08x", value.type, value.data);
+        // TODO: set value_source_resid for the style in the theme that was used.
       }
     }
 
     // Deal with the special @null value -- it turns back to TYPE_NULL.
-    if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
-      if (kDebugStyles) {
-        ALOGI("-> Setting to @null!");
-      }
-      value.dataType = Res_value::TYPE_NULL;
+    if (value.type == Res_value::TYPE_REFERENCE && value.data == 0U) {
+      DEBUG_LOG("-> Setting to @null!");
+      value.type = Res_value::TYPE_NULL;
       value.data = Res_value::DATA_NULL_UNDEFINED;
-      cookie = kInvalidCookie;
+      value.cookie = kInvalidCookie;
     }
 
-    if (kDebugStyles) {
-      ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
-    }
+    DEBUG_LOG("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.type, value.data);
 
     // Write the final value back to Java.
-    out_values[STYLE_TYPE] = value.dataType;
+    out_values[STYLE_TYPE] = value.type;
     out_values[STYLE_DATA] = value.data;
-    out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
-    out_values[STYLE_RESOURCE_ID] = resid;
-    out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
-    out_values[STYLE_DENSITY] = config.density;
+    out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie);
+    out_values[STYLE_RESOURCE_ID] = value.resid;
+    out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags;
+    out_values[STYLE_DENSITY] = value.config.density;
     out_values[STYLE_SOURCE_RESOURCE_ID] = value_source_resid;
 
-    if (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) {
-      indices_idx++;
-
+    if (value.type != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) {
       // out_indices must NOT be nullptr.
-      out_indices[indices_idx] = ii;
+      out_indices[++indices_idx] = ii;
     }
     out_values += STYLE_NUM_ENTRIES;
   }
 
   // out_indices must NOT be nullptr.
   out_indices[0] = indices_idx;
+  return {};
 }
 
-bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs,
-                        size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
-  ResTable_config config;
-  Res_value value;
-
+base::expected<std::monostate, IOError> RetrieveAttributes(AssetManager2* assetmanager,
+                                                           ResXMLParser* xml_parser,
+                                                           uint32_t* attrs,
+                                                           size_t attrs_length,
+                                                           uint32_t* out_values,
+                                                           uint32_t* out_indices) {
   int indices_idx = 0;
 
   // Retrieve the XML attributes, if requested.
-  const size_t xml_attr_count = xml_parser->getAttributeCount();
   size_t ix = 0;
+  const size_t xml_attr_count = xml_parser->getAttributeCount();
   uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix);
 
   // Now iterate through all of the attributes that the client has requested,
   // filling in each with whatever data we can find.
   for (size_t ii = 0; ii < attrs_length; ii++) {
     const uint32_t cur_ident = attrs[ii];
-    ApkAssetsCookie cookie = kInvalidCookie;
-    uint32_t type_set_flags = 0u;
-
-    value.dataType = Res_value::TYPE_NULL;
-    value.data = Res_value::DATA_NULL_UNDEFINED;
-    config.density = 0;
+     AssetManager2::SelectedValue value{};
 
     // Try to find a value for this attribute...
     // Skip through XML attributes until the end or the next possible match.
     while (ix < xml_attr_count && cur_ident > cur_xml_attr) {
-      ix++;
-      cur_xml_attr = xml_parser->getAttributeNameResID(ix);
-    }
-    // Retrieve the current XML attribute if it matches, and step to next.
-    if (ix < xml_attr_count && cur_ident == cur_xml_attr) {
-      xml_parser->getAttributeValue(ix, &value);
-      ix++;
-      cur_xml_attr = xml_parser->getAttributeNameResID(ix);
+      cur_xml_attr = xml_parser->getAttributeNameResID(++ix);
     }
 
-    uint32_t resid = 0u;
-    if (value.dataType != Res_value::TYPE_NULL) {
+    // Retrieve the current XML attribute if it matches, and step to next.
+    if (ix < xml_attr_count && cur_ident == cur_xml_attr) {
+      Res_value attribute_value;
+      xml_parser->getAttributeValue(ix, &attribute_value);
+      value.type = attribute_value.dataType;
+      value.data = attribute_value.data;
+      cur_xml_attr = xml_parser->getAttributeNameResID(++ix);
+    }
+
+    if (value.type != Res_value::TYPE_NULL) {
       // Take care of resolving the found resource to its final value.
-      ApkAssetsCookie new_cookie =
-          assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid);
-      if (new_cookie != kInvalidCookie) {
-        cookie = new_cookie;
+      auto result = assetmanager->ResolveReference(value);
+      if (UNLIKELY(IsIOError(result))) {
+        return base::unexpected(GetIOError(result.error()));
       }
     }
 
     // Deal with the special @null value -- it turns back to TYPE_NULL.
-    if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
-      value.dataType = Res_value::TYPE_NULL;
+    if (value.type == Res_value::TYPE_REFERENCE && value.data == 0U) {
+      value.type = Res_value::TYPE_NULL;
       value.data = Res_value::DATA_NULL_UNDEFINED;
-      cookie = kInvalidCookie;
+      value.cookie = kInvalidCookie;
     }
 
     // Write the final value back to Java.
-    out_values[STYLE_TYPE] = value.dataType;
+    out_values[STYLE_TYPE] = value.type;
     out_values[STYLE_DATA] = value.data;
-    out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
-    out_values[STYLE_RESOURCE_ID] = resid;
-    out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
-    out_values[STYLE_DENSITY] = config.density;
+    out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie);
+    out_values[STYLE_RESOURCE_ID] = value.resid;
+    out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags;
+    out_values[STYLE_DENSITY] = value.config.density;
 
     if (out_indices != nullptr &&
-        (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) {
-      indices_idx++;
-      out_indices[indices_idx] = ii;
+        (value.type != Res_value::TYPE_NULL ||
+         value.data == Res_value::DATA_NULL_EMPTY)) {
+      out_indices[++indices_idx] = ii;
     }
 
     out_values += STYLE_NUM_ENTRIES;
@@ -476,7 +419,7 @@
   if (out_indices != nullptr) {
     out_indices[0] = indices_idx;
   }
-  return true;
+  return {};
 }
 
 }  // namespace android
diff --git a/libs/androidfw/ChunkIterator.cpp b/libs/androidfw/ChunkIterator.cpp
index 8fc3219..25c8aa6 100644
--- a/libs/androidfw/ChunkIterator.cpp
+++ b/libs/androidfw/ChunkIterator.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "androidfw/Chunk.h"
+#include "androidfw/Util.h"
 
 #include "android-base/logging.h"
 
@@ -23,11 +24,11 @@
 Chunk ChunkIterator::Next() {
   CHECK(len_ != 0) << "called Next() after last chunk";
 
-  const ResChunk_header* this_chunk = next_chunk_;
+  const incfs::map_ptr<ResChunk_header> this_chunk = next_chunk_;
+  CHECK((bool) this_chunk) << "Next() called without verifying next chunk";
 
   // We've already checked the values of this_chunk, so safely increment.
-  next_chunk_ = reinterpret_cast<const ResChunk_header*>(
-      reinterpret_cast<const uint8_t*>(this_chunk) + dtohl(this_chunk->size));
+  next_chunk_ = this_chunk.offset(dtohl(this_chunk->size)).convert<ResChunk_header>();
   len_ -= dtohl(this_chunk->size);
 
   if (len_ != 0) {
@@ -36,7 +37,7 @@
       VerifyNextChunk();
     }
   }
-  return Chunk(this_chunk);
+  return Chunk(this_chunk.verified());
 }
 
 // TODO(b/111401637) remove this and have full resource file verification
@@ -47,6 +48,13 @@
     last_error_was_fatal_ = false;
     return false;
   }
+
+  if (!next_chunk_) {
+    last_error_ = "failed to read chunk from data";
+    last_error_was_fatal_ = false;
+    return false;
+  }
+
   const size_t size = dtohl(next_chunk_->size);
   if (size > len_) {
     last_error_ = "chunk size is bigger than given data";
@@ -58,12 +66,10 @@
 
 // Returns false if there was an error.
 bool ChunkIterator::VerifyNextChunk() {
-  const uintptr_t header_start = reinterpret_cast<uintptr_t>(next_chunk_);
-
   // This data must be 4-byte aligned, since we directly
   // access 32-bit words, which must be aligned on
   // certain architectures.
-  if (header_start & 0x03) {
+  if (!util::IsFourByteAligned(next_chunk_)) {
     last_error_ = "header not aligned on 4-byte boundary";
     return false;
   }
@@ -73,6 +79,11 @@
     return false;
   }
 
+  if (!next_chunk_) {
+    last_error_ = "failed to read chunk from data";
+    return false;
+  }
+
   const size_t header_size = dtohs(next_chunk_->headerSize);
   const size_t size = dtohl(next_chunk_->size);
   if (header_size < sizeof(ResChunk_header)) {
@@ -90,7 +101,7 @@
     return false;
   }
 
-  if ((size | header_size) & 0x03) {
+  if ((size | header_size) & 0x03U) {
     last_error_ = "header sizes are not aligned on 4-byte boundary";
     return false;
   }
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 4e03ce5..a613095 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -52,22 +52,22 @@
   uninit();
 }
 
-const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const {
+base::expected<StringPiece16, NullOrIOError> OverlayStringPool::stringAt(size_t idx) const {
   const size_t offset = dtohl(data_header_->string_pool_index_offset);
   if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
-    return idmap_string_pool_->stringAt(idx - offset, outLen);
+    return idmap_string_pool_->stringAt(idx - offset);
   }
 
-  return ResStringPool::stringAt(idx, outLen);
+  return ResStringPool::stringAt(idx);
 }
 
-const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const {
+base::expected<StringPiece, NullOrIOError> OverlayStringPool::string8At(size_t idx) const {
   const size_t offset = dtohl(data_header_->string_pool_index_offset);
   if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
-    return idmap_string_pool_->string8At(idx - offset, outLen);
+    return idmap_string_pool_->string8At(idx - offset);
   }
 
-  return ResStringPool::string8At(idx, outLen);
+  return ResStringPool::string8At(idx);
 }
 
 size_t OverlayStringPool::size() const {
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 70bb441..2fc3b05 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -38,7 +38,7 @@
 #include "androidfw/ResourceUtils.h"
 #include "androidfw/Util.h"
 
-using ::android::base::StringPrintf;
+using android::base::StringPrintf;
 
 namespace android {
 
@@ -51,17 +51,17 @@
 // the Type structs.
 class TypeSpecPtrBuilder {
  public:
-  explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header)
+  explicit TypeSpecPtrBuilder(incfs::verified_map_ptr<ResTable_typeSpec> header)
       : header_(header) {
   }
 
-  void AddType(const ResTable_type* type) {
+  void AddType(incfs::verified_map_ptr<ResTable_type> type) {
     types_.push_back(type);
   }
 
   TypeSpecPtr Build() {
     // Check for overflow.
-    using ElementType = const ResTable_type*;
+    using ElementType = incfs::verified_map_ptr<ResTable_type>;
     if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(ElementType) <
         types_.size()) {
       return {};
@@ -77,8 +77,8 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder);
 
-  const ResTable_typeSpec* header_;
-  std::vector<const ResTable_type*> types_;
+  incfs::verified_map_ptr<ResTable_typeSpec> header_;
+  std::vector<incfs::verified_map_ptr<ResTable_type>> types_;
 };
 
 }  // namespace
@@ -88,7 +88,7 @@
 
 // Precondition: The header passed in has already been verified, so reading any fields and trusting
 // the ResChunk_header is safe.
-static bool VerifyResTableType(const ResTable_type* header) {
+static bool VerifyResTableType(incfs::map_ptr<ResTable_type> header) {
   if (header->id == 0) {
     LOG(ERROR) << "RES_TABLE_TYPE_TYPE has invalid ID 0.";
     return false;
@@ -115,89 +115,99 @@
     return false;
   }
 
-  if (entries_offset & 0x03) {
+  if (entries_offset & 0x03U) {
     LOG(ERROR) << "RES_TABLE_TYPE_TYPE entries start at unaligned address.";
     return false;
   }
   return true;
 }
 
-static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) {
+static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry(
+    incfs::verified_map_ptr<ResTable_type> type, uint32_t entry_offset) {
   // Check that the offset is aligned.
-  if (entry_offset & 0x03) {
+  if (UNLIKELY(entry_offset & 0x03U)) {
     LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned.";
-    return false;
+    return base::unexpected(std::nullopt);
   }
 
   // Check that the offset doesn't overflow.
-  if (entry_offset > std::numeric_limits<uint32_t>::max() - dtohl(type->entriesStart)) {
+  if (UNLIKELY(entry_offset > std::numeric_limits<uint32_t>::max() - dtohl(type->entriesStart))) {
     // Overflow in offset.
     LOG(ERROR) << "Entry at offset " << entry_offset << " is too large.";
-    return false;
+    return base::unexpected(std::nullopt);
   }
 
   const size_t chunk_size = dtohl(type->header.size);
 
   entry_offset += dtohl(type->entriesStart);
-  if (entry_offset > chunk_size - sizeof(ResTable_entry)) {
+  if (UNLIKELY(entry_offset > chunk_size - sizeof(ResTable_entry))) {
     LOG(ERROR) << "Entry at offset " << entry_offset
                << " is too large. No room for ResTable_entry.";
-    return false;
+    return base::unexpected(std::nullopt);
   }
 
-  const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
-      reinterpret_cast<const uint8_t*>(type) + entry_offset);
+  auto entry = type.offset(entry_offset).convert<ResTable_entry>();
+  if (UNLIKELY(!entry)) {
+    return base::unexpected(IOError::PAGES_MISSING);
+  }
 
   const size_t entry_size = dtohs(entry->size);
-  if (entry_size < sizeof(*entry)) {
+  if (UNLIKELY(entry_size < sizeof(entry.value()))) {
     LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
                << " is too small.";
-    return false;
+    return base::unexpected(std::nullopt);
   }
 
-  if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) {
+  if (UNLIKELY(entry_size > chunk_size || entry_offset > chunk_size - entry_size)) {
     LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
                << " is too large.";
-    return false;
+    return base::unexpected(std::nullopt);
   }
 
   if (entry_size < sizeof(ResTable_map_entry)) {
     // There needs to be room for one Res_value struct.
-    if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) {
+    if (UNLIKELY(entry_offset + entry_size > chunk_size - sizeof(Res_value))) {
       LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset
                  << " for type " << (int)type->id << ".";
-      return false;
+      return base::unexpected(std::nullopt);
     }
 
-    const Res_value* value =
-        reinterpret_cast<const Res_value*>(reinterpret_cast<const uint8_t*>(entry) + entry_size);
+    auto value = entry.offset(entry_size).convert<Res_value>();
+    if (UNLIKELY(!value)) {
+       return base::unexpected(IOError::PAGES_MISSING);
+    }
+
     const size_t value_size = dtohs(value->size);
-    if (value_size < sizeof(Res_value)) {
+    if (UNLIKELY(value_size < sizeof(Res_value))) {
       LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small.";
-      return false;
+      return base::unexpected(std::nullopt);
     }
 
-    if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) {
+    if (UNLIKELY(value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size)) {
       LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset
                  << " is too large.";
-      return false;
+      return base::unexpected(std::nullopt);
     }
   } else {
-    const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry);
+    auto map = entry.convert<ResTable_map_entry>();
+    if (UNLIKELY(!map)) {
+      return base::unexpected(IOError::PAGES_MISSING);
+    }
+
     const size_t map_entry_count = dtohl(map->count);
     size_t map_entries_start = entry_offset + entry_size;
-    if (map_entries_start & 0x03) {
+    if (UNLIKELY(map_entries_start & 0x03U)) {
       LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset.";
-      return false;
+      return base::unexpected(std::nullopt);
     }
 
     // Each entry is sizeof(ResTable_map) big.
-    if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) {
+    if (UNLIKELY(map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map)))) {
       LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << ".";
-      return false;
+      return base::unexpected(std::nullopt);
     }
   }
-  return true;
+  return {};
 }
 
 LoadedPackage::iterator::iterator(const LoadedPackage* lp, size_t ti, size_t ei)
@@ -233,99 +243,125 @@
           entryIndex_);
 }
 
-const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk,
-                                              uint16_t entry_index) {
-  uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index);
-  if (entry_offset == ResTable_type::NO_ENTRY) {
-    return nullptr;
+base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntry(
+    incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index) {
+  base::expected<uint32_t, NullOrIOError> entry_offset = GetEntryOffset(type_chunk, entry_index);
+  if (UNLIKELY(!entry_offset.has_value())) {
+    return base::unexpected(entry_offset.error());
   }
-  return GetEntryFromOffset(type_chunk, entry_offset);
+  return GetEntryFromOffset(type_chunk, entry_offset.value());
 }
 
-uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) {
+base::expected<uint32_t, NullOrIOError> LoadedPackage::GetEntryOffset(
+    incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index) {
   // The configuration matches and is better than the previous selection.
   // Find the entry value if it exists for this configuration.
   const size_t entry_count = dtohl(type_chunk->entryCount);
   const size_t offsets_offset = dtohs(type_chunk->header.headerSize);
 
   // Check if there is the desired entry in this type.
-
   if (type_chunk->flags & ResTable_type::FLAG_SPARSE) {
     // This is encoded as a sparse map, so perform a binary search.
-    const ResTable_sparseTypeEntry* sparse_indices =
-        reinterpret_cast<const ResTable_sparseTypeEntry*>(
-            reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
-    const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count;
-    const ResTable_sparseTypeEntry* result =
-        std::lower_bound(sparse_indices, sparse_indices_end, entry_index,
-                         [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) {
-                           return dtohs(entry.idx) < entry_idx;
-                         });
+    bool error = false;
+    auto sparse_indices = type_chunk.offset(offsets_offset)
+                                    .convert<ResTable_sparseTypeEntry>().iterator();
+    auto sparse_indices_end = sparse_indices + entry_count;
+    auto result = std::lower_bound(sparse_indices, sparse_indices_end, entry_index,
+                                   [&error](const incfs::map_ptr<ResTable_sparseTypeEntry>& entry,
+                                            uint16_t entry_idx) {
+      if (UNLIKELY(!entry)) {
+        return error = true;
+      }
+      return dtohs(entry->idx) < entry_idx;
+    });
 
-    if (result == sparse_indices_end || dtohs(result->idx) != entry_index) {
+    if (result == sparse_indices_end) {
       // No entry found.
-      return ResTable_type::NO_ENTRY;
+      return base::unexpected(std::nullopt);
+    }
+
+    const incfs::verified_map_ptr<ResTable_sparseTypeEntry> entry = (*result).verified();
+    if (dtohs(entry->idx) != entry_index) {
+      if (error) {
+        return base::unexpected(IOError::PAGES_MISSING);
+      }
+      return base::unexpected(std::nullopt);
     }
 
     // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as
     // the real offset divided by 4.
-    return uint32_t{dtohs(result->offset)} * 4u;
+    return uint32_t{dtohs(entry->offset)} * 4u;
   }
 
   // This type is encoded as a dense array.
   if (entry_index >= entry_count) {
     // This entry cannot be here.
-    return ResTable_type::NO_ENTRY;
+    return base::unexpected(std::nullopt);
   }
 
-  const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
-      reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
-  return dtohl(entry_offsets[entry_index]);
-}
-
-const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk,
-                                                        uint32_t offset) {
-  if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) {
-    return nullptr;
+  const auto entry_offset_ptr = type_chunk.offset(offsets_offset).convert<uint32_t>() + entry_index;
+  if (UNLIKELY(!entry_offset_ptr)) {
+    return base::unexpected(IOError::PAGES_MISSING);
   }
-  return reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type_chunk) +
-                                                 offset + dtohl(type_chunk->entriesStart));
+
+  const uint32_t value = dtohl(entry_offset_ptr.value());
+  if (value == ResTable_type::NO_ENTRY) {
+    return base::unexpected(std::nullopt);
+  }
+
+  return value;
 }
 
-void LoadedPackage::CollectConfigurations(bool exclude_mipmap,
-                                          std::set<ResTable_config>* out_configs) const {
-  const static std::u16string kMipMap = u"mipmap";
+base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntryFromOffset(
+    incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset) {
+  auto valid = VerifyResTableEntry(type_chunk, offset);
+  if (UNLIKELY(!valid.has_value())) {
+    return base::unexpected(valid.error());
+  }
+  return type_chunk.offset(offset + dtohl(type_chunk->entriesStart)).convert<ResTable_entry>();
+}
+
+base::expected<std::monostate, IOError> LoadedPackage::CollectConfigurations(
+    bool exclude_mipmap, std::set<ResTable_config>* out_configs) const {
   const size_t type_count = type_specs_.size();
   for (size_t i = 0; i < type_count; i++) {
     const TypeSpecPtr& type_spec = type_specs_[i];
-    if (type_spec != nullptr) {
-      if (exclude_mipmap) {
-        const int type_idx = type_spec->type_spec->id - 1;
-        size_t type_name_len;
-        const char16_t* type_name16 = type_string_pool_.stringAt(type_idx, &type_name_len);
-        if (type_name16 != nullptr) {
-          if (kMipMap.compare(0, std::u16string::npos, type_name16, type_name_len) == 0) {
-            // This is a mipmap type, skip collection.
-            continue;
-          }
-        }
-        const char* type_name = type_string_pool_.string8At(type_idx, &type_name_len);
-        if (type_name != nullptr) {
-          if (strncmp(type_name, "mipmap", type_name_len) == 0) {
-            // This is a mipmap type, skip collection.
-            continue;
-          }
+    if (type_spec == nullptr) {
+      continue;
+    }
+    if (exclude_mipmap) {
+      const int type_idx = type_spec->type_spec->id - 1;
+      const auto type_name16 = type_string_pool_.stringAt(type_idx);
+      if (UNLIKELY(IsIOError(type_name16))) {
+        return base::unexpected(GetIOError(type_name16.error()));
+      }
+      if (type_name16.has_value()) {
+        if (strncmp16(type_name16->data(), u"mipmap", type_name16->size()) == 0) {
+          // This is a mipmap type, skip collection.
+          continue;
         }
       }
 
-      const auto iter_end = type_spec->types + type_spec->type_count;
-      for (auto iter = type_spec->types; iter != iter_end; ++iter) {
-        ResTable_config config;
-        config.copyFromDtoH((*iter)->config);
-        out_configs->insert(config);
+      const auto type_name = type_string_pool_.string8At(type_idx);
+      if (UNLIKELY(IsIOError(type_name))) {
+        return base::unexpected(GetIOError(type_name.error()));
+      }
+      if (type_name.has_value()) {
+        if (strncmp(type_name->data(), "mipmap", type_name->size()) == 0) {
+          // This is a mipmap type, skip collection.
+          continue;
+        }
       }
     }
+
+    const auto iter_end = type_spec->types + type_spec->type_count;
+    for (auto iter = type_spec->types; iter != iter_end; ++iter) {
+      ResTable_config config;
+      config.copyFromDtoH((*iter)->config);
+      out_configs->insert(config);
+    }
   }
+  return {};
 }
 
 void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const {
@@ -348,43 +384,53 @@
   }
 }
 
-uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name,
-                                        const std::u16string& entry_name) const {
-  ssize_t type_idx = type_string_pool_.indexOfString(type_name.data(), type_name.size());
-  if (type_idx < 0) {
-    return 0u;
+base::expected<uint32_t, NullOrIOError> LoadedPackage::FindEntryByName(
+    const std::u16string& type_name, const std::u16string& entry_name) const {
+  const base::expected<size_t, NullOrIOError> type_idx = type_string_pool_.indexOfString(
+      type_name.data(), type_name.size());
+  if (!type_idx.has_value()) {
+    return base::unexpected(type_idx.error());
   }
 
-  ssize_t key_idx = key_string_pool_.indexOfString(entry_name.data(), entry_name.size());
-  if (key_idx < 0) {
-    return 0u;
+  const base::expected<size_t, NullOrIOError> key_idx = key_string_pool_.indexOfString(
+      entry_name.data(), entry_name.size());
+  if (!key_idx.has_value()) {
+    return base::unexpected(key_idx.error());
   }
 
-  const TypeSpec* type_spec = type_specs_[type_idx].get();
+  const TypeSpec* type_spec = type_specs_[*type_idx].get();
   if (type_spec == nullptr) {
-    return 0u;
+    return base::unexpected(std::nullopt);
   }
 
   const auto iter_end = type_spec->types + type_spec->type_count;
   for (auto iter = type_spec->types; iter != iter_end; ++iter) {
-    const ResTable_type* type = *iter;
+    const incfs::verified_map_ptr<ResTable_type>& type = *iter;
+
     size_t entry_count = dtohl(type->entryCount);
     for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) {
-      const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
-          reinterpret_cast<const uint8_t*>(type) + dtohs(type->header.headerSize));
-      const uint32_t offset = dtohl(entry_offsets[entry_idx]);
+      auto entry_offset_ptr = type.offset(dtohs(type->header.headerSize)).convert<uint32_t>() +
+          entry_idx;
+      if (!entry_offset_ptr) {
+        return base::unexpected(IOError::PAGES_MISSING);
+      }
+
+      auto offset = dtohl(entry_offset_ptr.value());
       if (offset != ResTable_type::NO_ENTRY) {
-        const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
-            reinterpret_cast<const uint8_t*>(type) + dtohl(type->entriesStart) + offset);
-        if (dtohl(entry->key.index) == static_cast<uint32_t>(key_idx)) {
+        auto entry = type.offset(dtohl(type->entriesStart) + offset).convert<ResTable_entry>();
+        if (!entry) {
+          return base::unexpected(IOError::PAGES_MISSING);
+        }
+
+        if (dtohl(entry->key.index) == static_cast<uint32_t>(*key_idx)) {
           // The package ID will be overridden by the caller (due to runtime assignment of package
           // IDs for shared libraries).
-          return make_resid(0x00, type_idx + type_id_offset_ + 1, entry_idx);
+          return make_resid(0x00, *type_idx + type_id_offset_ + 1, entry_idx);
         }
       }
     }
   }
-  return 0u;
+  return base::unexpected(std::nullopt);
 }
 
 const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const {
@@ -405,8 +451,8 @@
   // was added.
   constexpr size_t kMinPackageSize =
       sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset);
-  const ResTable_package* header = chunk.header<ResTable_package, kMinPackageSize>();
-  if (header == nullptr) {
+  const incfs::map_ptr<ResTable_package> header = chunk.header<ResTable_package, kMinPackageSize>();
+  if (!header) {
     LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE too small.";
     return {};
   }
@@ -453,10 +499,13 @@
     const Chunk child_chunk = iter.Next();
     switch (child_chunk.type()) {
       case RES_STRING_POOL_TYPE: {
-        const uintptr_t pool_address =
-            reinterpret_cast<uintptr_t>(child_chunk.header<ResChunk_header>());
-        const uintptr_t header_address = reinterpret_cast<uintptr_t>(header);
-        if (pool_address == header_address + dtohl(header->typeStrings)) {
+        const auto pool_address = child_chunk.header<ResChunk_header>();
+        if (!pool_address) {
+          LOG(ERROR) << "RES_STRING_POOL_TYPE is incomplete due to incremental installation.";
+          return {};
+        }
+
+        if (pool_address == header.offset(dtohl(header->typeStrings)).convert<ResChunk_header>()) {
           // This string pool is the type string pool.
           status_t err = loaded_package->type_string_pool_.setTo(
               child_chunk.header<ResStringPool_header>(), child_chunk.size());
@@ -464,7 +513,8 @@
             LOG(ERROR) << "RES_STRING_POOL_TYPE for types corrupt.";
             return {};
           }
-        } else if (pool_address == header_address + dtohl(header->keyStrings)) {
+        } else if (pool_address == header.offset(dtohl(header->keyStrings))
+                                         .convert<ResChunk_header>()) {
           // This string pool is the key string pool.
           status_t err = loaded_package->key_string_pool_.setTo(
               child_chunk.header<ResStringPool_header>(), child_chunk.size());
@@ -478,8 +528,8 @@
       } break;
 
       case RES_TABLE_TYPE_SPEC_TYPE: {
-        const ResTable_typeSpec* type_spec = child_chunk.header<ResTable_typeSpec>();
-        if (type_spec == nullptr) {
+        const auto type_spec = child_chunk.header<ResTable_typeSpec>();
+        if (!type_spec) {
           LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small.";
           return {};
         }
@@ -514,7 +564,7 @@
 
         std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type_spec->id - 1];
         if (builder_ptr == nullptr) {
-          builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec);
+          builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec.verified());
           loaded_package->resource_ids_.set(type_spec->id, entry_count);
         } else {
           LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x",
@@ -523,8 +573,8 @@
       } break;
 
       case RES_TABLE_TYPE_TYPE: {
-        const ResTable_type* type = child_chunk.header<ResTable_type, kResTableTypeMinSize>();
-        if (type == nullptr) {
+        const auto type = child_chunk.header<ResTable_type, kResTableTypeMinSize>();
+        if (!type) {
           LOG(ERROR) << "RES_TABLE_TYPE_TYPE too small.";
           return {};
         }
@@ -536,7 +586,7 @@
         // Type chunks must be preceded by their TypeSpec chunks.
         std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type->id - 1];
         if (builder_ptr != nullptr) {
-          builder_ptr->AddType(type);
+          builder_ptr->AddType(type.verified());
         } else {
           LOG(ERROR) << StringPrintf(
               "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.",
@@ -546,8 +596,8 @@
       } break;
 
       case RES_TABLE_LIBRARY_TYPE: {
-        const ResTable_lib_header* lib = child_chunk.header<ResTable_lib_header>();
-        if (lib == nullptr) {
+        const auto lib = child_chunk.header<ResTable_lib_header>();
+        if (!lib) {
           LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small.";
           return {};
         }
@@ -559,10 +609,13 @@
 
         loaded_package->dynamic_package_map_.reserve(dtohl(lib->count));
 
-        const ResTable_lib_entry* const entry_begin =
-            reinterpret_cast<const ResTable_lib_entry*>(child_chunk.data_ptr());
-        const ResTable_lib_entry* const entry_end = entry_begin + dtohl(lib->count);
+        const auto entry_begin = child_chunk.data_ptr().convert<ResTable_lib_entry>();
+        const auto entry_end = entry_begin + dtohl(lib->count);
         for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) {
+          if (!entry_iter) {
+            return {};
+          }
+
           std::string package_name;
           util::ReadUtf16StringFromDevice(entry_iter->packageName,
                                           arraysize(entry_iter->packageName), &package_name);
@@ -580,17 +633,16 @@
       } break;
 
       case RES_TABLE_OVERLAYABLE_TYPE: {
-        const ResTable_overlayable_header* header =
-            child_chunk.header<ResTable_overlayable_header>();
-        if (header == nullptr) {
+        const auto overlayable = child_chunk.header<ResTable_overlayable_header>();
+        if (!overlayable) {
           LOG(ERROR) << "RES_TABLE_OVERLAYABLE_TYPE too small.";
           return {};
         }
 
         std::string name;
-        util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &name);
+        util::ReadUtf16StringFromDevice(overlayable->name, arraysize(overlayable->name), &name);
         std::string actor;
-        util::ReadUtf16StringFromDevice(header->actor, arraysize(header->actor), &actor);
+        util::ReadUtf16StringFromDevice(overlayable->actor, arraysize(overlayable->actor), &actor);
 
         if (loaded_package->overlayable_map_.find(name) !=
             loaded_package->overlayable_map_.end()) {
@@ -606,9 +658,9 @@
 
           switch (overlayable_child_chunk.type()) {
             case RES_TABLE_OVERLAYABLE_POLICY_TYPE: {
-              const ResTable_overlayable_policy_header* policy_header =
+              const auto policy_header =
                   overlayable_child_chunk.header<ResTable_overlayable_policy_header>();
-              if (policy_header == nullptr) {
+              if (!policy_header) {
                 LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small.";
                 return {};
               }
@@ -621,10 +673,12 @@
 
               // Retrieve all the resource ids belonging to this policy chunk
               std::unordered_set<uint32_t> ids;
-              const auto ids_begin =
-                  reinterpret_cast<const ResTable_ref*>(overlayable_child_chunk.data_ptr());
+              const auto ids_begin = overlayable_child_chunk.data_ptr().convert<ResTable_ref>();
               const auto ids_end = ids_begin + dtohl(policy_header->entry_count);
               for (auto id_iter = ids_begin; id_iter != ids_end; ++id_iter) {
+                if (!id_iter) {
+                  return {};
+                }
                 ids.insert(dtohl(id_iter->ident));
               }
 
@@ -633,7 +687,7 @@
               overlayable_info.name = name;
               overlayable_info.actor = actor;
               overlayable_info.policy_flags = policy_header->policy_flags;
-              loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
+              loaded_package->overlayable_infos_.emplace_back(overlayable_info, ids);
               loaded_package->defines_overlayable_ = true;
               break;
             }
@@ -683,8 +737,8 @@
 
 bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
                            package_property_t property_flags) {
-  const ResTable_header* header = chunk.header<ResTable_header>();
-  if (header == nullptr) {
+  incfs::map_ptr<ResTable_header> header = chunk.header<ResTable_header>();
+  if (!header) {
     LOG(ERROR) << "RES_TABLE_TYPE too small.";
     return false;
   }
@@ -747,7 +801,8 @@
   return true;
 }
 
-std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data,
+std::unique_ptr<const LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
+                                                   const size_t length,
                                                    const LoadedIdmap* loaded_idmap,
                                                    const package_property_t property_flags) {
   ATRACE_NAME("LoadedArsc::Load");
@@ -755,7 +810,7 @@
   // Not using make_unique because the constructor is private.
   std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
 
-  ChunkIterator iter(data.data(), data.size());
+  ChunkIterator iter(data, length);
   while (iter.HasNext()) {
     const Chunk chunk = iter.Next();
     switch (chunk.type()) {
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index dfb4009..4010e4e 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -104,22 +104,26 @@
     *dst = 0;
 }
 
-static status_t validate_chunk(const ResChunk_header* chunk,
+static status_t validate_chunk(const incfs::map_ptr<ResChunk_header>& chunk,
                                size_t minSize,
-                               const uint8_t* dataEnd,
+                               const incfs::map_ptr<uint8_t> dataEnd,
                                const char* name)
 {
+    if (!chunk) {
+      return BAD_TYPE;
+    }
+
     const uint16_t headerSize = dtohs(chunk->headerSize);
     const uint32_t size = dtohl(chunk->size);
 
     if (headerSize >= minSize) {
         if (headerSize <= size) {
             if (((headerSize|size)&0x3) == 0) {
-                if ((size_t)size <= (size_t)(dataEnd-((const uint8_t*)chunk))) {
+                if ((size_t)size <= (size_t)(dataEnd-chunk.convert<uint8_t>())) {
                     return NO_ERROR;
                 }
                 ALOGW("%s data size 0x%x extends beyond resource end %p.",
-                     name, size, (void*)(dataEnd-((const uint8_t*)chunk)));
+                     name, size, (void*)(dataEnd-chunk.convert<uint8_t>()));
                 return BAD_TYPE;
             }
             ALOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.",
@@ -450,7 +454,7 @@
     mHeader = (const ResStringPool_header*) header;
 }
 
-status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
+status_t ResStringPool::setTo(incfs::map_ptr<void> data, size_t size, bool copyData)
 {
     if (!data || !size) {
         return (mError=BAD_TYPE);
@@ -467,8 +471,8 @@
     // The data is at least as big as a ResChunk_header, so we can safely validate the other
     // header fields.
     // `data + size` is safe because the source of `size` comes from the kernel/filesystem.
-    if (validate_chunk(reinterpret_cast<const ResChunk_header*>(data), sizeof(ResStringPool_header),
-                       reinterpret_cast<const uint8_t*>(data) + size,
+    const auto chunk_header = data.convert<ResChunk_header>();
+    if (validate_chunk(chunk_header, sizeof(ResStringPool_header), data.convert<uint8_t>() + size,
                        "ResStringPool_header") != NO_ERROR) {
         ALOGW("Bad string block: malformed block dimensions");
         return (mError=BAD_TYPE);
@@ -481,16 +485,25 @@
         if (mOwnedData == NULL) {
             return (mError=NO_MEMORY);
         }
-        memcpy(mOwnedData, data, size);
+
+        if (!data.convert<uint8_t>().verify(size)) {
+            return (mError=NO_MEMORY);
+        }
+
+        memcpy(mOwnedData, data.unsafe_ptr(), size);
         data = mOwnedData;
     }
 
     // The size has been checked, so it is safe to read the data in the ResStringPool_header
     // data structure.
-    mHeader = (const ResStringPool_header*)data;
+    const auto header = data.convert<ResStringPool_header>();
+    if (!header) {
+      return (mError=BAD_TYPE);
+    }
 
+    mHeader = header.verified();
     if (notDeviceEndian) {
-        ResStringPool_header* h = const_cast<ResStringPool_header*>(mHeader);
+        ResStringPool_header* h = const_cast<ResStringPool_header*>(mHeader.unsafe_ptr());
         h->header.headerSize = dtohs(mHeader->header.headerSize);
         h->header.type = dtohs(mHeader->header.type);
         h->header.size = dtohl(mHeader->header.size);
@@ -508,8 +521,7 @@
         return (mError=BAD_TYPE);
     }
     mSize = mHeader->header.size;
-    mEntries = (const uint32_t*)
-        (((const uint8_t*)data)+mHeader->header.headerSize);
+    mEntries = data.offset(mHeader->header.headerSize).convert<uint32_t>();
 
     if (mHeader->stringCount > 0) {
         if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount)  // uint32 overflow?
@@ -536,9 +548,7 @@
             return (mError=BAD_TYPE);
         }
 
-        mStrings = (const void*)
-            (((const uint8_t*)data) + mHeader->stringsStart);
-
+        mStrings = data.offset(mHeader->stringsStart).convert<void>();
         if (mHeader->styleCount == 0) {
             mStringPoolSize = (mSize - mHeader->stringsStart) / charSize;
         } else {
@@ -560,31 +570,37 @@
 
         // check invariant: stringCount > 0 requires a string pool to exist
         if (mStringPoolSize == 0) {
-            ALOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount);
+            ALOGW("Bad string block: stringCount is %d but pool size is 0\n",
+                  (int)mHeader->stringCount);
             return (mError=BAD_TYPE);
         }
 
         if (notDeviceEndian) {
             size_t i;
-            uint32_t* e = const_cast<uint32_t*>(mEntries);
+            auto e = const_cast<uint32_t*>(mEntries.unsafe_ptr());
             for (i=0; i<mHeader->stringCount; i++) {
-                e[i] = dtohl(mEntries[i]);
+                e[i] = dtohl(e[i]);
             }
             if (!(mHeader->flags&ResStringPool_header::UTF8_FLAG)) {
-                const uint16_t* strings = (const uint16_t*)mStrings;
-                uint16_t* s = const_cast<uint16_t*>(strings);
+                uint16_t* s = const_cast<uint16_t*>(mStrings.convert<uint16_t>().unsafe_ptr());
                 for (i=0; i<mStringPoolSize; i++) {
-                    s[i] = dtohs(strings[i]);
+                    s[i] = dtohs(s[i]);
                 }
             }
         }
 
-        if ((mHeader->flags&ResStringPool_header::UTF8_FLAG &&
-                ((uint8_t*)mStrings)[mStringPoolSize-1] != 0) ||
-                (!(mHeader->flags&ResStringPool_header::UTF8_FLAG) &&
-                ((uint16_t*)mStrings)[mStringPoolSize-1] != 0)) {
-            ALOGW("Bad string block: last string is not 0-terminated\n");
-            return (mError=BAD_TYPE);
+        if (mHeader->flags&ResStringPool_header::UTF8_FLAG) {
+            auto end = mStrings.convert<uint8_t>() + (mStringPoolSize-1);
+            if (!end || end.value() != 0) {
+                ALOGW("Bad string block: last string is not 0-terminated\n");
+                return (mError=BAD_TYPE);
+            }
+        } else {
+            auto end = mStrings.convert<uint16_t>() + (mStringPoolSize-1);
+            if (!end || end.value() != 0) {
+                ALOGW("Bad string block: last string is not 0-terminated\n");
+                return (mError=BAD_TYPE);
+            }
         }
     } else {
         mStrings = NULL;
@@ -599,14 +615,13 @@
             return (mError=BAD_TYPE);
         }
 
-        if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) {
+        if ((mEntryStyles.convert<uint8_t>() - mHeader.convert<uint8_t>()) > (int)size) {
             ALOGW("Bad string block: entry of %d styles extends past data size %d\n",
-                    (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader),
+                    (int)(mEntryStyles.convert<uint8_t>()-mHeader.convert<uint8_t>()),
                     (int)size);
             return (mError=BAD_TYPE);
         }
-        mStyles = (const uint32_t*)
-            (((const uint8_t*)data)+mHeader->stylesStart);
+        mStyles = data.offset(mHeader->stylesStart).convert<uint32_t>();
         if (mHeader->stylesStart >= mHeader->header.size) {
             ALOGW("Bad string block: style pool starts %d, after total size %d\n",
                     (int)mHeader->stylesStart, (int)mHeader->header.size);
@@ -617,13 +632,13 @@
 
         if (notDeviceEndian) {
             size_t i;
-            uint32_t* e = const_cast<uint32_t*>(mEntryStyles);
+            uint32_t* e = const_cast<uint32_t*>(mEntryStyles.unsafe_ptr());
             for (i=0; i<mHeader->styleCount; i++) {
-                e[i] = dtohl(mEntryStyles[i]);
+                e[i] = dtohl(e[i]);
             }
-            uint32_t* s = const_cast<uint32_t*>(mStyles);
+            uint32_t* s = const_cast<uint32_t*>(mStyles.unsafe_ptr());
             for (i=0; i<mStylePoolSize; i++) {
-                s[i] = dtohl(mStyles[i]);
+                s[i] = dtohl(s[i]);
             }
         }
 
@@ -631,8 +646,9 @@
             { htodl(ResStringPool_span::END) },
             htodl(ResStringPool_span::END), htodl(ResStringPool_span::END)
         };
-        if (memcmp(&mStyles[mStylePoolSize-(sizeof(endSpan)/sizeof(uint32_t))],
-                   &endSpan, sizeof(endSpan)) != 0) {
+
+        auto stylesEnd = mStyles + (mStylePoolSize-(sizeof(endSpan)/sizeof(uint32_t)));
+        if (!stylesEnd || memcmp(stylesEnd.unsafe_ptr(), &endSpan, sizeof(endSpan)) != 0) {
             ALOGW("Bad string block: last style is not 0xFFFFFFFF-terminated\n");
             return (mError=BAD_TYPE);
         }
@@ -653,7 +669,7 @@
 void ResStringPool::uninit()
 {
     mError = NO_INIT;
-    if (mHeader != NULL && mCache != NULL) {
+    if (mHeader && mCache != NULL) {
         for (size_t x = 0; x < mHeader->stringCount; x++) {
             if (mCache[x] != NULL) {
                 free(mCache[x]);
@@ -679,15 +695,21 @@
  * data encoded. In that case, drop the high bit of the first character and
  * add it together with the next character.
  */
-static inline size_t
-decodeLength(const uint16_t** str)
+static inline base::expected<size_t, IOError> decodeLength(incfs::map_ptr<uint16_t>* str)
 {
-    size_t len = **str;
-    if ((len & 0x8000) != 0) {
-        (*str)++;
-        len = ((len & 0x7FFF) << 16) | **str;
+    if (UNLIKELY(!*str)) {
+        return base::unexpected(IOError::PAGES_MISSING);
     }
-    (*str)++;
+
+    size_t len = str->value();
+    if ((len & 0x8000U) != 0) {
+        ++(*str);
+        if (UNLIKELY(!*str)) {
+            return base::unexpected(IOError::PAGES_MISSING);
+        }
+        len = ((len & 0x7FFFU) << 16U) | str->value();
+    }
+    ++(*str);
     return len;
 }
 
@@ -701,82 +723,119 @@
  * data encoded. In that case, drop the high bit of the first character and
  * add it together with the next character.
  */
-static inline size_t
-decodeLength(const uint8_t** str)
+static inline base::expected<size_t, IOError> decodeLength(incfs::map_ptr<uint8_t>* str)
 {
-    size_t len = **str;
-    if ((len & 0x80) != 0) {
-        (*str)++;
-        len = ((len & 0x7F) << 8) | **str;
+    if (UNLIKELY(!*str)) {
+        return base::unexpected(IOError::PAGES_MISSING);
     }
-    (*str)++;
+
+    size_t len = str->value();
+    if ((len & 0x80U) != 0) {
+        ++(*str);
+        if (UNLIKELY(!*str)) {
+            return base::unexpected(IOError::PAGES_MISSING);
+        }
+        len = ((len & 0x7FU) << 8U) | str->value();
+    }
+    ++(*str);
     return len;
 }
 
-const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const
+base::expected<StringPiece16, NullOrIOError> ResStringPool::stringAt(size_t idx) const
 {
     if (mError == NO_ERROR && idx < mHeader->stringCount) {
         const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0;
-        const uint32_t off = mEntries[idx]/(isUTF8?sizeof(uint8_t):sizeof(uint16_t));
+        auto offPtr = mEntries + idx;
+        if (UNLIKELY(!offPtr)) {
+           return base::unexpected(IOError::PAGES_MISSING);
+        }
+
+        const uint32_t off = (offPtr.value())/(isUTF8?sizeof(uint8_t):sizeof(uint16_t));
         if (off < (mStringPoolSize-1)) {
             if (!isUTF8) {
-                const uint16_t* strings = (uint16_t*)mStrings;
-                const uint16_t* str = strings+off;
+                auto strings = mStrings.convert<uint16_t>();
+                auto str = strings+off;
 
-                *u16len = decodeLength(&str);
+                const base::expected<size_t, IOError> u16len = decodeLength(&str);
+                if (UNLIKELY(!u16len.has_value())) {
+                    return base::unexpected(u16len.error());
+                }
+
                 if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) {
                     // Reject malformed (non null-terminated) strings
-                    if (str[*u16len] != 0x0000) {
-                        ALOGW("Bad string block: string #%d is not null-terminated",
-                              (int)idx);
-                        return NULL;
+                    const auto nullAddress = str + (*u16len);
+                    if (UNLIKELY(!nullAddress)) {
+                       return base::unexpected(IOError::PAGES_MISSING);
                     }
-                    return reinterpret_cast<const char16_t*>(str);
+
+                    if (nullAddress.value() != 0x0000) {
+                        ALOGW("Bad string block: string #%d is not null-terminated", (int)idx);
+                        return base::unexpected(std::nullopt);
+                    }
+
+                    if (UNLIKELY(!str.verify(*u16len + 1U))) {
+                      return base::unexpected(IOError::PAGES_MISSING);
+                    }
+
+                    return StringPiece16(reinterpret_cast<const char16_t*>(str.unsafe_ptr()),
+                                         *u16len);
                 } else {
                     ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
                             (int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize);
                 }
             } else {
-                const uint8_t* strings = (uint8_t*)mStrings;
-                const uint8_t* u8str = strings+off;
+                auto strings = mStrings.convert<uint8_t>();
+                auto u8str = strings+off;
 
-                *u16len = decodeLength(&u8str);
-                size_t u8len = decodeLength(&u8str);
+                base::expected<size_t, IOError> u16len = decodeLength(&u8str);
+                if (UNLIKELY(!u16len.has_value())) {
+                    return base::unexpected(u16len.error());
+                }
+
+                const base::expected<size_t, IOError> u8len = decodeLength(&u8str);
+                if (UNLIKELY(!u8len.has_value())) {
+                    return base::unexpected(u8len.error());
+                }
 
                 // encLen must be less than 0x7FFF due to encoding.
-                if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) {
+                if ((uint32_t)(u8str+*u8len-strings) < mStringPoolSize) {
                     AutoMutex lock(mDecodeLock);
 
                     if (mCache != NULL && mCache[idx] != NULL) {
-                        return mCache[idx];
+                        return StringPiece16(mCache[idx], *u16len);
                     }
 
                     // Retrieve the actual length of the utf8 string if the
                     // encoded length was truncated
-                    if (stringDecodeAt(idx, u8str, u8len, &u8len) == NULL) {
-                        return NULL;
+                    auto decodedString = stringDecodeAt(idx, u8str, *u8len);
+                    if (!decodedString.has_value()) {
+                        return base::unexpected(decodedString.error());
                     }
 
                     // Since AAPT truncated lengths longer than 0x7FFF, check
                     // that the bits that remain after truncation at least match
                     // the bits of the actual length
-                    ssize_t actualLen = utf8_to_utf16_length(u8str, u8len);
-                    if (actualLen < 0 || ((size_t)actualLen & 0x7FFF) != *u16len) {
+                    ssize_t actualLen = utf8_to_utf16_length(
+                        reinterpret_cast<const uint8_t*>(decodedString->data()),
+                        decodedString->size());
+
+                    if (actualLen < 0 || ((size_t)actualLen & 0x7FFFU) != *u16len) {
                         ALOGW("Bad string block: string #%lld decoded length is not correct "
                                 "%lld vs %llu\n",
                                 (long long)idx, (long long)actualLen, (long long)*u16len);
-                        return NULL;
+                        return base::unexpected(std::nullopt);
                     }
 
-                    *u16len = (size_t) actualLen;
-                    char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t));
+                    u16len = (size_t) actualLen;
+                    auto u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t));
                     if (!u16str) {
                         ALOGW("No memory when trying to allocate decode cache for string #%d\n",
                                 (int)idx);
-                        return NULL;
+                        return base::unexpected(std::nullopt);
                     }
 
-                    utf8_to_utf16(u8str, u8len, u16str, *u16len + 1);
+                    utf8_to_utf16(reinterpret_cast<const uint8_t*>(decodedString->data()),
+                                  decodedString->size(), u16str, *u16len + 1);
 
                     if (mCache == NULL) {
 #ifndef __ANDROID__
@@ -793,19 +852,19 @@
                         if (mCache == NULL) {
                             ALOGW("No memory trying to allocate decode cache table of %d bytes\n",
                                   (int)(mHeader->stringCount*sizeof(char16_t**)));
-                            return NULL;
+                            return base::unexpected(std::nullopt);
                         }
                     }
 
                     if (kDebugStringPoolNoisy) {
-                      ALOGI("Caching UTF8 string: %s", u8str);
+                      ALOGI("Caching UTF8 string: %s", u8str.unsafe_ptr());
                     }
 
                     mCache[idx] = u16str;
-                    return u16str;
+                    return StringPiece16(u16str, *u16len);
                 } else {
                     ALOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n",
-                            (long long)idx, (long long)(u8str+u8len-strings),
+                            (long long)idx, (long long)(u8str+*u8len-strings),
                             (long long)mStringPoolSize);
                 }
             }
@@ -815,33 +874,43 @@
                     (int)(mStringPoolSize*sizeof(uint16_t)));
         }
     }
-    return NULL;
+    return base::unexpected(std::nullopt);
 }
 
-const char* ResStringPool::string8At(size_t idx, size_t* outLen) const
+base::expected<StringPiece, NullOrIOError> ResStringPool::string8At(size_t idx) const
 {
     if (mError == NO_ERROR && idx < mHeader->stringCount) {
         if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) == 0) {
-            return NULL;
+            return base::unexpected(std::nullopt);
         }
-        const uint32_t off = mEntries[idx]/sizeof(char);
+
+        auto offPtr = mEntries + idx;
+        if (UNLIKELY(!offPtr)) {
+           return base::unexpected(IOError::PAGES_MISSING);
+        }
+
+        const uint32_t off = (offPtr.value())/sizeof(char);
         if (off < (mStringPoolSize-1)) {
-            const uint8_t* strings = (uint8_t*)mStrings;
-            const uint8_t* str = strings+off;
+            auto strings = mStrings.convert<uint8_t>();
+            auto str = strings+off;
 
             // Decode the UTF-16 length. This is not used if we're not
             // converting to UTF-16 from UTF-8.
-            decodeLength(&str);
+            const base::expected<size_t, IOError> u16len = decodeLength(&str);
+            if (UNLIKELY(!u16len)) {
+                return base::unexpected(u16len.error());
+            }
 
-            const size_t encLen = decodeLength(&str);
-            *outLen = encLen;
+            const base::expected<size_t, IOError> u8len = decodeLength(&str);
+            if (UNLIKELY(!u8len)) {
+                return base::unexpected(u8len.error());
+            }
 
-            if ((uint32_t)(str+encLen-strings) < mStringPoolSize) {
-                return stringDecodeAt(idx, str, encLen, outLen);
-
+            if ((uint32_t)(str+*u8len-strings) < mStringPoolSize) {
+                return stringDecodeAt(idx, str, *u8len);
             } else {
                 ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
-                        (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize);
+                        (int)idx, (int)(str+*u8len-strings), (int)mStringPoolSize);
             }
         } else {
             ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n",
@@ -849,7 +918,7 @@
                     (int)(mStringPoolSize*sizeof(uint16_t)));
         }
     }
-    return NULL;
+    return base::unexpected(std::nullopt);
 }
 
 /**
@@ -859,74 +928,93 @@
  * bits. Strings that exceed the maximum encode length are not placed into
  * StringPools in AAPT2.
  **/
-const char* ResStringPool::stringDecodeAt(size_t idx, const uint8_t* str,
-                                          const size_t encLen, size_t* outLen) const {
-    const uint8_t* strings = (uint8_t*)mStrings;
-
+base::expected<StringPiece, NullOrIOError> ResStringPool::stringDecodeAt(
+      size_t idx, incfs::map_ptr<uint8_t> str, size_t encLen) const
+{
+    const auto strings = mStrings.convert<uint8_t>();
     size_t i = 0, end = encLen;
     while ((uint32_t)(str+end-strings) < mStringPoolSize) {
-        if (str[end] == 0x00) {
+        const auto nullAddress = str + end;
+        if (UNLIKELY(!nullAddress)) {
+            return base::unexpected(IOError::PAGES_MISSING);
+        }
+
+        if (nullAddress.value() == 0x00) {
             if (i != 0) {
                 ALOGW("Bad string block: string #%d is truncated (actual length is %d)",
                       (int)idx, (int)end);
             }
 
-            *outLen = end;
-            return (const char*)str;
+            if (UNLIKELY(!str.verify(end + 1U))) {
+              return base::unexpected(IOError::PAGES_MISSING);
+            }
+
+            return StringPiece((const char*) str.unsafe_ptr(), end);
         }
 
         end = (++i << (sizeof(uint8_t) * 8 * 2 - 1)) | encLen;
     }
 
     // Reject malformed (non null-terminated) strings
-    ALOGW("Bad string block: string #%d is not null-terminated",
-          (int)idx);
-    return NULL;
+    ALOGW("Bad string block: string #%d is not null-terminated", (int)idx);
+    return base::unexpected(std::nullopt);
 }
 
-const String8 ResStringPool::string8ObjectAt(size_t idx) const
+base::expected<String8, IOError> ResStringPool::string8ObjectAt(size_t idx) const
 {
-    size_t len;
-    const char *str = string8At(idx, &len);
-    if (str != NULL) {
-        return String8(str, len);
+    const base::expected<StringPiece, NullOrIOError> str = string8At(idx);
+    if (UNLIKELY(IsIOError(str))) {
+        return base::unexpected(GetIOError(str.error()));
+    }
+    if (str.has_value()) {
+        return String8(str->data(), str->size());
     }
 
-    const char16_t *str16 = stringAt(idx, &len);
-    if (str16 != NULL) {
-        return String8(str16, len);
+    const base::expected<StringPiece16, NullOrIOError> str16 = stringAt(idx);
+    if (UNLIKELY(IsIOError(str16))) {
+        return base::unexpected(GetIOError(str16.error()));
     }
+    if (str16.has_value()) {
+        return String8(str16->data(), str16->size());
+    }
+
     return String8();
 }
 
-const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const
+base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> ResStringPool::styleAt(
+    const ResStringPool_ref& ref) const
 {
     return styleAt(ref.index);
 }
 
-const ResStringPool_span* ResStringPool::styleAt(size_t idx) const
+base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> ResStringPool::styleAt(
+    size_t idx) const
 {
     if (mError == NO_ERROR && idx < mHeader->styleCount) {
-        const uint32_t off = (mEntryStyles[idx]/sizeof(uint32_t));
+        auto offPtr = mEntryStyles + idx;
+        if (UNLIKELY(!offPtr)) {
+           return base::unexpected(IOError::PAGES_MISSING);
+        }
+
+        const uint32_t off = ((offPtr.value())/sizeof(uint32_t));
         if (off < mStylePoolSize) {
-            return (const ResStringPool_span*)(mStyles+off);
+            return (mStyles+off).convert<ResStringPool_span>();
         } else {
             ALOGW("Bad string block: style #%d entry is at %d, past end at %d\n",
                     (int)idx, (int)(off*sizeof(uint32_t)),
                     (int)(mStylePoolSize*sizeof(uint32_t)));
         }
     }
-    return NULL;
+    return base::unexpected(std::nullopt);
 }
 
-ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
+base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_t* str,
+                                                                   size_t strLen) const
 {
     if (mError != NO_ERROR) {
-        return mError;
+        return base::unexpected(std::nullopt);
     }
 
-    size_t len;
-
     if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) {
         if (kDebugStringPoolNoisy) {
             ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string());
@@ -948,17 +1036,19 @@
             ssize_t mid;
             while (l <= h) {
                 mid = l + (h - l)/2;
-                const uint8_t* s = (const uint8_t*)string8At(mid, &len);
-                int c;
-                if (s != NULL) {
-                    char16_t* end = utf8_to_utf16(s, len, convBuffer, convBufferLen);
+                int c = -1;
+                const base::expected<StringPiece, NullOrIOError> s = string8At(mid);
+                if (UNLIKELY(IsIOError(s))) {
+                    return base::unexpected(s.error());
+                }
+                if (s.has_value()) {
+                    char16_t* end = utf8_to_utf16(reinterpret_cast<const uint8_t*>(s->data()),
+                                                  s->size(), convBuffer, convBufferLen);
                     c = strzcmp16(convBuffer, end-convBuffer, str, strLen);
-                } else {
-                    c = -1;
                 }
                 if (kDebugStringPoolNoisy) {
                     ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
-                            (const char*)s, c, (int)l, (int)mid, (int)h);
+                          s->data(), c, (int)l, (int)mid, (int)h);
                 }
                 if (c == 0) {
                     if (kDebugStringPoolNoisy) {
@@ -981,15 +1071,21 @@
             String8 str8(str, strLen);
             const size_t str8Len = str8.size();
             for (int i=mHeader->stringCount-1; i>=0; i--) {
-                const char* s = string8At(i, &len);
-                if (kDebugStringPoolNoisy) {
-                    ALOGI("Looking at %s, i=%d\n", String8(s).string(), i);
+                const base::expected<StringPiece, NullOrIOError> s = string8At(i);
+                if (UNLIKELY(IsIOError(s))) {
+                    return base::unexpected(s.error());
                 }
-                if (s && str8Len == len && memcmp(s, str8.string(), str8Len) == 0) {
+                if (s.has_value()) {
                     if (kDebugStringPoolNoisy) {
-                        ALOGI("MATCH!");
+                        ALOGI("Looking at %s, i=%d\n", s->data(), i);
                     }
-                    return i;
+                    if (str8Len == s->size()
+                            && memcmp(s->data(), str8.string(), str8Len) == 0) {
+                        if (kDebugStringPoolNoisy) {
+                            ALOGI("MATCH!");
+                        }
+                        return i;
+                    }
                 }
             }
         }
@@ -1007,11 +1103,14 @@
             ssize_t mid;
             while (l <= h) {
                 mid = l + (h - l)/2;
-                const char16_t* s = stringAt(mid, &len);
-                int c = s ? strzcmp16(s, len, str, strLen) : -1;
+                const base::expected<StringPiece16, NullOrIOError> s = stringAt(mid);
+                if (UNLIKELY(IsIOError(s))) {
+                    return base::unexpected(s.error());
+                }
+                int c = s.has_value() ? strzcmp16(s->data(), s->size(), str, strLen) : -1;
                 if (kDebugStringPoolNoisy) {
                     ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
-                            String8(s).string(), c, (int)l, (int)mid, (int)h);
+                          String8(s->data(), s->size()).string(), c, (int)l, (int)mid, (int)h);
                 }
                 if (c == 0) {
                     if (kDebugStringPoolNoisy) {
@@ -1030,11 +1129,15 @@
             // span tags; since those always appear at the end of the string
             // block, start searching at the back.
             for (int i=mHeader->stringCount-1; i>=0; i--) {
-                const char16_t* s = stringAt(i, &len);
-                if (kDebugStringPoolNoisy) {
-                    ALOGI("Looking at %s, i=%d\n", String8(s).string(), i);
+                const base::expected<StringPiece16, NullOrIOError> s = stringAt(i);
+                if (UNLIKELY(IsIOError(s))) {
+                    return base::unexpected(s.error());
                 }
-                if (s && strLen == len && strzcmp16(s, len, str, strLen) == 0) {
+                if (kDebugStringPoolNoisy) {
+                    ALOGI("Looking at %s, i=%d\n", String8(s->data(), s->size()).string(), i);
+                }
+                if (s.has_value() && strLen == s->size() &&
+                        strzcmp16(s->data(), s->size(), str, strLen) == 0) {
                     if (kDebugStringPoolNoisy) {
                         ALOGI("MATCH!");
                     }
@@ -1043,8 +1146,7 @@
             }
         }
     }
-
-    return NAME_NOT_FOUND;
+    return base::unexpected(std::nullopt);
 }
 
 size_t ResStringPool::size() const
@@ -1062,9 +1164,10 @@
     return (mError == NO_ERROR) ? mHeader->header.size : 0;
 }
 
-const void* ResStringPool::data() const
+incfs::map_ptr<void> ResStringPool::data() const
 {
-    return mHeader;
+
+    return mHeader.unsafe_ptr();
 }
 
 bool ResStringPool::isSorted() const
@@ -1121,7 +1224,7 @@
 const char16_t* ResXMLParser::getComment(size_t* outLen) const
 {
     int32_t id = getCommentID();
-    return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+    return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
 }
 
 uint32_t ResXMLParser::getLineNumber() const
@@ -1140,7 +1243,7 @@
 const char16_t* ResXMLParser::getText(size_t* outLen) const
 {
     int32_t id = getTextID();
-    return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+    return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
 }
 
 ssize_t ResXMLParser::getTextValue(Res_value* outValue) const
@@ -1164,7 +1267,7 @@
 {
     int32_t id = getNamespacePrefixID();
     //printf("prefix=%d  event=%p\n", id, mEventCode);
-    return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+    return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
 }
 
 int32_t ResXMLParser::getNamespaceUriID() const
@@ -1179,7 +1282,7 @@
 {
     int32_t id = getNamespaceUriID();
     //printf("uri=%d  event=%p\n", id, mEventCode);
-    return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+    return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
 }
 
 int32_t ResXMLParser::getElementNamespaceID() const
@@ -1196,7 +1299,7 @@
 const char16_t* ResXMLParser::getElementNamespace(size_t* outLen) const
 {
     int32_t id = getElementNamespaceID();
-    return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+    return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
 }
 
 int32_t ResXMLParser::getElementNameID() const
@@ -1213,7 +1316,7 @@
 const char16_t* ResXMLParser::getElementName(size_t* outLen) const
 {
     int32_t id = getElementNameID();
-    return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+    return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
 }
 
 size_t ResXMLParser::getAttributeCount() const
@@ -1246,7 +1349,7 @@
     if (kDebugXMLNoisy) {
         printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id);
     }
-    return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+    return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
 }
 
 const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) const
@@ -1256,7 +1359,7 @@
     if (kDebugXMLNoisy) {
         printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id);
     }
-    return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL;
+    return id >= 0 ? UnpackOptionalString(mTree.mStrings.string8At(id), outLen) : NULL;
 }
 
 int32_t ResXMLParser::getAttributeNameID(size_t idx) const
@@ -1281,7 +1384,7 @@
     if (kDebugXMLNoisy) {
         printf("getAttributeName 0x%zx=0x%x\n", idx, id);
     }
-    return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+    return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
 }
 
 const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const
@@ -1291,7 +1394,7 @@
     if (kDebugXMLNoisy) {
         printf("getAttributeName 0x%zx=0x%x\n", idx, id);
     }
-    return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL;
+    return id >= 0 ? UnpackOptionalString(mTree.mStrings.string8At(id), outLen) : NULL;
 }
 
 uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const
@@ -1328,7 +1431,7 @@
     if (kDebugXMLNoisy) {
         printf("getAttributeValue 0x%zx=0x%x\n", idx, id);
     }
-    return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+    return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
 }
 
 int32_t ResXMLParser::getAttributeDataType(size_t idx) const
@@ -3596,9 +3699,10 @@
     ssize_t findType16(const char16_t* type, size_t len) const {
         const size_t N = packages.size();
         for (size_t i = 0; i < N; i++) {
-            ssize_t index = packages[i]->typeStrings.indexOfString(type, len);
-            if (index >= 0) {
-                return index + packages[i]->typeIdOffset;
+            const base::expected<size_t, NullOrIOError> index =
+                packages[i]->typeStrings.indexOfString(type, len);
+            if (index.has_value()) {
+                return *index + packages[i]->typeIdOffset;
             }
         }
         return -1;
@@ -4304,21 +4408,21 @@
     outName->package = grp->name.string();
     outName->packageLen = grp->name.size();
     if (allowUtf8) {
-        outName->type8 = entry.typeStr.string8(&outName->typeLen);
-        outName->name8 = entry.keyStr.string8(&outName->nameLen);
+        outName->type8 = UnpackOptionalString(entry.typeStr.string8(), &outName->typeLen);
+        outName->name8 = UnpackOptionalString(entry.keyStr.string8(), &outName->nameLen);
     } else {
         outName->type8 = NULL;
         outName->name8 = NULL;
     }
     if (outName->type8 == NULL) {
-        outName->type = entry.typeStr.string16(&outName->typeLen);
+        outName->type = UnpackOptionalString(entry.typeStr.string16(), &outName->typeLen);
         // If we have a bad index for some reason, we should abort.
         if (outName->type == NULL) {
             return false;
         }
     }
     if (outName->name8 == NULL) {
-        outName->name = entry.keyStr.string16(&outName->nameLen);
+        outName->name = UnpackOptionalString(entry.keyStr.string16(), &outName->nameLen);
         // If we have a bad index for some reason, we should abort.
         if (outName->name == NULL) {
             return false;
@@ -4406,7 +4510,8 @@
                 entry.package->header->index,
                 outValue->dataType,
                 outValue->dataType == Res_value::TYPE_STRING ?
-                    String8(entry.package->header->values.stringAt(outValue->data, &len)).string() :
+                    String8(UnpackOptionalString(
+                        entry.package->header->values.stringAt(outValue->data), &len)).string() :
                     "",
                 outValue->data);
     }
@@ -4462,7 +4567,8 @@
         return NULL;
     }
     if (value->dataType == value->TYPE_STRING) {
-        return getTableStringBlock(stringBlock)->stringAt(value->data, outLen);
+        return UnpackOptionalString(getTableStringBlock(stringBlock)->stringAt(value->data),
+                                    outLen);
     }
     // XXX do int to string conversions.
     return NULL;
@@ -4978,15 +5084,13 @@
             size_t targetTypeLen = typeLen;
 
             do {
-                ssize_t ti = group->packages[pi]->typeStrings.indexOfString(
-                        targetType, targetTypeLen);
-                if (ti < 0) {
+                auto ti = group->packages[pi]->typeStrings.indexOfString(targetType, targetTypeLen);
+                if (!ti.has_value()) {
                     continue;
                 }
 
-                ti += group->packages[pi]->typeIdOffset;
-
-                const uint32_t identifier = findEntry(group, ti, name, nameLen,
+                *ti += group->packages[pi]->typeIdOffset;
+                const uint32_t identifier = findEntry(group, *ti, name, nameLen,
                         outTypeSpecFlags);
                 if (identifier != 0) {
                     if (fakePublic && outTypeSpecFlags) {
@@ -5009,8 +5113,9 @@
     const size_t typeCount = typeList.size();
     for (size_t i = 0; i < typeCount; i++) {
         const Type* t = typeList[i];
-        const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen);
-        if (ei < 0) {
+        const base::expected<size_t, NullOrIOError> ei =
+            t->package->keyStrings.indexOfString(name, nameLen);
+        if (!ei.has_value()) {
             continue;
         }
 
@@ -5025,7 +5130,7 @@
                     continue;
                 }
 
-                if (dtohl(entry->key.index) == (size_t) ei) {
+                if (dtohl(entry->key.index) == (size_t) *ei) {
                     uint32_t resId = Res_MAKEID(group->id - 1, typeIndex, iter.index());
                     if (outTypeSpecFlags) {
                         Entry result;
@@ -6187,8 +6292,9 @@
             for (size_t k = 0; k < numTypes; k++) {
                 const Type* type = typeList[k];
                 const ResStringPool& typeStrings = type->package->typeStrings;
-                if (ignoreMipmap && typeStrings.string8ObjectAt(
-                            type->typeSpec->id - 1) == "mipmap") {
+                const base::expected<String8, NullOrIOError> typeStr = typeStrings.string8ObjectAt(
+                    type->typeSpec->id - 1);
+                if (ignoreMipmap && typeStr.has_value() && *typeStr == "mipmap") {
                     continue;
                 }
 
@@ -6244,24 +6350,18 @@
 StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index)
     : mPool(pool), mIndex(index) {}
 
-const char* StringPoolRef::string8(size_t* outLen) const {
-    if (mPool != NULL) {
-        return mPool->string8At(mIndex, outLen);
+base::expected<StringPiece, NullOrIOError> StringPoolRef::string8() const {
+    if (LIKELY(mPool != NULL)) {
+        return mPool->string8At(mIndex);
     }
-    if (outLen != NULL) {
-        *outLen = 0;
-    }
-    return NULL;
+    return base::unexpected(std::nullopt);
 }
 
-const char16_t* StringPoolRef::string16(size_t* outLen) const {
-    if (mPool != NULL) {
-        return mPool->stringAt(mIndex, outLen);
+base::expected<StringPiece16, NullOrIOError> StringPoolRef::string16() const {
+    if (LIKELY(mPool != NULL)) {
+        return mPool->stringAt(mIndex);
     }
-    if (outLen != NULL) {
-        *outLen = 0;
-    }
-    return NULL;
+    return base::unexpected(std::nullopt);
 }
 
 bool ResTable::getResourceFlags(uint32_t resID, uint32_t* outFlags) const {
@@ -7380,13 +7480,13 @@
         printf("(dynamic attribute) 0x%08x\n", value.data);
     } else if (value.dataType == Res_value::TYPE_STRING) {
         size_t len;
-        const char* str8 = pkg->header->values.string8At(
-                value.data, &len);
+        const char* str8 = UnpackOptionalString(pkg->header->values.string8At(
+                value.data), &len);
         if (str8 != NULL) {
             printf("(string8) \"%s\"\n", normalizeForOutput(str8).string());
         } else {
-            const char16_t* str16 = pkg->header->values.stringAt(
-                    value.data, &len);
+            const char16_t* str16 = UnpackOptionalString(pkg->header->values.stringAt(
+                    value.data), &len);
             if (str16 != NULL) {
                 printf("(string16) \"%s\"\n",
                     normalizeForOutput(String8(str16, len).string()).string());
diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp
index c63dff8..a34aa72 100644
--- a/libs/androidfw/ResourceUtils.cpp
+++ b/libs/androidfw/ResourceUtils.cpp
@@ -48,61 +48,76 @@
          !(has_type_separator && out_type->empty());
 }
 
-bool ToResourceName(const StringPoolRef& type_string_ref,
-                    const StringPoolRef& entry_string_ref,
-                    const StringPiece& package_name,
-                    AssetManager2::ResourceName* out_name) {
-  out_name->package = package_name.data();
-  out_name->package_len = package_name.size();
+base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName(
+    const StringPoolRef& type_string_ref, const StringPoolRef& entry_string_ref,
+    const StringPiece& package_name) {
+  AssetManager2::ResourceName name{
+    .package = package_name.data(),
+    .package_len = package_name.size(),
+  };
 
-  out_name->type = type_string_ref.string8(&out_name->type_len);
-  out_name->type16 = nullptr;
-  if (out_name->type == nullptr) {
-    out_name->type16 = type_string_ref.string16(&out_name->type_len);
-    if (out_name->type16 == nullptr) {
-      return false;
+  if (base::expected<StringPiece, NullOrIOError> type_str = type_string_ref.string8()) {
+    name.type = type_str->data();
+    name.type_len = type_str->size();
+  } else if (UNLIKELY(IsIOError(type_str))) {
+    return base::unexpected(type_str.error());
+  }
+
+  if (name.type == nullptr) {
+    if (base::expected<StringPiece16, NullOrIOError> type16_str = type_string_ref.string16()) {
+      name.type16 = type16_str->data();
+      name.type_len = type16_str->size();
+    } else if (!type16_str.has_value()) {
+      return base::unexpected(type16_str.error());
     }
   }
 
-  out_name->entry = entry_string_ref.string8(&out_name->entry_len);
-  out_name->entry16 = nullptr;
-  if (out_name->entry == nullptr) {
-    out_name->entry16 = entry_string_ref.string16(&out_name->entry_len);
-    if (out_name->entry16 == nullptr) {
-      return false;
+  if (base::expected<StringPiece, NullOrIOError> entry_str = entry_string_ref.string8()) {
+    name.entry = entry_str->data();
+    name.entry_len = entry_str->size();
+  } else if (UNLIKELY(IsIOError(entry_str))) {
+    return base::unexpected(entry_str.error());
+  }
+
+  if (name.entry == nullptr) {
+    if (base::expected<StringPiece16, NullOrIOError> entry16_str = entry_string_ref.string16()) {
+      name.entry16 = entry16_str->data();
+      name.entry_len = entry16_str->size();
+    } else if (!entry16_str.has_value()) {
+      return base::unexpected(entry16_str.error());
     }
   }
 
-  return true;
+  return name;
 }
 
-std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name) {
+std::string ToFormattedResourceString(const AssetManager2::ResourceName& resource_name) {
   std::string result;
-  if (resource_name->package != nullptr) {
-    result.append(resource_name->package, resource_name->package_len);
+  if (resource_name.package != nullptr) {
+    result.append(resource_name.package, resource_name.package_len);
   }
 
-  if (resource_name->type != nullptr || resource_name->type16 != nullptr) {
+  if (resource_name.type != nullptr || resource_name.type16 != nullptr) {
     if (!result.empty()) {
       result += ":";
     }
 
-    if (resource_name->type != nullptr) {
-      result.append(resource_name->type, resource_name->type_len);
+    if (resource_name.type != nullptr) {
+      result.append(resource_name.type, resource_name.type_len);
     } else {
-      result += util::Utf16ToUtf8(StringPiece16(resource_name->type16, resource_name->type_len));
+      result += util::Utf16ToUtf8(StringPiece16(resource_name.type16, resource_name.type_len));
     }
   }
 
-  if (resource_name->entry != nullptr || resource_name->entry16 != nullptr) {
+  if (resource_name.entry != nullptr || resource_name.entry16 != nullptr) {
     if (!result.empty()) {
       result += "/";
     }
 
-    if (resource_name->entry != nullptr) {
-      result.append(resource_name->entry, resource_name->entry_len);
+    if (resource_name.entry != nullptr) {
+      result.append(resource_name.entry, resource_name.entry_len);
     } else {
-      result += util::Utf16ToUtf8(StringPiece16(resource_name->entry16, resource_name->entry_len));
+      result += util::Utf16ToUtf8(StringPiece16(resource_name.entry16, resource_name.entry_len));
     }
   }
 
diff --git a/libs/androidfw/StreamingZipInflater.cpp b/libs/androidfw/StreamingZipInflater.cpp
index b39b5f0..1c5e5d4 100644
--- a/libs/androidfw/StreamingZipInflater.cpp
+++ b/libs/androidfw/StreamingZipInflater.cpp
@@ -70,13 +70,13 @@
 /*
  * Streaming access to compressed data held in an mmapped region of memory
  */
-StreamingZipInflater::StreamingZipInflater(FileMap* dataMap, size_t uncompSize) {
+StreamingZipInflater::StreamingZipInflater(const incfs::IncFsFileMap* dataMap, size_t uncompSize) {
     mFd = -1;
     mDataMap = dataMap;
     mOutTotalSize = uncompSize;
-    mInTotalSize = dataMap->getDataLength();
+    mInTotalSize = dataMap->length();
 
-    mInBuf = (uint8_t*) dataMap->getDataPtr();
+    mInBuf = (uint8_t*) dataMap->unsafe_data(); // IncFs safety handled in zlib.
     mInBufSize = mInTotalSize;
 
     mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE;
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
index e77ac3d..52e7a70 100644
--- a/libs/androidfw/ZipFileRO.cpp
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -233,6 +233,29 @@
 }
 
 /*
+ * Create a new incfs::IncFsFileMap object that spans the data in "entry".
+ */
+std::optional<incfs::IncFsFileMap> ZipFileRO::createEntryIncFsFileMap(ZipEntryRO entry) const
+{
+    const _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
+    const ZipEntry& ze = zipEntry->entry;
+    int fd = GetFileDescriptor(mHandle);
+    size_t actualLen = 0;
+
+    if (ze.method == kCompressStored) {
+        actualLen = ze.uncompressed_length;
+    } else {
+        actualLen = ze.compressed_length;
+    }
+
+    incfs::IncFsFileMap newMap;
+    if (!newMap.Create(fd, ze.offset, actualLen, mFileName)) {
+        return std::nullopt;
+    }
+    return std::move(newMap);
+}
+
+/*
  * Uncompress an entry, in its entirety, into the provided output buffer.
  *
  * This doesn't verify the data's CRC, which might be useful for
diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
index 568e3b6..58fc5bb 100644
--- a/libs/androidfw/ZipUtils.cpp
+++ b/libs/androidfw/ZipUtils.cpp
@@ -40,7 +40,7 @@
     explicit FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) {
     }
 
-    bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
+    bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override {
         // Data is usually requested sequentially, so this helps avoid pointless
         // fseeks every time we perform a read. There's an impedence mismatch
         // here because the original API was designed around pread and pwrite.
@@ -71,7 +71,7 @@
     explicit FdReader(int fd) : mFd(fd) {
     }
 
-    bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
+    bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override {
       return android::base::ReadFullyAtOffset(mFd, buf, len, offset);
     }
 
@@ -81,22 +81,27 @@
 
 class BufferReader : public zip_archive::Reader {
   public:
-    BufferReader(const void* input, size_t inputSize) : Reader(),
-        mInput(reinterpret_cast<const uint8_t*>(input)),
+    BufferReader(incfs::map_ptr<void> input, size_t inputSize) : Reader(),
+        mInput(input.convert<uint8_t>()),
         mInputSize(inputSize) {
     }
 
-    bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
+    bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override {
         if (mInputSize < len || offset > mInputSize - len) {
             return false;
         }
 
-        memcpy(buf, mInput + offset, len);
+        const incfs::map_ptr<uint8_t> pos = mInput.offset(offset);
+        if (!pos.verify(len)) {
+          return false;
+        }
+
+        memcpy(buf, pos.unsafe_ptr(), len);
         return true;
     }
 
   private:
-    const uint8_t* mInput;
+    const incfs::map_ptr<uint8_t> mInput;
     const size_t mInputSize;
 };
 
@@ -138,7 +143,7 @@
     return (zip_archive::Inflate(reader, compressedLen, uncompressedLen, &writer, nullptr) == 0);
 }
 
-/*static*/ bool ZipUtils::inflateToBuffer(const void* in, void* buf,
+/*static*/ bool ZipUtils::inflateToBuffer(incfs::map_ptr<void> in, void* buf,
     long uncompressedLen, long compressedLen)
 {
     BufferReader reader(in, compressedLen);
diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp
index 96d44ab..5309ab2 100644
--- a/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp
+++ b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp
@@ -31,9 +31,6 @@
 using android::StringPiece;
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-
-  std::unique_ptr<const LoadedArsc> loaded_arsc =
-      LoadedArsc::Load(StringPiece(reinterpret_cast<const char*>(data), size));
-
+  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(data, size);
   return 0;
 }
\ No newline at end of file
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 298509e..80bae20 100644
--- a/libs/androidfw/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -23,18 +23,18 @@
 
 #include <stdio.h>
 #include <sys/types.h>
-
 #include <memory>
+#include <optional>
 
 #include <android-base/unique_fd.h>
+#include <util/map_ptr.h>
+
 #include <utils/Compat.h>
 #include <utils/Errors.h>
 #include <utils/String8.h>
 
 namespace android {
 
-class FileMap;
-
 /*
  * Instances of this class provide read-only operations on a byte stream.
  *
@@ -49,6 +49,8 @@
 class Asset {
 public:
     virtual ~Asset(void) = default;
+    Asset(const Asset& src) = delete;
+    Asset& operator=(const Asset& src) = delete;
 
     static int32_t getGlobalCount();
     static String8 getAssetAllocations();
@@ -87,8 +89,19 @@
 
     /*
      * Get a pointer to a buffer with the entire contents of the file.
+     * If `aligned` is true, the buffer data will be aligned to a 4-byte boundary.
+     *
+     * Use this function if the asset can never reside on IncFs.
      */
-    virtual const void* getBuffer(bool wordAligned) = 0;
+    virtual const void* getBuffer(bool aligned) = 0;
+
+    /*
+     * Get a incfs::map_ptr<void> to a buffer with the entire contents of the file.
+     * If `aligned` is true, the buffer data will be aligned to a 4-byte boundary.
+     *
+     * Use this function if the asset can potentially reside on IncFs.
+     */
+    virtual incfs::map_ptr<void> getIncFsBuffer(bool aligned) = 0;
 
     /*
      * Get the total amount of data that can be read.
@@ -152,10 +165,6 @@
     AccessMode getAccessMode(void) const { return mAccessMode; }
 
 private:
-    /* these operations are not implemented */
-    Asset(const Asset& src);
-    Asset& operator=(const Asset& src);
-
     /* AssetManager needs access to our "create" functions */
     friend class AssetManager;
     friend class ApkAssets;
@@ -169,8 +178,7 @@
     /*
      * Create the asset from a named, compressed file on disk (e.g. ".gz").
      */
-    static Asset* createFromCompressedFile(const char* fileName,
-        AccessMode mode);
+    static Asset* createFromCompressedFile(const char* fileName, AccessMode mode);
 
 #if 0
     /*
@@ -200,31 +208,21 @@
     /*
      * Create the asset from a memory-mapped file segment.
      *
-     * The asset takes ownership of the FileMap.
+     * The asset takes ownership of the incfs::IncFsFileMap and the file descriptor "fd". The
+     * file descriptor is used to request new file descriptors using "openFileDescriptor".
      */
-    static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode);
-
-    /*
-     * Create the asset from a memory-mapped file segment.
-     *
-     * The asset takes ownership of the FileMap and the file descriptor "fd". The file descriptor is
-     * used to request new file descriptors using "openFileDescriptor".
-     */
-    static std::unique_ptr<Asset> createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,
-        base::unique_fd fd, AccessMode mode);
+    static std::unique_ptr<Asset> createFromUncompressedMap(incfs::IncFsFileMap&& dataMap,
+                                                            AccessMode mode,
+                                                            base::unique_fd fd = {});
 
     /*
      * Create the asset from a memory-mapped file segment with compressed
      * data.
      *
-     * The asset takes ownership of the FileMap.
+     * The asset takes ownership of the incfs::IncFsFileMap.
      */
-    static Asset* createFromCompressedMap(FileMap* dataMap,
-        size_t uncompressedLen, AccessMode mode);
-
-    static std::unique_ptr<Asset> createFromCompressedMap(std::unique_ptr<FileMap> dataMap,
-        size_t uncompressedLen, AccessMode mode);
-
+    static std::unique_ptr<Asset> createFromCompressedMap(incfs::IncFsFileMap&& dataMap,
+                                                          size_t uncompressedLen, AccessMode mode);
 
     /*
      * Create from a reference-counted chunk of shared memory.
@@ -252,7 +250,7 @@
 class _FileAsset : public Asset {
 public:
     _FileAsset(void);
-    virtual ~_FileAsset(void);
+    ~_FileAsset(void) override;
 
     /*
      * Use a piece of an already-open file.
@@ -266,21 +264,24 @@
      *
      * On success, the object takes ownership of "dataMap" and "fd".
      */
-    status_t openChunk(FileMap* dataMap, base::unique_fd fd);
+    status_t openChunk(incfs::IncFsFileMap&& dataMap, base::unique_fd fd);
 
     /*
      * Standard Asset interfaces.
      */
-    virtual ssize_t read(void* buf, size_t count);
-    virtual off64_t seek(off64_t offset, int whence);
-    virtual void close(void);
-    virtual const void* getBuffer(bool wordAligned);
-    virtual off64_t getLength(void) const { return mLength; }
-    virtual off64_t getRemainingLength(void) const { return mLength-mOffset; }
-    virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const;
-    virtual bool isAllocated(void) const { return mBuf != NULL; }
+    ssize_t read(void* buf, size_t count) override;
+    off64_t seek(off64_t offset, int whence) override;
+    void close(void) override;
+    const void* getBuffer(bool aligned) override;
+    incfs::map_ptr<void> getIncFsBuffer(bool aligned) override;
+    off64_t getLength(void) const override { return mLength; }
+    off64_t getRemainingLength(void) const override { return mLength-mOffset; }
+    int openFileDescriptor(off64_t* outStart, off64_t* outLength) const override;
+    bool isAllocated(void) const override { return mBuf != NULL; }
 
 private:
+    incfs::map_ptr<void> ensureAlignment(const incfs::IncFsFileMap& map);
+
     off64_t         mStart;         // absolute file offset of start of chunk
     off64_t         mLength;        // length of the chunk
     off64_t         mOffset;        // current local offset, 0 == mStart
@@ -295,10 +296,8 @@
      */
     enum { kReadVsMapThreshold = 4096 };
 
-    FileMap*    mMap;           // for memory map
-    unsigned char* mBuf;        // for read
-
-    const void* ensureAlignment(FileMap* map);
+    unsigned char*                      mBuf;     // for read
+    std::optional<incfs::IncFsFileMap>  mMap;     // for memory map
 };
 
 
@@ -323,7 +322,7 @@
      *
      * On success, the object takes ownership of "fd".
      */
-    status_t openChunk(FileMap* dataMap, size_t uncompressedLen);
+    status_t openChunk(incfs::IncFsFileMap&& dataMap, size_t uncompressedLen);
 
     /*
      * Standard Asset interfaces.
@@ -331,24 +330,23 @@
     virtual ssize_t read(void* buf, size_t count);
     virtual off64_t seek(off64_t offset, int whence);
     virtual void close(void);
-    virtual const void* getBuffer(bool wordAligned);
+    virtual const void* getBuffer(bool aligned);
+    virtual incfs::map_ptr<void> getIncFsBuffer(bool aligned);
     virtual off64_t getLength(void) const { return mUncompressedLen; }
     virtual off64_t getRemainingLength(void) const { return mUncompressedLen-mOffset; }
     virtual int openFileDescriptor(off64_t* /* outStart */, off64_t* /* outLength */) const { return -1; }
     virtual bool isAllocated(void) const { return mBuf != NULL; }
 
 private:
-    off64_t     mStart;         // offset to start of compressed data
-    off64_t     mCompressedLen; // length of the compressed data
-    off64_t     mUncompressedLen; // length of the uncompressed data
-    off64_t     mOffset;        // current offset, 0 == start of uncomp data
+    off64_t mStart;           // offset to start of compressed data
+    off64_t mCompressedLen;   // length of the compressed data
+    off64_t mUncompressedLen; // length of the uncompressed data
+    off64_t mOffset;          // current offset, 0 == start of uncomp data
+    int     mFd;              // for file input
 
-    FileMap*    mMap;           // for memory-mapped input
-    int         mFd;            // for file input
-
-    class StreamingZipInflater* mZipInflater;  // for streaming large compressed assets
-
-    unsigned char*  mBuf;       // for getBuffer()
+    class StreamingZipInflater*         mZipInflater; // for streaming large compressed assets
+    unsigned char*                      mBuf;         // for getBuffer()
+    std::optional<incfs::IncFsFileMap>  mMap;         // for memory-mapped input
 };
 
 // need: shared mmap version?
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 30ef25c..4e993b0 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -131,8 +131,8 @@
   bool GetOverlayablesToString(const android::StringPiece& package_name,
                                std::string* out) const;
 
-  const std::unordered_map<std::string, std::string>*
-    GetOverlayableMapForPackage(uint32_t package_id) const;
+  const std::unordered_map<std::string, std::string>* GetOverlayableMapForPackage(
+      uint32_t package_id) const;
 
   // Returns whether the resources.arsc of any loaded apk assets is allocated in RAM (not mmapped).
   bool ContainsAllocatedTable() const;
@@ -145,14 +145,16 @@
     return configuration_;
   }
 
-  // Returns all configurations for which there are resources defined. This includes resource
-  // configurations in all the ApkAssets set for this AssetManager.
+  // Returns all configurations for which there are resources defined, or an I/O error if reading
+  // resource data failed.
+  //
+  // This includes resource configurations in all the ApkAssets set for this AssetManager.
   // If `exclude_system` is set to true, resource configurations from system APKs
   // ('android' package, other libraries) will be excluded from the list.
   // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap'
   // will be excluded from the list.
-  std::set<ResTable_config> GetResourceConfigurations(bool exclude_system = false,
-                                                      bool exclude_mipmap = false) const;
+  base::expected<std::set<ResTable_config>, IOError> GetResourceConfigurations(
+      bool exclude_system = false, bool exclude_mipmap = false) const;
 
   // Returns all the locales for which there are resources defined. This includes resource
   // locales in all the ApkAssets set for this AssetManager.
@@ -194,53 +196,106 @@
   std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie,
                                       Asset::AccessMode mode) const;
 
-  // Populates the `out_name` parameter with resource name information.
-  // Utf8 strings are preferred, and only if they are unavailable are
-  // the Utf16 variants populated.
-  // Returns false if the resource was not found or the name was missing/corrupt.
-  bool GetResourceName(uint32_t resid, ResourceName* out_name) const;
-
-  // Populates `out_flags` with the bitmask of configuration axis that this resource varies with.
-  // See ResTable_config for the list of configuration axis.
-  // Returns false if the resource was not found.
-  bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const;
+  // Returns the resource name of the specified resource ID.
+  //
+  // Utf8 strings are preferred, and only if they are unavailable are the Utf16 variants populated.
+  //
+  // Returns a null error if the name is missing/corrupt, or an I/O error if reading resource data
+  // failed.
+  base::expected<ResourceName, NullOrIOError> GetResourceName(uint32_t resid) const;
 
   // Finds the resource ID assigned to `resource_name`.
+  //
   // `resource_name` must be of the form '[package:][type/]entry'.
   // If no package is specified in `resource_name`, then `fallback_package` is used as the package.
   // If no type is specified in `resource_name`, then `fallback_type` is used as the type.
-  // Returns 0x0 if no resource by that name was found.
-  uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {},
-                         const std::string& fallback_package = {}) const;
-
-  // Retrieves the best matching resource with ID `resid`. The resource value is filled into
-  // `out_value` and the configuration for the selected value is populated in `out_selected_config`.
-  // `out_flags` holds the same flags as retrieved with GetResourceFlags().
-  // If `density_override` is non-zero, the configuration to match against is overridden with that
-  // density.
   //
-  // Returns a valid cookie if the resource was found. If the resource was not found, or if the
-  // resource was a map/bag type, then kInvalidCookie is returned. If `may_be_bag` is false,
-  // this function logs if the resource was a map/bag type before returning kInvalidCookie.
-  ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override,
-                              Res_value* out_value, ResTable_config* out_selected_config,
-                              uint32_t* out_flags) const;
+  // Returns a null error if no resource by that name was found, or an I/O error if reading resource
+  // data failed.
+  base::expected<uint32_t, NullOrIOError> GetResourceId(
+      const std::string& resource_name, const std::string& fallback_type = {},
+      const std::string& fallback_package = {}) const;
 
-  // Resolves the resource reference in `in_out_value` if the data type is
-  // Res_value::TYPE_REFERENCE.
-  // `cookie` is the ApkAssetsCookie of the reference in `in_out_value`.
-  // `in_out_value` is the reference to resolve. The result is placed back into this object.
-  // `in_out_flags` is the type spec flags returned from calls to GetResource() or
-  // GetResourceFlags(). Configuration flags of the values pointed to by the reference
-  // are OR'd together with `in_out_flags`.
-  // `in_out_config` is populated with the configuration for which the resolved value was defined.
-  // `out_last_reference` is populated with the last reference ID before resolving to an actual
-  // value. This is only initialized if the passed in `in_out_value` is a reference.
-  // Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if
-  // it was not found.
-  ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
-                                   ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
-                                   uint32_t* out_last_reference) const;
+  struct SelectedValue {
+    friend AssetManager2;
+    friend Theme;
+    SelectedValue() = default;
+    SelectedValue(const ResolvedBag* bag, const ResolvedBag::Entry& entry) :
+        cookie(entry.cookie), data(entry.value.data), type(entry.value.dataType),
+        flags(bag->type_spec_flags), resid(0U), config({}) {};
+
+    // The cookie representing the ApkAssets in which the value resides.
+    ApkAssetsCookie cookie = kInvalidCookie;
+
+    // The data for this value, as interpreted according to `type`.
+    Res_value::data_type data;
+
+    // Type of the data value.
+    uint8_t type;
+
+    // The bitmask of configuration axis that this resource varies with.
+    // See ResTable_config::CONFIG_*.
+    uint32_t flags;
+
+    // The resource ID from which this value was resolved.
+    uint32_t resid;
+
+    // The configuration for which the resolved value was defined.
+    ResTable_config config;
+
+   private:
+    SelectedValue(uint8_t value_type, Res_value::data_type value_data, ApkAssetsCookie cookie,
+                  uint32_t type_flags, uint32_t resid, const ResTable_config& config) :
+                  cookie(cookie), data(value_data), type(value_type), flags(type_flags),
+                  resid(resid), config(config) {};
+  };
+
+  // Retrieves the best matching resource value with ID `resid`.
+  //
+  // If `may_be_bag` is false, this function logs if the resource was a map/bag type and returns a
+  // null result. If `density_override` is non-zero, the configuration to match against is
+  // overridden with that density.
+  //
+  // Returns a null error if a best match could not be found, or an I/O error if reading resource
+  // data failed.
+  base::expected<SelectedValue, NullOrIOError> GetResource(uint32_t resid, bool may_be_bag = false,
+                                                           uint16_t density_override = 0U) const;
+
+  // Resolves the resource referenced in `value` if the type is Res_value::TYPE_REFERENCE.
+  //
+  // If the data type is not Res_value::TYPE_REFERENCE, no work is done. Configuration flags of the
+  // values pointed to by the reference are OR'd into `value.flags`.
+  //
+  // Returns a null error if the resource could not be resolved, or an I/O error if reading
+  // resource data failed.
+  base::expected<std::monostate, NullOrIOError> ResolveReference(SelectedValue& value) const;
+
+  // Retrieves the best matching bag/map resource with ID `resid`.
+  //
+  // This method will resolve all parent references for this bag and merge keys with the child.
+  // To iterate over the keys, use the following idiom:
+  //
+  //  base::expected<const ResolvedBag*, NullOrIOError> bag = asset_manager->GetBag(id);
+  //  if (bag.has_value()) {
+  //    for (auto iter = begin(*bag); iter != end(*bag); ++iter) {
+  //      ...
+  //    }
+  //  }
+  //
+  // Returns a null error if a best match could not be found, or an I/O error if reading resource
+  // data failed.
+  base::expected<const ResolvedBag*, NullOrIOError> GetBag(uint32_t resid) const;
+
+  // Retrieves the best matching bag/map resource of the resource referenced in `value`.
+  //
+  // If `value.type` is not Res_value::TYPE_REFERENCE, a null result is returned.
+  // Configuration flags of the bag pointed to by the reference are OR'd into `value.flags`.
+  //
+  // Returns a null error if a best match could not be found, or an I/O error if reading resource
+  // data failed.
+  base::expected<const ResolvedBag*, NullOrIOError> ResolveBag(SelectedValue& value) const;
+
+  const std::vector<uint32_t> GetBagResIdStack(uint32_t resid) const;
 
   // Resets the resource resolution structures in preparation for the next resource retrieval.
   void ResetResourceResolution() const;
@@ -252,20 +307,6 @@
   // resolved yet.
   std::string GetLastResourceResolution() const;
 
-  const std::vector<uint32_t> GetBagResIdStack(uint32_t resid);
-
-  // Retrieves the best matching bag/map resource with ID `resid`.
-  // This method will resolve all parent references for this bag and merge keys with the child.
-  // To iterate over the keys, use the following idiom:
-  //
-  //  const AssetManager2::ResolvedBag* bag = asset_manager->GetBag(id);
-  //  if (bag != nullptr) {
-  //    for (auto iter = begin(bag); iter != end(bag); ++iter) {
-  //      ...
-  //    }
-  //  }
-  const ResolvedBag* GetBag(uint32_t resid);
-
   // Creates a new Theme from this AssetManager.
   std::unique_ptr<Theme> NewTheme();
 
@@ -286,11 +327,15 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(AssetManager2);
 
+  struct TypeConfig {
+    incfs::verified_map_ptr<ResTable_type> type;
+    ResTable_config config;
+  };
+
   // A collection of configurations and their associated ResTable_type that match the current
   // AssetManager configuration.
   struct FilteredConfigGroup {
-      std::vector<ResTable_config> configurations;
-      std::vector<const ResTable_type*> types;
+      std::vector<TypeConfig> type_configs;
   };
 
   // Represents an single package.
@@ -331,9 +376,7 @@
   };
 
   // Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple
-  // Res_value, or a complex map/bag type. If successful, it is available in `out_entry`.
-  // Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with
-  // the ApkAssets in which the entry was found.
+  // Res_value, or a complex map/bag type. Returns a null result if a best entry cannot be found.
   //
   // `density_override` overrides the density of the current configuration when doing a search.
   //
@@ -347,13 +390,15 @@
   //
   // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly
   // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds.
-  ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
-                            bool ignore_configuration, FindEntryResult* out_entry) const;
+  base::expected<FindEntryResult, NullOrIOError> FindEntry(uint32_t resid,
+                                                           uint16_t density_override,
+                                                           bool stop_at_first_match,
+                                                           bool ignore_configuration) const;
 
-  ApkAssetsCookie FindEntryInternal(const PackageGroup& package_group, uint8_t type_idx,
-                                    uint16_t entry_idx, const ResTable_config& desired_config,
-                                    bool /*stop_at_first_match*/,
-                                    bool ignore_configuration, FindEntryResult* out_entry) const;
+  base::expected<FindEntryResult, NullOrIOError> FindEntryInternal(
+      const PackageGroup& package_group, uint8_t type_idx, uint16_t entry_idx,
+      const ResTable_config& desired_config, bool stop_at_first_match,
+      bool ignore_configuration) const;
 
   // Assigns package IDs to all shared library ApkAssets.
   // Should be called whenever the ApkAssets are changed.
@@ -372,7 +417,8 @@
 
   // AssetManager2::GetBag(resid) wraps this function to track which resource ids have already
   // been seen while traversing bag parents.
-  const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids);
+  base::expected<const ResolvedBag*, NullOrIOError> GetBag(
+      uint32_t resid, std::vector<uint32_t>& child_resids) const;
 
   // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
   // have a longer lifetime.
@@ -394,11 +440,11 @@
 
   // Cached set of bags. These are cached because they can inherit keys from parent bags,
   // which involves some calculation.
-  std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
+  mutable std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
 
   // Cached set of bag resid stacks for each bag. These are cached because they might be requested
   // a number of times for each view during View inspection.
-  std::unordered_map<uint32_t, std::vector<uint32_t>> cached_bag_resid_stacks_;
+  mutable std::unordered_map<uint32_t, std::vector<uint32_t>> cached_bag_resid_stacks_;
 
   // Whether or not to save resource resolution steps
   bool resource_resolution_logging_enabled_ = false;
@@ -455,55 +501,53 @@
  public:
   ~Theme();
 
-  // Applies the style identified by `resid` to this theme. This can be called
-  // multiple times with different styles. By default, any theme attributes that
-  // are already defined before this call are not overridden. If `force` is set
-  // to true, this behavior is changed and all theme attributes from the style at
-  // `resid` are applied.
-  // Returns false if the style failed to apply.
-  bool ApplyStyle(uint32_t resid, bool force = false);
+  // Applies the style identified by `resid` to this theme.
+  //
+  // This can be called multiple times with different styles. By default, any theme attributes that
+  // are already defined before this call are not overridden. If `force` is set to true, this
+  // behavior is changed and all theme attributes from the style at `resid` are applied.
+  //
+  // Returns a null error if the style could not be applied, or an I/O error if reading resource
+  // data failed.
+  base::expected<std::monostate, NullOrIOError> ApplyStyle(uint32_t resid, bool force = false);
 
-  // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme.
-  // If `o` does not have the same AssetManager as this theme, only attributes from ApkAssets loaded
-  // into both AssetManagers will be copied to this theme.
-  void SetTo(const Theme& o);
+  // Sets this Theme to be a copy of `other` if `other` has the same AssetManager as this Theme.
+  //
+  // If `other` does not have the same AssetManager as this theme, only attributes from ApkAssets
+  // loaded into both AssetManagers will be copied to this theme.
+  //
+  // Returns an I/O error if reading resource data failed.
+  base::expected<std::monostate, IOError> SetTo(const Theme& other);
 
   void Clear();
 
-  void Dump() const;
+  // Retrieves the value of attribute ID `resid` in the theme.
+  //
+  // NOTE: This function does not do reference traversal. If you want to follow references to other
+  // resources to get the "real" value to use, you need to call ResolveReference() after this
+  // function.
+  std::optional<AssetManager2::SelectedValue> GetAttribute(uint32_t resid) const;
 
-  inline const AssetManager2* GetAssetManager() const {
+  // This is like AssetManager2::ResolveReference(), but also takes care of resolving attribute
+  // references to the theme.
+  base::expected<std::monostate, NullOrIOError> ResolveAttributeReference(
+      AssetManager2::SelectedValue& value) const;
+
+  AssetManager2* GetAssetManager() {
     return asset_manager_;
   }
 
-  inline AssetManager2* GetAssetManager() {
+  const AssetManager2* GetAssetManager() const {
     return asset_manager_;
   }
 
   // Returns a bit mask of configuration changes that will impact this
   // theme (and thus require completely reloading it).
-  inline uint32_t GetChangingConfigurations() const {
+  uint32_t GetChangingConfigurations() const {
     return type_spec_flags_;
   }
 
-  // Retrieve a value in the theme. If the theme defines this value, returns an asset cookie
-  // indicating which ApkAssets it came from and populates `out_value` with the value.
-  // `out_flags` is populated with a bitmask of the configuration axis with which the resource
-  // varies.
-  //
-  // If the attribute is not found, returns kInvalidCookie.
-  //
-  // NOTE: This function does not do reference traversal. If you want to follow references to other
-  // resources to get the "real" value to use, you need to call ResolveReference() after this
-  // function.
-  ApkAssetsCookie GetAttribute(uint32_t resid, Res_value* out_value, uint32_t* out_flags) const;
-
-  // This is like AssetManager2::ResolveReference(), but also takes
-  // care of resolving attribute references to the theme.
-  ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
-                                            ResTable_config* in_out_selected_config = nullptr,
-                                            uint32_t* in_out_type_spec_flags = nullptr,
-                                            uint32_t* out_last_ref = nullptr) const;
+  void Dump() const;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(Theme);
diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h
index d71aad2..1a69a30 100644
--- a/libs/androidfw/include/androidfw/AttributeResolution.h
+++ b/libs/androidfw/include/androidfw/AttributeResolution.h
@@ -45,20 +45,28 @@
 
 // `out_values` must NOT be nullptr.
 // `out_indices` may be nullptr.
-bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid,
-                  uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
-                  size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
+base::expected<std::monostate, IOError> ResolveAttrs(Theme* theme, uint32_t def_style_attr,
+                                                     uint32_t def_style_resid, uint32_t* src_values,
+                                                     size_t src_values_length, uint32_t* attrs,
+                                                     size_t attrs_length, uint32_t* out_values,
+                                                     uint32_t* out_indices);
 
 // `out_values` must NOT be nullptr.
 // `out_indices` is NOT optional and must NOT be nullptr.
-void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
-                uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length,
-                uint32_t* out_values, uint32_t* out_indices);
+base::expected<std::monostate, IOError> ApplyStyle(Theme* theme, ResXMLParser* xml_parser,
+                                                   uint32_t def_style_attr,
+                                                   uint32_t def_style_resid,
+                                                   const uint32_t* attrs, size_t attrs_length,
+                                                   uint32_t* out_values, uint32_t* out_indices);
 
 // `out_values` must NOT be nullptr.
 // `out_indices` may be nullptr.
-bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs,
-                        size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
+base::expected<std::monostate, IOError> RetrieveAttributes(AssetManager2* assetmanager,
+                                                           ResXMLParser* xml_parser,
+                                                           uint32_t* attrs,
+                                                           size_t attrs_length,
+                                                           uint32_t* out_values,
+                                                           uint32_t* out_indices);
 
 }  // namespace android
 
diff --git a/libs/androidfw/include/androidfw/Chunk.h b/libs/androidfw/include/androidfw/Chunk.h
index a0f2343..f1c43b2 100644
--- a/libs/androidfw/include/androidfw/Chunk.h
+++ b/libs/androidfw/include/androidfw/Chunk.h
@@ -36,7 +36,7 @@
 // of the chunk.
 class Chunk {
  public:
-  explicit Chunk(const ResChunk_header* chunk) : device_chunk_(chunk) {}
+  explicit Chunk(incfs::verified_map_ptr<ResChunk_header> chunk) : device_chunk_(chunk) {}
 
   // Returns the type of the chunk. Caller need not worry about endianness.
   inline int type() const { return dtohs(device_chunk_->type); }
@@ -49,21 +49,18 @@
   inline size_t header_size() const { return dtohs(device_chunk_->headerSize); }
 
   template <typename T, size_t MinSize = sizeof(T)>
-  inline const T* header() const {
-    if (header_size() >= MinSize) {
-      return reinterpret_cast<const T*>(device_chunk_);
-    }
-    return nullptr;
+  inline incfs::map_ptr<T> header() const {
+    return (header_size() >= MinSize) ? device_chunk_.convert<T>() : nullptr;
   }
 
-  inline const void* data_ptr() const {
-    return reinterpret_cast<const uint8_t*>(device_chunk_) + header_size();
+  inline incfs::map_ptr<void> data_ptr() const {
+    return device_chunk_.offset(header_size());
   }
 
   inline size_t data_size() const { return size() - header_size(); }
 
  private:
-  const ResChunk_header* device_chunk_;
+  const incfs::verified_map_ptr<ResChunk_header> device_chunk_;
 };
 
 // Provides a Java style iterator over an array of ResChunk_header's.
@@ -84,11 +81,11 @@
 //
 class ChunkIterator {
  public:
-  ChunkIterator(const void* data, size_t len)
-      : next_chunk_(reinterpret_cast<const ResChunk_header*>(data)),
+  ChunkIterator(incfs::map_ptr<void> data, size_t len)
+      : next_chunk_(data.convert<ResChunk_header>()),
         len_(len),
         last_error_(nullptr) {
-    CHECK(next_chunk_ != nullptr) << "data can't be nullptr";
+    CHECK((bool) next_chunk_) << "data can't be null";
     if (len_ != 0) {
       VerifyNextChunk();
     }
@@ -113,7 +110,7 @@
   // Returns false if there was an error. For legacy purposes.
   bool VerifyNextChunkNonFatal();
 
-  const ResChunk_header* next_chunk_;
+  incfs::map_ptr<ResChunk_header> next_chunk_;
   size_t len_;
   const char* last_error_;
   bool last_error_was_fatal_ = true;
diff --git a/libs/androidfw/include/androidfw/Errors.h b/libs/androidfw/include/androidfw/Errors.h
new file mode 100644
index 0000000..948162d
--- /dev/null
+++ b/libs/androidfw/include/androidfw/Errors.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROIDFW_ERRORS_H_
+#define ANDROIDFW_ERRORS_H_
+
+#include <optional>
+#include <variant>
+
+#include <android-base/result.h>
+
+namespace android {
+
+enum class IOError {
+  // Used when reading a file residing on an IncFs file-system times out.
+  PAGES_MISSING = -1,
+};
+
+// Represents an absent result or an I/O error.
+using NullOrIOError = std::variant<std::nullopt_t, IOError>;
+
+// Checks whether the result holds an unexpected I/O error.
+template <typename T>
+static inline bool IsIOError(const base::expected<T, NullOrIOError> result) {
+  return !result.has_value() && std::holds_alternative<IOError>(result.error());
+}
+
+static inline IOError GetIOError(const NullOrIOError& error) {
+  return std::get<IOError>(error);
+}
+
+} // namespace android
+
+#endif //ANDROIDFW_ERRORS_H_
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index ab0f47f..fdab03b 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -40,8 +40,8 @@
 class OverlayStringPool : public ResStringPool {
  public:
   virtual ~OverlayStringPool();
-  const char16_t* stringAt(size_t idx, size_t* outLen) const override;
-  const char* string8At(size_t idx, size_t* outLen) const override;
+  base::expected<StringPiece16, NullOrIOError> stringAt(size_t idx) const override;
+  base::expected<StringPiece, NullOrIOError> string8At(size_t idx) const override;
   size_t size() const override;
 
   explicit OverlayStringPool(const LoadedIdmap* loaded_idmap);
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 89ff9f5..17d97a2 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -23,7 +23,8 @@
 #include <unordered_map>
 #include <unordered_set>
 
-#include "android-base/macros.h"
+#include <android-base/macros.h>
+#include <android-base/result.h>
 
 #include "androidfw/ByteBucketArray.h"
 #include "androidfw/Chunk.h"
@@ -49,7 +50,7 @@
   // Pointer to the mmapped data where flags are kept.
   // Flags denote whether the resource entry is public
   // and under which configurations it varies.
-  const ResTable_typeSpec* type_spec;
+  incfs::verified_map_ptr<ResTable_typeSpec> type_spec;
 
   // The number of types that follow this struct.
   // There is a type for each configuration that entries are defined for.
@@ -57,15 +58,17 @@
 
   // Trick to easily access a variable number of Type structs
   // proceeding this struct, and to ensure their alignment.
-  const ResTable_type* types[0];
+  incfs::verified_map_ptr<ResTable_type> types[0];
 
-  inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const {
+  base::expected<uint32_t, NullOrIOError> GetFlagsForEntryIndex(uint16_t entry_index) const {
     if (entry_index >= dtohl(type_spec->entryCount)) {
-      return 0u;
+      return 0U;
     }
-
-    const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec + 1);
-    return flags[entry_index];
+    const auto entry_flags_ptr = ((type_spec + 1).convert<uint32_t>() + entry_index);
+    if (!entry_flags_ptr) {
+      return base::unexpected(IOError::PAGES_MISSING);
+    }
+    return entry_flags_ptr.value();
   }
 };
 
@@ -161,13 +164,17 @@
   // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change.
   // Returns a partial resource ID, with the package ID left as 0x00. The caller is responsible
   // for patching the correct package ID to the resource ID.
-  uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const;
+  base::expected<uint32_t, NullOrIOError> FindEntryByName(const std::u16string& type_name,
+                                                          const std::u16string& entry_name) const;
 
-  static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index);
+  static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntry(
+      incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index);
 
-  static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index);
+  static base::expected<uint32_t, NullOrIOError> GetEntryOffset(
+      incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index);
 
-  static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset);
+  static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntryFromOffset(
+      incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset);
 
   // Returns the string pool where type names are stored.
   inline const ResStringPool* GetTypeStringPool() const {
@@ -220,7 +227,8 @@
 
   // Populates a set of ResTable_config structs, possibly excluding configurations defined for
   // the mipmap type.
-  void CollectConfigurations(bool exclude_mipmap, std::set<ResTable_config>* out_configs) const;
+  base::expected<std::monostate, IOError> CollectConfigurations(
+      bool exclude_mipmap, std::set<ResTable_config>* out_configs) const;
 
   // Populates a set of strings representing locales.
   // If `canonicalize` is set to true, each locale is transformed into its canonical format
@@ -300,7 +308,8 @@
   // If `load_as_shared_library` is set to true, the application package (0x7f) is treated
   // as a shared library (0x00). When loaded into an AssetManager, the package will be assigned an
   // ID.
-  static std::unique_ptr<const LoadedArsc> Load(const StringPiece& data,
+  static std::unique_ptr<const LoadedArsc> Load(incfs::map_ptr<void> data,
+                                                size_t length,
                                                 const LoadedIdmap* loaded_idmap = nullptr,
                                                 package_property_t property_flags = 0U);
 
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 04ba78b..fb5f864 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -20,7 +20,10 @@
 #ifndef _LIBS_UTILS_RESOURCE_TYPES_H
 #define _LIBS_UTILS_RESOURCE_TYPES_H
 
+#include <android-base/expected.h>
+
 #include <androidfw/Asset.h>
+#include <androidfw/Errors.h>
 #include <androidfw/LocaleData.h>
 #include <androidfw/StringPiece.h>
 #include <utils/Errors.h>
@@ -497,7 +500,7 @@
     virtual ~ResStringPool();
 
     void setToEmpty();
-    status_t setTo(const void* data, size_t size, bool copyData=false);
+    status_t setTo(incfs::map_ptr<void> data, size_t size, bool copyData=false);
 
     status_t getError() const;
 
@@ -505,48 +508,49 @@
 
     // Return string entry as UTF16; if the pool is UTF8, the string will
     // be converted before returning.
-    inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const {
-        return stringAt(ref.index, outLen);
+    inline base::expected<StringPiece16, NullOrIOError> stringAt(
+            const ResStringPool_ref& ref) const {
+        return stringAt(ref.index);
     }
-    virtual const char16_t* stringAt(size_t idx, size_t* outLen) const;
+    virtual base::expected<StringPiece16, NullOrIOError> stringAt(size_t idx) const;
 
     // Note: returns null if the string pool is not UTF8.
-    virtual const char* string8At(size_t idx, size_t* outLen) const;
+    virtual base::expected<StringPiece, NullOrIOError> string8At(size_t idx) const;
 
     // Return string whether the pool is UTF8 or UTF16.  Does not allow you
     // to distinguish null.
-    const String8 string8ObjectAt(size_t idx) const;
+    base::expected<String8, IOError> string8ObjectAt(size_t idx) const;
 
-    const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const;
-    const ResStringPool_span* styleAt(size_t idx) const;
+    base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> styleAt(
+        const ResStringPool_ref& ref) const;
+    base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> styleAt(size_t idx) const;
 
-    ssize_t indexOfString(const char16_t* str, size_t strLen) const;
+    base::expected<size_t, NullOrIOError> indexOfString(const char16_t* str, size_t strLen) const;
 
     virtual size_t size() const;
     size_t styleCount() const;
     size_t bytes() const;
-    const void* data() const;
-
+    incfs::map_ptr<void> data() const;
 
     bool isSorted() const;
     bool isUTF8() const;
 
 private:
-    status_t                    mError;
-    void*                       mOwnedData;
-    const ResStringPool_header* mHeader;
-    size_t                      mSize;
-    mutable Mutex               mDecodeLock;
-    const uint32_t*             mEntries;
-    const uint32_t*             mEntryStyles;
-    const void*                 mStrings;
-    char16_t mutable**          mCache;
-    uint32_t                    mStringPoolSize;    // number of uint16_t
-    const uint32_t*             mStyles;
-    uint32_t                    mStylePoolSize;    // number of uint32_t
+    status_t                                      mError;
+    void*                                         mOwnedData;
+    incfs::verified_map_ptr<ResStringPool_header> mHeader;
+    size_t                                        mSize;
+    mutable Mutex                                 mDecodeLock;
+    incfs::map_ptr<uint32_t>                      mEntries;
+    incfs::map_ptr<uint32_t>                      mEntryStyles;
+    incfs::map_ptr<void>                          mStrings;
+    char16_t mutable**                            mCache;
+    uint32_t                                      mStringPoolSize;    // number of uint16_t
+    incfs::map_ptr<uint32_t>                      mStyles;
+    uint32_t                                      mStylePoolSize;    // number of uint32_t
 
-    const char* stringDecodeAt(size_t idx, const uint8_t* str, const size_t encLen,
-                               size_t* outLen) const;
+    base::expected<StringPiece, NullOrIOError> stringDecodeAt(
+        size_t idx, incfs::map_ptr<uint8_t> str, size_t encLen) const;
 };
 
 /**
@@ -558,8 +562,8 @@
  StringPoolRef() = default;
  StringPoolRef(const ResStringPool* pool, uint32_t index);
 
- const char* string8(size_t* outLen) const;
- const char16_t* string16(size_t* outLen) const;
+ base::expected<StringPiece, NullOrIOError> string8() const;
+ base::expected<StringPiece16, NullOrIOError> string16() const;
 
 private:
  const ResStringPool* mPool = nullptr;
@@ -1797,6 +1801,16 @@
 
 bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue);
 
+template<typename TChar, typename E>
+static const TChar* UnpackOptionalString(base::expected<BasicStringPiece<TChar>, E>&& result,
+                                         size_t* outLen) {
+  if (result.has_value()) {
+    *outLen = result->size();
+    return result->data();
+  }
+  return NULL;
+}
+
 /**
  * Convenience class for accessing data in a ResTable resource.
  */
diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h
index e649940..bd1c440 100644
--- a/libs/androidfw/include/androidfw/ResourceUtils.h
+++ b/libs/androidfw/include/androidfw/ResourceUtils.h
@@ -30,13 +30,12 @@
 
 // Convert a type_string_ref, entry_string_ref, and package to AssetManager2::ResourceName.
 // Useful for getting resource name without re-running AssetManager2::FindEntry searches.
-bool ToResourceName(const StringPoolRef& type_string_ref,
-                    const StringPoolRef& entry_string_ref,
-                    const StringPiece& package_name,
-                    AssetManager2::ResourceName* out_name);
+base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName(
+    const StringPoolRef& type_string_ref, const StringPoolRef& entry_string_ref,
+    const StringPiece& package_name);
 
 // Formats a ResourceName to "package:type/entry_name".
-std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name);
+std::string ToFormattedResourceString(const AssetManager2::ResourceName& resource_name);
 
 inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) {
   return (resid & 0x00ffffffu) | (static_cast<uint32_t>(package_id) << 24);
diff --git a/libs/androidfw/include/androidfw/StreamingZipInflater.h b/libs/androidfw/include/androidfw/StreamingZipInflater.h
index 3ace5d5..472b794b 100644
--- a/libs/androidfw/include/androidfw/StreamingZipInflater.h
+++ b/libs/androidfw/include/androidfw/StreamingZipInflater.h
@@ -19,6 +19,8 @@
 
 #include <unistd.h>
 #include <inttypes.h>
+
+#include <util/map_ptr.h>
 #include <zlib.h>
 
 #include <utils/Compat.h>
@@ -34,7 +36,7 @@
     StreamingZipInflater(int fd, off64_t compDataStart, size_t uncompSize, size_t compSize);
 
     // Flavor that gets the compressed data from an in-memory buffer
-    StreamingZipInflater(class FileMap* dataMap, size_t uncompSize);
+    StreamingZipInflater(const incfs::IncFsFileMap* dataMap, size_t uncompSize);
 
     ~StreamingZipInflater();
 
@@ -54,7 +56,7 @@
     // where to find the uncompressed data
     int mFd;
     off64_t mInFileStart;         // where the compressed data lives in the file
-    class FileMap* mDataMap;
+    const incfs::IncFsFileMap* mDataMap;
 
     z_stream mInflateState;
     bool mStreamNeedsInit;
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index 9a3646b..aceeecc 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -22,7 +22,8 @@
 #include <sstream>
 #include <vector>
 
-#include "android-base/macros.h"
+#include <android-base/macros.h>
+#include <util/map_ptr.h>
 
 #include "androidfw/StringPiece.h"
 
@@ -126,6 +127,11 @@
 
 std::vector<std::string> SplitAndLowercase(const android::StringPiece& str, char sep);
 
+template <typename T>
+bool IsFourByteAligned(const incfs::map_ptr<T>& data) {
+  return ((size_t)data.unsafe_ptr() & 0x3U) == 0;
+}
+
 }  // namespace util
 }  // namespace android
 
diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h
index c221e3b..10f6d06 100644
--- a/libs/androidfw/include/androidfw/ZipFileRO.h
+++ b/libs/androidfw/include/androidfw/ZipFileRO.h
@@ -30,17 +30,20 @@
 #ifndef __LIBS_ZIPFILERO_H
 #define __LIBS_ZIPFILERO_H
 
-#include <utils/Compat.h>
-#include <utils/Errors.h>
-#include <utils/FileMap.h>
-#include <utils/threads.h>
-
+#include <optional>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <time.h>
 
+#include <util/map_ptr.h>
+
+#include <utils/Compat.h>
+#include <utils/Errors.h>
+#include <utils/FileMap.h>
+#include <utils/threads.h>
+
 struct ZipArchive;
 typedef ZipArchive* ZipArchiveHandle;
 
@@ -136,14 +139,26 @@
         uint32_t* pCrc32) const;
 
     /*
-     * Create a new FileMap object that maps a subset of the archive.  For
+     * Create a new FileMap object that maps a subset of the archive. For
      * an uncompressed entry this effectively provides a pointer to the
      * actual data, for a compressed entry this provides the input buffer
      * for inflate().
+     *
+     * Use this function if the archive can never reside on IncFs.
      */
     FileMap* createEntryFileMap(ZipEntryRO entry) const;
 
     /*
+     * Create a new incfs::IncFsFileMap object that maps a subset of the archive. For
+     * an uncompressed entry this effectively provides a pointer to the
+     * actual data, for a compressed entry this provides the input buffer
+     * for inflate().
+     *
+     * Use this function if the archive can potentially reside on IncFs.
+     */
+    std::optional<incfs::IncFsFileMap> createEntryIncFsFileMap(ZipEntryRO entry) const;
+
+    /*
      * Uncompress the data into a buffer.  Depending on the compression
      * format, this is either an "inflate" operation or a memcpy.
      *
diff --git a/libs/androidfw/include/androidfw/ZipUtils.h b/libs/androidfw/include/androidfw/ZipUtils.h
index 4d35e99..dbfec34 100644
--- a/libs/androidfw/include/androidfw/ZipUtils.h
+++ b/libs/androidfw/include/androidfw/ZipUtils.h
@@ -25,6 +25,8 @@
 #include <stdio.h>
 #include <time.h>
 
+#include "util/map_ptr.h"
+
 namespace android {
 
 /*
@@ -40,8 +42,8 @@
         long compressedLen);
     static bool inflateToBuffer(int fd, void* buf, long uncompressedLen,
         long compressedLen);
-    static bool inflateToBuffer(const void *in, void* buf, long uncompressedLen,
-        long compressedLen);
+    static bool inflateToBuffer(incfs::map_ptr<void> in, void* buf,
+        long uncompressedLen, long compressedLen);
 
     /*
      * Someday we might want to make this generic and handle bzip2 ".bz2"
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
index 437e147..c7ae618 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -139,9 +139,13 @@
   assets.SetApkAssets({apk.get()});
 
   while (state.KeepRunning()) {
-    const ResolvedBag* bag = assets.GetBag(app::R::style::StyleTwo);
-    const auto bag_end = end(bag);
-    for (auto iter = begin(bag); iter != bag_end; ++iter) {
+    auto bag = assets.GetBag(app::R::style::StyleTwo);
+    if (!bag.has_value()) {
+      state.SkipWithError("Failed to load get bag");
+      return;
+    }
+    const auto bag_end = end(*bag);
+    for (auto iter = begin(*bag); iter != bag_end; ++iter) {
       uint32_t key = iter->key;
       Res_value value = iter->value;
       benchmark::DoNotOptimize(key);
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 8c255d1..3638ce1 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -108,24 +108,18 @@
   assetmanager.SetConfiguration(desired_config);
   assetmanager.SetApkAssets({basic_assets_.get()});
 
-  Res_value value;
-  ResTable_config selected_config;
-  uint32_t flags;
-
-  ApkAssetsCookie cookie =
-      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
-                               0 /*density_override*/, &value, &selected_config, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
+  auto value = assetmanager.GetResource(basic::R::string::test1);
+  ASSERT_TRUE(value.has_value());
 
   // Came from our ApkAssets.
-  EXPECT_EQ(0, cookie);
+  EXPECT_EQ(0, value->cookie);
 
   // It is the default config.
-  EXPECT_EQ(0, selected_config.language[0]);
-  EXPECT_EQ(0, selected_config.language[1]);
+  EXPECT_EQ(0, value->config.language[0]);
+  EXPECT_EQ(0, value->config.language[1]);
 
   // It is a string.
-  EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+  EXPECT_EQ(Res_value::TYPE_STRING, value->type);
 }
 
 TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) {
@@ -138,24 +132,18 @@
   assetmanager.SetConfiguration(desired_config);
   assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()});
 
-  Res_value value;
-  ResTable_config selected_config;
-  uint32_t flags;
-
-  ApkAssetsCookie cookie =
-      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
-                               0 /*density_override*/, &value, &selected_config, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
+  auto value = assetmanager.GetResource(basic::R::string::test1);
+  ASSERT_TRUE(value.has_value());
 
   // Came from our de_fr ApkAssets.
-  EXPECT_EQ(1, cookie);
+  EXPECT_EQ(1, value->cookie);
 
   // The configuration is German.
-  EXPECT_EQ('d', selected_config.language[0]);
-  EXPECT_EQ('e', selected_config.language[1]);
+  EXPECT_EQ('d', value->config.language[0]);
+  EXPECT_EQ('e', value->config.language[1]);
 
   // It is a string.
-  EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+  EXPECT_EQ(Res_value::TYPE_STRING, value->type);
 }
 
 TEST_F(AssetManager2Test, FindsResourceFromSharedLibrary) {
@@ -166,44 +154,35 @@
   assetmanager.SetApkAssets(
       {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
 
-  Res_value value;
-  ResTable_config selected_config;
-  uint32_t flags;
-
-  ApkAssetsCookie cookie =
-      assetmanager.GetResource(libclient::R::string::foo_one, false /*may_be_bag*/,
-                               0 /*density_override*/, &value, &selected_config, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
+  auto value = assetmanager.GetResource(libclient::R::string::foo_one);
+  ASSERT_TRUE(value.has_value());
 
   // Reference comes from libclient.
-  EXPECT_EQ(2, cookie);
-  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+  EXPECT_EQ(2, value->cookie);
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
 
   // Lookup the reference.
-  cookie = assetmanager.GetResource(value.data, false /* may_be_bag */, 0 /* density_override*/,
-                                    &value, &selected_config, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(1, cookie);
-  EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+  value = assetmanager.GetResource(value->data);
+  ASSERT_TRUE(value.has_value());
+  EXPECT_EQ(1, value->cookie);
+  EXPECT_EQ(Res_value::TYPE_STRING, value->type);
   EXPECT_EQ(std::string("Foo from lib_one"),
-            GetStringFromPool(assetmanager.GetStringPoolForCookie(cookie), value.data));
+            GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie), value->data));
 
-  cookie = assetmanager.GetResource(libclient::R::string::foo_two, false /*may_be_bag*/,
-                                    0 /*density_override*/, &value, &selected_config, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
+  value = assetmanager.GetResource(libclient::R::string::foo_two);
+  ASSERT_TRUE(value.has_value());
 
   // Reference comes from libclient.
-  EXPECT_EQ(2, cookie);
-  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+  EXPECT_EQ(2, value->cookie);
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
 
   // Lookup the reference.
-  cookie = assetmanager.GetResource(value.data, false /* may_be_bag */, 0 /* density_override*/,
-                                    &value, &selected_config, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(0, cookie);
-  EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+  value = assetmanager.GetResource(value->data);
+  ASSERT_TRUE(value.has_value());
+  EXPECT_EQ(0, value->cookie);
+  EXPECT_EQ(Res_value::TYPE_STRING, value->type);
   EXPECT_EQ(std::string("Foo from lib_two"),
-            GetStringFromPool(assetmanager.GetStringPoolForCookie(cookie), value.data));
+            GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie), value->data));
 }
 
 TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) {
@@ -211,16 +190,10 @@
   assetmanager.SetApkAssets({appaslib_assets_.get()});
 
   // The appaslib package will have been assigned the package ID 0x02.
-
-  Res_value value;
-  ResTable_config selected_config;
-  uint32_t flags;
-  ApkAssetsCookie cookie = assetmanager.GetResource(
-      fix_package_id(appaslib::R::integer::number1, 0x02), false /*may_be_bag*/,
-      0u /*density_override*/, &value, &selected_config, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
-  EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data);
+  auto value = assetmanager.GetResource(fix_package_id(appaslib::R::integer::number1, 0x02));
+  ASSERT_TRUE(value.has_value());
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+  EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value->data);
 }
 
 TEST_F(AssetManager2Test, AssignsOverlayPackageIdLast) {
@@ -238,40 +211,40 @@
     return assetmanager.GetAssignedPackageId(apkAssets->GetLoadedArsc()->GetPackages()[0].get());
   };
 
-  ASSERT_EQ(get_first_package_id(overlayable_assets_.get()), 0x7f);
-  ASSERT_EQ(get_first_package_id(overlay_assets_.get()), 0x03);
-  ASSERT_EQ(get_first_package_id(lib_one_assets_.get()), 0x02);
+  ASSERT_EQ(0x7f, get_first_package_id(overlayable_assets_.get()));
+  ASSERT_EQ(0x03, get_first_package_id(overlay_assets_.get()));
+  ASSERT_EQ(0x02, get_first_package_id(lib_one_assets_.get()));
 }
 
 TEST_F(AssetManager2Test, GetSharedLibraryResourceName) {
   AssetManager2 assetmanager;
   assetmanager.SetApkAssets({lib_one_assets_.get()});
 
-  AssetManager2::ResourceName name;
-  ASSERT_TRUE(assetmanager.GetResourceName(lib_one::R::string::foo, &name));
-  std::string formatted_name = ToFormattedResourceString(&name);
-  ASSERT_EQ(formatted_name, "com.android.lib_one:string/foo");
+  auto name = assetmanager.GetResourceName(lib_one::R::string::foo);
+  ASSERT_TRUE(name.has_value());
+  ASSERT_EQ("com.android.lib_one:string/foo", ToFormattedResourceString(*name));
 }
 
 TEST_F(AssetManager2Test, FindsBagResourceFromSingleApkAssets) {
   AssetManager2 assetmanager;
   assetmanager.SetApkAssets({basic_assets_.get()});
 
-  const ResolvedBag* bag = assetmanager.GetBag(basic::R::array::integerArray1);
-  ASSERT_NE(nullptr, bag);
-  ASSERT_EQ(3u, bag->entry_count);
+  auto bag = assetmanager.GetBag(basic::R::array::integerArray1);
+  ASSERT_TRUE(bag.has_value());
 
-  EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[0].value.dataType);
-  EXPECT_EQ(1u, bag->entries[0].value.data);
-  EXPECT_EQ(0, bag->entries[0].cookie);
+  ASSERT_EQ(3u, (*bag)->entry_count);
 
-  EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[1].value.dataType);
-  EXPECT_EQ(2u, bag->entries[1].value.data);
-  EXPECT_EQ(0, bag->entries[1].cookie);
+  EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), (*bag)->entries[0].value.dataType);
+  EXPECT_EQ(1u, (*bag)->entries[0].value.data);
+  EXPECT_EQ(0, (*bag)->entries[0].cookie);
 
-  EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[2].value.dataType);
-  EXPECT_EQ(3u, bag->entries[2].value.data);
-  EXPECT_EQ(0, bag->entries[2].cookie);
+  EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), (*bag)->entries[1].value.dataType);
+  EXPECT_EQ(2u, (*bag)->entries[1].value.data);
+  EXPECT_EQ(0, (*bag)->entries[1].cookie);
+
+  EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), (*bag)->entries[2].value.dataType);
+  EXPECT_EQ(3u, (*bag)->entries[2].value.data);
+  EXPECT_EQ(0, (*bag)->entries[2].cookie);
 }
 
 TEST_F(AssetManager2Test, FindsBagResourceFromMultipleApkAssets) {}
@@ -284,15 +257,16 @@
   assetmanager.SetApkAssets(
       {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
 
-  const ResolvedBag* bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03));
-  ASSERT_NE(nullptr, bag);
-  ASSERT_GE(bag->entry_count, 2u);
+  auto bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03));
+  ASSERT_TRUE(bag.has_value());
+
+  ASSERT_GE((*bag)->entry_count, 2u);
 
   // First two attributes come from lib_one.
-  EXPECT_EQ(1, bag->entries[0].cookie);
-  EXPECT_EQ(0x03, get_package_id(bag->entries[0].key));
-  EXPECT_EQ(1, bag->entries[1].cookie);
-  EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
+  EXPECT_EQ(1, (*bag)->entries[0].cookie);
+  EXPECT_EQ(0x03, get_package_id((*bag)->entries[0].key));
+  EXPECT_EQ(1, (*bag)->entries[1].cookie);
+  EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key));
 }
 
 TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) {
@@ -303,17 +277,17 @@
   assetmanager.SetApkAssets(
       {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
 
-  const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib);
-  ASSERT_NE(nullptr, bag);
-  ASSERT_EQ(bag->entry_count, 2u);
+  auto bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib);
+  ASSERT_TRUE(bag.has_value());
+  ASSERT_EQ((*bag)->entry_count, 2u);
 
   // First attribute comes from lib_two.
-  EXPECT_EQ(2, bag->entries[0].cookie);
-  EXPECT_EQ(0x02, get_package_id(bag->entries[0].key));
+  EXPECT_EQ(2, (*bag)->entries[0].cookie);
+  EXPECT_EQ(0x02, get_package_id((*bag)->entries[0].key));
 
   // The next two attributes come from lib_one.
-  EXPECT_EQ(2, bag->entries[1].cookie);
-  EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
+  EXPECT_EQ(2, (*bag)->entries[1].cookie);
+  EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key));
 }
 
 TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) {
@@ -324,79 +298,79 @@
   assetmanager.SetApkAssets(
       {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
 
-  const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::Theme);
-  ASSERT_NE(nullptr, bag);
-  ASSERT_GE(bag->entry_count, 2u);
+  auto bag = assetmanager.GetBag(libclient::R::style::Theme);
+  ASSERT_TRUE(bag.has_value());
+  ASSERT_GE((*bag)->entry_count, 2u);
 
   // First two attributes come from lib_one.
-  EXPECT_EQ(1, bag->entries[0].cookie);
-  EXPECT_EQ(0x03, get_package_id(bag->entries[0].key));
-  EXPECT_EQ(1, bag->entries[1].cookie);
-  EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
+  EXPECT_EQ(1, (*bag)->entries[0].cookie);
+  EXPECT_EQ(0x03, get_package_id((*bag)->entries[0].key));
+  EXPECT_EQ(1, (*bag)->entries[1].cookie);
+  EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key));
 }
 
 TEST_F(AssetManager2Test, MergesStylesWithParentFromSingleApkAssets) {
   AssetManager2 assetmanager;
   assetmanager.SetApkAssets({style_assets_.get()});
 
-  const ResolvedBag* bag_one = assetmanager.GetBag(app::R::style::StyleOne);
-  ASSERT_NE(nullptr, bag_one);
-  ASSERT_EQ(2u, bag_one->entry_count);
+  auto bag_one = assetmanager.GetBag(app::R::style::StyleOne);
+  ASSERT_TRUE(bag_one.has_value());
+  ASSERT_EQ(2u, (*bag_one)->entry_count);
 
-  EXPECT_EQ(app::R::attr::attr_one, bag_one->entries[0].key);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[0].value.dataType);
-  EXPECT_EQ(1u, bag_one->entries[0].value.data);
-  EXPECT_EQ(0, bag_one->entries[0].cookie);
+  EXPECT_EQ(app::R::attr::attr_one, (*bag_one)->entries[0].key);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_one)->entries[0].value.dataType);
+  EXPECT_EQ(1u, (*bag_one)->entries[0].value.data);
+  EXPECT_EQ(0, (*bag_one)->entries[0].cookie);
 
-  EXPECT_EQ(app::R::attr::attr_two, bag_one->entries[1].key);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[1].value.dataType);
-  EXPECT_EQ(2u, bag_one->entries[1].value.data);
-  EXPECT_EQ(0, bag_one->entries[1].cookie);
+  EXPECT_EQ(app::R::attr::attr_two, (*bag_one)->entries[1].key);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_one)->entries[1].value.dataType);
+  EXPECT_EQ(2u, (*bag_one)->entries[1].value.data);
+  EXPECT_EQ(0, (*bag_one)->entries[1].cookie);
 
-  const ResolvedBag* bag_two = assetmanager.GetBag(app::R::style::StyleTwo);
-  ASSERT_NE(nullptr, bag_two);
-  ASSERT_EQ(6u, bag_two->entry_count);
+  auto bag_two = assetmanager.GetBag(app::R::style::StyleTwo);
+  ASSERT_TRUE(bag_two.has_value());
+  ASSERT_EQ(6u, (*bag_two)->entry_count);
 
   // attr_one is inherited from StyleOne.
-  EXPECT_EQ(app::R::attr::attr_one, bag_two->entries[0].key);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[0].value.dataType);
-  EXPECT_EQ(1u, bag_two->entries[0].value.data);
-  EXPECT_EQ(0, bag_two->entries[0].cookie);
-  EXPECT_EQ(app::R::style::StyleOne, bag_two->entries[0].style);
+  EXPECT_EQ(app::R::attr::attr_one, (*bag_two)->entries[0].key);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_two)->entries[0].value.dataType);
+  EXPECT_EQ(1u, (*bag_two)->entries[0].value.data);
+  EXPECT_EQ(0, (*bag_two)->entries[0].cookie);
+  EXPECT_EQ(app::R::style::StyleOne, (*bag_two)->entries[0].style);
 
   // attr_two should be overridden from StyleOne by StyleTwo.
-  EXPECT_EQ(app::R::attr::attr_two, bag_two->entries[1].key);
-  EXPECT_EQ(Res_value::TYPE_STRING, bag_two->entries[1].value.dataType);
-  EXPECT_EQ(0, bag_two->entries[1].cookie);
-  EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[1].style);
+  EXPECT_EQ(app::R::attr::attr_two, (*bag_two)->entries[1].key);
+  EXPECT_EQ(Res_value::TYPE_STRING, (*bag_two)->entries[1].value.dataType);
+  EXPECT_EQ(0, (*bag_two)->entries[1].cookie);
+  EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[1].style);
   EXPECT_EQ(std::string("string"), GetStringFromPool(assetmanager.GetStringPoolForCookie(0),
-                                                     bag_two->entries[1].value.data));
+                                                     (*bag_two)->entries[1].value.data));
 
   // The rest are new attributes.
 
-  EXPECT_EQ(app::R::attr::attr_three, bag_two->entries[2].key);
-  EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, bag_two->entries[2].value.dataType);
-  EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[2].value.data);
-  EXPECT_EQ(0, bag_two->entries[2].cookie);
-  EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[2].style);
+  EXPECT_EQ(app::R::attr::attr_three, (*bag_two)->entries[2].key);
+  EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, (*bag_two)->entries[2].value.dataType);
+  EXPECT_EQ(app::R::attr::attr_indirect, (*bag_two)->entries[2].value.data);
+  EXPECT_EQ(0, (*bag_two)->entries[2].cookie);
+  EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[2].style);
 
-  EXPECT_EQ(app::R::attr::attr_five, bag_two->entries[3].key);
-  EXPECT_EQ(Res_value::TYPE_REFERENCE, bag_two->entries[3].value.dataType);
-  EXPECT_EQ(app::R::string::string_one, bag_two->entries[3].value.data);
-  EXPECT_EQ(0, bag_two->entries[3].cookie);
-  EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[3].style);
+  EXPECT_EQ(app::R::attr::attr_five, (*bag_two)->entries[3].key);
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, (*bag_two)->entries[3].value.dataType);
+  EXPECT_EQ(app::R::string::string_one, (*bag_two)->entries[3].value.data);
+  EXPECT_EQ(0, (*bag_two)->entries[3].cookie);
+  EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[3].style);
 
-  EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[4].key);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[4].value.dataType);
-  EXPECT_EQ(3u, bag_two->entries[4].value.data);
-  EXPECT_EQ(0, bag_two->entries[4].cookie);
-  EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[4].style);
+  EXPECT_EQ(app::R::attr::attr_indirect, (*bag_two)->entries[4].key);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_two)->entries[4].value.dataType);
+  EXPECT_EQ(3u, (*bag_two)->entries[4].value.data);
+  EXPECT_EQ(0, (*bag_two)->entries[4].cookie);
+  EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[4].style);
 
-  EXPECT_EQ(app::R::attr::attr_empty, bag_two->entries[5].key);
-  EXPECT_EQ(Res_value::TYPE_NULL, bag_two->entries[5].value.dataType);
-  EXPECT_EQ(Res_value::DATA_NULL_EMPTY, bag_two->entries[5].value.data);
-  EXPECT_EQ(0, bag_two->entries[5].cookie);
-  EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[5].style);
+  EXPECT_EQ(app::R::attr::attr_empty, (*bag_two)->entries[5].key);
+  EXPECT_EQ(Res_value::TYPE_NULL, (*bag_two)->entries[5].value.dataType);
+  EXPECT_EQ(Res_value::DATA_NULL_EMPTY, (*bag_two)->entries[5].value.data);
+  EXPECT_EQ(0, (*bag_two)->entries[5].cookie);
+  EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[5].style);
 }
 
 TEST_F(AssetManager2Test, MergeStylesCircularDependency) {
@@ -405,55 +379,41 @@
 
   // GetBag should stop traversing the parents of styles when a circular
   // dependency is detected
-  const ResolvedBag* bag_one = assetmanager.GetBag(app::R::style::StyleFour);
-  ASSERT_NE(nullptr, bag_one);
-  ASSERT_EQ(3u, bag_one->entry_count);
+  auto bag = assetmanager.GetBag(app::R::style::StyleFour);
+  ASSERT_TRUE(bag.has_value());
+  ASSERT_EQ(3u, (*bag)->entry_count);
 }
 
 TEST_F(AssetManager2Test, ResolveReferenceToResource) {
   AssetManager2 assetmanager;
   assetmanager.SetApkAssets({basic_assets_.get()});
 
-  Res_value value;
-  ResTable_config selected_config;
-  uint32_t flags;
-  ApkAssetsCookie cookie =
-      assetmanager.GetResource(basic::R::integer::ref1, false /*may_be_bag*/,
-                               0u /*density_override*/, &value, &selected_config, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
+  auto value = assetmanager.GetResource(basic::R::integer::ref1);
+  ASSERT_TRUE(value.has_value());
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+  EXPECT_EQ(basic::R::integer::ref2, value->data);
 
-  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
-  EXPECT_EQ(basic::R::integer::ref2, value.data);
-
-  uint32_t last_ref = 0u;
-  cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
-  EXPECT_EQ(12000u, value.data);
-  EXPECT_EQ(basic::R::integer::ref2, last_ref);
+  auto result = assetmanager.ResolveReference(*value);
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+  EXPECT_EQ(12000u, value->data);
+  EXPECT_EQ(basic::R::integer::ref2, value->resid);
 }
 
 TEST_F(AssetManager2Test, ResolveReferenceToBag) {
   AssetManager2 assetmanager;
   assetmanager.SetApkAssets({basic_assets_.get()});
 
-  Res_value value;
-  ResTable_config selected_config;
-  uint32_t flags;
-  ApkAssetsCookie cookie =
-      assetmanager.GetResource(basic::R::integer::number2, true /*may_be_bag*/,
-                               0u /*density_override*/, &value, &selected_config, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
+  auto value = assetmanager.GetResource(basic::R::integer::number2, true /*may_be_bag*/);
+  ASSERT_TRUE(value.has_value());
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+  EXPECT_EQ(basic::R::array::integerArray1, value->data);
 
-  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
-  EXPECT_EQ(basic::R::array::integerArray1, value.data);
-
-  uint32_t last_ref = 0u;
-  cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
-  EXPECT_EQ(basic::R::array::integerArray1, value.data);
-  EXPECT_EQ(basic::R::array::integerArray1, last_ref);
+  auto result = assetmanager.ResolveReference(*value);
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+  EXPECT_EQ(basic::R::array::integerArray1, value->data);
+  EXPECT_EQ(basic::R::array::integerArray1, value->resid);
 }
 
 TEST_F(AssetManager2Test, ResolveDeepIdReference) {
@@ -461,31 +421,25 @@
   assetmanager.SetApkAssets({basic_assets_.get()});
 
   // Set up the resource ids
-  const uint32_t high_ref = assetmanager
-      .GetResourceId("@id/high_ref", "values", "com.android.basic");
-  ASSERT_NE(high_ref, 0u);
-  const uint32_t middle_ref = assetmanager
-      .GetResourceId("@id/middle_ref", "values", "com.android.basic");
-  ASSERT_NE(middle_ref, 0u);
-  const uint32_t low_ref = assetmanager
-      .GetResourceId("@id/low_ref", "values", "com.android.basic");
-  ASSERT_NE(low_ref, 0u);
+  auto high_ref = assetmanager.GetResourceId("@id/high_ref", "values", "com.android.basic");
+  ASSERT_TRUE(high_ref.has_value());
+
+  auto middle_ref = assetmanager.GetResourceId("@id/middle_ref", "values", "com.android.basic");
+  ASSERT_TRUE(middle_ref.has_value());
+
+  auto low_ref = assetmanager.GetResourceId("@id/low_ref", "values", "com.android.basic");
+  ASSERT_TRUE(low_ref.has_value());
 
   // Retrieve the most shallow resource
-  Res_value value;
-  ResTable_config config;
-  uint32_t flags;
-  ApkAssetsCookie cookie = assetmanager.GetResource(high_ref, false /*may_be_bag*/,
-                                                    0 /*density_override*/,
-                                                    &value, &config, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
-  EXPECT_EQ(middle_ref, value.data);
+  auto value = assetmanager.GetResource(*high_ref);
+  ASSERT_TRUE(value.has_value());
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+  EXPECT_EQ(*middle_ref, value->data);;
 
   // Check that resolving the reference resolves to the deepest id
-  uint32_t last_ref = high_ref;
-  assetmanager.ResolveReference(cookie, &value, &config, &flags, &last_ref);
-  EXPECT_EQ(last_ref, low_ref);
+  auto result = assetmanager.ResolveReference(*value);
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(*low_ref, value->resid);
 }
 
 TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) {
@@ -495,16 +449,16 @@
   ResTable_config selected_config;
   memset(&selected_config, 0, sizeof(selected_config));
 
-  uint32_t flags = 0u;
+  // Create some kind of value that is NOT a reference.
+  AssetManager2::SelectedValue value{};
+  value.cookie = 1;
+  value.type = Res_value::TYPE_STRING;
+  value.resid = basic::R::string::test1;
 
-  // Create some kind of Res_value that is NOT a reference.
-  Res_value value;
-  value.dataType = Res_value::TYPE_STRING;
-  value.data = 0;
-
-  uint32_t last_ref = basic::R::string::test1;
-  EXPECT_EQ(1, assetmanager.ResolveReference(1, &value, &selected_config, &flags, &last_ref));
-  EXPECT_EQ(basic::R::string::test1, last_ref);
+  auto result = assetmanager.ResolveReference(value);
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(1, value.cookie);
+  EXPECT_EQ(basic::R::string::test1, value.resid);
 }
 
 static bool IsConfigurationPresent(const std::set<ResTable_config>& configurations,
@@ -516,43 +470,45 @@
   AssetManager2 assetmanager;
   assetmanager.SetApkAssets({system_assets_.get(), basic_de_fr_assets_.get()});
 
-  std::set<ResTable_config> configurations = assetmanager.GetResourceConfigurations();
+  auto configurations = assetmanager.GetResourceConfigurations();
+  ASSERT_TRUE(configurations.has_value());
 
   // We expect the locale sv from the system assets, and de and fr from basic_de_fr assets.
   // And one extra for the default configuration.
-  EXPECT_EQ(4u, configurations.size());
+  EXPECT_EQ(4u, configurations->size());
 
   ResTable_config expected_config;
   memset(&expected_config, 0, sizeof(expected_config));
   expected_config.language[0] = 's';
   expected_config.language[1] = 'v';
-  EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+  EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config));
 
   expected_config.language[0] = 'd';
   expected_config.language[1] = 'e';
-  EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+  EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config));
 
   expected_config.language[0] = 'f';
   expected_config.language[1] = 'r';
-  EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+  EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config));
 
   // Take out the system assets.
   configurations = assetmanager.GetResourceConfigurations(true /* exclude_system */);
+  ASSERT_TRUE(configurations.has_value());
 
   // We expect de and fr from basic_de_fr assets.
-  EXPECT_EQ(2u, configurations.size());
+  EXPECT_EQ(2u, configurations->size());
 
   expected_config.language[0] = 's';
   expected_config.language[1] = 'v';
-  EXPECT_FALSE(IsConfigurationPresent(configurations, expected_config));
+  EXPECT_FALSE(IsConfigurationPresent(*configurations, expected_config));
 
   expected_config.language[0] = 'd';
   expected_config.language[1] = 'e';
-  EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+  EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config));
 
   expected_config.language[0] = 'f';
   expected_config.language[1] = 'r';
-  EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+  EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config));
 }
 
 TEST_F(AssetManager2Test, GetResourceLocales) {
@@ -578,12 +534,17 @@
   AssetManager2 assetmanager;
   assetmanager.SetApkAssets({basic_assets_.get()});
 
-  EXPECT_EQ(basic::R::layout::main,
-            assetmanager.GetResourceId("com.android.basic:layout/main", "", ""));
-  EXPECT_EQ(basic::R::layout::main,
-            assetmanager.GetResourceId("layout/main", "", "com.android.basic"));
-  EXPECT_EQ(basic::R::layout::main,
-            assetmanager.GetResourceId("main", "layout", "com.android.basic"));
+  auto resid = assetmanager.GetResourceId("com.android.basic:layout/main", "", "");
+  ASSERT_TRUE(resid.has_value());
+  EXPECT_EQ(basic::R::layout::main, *resid);
+
+  resid = assetmanager.GetResourceId("layout/main", "", "com.android.basic");
+  ASSERT_TRUE(resid.has_value());
+  EXPECT_EQ(basic::R::layout::main, *resid);
+
+  resid = assetmanager.GetResourceId("main", "layout", "com.android.basic");
+  ASSERT_TRUE(resid.has_value());
+  EXPECT_EQ(basic::R::layout::main, *resid);
 }
 
 TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {
@@ -658,14 +619,8 @@
   assetmanager.SetApkAssets({basic_assets_.get()});
   assetmanager.SetResourceResolutionLoggingEnabled(false);
 
-  Res_value value;
-  ResTable_config selected_config;
-  uint32_t flags;
-
-  ApkAssetsCookie cookie =
-      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
-                               0 /*density_override*/, &value, &selected_config, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
+  auto value = assetmanager.GetResource(basic::R::string::test1);
+  ASSERT_TRUE(value.has_value());
 
   auto result = assetmanager.GetLastResourceResolution();
   EXPECT_EQ("", result);
@@ -693,17 +648,12 @@
   assetmanager.SetConfiguration(desired_config);
   assetmanager.SetApkAssets({basic_assets_.get()});
 
-  Res_value value;
-  ResTable_config selected_config;
-  uint32_t flags;
-
-  ApkAssetsCookie cookie =
-      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
-                               0 /*density_override*/, &value, &selected_config, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
+  auto value = assetmanager.GetResource(basic::R::string::test1);
+  ASSERT_TRUE(value.has_value());
 
   auto result = assetmanager.GetLastResourceResolution();
-  EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic", result);
+  EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n"
+            "\tFor config -de\n\tFound initial: com.android.basic", result);
 }
 
 TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) {
@@ -717,17 +667,14 @@
   assetmanager.SetConfiguration(desired_config);
   assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()});
 
-  Res_value value = Res_value();
-  ResTable_config selected_config;
-  uint32_t flags;
-
-  ApkAssetsCookie cookie =
-      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
-                               0 /*density_override*/, &value, &selected_config, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
+  auto value = assetmanager.GetResource(basic::R::string::test1);
+  ASSERT_TRUE(value.has_value());
 
   auto result = assetmanager.GetLastResourceResolution();
-  EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic\n\tFound better: com.android.basic -de", result);
+  EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n"
+            "\tFor config -de\n"
+            "\tFound initial: com.android.basic\n"
+            "\tFound better: com.android.basic -de", result);
 }
 
 TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) {
@@ -739,14 +686,8 @@
   assetmanager.SetConfiguration(desired_config);
   assetmanager.SetApkAssets({basic_assets_.get()});
 
-  Res_value value = Res_value();
-  ResTable_config selected_config;
-  uint32_t flags;
-
-  ApkAssetsCookie cookie =
-      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
-                               0 /*density_override*/, &value, &selected_config, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
+  auto value = assetmanager.GetResource(basic::R::string::test1);
+  ASSERT_TRUE(value.has_value());
 
   auto resultEnabled = assetmanager.GetLastResourceResolution();
   ASSERT_NE("", resultEnabled);
diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp
index fa300c5..ddd8ab8 100644
--- a/libs/androidfw/tests/AttributeResolution_bench.cpp
+++ b/libs/androidfw/tests/AttributeResolution_bench.cpp
@@ -108,27 +108,20 @@
   device_config.screenHeightDp = 1024;
   device_config.sdkVersion = 27;
 
-  Res_value value;
-  ResTable_config config;
-  uint32_t flags = 0u;
-  ApkAssetsCookie cookie =
-      assetmanager.GetResource(basic::R::layout::layoutt, false /*may_be_bag*/,
-                               0u /*density_override*/, &value, &config, &flags);
-  if (cookie == kInvalidCookie) {
+  auto value = assetmanager.GetResource(basic::R::layout::layoutt);
+  if (!value.has_value()) {
     state.SkipWithError("failed to find R.layout.layout");
     return;
   }
 
-  size_t len = 0u;
-  const char* layout_path =
-      assetmanager.GetStringPoolForCookie(cookie)->string8At(value.data, &len);
-  if (layout_path == nullptr || len == 0u) {
+  auto layout_path = assetmanager.GetStringPoolForCookie(value->cookie)->string8At(value->data);
+  if (!layout_path.has_value()) {
     state.SkipWithError("failed to lookup layout path");
     return;
   }
 
-  std::unique_ptr<Asset> asset = assetmanager.OpenNonAsset(
-      StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER);
+  std::unique_ptr<Asset> asset = assetmanager.OpenNonAsset(layout_path->to_string(), value->cookie,
+                                                           Asset::ACCESS_BUFFER);
   if (asset == nullptr) {
     state.SkipWithError("failed to load layout");
     return;
diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp
index 24361b5..bb9129a 100644
--- a/libs/androidfw/tests/AttributeResolution_test.cpp
+++ b/libs/androidfw/tests/AttributeResolution_test.cpp
@@ -77,9 +77,9 @@
       {fix_package_id(R::attr::attr_one, 0x02), fix_package_id(R::attr::attr_two, 0x02)}};
   std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
   std::array<uint32_t, attrs.size() + 1> indices;
-  ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/,
-             fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(), values.data(),
-             indices.data());
+  ASSERT_TRUE(ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/,
+                         fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(),
+                         values.data(), indices.data()).has_value());
 
   const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
 
@@ -102,7 +102,7 @@
 
 TEST_F(AttributeResolutionTest, Theme) {
   std::unique_ptr<Theme> theme = assetmanager_.NewTheme();
-  ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo));
+  ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo).has_value());
 
   std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
                                  R::attr::attr_four, R::attr::attr_empty}};
@@ -110,7 +110,7 @@
 
   ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/,
                            nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(),
-                           attrs.size(), values.data(), nullptr /*out_indices*/));
+                           attrs.size(), values.data(), nullptr /*out_indices*/).has_value());
 
   const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
 
@@ -162,7 +162,7 @@
   std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
 
   ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(),
-                                 values.data(), nullptr /*out_indices*/));
+                                 values.data(), nullptr /*out_indices*/).has_value());
 
   uint32_t* values_cursor = values.data();
   EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
@@ -207,15 +207,15 @@
 
 TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) {
   std::unique_ptr<Theme> theme = assetmanager_.NewTheme();
-  ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo));
+  ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo).has_value());
 
   std::array<uint32_t, 6> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
                                  R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}};
   std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
   std::array<uint32_t, attrs.size() + 1> indices;
 
-  ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(),
-             attrs.size(), values.data(), indices.data());
+  ASSERT_TRUE(ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/,
+                         attrs.data(), attrs.size(), values.data(), indices.data()).has_value());
 
   const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
 
diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp
index faddfe5..0fa0573 100644
--- a/libs/androidfw/tests/BenchmarkHelpers.cpp
+++ b/libs/androidfw/tests/BenchmarkHelpers.cpp
@@ -71,15 +71,9 @@
     assetmanager.SetConfiguration(*config);
   }
 
-  Res_value value;
-  ResTable_config selected_config;
-  uint32_t flags;
-  uint32_t last_id = 0u;
-
   while (state.KeepRunning()) {
-    ApkAssetsCookie cookie = assetmanager.GetResource(
-        resid, false /* may_be_bag */, 0u /* density_override */, &value, &selected_config, &flags);
-    assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_id);
+    auto value = assetmanager.GetResource(resid);
+    assetmanager.ResolveReference(*value);
   }
 }
 
diff --git a/libs/androidfw/tests/CommonHelpers.cpp b/libs/androidfw/tests/CommonHelpers.cpp
index faa5350..3396729 100644
--- a/libs/androidfw/tests/CommonHelpers.cpp
+++ b/libs/androidfw/tests/CommonHelpers.cpp
@@ -58,8 +58,9 @@
 }
 
 std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx) {
-  String8 str = pool->string8ObjectAt(idx);
-  return std::string(str.string(), str.length());
+  auto str = pool->string8ObjectAt(idx);
+  CHECK(str.has_value()) << "failed to find string entry";
+  return std::string(str->string(), str->length());
 }
 
 }  // namespace android
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 7aa0dbb..3f0c7cb 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -62,10 +62,10 @@
   std::unique_ptr<const ApkAssets> overlayable_assets_;
 };
 
-std::string GetStringFromApkAssets(const AssetManager2& asset_manager, const Res_value& value,
-                                   ApkAssetsCookie cookie) {
+std::string GetStringFromApkAssets(const AssetManager2& asset_manager,
+                                   const AssetManager2::SelectedValue& value) {
   auto assets = asset_manager.GetApkAssets();
-  const ResStringPool* string_pool = assets[cookie]->GetLoadedArsc()->GetStringPool();
+  const ResStringPool* string_pool = assets[value.cookie]->GetLoadedArsc()->GetStringPool();
   return GetStringFromPool(string_pool, value.data);
 }
 
@@ -75,117 +75,88 @@
   AssetManager2 asset_manager;
   asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
                               overlay_assets_.get()});
-  Res_value val;
-  ResTable_config config;
-  uint32_t flags;
-  ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable5,
-                                                    false /* may_be_bag */,
-                                                    0 /* density_override */, &val, &config,
-                                                    &flags);
-  ASSERT_EQ(cookie, 2U);
-  ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
-  ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Overlay One");
+
+  auto value = asset_manager.GetResource(overlayable::R::string::overlayable5);
+  ASSERT_TRUE(value.has_value());
+  ASSERT_EQ(value->cookie, 2U);
+  ASSERT_EQ(value->type, Res_value::TYPE_STRING);
+  ASSERT_EQ("Overlay One", GetStringFromApkAssets(asset_manager, *value));
 }
 
 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingDifferentPackage) {
   AssetManager2 asset_manager;
   asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
                               overlay_assets_.get()});
-  Res_value val;
-  ResTable_config config;
-  uint32_t flags;
-  ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable10,
-                                                    false /* may_be_bag */,
-                                                    0 /* density_override */, &val, &config,
-                                                    &flags);
-  ASSERT_EQ(cookie, 0U);
-  ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
-  ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "yes");
+
+  auto value = asset_manager.GetResource(overlayable::R::string::overlayable10);
+  ASSERT_TRUE(value.has_value());
+  ASSERT_EQ(value->cookie, 0U);
+  ASSERT_EQ(value->type, Res_value::TYPE_STRING);
+  ASSERT_EQ("yes", GetStringFromApkAssets(asset_manager, *value));
 }
 
 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInternalResource) {
   AssetManager2 asset_manager;
   asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
                               overlay_assets_.get()});
-  Res_value val;
-  ResTable_config config;
-  uint32_t flags;
-  ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable8,
-                                                    false /* may_be_bag */,
-                                                    0 /* density_override */, &val, &config,
-                                                    &flags);
-  ASSERT_EQ(cookie, 2U);
-  ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE);
-  ASSERT_EQ(val.data, (overlay::R::string::internal & 0x00ffffff) | (0x02 << 24));
+
+  auto value = asset_manager.GetResource(overlayable::R::string::overlayable8);
+  ASSERT_TRUE(value.has_value());
+  ASSERT_EQ(value->cookie, 2U);
+  ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE);
+  ASSERT_EQ(value->data, (overlay::R::string::internal & 0x00ffffffU) | (0x02U << 24));
 }
 
 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineInteger) {
   AssetManager2 asset_manager;
   asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
                               overlay_assets_.get()});
-  Res_value val;
-  ResTable_config config;
-  uint32_t flags;
-  ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::integer::config_integer,
-                                                  false /* may_be_bag */,
-                                                  0 /* density_override */, &val, &config,
-                                                  &flags);
-  ASSERT_EQ(cookie, 2U);
-  ASSERT_EQ(val.dataType, Res_value::TYPE_INT_DEC);
-  ASSERT_EQ(val.data, 42);
+
+  auto value = asset_manager.GetResource(overlayable::R::integer::config_integer);
+  ASSERT_TRUE(value.has_value());
+  ASSERT_EQ(value->cookie, 2U);
+  ASSERT_EQ(value->type, Res_value::TYPE_INT_DEC);
+  ASSERT_EQ(value->data, 42);
 }
 
 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineString) {
   AssetManager2 asset_manager;
   asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
                               overlay_assets_.get()});
-  Res_value val;
-  ResTable_config config;
-  uint32_t flags;
 
-  ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11,
-                                                  false /* may_be_bag */,
-                                                  0 /* density_override */, &val, &config,
-                                                  &flags);
-  ASSERT_EQ(cookie, 2U);
-  ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
-  ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Hardcoded string");
+  auto value = asset_manager.GetResource(overlayable::R::string::overlayable11);
+  ASSERT_TRUE(value.has_value());
+  ASSERT_EQ(value->cookie, 2U);
+  ASSERT_EQ(value->type, Res_value::TYPE_STRING);
+  ASSERT_EQ("Hardcoded string", GetStringFromApkAssets(asset_manager, *value));
 }
 
 TEST_F(IdmapTest, OverlayOverridesResourceValueUsingOverlayingResource) {
   AssetManager2 asset_manager;
   asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
                               overlay_assets_.get()});
-  Res_value val;
-  ResTable_config config;
-  uint32_t flags;
-  ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable9,
-                                                    false /* may_be_bag */,
-                                                    0 /* density_override */, &val, &config,
-                                                    &flags);
-  ASSERT_EQ(cookie, 2U);
-  ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE);
-  ASSERT_EQ(val.data, overlayable::R::string::overlayable7);
+
+  auto value = asset_manager.GetResource(overlayable::R::string::overlayable9);
+  ASSERT_TRUE(value.has_value());
+  ASSERT_EQ(value->cookie, 2U);
+  ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE);
+  ASSERT_EQ(value->data, overlayable::R::string::overlayable7);
 }
 
 TEST_F(IdmapTest, OverlayOverridesXmlParser) {
   AssetManager2 asset_manager;
   asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
                               overlay_assets_.get()});
-  Res_value val;
-  ResTable_config config;
-  uint32_t flags;
-  ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::layout::hello_view,
-                                                    false /* may_be_bag */,
-                                                    0 /* density_override */, &val, &config,
-                                                    &flags);
-  ASSERT_EQ(cookie, 2U);
-  ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
-  ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "res/layout/hello_view.xml");
 
-  auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", cookie,
+  auto value = asset_manager.GetResource(overlayable::R::layout::hello_view);
+  ASSERT_TRUE(value.has_value());
+  ASSERT_EQ(value->cookie, 2U);
+  ASSERT_EQ(value->type, Res_value::TYPE_STRING);
+  ASSERT_EQ("res/layout/hello_view.xml", GetStringFromApkAssets(asset_manager, *value));
+
+  auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", value->cookie,
                                           Asset::ACCESS_RANDOM);
-  auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(cookie);
+  auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(value->cookie);
   auto xml_tree = util::make_unique<ResXMLTree>(std::move(dynamic_ref_table));
   status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), false);
   ASSERT_EQ(err, NO_ERROR);
@@ -216,32 +187,24 @@
   asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
                               overlay_assets_.get()});
 
-  AssetManager2::ResourceName name;
-  ASSERT_TRUE(asset_manager.GetResourceName(overlayable::R::string::overlayable9, &name));
-  ASSERT_EQ(std::string(name.package), "com.android.overlayable");
-  ASSERT_EQ(String16(name.type16), u"string");
-  ASSERT_EQ(std::string(name.entry), "overlayable9");
+  auto name = asset_manager.GetResourceName(overlayable::R::string::overlayable9);
+  ASSERT_TRUE(name.has_value());
+  ASSERT_EQ("com.android.overlayable", std::string(name->package));
+  ASSERT_EQ(std::u16string(u"string"), std::u16string(name->type16));
+  ASSERT_EQ("overlayable9", std::string(name->entry));
 }
 
 TEST_F(IdmapTest, OverlayLoaderInterop) {
-  std::string contents;
   auto loader_assets = ApkAssets::LoadTable("loader/resources.arsc", PROPERTY_LOADER);
-
   AssetManager2 asset_manager;
   asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(),
                               overlay_assets_.get()});
 
-  Res_value val;
-  ResTable_config config;
-  uint32_t flags;
-  ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11,
-                                                    false /* may_be_bag */,
-                                                    0 /* density_override */, &val, &config,
-                                                    &flags);
-  std::cout << asset_manager.GetLastResourceResolution();
-  ASSERT_EQ(cookie, 1U);
-  ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
-  ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "loader");
+  auto value = asset_manager.GetResource(overlayable::R::string::overlayable11);
+  ASSERT_TRUE(value.has_value());
+  ASSERT_EQ(1U, value->cookie);
+  ASSERT_EQ(Res_value::TYPE_STRING, value->type);
+  ASSERT_EQ("loader", GetStringFromApkAssets(asset_manager, *value));
 }
 
 TEST_F(IdmapTest, OverlayAssetsIsUpToDate) {
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 2d69dfe..6357411 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -50,7 +50,8 @@
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc",
                                       &contents));
 
-  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+  auto loaded_arsc = LoadedArsc::Load(reinterpret_cast<const void*>(contents.data()),
+                                                                    contents.length());
   ASSERT_THAT(loaded_arsc, NotNull());
 
   const LoadedPackage* package =
@@ -66,9 +67,8 @@
   ASSERT_THAT(type_spec, NotNull());
   ASSERT_THAT(type_spec->type_count, Ge(1u));
 
-  const ResTable_type* type = type_spec->types[0];
-  ASSERT_THAT(type, NotNull());
-  ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
+  auto type = type_spec->types[0];
+  ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value());
 }
 
 TEST(LoadedArscTest, LoadSparseEntryApp) {
@@ -76,7 +76,8 @@
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc",
                                       &contents));
 
-  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+                                                                   contents.length());
   ASSERT_THAT(loaded_arsc, NotNull());
 
   const LoadedPackage* package =
@@ -90,9 +91,8 @@
   ASSERT_THAT(type_spec, NotNull());
   ASSERT_THAT(type_spec->type_count, Ge(1u));
 
-  const ResTable_type* type = type_spec->types[0];
-  ASSERT_THAT(type, NotNull());
-  ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
+  auto type = type_spec->types[0];
+  ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value());
 }
 
 TEST(LoadedArscTest, LoadSharedLibrary) {
@@ -100,7 +100,8 @@
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc",
                                       &contents));
 
-  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+                                                                   contents.length());
   ASSERT_THAT(loaded_arsc, NotNull());
 
   const auto& packages = loaded_arsc->GetPackages();
@@ -120,7 +121,8 @@
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/libclient/libclient.apk",
                                       "resources.arsc", &contents));
 
-  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+                                                                   contents.length());
   ASSERT_THAT(loaded_arsc, NotNull());
 
   const auto& packages = loaded_arsc->GetPackages();
@@ -145,8 +147,10 @@
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk",
                                       "resources.arsc", &contents));
 
-  std::unique_ptr<const LoadedArsc> loaded_arsc =
-      LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, PROPERTY_DYNAMIC);
+  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+                                                                   contents.length(),
+                                                                   nullptr /* loaded_idmap */,
+                                                                   PROPERTY_DYNAMIC);
   ASSERT_THAT(loaded_arsc, NotNull());
 
   const auto& packages = loaded_arsc->GetPackages();
@@ -159,7 +163,8 @@
   std::string contents;
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc",
                                       &contents));
-  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+                                                                   contents.length());
   ASSERT_THAT(loaded_arsc, NotNull());
 
   const LoadedPackage* package =
@@ -172,15 +177,12 @@
   const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
   ASSERT_THAT(type_spec, NotNull());
   ASSERT_THAT(type_spec->type_count, Ge(1u));
-  ASSERT_THAT(type_spec->types[0], NotNull());
 
-  size_t len;
-  const char16_t* type_name16 =
-      package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len);
-  ASSERT_THAT(type_name16, NotNull());
-  EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string"));
+  auto type_name16 = package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1);
+  ASSERT_TRUE(type_name16.has_value());
+  EXPECT_THAT(util::Utf16ToUtf8(*type_name16), StrEq("string"));
 
-  ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull());
+  ASSERT_TRUE(LoadedPackage::GetEntry(type_spec->types[0], entry_index).has_value());
 }
 
 // AAPT(2) generates resource tables with chunks in a certain order. The rule is that
@@ -205,7 +207,8 @@
       ReadFileFromZipToString(GetTestDataPath() + "/out_of_order_types/out_of_order_types.apk",
                               "resources.arsc", &contents));
 
-  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+                                                                   contents.length());
   ASSERT_THAT(loaded_arsc, NotNull());
 
   ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u));
@@ -215,12 +218,10 @@
   const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0);
   ASSERT_THAT(type_spec, NotNull());
   ASSERT_THAT(type_spec->type_count, Ge(1u));
-  ASSERT_THAT(type_spec->types[0], NotNull());
 
   type_spec = package->GetTypeSpecByTypeIndex(1);
   ASSERT_THAT(type_spec, NotNull());
   ASSERT_THAT(type_spec->type_count, Ge(1u));
-  ASSERT_THAT(type_spec->types[0], NotNull());
 }
 
 TEST(LoadedArscTest, LoadOverlayable) {
@@ -228,7 +229,8 @@
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
                                       "resources.arsc", &contents));
 
-  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+                                                                   contents.length());
 
   ASSERT_THAT(loaded_arsc, NotNull());
   const LoadedPackage* package = loaded_arsc->GetPackageById(
@@ -272,7 +274,8 @@
   ASSERT_TRUE(
       ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents));
 
-  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+                                                                   contents.length());
   ASSERT_NE(nullptr, loaded_arsc);
 
   const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages();
@@ -320,7 +323,8 @@
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
                                       "resources.arsc", &contents));
 
-  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+                                                                   contents.length());
   ASSERT_NE(nullptr, loaded_arsc);
 
   const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages();
@@ -345,7 +349,7 @@
       asset->getLength());
 
   std::unique_ptr<const LoadedArsc> loaded_arsc =
-      LoadedArsc::Load(data, nullptr, PROPERTY_LOADER);
+      LoadedArsc::Load(data.data(), data.length(), nullptr, PROPERTY_LOADER);
   ASSERT_THAT(loaded_arsc, NotNull());
 
   const LoadedPackage* package =
@@ -361,9 +365,8 @@
   ASSERT_THAT(type_spec, NotNull());
   ASSERT_THAT(type_spec->type_count, Ge(1u));
 
-  const ResTable_type* type = type_spec->types[0];
-  ASSERT_THAT(type, NotNull());
-  ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
+  auto type = type_spec->types[0];
+  ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value());
 }
 
 // structs with size fields (like Res_value, ResTable_entry) should be
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index 326474e..9aeb00c 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -442,22 +442,22 @@
   ASSERT_LT(val.data, pool->size());
 
   // Make sure a string with a truncated length is read to its correct length
-  size_t str_len;
-  const char* target_str8 = pool->string8At(val.data, &str_len);
-  ASSERT_TRUE(target_str8 != NULL);
-  ASSERT_EQ(size_t(40076), String8(target_str8, str_len).size());
-  ASSERT_EQ(target_str8[40075], ']');
+  auto target_str8 = pool->string8At(val.data);
+  ASSERT_TRUE(target_str8.has_value());
+  ASSERT_EQ(size_t(40076), String8(target_str8->data(), target_str8->size()).size());
+  ASSERT_EQ(target_str8->data()[40075], ']');
 
-  const char16_t* target_str16 = pool->stringAt(val.data, &str_len);
-  ASSERT_TRUE(target_str16 != NULL);
-  ASSERT_EQ(size_t(40076), String16(target_str16, str_len).size());
-  ASSERT_EQ(target_str8[40075], (char16_t) ']');
+  auto target_str16 = pool->stringAt(val.data);
+  ASSERT_TRUE(target_str16.has_value());
+  ASSERT_EQ(size_t(40076), String16(target_str16->data(), target_str16->size()).size());
+  ASSERT_EQ(target_str8->data()[40075], (char16_t) ']');
 
   // Load an edited apk with the null terminator removed from the end of the
   // string
   std::string invalid_contents;
-  ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/length_decode/length_decode_invalid.apk",
-                                      "resources.arsc", &invalid_contents));
+  ASSERT_TRUE(ReadFileFromZipToString(
+      GetTestDataPath() + "/length_decode/length_decode_invalid.apk", "resources.arsc",
+      &invalid_contents));
   ResTable invalid_table;
   ASSERT_EQ(NO_ERROR, invalid_table.add(invalid_contents.data(), invalid_contents.size()));
 
@@ -472,8 +472,8 @@
 
   // Make sure a string with a truncated length that is not null terminated errors
   // and does not return the string
-  ASSERT_TRUE(invalid_pool->string8At(invalid_val.data, &str_len) == NULL);
-  ASSERT_TRUE(invalid_pool->stringAt(invalid_val.data, &str_len) == NULL);
+  ASSERT_FALSE(invalid_pool->string8At(invalid_val.data).has_value());
+  ASSERT_FALSE(invalid_pool->stringAt(invalid_val.data).has_value());
 }
 
 }  // namespace android
diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp
index a81bb6f..10c0a4f 100644
--- a/libs/androidfw/tests/TestHelpers.cpp
+++ b/libs/androidfw/tests/TestHelpers.cpp
@@ -73,11 +73,15 @@
     return AssertionFailure() << "table has no string pool for block " << block;
   }
 
-  const String8 actual_str = pool->string8ObjectAt(val.data);
-  if (String8(expected_str) != actual_str) {
-    return AssertionFailure() << actual_str.string();
+  auto actual_str = pool->string8ObjectAt(val.data);
+  if (!actual_str.has_value()) {
+    return AssertionFailure() << "could not find string entry";
   }
-  return AssertionSuccess() << actual_str.string();
+
+  if (String8(expected_str) != *actual_str) {
+    return AssertionFailure() << actual_str->string();
+  }
+  return AssertionSuccess() << actual_str->string();
 }
 
 }  // namespace android
diff --git a/libs/androidfw/tests/Theme_bench.cpp b/libs/androidfw/tests/Theme_bench.cpp
index 594c39e..f3d60bb 100644
--- a/libs/androidfw/tests/Theme_bench.cpp
+++ b/libs/androidfw/tests/Theme_bench.cpp
@@ -70,11 +70,8 @@
   auto theme = assets.NewTheme();
   theme->ApplyStyle(kStyleId, false /* force */);
 
-  Res_value value;
-  uint32_t flags;
-
   while (state.KeepRunning()) {
-    theme->GetAttribute(kAttrId, &value, &flags);
+    theme->GetAttribute(kAttrId);
   }
 }
 BENCHMARK(BM_ThemeGetAttribute);
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index 16b9c75..f658735 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -67,10 +67,7 @@
   std::unique_ptr<Theme> theme = assetmanager.NewTheme();
   EXPECT_EQ(0u, theme->GetChangingConfigurations());
   EXPECT_EQ(&assetmanager, theme->GetAssetManager());
-
-  Res_value value;
-  uint32_t flags;
-  EXPECT_EQ(kInvalidCookie, theme->GetAttribute(app::R::attr::attr_one, &value, &flags));
+  EXPECT_FALSE(theme->GetAttribute(app::R::attr::attr_one).has_value());
 }
 
 TEST_F(ThemeTest, SingleThemeNoParent) {
@@ -78,23 +75,19 @@
   assetmanager.SetApkAssets({style_assets_.get()});
 
   std::unique_ptr<Theme> theme = assetmanager.NewTheme();
-  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne));
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne).has_value());
 
-  Res_value value;
-  uint32_t flags;
-  ApkAssetsCookie cookie;
+  auto value = theme->GetAttribute(app::R::attr::attr_one);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+  EXPECT_EQ(1u, value->data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
 
-  cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
-  EXPECT_EQ(1u, value.data);
-  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
-
-  cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
-  EXPECT_EQ(2u, value.data);
-  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+  value = theme->GetAttribute(app::R::attr::attr_two);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+  EXPECT_EQ(2u, value->data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
 }
 
 TEST_F(ThemeTest, SingleThemeWithParent) {
@@ -102,32 +95,28 @@
   assetmanager.SetApkAssets({style_assets_.get()});
 
   std::unique_ptr<Theme> theme = assetmanager.NewTheme();
-  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value());
 
-  Res_value value;
-  uint32_t flags;
-  ApkAssetsCookie cookie;
+  auto value = theme->GetAttribute(app::R::attr::attr_one);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+  EXPECT_EQ(1u, value->data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
 
-  cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
-  EXPECT_EQ(1u, value.data);
-  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
-
-  cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
-  EXPECT_EQ(0, cookie);
+  value = theme->GetAttribute(app::R::attr::attr_two);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(Res_value::TYPE_STRING, value->type);
+  EXPECT_EQ(0, value->cookie);
   EXPECT_EQ(std::string("string"),
-            GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value.data));
-  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+            GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value->data));
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
 
   // This attribute should point to an attr_indirect, so the result should be 3.
-  cookie = theme->GetAttribute(app::R::attr::attr_three, &value, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
-  EXPECT_EQ(3u, value.data);
-  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+  value = theme->GetAttribute(app::R::attr::attr_three);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+  EXPECT_EQ(3u, value->data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
 }
 
 TEST_F(ThemeTest, TryToUseBadResourceId) {
@@ -135,11 +124,8 @@
   assetmanager.SetApkAssets({style_assets_.get()});
 
   std::unique_ptr<Theme> theme = assetmanager.NewTheme();
-  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
-
-  Res_value value;
-  uint32_t flags;
-  ASSERT_EQ(kInvalidCookie, theme->GetAttribute(0x7f000001, &value, &flags));
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value());
+  ASSERT_FALSE(theme->GetAttribute(0x7f000001));
 }
 
 TEST_F(ThemeTest, MultipleThemesOverlaidNotForce) {
@@ -147,33 +133,29 @@
   assetmanager.SetApkAssets({style_assets_.get()});
 
   std::unique_ptr<Theme> theme = assetmanager.NewTheme();
-  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
-  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree));
-
-  Res_value value;
-  uint32_t flags;
-  ApkAssetsCookie cookie;
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value());
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree).has_value());
 
   // attr_one is still here from the base.
-  cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
-  EXPECT_EQ(1u, value.data);
-  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+  auto value = theme->GetAttribute(app::R::attr::attr_one);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+  EXPECT_EQ(1u, value->data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
 
   // check for the new attr_six
-  cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
-  EXPECT_EQ(6u, value.data);
-  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+  value = theme->GetAttribute(app::R::attr::attr_six);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+  EXPECT_EQ(6u, value->data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
 
   // check for the old attr_five (force=true was not used).
-  cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
-  EXPECT_EQ(app::R::string::string_one, value.data);
-  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+  value = theme->GetAttribute(app::R::attr::attr_five);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+  EXPECT_EQ(app::R::string::string_one, value->data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
 }
 
 TEST_F(ThemeTest, MultipleThemesOverlaidForced) {
@@ -181,33 +163,29 @@
   assetmanager.SetApkAssets({style_assets_.get()});
 
   std::unique_ptr<Theme> theme = assetmanager.NewTheme();
-  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
-  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree, true /* force */));
-
-  Res_value value;
-  uint32_t flags;
-  ApkAssetsCookie cookie;
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value());
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree, true /* force */).has_value());
 
   // attr_one is still here from the base.
-  cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
-  EXPECT_EQ(1u, value.data);
-  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+  auto value = theme->GetAttribute(app::R::attr::attr_one);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+  EXPECT_EQ(1u, value->data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
 
   // check for the new attr_six
-  cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
-  EXPECT_EQ(6u, value.data);
-  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+  value = theme->GetAttribute(app::R::attr::attr_six);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+  EXPECT_EQ(6u, value->data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
 
   // check for the new attr_five (force=true was used).
-  cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
-  EXPECT_EQ(5u, value.data);
-  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+  value = theme->GetAttribute(app::R::attr::attr_five);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+  EXPECT_EQ(5u, value->data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
 }
 
 TEST_F(ThemeTest, ResolveDynamicAttributesAndReferencesToSharedLibrary) {
@@ -216,28 +194,24 @@
       {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
 
   std::unique_ptr<Theme> theme = assetmanager.NewTheme();
-  ASSERT_TRUE(theme->ApplyStyle(libclient::R::style::Theme, false /*force*/));
-
-  Res_value value;
-  uint32_t flags;
-  ApkAssetsCookie cookie;
+  ASSERT_TRUE(theme->ApplyStyle(libclient::R::style::Theme, false /*force*/).has_value());
 
   // The attribute should be resolved to the final value.
-  cookie = theme->GetAttribute(libclient::R::attr::foo, &value, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
-  EXPECT_EQ(700u, value.data);
-  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+  auto value = theme->GetAttribute(libclient::R::attr::foo);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+  EXPECT_EQ(700u, value->data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
 
   // The reference should be resolved to a TYPE_REFERENCE.
-  cookie = theme->GetAttribute(libclient::R::attr::bar, &value, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+  value = theme->GetAttribute(libclient::R::attr::bar);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
 
   // lib_one is assigned package ID 0x03.
-  EXPECT_EQ(3u, get_package_id(value.data));
-  EXPECT_EQ(get_type_id(lib_one::R::string::foo), get_type_id(value.data));
-  EXPECT_EQ(get_entry_id(lib_one::R::string::foo), get_entry_id(value.data));
+  EXPECT_EQ(3u, get_package_id(value->data));
+  EXPECT_EQ(get_type_id(lib_one::R::string::foo), get_type_id(value->data));
+  EXPECT_EQ(get_entry_id(lib_one::R::string::foo), get_entry_id(value->data));
 }
 
 TEST_F(ThemeTest, CopyThemeSameAssetManager) {
@@ -245,24 +219,20 @@
   assetmanager.SetApkAssets({style_assets_.get()});
 
   std::unique_ptr<Theme> theme_one = assetmanager.NewTheme();
-  ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne));
-
-  Res_value value;
-  uint32_t flags;
-  ApkAssetsCookie cookie;
+  ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne).has_value());
 
   // attr_one is still here from the base.
-  cookie = theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
-  EXPECT_EQ(1u, value.data);
-  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+  auto value = theme_one->GetAttribute(app::R::attr::attr_one);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+  EXPECT_EQ(1u, value->data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
 
   // attr_six is not here.
-  EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags));
+  ASSERT_FALSE(theme_one->GetAttribute(app::R::attr::attr_six).has_value());
 
   std::unique_ptr<Theme> theme_two = assetmanager.NewTheme();
-  ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree));
+  ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree).has_value());
 
   // Copy the theme to theme_one.
   theme_one->SetTo(*theme_two);
@@ -271,14 +241,14 @@
   theme_two->Clear();
 
   // attr_one is now not here.
-  EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags));
+  ASSERT_FALSE(theme_one->GetAttribute(app::R::attr::attr_one).has_value());
 
   // attr_six is now here because it was copied.
-  cookie = theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags);
-  ASSERT_NE(kInvalidCookie, cookie);
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
-  EXPECT_EQ(6u, value.data);
-  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+  value = theme_one->GetAttribute(app::R::attr::attr_six);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+  EXPECT_EQ(6u, value->data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
 }
 
 TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) {
@@ -291,39 +261,43 @@
                                  style_assets_.get()});
 
   auto theme_dst = assetmanager_dst.NewTheme();
-  ASSERT_TRUE(theme_dst->ApplyStyle(app::R::style::StyleOne));
+  ASSERT_TRUE(theme_dst->ApplyStyle(app::R::style::StyleOne).has_value());
 
   auto theme_src = assetmanager_src.NewTheme();
-  ASSERT_TRUE(theme_src->ApplyStyle(R::style::Theme_One));
-  ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleTwo));
+  ASSERT_TRUE(theme_src->ApplyStyle(R::style::Theme_One).has_value());
+  ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleTwo).has_value());
   ASSERT_TRUE(theme_src->ApplyStyle(fix_package_id(lib_one::R::style::Theme, 0x03),
-                                    false /*force*/));
+                                    false /*force*/).has_value());
   ASSERT_TRUE(theme_src->ApplyStyle(fix_package_id(lib_two::R::style::Theme, 0x02),
-                                    false /*force*/));
+                                    false /*force*/).has_value());
 
   theme_dst->SetTo(*theme_src);
 
-  Res_value value;
-  uint32_t flags;
-
   // System resources (present in destination asset manager).
-  EXPECT_EQ(0, theme_dst->GetAttribute(R::attr::foreground, &value, &flags));
+  auto value = theme_dst->GetAttribute(R::attr::foreground);
+  ASSERT_TRUE(value.has_value());
+  EXPECT_EQ(0, value->cookie);
 
   // The cookie of the style asset is 3 in the source and 2 in the destination.
   // Check that the cookie has been rewritten to the destination values.
-  EXPECT_EQ(2, theme_dst->GetAttribute(app::R::attr::attr_one, &value, &flags));
+  value = theme_dst->GetAttribute(app::R::attr::attr_one);
+  ASSERT_TRUE(value.has_value());
+  EXPECT_EQ(2, value->cookie);
 
   // The cookie of the lib_one asset is 2 in the source and 1 in the destination.
   // The package id of the lib_one package is 0x03 in the source and 0x02 in the destination
   // Check that the cookie and packages have been rewritten to the destination values.
-  EXPECT_EQ(1, theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02), &value,
-                                       &flags));
-  EXPECT_EQ(1, theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02), &value,
-                                       &flags));
+  value = theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02));
+  ASSERT_TRUE(value.has_value());
+  EXPECT_EQ(1, value->cookie);
+
+  value = theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02));
+  ASSERT_TRUE(value.has_value());
+  EXPECT_EQ(1, value->cookie);
 
   // attr2 references an attribute in lib_one. Check that the resolution of the attribute value is
   // correct after the value of attr2 had its package id rewritten to the destination package id.
-  EXPECT_EQ(700, value.data);
+  EXPECT_EQ(700, value->data);
 }
 
 TEST_F(ThemeTest, CopyNonReferencesWhenPackagesDiffer) {
@@ -335,28 +309,32 @@
 
   auto theme_dst = assetmanager_dst.NewTheme();
   auto theme_src = assetmanager_src.NewTheme();
-  ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleSeven));
+  ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleSeven).has_value());
   theme_dst->SetTo(*theme_src);
 
-  Res_value value;
-  uint32_t flags;
-
   // Allow inline resource values to be copied even if the source apk asset is not present in the
   // destination.
-  EXPECT_EQ(0, theme_dst->GetAttribute(0x0101021b /* android:versionCode */, &value, &flags));
+  auto value = theme_dst->GetAttribute(0x0101021b /* android:versionCode */);
+  ASSERT_TRUE(value.has_value());
+  EXPECT_EQ(0, value->cookie);
 
   // Do not copy strings since the data is an index into the values string pool of the source apk
   // asset.
-  EXPECT_EQ(-1, theme_dst->GetAttribute(0x01010001 /* android:label */, &value, &flags));
+  EXPECT_FALSE(theme_dst->GetAttribute(0x01010001 /* android:label */).has_value());
 
   // Do not copy values that reference another resource if the resource is not present in the
   // destination.
-  EXPECT_EQ(-1, theme_dst->GetAttribute(0x01010002 /* android:icon */, &value, &flags));
-  EXPECT_EQ(-1, theme_dst->GetAttribute(0x010100d1 /* android:tag */, &value, &flags));
+  EXPECT_FALSE(theme_dst->GetAttribute(0x01010002 /* android:icon */).has_value());
+  EXPECT_FALSE(theme_dst->GetAttribute(0x010100d1 /* android:tag */).has_value());
 
   // Allow @empty to and @null to be copied.
-  EXPECT_EQ(0, theme_dst->GetAttribute(0x010100d0 /* android:id */, &value, &flags));
-  EXPECT_EQ(0, theme_dst->GetAttribute(0x01010000 /* android:theme */, &value, &flags));
+  value = theme_dst->GetAttribute(0x010100d0 /* android:id */);
+  ASSERT_TRUE(value.has_value());
+  EXPECT_EQ(0, value->cookie);
+
+  value = theme_dst->GetAttribute(0x01010000 /* android:theme */);
+  ASSERT_TRUE(value.has_value());
+  EXPECT_EQ(0, value->cookie);
 }
 
 }  // namespace android