libandroidfw hardening for IncFs

Migrate libandroifw to using incfs::util::map_ptr to prevent processes
from crashing when parsing the resources.arsc, parsing compiled xml,
files, and retrieving resource values.

This change propagates incremental failures to the JNI level where they
are raised as ResourcesNotFoundException.

Performance of ResourcesPerfWorkloads without change (time in
nanoseconds):
[1/3] com.android.resources.perf.PerfTest#youtube: PASSED (11.883s)
    youtube_ns_median: 93812805
    youtube_ns_standardDeviation: 4387062
    youtube_ns_mean: 94455597
[2/3] com.android.resources.perf.PerfTest#maps: PASSED (11.265s)
    maps_ns_standardDeviation: 2997543
    maps_ns_mean: 83480371
    maps_ns_median: 82210941
[3/3] com.android.resources.perf.PerfTest#gmail: PASSED (24.963s)
    gmail_ns_median: 266141091
    gmail_ns_standardDeviation: 3492043
    gmail_ns_mean: 267472765

With change and verification forcibly enabled for all apks
(including the framework-res.apk):
[1/3] com.android.resources.perf.PerfTest#youtube: PASSED (11.646s)
    youtube_ns_median: 101999396
    youtube_ns_standardDeviation: 4625782
    youtube_ns_mean: 102631770
[2/3] com.android.resources.perf.PerfTest#maps: PASSED (11.286s)
    maps_ns_standardDeviation: 2692088
    maps_ns_mean: 91326538
    maps_ns_median: 90519884
[3/3] com.android.resources.perf.PerfTest#gmail: PASSED (24.694s)
    gmail_ns_median: 290284442
    gmail_ns_standardDeviation: 5764632
    gmail_ns_mean: 291660464

With change and verification disabled:
[1/3] com.android.resources.perf.PerfTest#youtube: PASSED (11.748s)
    youtube_ns_median: 95490747
    youtube_ns_standardDeviation: 7282249
    youtube_ns_mean: 98442515
[2/3] com.android.resources.perf.PerfTest#maps: PASSED (10.862s)
    maps_ns_standardDeviation: 4484213
    maps_ns_mean: 87912988
    maps_ns_median: 86325549
[3/3] com.android.resources.perf.PerfTest#gmail: PASSED (24.034s)
    gmail_ns_median: 282175838
    gmail_ns_standardDeviation: 6560876
    gmail_ns_mean: 282869146

These tests were done on a Pixel 3 and with cpu settings configured by
libs/hwui/tests/scripts/prep_generic.sh:

 Locked CPUs 4,5,6,7 to 1459200 / 2803200 KHz
 Disabled CPUs 0,1,2,3

Bug: 160635104
Bug: 169423204
Test: boot device && atest ResourcesPerfWorkloads

Change-Id: I5cd1bc8a2257bffaba6ca4a1c96f4e6640106866
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 903ca2a..696e227 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