Merge "Add SystemUI to OWNERS for Sharesheet."
diff --git a/OWNERS b/OWNERS
index f5eff4b..4160122 100644
--- a/OWNERS
+++ b/OWNERS
@@ -18,6 +18,10 @@
 svetoslavganov@google.com
 yamasani@google.com
 
+# API changes are already covered by API-Review+1 (http://mdb/android-api-council)
+# via https://android.git.corp.google.com/All-Projects/+/refs/meta/config/rules.pl.
+per-file */api/*current.txt = *
+
 # Support bulk translation updates
 per-file */res*/values*/*.xml = byi@google.com, delphij@google.com
 
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 878cef9..e21a6b2 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -181,7 +181,6 @@
         "idmap2/Dump.cpp",
         "idmap2/Lookup.cpp",
         "idmap2/Main.cpp",
-        "idmap2/Scan.cpp",
     ],
     target: {
         android: {
diff --git a/cmds/idmap2/idmap2/Commands.h b/cmds/idmap2/idmap2/Commands.h
index 69eea8d..4099671 100644
--- a/cmds/idmap2/idmap2/Commands.h
+++ b/cmds/idmap2/idmap2/Commands.h
@@ -26,6 +26,5 @@
 android::idmap2::Result<android::idmap2::Unit> CreateMultiple(const std::vector<std::string>& args);
 android::idmap2::Result<android::idmap2::Unit> Dump(const std::vector<std::string>& args);
 android::idmap2::Result<android::idmap2::Unit> Lookup(const std::vector<std::string>& args);
-android::idmap2::Result<android::idmap2::Unit> Scan(const std::vector<std::string>& args);
 
 #endif  // IDMAP2_IDMAP2_COMMANDS_H_
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index c441709..8323d0b 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -45,11 +45,8 @@
 using android::ApkAssetsCookie;
 using android::AssetManager2;
 using android::ConfigDescription;
-using android::is_valid_resid;
-using android::kInvalidCookie;
 using android::Res_value;
 using android::ResStringPool;
-using android::ResTable_config;
 using android::StringPiece16;
 using android::base::StringPrintf;
 using android::idmap2::CommandLineOptions;
@@ -59,7 +56,6 @@
 using android::idmap2::Result;
 using android::idmap2::Unit;
 using android::idmap2::utils::ExtractOverlayManifestInfo;
-using android::util::Utf16ToUtf8;
 
 namespace {
 
@@ -69,25 +65,23 @@
 
   // first, try to parse as a hex number
   char* endptr = nullptr;
-  ResourceId resid;
-  resid = strtol(res.c_str(), &endptr, kBaseHex);
+  const ResourceId parsed_resid = strtol(res.c_str(), &endptr, kBaseHex);
   if (*endptr == '\0') {
-    return resid;
+    return parsed_resid;
   }
 
   // next, try to parse as a package:type/name string
-  resid = am.GetResourceId(res, "", fallback_package);
-  if (is_valid_resid(resid)) {
-    return resid;
+  if (auto resid = am.GetResourceId(res, "", fallback_package)) {
+    return *resid;
   }
 
   // end of the road: res could not be parsed
   return Error("failed to obtain resource id for %s", res.c_str());
 }
 
-void PrintValue(AssetManager2* const am, const Res_value& value, const ApkAssetsCookie& cookie,
+void PrintValue(AssetManager2* const am, const AssetManager2::SelectedValue& value,
                 std::string* const out) {
-  switch (value.dataType) {
+  switch (value.type) {
     case Res_value::TYPE_INT_DEC:
       out->append(StringPrintf("%d", value.data));
       break;
@@ -98,30 +92,21 @@
       out->append(value.data != 0 ? "true" : "false");
       break;
     case Res_value::TYPE_STRING: {
-      const ResStringPool* pool = am->GetStringPoolForCookie(cookie);
+      const ResStringPool* pool = am->GetStringPoolForCookie(value.cookie);
       out->append("\"");
-      size_t len;
-      if (pool->isUTF8()) {
-        const char* str = pool->string8At(value.data, &len);
-        out->append(str, len);
-      } else {
-        const char16_t* str16 = pool->stringAt(value.data, &len);
-        out->append(Utf16ToUtf8(StringPiece16(str16, len)));
+      if (auto str = pool->string8ObjectAt(value.data)) {
+        out->append(*str);
       }
-      out->append("\"");
     } break;
     default:
-      out->append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
+      out->append(StringPrintf("dataType=0x%02x data=0x%08x", value.type, value.data));
       break;
   }
 }
 
 Result<std::string> WARN_UNUSED GetValue(AssetManager2* const am, ResourceId resid) {
-  Res_value value;
-  ResTable_config config;
-  uint32_t flags;
-  ApkAssetsCookie cookie = am->GetResource(resid, true, 0, &value, &config, &flags);
-  if (cookie == kInvalidCookie) {
+  auto value = am->GetResource(resid);
+  if (!value.has_value()) {
     return Error("no resource 0x%08x in asset manager", resid);
   }
 
@@ -129,41 +114,35 @@
 
   // TODO(martenkongstad): use optional parameter GetResource(..., std::string*
   // stacktrace = NULL) instead
-  out.append(StringPrintf("cookie=%d ", cookie));
+  out.append(StringPrintf("cookie=%d ", value->cookie));
 
   out.append("config='");
-  out.append(config.toString().c_str());
+  out.append(value->config.toString().c_str());
   out.append("' value=");
 
-  if (value.dataType == Res_value::TYPE_REFERENCE) {
-    const android::ResolvedBag* bag = am->GetBag(static_cast<uint32_t>(value.data));
-    if (bag == nullptr) {
-      out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
+  if (value->type == Res_value::TYPE_REFERENCE) {
+    auto bag_result = am->GetBag(static_cast<uint32_t>(value->data));
+    if (!bag_result.has_value()) {
+      out.append(StringPrintf("dataType=0x%02x data=0x%08x", value->type, value->data));
       return out;
     }
+
     out.append("[");
-    Res_value bag_val;
-    ResTable_config selected_config;
-    uint32_t flags;
-    uint32_t ref;
-    ApkAssetsCookie bag_cookie;
+    const android::ResolvedBag* bag = bag_result.value();
     for (size_t i = 0; i < bag->entry_count; ++i) {
-      const android::ResolvedBag::Entry& entry = bag->entries[i];
-      bag_val = entry.value;
-      bag_cookie = am->ResolveReference(entry.cookie, &bag_val, &selected_config, &flags, &ref);
-      if (bag_cookie == kInvalidCookie) {
-        out.append(
-            StringPrintf("Error: dataType=0x%02x data=0x%08x", bag_val.dataType, bag_val.data));
+      AssetManager2::SelectedValue entry(bag, bag->entries[i]);
+      if (am->ResolveReference(entry).has_value()) {
+        out.append(StringPrintf("Error: dataType=0x%02x data=0x%08x", entry.type, entry.data));
         continue;
       }
-      PrintValue(am, bag_val, bag_cookie, &out);
+      PrintValue(am, entry, &out);
       if (i != bag->entry_count - 1) {
         out.append(", ");
       }
     }
     out.append("]");
   } else {
-    PrintValue(am, value, cookie, &out);
+    PrintValue(am, *value, &out);
   }
 
   return out;
diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp
index fb093f0..aa6d0e7 100644
--- a/cmds/idmap2/idmap2/Main.cpp
+++ b/cmds/idmap2/idmap2/Main.cpp
@@ -53,8 +53,10 @@
 int main(int argc, char** argv) {
   SYSTRACE << "main";
   const NameToFunctionMap commands = {
-      {"create", Create}, {"create-multiple", CreateMultiple}, {"dump", Dump}, {"lookup", Lookup},
-      {"scan", Scan},
+      {"create", Create},
+      {"create-multiple", CreateMultiple},
+      {"dump", Dump},
+      {"lookup", Lookup},
   };
   if (argc <= 1) {
     PrintUsage(commands, std::cerr);
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
deleted file mode 100644
index 3625045..0000000
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <dirent.h>
-
-#include <fstream>
-#include <memory>
-#include <ostream>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "Commands.h"
-#include "android-base/properties.h"
-#include "idmap2/CommandLineOptions.h"
-#include "idmap2/CommandUtils.h"
-#include "idmap2/FileUtils.h"
-#include "idmap2/Idmap.h"
-#include "idmap2/Policies.h"
-#include "idmap2/PolicyUtils.h"
-#include "idmap2/ResourceUtils.h"
-#include "idmap2/Result.h"
-#include "idmap2/SysTrace.h"
-#include "idmap2/XmlParser.h"
-
-using android::idmap2::CommandLineOptions;
-using android::idmap2::Error;
-using android::idmap2::Idmap;
-using android::idmap2::Result;
-using android::idmap2::Unit;
-using android::idmap2::policy::kPolicyOdm;
-using android::idmap2::policy::kPolicyOem;
-using android::idmap2::policy::kPolicyProduct;
-using android::idmap2::policy::kPolicyPublic;
-using android::idmap2::policy::kPolicySystem;
-using android::idmap2::policy::kPolicyVendor;
-using android::idmap2::utils::ExtractOverlayManifestInfo;
-using android::idmap2::utils::FindFiles;
-using android::idmap2::utils::OverlayManifestInfo;
-using android::idmap2::utils::PoliciesToBitmaskResult;
-
-using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
-
-namespace {
-
-struct InputOverlay {
-  bool operator<(InputOverlay const& rhs) const {
-    return priority < rhs.priority || (priority == rhs.priority && apk_path < rhs.apk_path);
-  }
-
-  std::string apk_path;               // NOLINT(misc-non-private-member-variables-in-classes)
-  std::string idmap_path;             // NOLINT(misc-non-private-member-variables-in-classes)
-  int priority;                       // NOLINT(misc-non-private-member-variables-in-classes)
-  std::vector<std::string> policies;  // NOLINT(misc-non-private-member-variables-in-classes)
-  bool ignore_overlayable;            // NOLINT(misc-non-private-member-variables-in-classes)
-};
-
-bool VendorIsQOrLater() {
-  constexpr int kQSdkVersion = 29;
-  constexpr int kBase = 10;
-  std::string version_prop = android::base::GetProperty("ro.vndk.version", "29");
-  int version = strtol(version_prop.data(), nullptr, kBase);
-
-  // If the string cannot be parsed, it is a development sdk codename.
-  return version >= kQSdkVersion || version == 0;
-}
-
-Result<std::unique_ptr<std::vector<std::string>>> FindApkFiles(const std::vector<std::string>& dirs,
-                                                               bool recursive) {
-  SYSTRACE << "FindApkFiles " << dirs << " " << recursive;
-  const auto predicate = [](unsigned char type, const std::string& path) -> bool {
-    static constexpr size_t kExtLen = 4;  // strlen(".apk")
-    return type == DT_REG && path.size() > kExtLen &&
-           path.compare(path.size() - kExtLen, kExtLen, ".apk") == 0;
-  };
-  // pass apk paths through a set to filter out duplicates
-  std::set<std::string> paths;
-  for (const auto& dir : dirs) {
-    const auto apk_paths = FindFiles(dir, recursive, predicate);
-    if (!apk_paths) {
-      return Error("failed to open directory %s", dir.c_str());
-    }
-    paths.insert(apk_paths->cbegin(), apk_paths->cend());
-  }
-  return std::make_unique<std::vector<std::string>>(paths.cbegin(), paths.cend());
-}
-
-std::vector<std::string> PoliciesForPath(const std::string& apk_path) {
-  // clang-format off
-  static const std::vector<std::pair<std::string, std::string>> values = {
-      {"/odm/", kPolicyOdm},
-      {"/oem/", kPolicyOem},
-      {"/product/", kPolicyProduct},
-      {"/system/", kPolicySystem},
-      {"/system_ext/", kPolicySystem},
-      {"/vendor/", kPolicyVendor},
-  };
-  // clang-format on
-
-  std::vector<std::string> fulfilled_policies = {kPolicyPublic};
-  for (auto const& pair : values) {
-    if (apk_path.compare(0, pair.first.size(), pair.first) == 0) {
-      fulfilled_policies.emplace_back(pair.second);
-      break;
-    }
-  }
-
-  return fulfilled_policies;
-}
-
-}  // namespace
-
-Result<Unit> Scan(const std::vector<std::string>& args) {
-  SYSTRACE << "Scan " << args;
-  std::vector<std::string> input_directories;
-  std::string target_package_name;
-  std::string target_apk_path;
-  std::string output_directory;
-  std::vector<std::string> override_policies;
-  bool recursive = false;
-
-  const CommandLineOptions opts =
-      CommandLineOptions("idmap2 scan")
-          .MandatoryOption("--input-directory", "directory containing overlay apks to scan",
-                           &input_directories)
-          .OptionalFlag("--recursive", "also scan subfolders of overlay-directory", &recursive)
-          .MandatoryOption("--target-package-name", "package name of target package",
-                           &target_package_name)
-          .MandatoryOption("--target-apk-path", "path to target apk", &target_apk_path)
-          .MandatoryOption("--output-directory",
-                           "directory in which to write artifacts (idmap files and overlays.list)",
-                           &output_directory)
-          .OptionalOption(
-              "--override-policy",
-              "input: an overlayable policy this overlay fulfills "
-              "(if none or supplied, the overlays will not have their policies overriden",
-              &override_policies);
-  const auto opts_ok = opts.Parse(args);
-  if (!opts_ok) {
-    return opts_ok.GetError();
-  }
-
-  const auto apk_paths = FindApkFiles(input_directories, recursive);
-  if (!apk_paths) {
-    return Error(apk_paths.GetError(), "failed to find apk files");
-  }
-
-  std::vector<InputOverlay> interesting_apks;
-  for (const std::string& path : **apk_paths) {
-    Result<OverlayManifestInfo> overlay_info =
-        ExtractOverlayManifestInfo(path, /* assert_overlay */ false);
-    if (!overlay_info) {
-      return overlay_info.GetError();
-    }
-
-    if (!overlay_info->is_static) {
-      continue;
-    }
-
-    if (overlay_info->target_package.empty() ||
-        overlay_info->target_package != target_package_name) {
-      continue;
-    }
-
-    if (overlay_info->priority < 0) {
-      continue;
-    }
-
-    // Note that conditional property enablement/exclusion only applies if
-    // the attribute is present. In its absence, all overlays are presumed enabled.
-    if (!overlay_info->requiredSystemPropertyName.empty() &&
-        !overlay_info->requiredSystemPropertyValue.empty()) {
-      // if property set & equal to value, then include overlay - otherwise skip
-      if (android::base::GetProperty(overlay_info->requiredSystemPropertyName, "") !=
-          overlay_info->requiredSystemPropertyValue) {
-        continue;
-      }
-    }
-
-    std::vector<std::string> fulfilled_policies;
-    if (!override_policies.empty()) {
-      fulfilled_policies = override_policies;
-    } else {
-      fulfilled_policies = PoliciesForPath(path);
-    }
-
-    bool ignore_overlayable = false;
-    if (std::find(fulfilled_policies.begin(), fulfilled_policies.end(), kPolicyVendor) !=
-            fulfilled_policies.end() &&
-        !VendorIsQOrLater()) {
-      // If the overlay is on a pre-Q vendor partition, do not enforce overlayable
-      // restrictions on this overlay because the pre-Q platform has no understanding of
-      // overlayable.
-      ignore_overlayable = true;
-    }
-
-    std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, path);
-
-    // Sort the static overlays in ascending priority order
-    InputOverlay input{path, idmap_path, overlay_info->priority, fulfilled_policies,
-                       ignore_overlayable};
-    interesting_apks.insert(
-        std::lower_bound(interesting_apks.begin(), interesting_apks.end(), input), input);
-  }
-
-  std::stringstream stream;
-  for (const auto& overlay : interesting_apks) {
-    const auto policy_bitmask = PoliciesToBitmaskResult(overlay.policies);
-    if (!policy_bitmask) {
-      LOG(WARNING) << "failed to create idmap for overlay apk path \"" << overlay.apk_path
-                   << "\": " << policy_bitmask.GetErrorMessage();
-      continue;
-    }
-
-    if (!Verify(overlay.idmap_path, target_apk_path, overlay.apk_path, *policy_bitmask,
-                !overlay.ignore_overlayable)) {
-      std::vector<std::string> create_args = {"--target-apk-path",  target_apk_path,
-                                              "--overlay-apk-path", overlay.apk_path,
-                                              "--idmap-path",       overlay.idmap_path};
-      if (overlay.ignore_overlayable) {
-        create_args.emplace_back("--ignore-overlayable");
-      }
-
-      for (const std::string& policy : overlay.policies) {
-        create_args.emplace_back("--policy");
-        create_args.emplace_back(policy);
-      }
-
-      const auto create_ok = Create(create_args);
-      if (!create_ok) {
-        LOG(WARNING) << "failed to create idmap for overlay apk path \"" << overlay.apk_path
-                     << "\": " << create_ok.GetError().GetMessage();
-        continue;
-      }
-    }
-
-    stream << overlay.idmap_path << std::endl;
-  }
-
-  std::cout << stream.str();
-
-  return Unit{};
-}
diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h
index 3f03236..c4e0e1f 100644
--- a/cmds/idmap2/include/idmap2/FileUtils.h
+++ b/cmds/idmap2/include/idmap2/FileUtils.h
@@ -17,27 +17,13 @@
 #ifndef IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
 #define IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
 
-#include <sys/types.h>
-
-#include <functional>
-#include <memory>
 #include <string>
-#include <vector>
 
 namespace android::idmap2::utils {
 
 constexpr const char* kIdmapCacheDir = "/data/resource-cache";
 constexpr const mode_t kIdmapFilePermissionMask = 0133;  // u=rw,g=r,o=r
 
-typedef std::function<bool(unsigned char type /* DT_* from dirent.h */, const std::string& path)>
-    FindFilesPredicate;
-std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse,
-                                                    const FindFilesPredicate& predicate);
-
-std::unique_ptr<std::string> ReadFile(int fd);
-
-std::unique_ptr<std::string> ReadFile(const std::string& path);
-
 bool UidHasWriteAccessToPath(uid_t uid, const std::string& path);
 
 }  // namespace android::idmap2::utils
diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h
index 0a58ec4..f66916c 100644
--- a/cmds/idmap2/include/idmap2/ResourceMapping.h
+++ b/cmds/idmap2/include/idmap2/ResourceMapping.h
@@ -117,7 +117,8 @@
   static Result<ResourceMapping> CreateResourceMappingLegacy(const AssetManager2* target_am,
                                                              const AssetManager2* overlay_am,
                                                              const LoadedPackage* target_package,
-                                                             const LoadedPackage* overlay_package);
+                                                             const LoadedPackage* overlay_package,
+                                                             LogInfo& log_info);
 
   // Removes resources that do not pass policy or overlayable checks of the target package.
   void FilterOverlayableResources(const AssetManager2* target_am,
diff --git a/cmds/idmap2/libidmap2/FileUtils.cpp b/cmds/idmap2/libidmap2/FileUtils.cpp
index 3e8e329..3af1f70 100644
--- a/cmds/idmap2/libidmap2/FileUtils.cpp
+++ b/cmds/idmap2/libidmap2/FileUtils.cpp
@@ -16,19 +16,7 @@
 
 #include "idmap2/FileUtils.h"
 
-#include <dirent.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cerrno>
-#include <climits>
-#include <cstdlib>
-#include <cstring>
-#include <fstream>
-#include <memory>
 #include <string>
-#include <utility>
-#include <vector>
 
 #include "android-base/file.h"
 #include "android-base/macros.h"
@@ -37,54 +25,6 @@
 
 namespace android::idmap2::utils {
 
-std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse,
-                                                    const FindFilesPredicate& predicate) {
-  DIR* dir = opendir(root.c_str());
-  if (dir == nullptr) {
-    return nullptr;
-  }
-  std::unique_ptr<std::vector<std::string>> vector(new std::vector<std::string>());
-  struct dirent* dirent;
-  while ((dirent = readdir(dir)) != nullptr) {
-    const std::string path = root + "/" + dirent->d_name;
-    if (predicate(dirent->d_type, path)) {
-      vector->push_back(path);
-    }
-    if (recurse && dirent->d_type == DT_DIR && strcmp(dirent->d_name, ".") != 0 &&
-        strcmp(dirent->d_name, "..") != 0) {
-      auto sub_vector = FindFiles(path, recurse, predicate);
-      if (!sub_vector) {
-        closedir(dir);
-        return nullptr;
-      }
-      vector->insert(vector->end(), sub_vector->begin(), sub_vector->end());
-    }
-  }
-  closedir(dir);
-
-  return vector;
-}
-
-std::unique_ptr<std::string> ReadFile(const std::string& path) {
-  std::unique_ptr<std::string> str(new std::string());
-  std::ifstream fin(path);
-  str->append({std::istreambuf_iterator<char>(fin), std::istreambuf_iterator<char>()});
-  fin.close();
-  return str;
-}
-
-std::unique_ptr<std::string> ReadFile(int fd) {
-  static constexpr const size_t kBufSize = 1024;
-
-  std::unique_ptr<std::string> str(new std::string());
-  char buf[kBufSize];
-  ssize_t r;
-  while ((r = read(fd, buf, sizeof(buf))) > 0) {
-    str->append(buf, r);
-  }
-  return r == 0 ? std::move(str) : nullptr;
-}
-
 #ifdef __ANDROID__
 bool UidHasWriteAccessToPath(uid_t uid, const std::string& path) {
   // resolve symlinks and relative paths; the directories must exist
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index a93202a..3037a79 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -100,10 +100,9 @@
     stream_ << TAB << base::StringPrintf("0x%08x -> ", target_entry.target_id)
             << utils::DataTypeToString(target_entry.value.data_type);
 
-    size_t unused;
     if (target_entry.value.data_type == Res_value::TYPE_STRING) {
-      auto str = string_pool.stringAt(target_entry.value.data_value - string_pool_offset, &unused);
-      stream_ << " \"" << StringPiece16(str) << "\"";
+      auto str = string_pool.stringAt(target_entry.value.data_value - string_pool_offset);
+      stream_ << " \"" << str.value_or(StringPiece16(u"")) << "\"";
     } else {
       stream_ << " " << base::StringPrintf("0x%08x", target_entry.value.data_value);
     }
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index 31f1c16..d777cbf 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -71,9 +71,10 @@
   if (!target_package.DefinesOverlayable()) {
     return (sDefaultPolicies & fulfilled_policies) != 0
                ? Result<Unit>({})
-               : Error("overlay must be preinstalled, signed with the same signature as the target,"
-                       " or signed with the same signature as the package referenced through"
-                       " <overlay-config-signature>.");
+               : Error(
+                     "overlay must be preinstalled, signed with the same signature as the target,"
+                     " or signed with the same signature as the package referenced through"
+                     " <overlay-config-signature>.");
   }
 
   const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource);
@@ -119,32 +120,25 @@
 
 Result<std::unique_ptr<Asset>> OpenNonAssetFromResource(const ResourceId& resource_id,
                                                         const AssetManager2& asset_manager) {
-  Res_value value{};
-  ResTable_config selected_config{};
-  uint32_t flags;
-  auto cookie =
-      asset_manager.GetResource(resource_id, /* may_be_bag */ false,
-                                /* density_override */ 0U, &value, &selected_config, &flags);
-  if (cookie == kInvalidCookie) {
+  auto value = asset_manager.GetResource(resource_id);
+  if (!value.has_value()) {
     return Error("failed to find resource for id 0x%08x", resource_id);
   }
 
-  if (value.dataType != Res_value::TYPE_STRING) {
+  if (value->type != Res_value::TYPE_STRING) {
     return Error("resource for is 0x%08x is not a file", resource_id);
   }
 
-  auto string_pool = asset_manager.GetStringPoolForCookie(cookie);
-  size_t len;
-  auto file_path16 = string_pool->stringAt(value.data, &len);
-  if (file_path16 == nullptr) {
-    return Error("failed to find string for index %d", value.data);
+  auto string_pool = asset_manager.GetStringPoolForCookie(value->cookie);
+  auto file = string_pool->string8ObjectAt(value->data);
+  if (!file.has_value()) {
+    return Error("failed to find string for index %d", value->data);
   }
 
   // Load the overlay resource mappings from the file specified using android:resourcesMap.
-  auto file_path = String8(String16(file_path16));
-  auto asset = asset_manager.OpenNonAsset(file_path.c_str(), Asset::AccessMode::ACCESS_BUFFER);
+  auto asset = asset_manager.OpenNonAsset(file->c_str(), Asset::AccessMode::ACCESS_BUFFER);
   if (asset == nullptr) {
-    return Error("file \"%s\" not found", file_path.c_str());
+    return Error("file \"%s\" not found", file->c_str());
   }
 
   return asset;
@@ -190,16 +184,16 @@
       return Error(R"(<item> tag missing expected attribute "value")");
     }
 
-    ResourceId target_id =
+    auto target_id_result =
         target_am->GetResourceId(*target_resource, "", target_package->GetPackageName());
-    if (target_id == 0U) {
+    if (!target_id_result.has_value()) {
       log_info.Warning(LogMessage() << "failed to find resource \"" << *target_resource
                                     << "\" in target resources");
       continue;
     }
 
     // Retrieve the compile-time resource id of the target resource.
-    target_id = REWRITE_PACKAGE(target_id, target_package_id);
+    uint32_t target_id = REWRITE_PACKAGE(*target_id_result, target_package_id);
 
     if (overlay_resource->dataType == Res_value::TYPE_STRING) {
       overlay_resource->data += string_pool_offset;
@@ -220,7 +214,7 @@
 
 Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy(
     const AssetManager2* target_am, const AssetManager2* overlay_am,
-    const LoadedPackage* target_package, const LoadedPackage* overlay_package) {
+    const LoadedPackage* target_package, const LoadedPackage* overlay_package, LogInfo& log_info) {
   ResourceMapping resource_mapping;
   const uint8_t target_package_id = target_package->GetPackageId();
   const auto end = overlay_package->end();
@@ -234,13 +228,15 @@
     // Find the resource with the same type and entry name within the target package.
     const std::string full_name =
         base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str());
-    ResourceId target_resource = target_am->GetResourceId(full_name);
-    if (target_resource == 0U) {
+    auto target_resource_result = target_am->GetResourceId(full_name);
+    if (!target_resource_result.has_value()) {
+      log_info.Warning(LogMessage() << "failed to find resource \"" << full_name
+                                    << "\" in target resources");
       continue;
     }
 
     // Retrieve the compile-time resource id of the target resource.
-    target_resource = REWRITE_PACKAGE(target_resource, target_package_id);
+    ResourceId target_resource = REWRITE_PACKAGE(*target_resource_result, target_package_id);
     resource_mapping.AddMapping(target_resource, overlay_resid,
                                 false /* rewrite_overlay_reference */);
   }
@@ -347,7 +343,9 @@
     auto& string_pool = (*parser)->get_strings();
     string_pool_data_length = string_pool.bytes();
     string_pool_data.reset(new uint8_t[string_pool_data_length]);
-    memcpy(string_pool_data.get(), string_pool.data(), string_pool_data_length);
+
+    // Overlays should not be incrementally installed, so calling unsafe_ptr is fine here.
+    memcpy(string_pool_data.get(), string_pool.data().unsafe_ptr(), string_pool_data_length);
 
     // Offset string indices by the size of the overlay resource table string pool.
     string_pool_offset = overlay_arsc->GetStringPool()->size();
@@ -358,7 +356,7 @@
     // If no file is specified using android:resourcesMap, it is assumed that the overlay only
     // defines resources intended to override target resources of the same type and name.
     resource_mapping = CreateResourceMappingLegacy(&target_asset_manager, &overlay_asset_manager,
-                                                   target_pkg, overlay_pkg);
+                                                   target_pkg, overlay_pkg, log_info);
   }
 
   if (!resource_mapping) {
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index 98d026b..e817140 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -72,21 +72,21 @@
 }
 
 Result<std::string> ResToTypeEntryName(const AssetManager2& am, uint32_t resid) {
-  AssetManager2::ResourceName name;
-  if (!am.GetResourceName(resid, &name)) {
+  const auto name = am.GetResourceName(resid);
+  if (!name.has_value()) {
     return Error("no resource 0x%08x in asset manager", resid);
   }
   std::string out;
-  if (name.type != nullptr) {
-    out.append(name.type, name.type_len);
+  if (name->type != nullptr) {
+    out.append(name->type, name->type_len);
   } else {
-    out += Utf16ToUtf8(StringPiece16(name.type16, name.type_len));
+    out += Utf16ToUtf8(StringPiece16(name->type16, name->type_len));
   }
   out.append("/");
-  if (name.entry != nullptr) {
-    out.append(name.entry, name.entry_len);
+  if (name->entry != nullptr) {
+    out.append(name->entry, name->entry_len);
   } else {
-    out += Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
+    out += Utf16ToUtf8(StringPiece16(name->entry16, name->entry_len));
   }
   return out;
 }
diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp
index 526a560..7c55b64 100644
--- a/cmds/idmap2/libidmap2/XmlParser.cpp
+++ b/cmds/idmap2/libidmap2/XmlParser.cpp
@@ -98,18 +98,19 @@
 
   switch ((*value).dataType) {
     case Res_value::TYPE_STRING: {
-      size_t len;
-      const String16 value16(parser_.getStrings().stringAt((*value).data, &len));
-      return std::string(String8(value16).c_str());
+      if (auto str = parser_.getStrings().string8ObjectAt((*value).data)) {
+        return std::string(str->string());
+      }
+      break;
     }
     case Res_value::TYPE_INT_DEC:
     case Res_value::TYPE_INT_HEX:
     case Res_value::TYPE_INT_BOOLEAN: {
       return std::to_string((*value).data);
     }
-    default:
-      return Error(R"(Failed to convert attribute "%s" value to a string)", name.c_str());
   }
+
+  return Error(R"(Failed to convert attribute "%s" value to a string)", name.c_str());
 }
 
 Result<Res_value> XmlParser::Node::GetAttributeValue(const std::string& name) const {
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index 8af4037..5750ca1 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -14,73 +14,16 @@
  * limitations under the License.
  */
 
-#include <dirent.h>
-#include <fcntl.h>
-
-#include <set>
 #include <string>
 
 #include "TestHelpers.h"
-#include "android-base/macros.h"
 #include "android-base/stringprintf.h"
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "idmap2/FileUtils.h"
 #include "private/android_filesystem_config.h"
 
-using ::testing::NotNull;
-
 namespace android::idmap2::utils {
 
-TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) {
-  const auto& root = GetTestDataPath();
-  auto v = utils::FindFiles(root, false,
-                            [](unsigned char type ATTRIBUTE_UNUSED,
-                               const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; });
-  ASSERT_THAT(v, NotNull());
-  ASSERT_EQ(v->size(), 7U);
-  ASSERT_EQ(std::set<std::string>(v->begin(), v->end()), std::set<std::string>({
-                                                             root + "/.",
-                                                             root + "/..",
-                                                             root + "/overlay",
-                                                             root + "/target",
-                                                             root + "/signature-overlay",
-                                                             root + "/system-overlay",
-                                                             root + "/system-overlay-invalid",
-                                                         }));
-}
-
-TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) {
-  const auto& root = GetTestDataPath();
-  auto v = utils::FindFiles(root, true, [](unsigned char type, const std::string& path) -> bool {
-    return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0;
-  });
-  ASSERT_THAT(v, NotNull());
-  ASSERT_EQ(v->size(), 11U);
-  ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
-            std::set<std::string>(
-                {root + "/target/target.apk", root + "/target/target-no-overlayable.apk",
-                 root + "/overlay/overlay.apk", root + "/overlay/overlay-no-name.apk",
-                 root + "/overlay/overlay-no-name-static.apk", root + "/overlay/overlay-shared.apk",
-                 root + "/overlay/overlay-static-1.apk", root + "/overlay/overlay-static-2.apk",
-                 root + "/signature-overlay/signature-overlay.apk",
-                 root + "/system-overlay/system-overlay.apk",
-                 root + "/system-overlay-invalid/system-overlay-invalid.apk"}));
-}
-
-TEST(FileUtilsTests, ReadFile) {
-  int pipefd[2];
-  ASSERT_EQ(pipe2(pipefd, O_CLOEXEC), 0);
-
-  ASSERT_EQ(write(pipefd[1], "foobar", 6), 6);
-  close(pipefd[1]);
-
-  auto data = ReadFile(pipefd[0]);
-  ASSERT_THAT(data, NotNull());
-  ASSERT_EQ(*data, "foobar");
-  close(pipefd[0]);
-}
-
 #ifdef __ANDROID__
 TEST(FileUtilsTests, UidHasWriteAccessToPath) {
   constexpr const char* tmp_path = "/data/local/tmp/test@idmap";
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index eba102da..e7e9e4c 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -159,131 +159,6 @@
   unlink(GetIdmapPath().c_str());
 }
 
-TEST_F(Idmap2BinaryTests, Scan) {
-  SKIP_TEST_IF_CANT_EXEC_IDMAP2;
-
-  const std::string overlay_static_no_name_apk_path =
-      GetTestDataPath() + "/overlay/overlay-no-name-static.apk";
-  const std::string overlay_static_1_apk_path = GetTestDataPath() + "/overlay/overlay-static-1.apk";
-  const std::string overlay_static_2_apk_path = GetTestDataPath() + "/overlay/overlay-static-2.apk";
-  const std::string idmap_static_no_name_path =
-      Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_no_name_apk_path);
-  const std::string idmap_static_1_path =
-      Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_1_apk_path);
-  const std::string idmap_static_2_path =
-      Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_2_apk_path);
-
-  // single input directory, recursive
-  // clang-format off
-  auto result = ExecuteBinary({"idmap2",
-                               "scan",
-                               "--input-directory", GetTestDataPath(),
-                               "--recursive",
-                               "--target-package-name", "test.target",
-                               "--target-apk-path", GetTargetApkPath(),
-                               "--output-directory", GetTempDirPath(),
-                               "--override-policy", "public"});
-  // clang-format on
-  ASSERT_THAT(result, NotNull());
-  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
-  std::stringstream expected;
-  expected << idmap_static_no_name_path << std::endl;
-  expected << idmap_static_1_path << std::endl;
-  expected << idmap_static_2_path << std::endl;
-  ASSERT_EQ(result->stdout, expected.str());
-
-  auto idmap_static_no_name_raw_string = utils::ReadFile(idmap_static_no_name_path);
-  auto idmap_static_no_name_raw_stream = std::istringstream(*idmap_static_no_name_raw_string);
-  auto idmap_static_no_name = Idmap::FromBinaryStream(idmap_static_no_name_raw_stream);
-  ASSERT_TRUE(idmap_static_no_name);
-  ASSERT_IDMAP(**idmap_static_no_name, GetTargetApkPath(), overlay_static_no_name_apk_path);
-
-  auto idmap_static_1_raw_string = utils::ReadFile(idmap_static_1_path);
-  auto idmap_static_1_raw_stream = std::istringstream(*idmap_static_1_raw_string);
-  auto idmap_static_1 = Idmap::FromBinaryStream(idmap_static_1_raw_stream);
-  ASSERT_TRUE(idmap_static_1);
-  ASSERT_IDMAP(**idmap_static_1, GetTargetApkPath(), overlay_static_1_apk_path);
-
-  auto idmap_static_2_raw_string = utils::ReadFile(idmap_static_2_path);
-  auto idmap_static_2_raw_stream = std::istringstream(*idmap_static_2_raw_string);
-  auto idmap_static_2 = Idmap::FromBinaryStream(idmap_static_2_raw_stream);
-  ASSERT_TRUE(idmap_static_2);
-  ASSERT_IDMAP(**idmap_static_2, GetTargetApkPath(), overlay_static_2_apk_path);
-
-  unlink(idmap_static_no_name_path.c_str());
-  unlink(idmap_static_2_path.c_str());
-  unlink(idmap_static_1_path.c_str());
-
-  // multiple input directories, non-recursive
-  // clang-format off
-  result = ExecuteBinary({"idmap2",
-                          "scan",
-                          "--input-directory", GetTestDataPath() + "/target",
-                          "--input-directory", GetTestDataPath() + "/overlay",
-                          "--target-package-name", "test.target",
-                          "--target-apk-path", GetTargetApkPath(),
-                          "--output-directory", GetTempDirPath(),
-                          "--override-policy", "public"});
-  // clang-format on
-  ASSERT_THAT(result, NotNull());
-  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
-  ASSERT_EQ(result->stdout, expected.str());
-  unlink(idmap_static_no_name_path.c_str());
-  unlink(idmap_static_2_path.c_str());
-  unlink(idmap_static_1_path.c_str());
-
-  // the same input directory given twice, but no duplicate entries
-  // clang-format off
-  result = ExecuteBinary({"idmap2",
-                          "scan",
-                          "--input-directory", GetTestDataPath(),
-                          "--input-directory", GetTestDataPath(),
-                          "--recursive",
-                          "--target-package-name", "test.target",
-                          "--target-apk-path", GetTargetApkPath(),
-                          "--output-directory", GetTempDirPath(),
-                          "--override-policy", "public"});
-  // clang-format on
-  ASSERT_THAT(result, NotNull());
-  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
-  ASSERT_EQ(result->stdout, expected.str());
-  unlink(idmap_static_no_name_path.c_str());
-  unlink(idmap_static_2_path.c_str());
-  unlink(idmap_static_1_path.c_str());
-
-  // no APKs in input-directory: ok, but no output
-  // clang-format off
-  result = ExecuteBinary({"idmap2",
-                          "scan",
-                          "--input-directory", GetTempDirPath(),
-                          "--target-package-name", "test.target",
-                          "--target-apk-path", GetTargetApkPath(),
-                          "--output-directory", GetTempDirPath(),
-                          "--override-policy", "public"});
-  // clang-format on
-  ASSERT_THAT(result, NotNull());
-  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
-  ASSERT_EQ(result->stdout, "");
-
-  // the signature idmap failing to generate should not cause scanning to fail
-  // clang-format off
-  result = ExecuteBinary({"idmap2",
-                          "scan",
-                          "--input-directory", GetTestDataPath(),
-                          "--recursive",
-                          "--target-package-name", "test.target",
-                          "--target-apk-path", GetTargetApkPath(),
-                          "--output-directory", GetTempDirPath(),
-                          "--override-policy", "public"});
-  // clang-format on
-  ASSERT_THAT(result, NotNull());
-  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
-  ASSERT_EQ(result->stdout, expected.str());
-  unlink(idmap_static_no_name_path.c_str());
-  unlink(idmap_static_2_path.c_str());
-  unlink(idmap_static_1_path.c_str());
-}
-
 TEST_F(Idmap2BinaryTests, Lookup) {
   SKIP_TEST_IF_CANT_EXEC_IDMAP2;
 
diff --git a/cmds/idmap2/valgrind.sh b/cmds/idmap2/valgrind.sh
index b4ebab0..84daeec 100755
--- a/cmds/idmap2/valgrind.sh
+++ b/cmds/idmap2/valgrind.sh
@@ -53,7 +53,5 @@
 _eval "idmap2 create" "$valgrind idmap2 create --policy public --target-apk-path $target_path --overlay-apk-path $overlay_path --idmap-path $idmap_path"
 _eval "idmap2 dump" "$valgrind idmap2 dump --idmap-path $idmap_path"
 _eval "idmap2 lookup" "$valgrind idmap2 lookup --idmap-path $idmap_path --config '' --resid test.target:string/str1"
-_eval "idmap2 scan" "$valgrind idmap2 scan --input-directory ${prefix}/tests/data/overlay --recursive --target-package-name test.target --target-apk-path $target_path --output-directory /tmp --override-policy public"
-_eval "idmap2 verify" "$valgrind idmap2 verify --idmap-path $idmap_path"
 _eval "idmap2_tests" "$valgrind $ANDROID_HOST_OUT/nativetest64/idmap2_tests/idmap2_tests"
 exit $errors
diff --git a/core/api/current.txt b/core/api/current.txt
index d7c0213..1ef98be 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -29588,6 +29588,7 @@
     field public static final String ID;
     field public static final String MANUFACTURER;
     field public static final String MODEL;
+    field @NonNull public static final String ODM_SKU;
     field public static final String PRODUCT;
     field @Deprecated public static final String RADIO;
     field @Deprecated public static final String SERIAL;
@@ -40687,6 +40688,28 @@
     field public static final int IP_VERSION_MISMATCH = 2055; // 0x807
     field public static final int IRAT_HANDOVER_FAILED = 2194; // 0x892
     field public static final int IS707B_MAX_ACCESS_PROBES = 2089; // 0x829
+    field public static final int IWLAN_AUTHORIZATION_REJECTED = 9003; // 0x232b
+    field public static final int IWLAN_DNS_RESOLUTION_NAME_FAILURE = 16388; // 0x4004
+    field public static final int IWLAN_DNS_RESOLUTION_TIMEOUT = 16389; // 0x4005
+    field public static final int IWLAN_IKEV2_AUTH_FAILURE = 16385; // 0x4001
+    field public static final int IWLAN_IKEV2_CERT_INVALID = 16387; // 0x4003
+    field public static final int IWLAN_IKEV2_CONFIG_FAILURE = 16384; // 0x4000
+    field public static final int IWLAN_IKEV2_MSG_TIMEOUT = 16386; // 0x4002
+    field public static final int IWLAN_ILLEGAL_ME = 9006; // 0x232e
+    field public static final int IWLAN_IMEI_NOT_ACCEPTED = 11005; // 0x2afd
+    field public static final int IWLAN_MAX_CONNECTION_REACHED = 8193; // 0x2001
+    field public static final int IWLAN_NETWORK_FAILURE = 10500; // 0x2904
+    field public static final int IWLAN_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED = 9000; // 0x2328
+    field public static final int IWLAN_NO_APN_SUBSCRIPTION = 9002; // 0x232a
+    field public static final int IWLAN_PDN_CONNECTION_REJECTION = 8192; // 0x2000
+    field public static final int IWLAN_PLMN_NOT_ALLOWED = 11011; // 0x2b03
+    field public static final int IWLAN_RAT_TYPE_NOT_ALLOWED = 11001; // 0x2af9
+    field public static final int IWLAN_SEMANTIC_ERRORS_IN_PACKET_FILTERS = 8244; // 0x2034
+    field public static final int IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION = 8241; // 0x2031
+    field public static final int IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS = 8245; // 0x2035
+    field public static final int IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION = 8242; // 0x2032
+    field public static final int IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED = 11055; // 0x2b2f
+    field public static final int IWLAN_USER_UNKNOWN = 9001; // 0x2329
     field public static final int LIMITED_TO_IPV4 = 2234; // 0x8ba
     field public static final int LIMITED_TO_IPV6 = 2235; // 0x8bb
     field public static final int LLC_SNDCP = 25; // 0x19
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index e857951..b982dd7 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -6401,6 +6401,19 @@
     method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress);
   }
 
+  public final class TcpKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable {
+    ctor public TcpKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[], int, int, int, int, int, int) throws android.net.InvalidPacketException;
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.TcpKeepalivePacketData> CREATOR;
+    field public final int ipTos;
+    field public final int ipTtl;
+    field public final int tcpAck;
+    field public final int tcpSeq;
+    field public final int tcpWindow;
+    field public final int tcpWindowScale;
+  }
+
   public class TrafficStats {
     method public static void setThreadStatsTagApp();
     method public static void setThreadStatsTagBackup();
@@ -7001,6 +7014,7 @@
 
   public abstract static class BugreportManager.BugreportCallback {
     ctor public BugreportManager.BugreportCallback();
+    method public void onEarlyReportFinished();
     method public void onError(int);
     method public void onFinished();
     method public void onProgress(@FloatRange(from=0.0f, to=100.0f) float);
@@ -7885,6 +7899,7 @@
     field public static final String NAMESPACE_INTELLIGENCE_ATTENTION = "intelligence_attention";
     field public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
     field public static final String NAMESPACE_NETD_NATIVE = "netd_native";
+    field public static final String NAMESPACE_OTA = "ota";
     field public static final String NAMESPACE_PACKAGE_MANAGER_SERVICE = "package_manager_service";
     field public static final String NAMESPACE_PERMISSIONS = "permissions";
     field public static final String NAMESPACE_PRIVACY = "privacy";
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index f10853a..06ad9c9 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -1,4 +1,34 @@
+
+# Remain no owner because multiple modules may touch this file.
+per-file ContextImpl.java = *
+
+# ActivityThread
+per-file ActivityThread.java = file:/services/core/java/com/android/server/am/OWNERS
+per-file ActivityThread.java = file:/services/core/java/com/android/server/wm/OWNERS
+
+# Alarm
 per-file *Alarm* = file:/apex/jobscheduler/OWNERS
+
+# AppOps
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
+
+# Notification
 per-file *Notification* = file:/packages/SystemUI/OWNERS
+
+# ResourcesManager
+per-file ResourcesManager = rtmitchell@google.com, toddke@google.com
+
+# Wallpaper
+per-file *Wallpaper* = file:/core/java/android/service/wallpaper/OWNERS
+
+# WindowManager
+per-file *Activity* = file:/services/core/java/com/android/server/wm/OWNERS
+per-file ClientTransactionHandler.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file Fragment.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file *Task* = file:/services/core/java/com/android/server/wm/OWNERS
+per-file Window* = file:/services/core/java/com/android/server/wm/OWNERS
+
+# TODO(b/174932174): determine the ownership of KeyguardManager.java
+
+# Zygote
 per-file *Zygote* = file:/ZYGOTE_OWNERS
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 496ac3b..82e48bf 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -113,7 +113,6 @@
 import android.media.tv.tunerresourcemanager.TunerResourceManager;
 import android.net.ConnectivityDiagnosticsManager;
 import android.net.ConnectivityManager;
-import android.net.ConnectivityThread;
 import android.net.EthernetManager;
 import android.net.IConnectivityManager;
 import android.net.IEthernetManager;
@@ -768,8 +767,7 @@
             public LowpanManager createService(ContextImpl ctx) throws ServiceNotFoundException {
                 IBinder b = ServiceManager.getServiceOrThrow(Context.LOWPAN_SERVICE);
                 ILowpanManager service = ILowpanManager.Stub.asInterface(b);
-                return new LowpanManager(ctx.getOuterContext(), service,
-                        ConnectivityThread.getInstanceLooper());
+                return new LowpanManager(ctx.getOuterContext(), service);
             }});
 
         registerService(Context.ETHERNET_SERVICE, EthernetManager.class,
diff --git a/core/java/android/app/admin/OWNERS b/core/java/android/app/admin/OWNERS
index 64a1d27..8462cbe 100644
--- a/core/java/android/app/admin/OWNERS
+++ b/core/java/android/app/admin/OWNERS
@@ -1,4 +1,11 @@
 # Bug component: 142675
 
-yamasani@google.com
+# Android Enterprise team
 rubinxu@google.com
+sandness@google.com
+eranm@google.com
+alexkershaw@google.com
+pgrafov@google.com
+
+# Emeritus
+yamasani@google.com
diff --git a/core/java/android/app/time/OWNERS b/core/java/android/app/time/OWNERS
new file mode 100644
index 0000000..8f80897
--- /dev/null
+++ b/core/java/android/app/time/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/core/java/android/app/timedetector/GnssTimeSuggestion.aidl b/core/java/android/app/timedetector/GnssTimeSuggestion.aidl
new file mode 100644
index 0000000..81475ec
--- /dev/null
+++ b/core/java/android/app/timedetector/GnssTimeSuggestion.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+package android.app.timedetector;
+
+parcelable GnssTimeSuggestion;
diff --git a/core/java/android/app/timedetector/GnssTimeSuggestion.java b/core/java/android/app/timedetector/GnssTimeSuggestion.java
new file mode 100644
index 0000000..6478a2d
--- /dev/null
+++ b/core/java/android/app/timedetector/GnssTimeSuggestion.java
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+package android.app.timedetector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.TimestampedValue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A time signal from a GNSS source.
+ *
+ * <p>{@code utcTime} is the suggested time. The {@code utcTime.value} is the number of milliseconds
+ * elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the value of the
+ * elapsed realtime clock when the {@code utcTime.value} was established.
+ * Note that the elapsed realtime clock is considered accurate but it is volatile, so time
+ * suggestions cannot be persisted across device resets.
+ *
+ * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
+ * record why the suggestion exists and how it was entered. This information exists only to aid in
+ * debugging and therefore is used by {@link #toString()}, but it is not for use in detection
+ * logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
+ *
+ * @hide
+ */
+public final class GnssTimeSuggestion implements Parcelable {
+
+    public static final @NonNull Creator<GnssTimeSuggestion> CREATOR =
+            new Creator<GnssTimeSuggestion>() {
+                public GnssTimeSuggestion createFromParcel(Parcel in) {
+                    return GnssTimeSuggestion.createFromParcel(in);
+                }
+
+                public GnssTimeSuggestion[] newArray(int size) {
+                    return new GnssTimeSuggestion[size];
+                }
+            };
+
+    @NonNull private final TimestampedValue<Long> mUtcTime;
+    @Nullable private ArrayList<String> mDebugInfo;
+
+    public GnssTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) {
+        mUtcTime = Objects.requireNonNull(utcTime);
+        Objects.requireNonNull(utcTime.getValue());
+    }
+
+    private static GnssTimeSuggestion createFromParcel(Parcel in) {
+        TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
+        GnssTimeSuggestion suggestion = new GnssTimeSuggestion(utcTime);
+        @SuppressWarnings("unchecked")
+        ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
+        suggestion.mDebugInfo = debugInfo;
+        return suggestion;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeParcelable(mUtcTime, 0);
+        dest.writeList(mDebugInfo);
+    }
+
+    @NonNull
+    public TimestampedValue<Long> getUtcTime() {
+        return mUtcTime;
+    }
+
+    @NonNull
+    public List<String> getDebugInfo() {
+        return mDebugInfo == null
+                ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
+    }
+
+    /**
+     * Associates information with the instance that can be useful for debugging / logging. The
+     * information is present in {@link #toString()} but is not considered for
+     * {@link #equals(Object)} and {@link #hashCode()}.
+     */
+    public void addDebugInfo(String... debugInfos) {
+        if (mDebugInfo == null) {
+            mDebugInfo = new ArrayList<>();
+        }
+        mDebugInfo.addAll(Arrays.asList(debugInfos));
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        GnssTimeSuggestion that = (GnssTimeSuggestion) o;
+        return Objects.equals(mUtcTime, that.mUtcTime);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUtcTime);
+    }
+
+    @Override
+    public String toString() {
+        return "GnssTimeSuggestion{"
+                + "mUtcTime=" + mUtcTime
+                + ", mDebugInfo=" + mDebugInfo
+                + '}';
+    }
+}
diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl
index 7bea5d7..87e7233 100644
--- a/core/java/android/app/timedetector/ITimeDetectorService.aidl
+++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl
@@ -16,6 +16,7 @@
 
 package android.app.timedetector;
 
+import android.app.timedetector.GnssTimeSuggestion;
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.NetworkTimeSuggestion;
 import android.app.timedetector.TelephonyTimeSuggestion;
@@ -34,6 +35,7 @@
  * {@hide}
  */
 interface ITimeDetectorService {
+  void suggestGnssTime(in GnssTimeSuggestion timeSuggestion);
   boolean suggestManualTime(in ManualTimeSuggestion timeSuggestion);
   void suggestNetworkTime(in NetworkTimeSuggestion timeSuggestion);
   void suggestTelephonyTime(in TelephonyTimeSuggestion timeSuggestion);
diff --git a/core/java/android/app/timedetector/OWNERS b/core/java/android/app/timedetector/OWNERS
index 8c11324..941eed8 100644
--- a/core/java/android/app/timedetector/OWNERS
+++ b/core/java/android/app/timedetector/OWNERS
@@ -1,4 +1,4 @@
 # Bug component: 847766
-
+mingaleev@google.com
 narayan@google.com
 nfuller@google.com
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index 162e182..52016b6 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -71,4 +71,12 @@
      */
     @RequiresPermission(android.Manifest.permission.SET_TIME)
     void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion);
+
+    /**
+     * Suggests the time according to a gnss time source.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.SET_TIME)
+    void suggestGnssTime(GnssTimeSuggestion timeSuggestion);
 }
diff --git a/core/java/android/app/timedetector/TimeDetectorImpl.java b/core/java/android/app/timedetector/TimeDetectorImpl.java
index ac02c89..b0aa3c8 100644
--- a/core/java/android/app/timedetector/TimeDetectorImpl.java
+++ b/core/java/android/app/timedetector/TimeDetectorImpl.java
@@ -74,4 +74,16 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    @Override
+    public void suggestGnssTime(GnssTimeSuggestion timeSuggestion) {
+        if (DEBUG) {
+            Log.d(TAG, "suggestGnssTime called: " + timeSuggestion);
+        }
+        try {
+            mITimeDetectorService.suggestGnssTime(timeSuggestion);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/timezone/OWNERS b/core/java/android/app/timezone/OWNERS
index 8c11324..8f80897 100644
--- a/core/java/android/app/timezone/OWNERS
+++ b/core/java/android/app/timezone/OWNERS
@@ -1,4 +1,3 @@
 # Bug component: 847766
-
-narayan@google.com
-nfuller@google.com
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/core/java/android/app/timezonedetector/OWNERS b/core/java/android/app/timezonedetector/OWNERS
new file mode 100644
index 0000000..8f80897
--- /dev/null
+++ b/core/java/android/app/timezonedetector/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
new file mode 100644
index 0000000..c1e7e41
--- /dev/null
+++ b/core/java/android/content/OWNERS
@@ -0,0 +1,3 @@
+# Remain no owner because multiple modules may touch this file.
+per-file Context.java = *
+per-file ContextWrapper.java = *
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 36986de..13433dc 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1574,7 +1574,6 @@
     private static native long nativeAssetGetLength(long assetPtr);
     private static native long nativeAssetGetRemainingLength(long assetPtr);
 
-    private static native String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
     private static native @Nullable Map nativeGetOverlayableMap(long ptr,
             @NonNull String packageName);
     private static native @Nullable String nativeGetOverlayablesToString(long ptr,
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 920d34f..a00ff8e 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -1693,35 +1693,24 @@
     private static native long nativeAllocateCopy(long ptr)
             throws NullPointerException;
 
-    @FastNative
     private static synchronized native void nativeWriteToParcel(Parcel dest, long ptr);
-    @FastNative
     private static synchronized native void nativeReadFromParcel(Parcel source, long ptr);
-    @FastNative
     private static synchronized native void nativeSwap(long ptr, long otherPtr)
             throws NullPointerException;
-    @FastNative
     private static synchronized native void nativeClose(long ptr);
-    @FastNative
     private static synchronized native boolean nativeIsEmpty(long ptr);
-    @FastNative
     private static synchronized native int nativeGetEntryCount(long ptr);
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @FastNative
     private static synchronized native byte[] nativeReadValues(int tag, long ptr);
-    @FastNative
     private static synchronized native void nativeWriteValues(int tag, byte[] src, long ptr);
     private static synchronized native void nativeDump(long ptr) throws IOException; // dump to LOGD
 
-    @FastNative
     private static synchronized native ArrayList nativeGetAllVendorKeys(long ptr, Class keyClass);
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @FastNative
     private static synchronized native int nativeGetTagFromKeyLocal(long ptr, String keyName)
             throws IllegalArgumentException;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @FastNative
     private static synchronized native int nativeGetTypeFromTagLocal(long ptr, int tag)
             throws IllegalArgumentException;
     @FastNative
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index cf5d4e5..1e96872 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2108,17 +2108,6 @@
         // ignored
     }
 
-    /** {@hide} */
-    @Deprecated
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public NetworkQuotaInfo getActiveNetworkQuotaInfo() {
-        try {
-            return mService.getActiveNetworkQuotaInfo();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
     /**
      * @hide
      * @deprecated Talk to TelephonyManager directly
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 4173200..fb01283 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -25,7 +25,6 @@
 import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
-import android.net.NetworkQuotaInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkState;
 import android.net.ISocketKeepaliveCallback;
@@ -76,7 +75,6 @@
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     NetworkState[] getAllNetworkState();
 
-    NetworkQuotaInfo getActiveNetworkQuotaInfo();
     boolean isActiveNetworkMetered();
 
     boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress,
diff --git a/core/java/android/net/TcpKeepalivePacketData.java b/core/java/android/net/TcpKeepalivePacketData.java
new file mode 100644
index 0000000..ddb3a6a7
--- /dev/null
+++ b/core/java/android/net/TcpKeepalivePacketData.java
@@ -0,0 +1,163 @@
+/*
+ * 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.
+ */
+package android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetAddress;
+import java.util.Objects;
+
+/**
+ * Represents the actual tcp keep alive packets which will be used for hardware offload.
+ * @hide
+ */
+@SystemApi
+public final class TcpKeepalivePacketData extends KeepalivePacketData implements Parcelable {
+    private static final String TAG = "TcpKeepalivePacketData";
+
+    /** TCP sequence number. */
+    public final int tcpSeq;
+
+    /** TCP ACK number. */
+    public final int tcpAck;
+
+    /** TCP RCV window. */
+    public final int tcpWindow;
+
+    /** TCP RCV window scale. */
+    public final int tcpWindowScale;
+
+    /** IP TOS. */
+    public final int ipTos;
+
+    /** IP TTL. */
+    public final int ipTtl;
+
+    public TcpKeepalivePacketData(@NonNull final InetAddress srcAddress, int srcPort,
+            @NonNull final InetAddress dstAddress, int dstPort, @NonNull final byte[] data,
+            int tcpSeq, int tcpAck, int tcpWindow, int tcpWindowScale, int ipTos, int ipTtl)
+            throws InvalidPacketException {
+        super(srcAddress, srcPort, dstAddress, dstPort, data);
+        this.tcpSeq = tcpSeq;
+        this.tcpAck = tcpAck;
+        this.tcpWindow = tcpWindow;
+        this.tcpWindowScale = tcpWindowScale;
+        this.ipTos = ipTos;
+        this.ipTtl = ipTtl;
+    }
+
+    @Override
+    public boolean equals(@Nullable final Object o) {
+        if (!(o instanceof TcpKeepalivePacketData)) return false;
+        final TcpKeepalivePacketData other = (TcpKeepalivePacketData) o;
+        final InetAddress srcAddress = getSrcAddress();
+        final InetAddress dstAddress = getDstAddress();
+        return srcAddress.equals(other.getSrcAddress())
+                && dstAddress.equals(other.getDstAddress())
+                && getSrcPort() == other.getSrcPort()
+                && getDstPort() == other.getDstPort()
+                && this.tcpAck == other.tcpAck
+                && this.tcpSeq == other.tcpSeq
+                && this.tcpWindow == other.tcpWindow
+                && this.tcpWindowScale == other.tcpWindowScale
+                && this.ipTos == other.ipTos
+                && this.ipTtl == other.ipTtl;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getSrcAddress(), getDstAddress(), getSrcPort(), getDstPort(),
+                tcpAck, tcpSeq, tcpWindow, tcpWindowScale, ipTos, ipTtl);
+    }
+
+    /**
+     * Parcelable Implementation.
+     * Note that this object implements parcelable (and needs to keep doing this as it inherits
+     * from a class that does), but should usually be parceled as a stable parcelable using
+     * the toStableParcelable() and fromStableParcelable() methods.
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Write to parcel. */
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeString(getSrcAddress().getHostAddress());
+        out.writeString(getDstAddress().getHostAddress());
+        out.writeInt(getSrcPort());
+        out.writeInt(getDstPort());
+        out.writeByteArray(getPacket());
+        out.writeInt(tcpSeq);
+        out.writeInt(tcpAck);
+        out.writeInt(tcpWindow);
+        out.writeInt(tcpWindowScale);
+        out.writeInt(ipTos);
+        out.writeInt(ipTtl);
+    }
+
+    private static TcpKeepalivePacketData readFromParcel(Parcel in) throws InvalidPacketException {
+        InetAddress srcAddress = InetAddresses.parseNumericAddress(in.readString());
+        InetAddress dstAddress = InetAddresses.parseNumericAddress(in.readString());
+        int srcPort = in.readInt();
+        int dstPort = in.readInt();
+        byte[] packet = in.createByteArray();
+        int tcpSeq = in.readInt();
+        int tcpAck = in.readInt();
+        int tcpWnd = in.readInt();
+        int tcpWndScale = in.readInt();
+        int ipTos = in.readInt();
+        int ipTtl = in.readInt();
+        return new TcpKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, packet, tcpSeq,
+                tcpAck, tcpWnd, tcpWndScale, ipTos, ipTtl);
+    }
+
+    /** Parcelable Creator. */
+    public static final @NonNull Parcelable.Creator<TcpKeepalivePacketData> CREATOR =
+            new Parcelable.Creator<TcpKeepalivePacketData>() {
+                public TcpKeepalivePacketData createFromParcel(Parcel in) {
+                    try {
+                        return readFromParcel(in);
+                    } catch (InvalidPacketException e) {
+                        throw new IllegalArgumentException(
+                                "Invalid TCP keepalive data: " + e.getError());
+                    }
+                }
+
+                public TcpKeepalivePacketData[] newArray(int size) {
+                    return new TcpKeepalivePacketData[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        return "saddr: " + getSrcAddress()
+                + " daddr: " + getDstAddress()
+                + " sport: " + getSrcPort()
+                + " dport: " + getDstPort()
+                + " seq: " + tcpSeq
+                + " ack: " + tcpAck
+                + " window: " + tcpWindow
+                + " windowScale: " + tcpWindowScale
+                + " tos: " + ipTos
+                + " ttl: " + ipTtl;
+    }
+}
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index fe4d729..46ad7b8 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -26,7 +26,6 @@
 import android.annotation.SystemService;
 import android.app.ActivityManager;
 import android.content.Context;
-import android.content.Intent;
 import android.os.Handler;
 import android.util.Log;
 import android.widget.Toast;
@@ -52,8 +51,6 @@
 public final class BugreportManager {
 
     private static final String TAG = "BugreportManager";
-    private static final String INTENT_UI_INTENSIVE_BUGREPORT_DUMPS_FINISHED =
-            "com.android.internal.intent.action.UI_INTENSIVE_BUGREPORT_DUMPS_FINISHED";
 
     private final Context mContext;
     private final IDumpstate mBinder;
@@ -126,6 +123,12 @@
          * Called when taking bugreport finishes successfully.
          */
         public void onFinished() {}
+
+        /**
+         * Called when it is ready for calling app to show UI, showing any extra UI before this
+         * callback can interfere with bugreport generation.
+         */
+        public void onEarlyReportFinished() {}
     }
 
     /**
@@ -288,21 +291,12 @@
         }
 
         @Override
-        public void onUiIntensiveBugreportDumpsFinished(String callingPackage)
+        public void onUiIntensiveBugreportDumpsFinished()
                 throws RemoteException {
             final long identity = Binder.clearCallingIdentity();
             try {
                 mExecutor.execute(() -> {
-                    // Send intent to let calling app to show UI safely without interfering with
-                    // the bugreport/screenshot generation.
-                    // TODO(b/154298410): When S is ready for API change, add a method in
-                    // BugreportCallback so we can just call the callback instead of using
-                    // broadcast.
-                    Intent intent = new Intent(INTENT_UI_INTENSIVE_BUGREPORT_DUMPS_FINISHED);
-                    intent.setPackage(callingPackage);
-                    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                    intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-                    mContext.sendBroadcast(intent, android.Manifest.permission.DUMP);
+                    mCallback.onEarlyReportFinished();
                 });
             } finally {
                 Binder.restoreCallingIdentity(identity);
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 78ba7f0..0d8769e 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -106,12 +106,24 @@
     public static final String HARDWARE = getString("ro.hardware");
 
     /**
-     * The hardware variant (SKU), if available.
+     * The SKU of the hardware (from the kernel command line). The SKU is reported by the bootloader
+     * to configure system software features.
      */
     @NonNull
     public static final String SKU = getString("ro.boot.hardware.sku");
 
     /**
+     * The SKU of the device as set by the original design manufacturer (ODM). This is a
+     * runtime-initialized property set during startup to configure device services.
+     *
+     * <p>The ODM SKU may have multiple variants for the same system SKU in case a manufacturer
+     * produces variants of the same design. For example, the same build may be released with
+     * variations in physical keyboard and/or display hardware, each with a different ODM SKU.
+     */
+    @NonNull
+    public static final String ODM_SKU = getString("ro.boot.product.hardware.sku");
+
+    /**
      * Whether this build was for an emulator device.
      * @hide
      */
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index e1d9005..25d84ba 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -321,16 +321,6 @@
     void setFirewallChainEnabled(int chain, boolean enable);
 
     /**
-     * Set all packets from users in ranges to go through VPN specified by netId.
-     */
-    void addVpnUidRanges(int netId, in UidRange[] ranges);
-
-    /**
-     * Clears the special VPN rules for users in ranges and VPN specified by netId.
-     */
-    void removeVpnUidRanges(int netId, in UidRange[] ranges);
-
-    /**
      * Start listening for mobile activity state changes.
      */
     void registerNetworkActivityListener(INetworkActivityListener listener);
@@ -361,7 +351,5 @@
     void removeInterfaceFromLocalNetwork(String iface);
     int removeRoutesFromLocalNetwork(in List<RouteInfo> routes);
 
-    void setAllowOnlyVpnForUids(boolean enable, in UidRange[] uidRanges);
-
     boolean isNetworkRestricted(int uid);
 }
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 6aa1be9..563ff62 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -28,3 +28,6 @@
 per-file *Power* = file:/services/core/java/com/android/server/power/OWNERS
 per-file *Telephony* = file:/telephony/OWNERS
 per-file *Zygote* = file:/ZYGOTE_OWNERS
+
+# RecoverySystem
+per-file *Recovery* = file:/services/core/java/com/android/server/recoverysystem/OWNERS
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 4d67d46..e7e2c61 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -403,6 +403,14 @@
     public static final String NAMESPACE_PERMISSIONS = "permissions";
 
     /**
+     * Namespace for ota related features.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_OTA = "ota";
+
+    /**
      * Namespace for all widget related features.
      *
      * @hide
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 6ef9e7e..017f405 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -16,18 +16,18 @@
 
 package android.security.keymaster;
 
-import android.hardware.keymint.Algorithm;
-import android.hardware.keymint.BlockMode;
-import android.hardware.keymint.Digest;
-import android.hardware.keymint.ErrorCode;
-import android.hardware.keymint.HardwareAuthenticatorType;
-import android.hardware.keymint.KeyFormat;
-import android.hardware.keymint.KeyOrigin;
-import android.hardware.keymint.KeyPurpose;
-import android.hardware.keymint.PaddingMode;
-import android.hardware.keymint.SecurityLevel;
-import android.hardware.keymint.Tag;
-import android.hardware.keymint.TagType;
+import android.hardware.security.keymint.Algorithm;
+import android.hardware.security.keymint.BlockMode;
+import android.hardware.security.keymint.Digest;
+import android.hardware.security.keymint.ErrorCode;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
+import android.hardware.security.keymint.KeyFormat;
+import android.hardware.security.keymint.KeyOrigin;
+import android.hardware.security.keymint.KeyPurpose;
+import android.hardware.security.keymint.PaddingMode;
+import android.hardware.security.keymint.SecurityLevel;
+import android.hardware.security.keymint.Tag;
+import android.hardware.security.keymint.TagType;
 
 import java.util.HashMap;
 import java.util.Map;
diff --git a/core/java/android/service/textservice/OWNERS b/core/java/android/service/textservice/OWNERS
index a637754..10b8b76 100644
--- a/core/java/android/service/textservice/OWNERS
+++ b/core/java/android/service/textservice/OWNERS
@@ -1,5 +1,3 @@
 # Bug component: 34867
 
-ogunwale@google.com
-roosa@google.com
-yukawa@google.com
+include ../../inputmethodservice/OWNERS
\ No newline at end of file
diff --git a/core/java/android/timezone/OWNERS b/core/java/android/timezone/OWNERS
index 09447a9..8f80897 100644
--- a/core/java/android/timezone/OWNERS
+++ b/core/java/android/timezone/OWNERS
@@ -1 +1,3 @@
-include /core/java/android/app/timezone/OWNERS
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index 4f82b86..72fa4c3 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -8,19 +8,67 @@
 sumir@google.com
 ogunwale@google.com
 jjaggi@google.com
+roosa@google.com
 
 # Display
-per-file Display.java = michaelwr@google.com, santoscordon@google.com
-per-file DisplayInfo.java = michaelwr@google.com, santoscordon@google.com
+per-file Display*.java = file:/services/core/java/com/android/server/display/OWNERS
+per-file Display*.aidl = file:/services/core/java/com/android/server/display/OWNERS
 
 # Haptics
-per-file HapticFeedbackConstants.java = michaelwr@google.com, santoscordon@google.com
+per-file HapticFeedbackConstants.java = file:/services/core/java/com/android/server/input/OWNERS
+
+# Ime
+per-file Ime*.java = file:/core/java/android/view/inputmethod/OWNERS
 
 # Input
-per-file IInputMonitorHost.aidl = michaelwr@google.com, svv@google.com
-per-file Input*.java = michaelwr@google.com, svv@google.com
-per-file Input*.aidl = michaelwr@google.com, svv@google.com
-per-file KeyEvent.java = michaelwr@google.com, svv@google.com
-per-file MotionEvent.java = michaelwr@google.com, svv@google.com
-per-file PointerIcon.java = michaelwr@google.com, svv@google.com
-per-file SimulatedDpad.java = michaelwr@google.com, svv@google.com
+per-file IInput*.aidl = file:/services/core/java/com/android/server/input/OWNERS
+per-file Input*.java = file:/services/core/java/com/android/server/input/OWNERS
+per-file Input*.aidl = file:/services/core/java/com/android/server/input/OWNERS
+per-file KeyEvent.java = file:/services/core/java/com/android/server/input/OWNERS
+per-file MotionEvent.java = file:/services/core/java/com/android/server/input/OWNERS
+per-file PointerIcon.java = file:/services/core/java/com/android/server/input/OWNERS
+per-file SimulatedDpad.java = file:/services/core/java/com/android/server/input/OWNERS
+
+# InputWindowHandle
+per-file InputWindowHandle.java  = file:/services/core/java/com/android/server/input/OWNERS
+per-file InputWindowHandle.java  = file:/services/core/java/com/android/server/wm/OWNERS
+
+# Surface
+per-file Surface.java = file:/graphics/java/android/graphics/OWNERS
+per-file Surface.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file SurfaceView.java = file:/graphics/java/android/graphics/OWNERS
+per-file SurfaceView.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file SurfaceHolder.java = file:/graphics/java/android/graphics/OWNERS
+per-file SurfaceHolder.java = file:/services/core/java/com/android/server/wm/OWNERS
+
+# View
+per-file View.java = file:/services/accessibility/OWNERS
+per-file View.java = file:/core/java/android/service/autofill/OWNERS
+per-file View.java = file:/graphics/java/android/graphics/OWNERS
+per-file View.java = file:/services/core/java/com/android/server/input/OWNERS
+per-file View.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file ViewRootImpl.java = file:/services/accessibility/OWNERS
+per-file ViewRootImpl.java = file:/core/java/android/service/autofill/OWNERS
+per-file ViewRootImpl.java = file:/graphics/java/android/graphics/OWNERS
+per-file ViewRootImpl.java = file:/services/core/java/com/android/server/input/OWNERS
+per-file ViewRootImpl.java = file:/services/core/java/com/android/server/wm/OWNERS
+
+# WindowManager
+per-file DisplayCutout.aidl = file:/services/core/java/com/android/server/wm/OWNERS
+per-file DisplayCutout.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file IDisplay*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
+per-file Inset*.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file Inset*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
+per-file IPinnedStackListener.aidl = file:/services/core/java/com/android/server/wm/OWNERS
+per-file IRecents*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
+per-file IRemote*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
+per-file IWindow*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
+per-file RemoteAnimation*.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file RemoteAnimation*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
+per-file SurfaceControl*.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file SurfaceControl*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
+per-file SurfaceSession.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file SyncRtSurfaceTransactionApplier.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file ViewRootInsetsControllerHost.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file Window*.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file Window*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/java/android/view/textservice/OWNERS b/core/java/android/view/textservice/OWNERS
index a637754..582be8d 100644
--- a/core/java/android/view/textservice/OWNERS
+++ b/core/java/android/view/textservice/OWNERS
@@ -1,5 +1,3 @@
 # Bug component: 34867
 
-ogunwale@google.com
-roosa@google.com
-yukawa@google.com
+include ../inputmethod/OWNERS
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index fbb975b..718076b 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -7,3 +7,5 @@
 siyamed@google.com
 
 per-file TextView.java, EditText.java, Editor.java = siyamed@google.com, nona@google.com, clarabayarri@google.com
+
+per-file SpellChecker.java = file:../view/inputmethod/OWNERS
diff --git a/core/java/com/android/internal/view/OWNERS b/core/java/com/android/internal/view/OWNERS
new file mode 100644
index 0000000..851d1f3
--- /dev/null
+++ b/core/java/com/android/internal/view/OWNERS
@@ -0,0 +1,20 @@
+# Bug component: 25700
+
+file:/core/java/android/view/OWNERS
+
+# Autofill
+per-file IInlineSuggestions*.aidl = file:/core/java/android/service/autofill/OWNERS
+per-file InlineSuggestions*.java = file:/core/java/android/service/autofill/OWNERS
+
+# Ime
+per-file *Input* = file:/services/core/java/com/android/server/inputmethod/OWNERS
+
+# Surface
+per-file *Surface* = file:/graphics/java/android/graphics/OWNERS
+per-file *Surface* = file:/services/core/java/com/android/server/wm/OWNERS
+
+# WindowManager
+per-file AppearanceRegion = file:/services/core/java/com/android/server/wm/OWNERS
+per-file BaseIWIndow.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file RotationPolicy.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file WindowManagerPolicyThread.java = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index bcd1278..54b0340 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -6,21 +6,31 @@
 per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
 
 # Display
-per-file android_hardware_display_* = michaelwr@google.com, santoscordon@google.com
+per-file android_hardware_display_* = file:/services/core/java/com/android/server/display/OWNERS
 
 # Input
-per-file android_hardware_input* = michaelwr@google.com, svv@google.com
-per-file android_view_Input* = michaelwr@google.com, svv@google.com
-per-file android_view_KeyCharacterMap.* = michaelwr@google.com, svv@google.com
-per-file android_view_*KeyEvent.* = michaelwr@google.com, svv@google.com
-per-file android_view_*MotionEvent.* = michaelwr@google.com, svv@google.com
-per-file android_view_PointerIcon.* = michaelwr@google.com, svv@google.com
+per-file android_hardware_input* = file:/services/core/java/com/android/server/input/OWNERS
+per-file android_view_Input* = file:/services/core/java/com/android/server/input/OWNERS
+per-file android_view_KeyCharacterMap.* = file:/services/core/java/com/android/server/input/OWNERS
+per-file android_view_*KeyEvent.* = file:/services/core/java/com/android/server/input/OWNERS
+per-file android_view_*MotionEvent.* = file:/services/core/java/com/android/server/input/OWNERS
+per-file android_view_PointerIcon.* = file:/services/core/java/com/android/server/input/OWNERS
+
+# WindowManager
+per-file android_graphics_BLASTBufferQueue.cpp = file:/services/core/java/com/android/server/wm/OWNERS
+per-file android_view_Surface* = file:/services/core/java/com/android/server/wm/OWNERS
+
+# Resources
+per-file android_content_res_* = file:/core/java/android/content/res/OWNERS
+per-file android_util_AssetManager* = file:/core/java/android/content/res/OWNERS
+per-file android_util_StringBlock* = file:/core/java/android/content/res/OWNERS
+per-file android_util_XmlBlock* = file:/core/java/android/content/res/OWNERS
+per-file com_android_internal_content_om_OverlayConfig* = file:/core/java/android/content/res/OWNERS
 
 per-file *Zygote* = file:/ZYGOTE_OWNERS
 per-file Android.bp = file:platform/build/soong:/OWNERS
 per-file android_animation_* = file:/core/java/android/animation/OWNERS
 per-file android_app_admin_* = file:/core/java/android/app/admin/OWNERS
-per-file android_content_res_* = file:/core/java/android/content/res/OWNERS
 per-file android_graphics_* = file:/graphics/java/android/graphics/OWNERS
 per-file android_hardware_Usb* = file:/services/usb/OWNERS
 per-file android_hardware_display_* = file:/core/java/android/hardware/display/OWNERS
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index fbdd406..444bb66 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -24,6 +24,7 @@
 #include "utils/misc.h"
 #include "utils/Trace.h"
 
+#include "android_util_AssetManager_private.h"
 #include "core_jni_helpers.h"
 #include "jni.h"
 #include "nativehelper/ScopedUtfChars.h"
@@ -347,12 +348,17 @@
     return 0;
   }
 
+  const auto buffer = asset->getIncFsBuffer(true /* aligned */);
+  const size_t length = asset->getLength();
+  if (!buffer.convert<uint8_t>().verify(length)) {
+    jniThrowException(env, kResourcesNotFound, kIOErrorMessage);
+    return 0;
+  }
+
   // DynamicRefTable is only needed when looking up resource references. Opening an XML file
   // directly from an ApkAssets has no notion of proper resource references.
-  std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(nullptr /*dynamicRefTable*/);
-  status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
-  asset.reset();
-
+  auto xml_tree = util::make_unique<ResXMLTree>(nullptr /*dynamicRefTable*/);
+  status_t err = xml_tree->setTo(buffer.unsafe_ptr(), length, true);
   if (err != NO_ERROR) {
     jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
     return 0;
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index e6881b3..b2c69a0 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -39,10 +39,10 @@
 #include "androidfw/AssetManager2.h"
 #include "androidfw/AttributeResolution.h"
 #include "androidfw/MutexGuard.h"
-#include "androidfw/PosixUtils.h"
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/ResourceUtils.h"
 
+#include "android_util_AssetManager_private.h"
 #include "core_jni_helpers.h"
 #include "jni.h"
 #include "nativehelper/JNIPlatformHelp.h"
@@ -58,7 +58,6 @@
 extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
 
 using ::android::base::StringPrintf;
-using ::android::util::ExecuteBinary;
 
 namespace android {
 
@@ -114,101 +113,17 @@
   return cookie > 0 ? static_cast<ApkAssetsCookie>(cookie - 1) : kInvalidCookie;
 }
 
-static jobjectArray NativeCreateIdmapsForStaticOverlaysTargetingAndroid(JNIEnv* env,
-                                                                        jclass /*clazz*/) {
-  // --input-directory can be given multiple times, but idmap2 expects the directory to exist
-  std::vector<std::string> input_dirs;
-  struct stat st;
-  if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) {
-    input_dirs.push_back(AssetManager::VENDOR_OVERLAY_DIR);
-  }
-
-  if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
-    input_dirs.push_back(AssetManager::PRODUCT_OVERLAY_DIR);
-  }
-
-  if (stat(AssetManager::SYSTEM_EXT_OVERLAY_DIR, &st) == 0) {
-    input_dirs.push_back(AssetManager::SYSTEM_EXT_OVERLAY_DIR);
-  }
-
-  if (stat(AssetManager::ODM_OVERLAY_DIR, &st) == 0) {
-    input_dirs.push_back(AssetManager::ODM_OVERLAY_DIR);
-  }
-
-  if (stat(AssetManager::OEM_OVERLAY_DIR, &st) == 0) {
-    input_dirs.push_back(AssetManager::OEM_OVERLAY_DIR);
-  }
-
-  if (input_dirs.empty()) {
-    LOG(WARNING) << "no directories for idmap2 to scan";
-    return env->NewObjectArray(0, g_stringClass, nullptr);
-  }
-
-  if (access("/system/bin/idmap2", X_OK) == -1) {
-    PLOG(WARNING) << "unable to execute idmap2";
-    return nullptr;
-  }
-
-  std::vector<std::string> argv{"/system/bin/idmap2",
-    "scan",
-    "--recursive",
-    "--target-package-name", "android",
-    "--target-apk-path", "/system/framework/framework-res.apk",
-    "--output-directory", "/data/resource-cache"};
-
-  for (const auto& dir : input_dirs) {
-    argv.push_back("--input-directory");
-    argv.push_back(dir);
-  }
-
-  const auto result = ExecuteBinary(argv);
-
-  if (!result) {
-      LOG(ERROR) << "failed to execute idmap2";
-      return nullptr;
-  }
-
-  if (result->status != 0) {
-    LOG(ERROR) << "idmap2: " << result->stderr;
-    return nullptr;
-  }
-
-  std::vector<std::string> idmap_paths;
-  std::istringstream input(result->stdout);
-  std::string path;
-  while (std::getline(input, path)) {
-    idmap_paths.push_back(path);
-  }
-
-  jobjectArray array = env->NewObjectArray(idmap_paths.size(), g_stringClass, nullptr);
-  if (array == nullptr) {
-    return nullptr;
-  }
-  for (size_t i = 0; i < idmap_paths.size(); i++) {
-    const std::string path = idmap_paths[i];
-    jstring java_string = env->NewStringUTF(path.c_str());
-    if (env->ExceptionCheck()) {
-      return nullptr;
-    }
-    env->SetObjectArrayElement(array, i, java_string);
-    env->DeleteLocalRef(java_string);
-  }
-  return array;
-}
-
-static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref,
-                      uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) {
-  env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType);
+static jint CopyValue(JNIEnv* env, const AssetManager2::SelectedValue& value,
+                      jobject out_typed_value) {
+  env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.type);
   env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie,
-                   ApkAssetsCookieToJavaCookie(cookie));
+                   ApkAssetsCookieToJavaCookie(value.cookie));
   env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data);
   env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr);
-  env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref);
-  env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags);
-  if (config != nullptr) {
-    env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density);
-  }
-  return static_cast<jint>(ApkAssetsCookieToJavaCookie(cookie));
+  env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, value.resid);
+  env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, value.flags);
+  env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, value.config.density);
+  return static_cast<jint>(ApkAssetsCookieToJavaCookie(value.cookie));
 }
 
 // ----------------------------------------------------------------------------
@@ -653,15 +568,15 @@
     return 0;
   }
 
-  // May be nullptr.
-  std::shared_ptr<const DynamicRefTable> dynamic_ref_table =
-      assetmanager->GetDynamicRefTableForCookie(cookie);
+  const incfs::map_ptr<void> buffer = asset->getIncFsBuffer(true /* aligned */);
+  const size_t length = asset->getLength();
+  if (!buffer.convert<uint8_t>().verify(length)) {
+    jniThrowException(env, kResourcesNotFound, kIOErrorMessage);
+    return 0;
+  }
 
-  std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(
-      std::move(dynamic_ref_table));
-  status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
-  asset.reset();
-
+  auto xml_tree = util::make_unique<ResXMLTree>(assetmanager->GetDynamicRefTableForCookie(cookie));
+  status_t err = xml_tree->setTo(buffer.unsafe_ptr(), length, true);
   if (err != NO_ERROR) {
     jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
     return 0;
@@ -690,15 +605,15 @@
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
   ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
 
-  // May be nullptr.
-   std::shared_ptr<const DynamicRefTable> dynamic_ref_table =
-       assetmanager->GetDynamicRefTableForCookie(cookie);
+  const incfs::map_ptr<void> buffer = asset->getIncFsBuffer(true /* aligned */);
+  const size_t length = asset->getLength();
+  if (!buffer.convert<uint8_t>().verify(length)) {
+    jniThrowException(env, kResourcesNotFound, kIOErrorMessage);
+    return 0;
+  }
 
-  std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(
-      std::move(dynamic_ref_table));
-  status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
-  asset.reset();
-
+  auto xml_tree = util::make_unique<ResXMLTree>(assetmanager->GetDynamicRefTableForCookie(cookie));
+  status_t err = xml_tree->setTo(buffer.unsafe_ptr(), length, true);
   if (err != NO_ERROR) {
     jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
     return 0;
@@ -710,67 +625,62 @@
                                    jshort density, jobject typed_value,
                                    jboolean resolve_references) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  Res_value value;
-  ResTable_config selected_config;
-  uint32_t flags;
-  ApkAssetsCookie cookie =
-      assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
-                                static_cast<uint16_t>(density), &value, &selected_config, &flags);
-  if (cookie == kInvalidCookie) {
+  auto value = assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
+                                         static_cast<uint16_t>(density));
+  if (!value.has_value()) {
+    ThrowIfIOError(env, value);
     return ApkAssetsCookieToJavaCookie(kInvalidCookie);
   }
 
-  uint32_t ref = static_cast<uint32_t>(resid);
   if (resolve_references) {
-    cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
-    if (cookie == kInvalidCookie) {
+    auto result = assetmanager->ResolveReference(value.value());
+    if (!result.has_value()) {
+      ThrowIfIOError(env, result);
       return ApkAssetsCookieToJavaCookie(kInvalidCookie);
     }
   }
-  return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value);
+  return CopyValue(env, *value, typed_value);
 }
 
 static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
                                       jint bag_entry_id, jobject typed_value) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
-  if (bag == nullptr) {
+  auto bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+  if (!bag.has_value()) {
+    ThrowIfIOError(env, bag);
     return ApkAssetsCookieToJavaCookie(kInvalidCookie);
   }
 
-  uint32_t type_spec_flags = bag->type_spec_flags;
-  ApkAssetsCookie cookie = kInvalidCookie;
-  const Res_value* bag_value = nullptr;
-  for (const ResolvedBag::Entry& entry : bag) {
-    if (entry.key == static_cast<uint32_t>(bag_entry_id)) {
-      cookie = entry.cookie;
-      bag_value = &entry.value;
+  // The legacy would find the last entry with the target bag entry id
+  using reverse_bag_iterator = std::reverse_iterator<const ResolvedBag::Entry*>;
+  const auto rbegin = reverse_bag_iterator(end(*bag));
+  const auto rend = reverse_bag_iterator(begin(*bag));
+  auto entry = std::find_if(rbegin, rend, [bag_entry_id](auto&& e) {
+    return e.key == static_cast<uint32_t>(bag_entry_id);
+  });
 
-      // Keep searching (the old implementation did that).
-    }
-  }
-
-  if (cookie == kInvalidCookie) {
+  if (entry == rend) {
     return ApkAssetsCookieToJavaCookie(kInvalidCookie);
   }
 
-  Res_value value = *bag_value;
-  uint32_t ref = static_cast<uint32_t>(resid);
-  ResTable_config selected_config;
-  cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref);
-  if (cookie == kInvalidCookie) {
+  AssetManager2::SelectedValue attr_value(*bag, *entry);
+  auto result = assetmanager->ResolveReference(attr_value);
+  if (!result.has_value()) {
+    ThrowIfIOError(env, result);
     return ApkAssetsCookieToJavaCookie(kInvalidCookie);
   }
-  return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value);
+  return CopyValue(env, attr_value, typed_value);
 }
 
 static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
-  if (bag == nullptr) {
+  auto bag_result = assetmanager->GetBag(static_cast<uint32_t>(resid));
+  if (!bag_result.has_value()) {
+    ThrowIfIOError(env, bag_result);
     return nullptr;
   }
 
+  const ResolvedBag* bag = *bag_result;
   jintArray array = env->NewIntArray(bag->entry_count);
   if (env->ExceptionCheck()) {
     return nullptr;
@@ -786,42 +696,47 @@
 static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr,
                                                  jint resid) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
-  if (bag == nullptr) {
+  auto bag_result = assetmanager->GetBag(static_cast<uint32_t>(resid));
+  if (!bag_result.has_value()) {
+    ThrowIfIOError(env, bag_result);
     return nullptr;
   }
 
+  const ResolvedBag* bag = *bag_result;
   jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr);
   if (array == nullptr) {
     return nullptr;
   }
 
   for (uint32_t i = 0; i < bag->entry_count; i++) {
-    const ResolvedBag::Entry& entry = bag->entries[i];
-
     // Resolve any references to their final value.
-    Res_value value = entry.value;
-    ResTable_config selected_config;
-    uint32_t flags;
-    uint32_t ref;
-    ApkAssetsCookie cookie =
-        assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
-    if (cookie == kInvalidCookie) {
+    AssetManager2::SelectedValue attr_value(bag, bag->entries[i]);
+    auto result = assetmanager->ResolveReference(attr_value);
+    if (!result.has_value()) {
+      ThrowIfIOError(env, result);
       return nullptr;
     }
 
-    if (value.dataType == Res_value::TYPE_STRING) {
-      const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie];
+    if (attr_value.type == Res_value::TYPE_STRING) {
+      const ApkAssets* apk_assets = assetmanager->GetApkAssets()[attr_value.cookie];
       const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool();
 
       jstring java_string = nullptr;
-      size_t str_len;
-      const char* str_utf8 = pool->string8At(value.data, &str_len);
-      if (str_utf8 != nullptr) {
-        java_string = env->NewStringUTF(str_utf8);
+      auto str_utf8 = pool->string8At(attr_value.data);
+      if (UNLIKELY(ThrowIfIOError(env, str_utf8))) {
+        return nullptr;
+      }
+
+      if (str_utf8.has_value()) {
+        java_string = env->NewStringUTF(str_utf8->data());
       } else {
-        const char16_t* str_utf16 = pool->stringAt(value.data, &str_len);
-        java_string = env->NewString(reinterpret_cast<const jchar*>(str_utf16), str_len);
+        auto str_utf16 = pool->stringAt(attr_value.data);
+        if (!str_utf16.has_value()) {
+          ThrowIfIOError(env, str_utf16);
+          return nullptr;
+        }
+        java_string = env->NewString(reinterpret_cast<const jchar*>(str_utf16->data()),
+                                     str_utf16->size());
       }
 
       // Check for errors creating the strings (if malformed or no memory).
@@ -842,11 +757,13 @@
 static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
                                                   jint resid) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
-  if (bag == nullptr) {
+  auto bag_result = assetmanager->GetBag(static_cast<uint32_t>(resid));
+  if (!bag_result.has_value()) {
+    ThrowIfIOError(env, bag_result);
     return nullptr;
   }
 
+  const ResolvedBag* bag = *bag_result;
   jintArray array = env->NewIntArray(bag->entry_count * 2);
   if (array == nullptr) {
     return nullptr;
@@ -858,24 +775,20 @@
   }
 
   for (size_t i = 0; i < bag->entry_count; i++) {
-    const ResolvedBag::Entry& entry = bag->entries[i];
-    Res_value value = entry.value;
-    ResTable_config selected_config;
-    uint32_t flags;
-    uint32_t ref;
-    ApkAssetsCookie cookie =
-        assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
-    if (cookie == kInvalidCookie) {
+    AssetManager2::SelectedValue attr_value(bag, bag->entries[i]);
+    auto result = assetmanager->ResolveReference(attr_value);
+    if (!result.has_value()) {
       env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
+      ThrowIfIOError(env, result);
       return nullptr;
     }
 
     jint string_index = -1;
-    if (value.dataType == Res_value::TYPE_STRING) {
-      string_index = static_cast<jint>(value.data);
+    if (attr_value.type == Res_value::TYPE_STRING) {
+      string_index = static_cast<jint>(attr_value.data);
     }
 
-    buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie);
+    buffer[i * 2] = ApkAssetsCookieToJavaCookie(attr_value.cookie);
     buffer[(i * 2) + 1] = string_index;
   }
   env->ReleasePrimitiveArrayCritical(array, buffer, 0);
@@ -884,11 +797,13 @@
 
 static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
-  if (bag == nullptr) {
+  auto bag_result = assetmanager->GetBag(static_cast<uint32_t>(resid));
+  if (!bag_result.has_value()) {
+    ThrowIfIOError(env, bag_result);
     return nullptr;
   }
 
+  const ResolvedBag* bag = *bag_result;
   jintArray array = env->NewIntArray(bag->entry_count);
   if (array == nullptr) {
     return nullptr;
@@ -900,40 +815,39 @@
   }
 
   for (size_t i = 0; i < bag->entry_count; i++) {
-    const ResolvedBag::Entry& entry = bag->entries[i];
-    Res_value value = entry.value;
-    ResTable_config selected_config;
-    uint32_t flags;
-    uint32_t ref;
-    ApkAssetsCookie cookie =
-        assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
-    if (cookie == kInvalidCookie) {
-      env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
+    AssetManager2::SelectedValue attr_value(bag, bag->entries[i]);
+    auto result = assetmanager->ResolveReference(attr_value);
+    if (!result.has_value()) {
+      env->ReleasePrimitiveArrayCritical(array, buffer, 0);
+      ThrowIfIOError(env, result);
       return nullptr;
     }
 
-    if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) {
-      buffer[i] = static_cast<jint>(value.data);
+    if (attr_value.type >= Res_value::TYPE_FIRST_INT &&
+      attr_value.type <= Res_value::TYPE_LAST_INT) {
+      buffer[i] = static_cast<jint>(attr_value.data);
     }
   }
   env->ReleasePrimitiveArrayCritical(array, buffer, 0);
   return array;
 }
 
-static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) {
-  ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
-  if (bag == nullptr) {
-    return -1;
-  }
-  return static_cast<jint>(bag->entry_count);
+static jint NativeGetResourceArraySize(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+    ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+    auto bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+    if (!bag.has_value()) {
+      ThrowIfIOError(env, bag);
+      return -1;
+    }
+    return static_cast<jint>((*bag)->entry_count);
 }
 
 static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
                                    jintArray out_data) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
-  if (bag == nullptr) {
+  auto bag_result = assetmanager->GetBag(static_cast<uint32_t>(resid));
+  if (!bag_result.has_value()) {
+    ThrowIfIOError(env, bag_result);
     return -1;
   }
 
@@ -942,8 +856,10 @@
     return -1;
   }
 
+  const ResolvedBag* bag = *bag_result;
   if (static_cast<jsize>(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) {
-    jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough");
+    jniThrowException(env, "java/lang/IllegalArgumentException",
+                      "Input array is not large enough");
     return -1;
   }
 
@@ -954,31 +870,26 @@
 
   jint* cursor = buffer;
   for (size_t i = 0; i < bag->entry_count; i++) {
-    const ResolvedBag::Entry& entry = bag->entries[i];
-    Res_value value = entry.value;
-    ResTable_config selected_config;
-    selected_config.density = 0;
-    uint32_t flags = bag->type_spec_flags;
-    uint32_t ref = 0;
-    ApkAssetsCookie cookie =
-        assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
-    if (cookie == kInvalidCookie) {
+    AssetManager2::SelectedValue attr_value(bag, bag->entries[i]);
+    auto result = assetmanager->ResolveReference(attr_value);
+    if (!result.has_value()) {
       env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT);
+      ThrowIfIOError(env, bag_result);
       return -1;
     }
 
     // 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;
-      value.data = Res_value::DATA_NULL_UNDEFINED;
+    if (attr_value.type == Res_value::TYPE_REFERENCE && attr_value.data == 0) {
+      attr_value.type = Res_value::TYPE_NULL;
+      attr_value.data = Res_value::DATA_NULL_UNDEFINED;
     }
 
-    cursor[STYLE_TYPE] = static_cast<jint>(value.dataType);
-    cursor[STYLE_DATA] = static_cast<jint>(value.data);
-    cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
-    cursor[STYLE_RESOURCE_ID] = static_cast<jint>(ref);
-    cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast<jint>(flags);
-    cursor[STYLE_DENSITY] = static_cast<jint>(selected_config.density);
+    cursor[STYLE_TYPE] = static_cast<jint>(attr_value.type);
+    cursor[STYLE_DATA] = static_cast<jint>(attr_value.data);
+    cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(attr_value.cookie);
+    cursor[STYLE_RESOURCE_ID] = static_cast<jint>(attr_value.resid);
+    cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast<jint>(attr_value.flags);
+    cursor[STYLE_DENSITY] = static_cast<jint>(attr_value.config.density);
     cursor += STYLE_NUM_ENTRIES;
   }
   env->ReleasePrimitiveArrayCritical(out_data, buffer, 0);
@@ -1006,60 +917,71 @@
     CHECK(package_utf8.c_str() != nullptr);
     package = package_utf8.c_str();
   }
+
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  return static_cast<jint>(assetmanager->GetResourceId(name_utf8.c_str(), type, package));
+  auto resid = assetmanager->GetResourceId(name_utf8.c_str(), type, package);
+  if (!resid.has_value()) {
+    ThrowIfIOError(env, resid);
+    return 0;
+  }
+
+  return static_cast<jint>(*resid);
 }
 
 static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  AssetManager2::ResourceName name;
-  if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+  auto name = assetmanager->GetResourceName(static_cast<uint32_t>(resid));
+  if (!name.has_value()) {
+    ThrowIfIOError(env, name);
     return nullptr;
   }
 
-  std::string result = ToFormattedResourceString(&name);
+  const std::string result = ToFormattedResourceString(name.value());
   return env->NewStringUTF(result.c_str());
 }
 
 static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  AssetManager2::ResourceName name;
-  if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+  auto name = assetmanager->GetResourceName(static_cast<uint32_t>(resid));
+  if (!name.has_value()) {
+    ThrowIfIOError(env, name);
     return nullptr;
   }
 
-  if (name.package != nullptr) {
-    return env->NewStringUTF(name.package);
+  if (name->package != nullptr) {
+    return env->NewStringUTF(name->package);
   }
   return nullptr;
 }
 
 static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  AssetManager2::ResourceName name;
-  if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+  auto name = assetmanager->GetResourceName(static_cast<uint32_t>(resid));
+  if (!name.has_value()) {
+    ThrowIfIOError(env, name);
     return nullptr;
   }
 
-  if (name.type != nullptr) {
-    return env->NewStringUTF(name.type);
-  } else if (name.type16 != nullptr) {
-    return env->NewString(reinterpret_cast<const jchar*>(name.type16), name.type_len);
+  if (name->type != nullptr) {
+    return env->NewStringUTF(name->type);
+  } else if (name->type16 != nullptr) {
+    return env->NewString(reinterpret_cast<const jchar*>(name->type16), name->type_len);
   }
   return nullptr;
 }
 
 static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  AssetManager2::ResourceName name;
-  if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+  auto name = assetmanager->GetResourceName(static_cast<uint32_t>(resid));
+  if (!name.has_value()) {
+    ThrowIfIOError(env, name);
     return nullptr;
   }
 
-  if (name.entry != nullptr) {
-    return env->NewStringUTF(name.entry);
-  } else if (name.entry16 != nullptr) {
-    return env->NewString(reinterpret_cast<const jchar*>(name.entry16), name.entry_len);
+  if (name->entry != nullptr) {
+    return env->NewStringUTF(name->entry);
+  } else if (name->entry16 != nullptr) {
+    return env->NewString(reinterpret_cast<const jchar*>(name->entry16), name->entry_len);
   }
   return nullptr;
 }
@@ -1123,17 +1045,21 @@
 
 static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  std::set<ResTable_config> configurations =
-      assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/);
+  auto configurations = assetmanager->GetResourceConfigurations(true /*exclude_system*/,
+                                                                false /*exclude_mipmap*/);
+  if (!configurations.has_value()) {
+    ThrowIfIOError(env, configurations);
+    return nullptr;
+  }
 
   jobjectArray array =
-      env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr);
+      env->NewObjectArray(configurations->size(), gConfigurationOffsets.classObject, nullptr);
   if (array == nullptr) {
     return nullptr;
   }
 
   size_t idx = 0;
-  for (const ResTable_config& configuration : configurations) {
+  for (const ResTable_config& configuration : *configurations) {
     jobject java_configuration = ConstructConfigurationObject(env, configuration);
     if (java_configuration == nullptr) {
       return nullptr;
@@ -1156,13 +1082,10 @@
   (void) assetmanager;
 
   // 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;
-      }
+    auto value = theme->GetAttribute(def_style_attr);
+    if (value.has_value() && value->type == Res_value::TYPE_REFERENCE) {
+      def_style_resid = value->data;
     }
   }
 
@@ -1203,10 +1126,11 @@
     return;
   }
 
-  ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr),
-             static_cast<uint32_t>(def_style_resid), reinterpret_cast<uint32_t*>(attrs), attrs_len,
-             out_values, out_indices);
+  auto result = ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr),
+                           static_cast<uint32_t>(def_style_resid),
+                           reinterpret_cast<uint32_t*>(attrs), attrs_len, out_values, out_indices);
   env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+  ThrowIfIOError(env, result);
 }
 
 static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
@@ -1267,11 +1191,12 @@
   Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
   CHECK(theme->GetAssetManager() == &(*assetmanager));
   (void) assetmanager;
-
-  bool result = ResolveAttrs(
-      theme, static_cast<uint32_t>(def_style_attr), static_cast<uint32_t>(def_style_resid),
-      reinterpret_cast<uint32_t*>(values), values_len, reinterpret_cast<uint32_t*>(attrs),
-      attrs_len, reinterpret_cast<uint32_t*>(out_values), reinterpret_cast<uint32_t*>(out_indices));
+  auto result =
+          ResolveAttrs(theme, static_cast<uint32_t>(def_style_attr),
+                       static_cast<uint32_t>(def_style_resid), reinterpret_cast<uint32_t*>(values),
+                       values_len, reinterpret_cast<uint32_t*>(attrs), attrs_len,
+                       reinterpret_cast<uint32_t*>(out_values),
+                       reinterpret_cast<uint32_t*>(out_indices));
   if (out_indices != nullptr) {
     env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
   }
@@ -1280,8 +1205,13 @@
   if (values != nullptr) {
     env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
   }
+
   env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
-  return result ? JNI_TRUE : JNI_FALSE;
+  if (!result.has_value()) {
+    ThrowIfIOError(env, result);
+    return JNI_FALSE;
+  }
+  return JNI_TRUE;
 }
 
 static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr,
@@ -1322,18 +1252,22 @@
 
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
   ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
-
-  bool result = RetrieveAttributes(assetmanager.get(), xml_parser,
-                                   reinterpret_cast<uint32_t*>(attrs), attrs_len,
-                                   reinterpret_cast<uint32_t*>(out_values),
-                                   reinterpret_cast<uint32_t*>(out_indices));
+  auto result =
+          RetrieveAttributes(assetmanager.get(), xml_parser, reinterpret_cast<uint32_t*>(attrs),
+                             attrs_len, reinterpret_cast<uint32_t*>(out_values),
+                             reinterpret_cast<uint32_t*>(out_indices));
 
   if (out_indices != nullptr) {
     env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
   }
+
   env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0);
   env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
-  return result ? JNI_TRUE : JNI_FALSE;
+  if (!result.has_value()) {
+    ThrowIfIOError(env, result);
+    return JNI_FALSE;
+  }
+  return JNI_TRUE;
 }
 
 static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
@@ -1352,7 +1286,9 @@
   Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
   CHECK(theme->GetAssetManager() == &(*assetmanager));
   (void) assetmanager;
-  theme->ApplyStyle(static_cast<uint32_t>(resid), force);
+
+  auto result = theme->ApplyStyle(static_cast<uint32_t>(resid), force);
+  ThrowIfIOError(env, result);
 
   // TODO(adamlesinski): Consider surfacing exception when result is failure.
   // CTS currently expects no exceptions from this method.
@@ -1365,19 +1301,22 @@
   Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr);
   Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr);
 
+  ScopedLock<AssetManager2> src_assetmanager(AssetManagerFromLong(src_asset_manager_ptr));
+  CHECK(src_theme->GetAssetManager() == &(*src_assetmanager));
+  (void) src_assetmanager;
+
   if (dst_asset_manager_ptr != src_asset_manager_ptr) {
     ScopedLock<AssetManager2> dst_assetmanager(AssetManagerFromLong(dst_asset_manager_ptr));
     CHECK(dst_theme->GetAssetManager() == &(*dst_assetmanager));
     (void) dst_assetmanager;
 
-    ScopedLock <AssetManager2> src_assetmanager(AssetManagerFromLong(src_asset_manager_ptr));
-    CHECK(src_theme->GetAssetManager() == &(*src_assetmanager));
-    (void) src_assetmanager;
-
-    dst_theme->SetTo(*src_theme);
-  } else {
-    dst_theme->SetTo(*src_theme);
+    auto result = dst_theme->SetTo(*src_theme);
+    ThrowIfIOError(env, result);
+    return;
   }
+
+  auto result = dst_theme->SetTo(*src_theme);
+  ThrowIfIOError(env, result);
 }
 
 static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
@@ -1392,23 +1331,21 @@
   CHECK(theme->GetAssetManager() == &(*assetmanager));
   (void) assetmanager;
 
-  Res_value value;
-  uint32_t flags;
-  ApkAssetsCookie cookie = theme->GetAttribute(static_cast<uint32_t>(resid), &value, &flags);
-  if (cookie == kInvalidCookie) {
+  auto value = theme->GetAttribute(static_cast<uint32_t>(resid));
+  if (!value.has_value()) {
     return ApkAssetsCookieToJavaCookie(kInvalidCookie);
   }
 
-  uint32_t ref = 0u;
-  if (resolve_references) {
-    ResTable_config selected_config;
-    cookie =
-        theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
-    if (cookie == kInvalidCookie) {
-      return ApkAssetsCookieToJavaCookie(kInvalidCookie);
-    }
+  if (!resolve_references) {
+    return CopyValue(env, *value, typed_value);
   }
-  return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value);
+
+  auto result = theme->GetAssetManager()->ResolveReference(*value);
+  if (!result.has_value()) {
+    ThrowIfIOError(env, result);
+    return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+  }
+  return CopyValue(env, *value, typed_value);
 }
 
 static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
@@ -1563,8 +1500,6 @@
     {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
 
     // System/idmap related methods.
-    {"nativeCreateIdmapsForStaticOverlaysTargetingAndroid", "()[Ljava/lang/String;",
-     (void*)NativeCreateIdmapsForStaticOverlaysTargetingAndroid},
     {"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;",
      (void*)NativeGetOverlayableMap},
     {"nativeGetOverlayablesToString", "(JLjava/lang/String;)Ljava/lang/String;",
diff --git a/core/jni/android_util_AssetManager_private.h b/core/jni/android_util_AssetManager_private.h
new file mode 100644
index 0000000..153509b9
--- /dev/null
+++ b/core/jni/android_util_AssetManager_private.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 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 ANDROID_UTIL_ASSETMANAGER_PRIVATE_H
+#define ANDROID_UTIL_ASSETMANAGER_PRIVATE_H
+
+#include <optional>
+
+#include <androidfw/Errors.h>
+#include <android-base/expected.h>
+
+#include "core_jni_helpers.h"
+#include "jni.h"
+#include "nativehelper/JNIHelp.h"
+
+namespace android {
+
+constexpr const char* kResourcesNotFound = "android/content/res/Resources$NotFoundException";
+constexpr const static char* kIOErrorMessage = "failed to read resources.arsc data";
+
+template <typename T, typename E>
+static bool ThrowIfIOError(JNIEnv* env, const base::expected<T, E>& result) {
+  if constexpr (std::is_same<NullOrIOError, E>::value) {
+    if (IsIOError(result)) {
+      jniThrowException(env, kResourcesNotFound, kIOErrorMessage);
+      return true;
+    }
+     return false;
+  } else {
+    if (!result.has_value()) {
+      static_assert(std::is_same<IOError, E>::value, "Unknown result error type");
+      jniThrowException(env, kResourcesNotFound, kIOErrorMessage);
+      return true;
+    }
+    return false;
+  }
+}
+
+} // namespace android
+
+#endif //ANDROID_UTIL_ASSETMANAGER_PRIVATE_H
diff --git a/core/jni/android_util_StringBlock.cpp b/core/jni/android_util_StringBlock.cpp
index 760f9e3..45f6b72 100644
--- a/core/jni/android_util_StringBlock.cpp
+++ b/core/jni/android_util_StringBlock.cpp
@@ -17,6 +17,7 @@
 
 #define LOG_TAG "StringBlock"
 
+#include "android_util_AssetManager_private.h"
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
 #include <utils/misc.h>
@@ -31,10 +32,8 @@
 
 // ----------------------------------------------------------------------------
 
-static jlong android_content_StringBlock_nativeCreate(JNIEnv* env, jobject clazz,
-                                                  jbyteArray bArray,
-                                                  jint off, jint len)
-{
+static jlong android_content_StringBlock_nativeCreate(JNIEnv* env, jobject clazz, jbyteArray bArray,
+                                                      jint off, jint len) {
     if (bArray == NULL) {
         jniThrowNullPointerException(env, NULL);
         return 0;
@@ -59,9 +58,7 @@
     return reinterpret_cast<jlong>(osb);
 }
 
-static jint android_content_StringBlock_nativeGetSize(JNIEnv* env, jobject clazz,
-                                                   jlong token)
-{
+static jint android_content_StringBlock_nativeGetSize(JNIEnv* env, jobject clazz, jlong token) {
     ResStringPool* osb = reinterpret_cast<ResStringPool*>(token);
     if (osb == NULL) {
         jniThrowNullPointerException(env, NULL);
@@ -71,76 +68,84 @@
     return osb->size();
 }
 
-static jstring android_content_StringBlock_nativeGetString(JNIEnv* env, jobject clazz,
-                                                        jlong token, jint idx)
-{
+static jstring android_content_StringBlock_nativeGetString(JNIEnv* env, jobject clazz, jlong token,
+                                                           jint idx) {
     ResStringPool* osb = reinterpret_cast<ResStringPool*>(token);
     if (osb == NULL) {
         jniThrowNullPointerException(env, NULL);
         return 0;
     }
 
-    size_t len;
-    const char* str8 = osb->string8At(idx, &len);
-    if (str8 != NULL) {
-        return env->NewStringUTF(str8);
+    auto str8 = osb->string8At(idx);
+    if (UNLIKELY(ThrowIfIOError(env, str8))) {
+        return 0;
+    } else if (str8.has_value()) {
+        return env->NewStringUTF(str8->data());
     }
 
-    const char16_t* str = osb->stringAt(idx, &len);
-    if (str == NULL) {
+    auto str = osb->stringAt(idx);
+    if (UNLIKELY(ThrowIfIOError(env, str))) {
+        return 0;
+    } else if (UNLIKELY(!str.has_value())) {
         jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL);
         return 0;
     }
 
-    return env->NewString((const jchar*)str, len);
+    return env->NewString((const jchar*)str->data(), str->size());
 }
 
-static jintArray android_content_StringBlock_nativeGetStyle(JNIEnv* env, jobject clazz,
-                                                         jlong token, jint idx)
-{
+static jintArray android_content_StringBlock_nativeGetStyle(JNIEnv* env, jobject clazz, jlong token,
+                                                            jint idx) {
     ResStringPool* osb = reinterpret_cast<ResStringPool*>(token);
     if (osb == NULL) {
         jniThrowNullPointerException(env, NULL);
         return NULL;
     }
 
-    const ResStringPool_span* spans = osb->styleAt(idx);
-    if (spans == NULL) {
+    auto spans = osb->styleAt(idx);
+    if (!spans.has_value()) {
+        ThrowIfIOError(env, spans);
         return NULL;
     }
 
-    const ResStringPool_span* pos = spans;
-    int num = 0;
-    while (pos->name.index != ResStringPool_span::END) {
-        num++;
-        pos++;
-    }
+    jintArray array;
+    {
+        int num = 0;
+        auto pos = *spans;
+        while (true) {
+            if (UNLIKELY(!pos)) {
+                jniThrowException(env, kResourcesNotFound, kIOErrorMessage);
+                return NULL;
+            }
+            if (pos->name.index == ResStringPool_span::END) {
+                break;
+            }
+            num++;
+            pos++;
+        }
 
-    if (num == 0) {
-        return NULL;
-    }
+        if (num == 0) {
+            return NULL;
+        }
 
-    jintArray array = env->NewIntArray((num*sizeof(ResStringPool_span))/sizeof(jint));
-    if (array == NULL) { // NewIntArray already threw OutOfMemoryError.
-        return NULL;
+        array = env->NewIntArray((num * sizeof(ResStringPool_span)) / sizeof(jint));
+        if (array == NULL) { // NewIntArray already threw OutOfMemoryError.
+            return NULL;
+        }
     }
-
-    num = 0;
-    static const int numInts = sizeof(ResStringPool_span)/sizeof(jint);
-    while (spans->name.index != ResStringPool_span::END) {
-        env->SetIntArrayRegion(array,
-                                  num*numInts, numInts,
-                                  (jint*)spans);
-        spans++;
-        num++;
+    {
+        int num = 0;
+        static const int numInts = sizeof(ResStringPool_span) / sizeof(jint);
+        while ((*spans)->name.index != ResStringPool_span::END) {
+            env->SetIntArrayRegion(array, num * numInts, numInts, (jint*)spans->unsafe_ptr());
+            (*spans)++;
+            num++;
+        }
     }
-
     return array;
 }
 
-static void android_content_StringBlock_nativeDestroy(JNIEnv* env, jobject clazz,
-                                                   jlong token)
-{
+static void android_content_StringBlock_nativeDestroy(JNIEnv* env, jobject clazz, jlong token) {
     ResStringPool* osb = reinterpret_cast<ResStringPool*>(token);
     if (osb == NULL) {
         jniThrowNullPointerException(env, NULL);
diff --git a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
index 9246a23..f9e3bc6 100644
--- a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
+++ b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
@@ -118,6 +118,7 @@
         // Wifi bugreports should not receive any progress.
         assertThat(callback.hasReceivedProgress()).isFalse();
         assertThat(mBugreportFile.length()).isGreaterThan(0L);
+        assertThat(callback.hasEarlyReportFinished()).isTrue();
         assertFdsAreClosed(mBugreportFd);
     }
 
@@ -135,6 +136,7 @@
         // Interactive bugreports show progress updates.
         assertThat(callback.hasReceivedProgress()).isTrue();
         assertThat(mBugreportFile.length()).isGreaterThan(0L);
+        assertThat(callback.hasEarlyReportFinished()).isTrue();
         assertFdsAreClosed(mBugreportFd);
     }
 
@@ -246,6 +248,7 @@
         private int mErrorCode = -1;
         private boolean mSuccess = false;
         private boolean mReceivedProgress = false;
+        private boolean mEarlyReportFinished = false;
         private final Object mLock = new Object();
 
         @Override
@@ -271,6 +274,13 @@
             }
         }
 
+        @Override
+        public void onEarlyReportFinished() {
+            synchronized (mLock) {
+                mEarlyReportFinished = true;
+            }
+        }
+
         /* Indicates completion; and ended up with a success or error. */
         public boolean isDone() {
             synchronized (mLock) {
@@ -295,6 +305,12 @@
                 return mReceivedProgress;
             }
         }
+
+        public boolean hasEarlyReportFinished() {
+            synchronized (mLock) {
+                return mEarlyReportFinished;
+            }
+        }
     }
 
     public static BugreportManager getBugreportManager() {
diff --git a/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS
new file mode 100644
index 0000000..bd7da0c
--- /dev/null
+++ b/core/tests/coretests/src/android/app/OWNERS
@@ -0,0 +1 @@
+per-file Window*.java = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/tests/coretests/src/android/app/activity/OWNERS b/core/tests/coretests/src/android/app/activity/OWNERS
new file mode 100644
index 0000000..0862c05
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/core/tests/coretests/src/android/app/servertransaction/OWNERS b/core/tests/coretests/src/android/app/servertransaction/OWNERS
new file mode 100644
index 0000000..0862c05
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/core/tests/coretests/src/android/app/time/OWNERS b/core/tests/coretests/src/android/app/time/OWNERS
new file mode 100644
index 0000000..8f80897
--- /dev/null
+++ b/core/tests/coretests/src/android/app/time/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/core/tests/coretests/src/android/app/timedetector/GnssTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/GnssTimeSuggestionTest.java
new file mode 100644
index 0000000..e248010
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timedetector/GnssTimeSuggestionTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 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.
+ */
+
+package android.app.timedetector;
+
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
+import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.TimestampedValue;
+
+import org.junit.Test;
+
+public class GnssTimeSuggestionTest {
+
+    private static final TimestampedValue<Long> ARBITRARY_TIME =
+            new TimestampedValue<>(1111L, 2222L);
+
+    @Test
+    public void testEquals() {
+        GnssTimeSuggestion one = new GnssTimeSuggestion(ARBITRARY_TIME);
+        assertEquals(one, one);
+
+        GnssTimeSuggestion two = new GnssTimeSuggestion(ARBITRARY_TIME);
+        assertEquals(one, two);
+        assertEquals(two, one);
+
+        TimestampedValue<Long> differentTime = new TimestampedValue<>(
+                ARBITRARY_TIME.getReferenceTimeMillis() + 1,
+                ARBITRARY_TIME.getValue());
+        GnssTimeSuggestion three = new GnssTimeSuggestion(differentTime);
+        assertNotEquals(one, three);
+        assertNotEquals(three, one);
+
+        // DebugInfo must not be considered in equals().
+        one.addDebugInfo("Debug info 1");
+        two.addDebugInfo("Debug info 2");
+        assertEquals(one, two);
+    }
+
+    @Test
+    public void testParcelable() {
+        GnssTimeSuggestion suggestion = new GnssTimeSuggestion(ARBITRARY_TIME);
+        assertRoundTripParcelable(suggestion);
+
+        // DebugInfo should also be stored (but is not checked by equals()
+        suggestion.addDebugInfo("This is debug info");
+        GnssTimeSuggestion rtSuggestion = roundTripParcelable(suggestion);
+        assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo());
+    }
+}
diff --git a/core/tests/coretests/src/android/app/timedetector/OWNERS b/core/tests/coretests/src/android/app/timedetector/OWNERS
new file mode 100644
index 0000000..8f80897
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timedetector/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/core/tests/coretests/src/android/app/timezone/OWNERS b/core/tests/coretests/src/android/app/timezone/OWNERS
new file mode 100644
index 0000000..8f80897
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timezone/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/core/tests/coretests/src/android/app/timezonedetector/OWNERS b/core/tests/coretests/src/android/app/timezonedetector/OWNERS
new file mode 100644
index 0000000..8f80897
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timezonedetector/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/core/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS
new file mode 100644
index 0000000..911efb2
--- /dev/null
+++ b/core/tests/coretests/src/android/content/OWNERS
@@ -0,0 +1 @@
+per-file ContextTest.java = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/tests/mockingcoretests/src/android/app/activity/OWNERS b/core/tests/mockingcoretests/src/android/app/activity/OWNERS
new file mode 100644
index 0000000..0862c05
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/app/activity/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/graphics/java/android/graphics/OWNERS b/graphics/java/android/graphics/OWNERS
index 6196889..a8a3631 100644
--- a/graphics/java/android/graphics/OWNERS
+++ b/graphics/java/android/graphics/OWNERS
@@ -4,3 +4,8 @@
 jreck@google.com
 njawad@google.com
 sumir@google.com
+
+per-file BLASTBufferQueue.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file FontFamily.java = file:fonts/OWNERS
+per-file FontListParser.java = file:fonts/OWNERS
+per-file Typeface.java = file:fonts/OWNERS
diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java
index 49a4887..7ea9e14 100644
--- a/keystore/java/android/security/KeyStoreOperation.java
+++ b/keystore/java/android/security/KeyStoreOperation.java
@@ -17,7 +17,7 @@
 package android.security;
 
 import android.annotation.NonNull;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.security.keymaster.KeymasterDefs;
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 7c3de8b..3ef4aa5 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.app.compat.CompatChanges;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.security.keystore.BackendBusyException;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
index 69c7a25..0775a1a 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
@@ -17,7 +17,7 @@
 package android.security.keystore2;
 
 import android.annotation.NonNull;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.ArrayUtils;
 import android.security.keystore.KeyProperties;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
index 2b5f6c3..bc56f01 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
 import android.security.KeyStoreException;
 import android.security.KeyStoreOperation;
 import android.security.keymaster.KeymasterDefs;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
index 18d2692..a3b04ab 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -19,7 +19,7 @@
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
 import android.security.KeyStoreException;
 import android.security.KeyStoreOperation;
 import android.security.keymaster.KeymasterDefs;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
index 2250c89..d1ef1df 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
@@ -17,7 +17,7 @@
 package android.security.keystore2;
 
 import android.annotation.NonNull;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
 import android.security.KeyStoreException;
 import android.security.KeyStoreOperation;
 import android.security.keymaster.KeymasterDefs;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
index eea45c2..8475ad9 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
@@ -16,7 +16,7 @@
 
 package android.security.keystore2;
 
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
 import android.security.KeyStoreException;
 import android.security.KeyStoreOperation;
 import android.security.keymaster.KeymasterDefs;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
index 479fd8a..233f352 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
@@ -16,8 +16,8 @@
 
 package android.security.keystore2;
 
-import android.hardware.keymint.KeyParameter;
-import android.hardware.keymint.SecurityLevel;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.SecurityLevel;
 import android.security.KeyStore2;
 import android.security.KeyStoreSecurityLevel;
 import android.security.keymaster.KeymasterArguments;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 61725e3..df0e146 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -18,8 +18,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.hardware.keymint.KeyParameter;
-import android.hardware.keymint.SecurityLevel;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.SecurityLevel;
 import android.os.Build;
 import android.security.KeyPairGeneratorSpec;
 import android.security.KeyStore2;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
index 2686ddc..951f918 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeymasterUtils;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
index 444dad4..ab75591 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
@@ -17,7 +17,7 @@
 package android.security.keystore2;
 
 import android.annotation.NonNull;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.KeyProperties;
 
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
index a168f8f..9b4f01e 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
@@ -18,7 +18,7 @@
 
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
 import android.security.KeyStoreException;
 import android.security.KeyStoreOperation;
 import android.security.keymaster.KeymasterDefs;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 9790a4a..aca5314 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -18,9 +18,9 @@
 
 import android.annotation.NonNull;
 import android.hardware.biometrics.BiometricManager;
-import android.hardware.keymint.HardwareAuthenticatorType;
-import android.hardware.keymint.KeyParameter;
-import android.hardware.keymint.SecurityLevel;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.SecurityLevel;
 import android.security.GateKeeper;
 import android.security.KeyStore2;
 import android.security.KeyStoreParameter;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
index a2d4528..4d4b0d8 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.ArrayUtils;
 import android.security.keystore.KeyProperties;
diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
index 8fa532b..18c786a 100644
--- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
@@ -18,8 +18,8 @@
 
 import android.annotation.NonNull;
 import android.hardware.biometrics.BiometricManager;
-import android.hardware.keymint.KeyParameter;
-import android.hardware.keymint.SecurityLevel;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.SecurityLevel;
 import android.security.GateKeeper;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.KeyProperties;
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index aa34edf..6a7df94 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..a545b3d 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,208 @@
   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;
+base::expected<std::monostate, NullOrIOError> AssetManager2::ResolveReference(
+    AssetManager2::SelectedValue& value, bool cache_value) const {
+  if (value.type != Res_value::TYPE_REFERENCE || value.data == 0U) {
+    // Not a reference. Nothing to do.
+    return {};
+  }
 
-  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;
+  const uint32_t original_flags = value.flags;
+  const uint32_t original_resid = value.data;
+  if (cache_value) {
+    auto cached_value = cached_resolved_values_.find(value.data);
+    if (cached_value != cached_resolved_values_.end()) {
+      value = cached_value->second;
+      value.flags |= original_flags;
+      return {};
     }
   }
-  return cookie;
+
+  uint32_t combined_flags = 0U;
+  uint32_t resolve_resid = original_resid;
+  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()) {
+      value.resid = resolve_resid;
+      return base::unexpected(result.error());
+    }
+
+    // If resource resolution fails, the value should be set to the last reference that was able to
+    // be resolved successfully.
+    value = *result;
+    value.flags |= combined_flags;
+
+    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.
+      if (cache_value) {
+        cached_resolved_values_[original_resid] = value;
+      }
+
+      // Above value is cached without original_flags to ensure they don't get included in future
+      // queries that hit the cache
+      value.flags |= original_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 +1130,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 +1201,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 +1247,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 +1271,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 +1293,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 +1312,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 +1349,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});
           }
         }
       });
@@ -1309,6 +1375,8 @@
       ++iter;
     }
   }
+
+  cached_resolved_values_.clear();
 }
 
 uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const {
@@ -1354,16 +1422,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 +1441,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 +1496,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 +1527,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, true /* cache_value */);
+  if (resolve_result.has_value()) {
+    result->flags |= value.flags;
+    value = *result;
+  }
+  return resolve_result;
 }
 
 void Theme::Clear() {
@@ -1507,9 +1572,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 +1625,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 +1723,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 +1780,7 @@
       }
     }
   }
+  return {};
 }
 
 void Theme::Dump() const {
diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
index e62fb61..c188712 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);
@@ -36,8 +39,7 @@
     : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> {
  public:
   explicit XmlAttributeFinder(const ResXMLParser* parser)
-      : BackTrackingAttributeFinder(
-            0, parser != nullptr ? parser->getAttributeCount() : 0),
+      : BackTrackingAttributeFinder(0, parser != nullptr ? parser->getAttributeCount() : 0),
         parser_(parser) {}
 
   inline uint32_t GetAttribute(size_t index) const {
@@ -61,136 +63,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, true /* cache_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 +214,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 +264,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, true /* cache_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 +418,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..a92694c 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,109 @@
   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`. If `cache_value` is true, then
+  // the resolved value will be cached and used when attempting to resolve the resource id specified
+  // in `value`.
+  //
+  // 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,
+                                                                 bool cache_value = false) 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 +310,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 +330,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 +379,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 +393,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 +420,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,19 +443,20 @@
 
   // 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_;
+
+  // Cached set of resolved resource values.
+  mutable std::unordered_map<uint32_t, SelectedValue> cached_resolved_values_;
 
   // Whether or not to save resource resolution steps
   bool resource_resolution_logging_enabled_ = false;
 
   struct Resolution {
-
     struct Step {
-
       enum class Type {
         INITIAL,
         BETTER_MATCH,
@@ -455,55 +505,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..471b0ee 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,50 +421,107 @@
   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) {
   AssetManager2 assetmanager;
   assetmanager.SetApkAssets({basic_assets_.get()});
 
-  ResTable_config selected_config;
-  memset(&selected_config, 0, sizeof(selected_config));
+  // 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;
 
-  uint32_t flags = 0u;
+  auto result = assetmanager.ResolveReference(value);
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(1, value.cookie);
+  EXPECT_EQ(basic::R::string::test1, value.resid);
+}
 
-  // Create some kind of Res_value that is NOT a reference.
-  Res_value value;
-  value.dataType = Res_value::TYPE_STRING;
-  value.data = 0;
+TEST_F(AssetManager2Test, ResolveReferenceMissingResourceDoNotCacheFlags) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({basic_assets_.get()});
+  {
+    AssetManager2::SelectedValue value{};
+    value.data = basic::R::string::test1;
+    value.type = Res_value::TYPE_REFERENCE;
+    value.flags = ResTable_config::CONFIG_KEYBOARD;
 
-  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(Res_value::TYPE_STRING, value.type);
+    EXPECT_EQ(0, value.cookie);
+    EXPECT_EQ(basic::R::string::test1, value.resid);
+    EXPECT_EQ(ResTable_typeSpec::SPEC_PUBLIC | ResTable_config::CONFIG_KEYBOARD, value.flags);
+  }
+  {
+    AssetManager2::SelectedValue value{};
+    value.data = basic::R::string::test1;
+    value.type = Res_value::TYPE_REFERENCE;
+    value.flags = ResTable_config::CONFIG_COLOR_MODE;
+
+    auto result = assetmanager.ResolveReference(value);
+    ASSERT_TRUE(result.has_value());
+    EXPECT_EQ(Res_value::TYPE_STRING, value.type);
+    EXPECT_EQ(0, value.cookie);
+    EXPECT_EQ(basic::R::string::test1, value.resid);
+    EXPECT_EQ(ResTable_typeSpec::SPEC_PUBLIC | ResTable_config::CONFIG_COLOR_MODE, value.flags);
+  }
+}
+
+TEST_F(AssetManager2Test, ResolveReferenceMissingResource) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  const uint32_t kMissingResId = 0x8001ffff;
+  AssetManager2::SelectedValue value{};
+  value.type = Res_value::TYPE_REFERENCE;
+  value.data = kMissingResId;
+
+  auto result = assetmanager.ResolveReference(value);
+  ASSERT_FALSE(result.has_value());
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.type);
+  EXPECT_EQ(kMissingResId, value.data);
+  EXPECT_EQ(kMissingResId, value.resid);
+  EXPECT_EQ(-1, value.cookie);
+  EXPECT_EQ(0, value.flags);
+}
+
+TEST_F(AssetManager2Test, ResolveReferenceMissingResourceLib) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({libclient_assets_.get()});
+
+  AssetManager2::SelectedValue value{};
+  value.type = Res_value::TYPE_REFERENCE;
+  value.data = libclient::R::string::foo_one;
+
+  auto result = assetmanager.ResolveReference(value);
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(Res_value::TYPE_DYNAMIC_REFERENCE, value.type);
+  EXPECT_EQ(lib_one::R::string::foo, value.data);
+  EXPECT_EQ(libclient::R::string::foo_one, value.resid);
+  EXPECT_EQ(0, value.cookie);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value.flags);
 }
 
 static bool IsConfigurationPresent(const std::set<ResTable_config>& configurations,
@@ -516,43 +533,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 +597,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 +682,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 +711,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 +730,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 +749,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
diff --git a/location/java/android/location/OWNERS b/location/java/android/location/OWNERS
index 383321b..6032144 100644
--- a/location/java/android/location/OWNERS
+++ b/location/java/android/location/OWNERS
@@ -1,6 +1,6 @@
 # Bug component: 880425
 
-mstogaitis@google.com
+sooniln@google.com
 wyattriley@google.com
-etn@google.com
+yuhany@google.com
 weiwa@google.com
diff --git a/lowpan/java/android/net/lowpan/LowpanManager.java b/lowpan/java/android/net/lowpan/LowpanManager.java
index 76876ce..33b35e6 100644
--- a/lowpan/java/android/net/lowpan/LowpanManager.java
+++ b/lowpan/java/android/net/lowpan/LowpanManager.java
@@ -24,6 +24,10 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+
 import java.lang.ref.WeakReference;
 import java.util.HashMap;
 import java.util.Map;
@@ -97,10 +101,14 @@
      *
      * @param context the application context
      * @param service the Binder interface
-     * @param looper the default Looper to run callbacks on
      * @hide - hide this because it takes in a parameter of type ILowpanManager, which is a system
      *     private class.
      */
+    public LowpanManager(Context context, ILowpanManager service) {
+        this(context, service, BackgroundThread.get().getLooper());
+    }
+
+    @VisibleForTesting
     public LowpanManager(Context context, ILowpanManager service, Looper looper) {
         mContext = context;
         mService = service;
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index c2168f1..e7e83eb 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -25,6 +25,7 @@
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
+import android.os.Process;
 import android.os.SystemProperties;
 import android.util.Log;
 import android.util.Pair;
@@ -188,13 +189,14 @@
 
     // COMMON CONSTANTS
     private static final Range<Integer> POSITIVE_INTEGERS =
-        Range.create(1, Integer.MAX_VALUE);
+            Range.create(1, Integer.MAX_VALUE);
     private static final Range<Long> POSITIVE_LONGS =
-        Range.create(1l, Long.MAX_VALUE);
+            Range.create(1L, Long.MAX_VALUE);
     private static final Range<Rational> POSITIVE_RATIONALS =
-        Range.create(new Rational(1, Integer.MAX_VALUE),
-                     new Rational(Integer.MAX_VALUE, 1));
-    private static final Range<Integer> SIZE_RANGE = Range.create(1, 32768);
+            Range.create(new Rational(1, Integer.MAX_VALUE),
+                         new Rational(Integer.MAX_VALUE, 1));
+    private static final Range<Integer> SIZE_RANGE =
+            Process.is64Bit() ? Range.create(1, 32768) : Range.create(1, 4096);
     private static final Range<Integer> FRAME_RATE_RANGE = Range.create(0, 960);
     private static final Range<Integer> BITRATE_RANGE = Range.create(0, 500000000);
     private static final int DEFAULT_MAX_SUPPORTED_INSTANCES = 32;
@@ -1399,6 +1401,9 @@
 
         /**
          * Returns the range of supported video widths.
+         * <p class=note>
+         * 32-bit processes will not support resolutions larger than 4096x4096 due to
+         * the limited address space.
          */
         public Range<Integer> getSupportedWidths() {
             return mWidthRange;
@@ -1406,6 +1411,9 @@
 
         /**
          * Returns the range of supported video heights.
+         * <p class=note>
+         * 32-bit processes will not support resolutions larger than 4096x4096 due to
+         * the limited address space.
          */
         public Range<Integer> getSupportedHeights() {
             return mHeightRange;
@@ -1857,6 +1865,10 @@
                         && aligned.mMaxMacroBlockRate >= otherAligned.mMaxMacroBlockRate);
             }
 
+            /* package private */ boolean isEqualDimension(@NonNull PerformancePoint other) {
+                return mWidth == other.mWidth && mHeight == other.mHeight;
+            }
+
             private @NonNull Size getCommonBlockSize(@NonNull PerformancePoint other) {
                 return new Size(
                         Math.max(mBlockSize.getWidth(), other.mBlockSize.getWidth()) * 16,
@@ -1997,6 +2009,9 @@
          * Performance points assume a single active codec. For use cases where multiple
          * codecs are active, should use that highest pixel count, and add the frame rates of
          * each individual codec.
+         * <p class=note>
+         * Supported resolution could be further restricted for 32-bit processes due to
+         * the limited virtual memory space.
          */
         @Nullable
         public List<PerformancePoint> getSupportedPerformancePoints() {
@@ -2164,6 +2179,12 @@
                 if (size == null || size.getWidth() * size.getHeight() <= 0) {
                     continue;
                 }
+                if (size.getWidth() > SIZE_RANGE.getUpper()
+                        || size.getHeight() > SIZE_RANGE.getUpper()) {
+                    size = new Size(
+                            Math.min(size.getWidth(), SIZE_RANGE.getUpper()),
+                            Math.min(size.getHeight(), SIZE_RANGE.getUpper()));
+                }
                 Range<Long> range = Utils.parseLongRange(map.get(key), null);
                 if (range == null || range.getLower() < 0 || range.getUpper() < 0) {
                     continue;
@@ -2193,6 +2214,29 @@
                                (a.getMaxMacroBlockRate() < b.getMaxMacroBlockRate() ? -1 : 1) :
                        (a.getMaxFrameRate() != b.getMaxFrameRate()) ?
                                (a.getMaxFrameRate() < b.getMaxFrameRate() ? -1 : 1) : 0));
+
+            // remove redundant points
+            for (int i = 1; i < ret.size(); ++i) {
+                PerformancePoint a = ret.get(i);
+                for (int j = 0; j < i; ++j) {
+                    PerformancePoint b = ret.get(j);
+                    if (b.isEqualDimension(a) && b.covers(a)) {
+                        ret.set(i, null);
+                        break;
+                    }
+                }
+            }
+            int newSize = 0;
+            for (int i = 0; i < ret.size(); ++i) {
+                PerformancePoint a = ret.get(i);
+                if (a == null) {
+                    continue;
+                }
+                ret.set(newSize, a);
+                ++newSize;
+            }
+            ret.setSize(newSize);
+
             return Collections.unmodifiableList(ret);
         }
 
diff --git a/packages/Connectivity/OWNERS b/packages/Connectivity/OWNERS
new file mode 100644
index 0000000..48e54da
--- /dev/null
+++ b/packages/Connectivity/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include platform/frameworks/base:/services/core/java/com/android/server/net/OWNERS
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 51f69a9..2eba38e 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -180,7 +180,7 @@
     <!-- Default state of tap to wake -->
     <bool name="def_double_tap_to_wake">true</bool>
 
-    <!-- Default for Settings.Secure.NFC_PAYMENT_COMPONENT -->
+    <!-- Default for Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT -->
     <string name="def_nfc_payment_component"></string>
 
     <!-- Default setting for ability to add users from the lock screen -->
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 757bbda..5d6c4c3 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -154,6 +154,7 @@
     <uses-permission android:name="android.permission.MANAGE_CONTENT_CAPTURE" />
     <uses-permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS" />
     <uses-permission android:name="android.permission.MANAGE_APP_PREDICTIONS" />
+    <uses-permission android:name="android.permission.MANAGE_SEARCH_UI" />
     <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.SET_TIME" />
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 02815a57..63b9bb3 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -378,6 +378,9 @@
             }
         }
 
+        @Override
+        public void onEarlyReportFinished() {}
+
         /**
          * Reads bugreport id and links it to the bugreport info to track a bugreport that is in
          * process. id is incremented in the dumpstate code.
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 0b8b352..aab0553 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1265,7 +1265,7 @@
     @Override
     public boolean bindBluetoothProfileService(int bluetoothProfile,
             IBluetoothProfileServiceConnection proxy) {
-        if (!mEnable) {
+        if (mState != BluetoothAdapter.STATE_ON) {
             if (DBG) {
                 Slog.d(TAG, "Trying to bind to profile: " + bluetoothProfile
                         + ", while Bluetooth was disabled");
@@ -1431,7 +1431,7 @@
                 mBluetoothLock.readLock().unlock();
             }
 
-            if (!mEnable || state != BluetoothAdapter.STATE_ON) {
+            if (state != BluetoothAdapter.STATE_ON) {
                 if (DBG) {
                     Slog.d(TAG, "Unable to bindService while Bluetooth is disabled");
                 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d867442..2c765bd 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -113,7 +113,6 @@
 import android.net.NetworkMonitorManager;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkProvider;
-import android.net.NetworkQuotaInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
@@ -129,6 +128,7 @@
 import android.net.SocketKeepalive;
 import android.net.TetheringManager;
 import android.net.UidRange;
+import android.net.UidRangeParcel;
 import android.net.Uri;
 import android.net.VpnManager;
 import android.net.VpnService;
@@ -1780,14 +1780,6 @@
     }
 
     @Override
-    @Deprecated
-    public NetworkQuotaInfo getActiveNetworkQuotaInfo() {
-        Log.w(TAG, "Shame on UID " + Binder.getCallingUid()
-                + " for calling the hidden API getNetworkQuotaInfo(). Shame!");
-        return new NetworkQuotaInfo();
-    }
-
-    @Override
     public boolean isActiveNetworkMetered() {
         enforceAccessPermission();
 
@@ -2822,6 +2814,7 @@
                     break;
                 }
                 case NetworkAgent.EVENT_UNDERLYING_NETWORKS_CHANGED: {
+                    // TODO: prevent loops, e.g., if a network declares itself as underlying.
                     if (!nai.supportsUnderlyingNetworks()) {
                         Log.wtf(TAG, "Non-virtual networks cannot have underlying networks");
                         break;
@@ -3421,6 +3414,7 @@
             }
         }
         nai.clearLingerState();
+        propagateUnderlyingNetworkCapabilities(nai.network);
         if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
             mDefaultNetworkNai = null;
             updateDataActivityTracking(null /* newNetwork */, nai);
@@ -3428,9 +3422,6 @@
             ensureNetworkTransitionWakelock(nai.toShortString());
         }
         mLegacyTypeTracker.remove(nai, wasDefault);
-        if (!nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
-            propagateUnderlyingNetworkCapabilities();
-        }
         rematchAllNetworksAndRequests();
         mLingerMonitor.noteDisconnect(nai);
         if (nai.created) {
@@ -4819,17 +4810,35 @@
         }
     }
 
+    private Network[] underlyingNetworksOrDefault(Network[] underlyingNetworks) {
+        final Network defaultNetwork = getNetwork(getDefaultNetwork());
+        if (underlyingNetworks == null && defaultNetwork != null) {
+            // null underlying networks means to track the default.
+            underlyingNetworks = new Network[] { defaultNetwork };
+        }
+        return underlyingNetworks;
+    }
+
+    // Returns true iff |network| is an underlying network of |nai|.
+    private boolean hasUnderlyingNetwork(NetworkAgentInfo nai, Network network) {
+        // TODO: support more than one level of underlying networks, either via a fixed-depth search
+        // (e.g., 2 levels of underlying networks), or via loop detection, or....
+        if (!nai.supportsUnderlyingNetworks()) return false;
+        final Network[] underlying = underlyingNetworksOrDefault(nai.declaredUnderlyingNetworks);
+        return ArrayUtils.contains(underlying, network);
+    }
+
     /**
-     * Ask all networks with underlying networks to recompute and update their capabilities.
+     * Recompute the capabilities for any networks that had a specific network as underlying.
      *
      * When underlying networks change, such networks may have to update capabilities to reflect
      * things like the metered bit, their transports, and so on. The capabilities are calculated
      * immediately. This method runs on the ConnectivityService thread.
      */
-    private void propagateUnderlyingNetworkCapabilities() {
+    private void propagateUnderlyingNetworkCapabilities(Network updatedNetwork) {
         ensureRunningOnConnectivityServiceThread();
         for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
-            if (nai.supportsUnderlyingNetworks()) {
+            if (updatedNetwork == null || hasUnderlyingNetwork(nai, updatedNetwork)) {
                 updateCapabilitiesForNetwork(nai);
             }
         }
@@ -5152,7 +5161,7 @@
                 loge("Starting user already has a VPN");
                 return;
             }
-            userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId, mKeyStore);
+            userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId, mKeyStore);
             mVpns.put(userId, userVpn);
             if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
                 updateLockdownVpn();
@@ -6368,27 +6377,28 @@
      * This method should never alter the agent's NetworkCapabilities, only store data in |nai|.
      */
     private void processCapabilitiesFromAgent(NetworkAgentInfo nai, NetworkCapabilities nc) {
-        nai.declaredMetered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
+        // Note: resetting the owner UID before storing the agent capabilities in NAI means that if
+        // the agent attempts to change the owner UID, then nai.declaredCapabilities will not
+        // actually be the same as the capabilities sent by the agent. Still, it is safer to reset
+        // the owner UID here and behave as if the agent had never tried to change it.
         if (nai.networkCapabilities.getOwnerUid() != nc.getOwnerUid()) {
             Log.e(TAG, nai.toShortString() + ": ignoring attempt to change owner from "
                     + nai.networkCapabilities.getOwnerUid() + " to " + nc.getOwnerUid());
             nc.setOwnerUid(nai.networkCapabilities.getOwnerUid());
         }
+        nai.declaredCapabilities = new NetworkCapabilities(nc);
     }
 
-    /** Modifies |caps| based on the capabilities of the specified underlying networks. */
+    /** Modifies |newNc| based on the capabilities of |underlyingNetworks| and |agentCaps|. */
     @VisibleForTesting
     void applyUnderlyingCapabilities(@Nullable Network[] underlyingNetworks,
-            @NonNull NetworkCapabilities caps,  boolean declaredMetered) {
-        final Network defaultNetwork = getNetwork(getDefaultNetwork());
-        if (underlyingNetworks == null && defaultNetwork != null) {
-            // null underlying networks means to track the default.
-            underlyingNetworks = new Network[] { defaultNetwork };
-        }
-        int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
+            @NonNull NetworkCapabilities agentCaps, @NonNull NetworkCapabilities newNc) {
+        underlyingNetworks = underlyingNetworksOrDefault(underlyingNetworks);
+        int[] transportTypes = agentCaps.getTransportTypes();
         int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
         int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
-        boolean metered = declaredMetered; // metered if any underlying is metered, or agentMetered
+        // metered if any underlying is metered, or originally declared metered by the agent.
+        boolean metered = !agentCaps.hasCapability(NET_CAPABILITY_NOT_METERED);
         boolean roaming = false; // roaming if any underlying is roaming
         boolean congested = false; // congested if any underlying is congested
         boolean suspended = true; // suspended if all underlying are suspended
@@ -6434,13 +6444,13 @@
             suspended = false;
         }
 
-        caps.setTransportTypes(transportTypes);
-        caps.setLinkDownstreamBandwidthKbps(downKbps);
-        caps.setLinkUpstreamBandwidthKbps(upKbps);
-        caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
-        caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
-        caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
-        caps.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended);
+        newNc.setTransportTypes(transportTypes);
+        newNc.setLinkDownstreamBandwidthKbps(downKbps);
+        newNc.setLinkUpstreamBandwidthKbps(upKbps);
+        newNc.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
+        newNc.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
+        newNc.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
+        newNc.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended);
     }
 
     /**
@@ -6497,7 +6507,8 @@
         }
 
         if (nai.supportsUnderlyingNetworks()) {
-            applyUnderlyingCapabilities(nai.declaredUnderlyingNetworks, newNc, nai.declaredMetered);
+            applyUnderlyingCapabilities(nai.declaredUnderlyingNetworks, nai.declaredCapabilities,
+                    newNc);
         }
 
         return newNc;
@@ -6576,11 +6587,8 @@
             }
         }
 
-        if (!newNc.hasTransport(TRANSPORT_VPN)) {
-            // Tell VPNs about updated capabilities, since they may need to
-            // bubble those changes through.
-            propagateUnderlyingNetworkCapabilities();
-        }
+        // This network might have been underlying another network. Propagate its capabilities.
+        propagateUnderlyingNetworkCapabilities(nai.network);
 
         if (!newNc.equalsTransportTypes(prevNc)) {
             mDnsManager.updateTransportsForNetwork(
@@ -6622,6 +6630,16 @@
                 && (lp.hasIpv6DefaultRoute() || lp.hasIpv6UnreachableDefaultRoute());
     }
 
+    private static UidRangeParcel[] toUidRangeStableParcels(final @NonNull Set<UidRange> ranges) {
+        final UidRangeParcel[] stableRanges = new UidRangeParcel[ranges.size()];
+        int index = 0;
+        for (UidRange range : ranges) {
+            stableRanges[index] = new UidRangeParcel(range.start, range.stop);
+            index++;
+        }
+        return stableRanges;
+    }
+
     private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
             NetworkCapabilities newNc) {
         Set<UidRange> prevRanges = null == prevNc ? null : prevNc.getUids();
@@ -6641,14 +6659,11 @@
             // removing old range works because, unlike the filtering rules below, it's possible to
             // add duplicate UID routing rules.
             if (!newRanges.isEmpty()) {
-                final UidRange[] addedRangesArray = new UidRange[newRanges.size()];
-                newRanges.toArray(addedRangesArray);
-                mNMS.addVpnUidRanges(nai.network.getNetId(), addedRangesArray);
+                mNetd.networkAddUidRanges(nai.network.netId, toUidRangeStableParcels(newRanges));
             }
             if (!prevRanges.isEmpty()) {
-                final UidRange[] removedRangesArray = new UidRange[prevRanges.size()];
-                prevRanges.toArray(removedRangesArray);
-                mNMS.removeVpnUidRanges(nai.network.getNetId(), removedRangesArray);
+                mNetd.networkRemoveUidRanges(
+                        nai.network.netId, toUidRangeStableParcels(prevRanges));
             }
             final boolean wasFiltering = requiresVpnIsolation(nai, prevNc, nai.linkProperties);
             final boolean shouldFilter = requiresVpnIsolation(nai, newNc, nai.linkProperties);
@@ -6903,8 +6918,10 @@
         updateTcpBufferSizes(null != newNetwork
                 ? newNetwork.linkProperties.getTcpBufferSizes() : null);
         notifyIfacesChangedForNetworkStats();
-        // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
-        propagateUnderlyingNetworkCapabilities();
+        // Fix up the NetworkCapabilities of any networks that have this network as underlying.
+        if (newNetwork != null) {
+            propagateUnderlyingNetworkCapabilities(newNetwork.network);
+        }
     }
 
     private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
@@ -7360,13 +7377,11 @@
             networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
 
             if (!createNativeNetwork(networkAgent)) return;
-            if (networkAgent.isVPN()) {
-                // Initialize the VPN capabilities to their starting values according to the
-                // underlying networks. This will avoid a spurious callback to
-                // onCapabilitiesUpdated being sent in updateAllVpnCapabilities below as
-                // the VPN would switch from its default, blank capabilities to those
-                // that reflect the capabilities of its underlying networks.
-                propagateUnderlyingNetworkCapabilities();
+            if (networkAgent.supportsUnderlyingNetworks()) {
+                // Initialize the network's capabilities to their starting values according to the
+                // underlying networks. This ensures that the capabilities are correct before
+                // anything happens to the network.
+                updateCapabilitiesForNetwork(networkAgent);
             }
             networkAgent.created = true;
         }
@@ -7408,10 +7423,6 @@
             // doing.
             updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
 
-            if (networkAgent.supportsUnderlyingNetworks()) {
-                propagateUnderlyingNetworkCapabilities();
-            }
-
             // Consider network even though it is not yet validated.
             rematchAllNetworksAndRequests();
 
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 5e86f85..636da6f 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -60,7 +60,6 @@
 import android.net.NetworkStats;
 import android.net.RouteInfo;
 import android.net.TetherStatsParcel;
-import android.net.UidRange;
 import android.net.UidRangeParcel;
 import android.net.shared.NetdUtils;
 import android.net.shared.RouteUtils;
@@ -91,7 +90,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.Preconditions;
 
@@ -420,8 +418,6 @@
                     getBatteryStats().noteMobileRadioPowerState(powerState, tsNanos, uid);
                 } catch (RemoteException e) {
                 }
-                FrameworkStatsLog.write_non_chained(
-                        FrameworkStatsLog.MOBILE_RADIO_POWER_STATE_CHANGED, uid, null, powerState);
             }
         }
 
@@ -432,8 +428,6 @@
                     getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos, uid);
                 } catch (RemoteException e) {
                 }
-                FrameworkStatsLog.write_non_chained(
-                        FrameworkStatsLog.WIFI_RADIO_POWER_STATE_CHANGED, uid, null, powerState);
             }
         }
 
@@ -1393,38 +1387,6 @@
         }
     }
 
-    private static UidRangeParcel makeUidRangeParcel(int start, int stop) {
-        UidRangeParcel range = new UidRangeParcel();
-        range.start = start;
-        range.stop = stop;
-        return range;
-    }
-
-    private static UidRangeParcel[] toStableParcels(UidRange[] ranges) {
-        UidRangeParcel[] stableRanges = new UidRangeParcel[ranges.length];
-        for (int i = 0; i < ranges.length; i++) {
-            stableRanges[i] = makeUidRangeParcel(ranges[i].start, ranges[i].stop);
-        }
-        return stableRanges;
-    }
-
-    @Override
-    public void setAllowOnlyVpnForUids(boolean add, UidRange[] uidRanges)
-            throws ServiceSpecificException {
-        NetworkStack.checkNetworkStackPermission(mContext);
-        try {
-            mNetdService.networkRejectNonSecureVpn(add, toStableParcels(uidRanges));
-        } catch (ServiceSpecificException e) {
-            Log.w(TAG, "setAllowOnlyVpnForUids(" + add + ", " + Arrays.toString(uidRanges) + ")"
-                    + ": netd command failed", e);
-            throw e;
-        } catch (RemoteException e) {
-            Log.w(TAG, "setAllowOnlyVpnForUids(" + add + ", " + Arrays.toString(uidRanges) + ")"
-                    + ": netd command failed", e);
-            throw e.rethrowAsRuntimeException();
-        }
-    }
-
     private void applyUidCleartextNetworkPolicy(int uid, int policy) {
         final int policyValue;
         switch (policy) {
@@ -1553,27 +1515,6 @@
     }
 
     @Override
-    public void addVpnUidRanges(int netId, UidRange[] ranges) {
-        NetworkStack.checkNetworkStackPermission(mContext);
-
-        try {
-            mNetdService.networkAddUidRanges(netId, toStableParcels(ranges));
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
-    public void removeVpnUidRanges(int netId, UidRange[] ranges) {
-        NetworkStack.checkNetworkStackPermission(mContext);
-        try {
-            mNetdService.networkRemoveUidRanges(netId, toStableParcels(ranges));
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
     public void setFirewallEnabled(boolean enabled) {
         enforceSystemUid();
         try {
@@ -1616,7 +1557,7 @@
             ranges = new UidRangeParcel[] {
                 // TODO: is there a better way of finding all existing users? If so, we could
                 // specify their ranges here.
-                makeUidRangeParcel(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE),
+                new UidRangeParcel(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE),
             };
             // ... except for the UIDs that have allow rules.
             synchronized (mRulesLock) {
@@ -1647,7 +1588,7 @@
                 for (int i = 0; i < ranges.length; i++) {
                     if (rules.valueAt(i) == FIREWALL_RULE_DENY) {
                         int uid = rules.keyAt(i);
-                        ranges[numUids] = makeUidRangeParcel(uid, uid);
+                        ranges[numUids] = new UidRangeParcel(uid, uid);
                         numUids++;
                     }
                 }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index ff0596c..dd497a6 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -4612,14 +4612,7 @@
 
                     // Create package obb and data dir if it doesn't exist.
                     int appUid = UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid());
-                    File file = new File(packageObbDir);
-                    if (!file.exists()) {
-                        vold.setupAppDir(packageObbDir, appUid);
-                    }
-                    file = new File(packageDataDir);
-                    if (!file.exists()) {
-                        vold.setupAppDir(packageDataDir, appUid);
-                    }
+                    vold.ensureAppDirsCreated(new String[] {packageObbDir, packageDataDir}, appUid);
                 }
             } catch (ServiceManager.ServiceNotFoundException | RemoteException e) {
                 Slog.e(TAG, "Unable to create obb and data directories for " + processName,e);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 090ac54..1ade8e7 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -688,6 +688,8 @@
         if (update) {
             mWorker.scheduleSync("modem-data", BatteryExternalStatsWorker.UPDATE_RADIO);
         }
+        FrameworkStatsLog.write_non_chained(
+                FrameworkStatsLog.MOBILE_RADIO_POWER_STATE_CHANGED, uid, null, powerState);
     }
 
     public void notePhoneOn() {
@@ -869,6 +871,8 @@
             }
             mStats.noteWifiRadioPowerState(powerState, tsNanos, uid);
         }
+        FrameworkStatsLog.write_non_chained(
+                FrameworkStatsLog.WIFI_RADIO_POWER_STATE_CHANGED, uid, null, powerState);
     }
 
     public void noteWifiRunning(WorkSource ws) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index ccd1f3b..52b9f5c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -138,9 +138,10 @@
     // not guaranteed to be current or correct, or even to exist.
     public @Nullable Network[] declaredUnderlyingNetworks;
 
-    // Whether this network is always metered even if its underlying networks are unmetered.
-    // Only relevant if #supportsUnderlyingNetworks is true.
-    public boolean declaredMetered;
+    // The capabilities originally announced by the NetworkAgent, regardless of any capabilities
+    // that were added or removed due to this network's underlying networks.
+    // Only set if #supportsUnderlyingNetworks is true.
+    public @Nullable NetworkCapabilities declaredCapabilities;
 
     // Indicates if netd has been told to create this Network. From this point on the appropriate
     // routing rules are setup and routes are added so packets can begin flowing over the Network.
diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
index 1129899..b5f20d7 100644
--- a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
+++ b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
@@ -36,6 +36,7 @@
 import android.net.TcpKeepalivePacketData;
 import android.net.TcpKeepalivePacketDataParcelable;
 import android.net.TcpRepairWindow;
+import android.net.util.KeepalivePacketDataUtil;
 import android.os.Handler;
 import android.os.MessageQueue;
 import android.os.Messenger;
@@ -112,7 +113,7 @@
             throws InvalidPacketException, InvalidSocketException {
         try {
             final TcpKeepalivePacketDataParcelable tcpDetails = switchToRepairMode(fd);
-            return TcpKeepalivePacketData.tcpKeepalivePacket(tcpDetails);
+            return KeepalivePacketDataUtil.fromStableParcelable(tcpDetails);
         } catch (InvalidPacketException | InvalidSocketException e) {
             switchOutOfRepairMode(fd);
             throw e;
@@ -122,7 +123,7 @@
      * Switch the tcp socket to repair mode and query detail tcp information.
      *
      * @param fd the fd of socket on which to use keepalive offload.
-     * @return a {@link TcpKeepalivePacketData#TcpKeepalivePacketDataParcelable} object for current
+     * @return a {@link TcpKeepalivePacketDataParcelable} object for current
      * tcp/ip information.
      */
     private static TcpKeepalivePacketDataParcelable switchToRepairMode(FileDescriptor fd)
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 66bb4d7..cabfbc0 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -48,6 +48,7 @@
 import android.content.pm.UserInfo;
 import android.net.ConnectivityManager;
 import android.net.DnsResolver;
+import android.net.INetd;
 import android.net.INetworkManagementEventObserver;
 import android.net.Ikev2VpnProfile;
 import android.net.IpPrefix;
@@ -68,6 +69,7 @@
 import android.net.NetworkRequest;
 import android.net.RouteInfo;
 import android.net.UidRange;
+import android.net.UidRangeParcel;
 import android.net.VpnManager;
 import android.net.VpnService;
 import android.net.ipsec.ike.ChildSessionCallback;
@@ -188,7 +190,8 @@
 
     private PendingIntent mStatusIntent;
     private volatile boolean mEnableTeardown = true;
-    private final INetworkManagementService mNetd;
+    private final INetworkManagementService mNms;
+    private final INetd mNetd;
     @VisibleForTesting
     protected VpnConfig mConfig;
     private final NetworkProvider mNetworkProvider;
@@ -234,7 +237,7 @@
      * @see mLockdown
      */
     @GuardedBy("this")
-    private final Set<UidRange> mBlockedUidsAsToldToNetd = new ArraySet<>();
+    private final Set<UidRangeParcel> mBlockedUidsAsToldToNetd = new ArraySet<>();
 
     // The user id of initiating VPN.
     private final int mUserId;
@@ -363,22 +366,23 @@
         }
     }
 
-    public Vpn(Looper looper, Context context, INetworkManagementService netService,
+    public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
             @UserIdInt int userId, @NonNull KeyStore keyStore) {
-        this(looper, context, new Dependencies(), netService, userId, keyStore,
+        this(looper, context, new Dependencies(), netService, netd, userId, keyStore,
                 new SystemServices(context), new Ikev2SessionCreator());
     }
 
     @VisibleForTesting
     protected Vpn(Looper looper, Context context, Dependencies deps,
-            INetworkManagementService netService,
+            INetworkManagementService netService, INetd netd,
             int userId, @NonNull KeyStore keyStore, SystemServices systemServices,
             Ikev2SessionCreator ikev2SessionCreator) {
         mContext = context;
         mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
         mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
         mDeps = deps;
-        mNetd = netService;
+        mNms = netService;
+        mNetd = netd;
         mUserId = userId;
         mLooper = looper;
         mSystemServices = systemServices;
@@ -912,7 +916,7 @@
             }
 
             try {
-                mNetd.denyProtect(mOwnerUID);
+                mNms.denyProtect(mOwnerUID);
             } catch (Exception e) {
                 Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
             }
@@ -922,7 +926,7 @@
             mOwnerUID = getAppUid(newPackage, mUserId);
             mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(newPackage);
             try {
-                mNetd.allowProtect(mOwnerUID);
+                mNms.allowProtect(mOwnerUID);
             } catch (Exception e) {
                 Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
             }
@@ -1579,24 +1583,25 @@
             exemptedPackages = new ArrayList<>(mLockdownAllowlist);
             exemptedPackages.add(mPackage);
         }
-        final Set<UidRange> rangesToTellNetdToRemove = new ArraySet<>(mBlockedUidsAsToldToNetd);
+        final Set<UidRangeParcel> rangesToTellNetdToRemove =
+                new ArraySet<>(mBlockedUidsAsToldToNetd);
 
-        final Set<UidRange> rangesToTellNetdToAdd;
+        final Set<UidRangeParcel> rangesToTellNetdToAdd;
         if (enforce) {
-            final Set<UidRange> rangesThatShouldBeBlocked =
+            final Set<UidRange> restrictedProfilesRanges =
                     createUserAndRestrictedProfilesRanges(mUserId,
-                            /* allowedApplications */ null,
-                            /* disallowedApplications */ exemptedPackages);
+                    /* allowedApplications */ null,
+                    /* disallowedApplications */ exemptedPackages);
+            final Set<UidRangeParcel> rangesThatShouldBeBlocked = new ArraySet<>();
 
             // The UID range of the first user (0-99999) would block the IPSec traffic, which comes
             // directly from the kernel and is marked as uid=0. So we adjust the range to allow
             // it through (b/69873852).
-            for (UidRange range : rangesThatShouldBeBlocked) {
-                if (range.start == 0) {
-                    rangesThatShouldBeBlocked.remove(range);
-                    if (range.stop != 0) {
-                        rangesThatShouldBeBlocked.add(new UidRange(1, range.stop));
-                    }
+            for (UidRange range : restrictedProfilesRanges) {
+                if (range.start == 0 && range.stop != 0) {
+                    rangesThatShouldBeBlocked.add(new UidRangeParcel(1, range.stop));
+                } else if (range.start != 0) {
+                    rangesThatShouldBeBlocked.add(new UidRangeParcel(range.start, range.stop));
                 }
             }
 
@@ -1628,13 +1633,13 @@
      *         including added ranges that already existed or removed ones that didn't.
      */
     @GuardedBy("this")
-    private boolean setAllowOnlyVpnForUids(boolean enforce, Collection<UidRange> ranges) {
+    private boolean setAllowOnlyVpnForUids(boolean enforce, Collection<UidRangeParcel> ranges) {
         if (ranges.size() == 0) {
             return true;
         }
-        final UidRange[] rangesArray = ranges.toArray(new UidRange[ranges.size()]);
+        final UidRangeParcel[] stableRanges = ranges.toArray(new UidRangeParcel[ranges.size()]);
         try {
-            mNetd.setAllowOnlyVpnForUids(enforce, rangesArray);
+            mNetd.networkRejectNonSecureVpn(enforce, stableRanges);
         } catch (RemoteException | RuntimeException e) {
             Log.e(TAG, "Updating blocked=" + enforce
                     + " for UIDs " + Arrays.toString(ranges.toArray()) + " failed", e);
@@ -1849,10 +1854,20 @@
         if (mNetworkInfo.isConnected()) {
             return !appliesToUid(uid);
         } else {
-            return UidRange.containsUid(mBlockedUidsAsToldToNetd, uid);
+            return containsUid(mBlockedUidsAsToldToNetd, uid);
         }
     }
 
+    private boolean containsUid(Collection<UidRangeParcel> ranges, int uid) {
+        if (ranges == null) return false;
+        for (UidRangeParcel range : ranges) {
+            if (range.start <= uid && uid <= range.stop) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void updateAlwaysOnNotification(DetailedState networkState) {
         final boolean visible = (mAlwaysOn && networkState != DetailedState.CONNECTED);
 
@@ -2495,7 +2510,7 @@
                                     address /* unused */,
                                     address /* unused */,
                                     network);
-                    mNetd.setInterfaceUp(mTunnelIface.getInterfaceName());
+                    mNms.setInterfaceUp(mTunnelIface.getInterfaceName());
 
                     mSession = mIkev2SessionCreator.createIkeSession(
                             mContext,
diff --git a/services/core/java/com/android/server/location/OWNERS b/services/core/java/com/android/server/location/OWNERS
index c2c95e6..696a0c2 100644
--- a/services/core/java/com/android/server/location/OWNERS
+++ b/services/core/java/com/android/server/location/OWNERS
@@ -1,8 +1 @@
-aadmal@google.com
-arthuri@google.com
-bduddie@google.com
-gomo@google.com
-sooniln@google.com
-weiwa@google.com
-wyattriley@google.com
-yuhany@google.com
+file:/location/java/android/location/OWNERS
diff --git a/services/core/java/com/android/server/location/contexthub/OWNERS b/services/core/java/com/android/server/location/contexthub/OWNERS
new file mode 100644
index 0000000..d4393d6
--- /dev/null
+++ b/services/core/java/com/android/server/location/contexthub/OWNERS
@@ -0,0 +1,2 @@
+arthuri@google.com
+bduddie@google.com
diff --git a/services/core/java/com/android/server/locksettings/OWNERS b/services/core/java/com/android/server/locksettings/OWNERS
index dad6e39..08b8a8c 100644
--- a/services/core/java/com/android/server/locksettings/OWNERS
+++ b/services/core/java/com/android/server/locksettings/OWNERS
@@ -1,2 +1,3 @@
 jaggies@google.com
 kchyn@google.com
+rubinxu@google.com
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 5787f7c4..8d5f553 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -15,20 +15,15 @@
  */
 
 package com.android.server.locksettings;
-
 import static android.os.UserHandle.USER_SYSTEM;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.Context;
 import android.content.pm.UserInfo;
-import android.hardware.rebootescrow.IRebootEscrow;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.os.UserManager;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.util.Slog;
 
@@ -44,7 +39,6 @@
 import java.util.Date;
 import java.util.List;
 import java.util.Locale;
-import java.util.NoSuchElementException;
 
 class RebootEscrowManager {
     private static final String TAG = "RebootEscrowManager";
@@ -116,8 +110,24 @@
     static class Injector {
         protected Context mContext;
 
+        private final RebootEscrowProviderInterface mRebootEscrowProvider;
+
         Injector(Context context) {
             mContext = context;
+            RebootEscrowProviderInterface rebootEscrowProvider = null;
+            // TODO(xunchang) add implementation for server based ror.
+            if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
+                    "server_based_ror_enabled", false)) {
+                Slog.e(TAG, "Server based ror isn't implemented yet.");
+            } else {
+                rebootEscrowProvider = new RebootEscrowProviderHalImpl();
+            }
+
+            if (rebootEscrowProvider != null && rebootEscrowProvider.hasRebootEscrowSupport()) {
+                mRebootEscrowProvider = rebootEscrowProvider;
+            } else {
+                mRebootEscrowProvider = null;
+            }
         }
 
         public Context getContext() {
@@ -128,15 +138,8 @@
             return (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         }
 
-        @Nullable
-        public IRebootEscrow getRebootEscrow() {
-            try {
-                return IRebootEscrow.Stub.asInterface(ServiceManager.getService(
-                        "android.hardware.rebootescrow.IRebootEscrow/default"));
-            } catch (NoSuchElementException e) {
-                Slog.i(TAG, "Device doesn't implement RebootEscrow HAL");
-            }
-            return null;
+        public RebootEscrowProviderInterface getRebootEscrowProvider() {
+            return mRebootEscrowProvider;
         }
 
         public int getBootCount() {
@@ -210,45 +213,18 @@
     }
 
     private RebootEscrowKey getAndClearRebootEscrowKey() {
-        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
-        if (rebootEscrow == null) {
-            Slog.w(TAG, "Had reboot escrow data for users, but RebootEscrow HAL is unavailable");
+        RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
+        if (rebootEscrowProvider == null) {
+            Slog.w(TAG,
+                    "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
             return null;
         }
 
-        try {
-            byte[] escrowKeyBytes = rebootEscrow.retrieveKey();
-            if (escrowKeyBytes == null) {
-                Slog.w(TAG, "Had reboot escrow data for users, but could not retrieve key");
-                return null;
-            } else if (escrowKeyBytes.length != 32) {
-                Slog.e(TAG, "IRebootEscrow returned key of incorrect size "
-                        + escrowKeyBytes.length);
-                return null;
-            }
-
-            // Make sure we didn't get the null key.
-            int zero = 0;
-            for (int i = 0; i < escrowKeyBytes.length; i++) {
-                zero |= escrowKeyBytes[i];
-            }
-            if (zero == 0) {
-                Slog.w(TAG, "IRebootEscrow returned an all-zeroes key");
-                return null;
-            }
-
-            // Overwrite the existing key with the null key
-            rebootEscrow.storeKey(new byte[32]);
-
+        RebootEscrowKey key = rebootEscrowProvider.getAndClearRebootEscrowKey(null);
+        if (key != null) {
             mEventLog.addEntry(RebootEscrowEvent.RETRIEVED_STORED_KEK);
-            return RebootEscrowKey.fromKeyBytes(escrowKeyBytes);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Could not retrieve escrow data");
-            return null;
-        } catch (ServiceSpecificException e) {
-            Slog.w(TAG, "Got service-specific exception: " + e.errorCode);
-            return null;
         }
+        return key;
     }
 
     private boolean restoreRebootEscrowForUser(@UserIdInt int userId, RebootEscrowKey key) {
@@ -279,9 +255,9 @@
             return;
         }
 
-        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
-        if (rebootEscrow == null) {
-            Slog.w(TAG, "Reboot escrow requested, but RebootEscrow HAL is unavailable");
+        if (mInjector.getRebootEscrowProvider() == null) {
+            Slog.w(TAG,
+                    "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
             return;
         }
 
@@ -293,6 +269,7 @@
 
         final RebootEscrowData escrowData;
         try {
+            // TODO(xunchang) further wrap the escrowData with a key from keystore.
             escrowData = RebootEscrowData.fromSyntheticPassword(escrowKey, spVersion,
                     syntheticPassword);
         } catch (IOException e) {
@@ -330,18 +307,16 @@
         mRebootEscrowWanted = false;
         setRebootEscrowReady(false);
 
-        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
-        if (rebootEscrow == null) {
+
+        RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
+        if (rebootEscrowProvider == null) {
+            Slog.w(TAG,
+                    "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
             return;
         }
 
         mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM);
-
-        try {
-            rebootEscrow.storeKey(new byte[32]);
-        } catch (RemoteException | ServiceSpecificException e) {
-            Slog.w(TAG, "Could not call RebootEscrow HAL to shred key");
-        }
+        rebootEscrowProvider.clearRebootEscrowKey();
 
         List<UserInfo> users = mUserManager.getUsers();
         for (UserInfo user : users) {
@@ -356,9 +331,10 @@
             return false;
         }
 
-        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
-        if (rebootEscrow == null) {
-            Slog.w(TAG, "Escrow marked as ready, but RebootEscrow HAL is unavailable");
+        RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
+        if (rebootEscrowProvider == null) {
+            Slog.w(TAG,
+                    "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
             return false;
         }
 
@@ -372,15 +348,7 @@
             return false;
         }
 
-        boolean armedRebootEscrow = false;
-        try {
-            rebootEscrow.storeKey(escrowKey.getKeyBytes());
-            armedRebootEscrow = true;
-            Slog.i(TAG, "Reboot escrow key stored with RebootEscrow HAL");
-        } catch (RemoteException | ServiceSpecificException e) {
-            Slog.e(TAG, "Failed escrow secret to RebootEscrow HAL", e);
-        }
-
+        boolean armedRebootEscrow = rebootEscrowProvider.storeRebootEscrowKey(escrowKey, null);
         if (armedRebootEscrow) {
             mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM);
             mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS);
@@ -397,7 +365,7 @@
     }
 
     boolean prepareRebootEscrow() {
-        if (mInjector.getRebootEscrow() == null) {
+        if (mInjector.getRebootEscrowProvider() == null) {
             return false;
         }
 
@@ -408,7 +376,7 @@
     }
 
     boolean clearRebootEscrow() {
-        if (mInjector.getRebootEscrow() == null) {
+        if (mInjector.getRebootEscrowProvider() == null) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java
new file mode 100644
index 0000000..6c1040b
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+package com.android.server.locksettings;
+
+import android.annotation.Nullable;
+import android.hardware.rebootescrow.IRebootEscrow;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.NoSuchElementException;
+
+import javax.crypto.SecretKey;
+
+/**
+ * An implementation of the {@link RebootEscrowProviderInterface} by calling the RebootEscrow HAL.
+ */
+class RebootEscrowProviderHalImpl implements RebootEscrowProviderInterface {
+    private static final String TAG = "RebootEscrowProvider";
+
+    private final Injector mInjector;
+
+    static class Injector {
+        @Nullable
+        public IRebootEscrow getRebootEscrow() {
+            try {
+                return IRebootEscrow.Stub.asInterface(ServiceManager.getService(
+                        "android.hardware.rebootescrow.IRebootEscrow/default"));
+            } catch (NoSuchElementException e) {
+                Slog.i(TAG, "Device doesn't implement RebootEscrow HAL");
+            }
+            return null;
+        }
+    }
+
+    RebootEscrowProviderHalImpl() {
+        mInjector = new Injector();
+    }
+
+    @VisibleForTesting
+    RebootEscrowProviderHalImpl(Injector injector) {
+        mInjector = injector;
+    }
+
+    @Override
+    public boolean hasRebootEscrowSupport() {
+        return mInjector.getRebootEscrow() != null;
+    }
+
+    @Override
+    public RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) {
+        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
+        if (rebootEscrow == null) {
+            Slog.w(TAG, "Had reboot escrow data for users, but RebootEscrow HAL is unavailable");
+            return null;
+        }
+
+        try {
+            byte[] escrowKeyBytes = rebootEscrow.retrieveKey();
+            if (escrowKeyBytes == null) {
+                Slog.w(TAG, "Had reboot escrow data for users, but could not retrieve key");
+                return null;
+            } else if (escrowKeyBytes.length != 32) {
+                Slog.e(TAG, "IRebootEscrow returned key of incorrect size "
+                        + escrowKeyBytes.length);
+                return null;
+            }
+
+            // Make sure we didn't get the null key.
+            int zero = 0;
+            for (int i = 0; i < escrowKeyBytes.length; i++) {
+                zero |= escrowKeyBytes[i];
+            }
+            if (zero == 0) {
+                Slog.w(TAG, "IRebootEscrow returned an all-zeroes key");
+                return null;
+            }
+
+            // Overwrite the existing key with the null key
+            rebootEscrow.storeKey(new byte[32]);
+
+            return RebootEscrowKey.fromKeyBytes(escrowKeyBytes);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Could not retrieve escrow data");
+            return null;
+        } catch (ServiceSpecificException e) {
+            Slog.w(TAG, "Got service-specific exception: " + e.errorCode);
+            return null;
+        }
+    }
+
+    @Override
+    public void clearRebootEscrowKey() {
+        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
+        if (rebootEscrow == null) {
+            return;
+        }
+
+        try {
+            rebootEscrow.storeKey(new byte[32]);
+        } catch (RemoteException | ServiceSpecificException e) {
+            Slog.w(TAG, "Could not call RebootEscrow HAL to shred key");
+        }
+
+    }
+
+    @Override
+    public boolean storeRebootEscrowKey(RebootEscrowKey escrowKey, SecretKey encryptionKey) {
+        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
+        if (rebootEscrow == null) {
+            Slog.w(TAG, "Escrow marked as ready, but RebootEscrow HAL is unavailable");
+            return false;
+        }
+
+        try {
+            // The HAL interface only accept 32 bytes data. And the encrypted bytes for the escrow
+            // key may exceed that limit. So we just store the raw key bytes here.
+            rebootEscrow.storeKey(escrowKey.getKeyBytes());
+            Slog.i(TAG, "Reboot escrow key stored with RebootEscrow HAL");
+            return true;
+        } catch (RemoteException | ServiceSpecificException e) {
+            Slog.e(TAG, "Failed escrow secret to RebootEscrow HAL", e);
+        }
+        return false;
+    }
+}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java
new file mode 100644
index 0000000..857ad5f
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package com.android.server.locksettings;
+
+import javax.crypto.SecretKey;
+
+/**
+ * Provides APIs for {@link RebootEscrowManager} to access and manage the reboot escrow key.
+ * Implementations need to find a way to persist the key across a reboot, and securely discards the
+ * persisted copy.
+ *
+ * @hide
+ */
+public interface RebootEscrowProviderInterface {
+    /**
+     * Returns true if the secure store/discard of reboot escrow key is supported.
+     */
+    boolean hasRebootEscrowSupport();
+
+    /**
+     * Returns the stored RebootEscrowKey, and clears the storage. If the stored key is encrypted,
+     * use the input key to decrypt the RebootEscrowKey. Returns null on failure.
+     */
+    RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey);
+
+    /**
+     * Clears the stored RebootEscrowKey.
+     */
+    void clearRebootEscrowKey();
+
+    /**
+     * Saves the given RebootEscrowKey, optionally encrypt the storage with the encryptionKey.
+     */
+    boolean storeRebootEscrowKey(RebootEscrowKey escrowKey, SecretKey encryptionKey);
+}
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 0b3cdae..7afa81a 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -165,11 +165,13 @@
 
     private void buildBluetoothRoutes() {
         mBluetoothRoutes.clear();
-        for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) {
-            if (device.isConnected()) {
-                BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
-                if (newBtRoute.connectedProfiles.size() > 0) {
-                    mBluetoothRoutes.put(device.getAddress(), newBtRoute);
+        if (mBluetoothAdapter.getBondedDevices() != null) {
+            for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) {
+                if (device.isConnected()) {
+                    BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
+                    if (newBtRoute.connectedProfiles.size() > 0) {
+                        mBluetoothRoutes.put(device.getAddress(), newBtRoute);
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 47fcc08..dd507a3 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -297,9 +297,8 @@
         }
 
         @Override
-        public void onUiIntensiveBugreportDumpsFinished(String callingPackage)
-                throws RemoteException {
-            mListener.onUiIntensiveBugreportDumpsFinished(callingPackage);
+        public void onUiIntensiveBugreportDumpsFinished() throws RemoteException {
+            mListener.onUiIntensiveBugreportDumpsFinished();
         }
 
         @Override
diff --git a/services/core/java/com/android/server/policy/OWNERS b/services/core/java/com/android/server/policy/OWNERS
index 0862c05..d25ec4a 100644
--- a/services/core/java/com/android/server/policy/OWNERS
+++ b/services/core/java/com/android/server/policy/OWNERS
@@ -1 +1,2 @@
 include /services/core/java/com/android/server/wm/OWNERS
+include /services/core/java/com/android/server/input/OWNERS
diff --git a/services/core/java/com/android/server/recoverysystem/OWNERS b/services/core/java/com/android/server/recoverysystem/OWNERS
new file mode 100644
index 0000000..79ded0d
--- /dev/null
+++ b/services/core/java/com/android/server/recoverysystem/OWNERS
@@ -0,0 +1,2 @@
+rvrolyk@google.com
+zhaojiac@google.com
diff --git a/services/core/java/com/android/server/timedetector/OWNERS b/services/core/java/com/android/server/timedetector/OWNERS
index 09447a9..8f80897 100644
--- a/services/core/java/com/android/server/timedetector/OWNERS
+++ b/services/core/java/com/android/server/timedetector/OWNERS
@@ -1 +1,3 @@
-include /core/java/android/app/timezone/OWNERS
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 6dac6b1..84776ea 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.timedetector.GnssTimeSuggestion;
 import android.app.timedetector.ITimeDetectorService;
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.NetworkTimeSuggestion;
@@ -122,6 +123,14 @@
         mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal));
     }
 
+    @Override
+    public void suggestGnssTime(@NonNull GnssTimeSuggestion timeSignal) {
+        enforceSuggestGnssTimePermission();
+        Objects.requireNonNull(timeSignal);
+
+        mHandler.post(() -> mTimeDetectorStrategy.suggestGnssTime(timeSignal));
+    }
+
     /** Internal method for handling the auto time setting being changed. */
     @VisibleForTesting
     public void handleAutoTimeDetectionChanged() {
@@ -153,4 +162,10 @@
                 android.Manifest.permission.SET_TIME,
                 "set time");
     }
+
+    private void enforceSuggestGnssTimePermission() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.SET_TIME,
+                "suggest gnss time");
+    }
 }
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index abd4c8c..de6412c 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.timedetector.GnssTimeSuggestion;
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.NetworkTimeSuggestion;
 import android.app.timedetector.TelephonyTimeSuggestion;
@@ -40,7 +41,7 @@
  */
 public interface TimeDetectorStrategy {
 
-    @IntDef({ ORIGIN_TELEPHONY, ORIGIN_MANUAL, ORIGIN_NETWORK })
+    @IntDef({ ORIGIN_TELEPHONY, ORIGIN_MANUAL, ORIGIN_NETWORK, ORIGIN_GNSS })
     @Retention(RetentionPolicy.SOURCE)
     @interface Origin {}
 
@@ -56,6 +57,10 @@
     @Origin
     int ORIGIN_NETWORK = 3;
 
+    /** Used when a time value originated from a gnss signal. */
+    @Origin
+    int ORIGIN_GNSS = 4;
+
     /** Processes the suggested time from telephony sources. */
     void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSuggestion);
 
@@ -70,6 +75,9 @@
     /** Processes the suggested time from network sources. */
     void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion);
 
+    /** Processes the suggested time from gnss sources. */
+    void suggestGnssTime(@NonNull GnssTimeSuggestion timeSuggestion);
+
     /**
      * Handles the auto-time configuration changing For example, when the auto-time setting is
      * toggled on or off.
@@ -102,6 +110,8 @@
                 return "network";
             case ORIGIN_TELEPHONY:
                 return "telephony";
+            case ORIGIN_GNSS:
+                return "gnss";
             default:
                 throw new IllegalArgumentException("origin=" + origin);
         }
@@ -119,6 +129,8 @@
                 return ORIGIN_NETWORK;
             case "telephony":
                 return ORIGIN_TELEPHONY;
+            case "gnss":
+                return ORIGIN_GNSS;
             default:
                 throw new IllegalArgumentException("originString=" + originString);
         }
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index b3c8c90..6fe3782 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -23,6 +23,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AlarmManager;
+import android.app.timedetector.GnssTimeSuggestion;
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.NetworkTimeSuggestion;
 import android.app.timedetector.TelephonyTimeSuggestion;
@@ -109,6 +110,10 @@
     private final ReferenceWithHistory<NetworkTimeSuggestion> mLastNetworkSuggestion =
             new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
 
+    @GuardedBy("this")
+    private final ReferenceWithHistory<GnssTimeSuggestion> mLastGnssSuggestion =
+            new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
+
     /**
      * The interface used by the strategy to interact with the surrounding service.
      *
@@ -166,6 +171,20 @@
     }
 
     @Override
+    public synchronized void suggestGnssTime(@NonNull GnssTimeSuggestion timeSuggestion) {
+        final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime();
+
+        if (!validateAutoSuggestionTime(newUtcTime, timeSuggestion)) {
+            return;
+        }
+
+        mLastGnssSuggestion.set(timeSuggestion);
+
+        String reason = "GNSS time suggestion received: suggestion=" + timeSuggestion;
+        doAutoTimeDetection(reason);
+    }
+
+    @Override
     public synchronized boolean suggestManualTime(@NonNull ManualTimeSuggestion suggestion) {
         final TimestampedValue<Long> newUtcTime = suggestion.getUtcTime();
 
@@ -280,6 +299,11 @@
         mLastNetworkSuggestion.dump(ipw);
         ipw.decreaseIndent(); // level 2
 
+        ipw.println("Gnss suggestion history:");
+        ipw.increaseIndent(); // level 2
+        mLastGnssSuggestion.dump(ipw);
+        ipw.decreaseIndent(); // level 2
+
         ipw.decreaseIndent(); // level 1
         ipw.flush();
     }
@@ -386,6 +410,14 @@
                             + ", networkSuggestion=" + networkSuggestion
                             + ", detectionReason=" + detectionReason;
                 }
+            } else if (origin == ORIGIN_GNSS) {
+                GnssTimeSuggestion gnssTimeSuggestion = findLatestValidGnssSuggestion();
+                if (gnssTimeSuggestion != null) {
+                    newUtcTime = gnssTimeSuggestion.getUtcTime();
+                    cause = "Found good gnss suggestion."
+                            + ", gnssTimeSuggestion=" + gnssTimeSuggestion
+                            + ", detectionReason=" + detectionReason;
+                }
             } else {
                 Slog.w(LOG_TAG, "Unknown or unsupported origin=" + origin
                         + " in " + Arrays.toString(originPriorities)
@@ -527,6 +559,26 @@
         return networkSuggestion;
     }
 
+    /** Returns the latest, valid, gnss suggestion. Returns {@code null} if there isn't one. */
+    @GuardedBy("this")
+    @Nullable
+    private GnssTimeSuggestion findLatestValidGnssSuggestion() {
+        GnssTimeSuggestion gnssTimeSuggestion = mLastGnssSuggestion.get();
+        if (gnssTimeSuggestion == null) {
+            // No gnss suggestions received. This is normal if there's no gnss signal.
+            return null;
+        }
+
+        TimestampedValue<Long> utcTime = gnssTimeSuggestion.getUtcTime();
+        long elapsedRealTimeMillis = mCallback.elapsedRealtimeMillis();
+        if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) {
+            // The latest suggestion is not valid, usually due to its age.
+            return null;
+        }
+
+        return gnssTimeSuggestion;
+    }
+
     @GuardedBy("this")
     private boolean setSystemClockIfRequired(
             @Origin int origin, @NonNull TimestampedValue<Long> time, @NonNull String cause) {
@@ -655,6 +707,16 @@
     }
 
     /**
+     * Returns the latest valid gnss suggestion. Not intended for general use: it is used during
+     * tests to check strategy behavior.
+     */
+    @VisibleForTesting
+    @Nullable
+    public synchronized GnssTimeSuggestion findLatestValidGnssSuggestionForTests() {
+        return findLatestValidGnssSuggestion();
+    }
+
+    /**
      * A method used to inspect state during tests. Not intended for general use.
      */
     @VisibleForTesting
@@ -672,6 +734,15 @@
         return mLastNetworkSuggestion.get();
     }
 
+    /**
+     * A method used to inspect state during tests. Not intended for general use.
+     */
+    @VisibleForTesting
+    @Nullable
+    public synchronized GnssTimeSuggestion getLatestGnssSuggestion() {
+        return mLastGnssSuggestion.get();
+    }
+
     private static boolean validateSuggestionUtcTime(
             long elapsedRealtimeMillis, TimestampedValue<Long> utcTime) {
         long referenceTimeMillis = utcTime.getReferenceTimeMillis();
diff --git a/services/core/java/com/android/server/timezone/OWNERS b/services/core/java/com/android/server/timezone/OWNERS
index 09447a9..8f80897 100644
--- a/services/core/java/com/android/server/timezone/OWNERS
+++ b/services/core/java/com/android/server/timezone/OWNERS
@@ -1 +1,3 @@
-include /core/java/android/app/timezone/OWNERS
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/services/core/java/com/android/server/timezonedetector/OWNERS b/services/core/java/com/android/server/timezonedetector/OWNERS
index 09447a9..8f80897 100644
--- a/services/core/java/com/android/server/timezonedetector/OWNERS
+++ b/services/core/java/com/android/server/timezonedetector/OWNERS
@@ -1 +1,3 @@
-include /core/java/android/app/timezone/OWNERS
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 29bf374..ae9bd41 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -37,7 +37,6 @@
         "java/android/net/util/NetworkConstants.java",
         "java/android/net/IpMemoryStore.java",
         "java/android/net/NetworkMonitorManager.java",
-        "java/android/net/TcpKeepalivePacketData.java",
     ],
     sdk_version: "module_current",
     min_sdk_version: "30",
diff --git a/services/net/java/android/net/TcpKeepalivePacketData.java b/services/net/java/android/net/TcpKeepalivePacketData.java
deleted file mode 100644
index 4875c7c..0000000
--- a/services/net/java/android/net/TcpKeepalivePacketData.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-package android.net;
-
-import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.system.OsConstants;
-
-import com.android.net.module.util.IpUtils;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Objects;
-
-/**
- * Represents the actual tcp keep alive packets which will be used for hardware offload.
- * @hide
- */
-public class TcpKeepalivePacketData extends KeepalivePacketData implements Parcelable {
-    private static final String TAG = "TcpKeepalivePacketData";
-
-    /** TCP sequence number. */
-    public final int tcpSeq;
-
-    /** TCP ACK number. */
-    public final int tcpAck;
-
-    /** TCP RCV window. */
-    public final int tcpWnd;
-
-    /** TCP RCV window scale. */
-    public final int tcpWndScale;
-
-    /** IP TOS. */
-    public final int ipTos;
-
-    /** IP TTL. */
-    public final int ipTtl;
-
-    private static final int IPV4_HEADER_LENGTH = 20;
-    private static final int IPV6_HEADER_LENGTH = 40;
-    private static final int TCP_HEADER_LENGTH = 20;
-
-    // This should only be constructed via static factory methods, such as
-    // tcpKeepalivePacket.
-    private TcpKeepalivePacketData(final TcpKeepalivePacketDataParcelable tcpDetails,
-            final byte[] data) throws InvalidPacketException, UnknownHostException {
-        super(InetAddress.getByAddress(tcpDetails.srcAddress), tcpDetails.srcPort,
-                InetAddress.getByAddress(tcpDetails.dstAddress), tcpDetails.dstPort, data);
-        tcpSeq = tcpDetails.seq;
-        tcpAck = tcpDetails.ack;
-        // In the packet, the window is shifted right by the window scale.
-        tcpWnd = tcpDetails.rcvWnd;
-        tcpWndScale = tcpDetails.rcvWndScale;
-        ipTos = tcpDetails.tos;
-        ipTtl = tcpDetails.ttl;
-    }
-
-    private TcpKeepalivePacketData(final InetAddress srcAddress, int srcPort,
-            final InetAddress dstAddress, int dstPort, final byte[] data, int tcpSeq,
-            int tcpAck, int tcpWnd, int tcpWndScale, int ipTos, int ipTtl)
-            throws InvalidPacketException {
-        super(srcAddress, srcPort, dstAddress, dstPort, data);
-        this.tcpSeq = tcpSeq;
-        this.tcpAck = tcpAck;
-        this.tcpWnd = tcpWnd;
-        this.tcpWndScale = tcpWndScale;
-        this.ipTos = ipTos;
-        this.ipTtl = ipTtl;
-    }
-
-    /**
-     * Factory method to create tcp keepalive packet structure.
-     */
-    public static TcpKeepalivePacketData tcpKeepalivePacket(
-            TcpKeepalivePacketDataParcelable tcpDetails) throws InvalidPacketException {
-        final byte[] packet;
-        try {
-            if ((tcpDetails.srcAddress != null) && (tcpDetails.dstAddress != null)
-                    && (tcpDetails.srcAddress.length == 4 /* V4 IP length */)
-                    && (tcpDetails.dstAddress.length == 4 /* V4 IP length */)) {
-                packet = buildV4Packet(tcpDetails);
-            } else {
-                // TODO: support ipv6
-                throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
-            }
-            return new TcpKeepalivePacketData(tcpDetails, packet);
-        } catch (UnknownHostException e) {
-            throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
-        }
-
-    }
-
-    /**
-     * Build ipv4 tcp keepalive packet, not including the link-layer header.
-     */
-    // TODO : if this code is ever moved to the network stack, factorize constants with the ones
-    // over there.
-    private static byte[] buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails) {
-        final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH;
-        ByteBuffer buf = ByteBuffer.allocate(length);
-        buf.order(ByteOrder.BIG_ENDIAN);
-        buf.put((byte) 0x45);                       // IP version and IHL
-        buf.put((byte) tcpDetails.tos);             // TOS
-        buf.putShort((short) length);
-        buf.putInt(0x00004000);                     // ID, flags=DF, offset
-        buf.put((byte) tcpDetails.ttl);             // TTL
-        buf.put((byte) OsConstants.IPPROTO_TCP);
-        final int ipChecksumOffset = buf.position();
-        buf.putShort((short) 0);                    // IP checksum
-        buf.put(tcpDetails.srcAddress);
-        buf.put(tcpDetails.dstAddress);
-        buf.putShort((short) tcpDetails.srcPort);
-        buf.putShort((short) tcpDetails.dstPort);
-        buf.putInt(tcpDetails.seq);                 // Sequence Number
-        buf.putInt(tcpDetails.ack);                 // ACK
-        buf.putShort((short) 0x5010);               // TCP length=5, flags=ACK
-        buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale));   // Window size
-        final int tcpChecksumOffset = buf.position();
-        buf.putShort((short) 0);                    // TCP checksum
-        // URG is not set therefore the urgent pointer is zero.
-        buf.putShort((short) 0);                    // Urgent pointer
-
-        buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
-        buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum(
-                buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH));
-
-        return buf.array();
-    }
-
-    // TODO: add buildV6Packet.
-
-    @Override
-    public boolean equals(@Nullable final Object o) {
-        if (!(o instanceof TcpKeepalivePacketData)) return false;
-        final TcpKeepalivePacketData other = (TcpKeepalivePacketData) o;
-        final InetAddress srcAddress = getSrcAddress();
-        final InetAddress dstAddress = getDstAddress();
-        return srcAddress.equals(other.getSrcAddress())
-                && dstAddress.equals(other.getDstAddress())
-                && getSrcPort() == other.getSrcPort()
-                && getDstPort() == other.getDstPort()
-                && this.tcpAck == other.tcpAck
-                && this.tcpSeq == other.tcpSeq
-                && this.tcpWnd == other.tcpWnd
-                && this.tcpWndScale == other.tcpWndScale
-                && this.ipTos == other.ipTos
-                && this.ipTtl == other.ipTtl;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(getSrcAddress(), getDstAddress(), getSrcPort(), getDstPort(),
-                tcpAck, tcpSeq, tcpWnd, tcpWndScale, ipTos, ipTtl);
-    }
-
-    /**
-     * Parcelable Implementation.
-     * Note that this object implements parcelable (and needs to keep doing this as it inherits
-     * from a class that does), but should usually be parceled as a stable parcelable using
-     * the toStableParcelable() and fromStableParcelable() methods.
-     */
-    public int describeContents() {
-        return 0;
-    }
-
-    /** Write to parcel. */
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeString(getSrcAddress().getHostAddress());
-        out.writeString(getDstAddress().getHostAddress());
-        out.writeInt(getSrcPort());
-        out.writeInt(getDstPort());
-        out.writeByteArray(getPacket());
-        out.writeInt(tcpSeq);
-        out.writeInt(tcpAck);
-        out.writeInt(tcpWnd);
-        out.writeInt(tcpWndScale);
-        out.writeInt(ipTos);
-        out.writeInt(ipTtl);
-    }
-
-    private static TcpKeepalivePacketData readFromParcel(Parcel in) throws InvalidPacketException {
-        InetAddress srcAddress = InetAddresses.parseNumericAddress(in.readString());
-        InetAddress dstAddress = InetAddresses.parseNumericAddress(in.readString());
-        int srcPort = in.readInt();
-        int dstPort = in.readInt();
-        byte[] packet = in.createByteArray();
-        int tcpSeq = in.readInt();
-        int tcpAck = in.readInt();
-        int tcpWnd = in.readInt();
-        int tcpWndScale = in.readInt();
-        int ipTos = in.readInt();
-        int ipTtl = in.readInt();
-        return new TcpKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, packet, tcpSeq,
-                tcpAck, tcpWnd, tcpWndScale, ipTos, ipTtl);
-    }
-
-    /** Parcelable Creator. */
-    public static final @NonNull Parcelable.Creator<TcpKeepalivePacketData> CREATOR =
-            new Parcelable.Creator<TcpKeepalivePacketData>() {
-                public TcpKeepalivePacketData createFromParcel(Parcel in) {
-                    try {
-                        return readFromParcel(in);
-                    } catch (InvalidPacketException e) {
-                        throw new IllegalArgumentException(
-                                "Invalid NAT-T keepalive data: " + e.getError());
-                    }
-                }
-
-                public TcpKeepalivePacketData[] newArray(int size) {
-                    return new TcpKeepalivePacketData[size];
-                }
-            };
-
-    /**
-     * Convert this TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable.
-     */
-    @NonNull
-    public TcpKeepalivePacketDataParcelable toStableParcelable() {
-        final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable();
-        final InetAddress srcAddress = getSrcAddress();
-        final InetAddress dstAddress = getDstAddress();
-        parcel.srcAddress = srcAddress.getAddress();
-        parcel.srcPort = getSrcPort();
-        parcel.dstAddress = dstAddress.getAddress();
-        parcel.dstPort = getDstPort();
-        parcel.seq = tcpSeq;
-        parcel.ack = tcpAck;
-        parcel.rcvWnd = tcpWnd;
-        parcel.rcvWndScale = tcpWndScale;
-        parcel.tos = ipTos;
-        parcel.ttl = ipTtl;
-        return parcel;
-    }
-
-    @Override
-    public String toString() {
-        return "saddr: " + getSrcAddress()
-                + " daddr: " + getDstAddress()
-                + " sport: " + getSrcPort()
-                + " dport: " + getDstPort()
-                + " seq: " + tcpSeq
-                + " ack: " + tcpAck
-                + " wnd: " + tcpWnd
-                + " wndScale: " + tcpWndScale
-                + " tos: " + ipTos
-                + " ttl: " + ipTtl;
-    }
-}
diff --git a/services/net/java/android/net/ip/IpClientManager.java b/services/net/java/android/net/ip/IpClientManager.java
index db464e7..274b6dc 100644
--- a/services/net/java/android/net/ip/IpClientManager.java
+++ b/services/net/java/android/net/ip/IpClientManager.java
@@ -21,6 +21,7 @@
 import android.net.NattKeepalivePacketData;
 import android.net.ProxyInfo;
 import android.net.TcpKeepalivePacketData;
+import android.net.TcpKeepalivePacketDataParcelable;
 import android.net.shared.Layer2Information;
 import android.net.shared.ProvisioningConfiguration;
 import android.net.util.KeepalivePacketDataUtil;
@@ -215,9 +216,20 @@
      * Add a TCP keepalive packet filter before setting up keepalive offload.
      */
     public boolean addKeepalivePacketFilter(int slot, TcpKeepalivePacketData pkt) {
+        return addKeepalivePacketFilter(slot, KeepalivePacketDataUtil.toStableParcelable(pkt));
+    }
+
+    /**
+     * Add a TCP keepalive packet filter before setting up keepalive offload.
+     * @deprecated This method is for use on pre-S platforms where TcpKeepalivePacketData is not
+     *             system API. On newer platforms use
+     *             addKeepalivePacketFilter(int, TcpKeepalivePacketData) instead.
+     */
+    @Deprecated
+    public boolean addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) {
         final long token = Binder.clearCallingIdentity();
         try {
-            mIpClient.addKeepalivePacketFilter(slot, pkt.toStableParcelable());
+            mIpClient.addKeepalivePacketFilter(slot, pkt);
             return true;
         } catch (RemoteException e) {
             log("Error adding Keepalive Packet Filter ", e);
diff --git a/services/net/java/android/net/util/KeepalivePacketDataUtil.java b/services/net/java/android/net/util/KeepalivePacketDataUtil.java
index 4466ea0..6e539bb 100644
--- a/services/net/java/android/net/util/KeepalivePacketDataUtil.java
+++ b/services/net/java/android/net/util/KeepalivePacketDataUtil.java
@@ -16,20 +16,47 @@
 
 package android.net.util;
 
+import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.InvalidPacketException;
+import android.net.KeepalivePacketData;
 import android.net.NattKeepalivePacketData;
 import android.net.NattKeepalivePacketDataParcelable;
+import android.net.TcpKeepalivePacketData;
+import android.net.TcpKeepalivePacketDataParcelable;
+import android.os.Build;
+import android.system.OsConstants;
+import android.util.Log;
+
+import com.android.net.module.util.IpUtils;
 
 import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 
-/** @hide */
+/**
+ * Utility class to convert to/from keepalive data parcelables.
+ *
+ * TODO: move to networkstack-client library when it is moved to frameworks/libs/net.
+ * This class cannot go into other shared libraries as it depends on NetworkStack AIDLs.
+ * @hide
+ */
 public final class KeepalivePacketDataUtil {
-     /**
-     * Convert this NattKeepalivePacketData to a NattKeepalivePacketDataParcelable.
+    private static final int IPV4_HEADER_LENGTH = 20;
+    private static final int IPV6_HEADER_LENGTH = 40;
+    private static final int TCP_HEADER_LENGTH = 20;
+
+    private static final String TAG = KeepalivePacketDataUtil.class.getSimpleName();
+
+    /**
+     * Convert a NattKeepalivePacketData to a NattKeepalivePacketDataParcelable.
      */
     @NonNull
     public static NattKeepalivePacketDataParcelable toStableParcelable(
-            NattKeepalivePacketData pkt) {
+            @NonNull NattKeepalivePacketData pkt) {
         final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable();
         final InetAddress srcAddress = pkt.getSrcAddress();
         final InetAddress dstAddress = pkt.getDstAddress();
@@ -39,4 +66,158 @@
         parcel.dstPort = pkt.getDstPort();
         return parcel;
     }
+
+    /**
+     * Convert a TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable.
+     */
+    @NonNull
+    public static TcpKeepalivePacketDataParcelable toStableParcelable(
+            @NonNull TcpKeepalivePacketData pkt) {
+        final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable();
+        final InetAddress srcAddress = pkt.getSrcAddress();
+        final InetAddress dstAddress = pkt.getDstAddress();
+        parcel.srcAddress = srcAddress.getAddress();
+        parcel.srcPort = pkt.getSrcPort();
+        parcel.dstAddress = dstAddress.getAddress();
+        parcel.dstPort = pkt.getDstPort();
+        parcel.seq = pkt.tcpSeq;
+        parcel.ack = pkt.tcpAck;
+        parcel.rcvWnd = pkt.tcpWindow;
+        parcel.rcvWndScale = pkt.tcpWindowScale;
+        parcel.tos = pkt.ipTos;
+        parcel.ttl = pkt.ipTtl;
+        return parcel;
+    }
+
+    /**
+     * Factory method to create tcp keepalive packet structure.
+     * @hide
+     */
+    public static TcpKeepalivePacketData fromStableParcelable(
+            TcpKeepalivePacketDataParcelable tcpDetails) throws InvalidPacketException {
+        final byte[] packet;
+        try {
+            if ((tcpDetails.srcAddress != null) && (tcpDetails.dstAddress != null)
+                    && (tcpDetails.srcAddress.length == 4 /* V4 IP length */)
+                    && (tcpDetails.dstAddress.length == 4 /* V4 IP length */)) {
+                packet = buildV4Packet(tcpDetails);
+            } else {
+                // TODO: support ipv6
+                throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+            }
+            return new TcpKeepalivePacketData(
+                    InetAddress.getByAddress(tcpDetails.srcAddress),
+                    tcpDetails.srcPort,
+                    InetAddress.getByAddress(tcpDetails.dstAddress),
+                    tcpDetails.dstPort,
+                    packet,
+                    tcpDetails.seq, tcpDetails.ack, tcpDetails.rcvWnd, tcpDetails.rcvWndScale,
+                    tcpDetails.tos, tcpDetails.ttl);
+        } catch (UnknownHostException e) {
+            throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+        }
+
+    }
+
+    /**
+     * Build ipv4 tcp keepalive packet, not including the link-layer header.
+     */
+    // TODO : if this code is ever moved to the network stack, factorize constants with the ones
+    // over there.
+    private static byte[] buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails) {
+        final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH;
+        ByteBuffer buf = ByteBuffer.allocate(length);
+        buf.order(ByteOrder.BIG_ENDIAN);
+        buf.put((byte) 0x45);                       // IP version and IHL
+        buf.put((byte) tcpDetails.tos);             // TOS
+        buf.putShort((short) length);
+        buf.putInt(0x00004000);                     // ID, flags=DF, offset
+        buf.put((byte) tcpDetails.ttl);             // TTL
+        buf.put((byte) OsConstants.IPPROTO_TCP);
+        final int ipChecksumOffset = buf.position();
+        buf.putShort((short) 0);                    // IP checksum
+        buf.put(tcpDetails.srcAddress);
+        buf.put(tcpDetails.dstAddress);
+        buf.putShort((short) tcpDetails.srcPort);
+        buf.putShort((short) tcpDetails.dstPort);
+        buf.putInt(tcpDetails.seq);                 // Sequence Number
+        buf.putInt(tcpDetails.ack);                 // ACK
+        buf.putShort((short) 0x5010);               // TCP length=5, flags=ACK
+        buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale));   // Window size
+        final int tcpChecksumOffset = buf.position();
+        buf.putShort((short) 0);                    // TCP checksum
+        // URG is not set therefore the urgent pointer is zero.
+        buf.putShort((short) 0);                    // Urgent pointer
+
+        buf.putShort(ipChecksumOffset, com.android.net.module.util.IpUtils.ipChecksum(buf, 0));
+        buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum(
+                buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH));
+
+        return buf.array();
+    }
+
+    // TODO: add buildV6Packet.
+
+    /**
+     * Get a {@link TcpKeepalivePacketDataParcelable} from {@link KeepalivePacketData}, if the
+     * generic class actually contains TCP keepalive data.
+     *
+     * @deprecated This method is used on R platforms where android.net.TcpKeepalivePacketData was
+     * not yet system API. Newer platforms should use android.net.TcpKeepalivePacketData directly.
+     *
+     * @param data A {@link KeepalivePacketData} that may contain TCP keepalive data.
+     * @return A parcelable containing TCP keepalive data, or null if the input data does not
+     *         contain TCP keepalive data.
+     */
+    @Deprecated
+    @SuppressWarnings("AndroidFrameworkCompatChange") // API version check used to Log.wtf
+    @Nullable
+    public static TcpKeepalivePacketDataParcelable parseTcpKeepalivePacketData(
+            @Nullable KeepalivePacketData data) {
+        if (data == null) return null;
+
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
+            Log.wtf(TAG, "parseTcpKeepalivePacketData should not be used after R, use "
+                    + "TcpKeepalivePacketData instead.");
+        }
+
+        // Reconstruct TcpKeepalivePacketData from the packet contained in KeepalivePacketData
+        final ByteBuffer buffer = ByteBuffer.wrap(data.getPacket());
+        buffer.order(ByteOrder.BIG_ENDIAN);
+
+        // Most of the fields are accessible from the KeepalivePacketData superclass: instead of
+        // using Struct to parse everything, just extract the extra fields necessary for
+        // TcpKeepalivePacketData.
+        final int tcpSeq;
+        final int tcpAck;
+        final int wndSize;
+        final int ipTos;
+        final int ttl;
+        try {
+            // This only support IPv4, because TcpKeepalivePacketData only supports IPv4 for R and
+            // below, and this method should not be used on newer platforms.
+            tcpSeq = buffer.getInt(IPV4_HEADER_LENGTH + 4);
+            tcpAck = buffer.getInt(IPV4_HEADER_LENGTH + 8);
+            wndSize = buffer.getShort(IPV4_HEADER_LENGTH + 14);
+            ipTos = buffer.get(1);
+            ttl = buffer.get(8);
+        } catch (IndexOutOfBoundsException e) {
+            return null;
+        }
+
+        final TcpKeepalivePacketDataParcelable p = new TcpKeepalivePacketDataParcelable();
+        p.srcAddress = data.getSrcAddress().getAddress();
+        p.srcPort = data.getSrcPort();
+        p.dstAddress = data.getDstAddress().getAddress();
+        p.dstPort = data.getDstPort();
+        p.seq = tcpSeq;
+        p.ack = tcpAck;
+        // TcpKeepalivePacketData could actually use non-zero wndScale, but this does not affect
+        // actual functionality as generated packets will be the same (no wndScale option added)
+        p.rcvWnd = wndSize;
+        p.rcvWndScale = 0;
+        p.tos = ipTos;
+        p.ttl = ttl;
+        return p;
+    }
 }
diff --git a/services/robotests/src/com/android/server/location/OWNERS b/services/robotests/src/com/android/server/location/OWNERS
new file mode 100644
index 0000000..696a0c2
--- /dev/null
+++ b/services/robotests/src/com/android/server/location/OWNERS
@@ -0,0 +1 @@
+file:/location/java/android/location/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index c4d1211..98d6452 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -95,13 +95,24 @@
 
     static class MockInjector extends RebootEscrowManager.Injector {
         private final IRebootEscrow mRebootEscrow;
+        private final RebootEscrowProviderInterface mRebootEscrowProvider;
         private final UserManager mUserManager;
         private final MockableRebootEscrowInjected mInjected;
 
-        MockInjector(Context context, UserManager userManager, IRebootEscrow rebootEscrow,
+        MockInjector(Context context, UserManager userManager,
+                IRebootEscrow rebootEscrow,
                 MockableRebootEscrowInjected injected) {
             super(context);
             mRebootEscrow = rebootEscrow;
+
+            RebootEscrowProviderHalImpl.Injector halInjector =
+                    new RebootEscrowProviderHalImpl.Injector() {
+                        @Override
+                        public IRebootEscrow getRebootEscrow() {
+                            return mRebootEscrow;
+                        }
+                    };
+            mRebootEscrowProvider = new RebootEscrowProviderHalImpl(halInjector);
             mUserManager = userManager;
             mInjected = injected;
         }
@@ -112,8 +123,8 @@
         }
 
         @Override
-        public IRebootEscrow getRebootEscrow() {
-            return mRebootEscrow;
+        public RebootEscrowProviderInterface getRebootEscrowProvider() {
+            return mRebootEscrowProvider;
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/OWNERS b/services/tests/servicestests/src/com/android/server/recoverysystem/OWNERS
new file mode 100644
index 0000000..3880038
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/recoverysystem/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/OWNERS b/services/tests/servicestests/src/com/android/server/timedetector/OWNERS
index 09447a9..8f80897 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/timedetector/OWNERS
@@ -1 +1,3 @@
-include /core/java/android/app/timezone/OWNERS
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 22addf9..f0b1be5 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.timedetector.GnssTimeSuggestion;
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.NetworkTimeSuggestion;
 import android.app.timedetector.TelephonyTimeSuggestion;
@@ -172,6 +173,36 @@
         mStubbedTimeDetectorStrategy.verifySuggestNetworkTimeCalled(NetworkTimeSuggestion);
     }
 
+    @Test(expected = SecurityException.class)
+    public void testSuggestGnssTime_withoutPermission() {
+        doThrow(new SecurityException("Mock"))
+                .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+        GnssTimeSuggestion gnssTimeSuggestion = createGnssTimeSuggestion();
+
+        try {
+            mTimeDetectorService.suggestGnssTime(gnssTimeSuggestion);
+            fail();
+        } finally {
+            verify(mMockContext).enforceCallingOrSelfPermission(
+                    eq(android.Manifest.permission.SET_TIME), anyString());
+        }
+    }
+
+    @Test
+    public void testSuggestGnssTime() throws Exception {
+        doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+
+        GnssTimeSuggestion gnssTimeSuggestion = createGnssTimeSuggestion();
+        mTimeDetectorService.suggestGnssTime(gnssTimeSuggestion);
+        mTestHandler.assertTotalMessagesEnqueued(1);
+
+        verify(mMockContext).enforceCallingOrSelfPermission(
+                eq(android.Manifest.permission.SET_TIME), anyString());
+
+        mTestHandler.waitForMessagesToBeProcessed();
+        mStubbedTimeDetectorStrategy.verifySuggestGnssTimeCalled(gnssTimeSuggestion);
+    }
+
     @Test
     public void testDump() {
         when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
@@ -216,12 +247,18 @@
         return new NetworkTimeSuggestion(timeValue);
     }
 
+    private static GnssTimeSuggestion createGnssTimeSuggestion() {
+        TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
+        return new GnssTimeSuggestion(timeValue);
+    }
+
     private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy {
 
         // Call tracking.
         private TelephonyTimeSuggestion mLastTelephonySuggestion;
         private ManualTimeSuggestion mLastManualSuggestion;
         private NetworkTimeSuggestion mLastNetworkSuggestion;
+        private GnssTimeSuggestion mLastGnssSuggestion;
         private boolean mHandleAutoTimeDetectionChangedCalled;
         private boolean mDumpCalled;
 
@@ -242,6 +279,11 @@
         }
 
         @Override
+        public void suggestGnssTime(GnssTimeSuggestion timeSuggestion) {
+            mLastGnssSuggestion = timeSuggestion;
+        }
+
+        @Override
         public void handleAutoTimeConfigChanged() {
             mHandleAutoTimeDetectionChangedCalled = true;
         }
@@ -255,6 +297,7 @@
             mLastTelephonySuggestion = null;
             mLastManualSuggestion = null;
             mLastNetworkSuggestion = null;
+            mLastGnssSuggestion = null;
             mHandleAutoTimeDetectionChangedCalled = false;
             mDumpCalled = false;
         }
@@ -271,6 +314,10 @@
             assertEquals(expectedSuggestion, mLastNetworkSuggestion);
         }
 
+        void verifySuggestGnssTimeCalled(GnssTimeSuggestion expectedSuggestion) {
+            assertEquals(expectedSuggestion, mLastGnssSuggestion);
+        }
+
         void verifyHandleAutoTimeDetectionChangedCalled() {
             assertTrue(mHandleAutoTimeDetectionChangedCalled);
         }
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index 21396fd..b1adb0b 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.timedetector;
 
+import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_GNSS;
 import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK;
 import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY;
 
@@ -25,6 +26,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.app.timedetector.GnssTimeSuggestion;
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.NetworkTimeSuggestion;
 import android.app.timedetector.TelephonyTimeSuggestion;
@@ -569,7 +571,53 @@
     }
 
     @Test
-    public void highPrioritySuggestionsShouldBeatLowerPrioritySuggestions() {
+    public void testSuggestGnssTime_autoTimeEnabled() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoOriginPriorities(ORIGIN_GNSS)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        GnssTimeSuggestion timeSuggestion =
+                mScript.generateGnssTimeSuggestion(ARBITRARY_TEST_TIME);
+
+        mScript.simulateTimePassing();
+
+        long expectedSystemClockMillis =
+                mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
+        mScript.simulateGnssTimeSuggestion(timeSuggestion)
+                .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis);
+    }
+
+    @Test
+    public void testSuggestGnssTime_autoTimeDisabled() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoOriginPriorities(ORIGIN_GNSS)
+                .pokeAutoTimeDetectionEnabled(false);
+
+        GnssTimeSuggestion timeSuggestion =
+                mScript.generateGnssTimeSuggestion(ARBITRARY_TEST_TIME);
+
+        mScript.simulateTimePassing()
+                .simulateGnssTimeSuggestion(timeSuggestion)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+    }
+
+    @Test
+    public void gnssTimeSuggestion_ignoredWhenReferencedTimeIsInThePast() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoOriginPriorities(ORIGIN_GNSS)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        Instant suggestedTime = TIME_LOWER_BOUND.minus(Duration.ofDays(1));
+        GnssTimeSuggestion timeSuggestion = mScript
+                .generateGnssTimeSuggestion(suggestedTime);
+
+        mScript.simulateGnssTimeSuggestion(timeSuggestion)
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestGnssSuggestion(null);
+    }
+
+    @Test
+    public void highPrioritySuggestionsBeatLowerPrioritySuggestions_telephonyNetworkOrigins() {
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeAutoTimeDetectionEnabled(true)
                 .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK);
@@ -672,22 +720,130 @@
     }
 
     @Test
+    public void highPrioritySuggestionsBeatLowerPrioritySuggestions_networkGnssOrigins() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true)
+                .pokeAutoOriginPriorities(ORIGIN_NETWORK, ORIGIN_GNSS);
+
+        // Three obviously different times that could not be mistaken for each other.
+        Instant gnssTime1 = ARBITRARY_TEST_TIME;
+        Instant gnssTime2 = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30));
+        Instant networkTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(60));
+        // A small increment used to simulate the passage of time, but not enough to interfere with
+        // macro-level time changes associated with suggestion age.
+        final long smallTimeIncrementMillis = 101;
+
+        // A gnss suggestion is made. It should be used because there is no network suggestion.
+        GnssTimeSuggestion gnssTimeSuggestion1 =
+                mScript.generateGnssTimeSuggestion(gnssTime1);
+        mScript.simulateTimePassing(smallTimeIncrementMillis)
+                .simulateGnssTimeSuggestion(gnssTimeSuggestion1)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        mScript.calculateTimeInMillisForNow(gnssTimeSuggestion1.getUtcTime()));
+
+        // Check internal state.
+        mScript.assertLatestNetworkSuggestion(null)
+                .assertLatestGnssSuggestion(gnssTimeSuggestion1);
+        assertEquals(gnssTimeSuggestion1, mScript.peekLatestValidGnssSuggestion());
+        assertNull("No network suggestions were made:", mScript.peekLatestValidNetworkSuggestion());
+
+        // Simulate a little time passing.
+        mScript.simulateTimePassing(smallTimeIncrementMillis)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+
+        // Now a network suggestion is made. Network suggestions are prioritized over gnss
+        // suggestions so it should "win".
+        NetworkTimeSuggestion networkTimeSuggestion =
+                mScript.generateNetworkTimeSuggestion(networkTime);
+        mScript.simulateTimePassing(smallTimeIncrementMillis)
+                .simulateNetworkTimeSuggestion(networkTimeSuggestion)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        mScript.calculateTimeInMillisForNow(networkTimeSuggestion.getUtcTime()));
+
+        // Check internal state.
+        mScript.assertLatestNetworkSuggestion(networkTimeSuggestion)
+                .assertLatestGnssSuggestion(gnssTimeSuggestion1);
+        assertEquals(gnssTimeSuggestion1, mScript.peekLatestValidGnssSuggestion());
+        assertEquals(networkTimeSuggestion, mScript.peekLatestValidNetworkSuggestion());
+
+        // Simulate some significant time passing: half the time allowed before a time signal
+        // becomes "too old to use".
+        mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+
+        // Now another gnss suggestion is made. Network suggestions are prioritized over
+        // gnss suggestions so the latest network suggestion should still "win".
+        GnssTimeSuggestion gnssTimeSuggestion2 =
+                mScript.generateGnssTimeSuggestion(gnssTime2);
+        mScript.simulateTimePassing(smallTimeIncrementMillis)
+                .simulateGnssTimeSuggestion(gnssTimeSuggestion2)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+
+        // Check internal state.
+        mScript.assertLatestNetworkSuggestion(networkTimeSuggestion)
+                .assertLatestGnssSuggestion(gnssTimeSuggestion2);
+        assertEquals(gnssTimeSuggestion2, mScript.peekLatestValidGnssSuggestion());
+        assertEquals(networkTimeSuggestion, mScript.peekLatestValidNetworkSuggestion());
+
+        // Simulate some significant time passing: half the time allowed before a time signal
+        // becomes "too old to use". This should mean that telephonyTimeSuggestion is now too old to
+        // be used but networkTimeSuggestion2 is not.
+        mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2);
+
+        // NOTE: The TimeDetectorStrategyImpl doesn't set an alarm for the point when the last
+        // suggestion it used becomes too old: it requires a new suggestion or an auto-time toggle
+        // to re-run the detection logic. This may change in future but until then we rely on a
+        // steady stream of suggestions to re-evaluate.
+        mScript.verifySystemClockWasNotSetAndResetCallTracking();
+
+        // Check internal state.
+        mScript.assertLatestNetworkSuggestion(networkTimeSuggestion)
+                .assertLatestGnssSuggestion(gnssTimeSuggestion2);
+        assertEquals(gnssTimeSuggestion2, mScript.peekLatestValidGnssSuggestion());
+        assertNull(
+                "Network suggestion should be expired:",
+                mScript.peekLatestValidNetworkSuggestion());
+
+        // Toggle auto-time off and on to force the detection logic to run.
+        mScript.simulateAutoTimeDetectionToggle()
+                .simulateTimePassing(smallTimeIncrementMillis)
+                .simulateAutoTimeDetectionToggle();
+
+        // Verify the latest gnss time now wins.
+        mScript.verifySystemClockWasSetAndResetCallTracking(
+                mScript.calculateTimeInMillisForNow(gnssTimeSuggestion2.getUtcTime()));
+
+        // Check internal state.
+        mScript.assertLatestNetworkSuggestion(networkTimeSuggestion)
+                .assertLatestGnssSuggestion(gnssTimeSuggestion2);
+        assertEquals(gnssTimeSuggestion2, mScript.peekLatestValidGnssSuggestion());
+        assertNull(
+                "Network suggestion should still be expired:",
+                mScript.peekLatestValidNetworkSuggestion());
+    }
+
+    @Test
     public void whenAllTimeSuggestionsAreAvailable_higherPriorityWins_lowerPriorityComesFirst() {
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeAutoTimeDetectionEnabled(true)
-                .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK);
+                .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK, ORIGIN_GNSS);
 
         Instant networkTime = ARBITRARY_TEST_TIME;
-        Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30));
+        Instant gnssTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30));
+        Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(60));
 
         NetworkTimeSuggestion networkTimeSuggestion =
                 mScript.generateNetworkTimeSuggestion(networkTime);
+        GnssTimeSuggestion gnssTimeSuggestion =
+                mScript.generateGnssTimeSuggestion(gnssTime);
         TelephonyTimeSuggestion telephonyTimeSuggestion =
                 mScript.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, telephonyTime);
 
         mScript.simulateNetworkTimeSuggestion(networkTimeSuggestion)
+                .simulateGnssTimeSuggestion(gnssTimeSuggestion)
                 .simulateTelephonyTimeSuggestion(telephonyTimeSuggestion)
                 .assertLatestNetworkSuggestion(networkTimeSuggestion)
+                .assertLatestGnssSuggestion(gnssTimeSuggestion)
                 .assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion)
                 .verifySystemClockWasSetAndResetCallTracking(telephonyTime.toEpochMilli());
     }
@@ -696,20 +852,25 @@
     public void whenAllTimeSuggestionsAreAvailable_higherPriorityWins_higherPriorityComesFirst() {
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeAutoTimeDetectionEnabled(true)
-                .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK);
+                .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK, ORIGIN_GNSS);
 
         Instant networkTime = ARBITRARY_TEST_TIME;
         Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30));
+        Instant gnssTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(60));
 
         NetworkTimeSuggestion networkTimeSuggestion =
                 mScript.generateNetworkTimeSuggestion(networkTime);
         TelephonyTimeSuggestion telephonyTimeSuggestion =
                 mScript.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, telephonyTime);
+        GnssTimeSuggestion gnssTimeSuggestion =
+                mScript.generateGnssTimeSuggestion(gnssTime);
 
         mScript.simulateTelephonyTimeSuggestion(telephonyTimeSuggestion)
                 .simulateNetworkTimeSuggestion(networkTimeSuggestion)
+                .simulateGnssTimeSuggestion(gnssTimeSuggestion)
                 .assertLatestNetworkSuggestion(networkTimeSuggestion)
                 .assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion)
+                .assertLatestGnssSuggestion(gnssTimeSuggestion)
                 .verifySystemClockWasSetAndResetCallTracking(telephonyTime.toEpochMilli());
     }
 
@@ -728,7 +889,37 @@
     }
 
     @Test
-    public void suggestionsFromSourceNotListedInPrioritiesList_areIgnored() {
+    public void whenHigherPrioritySuggestionsAreNotAvailable_fallbacksToNext() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true)
+                .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK, ORIGIN_GNSS);
+
+        GnssTimeSuggestion timeSuggestion =
+                mScript.generateGnssTimeSuggestion(ARBITRARY_TEST_TIME);
+
+        mScript.simulateGnssTimeSuggestion(timeSuggestion)
+                .assertLatestGnssSuggestion(timeSuggestion)
+                .verifySystemClockWasSetAndResetCallTracking(ARBITRARY_TEST_TIME.toEpochMilli());
+    }
+
+    @Test
+    public void suggestionsFromTelephonyOriginNotInPriorityList_areIgnored() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true)
+                .pokeAutoOriginPriorities(ORIGIN_NETWORK);
+
+        int slotIndex = ARBITRARY_SLOT_INDEX;
+        Instant testTime = ARBITRARY_TEST_TIME;
+        TelephonyTimeSuggestion timeSuggestion =
+                mScript.generateTelephonyTimeSuggestion(slotIndex, testTime);
+
+        mScript.simulateTelephonyTimeSuggestion(timeSuggestion)
+                .assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, timeSuggestion)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+    }
+
+    @Test
+    public void suggestionsFromNetworkOriginNotInPriorityList_areIgnored() {
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeAutoTimeDetectionEnabled(true)
                 .pokeAutoOriginPriorities(ORIGIN_TELEPHONY);
@@ -742,6 +933,20 @@
     }
 
     @Test
+    public void suggestionsFromGnssOriginNotInPriorityList_areIgnored() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true)
+                .pokeAutoOriginPriorities(ORIGIN_TELEPHONY);
+
+        GnssTimeSuggestion timeSuggestion = mScript.generateGnssTimeSuggestion(
+                ARBITRARY_TEST_TIME);
+
+        mScript.simulateGnssTimeSuggestion(timeSuggestion)
+                .assertLatestGnssSuggestion(timeSuggestion)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+    }
+
+    @Test
     public void autoOriginPrioritiesList_doesNotAffectManualSuggestion() {
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeAutoTimeDetectionEnabled(false)
@@ -945,6 +1150,11 @@
             return this;
         }
 
+        Script simulateGnssTimeSuggestion(GnssTimeSuggestion timeSuggestion) {
+            mTimeDetectorStrategy.suggestGnssTime(timeSuggestion);
+            return this;
+        }
+
         Script simulateAutoTimeDetectionToggle() {
             mFakeCallback.simulateAutoTimeZoneDetectionToggle();
             mTimeDetectorStrategy.handleAutoTimeConfigChanged();
@@ -995,6 +1205,14 @@
         }
 
         /**
+         * White box test info: Asserts the latest gnss suggestion is as expected.
+         */
+        Script assertLatestGnssSuggestion(GnssTimeSuggestion expected) {
+            assertEquals(expected, mTimeDetectorStrategy.getLatestGnssSuggestion());
+            return this;
+        }
+
+        /**
          * White box test info: Returns the telephony suggestion that would be used, if any, given
          * the current elapsed real time clock and regardless of origin prioritization.
          */
@@ -1011,6 +1229,14 @@
         }
 
         /**
+         * White box test info: Returns the gnss suggestion that would be used, if any, given the
+         * current elapsed real time clock and regardless of origin prioritization.
+         */
+        GnssTimeSuggestion peekLatestValidGnssSuggestion() {
+            return mTimeDetectorStrategy.findLatestValidGnssSuggestionForTests();
+        }
+
+        /**
          * Generates a ManualTimeSuggestion using the current elapsed realtime clock for the
          * reference time.
          */
@@ -1057,6 +1283,18 @@
         }
 
         /**
+         * Generates a GnssTimeSuggestion using the current elapsed realtime clock for the
+         * reference time.
+         */
+        GnssTimeSuggestion generateGnssTimeSuggestion(Instant suggestedTime) {
+            TimestampedValue<Long> utcTime =
+                    new TimestampedValue<>(
+                            mFakeCallback.peekElapsedRealtimeMillis(),
+                            suggestedTime.toEpochMilli());
+            return new GnssTimeSuggestion(utcTime);
+        }
+
+        /**
          * Calculates what the supplied time would be when adjusted for the movement of the fake
          * elapsed realtime clock.
          */
diff --git a/services/tests/servicestests/src/com/android/server/timezone/OWNERS b/services/tests/servicestests/src/com/android/server/timezone/OWNERS
index 09447a9..8f80897 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/timezone/OWNERS
@@ -1 +1,3 @@
-include /core/java/android/app/timezone/OWNERS
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS b/services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS
index 09447a9..8f80897 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS
@@ -1 +1,3 @@
-include /core/java/android/app/timezone/OWNERS
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS b/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS
new file mode 100644
index 0000000..e5d0370
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS
@@ -0,0 +1,2 @@
+ytai@google.com
+elaurent@google.com
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index 8b7a243..d502da9 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -916,6 +916,84 @@
     /** System preference change back to SRAT during handoff */
     public static final int HANDOFF_PREFERENCE_CHANGED = 0x8CB;
 
+    //IKE error notifications message as specified in 3GPP TS 24.302 (Section 8.1.2.2).
+
+    /** The PDN connection corresponding to the requested APN has been rejected. */
+    public static final int IWLAN_PDN_CONNECTION_REJECTION = 0x2000;
+    /**
+     * The PDN connection has been rejected. No additional PDN connections can be established
+     * for the UE due to the network policies or capabilities.
+     */
+    public static final int IWLAN_MAX_CONNECTION_REACHED = 0x2001;
+    /**
+     * The PDN connection has been rejected due to a semantic error in TFT operation.
+     */
+    public static final int IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION = 0x2031;
+    /**
+     * The PDN connection has been rejected due to a syntactic error in TFT operation.
+     */
+    public static final int IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION = 0x2032;
+    /**
+     * The PDN connection has been rejected due to sematic errors in the packet filter.
+     */
+    public static final int IWLAN_SEMANTIC_ERRORS_IN_PACKET_FILTERS = 0x2034;
+    /**
+     * The PDN connection has been rejected due to syntactic errors in the packet filter.
+     */
+    public static final int IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS = 0x2035;
+    /**
+     * No non-3GPP subscription is associated with the IMSI.
+     * The UE is not allowed to use non-3GPP access to EPC.
+     */
+    public static final int IWLAN_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED = 0x2328;
+    /** The user identified by the IMSI is unknown. */
+    public static final int IWLAN_USER_UNKNOWN = 0x2329;
+    /**
+     * The requested APN is not included in the user's profile,
+     * and therefore is not authorized for that user.
+     */
+    public static final int IWLAN_NO_APN_SUBSCRIPTION = 0x232A;
+    /** The user is barred from using the non-3GPP access or the subscribed APN. */
+    public static final int IWLAN_AUTHORIZATION_REJECTED = 0x232B;
+    /** The Mobile Equipment used is not acceptable to the network */
+    public static final int IWLAN_ILLEGAL_ME = 0x232E;
+    /**
+     * The network has determined that the requested procedure cannot be completed successfully
+     * due to network failure.
+     */
+    public static final int IWLAN_NETWORK_FAILURE = 0x2904;
+    /** The access type is restricted to the user. */
+    public static final int IWLAN_RAT_TYPE_NOT_ALLOWED = 0x2AF9;
+    /** The network does not accept emergency PDN bringup request using an IMEI */
+    public static final int IWLAN_IMEI_NOT_ACCEPTED = 0x2AFD;
+    /**
+     * The ePDG performs PLMN filtering (based on roaming agreements) and rejects
+     * the request from the UE.
+     * The UE requests service in a PLMN where the UE is not allowed to operate.
+     */
+    public static final int IWLAN_PLMN_NOT_ALLOWED = 0x2B03;
+    /** The ePDG does not support un-authenticated IMSI based emergency PDN bringup **/
+    public static final int IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED = 0x2B2F;
+
+    // Device is unable to establish an IPSec tunnel with the ePDG for any reason
+    // e.g authentication fail or certificate validation or DNS Resolution and timeout failure.
+
+    /** IKE configuration error resulting in failure  */
+    public static final int IWLAN_IKEV2_CONFIG_FAILURE = 0x4000;
+    /**
+     * Sent in the response to an IKE_AUTH message when, for some reason,
+     * the authentication failed.
+     */
+    public static final int IWLAN_IKEV2_AUTH_FAILURE = 0x4001;
+    /** IKE message timeout, tunnel setup failed due to no response from EPDG  */
+    public static final int IWLAN_IKEV2_MSG_TIMEOUT = 0x4002;
+    /** IKE Certification validation failure  */
+    public static final int IWLAN_IKEV2_CERT_INVALID = 0x4003;
+    /** Unable to resolve FQDN for the ePDG to an IP address */
+    public static final int IWLAN_DNS_RESOLUTION_NAME_FAILURE = 0x4004;
+    /** No response received from the DNS Server due to a timeout*/
+    public static final int IWLAN_DNS_RESOLUTION_TIMEOUT = 0x4005;
+
     // OEM sepecific error codes. To be used by OEMs when they don't
     // want to reveal error code which would be replaced by ERROR_UNSPECIFIED
     public static final int OEM_DCFAILCAUSE_1 = 0x1001;
@@ -1341,6 +1419,34 @@
         sFailCauseMap.put(VSNCP_RECONNECT_NOT_ALLOWED, "VSNCP_RECONNECT_NOT_ALLOWED");
         sFailCauseMap.put(IPV6_PREFIX_UNAVAILABLE, "IPV6_PREFIX_UNAVAILABLE");
         sFailCauseMap.put(HANDOFF_PREFERENCE_CHANGED, "HANDOFF_PREFERENCE_CHANGED");
+        sFailCauseMap.put(IWLAN_PDN_CONNECTION_REJECTION, "IWLAN_PDN_CONNECTION_REJECTION");
+        sFailCauseMap.put(IWLAN_MAX_CONNECTION_REACHED, "IWLAN_MAX_CONNECTION_REACHED");
+        sFailCauseMap.put(IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION,
+                "IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION");
+        sFailCauseMap.put(IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION,
+                "IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION");
+        sFailCauseMap.put(IWLAN_SEMANTIC_ERRORS_IN_PACKET_FILTERS,
+                "IWLAN_SEMANTIC_ERRORS_IN_PACKET_FILTERS");
+        sFailCauseMap.put(IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS,
+                "IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS");
+        sFailCauseMap.put(IWLAN_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED,
+                "IWLAN_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED");
+        sFailCauseMap.put(IWLAN_USER_UNKNOWN, "IWLAN_USER_UNKNOWN");
+        sFailCauseMap.put(IWLAN_NO_APN_SUBSCRIPTION, "IWLAN_NO_APN_SUBSCRIPTION");
+        sFailCauseMap.put(IWLAN_AUTHORIZATION_REJECTED, "IWLAN_AUTHORIZATION_REJECTED");
+        sFailCauseMap.put(IWLAN_ILLEGAL_ME, "IWLAN_ILLEGAL_ME");
+        sFailCauseMap.put(IWLAN_NETWORK_FAILURE, "IWLAN_NETWORK_FAILURE");
+        sFailCauseMap.put(IWLAN_RAT_TYPE_NOT_ALLOWED, "IWLAN_RAT_TYPE_NOT_ALLOWED");
+        sFailCauseMap.put(IWLAN_IMEI_NOT_ACCEPTED, "IWLAN_IMEI_NOT_ACCEPTED");
+        sFailCauseMap.put(IWLAN_PLMN_NOT_ALLOWED, "IWLAN_PLMN_NOT_ALLOWED");
+        sFailCauseMap.put(IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED,
+                "IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED");
+        sFailCauseMap.put(IWLAN_IKEV2_CONFIG_FAILURE, "IWLAN_IKEV2_CONFIG_FAILURE");
+        sFailCauseMap.put(IWLAN_IKEV2_AUTH_FAILURE, "IWLAN_IKEV2_AUTH_FAILURE");
+        sFailCauseMap.put(IWLAN_IKEV2_MSG_TIMEOUT, "IWLAN_IKEV2_MSG_TIMEOUT");
+        sFailCauseMap.put(IWLAN_IKEV2_CERT_INVALID, "IWLAN_IKEV2_CERT_INVALID");
+        sFailCauseMap.put(IWLAN_DNS_RESOLUTION_NAME_FAILURE, "IWLAN_DNS_RESOLUTION_NAME_FAILURE");
+        sFailCauseMap.put(IWLAN_DNS_RESOLUTION_TIMEOUT, "IWLAN_DNS_RESOLUTION_TIMEOUT");
         sFailCauseMap.put(OEM_DCFAILCAUSE_1, "OEM_DCFAILCAUSE_1");
         sFailCauseMap.put(OEM_DCFAILCAUSE_2, "OEM_DCFAILCAUSE_2");
         sFailCauseMap.put(OEM_DCFAILCAUSE_3, "OEM_DCFAILCAUSE_3");
diff --git a/test-mock/src/android/test/mock/OWNERS b/test-mock/src/android/test/mock/OWNERS
new file mode 100644
index 0000000..9551245
--- /dev/null
+++ b/test-mock/src/android/test/mock/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+*
diff --git a/tests/Compatibility/Android.bp b/tests/Compatibility/Android.bp
index 7dc44fa..c14e705 100644
--- a/tests/Compatibility/Android.bp
+++ b/tests/Compatibility/Android.bp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-android_test {
+android_test_helper_app {
     name: "AppCompatibilityTest",
     static_libs: ["androidx.test.rules"],
     // Include all test java files.
diff --git a/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt b/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt
new file mode 100644
index 0000000..6770066
--- /dev/null
+++ b/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+package android.net
+
+import android.net.InetAddresses.parseNumericAddress
+import android.os.Build
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.assertFieldCountEquals
+import com.android.testutils.assertParcelSane
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.net.InetAddress
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
+import kotlin.test.assertTrue
+
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) // TcpKeepalivePacketData added to SDK in S
+class TcpKeepalivePacketDataTest {
+    private fun makeData(
+        srcAddress: InetAddress = parseNumericAddress("192.0.2.123"),
+        srcPort: Int = 1234,
+        dstAddress: InetAddress = parseNumericAddress("192.0.2.231"),
+        dstPort: Int = 4321,
+        data: ByteArray = byteArrayOf(1, 2, 3),
+        tcpSeq: Int = 135,
+        tcpAck: Int = 246,
+        tcpWnd: Int = 1234,
+        tcpWndScale: Int = 2,
+        ipTos: Int = 0x12,
+        ipTtl: Int = 10
+    ) = TcpKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, data, tcpSeq, tcpAck,
+            tcpWnd, tcpWndScale, ipTos, ipTtl)
+
+    @Test
+    fun testEquals() {
+        val data1 = makeData()
+        val data2 = makeData()
+        assertEquals(data1, data2)
+        assertEquals(data1.hashCode(), data2.hashCode())
+    }
+
+    @Test
+    fun testNotEquals() {
+        assertNotEquals(makeData(srcAddress = parseNumericAddress("192.0.2.124")), makeData())
+        assertNotEquals(makeData(srcPort = 1235), makeData())
+        assertNotEquals(makeData(dstAddress = parseNumericAddress("192.0.2.232")), makeData())
+        assertNotEquals(makeData(dstPort = 4322), makeData())
+        // .equals does not test .packet, as it should be generated from the other fields
+        assertNotEquals(makeData(tcpSeq = 136), makeData())
+        assertNotEquals(makeData(tcpAck = 247), makeData())
+        assertNotEquals(makeData(tcpWnd = 1235), makeData())
+        assertNotEquals(makeData(tcpWndScale = 3), makeData())
+        assertNotEquals(makeData(ipTos = 0x14), makeData())
+        assertNotEquals(makeData(ipTtl = 11), makeData())
+
+        // Update above assertions if field is added
+        assertFieldCountEquals(5, KeepalivePacketData::class.java)
+        assertFieldCountEquals(6, TcpKeepalivePacketData::class.java)
+    }
+
+    @Test
+    fun testParcelUnparcel() {
+        assertParcelSane(makeData(), fieldCount = 6) { a, b ->
+            // .equals() does not verify .packet
+            a == b && a.packet contentEquals b.packet
+        }
+    }
+
+    @Test
+    fun testToString() {
+        val data = makeData()
+        val str = data.toString()
+
+        assertTrue(str.contains(data.srcAddress.hostAddress))
+        assertTrue(str.contains(data.srcPort.toString()))
+        assertTrue(str.contains(data.dstAddress.hostAddress))
+        assertTrue(str.contains(data.dstPort.toString()))
+        // .packet not included in toString()
+        assertTrue(str.contains(data.tcpSeq.toString()))
+        assertTrue(str.contains(data.tcpAck.toString()))
+        assertTrue(str.contains(data.tcpWindow.toString()))
+        assertTrue(str.contains(data.tcpWindowScale.toString()))
+        assertTrue(str.contains(data.ipTos.toString()))
+        assertTrue(str.contains(data.ipTtl.toString()))
+
+        // Update above assertions if field is added
+        assertFieldCountEquals(5, KeepalivePacketData::class.java)
+        assertFieldCountEquals(6, TcpKeepalivePacketData::class.java)
+    }
+}
\ No newline at end of file
diff --git a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java b/tests/net/java/android/net/KeepalivePacketDataUtilTest.java
similarity index 65%
rename from tests/net/java/android/net/TcpKeepalivePacketDataTest.java
rename to tests/net/java/android/net/KeepalivePacketDataUtilTest.java
index c5b25bd..fc739fb 100644
--- a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
+++ b/tests/net/java/android/net/KeepalivePacketDataUtilTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -20,8 +20,11 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.net.util.KeepalivePacketDataUtil;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -31,7 +34,7 @@
 import java.nio.ByteBuffer;
 
 @RunWith(JUnit4.class)
-public final class TcpKeepalivePacketDataTest {
+public final class KeepalivePacketDataUtilTest {
     private static final byte[] IPV4_KEEPALIVE_SRC_ADDR = {10, 0, 0, 1};
     private static final byte[] IPV4_KEEPALIVE_DST_ADDR = {10, 0, 0, 5};
 
@@ -39,7 +42,7 @@
     public void setUp() {}
 
     @Test
-    public void testV4TcpKeepalivePacket() throws Exception {
+    public void testFromTcpKeepaliveStableParcelable() throws Exception {
         final int srcPort = 1234;
         final int dstPort = 4321;
         final int seq = 0x11111111;
@@ -61,7 +64,7 @@
         testInfo.tos = tos;
         testInfo.ttl = ttl;
         try {
-            resultData = TcpKeepalivePacketData.tcpKeepalivePacket(testInfo);
+            resultData = KeepalivePacketDataUtil.fromStableParcelable(testInfo);
         } catch (InvalidPacketException e) {
             fail("InvalidPacketException: " + e);
         }
@@ -72,8 +75,8 @@
         assertEquals(testInfo.dstPort, resultData.getDstPort());
         assertEquals(testInfo.seq, resultData.tcpSeq);
         assertEquals(testInfo.ack, resultData.tcpAck);
-        assertEquals(testInfo.rcvWnd, resultData.tcpWnd);
-        assertEquals(testInfo.rcvWndScale, resultData.tcpWndScale);
+        assertEquals(testInfo.rcvWnd, resultData.tcpWindow);
+        assertEquals(testInfo.rcvWndScale, resultData.tcpWindowScale);
         assertEquals(testInfo.tos, resultData.ipTos);
         assertEquals(testInfo.ttl, resultData.ipTtl);
 
@@ -113,7 +116,7 @@
     //TODO: add ipv6 test when ipv6 supported
 
     @Test
-    public void testParcel() throws Exception {
+    public void testToTcpKeepaliveStableParcelable() throws Exception {
         final int srcPort = 1234;
         final int dstPort = 4321;
         final int sequence = 0x11111111;
@@ -135,8 +138,8 @@
         testInfo.ttl = ttl;
         TcpKeepalivePacketData testData = null;
         TcpKeepalivePacketDataParcelable resultData = null;
-        testData = TcpKeepalivePacketData.tcpKeepalivePacket(testInfo);
-        resultData = testData.toStableParcelable();
+        testData = KeepalivePacketDataUtil.fromStableParcelable(testInfo);
+        resultData = KeepalivePacketDataUtil.toStableParcelable(testData);
         assertArrayEquals(resultData.srcAddress, IPV4_KEEPALIVE_SRC_ADDR);
         assertArrayEquals(resultData.dstAddress, IPV4_KEEPALIVE_DST_ADDR);
         assertEquals(resultData.srcPort, srcPort);
@@ -154,4 +157,49 @@
                 + " ack: 572662306, rcvWnd: 48000, rcvWndScale: 2, tos: 4, ttl: 64}";
         assertEquals(expected, resultData.toString());
     }
+
+    @Test
+    public void testParseTcpKeepalivePacketData() throws Exception {
+        final int srcPort = 1234;
+        final int dstPort = 4321;
+        final int sequence = 0x11111111;
+        final int ack = 0x22222222;
+        final int wnd = 4800;
+        final int wndScale = 2;
+        final int tos = 4;
+        final int ttl = 64;
+        final TcpKeepalivePacketDataParcelable testParcel = new TcpKeepalivePacketDataParcelable();
+        testParcel.srcAddress = IPV4_KEEPALIVE_SRC_ADDR;
+        testParcel.srcPort = srcPort;
+        testParcel.dstAddress = IPV4_KEEPALIVE_DST_ADDR;
+        testParcel.dstPort = dstPort;
+        testParcel.seq = sequence;
+        testParcel.ack = ack;
+        testParcel.rcvWnd = wnd;
+        testParcel.rcvWndScale = wndScale;
+        testParcel.tos = tos;
+        testParcel.ttl = ttl;
+
+        final KeepalivePacketData testData =
+                KeepalivePacketDataUtil.fromStableParcelable(testParcel);
+        final TcpKeepalivePacketDataParcelable parsedParcelable =
+                KeepalivePacketDataUtil.parseTcpKeepalivePacketData(testData);
+        final TcpKeepalivePacketData roundTripData =
+                KeepalivePacketDataUtil.fromStableParcelable(parsedParcelable);
+
+        // Generated packet is the same, but rcvWnd / wndScale will differ if scale is non-zero
+        assertTrue(testData.getPacket().length > 0);
+        assertArrayEquals(testData.getPacket(), roundTripData.getPacket());
+
+        testParcel.rcvWndScale = 0;
+        final KeepalivePacketData noScaleTestData =
+                KeepalivePacketDataUtil.fromStableParcelable(testParcel);
+        final TcpKeepalivePacketDataParcelable noScaleParsedParcelable =
+                KeepalivePacketDataUtil.parseTcpKeepalivePacketData(noScaleTestData);
+        final TcpKeepalivePacketData noScaleRoundTripData =
+                KeepalivePacketDataUtil.fromStableParcelable(noScaleParsedParcelable);
+        assertEquals(noScaleTestData, noScaleRoundTripData);
+        assertTrue(noScaleTestData.getPacket().length > 0);
+        assertArrayEquals(noScaleTestData.getPacket(), noScaleRoundTripData.getPacket());
+    }
 }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 9d308c5..8c403f1 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -188,6 +188,7 @@
 import android.net.RouteInfoParcel;
 import android.net.SocketKeepalive;
 import android.net.UidRange;
+import android.net.UidRangeParcel;
 import android.net.Uri;
 import android.net.VpnManager;
 import android.net.metrics.IpConnectivityLog;
@@ -1055,7 +1056,7 @@
 
         public MockVpn(int userId) {
             super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
-                    userId, mock(KeyStore.class));
+                    mMockNetd, userId, mock(KeyStore.class));
             mConfig = new VpnConfig();
         }
 
@@ -1094,10 +1095,11 @@
             mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
                     mNetworkCapabilities);
             mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
-            verify(mNetworkManagementService, times(1))
-                    .addVpnUidRanges(eq(mMockVpn.getNetId()), eq(uids.toArray(new UidRange[0])));
-            verify(mNetworkManagementService, never())
-                    .removeVpnUidRanges(eq(mMockVpn.getNetId()), any());
+
+            verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetId()),
+                    eq(toUidRangeStableParcels(uids)));
+            verify(mMockNetd, never())
+                    .networkRemoveUidRanges(eq(mMockVpn.getNetId()), any());
             mAgentRegistered = true;
             mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
             mNetworkAgent = mMockNetworkAgent.getNetworkAgent();
@@ -1169,6 +1171,11 @@
         }
     }
 
+    private UidRangeParcel[] toUidRangeStableParcels(final @NonNull Set<UidRange> ranges) {
+        return ranges.stream().map(
+                r -> new UidRangeParcel(r.start, r.stop)).toArray(UidRangeParcel[]::new);
+    }
+
     private void mockVpn(int uid) {
         synchronized (mService.mVpns) {
             int userId = UserHandle.getUserId(uid);
@@ -4947,8 +4954,8 @@
         expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
         reset(mStatsService);
 
-        // Captive portal change shouldn't update ifaces
-        mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
+        // Temp metered change shouldn't update ifaces
+        mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED);
         waitForIdle();
         verify(mStatsService, never())
                 .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
@@ -5464,6 +5471,7 @@
         final Network wifi = mWiFiNetworkAgent.getNetwork();
 
         final NetworkCapabilities initialCaps = new NetworkCapabilities();
+        initialCaps.addTransportType(TRANSPORT_VPN);
         initialCaps.addCapability(NET_CAPABILITY_INTERNET);
         initialCaps.removeCapability(NET_CAPABILITY_NOT_VPN);
 
@@ -5495,44 +5503,45 @@
         withWifiAndMobileUnderlying.setLinkDownstreamBandwidthKbps(10);
         withWifiAndMobileUnderlying.setLinkUpstreamBandwidthKbps(20);
 
+        final NetworkCapabilities initialCapsNotMetered = new NetworkCapabilities(initialCaps);
+        initialCapsNotMetered.addCapability(NET_CAPABILITY_NOT_METERED);
+
         NetworkCapabilities caps = new NetworkCapabilities(initialCaps);
-        final boolean notDeclaredMetered = false;
-        mService.applyUnderlyingCapabilities(new Network[]{}, caps, notDeclaredMetered);
+        mService.applyUnderlyingCapabilities(new Network[]{}, initialCapsNotMetered, caps);
         assertEquals(withNoUnderlying, caps);
 
         caps = new NetworkCapabilities(initialCaps);
-        mService.applyUnderlyingCapabilities(new Network[]{null}, caps, notDeclaredMetered);
+        mService.applyUnderlyingCapabilities(new Network[]{null}, initialCapsNotMetered, caps);
         assertEquals(withNoUnderlying, caps);
 
         caps = new NetworkCapabilities(initialCaps);
-        mService.applyUnderlyingCapabilities(new Network[]{mobile}, caps, notDeclaredMetered);
+        mService.applyUnderlyingCapabilities(new Network[]{mobile}, initialCapsNotMetered, caps);
         assertEquals(withMobileUnderlying, caps);
 
-        mService.applyUnderlyingCapabilities(new Network[]{wifi}, caps, notDeclaredMetered);
+        mService.applyUnderlyingCapabilities(new Network[]{wifi}, initialCapsNotMetered, caps);
         assertEquals(withWifiUnderlying, caps);
 
-        final boolean isDeclaredMetered = true;
         withWifiUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED);
         caps = new NetworkCapabilities(initialCaps);
-        mService.applyUnderlyingCapabilities(new Network[]{wifi}, caps, isDeclaredMetered);
+        mService.applyUnderlyingCapabilities(new Network[]{wifi}, initialCaps, caps);
         assertEquals(withWifiUnderlying, caps);
 
         caps = new NetworkCapabilities(initialCaps);
-        mService.applyUnderlyingCapabilities(new Network[]{mobile, wifi}, caps, isDeclaredMetered);
+        mService.applyUnderlyingCapabilities(new Network[]{mobile, wifi}, initialCaps, caps);
         assertEquals(withWifiAndMobileUnderlying, caps);
 
         withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED);
         caps = new NetworkCapabilities(initialCaps);
         mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
-                caps, notDeclaredMetered);
+                initialCapsNotMetered, caps);
         assertEquals(withWifiAndMobileUnderlying, caps);
 
         caps = new NetworkCapabilities(initialCaps);
         mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
-                caps, notDeclaredMetered);
+                initialCapsNotMetered, caps);
         assertEquals(withWifiAndMobileUnderlying, caps);
 
-        mService.applyUnderlyingCapabilities(null, caps, notDeclaredMetered);
+        mService.applyUnderlyingCapabilities(null, initialCapsNotMetered, caps);
         assertEquals(withWifiUnderlying, caps);
     }
 
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 337507a..6e380be 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -58,6 +58,7 @@
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
+import android.net.INetd;
 import android.net.Ikev2VpnProfile;
 import android.net.InetAddresses;
 import android.net.IpPrefix;
@@ -70,6 +71,7 @@
 import android.net.NetworkInfo.DetailedState;
 import android.net.RouteInfo;
 import android.net.UidRange;
+import android.net.UidRangeParcel;
 import android.net.VpnManager;
 import android.net.VpnService;
 import android.net.ipsec.ike.IkeSessionCallback;
@@ -172,11 +174,13 @@
             mPackages.put(PKGS[i], PKG_UIDS[i]);
         }
     }
+    private static final UidRange PRI_USER_RANGE = UidRange.createForUser(primaryUser.id);
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext;
     @Mock private UserManager mUserManager;
     @Mock private PackageManager mPackageManager;
     @Mock private INetworkManagementService mNetService;
+    @Mock private INetd mNetd;
     @Mock private AppOpsManager mAppOps;
     @Mock private NotificationManager mNotificationManager;
     @Mock private Vpn.SystemServices mSystemServices;
@@ -256,8 +260,7 @@
                 null, null);
 
         assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
-            UidRange.createForUser(primaryUser.id),
-            UidRange.createForUser(restrictedProfileA.id)
+                PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id)
         })), ranges);
     }
 
@@ -269,9 +272,7 @@
         final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
                 null, null);
 
-        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
-            UidRange.createForUser(primaryUser.id)
-        })), ranges);
+        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] { PRI_USER_RANGE })), ranges);
     }
 
     @Test
@@ -282,15 +283,13 @@
         final Set<UidRange> ranges = new ArraySet<>();
         vpn.addUserToRanges(ranges, primaryUser.id, null, null);
 
-        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
-            UidRange.createForUser(primaryUser.id)
-        })), ranges);
+        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] { PRI_USER_RANGE })), ranges);
     }
 
     @Test
     public void testUidAllowAndDenylist() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
-        final UidRange user = UidRange.createForUser(primaryUser.id);
+        final UidRange user = PRI_USER_RANGE;
         final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
 
         // Allowed list
@@ -339,62 +338,67 @@
     @Test
     public void testLockdownChangingPackage() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
-        final UidRange user = UidRange.createForUser(primaryUser.id);
+        final UidRange user = PRI_USER_RANGE;
 
         // Default state.
-        assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
+        assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1],
+                user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
 
         // Set always-on without lockdown.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null, mKeyStore));
-        assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
+        assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1],
+                user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
 
         // Set always-on with lockdown.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null, mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-            new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
-            new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
         }));
-        assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
+
+        assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2],
+                user.start + PKG_UIDS[3]);
         assertUnblocked(vpn, user.start + PKG_UIDS[1]);
 
         // Switch to another app.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
-            new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
-            new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
         }));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-            new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
-            new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
+
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
         }));
-        assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
+        assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1],
+                user.start + PKG_UIDS[2]);
         assertUnblocked(vpn, user.start + PKG_UIDS[3]);
     }
 
     @Test
     public void testLockdownAllowlist() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
-        final UidRange user = UidRange.createForUser(primaryUser.id);
+        final UidRange user = PRI_USER_RANGE;
 
         // Set always-on with lockdown and allow app PKGS[2] from lockdown.
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[1], true, Collections.singletonList(PKGS[2]), mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-                new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
-                new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
         }));
         assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]);
         assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
-
         // Change allowed app list to PKGS[3].
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[1], true, Collections.singletonList(PKGS[3]), mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
-                new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
         }));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-                new UidRange(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1),
-                new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
         }));
         assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2]);
         assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[3]);
@@ -402,25 +406,25 @@
         // Change the VPN app.
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[0], true, Collections.singletonList(PKGS[3]), mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
-                new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
-                new UidRange(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1)
+        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1)
         }));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-                new UidRange(user.start, user.start + PKG_UIDS[0] - 1),
-                new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1)
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start, user.start + PKG_UIDS[0] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1)
         }));
         assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
         assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]);
 
         // Remove the list of allowed packages.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null, mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
-                new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1),
-                new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
         }));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-                new UidRange(user.start + PKG_UIDS[0] + 1, user.stop),
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.stop),
         }));
         assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2],
                 user.start + PKG_UIDS[3]);
@@ -429,12 +433,12 @@
         // Add the list of allowed packages.
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[0], true, Collections.singletonList(PKGS[1]), mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
-                new UidRange(user.start + PKG_UIDS[0] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.stop)
         }));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-                new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
-                new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
         }));
         assertBlocked(vpn, user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
         assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1]);
@@ -447,13 +451,13 @@
         // allowed package should change from PGKS[1] to PKGS[2].
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app"), mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[]{
-                new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
-                new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[]{
+                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
         }));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[]{
-                new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[2] - 1),
-                new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[]{
+                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[2] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
         }));
     }
 
@@ -467,86 +471,86 @@
                 restrictedProfileA.flags);
         tempProfile.restrictedProfileParentId = primaryUser.id;
 
-        final UidRange user = UidRange.createForUser(primaryUser.id);
+        final UidRange user = PRI_USER_RANGE;
         final UidRange profile = UidRange.createForUser(tempProfile.id);
 
         // Set lockdown.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-            new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
-            new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
         }));
-
         // Verify restricted user isn't affected at first.
         assertUnblocked(vpn, profile.start + PKG_UIDS[0]);
 
         // Add the restricted user.
         setMockedUsers(primaryUser, tempProfile);
         vpn.onUserAdded(tempProfile.id);
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-            new UidRange(profile.start, profile.start + PKG_UIDS[3] - 1),
-            new UidRange(profile.start + PKG_UIDS[3] + 1, profile.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(profile.start, profile.start + PKG_UIDS[3] - 1),
+                new UidRangeParcel(profile.start + PKG_UIDS[3] + 1, profile.stop)
         }));
 
         // Remove the restricted user.
         tempProfile.partial = true;
         vpn.onUserRemoved(tempProfile.id);
-        verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
-            new UidRange(profile.start, profile.start + PKG_UIDS[3] - 1),
-            new UidRange(profile.start + PKG_UIDS[3] + 1, profile.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(profile.start, profile.start + PKG_UIDS[3] - 1),
+                new UidRangeParcel(profile.start + PKG_UIDS[3] + 1, profile.stop)
         }));
     }
 
     @Test
     public void testLockdownRuleRepeatability() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
-
+        final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] {
+                new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)};
         // Given legacy lockdown is already enabled,
         vpn.setLockdown(true);
-        verify(mNetService, times(1)).setAllowOnlyVpnForUids(
-                eq(true), aryEq(new UidRange[] {UidRange.createForUser(primaryUser.id)}));
+
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(primaryUserRangeParcel));
 
         // Enabling legacy lockdown twice should do nothing.
         vpn.setLockdown(true);
-        verify(mNetService, times(1)).setAllowOnlyVpnForUids(anyBoolean(), any(UidRange[].class));
+        verify(mNetd, times(1))
+                .networkRejectNonSecureVpn(anyBoolean(), any(UidRangeParcel[].class));
 
         // And disabling should remove the rules exactly once.
         vpn.setLockdown(false);
-        verify(mNetService, times(1)).setAllowOnlyVpnForUids(
-                eq(false), aryEq(new UidRange[] {UidRange.createForUser(primaryUser.id)}));
+        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(primaryUserRangeParcel));
 
         // Removing the lockdown again should have no effect.
         vpn.setLockdown(false);
-        verify(mNetService, times(2)).setAllowOnlyVpnForUids(anyBoolean(), any(UidRange[].class));
+        verify(mNetd, times(2)).networkRejectNonSecureVpn(
+                anyBoolean(), any(UidRangeParcel[].class));
     }
 
     @Test
     public void testLockdownRuleReversibility() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
-
-        final UidRange[] entireUser = {
-            UidRange.createForUser(primaryUser.id)
+        final UidRangeParcel[] entireUser = {
+            new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)
         };
-        final UidRange[] exceptPkg0 = {
-            new UidRange(entireUser[0].start, entireUser[0].start + PKG_UIDS[0] - 1),
-            new UidRange(entireUser[0].start + PKG_UIDS[0] + 1, entireUser[0].stop)
+        final UidRangeParcel[] exceptPkg0 = {
+            new UidRangeParcel(entireUser[0].start, entireUser[0].start + PKG_UIDS[0] - 1),
+            new UidRangeParcel(entireUser[0].start + PKG_UIDS[0] + 1, entireUser[0].stop)
         };
 
-        final InOrder order = inOrder(mNetService);
+        final InOrder order = inOrder(mNetd);
 
         // Given lockdown is enabled with no package (legacy VPN),
         vpn.setLockdown(true);
-        order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
+        order.verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(entireUser));
 
         // When a new VPN package is set the rules should change to cover that package.
         vpn.prepare(null, PKGS[0], VpnManager.TYPE_VPN_SERVICE);
-        order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(entireUser));
-        order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(exceptPkg0));
+        order.verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(entireUser));
+        order.verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(exceptPkg0));
 
         // When that VPN package is unset, everything should be undone again in reverse.
         vpn.prepare(null, VpnConfig.LEGACY_VPN, VpnManager.TYPE_VPN_SERVICE);
-        order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(exceptPkg0));
-        order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
+        order.verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(exceptPkg0));
+        order.verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(entireUser));
     }
 
     @Test
@@ -1186,7 +1190,7 @@
                 .thenReturn(asUserContext);
         final TestLooper testLooper = new TestLooper();
         final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService,
-                userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
+                mNetd, userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
         verify(mConnectivityManager, times(1)).registerNetworkProvider(argThat(
                 provider -> provider.getName().contains("VpnNetworkProvider")
         ));
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index ab6dced..dd3ebdb 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -519,7 +519,7 @@
                         String8(parser.getElementName(&len)).string(), attr);
                 return ATTR_NOT_FOUND;
             }
-            if ((str=pool->stringAt(value.data, &len)) == NULL) {
+            if ((str = UnpackOptionalString(pool->stringAt(value.data), &len)) == NULL) {
                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n",
                         path.string(), parser.getLineNumber(),
                         String8(parser.getElementName(&len)).string(), attr);
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index d02f44e..257e96b 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -3066,7 +3066,7 @@
         for (size_t ti=0; ti<N; ti++) {
             // Retrieve them in the same order as the type string block.
             size_t len;
-            String16 typeName(p->getTypeStrings().stringAt(ti, &len));
+            String16 typeName(UnpackOptionalString(p->getTypeStrings().stringAt(ti), &len));
             sp<Type> t = p->getTypes().valueFor(typeName);
             LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"),
                                 "Type name %s not found",
@@ -4169,7 +4169,7 @@
         const size_t N = strings->size();
         for (size_t i=0; i<N; i++) {
             size_t len;
-            mappings->add(String16(strings->stringAt(i, &len)), i);
+            mappings->add(String16(UnpackOptionalString(strings->stringAt(i), &len)), i);
         }
     }
     return err;
diff --git a/tools/aapt/StringPool.cpp b/tools/aapt/StringPool.cpp
index 37b61bf..6cacd32 100644
--- a/tools/aapt/StringPool.cpp
+++ b/tools/aapt/StringPool.cpp
@@ -52,9 +52,9 @@
     for (size_t i=0; i<N; i++) {
         size_t len;
         if (pool->isUTF8()) {
-            uniqueStrings.add(pool->string8At(i, &len));
+            uniqueStrings.add(UnpackOptionalString(pool->string8At(i), &len));
         } else {
-            uniqueStrings.add(pool->stringAt(i, &len));
+            uniqueStrings.add(UnpackOptionalString(pool->stringAt(i), &len));
         }
     }
 
@@ -66,8 +66,8 @@
 
     const size_t NS = pool->size();
     for (size_t s=0; s<NS; s++) {
-        String8 str = pool->string8ObjectAt(s);
-        printf("String #" ZD ": %s\n", (ZD_TYPE) s, str.string());
+        auto str = pool->string8ObjectAt(s);
+        printf("String #" ZD ": %s\n", (ZD_TYPE) s, (str.has_value() ? str->string() : ""));
     }
 }
 
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 439f231..82da249 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -436,9 +436,9 @@
   for (size_t i=0; i<N; i++) {
     size_t len;
     if (pool->isUTF8()) {
-      uniqueStrings.add(pool->string8At(i, &len));
+      uniqueStrings.add(UnpackOptionalString(pool->string8At(i), &len));
     } else {
-      uniqueStrings.add(pool->stringAt(i, &len));
+      uniqueStrings.add(UnpackOptionalString(pool->stringAt(i), &len));
     }
   }
 
@@ -450,8 +450,8 @@
 
   const size_t NS = pool->size();
   for (size_t s=0; s<NS; s++) {
-    String8 str = pool->string8ObjectAt(s);
-    printer->Print(StringPrintf("String #%zd : %s\n", s, str.string()));
+    auto str = pool->string8ObjectAt(s);
+    printer->Print(StringPrintf("String #%zd : %s\n", s, str.has_value() ? str->string() : ""));
   }
 }
 
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 469128b..f26e995 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -751,10 +751,12 @@
   switch (res_value.dataType) {
     case android::Res_value::TYPE_STRING: {
       const std::string str = util::GetString(src_pool, data);
-      const android::ResStringPool_span* spans = src_pool.styleAt(data);
+      auto spans_result = src_pool.styleAt(data);
 
       // Check if the string has a valid style associated with it.
-      if (spans != nullptr && spans->name.index != android::ResStringPool_span::END) {
+      if (spans_result.has_value() &&
+          (*spans_result)->name.index != android::ResStringPool_span::END) {
+        const android::ResStringPool_span* spans = spans_result->unsafe_ptr();
         StyleString style_str = {str};
         while (spans->name.index != android::ResStringPool_span::END) {
           style_str.spans.push_back(Span{util::GetString(src_pool, spans->name.index),
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index 9a7238b..6e5200b 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -223,11 +223,11 @@
   std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
   ResStringPool test;
   ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
-  size_t len = 0;
-  const char16_t* str = test.stringAt(0, &len);
-  EXPECT_THAT(len, Eq(1u));
-  EXPECT_THAT(str, Pointee(Eq(u'\u093f')));
-  EXPECT_THAT(str[1], Eq(0u));
+  auto str = test.stringAt(0);
+  ASSERT_TRUE(str.has_value());
+  EXPECT_THAT(str->size(), Eq(1u));
+  EXPECT_THAT(str->data(), Pointee(Eq(u'\u093f')));
+  EXPECT_THAT(str->data()[1], Eq(0u));
 }
 
 constexpr const char* sLongString =
@@ -278,14 +278,15 @@
     EXPECT_THAT(util::GetString(test, 3), Eq(sLongString));
     EXPECT_THAT(util::GetString16(test, 3), Eq(util::Utf8ToUtf16(sLongString)));
 
-    size_t len;
-    EXPECT_TRUE(test.stringAt(4, &len) != nullptr || test.string8At(4, &len) != nullptr);
+    EXPECT_TRUE(test.stringAt(4).has_value() || test.string8At(4).has_value());
 
     EXPECT_THAT(util::GetString(test, 0), Eq("style"));
     EXPECT_THAT(util::GetString16(test, 0), Eq(u"style"));
 
-    const ResStringPool_span* span = test.styleAt(0);
-    ASSERT_THAT(span, NotNull());
+    auto span_result = test.styleAt(0);
+    ASSERT_TRUE(span_result.has_value());
+
+    const ResStringPool_span* span = span_result->unsafe_ptr();
     EXPECT_THAT(util::GetString(test, span->name.index), Eq("b"));
     EXPECT_THAT(util::GetString16(test, span->name.index), Eq(u"b"));
     EXPECT_THAT(span->firstChar, Eq(0u));
@@ -318,16 +319,17 @@
   // Check that the codepoints are encoded using two three-byte surrogate pairs
   ResStringPool test;
   ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
-  size_t len;
-  const char* str = test.string8At(0, &len);
-  ASSERT_THAT(str, NotNull());
-  EXPECT_THAT(std::string(str, len), Eq("\xED\xA0\x81\xED\xB0\x80"));
-  str = test.string8At(1, &len);
-  ASSERT_THAT(str, NotNull());
-  EXPECT_THAT(std::string(str, len), Eq("foo \xED\xA0\x81\xED\xB0\xB7 bar"));
-  str = test.string8At(2, &len);
-  ASSERT_THAT(str, NotNull());
-  EXPECT_THAT(std::string(str, len), Eq("\xED\xA0\x81\xED\xB0\x80\xED\xA0\x81\xED\xB0\xB7"));
+  auto str = test.string8At(0);
+  ASSERT_TRUE(str.has_value());
+  EXPECT_THAT(str->to_string(), Eq("\xED\xA0\x81\xED\xB0\x80"));
+
+  str = test.string8At(1);
+  ASSERT_TRUE(str.has_value());
+  EXPECT_THAT(str->to_string(), Eq("foo \xED\xA0\x81\xED\xB0\xB7 bar"));
+
+  str = test.string8At(2);
+  ASSERT_TRUE(str.has_value());
+  EXPECT_THAT(str->to_string(), Eq("\xED\xA0\x81\xED\xB0\x80\xED\xA0\x81\xED\xB0\xB7"));
 
   // Check that retrieving the strings returns the original UTF-8 character bytes
   EXPECT_THAT(util::GetString(test, 0), Eq("\xF0\x90\x90\x80"));
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index fb786a3..ae9f792 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -252,5 +252,4 @@
   AssertTranslations(this, "donottranslate", expected_not_translatable);
   AssertTranslations(this, "donottranslate_foo", expected_not_translatable);
 }
-
 }  // namespace aapt
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index fd12d02..faabe46 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -730,22 +730,6 @@
   return true;
 }
 
-static android::ApkAssetsCookie FindFrameworkAssetManagerCookie(
-    const android::AssetManager2& assets) {
-  using namespace android;
-
-  // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so
-  // we're looking for the first attribute resource in the system package.
-  Res_value val{};
-  ResTable_config config{};
-  uint32_t type_spec_flags;
-  ApkAssetsCookie idx = assets.GetResource(0x01010000, true /** may_be_bag */,
-                                           0 /** density_override */, &val, &config,
-                                           &type_spec_flags);
-
-  return idx;
-}
-
 class Linker {
  public:
   Linker(LinkContext* context, const LinkOptions& options)
@@ -758,8 +742,12 @@
   void ExtractCompileSdkVersions(android::AssetManager2* assets) {
     using namespace android;
 
-    android::ApkAssetsCookie cookie = FindFrameworkAssetManagerCookie(*assets);
-    if (cookie == android::kInvalidCookie) {
+    // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so
+    // we're looking for the first attribute resource in the system package.
+    android::ApkAssetsCookie cookie;
+    if (auto value = assets->GetResource(0x01010000, true /** may_be_bag */); value.has_value()) {
+      cookie = value->cookie;
+    } else {
       // No Framework assets loaded. Not a failure.
       return;
     }
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index 6932baf..f8b8a1c 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -189,16 +189,16 @@
                      ResTable_config::CONFIG_VERSION));
 
   std::u16string foo_str = u"foo";
-  ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
-  ASSERT_GE(idx, 0);
+  auto idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
+  ASSERT_TRUE(idx.has_value());
   EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {},
-                     Res_value::TYPE_STRING, (uint32_t)idx, 0u));
+                     Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
 
   std::u16string bar_path = u"res/layout/bar.xml";
   idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
-  ASSERT_GE(idx, 0);
+  ASSERT_TRUE(idx.has_value());
   EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/bar", ResourceId(0x7f050000), {},
-                     Res_value::TYPE_STRING, (uint32_t)idx, 0u));
+                     Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
 }
 
 TEST_F(TableFlattenerTest, FlattenEntriesWithGapsInIds) {
@@ -603,16 +603,16 @@
                      2u, ResTable_config::CONFIG_VERSION));
 
   std::u16string foo_str = u"foo";
-  ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
-  ASSERT_GE(idx, 0);
+  auto idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
+  ASSERT_TRUE(idx.has_value());
   EXPECT_TRUE(Exists(&res_table, "com.app.test:string/0_resource_name_obfuscated",
-                     ResourceId(0x7f040000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u));
+                     ResourceId(0x7f040000), {}, Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
 
   std::u16string bar_path = u"res/layout/bar.xml";
   idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
-  ASSERT_GE(idx, 0);
+  ASSERT_TRUE(idx.has_value());
   EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated",
-                     ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u));
+                     ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
 }
 
 TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithNameCollapseExemptionsSucceeds) {
@@ -659,16 +659,16 @@
                      2u, ResTable_config::CONFIG_VERSION));
 
   std::u16string foo_str = u"foo";
-  ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
-  ASSERT_GE(idx, 0);
+  auto idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
+  ASSERT_TRUE(idx.has_value());
   EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {},
-                     Res_value::TYPE_STRING, (uint32_t)idx, 0u));
+                     Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
 
   std::u16string bar_path = u"res/layout/bar.xml";
   idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
-  ASSERT_GE(idx, 0);
+  ASSERT_TRUE(idx.has_value());
   EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated",
-                     ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u));
+                     ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
 }
 
 TEST_F(TableFlattenerTest, FlattenOverlayable) {
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 897fa80..ad716c7 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -265,21 +265,22 @@
 
 static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable(
     android::AssetManager2& am, ResourceId id) {
+  using namespace android;
   if (am.GetApkAssets().empty()) {
     return {};
   }
 
-  const android::ResolvedBag* bag = am.GetBag(id.id);
-  if (bag == nullptr) {
+  auto bag_result = am.GetBag(id.id);
+  if (!bag_result.has_value()) {
     return nullptr;
   }
 
   // We found a resource.
   std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>(id);
-
+  const ResolvedBag* bag = *bag_result;
   const size_t count = bag->entry_count;
   for (uint32_t i = 0; i < count; i++) {
-    if (bag->entries[i].key == android::ResTable_map::ATTR_TYPE) {
+    if (bag->entries[i].key == ResTable_map::ATTR_TYPE) {
       s->attribute = std::make_shared<Attribute>(bag->entries[i].value.data);
       break;
     }
@@ -287,25 +288,25 @@
 
   if (s->attribute) {
     for (size_t i = 0; i < count; i++) {
-      const android::ResolvedBag::Entry& map_entry = bag->entries[i];
+      const ResolvedBag::Entry& map_entry = bag->entries[i];
       if (Res_INTERNALID(map_entry.key)) {
         switch (map_entry.key) {
-          case android::ResTable_map::ATTR_MIN:
+          case ResTable_map::ATTR_MIN:
             s->attribute->min_int = static_cast<int32_t>(map_entry.value.data);
             break;
-          case android::ResTable_map::ATTR_MAX:
+          case ResTable_map::ATTR_MAX:
             s->attribute->max_int = static_cast<int32_t>(map_entry.value.data);
             break;
         }
         continue;
       }
 
-      android::AssetManager2::ResourceName name;
-      if (!am.GetResourceName(map_entry.key, &name)) {
+      auto name = am.GetResourceName(map_entry.key);
+      if (!name.has_value()) {
         return nullptr;
       }
 
-      Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(name);
+      Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(*name);
       if (!parsed_name) {
         return nullptr;
       }
@@ -328,7 +329,7 @@
 
   bool found = false;
   ResourceId res_id = 0;
-  uint32_t type_spec_flags;
+  uint32_t type_spec_flags = 0;
   ResourceName real_name;
 
   // There can be mangled resources embedded within other packages. Here we will
@@ -340,8 +341,19 @@
       real_name.package = package_name;
     }
 
-    res_id = asset_manager_.GetResourceId(real_name.to_string());
-    if (res_id.is_valid_static() && asset_manager_.GetResourceFlags(res_id.id, &type_spec_flags)) {
+    auto real_res_id = asset_manager_.GetResourceId(real_name.to_string());
+    if (!real_res_id.has_value()) {
+      return true;
+    }
+
+    res_id.id = *real_res_id;
+    if (!res_id.is_valid_static()) {
+      return true;
+    }
+
+    auto value = asset_manager_.GetResource(res_id.id, true /* may_be_bag */);
+    if (value.has_value()) {
+      type_spec_flags = value->flags;
       found = true;
       return false;
     }
@@ -371,11 +383,11 @@
 
 static Maybe<ResourceName> GetResourceName(android::AssetManager2& am,
                                            ResourceId id) {
-  android::AssetManager2::ResourceName name;
-  if (!am.GetResourceName(id.id, &name)) {
+  auto name = am.GetResourceName(id.id);
+  if (!name.has_value()) {
     return {};
   }
-  return ResourceUtils::ToResourceName(name);
+  return ResourceUtils::ToResourceName(*name);
 }
 
 std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
@@ -394,9 +406,8 @@
     return {};
   }
 
-
-  uint32_t type_spec_flags = 0;
-  if (!asset_manager_.GetResourceFlags(id.id, &type_spec_flags)) {
+  auto value = asset_manager_.GetResource(id.id, true /* may_be_bag */);
+  if (!value.has_value()) {
     return {};
   }
 
@@ -411,7 +422,7 @@
   }
 
   if (s) {
-    s->is_public = (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
+    s->is_public = (value->flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
     return s;
   }
   return {};
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index 37ce65e..ef33c34 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -531,19 +531,15 @@
 }
 
 StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx) {
-  size_t len;
-  const char16_t* str = pool.stringAt(idx, &len);
-  if (str != nullptr) {
-    return StringPiece16(str, len);
+  if (auto str = pool.stringAt(idx)) {
+    return *str;
   }
   return StringPiece16();
 }
 
 std::string GetString(const android::ResStringPool& pool, size_t idx) {
-  size_t len;
-  const char* str = pool.string8At(idx, &len);
-  if (str != nullptr) {
-    return ModifiedUtf8ToUtf8(std::string(str, len));
+  if (auto str = pool.string8At(idx)) {
+    return ModifiedUtf8ToUtf8(str->to_string());
   }
   return Utf16ToUtf8(GetString16(pool, idx));
 }
diff --git a/tools/split-select/Main.cpp b/tools/split-select/Main.cpp
index d3eb012..e6966db 100644
--- a/tools/split-select/Main.cpp
+++ b/tools/split-select/Main.cpp
@@ -182,14 +182,18 @@
                 if (type >= Res_value::TYPE_FIRST_INT && type <= Res_value::TYPE_LAST_INT) {
                     outInfo.minSdkVersion = xml.getAttributeData(idx);
                 } else if (type == Res_value::TYPE_STRING) {
-                    String8 minSdk8(xml.getStrings().string8ObjectAt(idx));
-                    char* endPtr;
-                    int minSdk = strtol(minSdk8.string(), &endPtr, 10);
-                    if (endPtr != minSdk8.string() + minSdk8.size()) {
-                        fprintf(stderr, "warning: failed to parse android:minSdkVersion '%s'\n",
-                                minSdk8.string());
+                    auto minSdk8 = xml.getStrings().string8ObjectAt(idx);
+                    if (!minSdk8.has_value()) {
+                        fprintf(stderr, "warning: failed to retrieve android:minSdkVersion.\n");
                     } else {
-                        outInfo.minSdkVersion = minSdk;
+                        char *endPtr;
+                        int minSdk = strtol(minSdk8->string(), &endPtr, 10);
+                        if (endPtr != minSdk8->string() + minSdk8->size()) {
+                            fprintf(stderr, "warning: failed to parse android:minSdkVersion '%s'\n",
+                                    minSdk8->string());
+                        } else {
+                            outInfo.minSdkVersion = minSdk;
+                        }
                     }
                 } else {
                     fprintf(stderr, "warning: unrecognized value for android:minSdkVersion.\n");
diff --git a/tools/split-select/OWNERS b/tools/split-select/OWNERS
new file mode 100644
index 0000000..6c50ede
--- /dev/null
+++ b/tools/split-select/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/res/OWNERS
\ No newline at end of file
diff --git a/tools/stats_log_api_gen/.clang-format b/tools/stats_log_api_gen/.clang-format
deleted file mode 100644
index cead3a0..0000000
--- a/tools/stats_log_api_gen/.clang-format
+++ /dev/null
@@ -1,17 +0,0 @@
-BasedOnStyle: Google
-AllowShortIfStatementsOnASingleLine: true
-AllowShortFunctionsOnASingleLine: false
-AllowShortLoopsOnASingleLine: true
-BinPackArguments: true
-BinPackParameters: true
-ColumnLimit: 100
-CommentPragmas: NOLINT:.*
-ContinuationIndentWidth: 8
-DerivePointerAlignment: false
-IndentWidth: 4
-PointerAlignment: Left
-TabWidth: 4
-AccessModifierOffset: -4
-IncludeCategories:
-  - Regex:    '^"Log\.h"'
-    Priority:    -1
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
deleted file mode 100644
index c0893f7..0000000
--- a/tools/stats_log_api_gen/Android.bp
+++ /dev/null
@@ -1,134 +0,0 @@
-//
-// Copyright (C) 2017 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.
-//
-
-// ==========================================================
-// Build the host executable: stats-log-api-gen
-// ==========================================================
-cc_binary_host {
-    name: "stats-log-api-gen",
-    srcs: [
-        "Collation.cpp",
-        "java_writer.cpp",
-        "java_writer_q.cpp",
-        "main.cpp",
-        "native_writer.cpp",
-        "utils.cpp",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-
-    shared_libs: [
-        "libstats_proto_host",
-        "libprotobuf-cpp-full",
-        "libbase",
-    ],
-
-    proto: {
-        type: "full",
-    },
-}
-
-// ==========================================================
-// Build the host test executable: stats-log-api-gen
-// ==========================================================
-cc_test_host {
-    name: "stats-log-api-gen-test",
-    cflags: [
-        "-Wall",
-        "-Wextra",
-        "-Werror",
-        "-g",
-        "-DUNIT_TEST",
-    ],
-    srcs: [
-        "Collation.cpp",
-        "test_collation.cpp",
-        "test.proto",
-    ],
-
-    static_libs: [
-        "libgmock_host",
-    ],
-
-    shared_libs: [
-        "libstats_proto_host",
-        "libprotobuf-cpp-full",
-    ],
-
-    proto: {
-        type: "full",
-        include_dirs: [
-            "external/protobuf/src",
-        ],
-    },
-}
-
-// ==========================================================
-// Native library
-// ==========================================================
-genrule {
-    name: "statslog.h",
-    tools: ["stats-log-api-gen"],
-    cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog.h",
-    out: [
-        "statslog.h",
-    ],
-}
-
-genrule {
-    name: "statslog.cpp",
-    tools: ["stats-log-api-gen"],
-    cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog.cpp",
-    out: [
-        "statslog.cpp",
-    ],
-}
-
-cc_library {
-    name: "libstatslog",
-    host_supported: true,
-    generated_sources: [
-        "statslog.cpp",
-    ],
-    generated_headers: [
-        "statslog.h"
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-    export_generated_headers: [
-        "statslog.h"
-    ],
-    shared_libs: [
-        "liblog",
-        "libcutils",
-    ],
-    target: {
-        android: {
-            shared_libs: ["libstatssocket"],
-        },
-        host: {
-            static_libs: ["libstatssocket"],
-        },
-        darwin: {
-            enabled: false,
-        },
-    },
-}
-
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
deleted file mode 100644
index 2608097..0000000
--- a/tools/stats_log_api_gen/Collation.cpp
+++ /dev/null
@@ -1,576 +0,0 @@
-/*
- * Copyright (C) 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Collation.h"
-
-#include <stdio.h>
-
-#include <map>
-
-#include "frameworks/proto_logging/stats/atoms.pb.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-using google::protobuf::EnumDescriptor;
-using google::protobuf::FieldDescriptor;
-using google::protobuf::FileDescriptor;
-using google::protobuf::SourceLocation;
-using std::make_shared;
-using std::map;
-
-const bool dbg = false;
-
-//
-// AtomDecl class
-//
-
-AtomDecl::AtomDecl() : code(0), name() {
-}
-
-AtomDecl::AtomDecl(const AtomDecl& that)
-    : code(that.code),
-      name(that.name),
-      message(that.message),
-      fields(that.fields),
-      fieldNumberToAnnotations(that.fieldNumberToAnnotations),
-      primaryFields(that.primaryFields),
-      exclusiveField(that.exclusiveField),
-      defaultState(that.defaultState),
-      triggerStateReset(that.triggerStateReset),
-      nested(that.nested),
-      uidField(that.uidField) {
-}
-
-AtomDecl::AtomDecl(int c, const string& n, const string& m) : code(c), name(n), message(m) {
-}
-
-AtomDecl::~AtomDecl() {
-}
-
-/**
- * Print an error message for a FieldDescriptor, including the file name and
- * line number.
- */
-static void print_error(const FieldDescriptor* field, const char* format, ...) {
-    const Descriptor* message = field->containing_type();
-    const FileDescriptor* file = message->file();
-
-    SourceLocation loc;
-    if (field->GetSourceLocation(&loc)) {
-        // TODO: this will work if we can figure out how to pass
-        // --include_source_info to protoc
-        fprintf(stderr, "%s:%d: ", file->name().c_str(), loc.start_line);
-    } else {
-        fprintf(stderr, "%s: ", file->name().c_str());
-    }
-    va_list args;
-    va_start(args, format);
-    vfprintf(stderr, format, args);
-    va_end(args);
-}
-
-/**
- * Convert a protobuf type into a java type.
- */
-static java_type_t java_type(const FieldDescriptor* field) {
-    int protoType = field->type();
-    switch (protoType) {
-        case FieldDescriptor::TYPE_DOUBLE:
-            return JAVA_TYPE_DOUBLE;
-        case FieldDescriptor::TYPE_FLOAT:
-            return JAVA_TYPE_FLOAT;
-        case FieldDescriptor::TYPE_INT64:
-            return JAVA_TYPE_LONG;
-        case FieldDescriptor::TYPE_UINT64:
-            return JAVA_TYPE_LONG;
-        case FieldDescriptor::TYPE_INT32:
-            return JAVA_TYPE_INT;
-        case FieldDescriptor::TYPE_FIXED64:
-            return JAVA_TYPE_LONG;
-        case FieldDescriptor::TYPE_FIXED32:
-            return JAVA_TYPE_INT;
-        case FieldDescriptor::TYPE_BOOL:
-            return JAVA_TYPE_BOOLEAN;
-        case FieldDescriptor::TYPE_STRING:
-            return JAVA_TYPE_STRING;
-        case FieldDescriptor::TYPE_GROUP:
-            return JAVA_TYPE_UNKNOWN;
-        case FieldDescriptor::TYPE_MESSAGE:
-            // TODO: not the final package name
-            if (field->message_type()->full_name() == "android.os.statsd.AttributionNode") {
-                return JAVA_TYPE_ATTRIBUTION_CHAIN;
-            } else if (field->message_type()->full_name() == "android.os.statsd.KeyValuePair") {
-                return JAVA_TYPE_KEY_VALUE_PAIR;
-            } else if (field->options().GetExtension(os::statsd::log_mode) ==
-                       os::statsd::LogMode::MODE_BYTES) {
-                return JAVA_TYPE_BYTE_ARRAY;
-            } else {
-                return JAVA_TYPE_OBJECT;
-            }
-        case FieldDescriptor::TYPE_BYTES:
-            return JAVA_TYPE_BYTE_ARRAY;
-        case FieldDescriptor::TYPE_UINT32:
-            return JAVA_TYPE_INT;
-        case FieldDescriptor::TYPE_ENUM:
-            return JAVA_TYPE_ENUM;
-        case FieldDescriptor::TYPE_SFIXED32:
-            return JAVA_TYPE_INT;
-        case FieldDescriptor::TYPE_SFIXED64:
-            return JAVA_TYPE_LONG;
-        case FieldDescriptor::TYPE_SINT32:
-            return JAVA_TYPE_INT;
-        case FieldDescriptor::TYPE_SINT64:
-            return JAVA_TYPE_LONG;
-        default:
-            return JAVA_TYPE_UNKNOWN;
-    }
-}
-
-/**
- * Gather the enums info.
- */
-void collate_enums(const EnumDescriptor& enumDescriptor, AtomField* atomField) {
-    for (int i = 0; i < enumDescriptor.value_count(); i++) {
-        atomField->enumValues[enumDescriptor.value(i)->number()] =
-                enumDescriptor.value(i)->name().c_str();
-    }
-}
-
-static void addAnnotationToAtomDecl(AtomDecl* atomDecl, const int fieldNumber,
-                                    const AnnotationId annotationId,
-                                    const AnnotationType annotationType,
-                                    const AnnotationValue annotationValue) {
-    if (dbg) {
-        printf("   Adding annotation to %s: [%d] = {id: %d, type: %d}\n", atomDecl->name.c_str(),
-               fieldNumber, annotationId, annotationType);
-    }
-    atomDecl->fieldNumberToAnnotations[fieldNumber].insert(
-            make_shared<Annotation>(annotationId, atomDecl->code, annotationType, annotationValue));
-}
-
-static int collate_field_annotations(AtomDecl* atomDecl, const FieldDescriptor* field,
-                                     const int fieldNumber, const java_type_t& javaType) {
-    int errorCount = 0;
-
-    if (field->options().HasExtension(os::statsd::state_field_option)) {
-        const os::statsd::StateAtomFieldOption& stateFieldOption =
-                field->options().GetExtension(os::statsd::state_field_option);
-        const bool primaryField = stateFieldOption.primary_field();
-        const bool exclusiveState = stateFieldOption.exclusive_state();
-        const bool primaryFieldFirstUid = stateFieldOption.primary_field_first_uid();
-
-        // Check the field is only one of primaryField, exclusiveState, or primaryFieldFirstUid.
-        if (primaryField + primaryFieldFirstUid + exclusiveState > 1) {
-            print_error(field,
-                        "Field can be max 1 of primary_field, exclusive_state, "
-                        "or primary_field_first_uid: '%s'\n",
-                        atomDecl->message.c_str());
-            errorCount++;
-        }
-
-        if (primaryField) {
-            if (javaType == JAVA_TYPE_UNKNOWN || javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
-                javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
-                print_error(field, "Invalid primary state field: '%s'\n",
-                            atomDecl->message.c_str());
-                errorCount++;
-            } else {
-                atomDecl->primaryFields.push_back(fieldNumber);
-                addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_PRIMARY_FIELD,
-                                        ANNOTATION_TYPE_BOOL, AnnotationValue(true));
-            }
-        }
-
-        if (primaryFieldFirstUid) {
-            if (javaType != JAVA_TYPE_ATTRIBUTION_CHAIN) {
-                print_error(field,
-                            "PRIMARY_FIELD_FIRST_UID annotation is only for AttributionChains: "
-                            "'%s'\n",
-                            atomDecl->message.c_str());
-                errorCount++;
-            } else {
-                atomDecl->primaryFields.push_back(FIRST_UID_IN_CHAIN_ID);
-                addAnnotationToAtomDecl(atomDecl, fieldNumber,
-                                        ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, ANNOTATION_TYPE_BOOL,
-                                        AnnotationValue(true));
-            }
-        }
-
-        if (exclusiveState) {
-            if (javaType == JAVA_TYPE_UNKNOWN || javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
-                javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
-                print_error(field, "Invalid exclusive state field: '%s'\n",
-                            atomDecl->message.c_str());
-                errorCount++;
-            }
-
-            if (atomDecl->exclusiveField != 0) {
-                print_error(field,
-                            "Cannot have more than one exclusive state field in an "
-                            "atom: '%s'\n",
-                            atomDecl->message.c_str());
-                errorCount++;
-            } else {
-                atomDecl->exclusiveField = fieldNumber;
-                addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_EXCLUSIVE_STATE,
-                                        ANNOTATION_TYPE_BOOL, AnnotationValue(true));
-            }
-
-            if (stateFieldOption.has_default_state_value()) {
-                const int defaultState = stateFieldOption.default_state_value();
-                atomDecl->defaultState = defaultState;
-
-                addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_DEFAULT_STATE,
-                                        ANNOTATION_TYPE_INT, AnnotationValue(defaultState));
-            }
-
-            if (stateFieldOption.has_trigger_state_reset_value()) {
-                const int triggerStateReset = stateFieldOption.trigger_state_reset_value();
-
-                atomDecl->triggerStateReset = triggerStateReset;
-                addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_TRIGGER_STATE_RESET,
-                                        ANNOTATION_TYPE_INT, AnnotationValue(triggerStateReset));
-            }
-
-            if (stateFieldOption.has_nested()) {
-                const bool nested = stateFieldOption.nested();
-                atomDecl->nested = nested;
-
-                addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_STATE_NESTED,
-                                        ANNOTATION_TYPE_BOOL, AnnotationValue(nested));
-            }
-        }
-    }
-
-    if (field->options().GetExtension(os::statsd::is_uid) == true) {
-        if (javaType != JAVA_TYPE_INT) {
-            print_error(field, "is_uid annotation can only be applied to int32 fields: '%s'\n",
-                        atomDecl->message.c_str());
-            errorCount++;
-        }
-
-        if (atomDecl->uidField == 0) {
-            atomDecl->uidField = fieldNumber;
-
-            addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_IS_UID,
-                                    ANNOTATION_TYPE_BOOL, AnnotationValue(true));
-        } else {
-            print_error(field,
-                        "Cannot have more than one field in an atom with is_uid "
-                        "annotation: '%s'\n",
-                        atomDecl->message.c_str());
-            errorCount++;
-        }
-    }
-
-    return errorCount;
-}
-
-/**
- * Gather the info about an atom proto.
- */
-int collate_atom(const Descriptor* atom, AtomDecl* atomDecl, vector<java_type_t>* signature) {
-    int errorCount = 0;
-
-    // Build a sorted list of the fields. Descriptor has them in source file
-    // order.
-    map<int, const FieldDescriptor*> fields;
-    for (int j = 0; j < atom->field_count(); j++) {
-        const FieldDescriptor* field = atom->field(j);
-        fields[field->number()] = field;
-    }
-
-    // Check that the parameters start at 1 and go up sequentially.
-    int expectedNumber = 1;
-    for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
-         it++) {
-        const int number = it->first;
-        const FieldDescriptor* field = it->second;
-        if (number != expectedNumber) {
-            print_error(field,
-                        "Fields must be numbered consecutively starting at 1:"
-                        " '%s' is %d but should be %d\n",
-                        field->name().c_str(), number, expectedNumber);
-            errorCount++;
-            expectedNumber = number;
-            continue;
-        }
-        expectedNumber++;
-    }
-
-    // Check that only allowed types are present. Remove any invalid ones.
-    for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
-         it++) {
-        const FieldDescriptor* field = it->second;
-        bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
-                             os::statsd::LogMode::MODE_BYTES;
-
-        java_type_t javaType = java_type(field);
-
-        if (javaType == JAVA_TYPE_UNKNOWN) {
-            print_error(field, "Unknown type for field: %s\n", field->name().c_str());
-            errorCount++;
-            continue;
-        } else if (javaType == JAVA_TYPE_OBJECT && atomDecl->code < PULL_ATOM_START_ID) {
-            // Allow attribution chain, but only at position 1.
-            print_error(field, "Message type not allowed for field in pushed atoms: %s\n",
-                        field->name().c_str());
-            errorCount++;
-            continue;
-        } else if (javaType == JAVA_TYPE_BYTE_ARRAY && !isBinaryField) {
-            print_error(field, "Raw bytes type not allowed for field: %s\n", field->name().c_str());
-            errorCount++;
-            continue;
-        }
-
-        if (isBinaryField && javaType != JAVA_TYPE_BYTE_ARRAY) {
-            print_error(field, "Cannot mark field %s as bytes.\n", field->name().c_str());
-            errorCount++;
-            continue;
-        }
-
-        // Doubles are not supported yet.
-        if (javaType == JAVA_TYPE_DOUBLE) {
-            print_error(field,
-                        "Doubles are not supported in atoms. Please change field %s "
-                        "to float\n",
-                        field->name().c_str());
-            errorCount++;
-            continue;
-        }
-
-        if (field->is_repeated() &&
-            !(javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || javaType == JAVA_TYPE_KEY_VALUE_PAIR)) {
-            print_error(field,
-                        "Repeated fields are not supported in atoms. Please make "
-                        "field %s not "
-                        "repeated.\n",
-                        field->name().c_str());
-            errorCount++;
-            continue;
-        }
-    }
-
-    // Check that if there's an attribution chain, it's at position 1.
-    for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
-         it++) {
-        int number = it->first;
-        if (number != 1) {
-            const FieldDescriptor* field = it->second;
-            java_type_t javaType = java_type(field);
-            if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-                print_error(field,
-                            "AttributionChain fields must have field id 1, in message: '%s'\n",
-                            atom->name().c_str());
-                errorCount++;
-            }
-        }
-    }
-
-    // Build the type signature and the atom data.
-    for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
-         it++) {
-        const FieldDescriptor* field = it->second;
-        java_type_t javaType = java_type(field);
-        bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
-                             os::statsd::LogMode::MODE_BYTES;
-
-        AtomField atField(field->name(), javaType);
-
-        if (javaType == JAVA_TYPE_ENUM) {
-            // All enums are treated as ints when it comes to function signatures.
-            collate_enums(*field->enum_type(), &atField);
-        }
-
-        // Generate signature for pushed atoms
-        if (atomDecl->code < PULL_ATOM_START_ID) {
-            if (javaType == JAVA_TYPE_ENUM) {
-                // All enums are treated as ints when it comes to function signatures.
-                signature->push_back(JAVA_TYPE_INT);
-            } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
-                signature->push_back(JAVA_TYPE_BYTE_ARRAY);
-            } else {
-                signature->push_back(javaType);
-            }
-        }
-
-        atomDecl->fields.push_back(atField);
-
-        errorCount += collate_field_annotations(atomDecl, field, it->first, javaType);
-    }
-
-    return errorCount;
-}
-
-// This function flattens the fields of the AttributionNode proto in an Atom
-// proto and generates the corresponding atom decl and signature.
-bool get_non_chained_node(const Descriptor* atom, AtomDecl* atomDecl,
-                          vector<java_type_t>* signature) {
-    // Build a sorted list of the fields. Descriptor has them in source file
-    // order.
-    map<int, const FieldDescriptor*> fields;
-    for (int j = 0; j < atom->field_count(); j++) {
-        const FieldDescriptor* field = atom->field(j);
-        fields[field->number()] = field;
-    }
-
-    AtomDecl attributionDecl;
-    vector<java_type_t> attributionSignature;
-    collate_atom(android::os::statsd::AttributionNode::descriptor(), &attributionDecl,
-                 &attributionSignature);
-
-    // Build the type signature and the atom data.
-    bool has_attribution_node = false;
-    for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
-         it++) {
-        const FieldDescriptor* field = it->second;
-        java_type_t javaType = java_type(field);
-        if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-            atomDecl->fields.insert(atomDecl->fields.end(), attributionDecl.fields.begin(),
-                                    attributionDecl.fields.end());
-            signature->insert(signature->end(), attributionSignature.begin(),
-                              attributionSignature.end());
-            has_attribution_node = true;
-
-        } else {
-            AtomField atField(field->name(), javaType);
-            if (javaType == JAVA_TYPE_ENUM) {
-                // All enums are treated as ints when it comes to function signatures.
-                signature->push_back(JAVA_TYPE_INT);
-                collate_enums(*field->enum_type(), &atField);
-            } else {
-                signature->push_back(javaType);
-            }
-            atomDecl->fields.push_back(atField);
-        }
-    }
-    return has_attribution_node;
-}
-
-static void populateFieldNumberToAtomDeclSet(const shared_ptr<AtomDecl>& atomDecl,
-                                             FieldNumberToAtomDeclSet* fieldNumberToAtomDeclSet) {
-    for (FieldNumberToAnnotations::const_iterator it = atomDecl->fieldNumberToAnnotations.begin();
-         it != atomDecl->fieldNumberToAnnotations.end(); it++) {
-        const int fieldNumber = it->first;
-        (*fieldNumberToAtomDeclSet)[fieldNumber].insert(atomDecl);
-    }
-}
-
-/**
- * Gather the info about the atoms.
- */
-int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* atoms) {
-    int errorCount = 0;
-
-    for (int i = 0; i < descriptor->field_count(); i++) {
-        const FieldDescriptor* atomField = descriptor->field(i);
-
-        if (moduleName != DEFAULT_MODULE_NAME) {
-            const int moduleCount = atomField->options().ExtensionSize(os::statsd::module);
-            int j;
-            for (j = 0; j < moduleCount; ++j) {
-                const string atomModuleName =
-                        atomField->options().GetExtension(os::statsd::module, j);
-                if (atomModuleName == moduleName) {
-                    break;
-                }
-            }
-
-            // This atom is not in the module we're interested in; skip it.
-            if (moduleCount == j) {
-                if (dbg) {
-                    printf("   Skipping %s (%d)\n", atomField->name().c_str(), atomField->number());
-                }
-                continue;
-            }
-        }
-
-        if (dbg) {
-            printf("   %s (%d)\n", atomField->name().c_str(), atomField->number());
-        }
-
-        // StatsEvent only has one oneof, which contains only messages. Don't allow
-        // other types.
-        if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
-            print_error(atomField,
-                        "Bad type for atom. StatsEvent can only have message type "
-                        "fields: %s\n",
-                        atomField->name().c_str());
-            errorCount++;
-            continue;
-        }
-
-        const Descriptor* atom = atomField->message_type();
-        shared_ptr<AtomDecl> atomDecl =
-                make_shared<AtomDecl>(atomField->number(), atomField->name(), atom->name());
-
-        if (atomDecl->code < PULL_ATOM_START_ID &&
-            atomField->options().GetExtension(os::statsd::truncate_timestamp)) {
-            addAnnotationToAtomDecl(atomDecl.get(), ATOM_ID_FIELD_NUMBER,
-                                    ANNOTATION_ID_TRUNCATE_TIMESTAMP, ANNOTATION_TYPE_BOOL,
-                                    AnnotationValue(true));
-            if (dbg) {
-                printf("%s can have timestamp truncated\n", atomField->name().c_str());
-            }
-        }
-
-        vector<java_type_t> signature;
-        errorCount += collate_atom(atom, atomDecl.get(), &signature);
-        if (atomDecl->primaryFields.size() != 0 && atomDecl->exclusiveField == 0) {
-            print_error(atomField, "Cannot have a primary field without an exclusive field: %s\n",
-                        atomField->name().c_str());
-            errorCount++;
-            continue;
-        }
-
-        FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = atoms->signatureInfoMap[signature];
-        populateFieldNumberToAtomDeclSet(atomDecl, &fieldNumberToAtomDeclSet);
-
-        atoms->decls.insert(atomDecl);
-
-        shared_ptr<AtomDecl> nonChainedAtomDecl =
-                make_shared<AtomDecl>(atomField->number(), atomField->name(), atom->name());
-        vector<java_type_t> nonChainedSignature;
-        if (get_non_chained_node(atom, nonChainedAtomDecl.get(), &nonChainedSignature)) {
-            FieldNumberToAtomDeclSet& nonChainedFieldNumberToAtomDeclSet =
-                    atoms->nonChainedSignatureInfoMap[nonChainedSignature];
-            populateFieldNumberToAtomDeclSet(nonChainedAtomDecl,
-                                             &nonChainedFieldNumberToAtomDeclSet);
-
-            atoms->non_chained_decls.insert(nonChainedAtomDecl);
-        }
-    }
-
-    if (dbg) {
-        printf("signatures = [\n");
-        for (SignatureInfoMap::const_iterator it = atoms->signatureInfoMap.begin();
-             it != atoms->signatureInfoMap.end(); it++) {
-            printf("   ");
-            for (vector<java_type_t>::const_iterator jt = it->first.begin(); jt != it->first.end();
-                 jt++) {
-                printf(" %d", (int)*jt);
-            }
-            printf("\n");
-        }
-        printf("]\n");
-    }
-
-    return errorCount;
-}
-
-}  // namespace stats_log_api_gen
-}  // namespace android
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
deleted file mode 100644
index 3deb3ae..0000000
--- a/tools/stats_log_api_gen/Collation.h
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2017, 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 ANDROID_STATS_LOG_API_GEN_COLLATION_H
-#define ANDROID_STATS_LOG_API_GEN_COLLATION_H
-
-#include <google/protobuf/descriptor.h>
-#include <stdint.h>
-
-#include <map>
-#include <set>
-#include <vector>
-
-#include "frameworks/proto_logging/stats/atom_field_options.pb.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-using google::protobuf::Descriptor;
-using google::protobuf::FieldDescriptor;
-using std::map;
-using std::set;
-using std::shared_ptr;
-using std::string;
-using std::vector;
-
-const int PULL_ATOM_START_ID = 10000;
-
-const int FIRST_UID_IN_CHAIN_ID = 0;
-
-enum AnnotationId : uint8_t {
-    ANNOTATION_ID_IS_UID = 1,
-    ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2,
-    ANNOTATION_ID_PRIMARY_FIELD = 3,
-    ANNOTATION_ID_EXCLUSIVE_STATE = 4,
-    ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5,
-    ANNOTATION_ID_DEFAULT_STATE = 6,
-    ANNOTATION_ID_TRIGGER_STATE_RESET = 7,
-    ANNOTATION_ID_STATE_NESTED = 8,
-};
-
-const int ATOM_ID_FIELD_NUMBER = -1;
-
-const string DEFAULT_MODULE_NAME = "DEFAULT";
-
-/**
- * The types for atom parameters.
- */
-typedef enum {
-    JAVA_TYPE_UNKNOWN = 0,
-
-    JAVA_TYPE_ATTRIBUTION_CHAIN = 1,
-    JAVA_TYPE_BOOLEAN = 2,
-    JAVA_TYPE_INT = 3,
-    JAVA_TYPE_LONG = 4,
-    JAVA_TYPE_FLOAT = 5,
-    JAVA_TYPE_DOUBLE = 6,
-    JAVA_TYPE_STRING = 7,
-    JAVA_TYPE_ENUM = 8,
-    JAVA_TYPE_KEY_VALUE_PAIR = 9,
-
-    JAVA_TYPE_OBJECT = -1,
-    JAVA_TYPE_BYTE_ARRAY = -2,
-} java_type_t;
-
-enum AnnotationType {
-    ANNOTATION_TYPE_UNKNOWN = 0,
-    ANNOTATION_TYPE_INT = 1,
-    ANNOTATION_TYPE_BOOL = 2,
-};
-
-union AnnotationValue {
-    int intValue;
-    bool boolValue;
-
-    AnnotationValue(const int value) : intValue(value) {
-    }
-    AnnotationValue(const bool value) : boolValue(value) {
-    }
-};
-
-struct Annotation {
-    const AnnotationId annotationId;
-    const int atomId;
-    AnnotationType type;
-    AnnotationValue value;
-
-    inline Annotation(AnnotationId annotationId, int atomId, AnnotationType type,
-                      AnnotationValue value)
-        : annotationId(annotationId), atomId(atomId), type(type), value(value) {
-    }
-    inline ~Annotation() {
-    }
-
-    inline bool operator<(const Annotation& that) const {
-        return atomId == that.atomId ? annotationId < that.annotationId : atomId < that.atomId;
-    }
-};
-
-struct SharedComparator {
-    template <typename T>
-    inline bool operator()(const shared_ptr<T>& lhs, const shared_ptr<T>& rhs) const {
-        return (*lhs) < (*rhs);
-    }
-};
-
-using AnnotationSet = set<shared_ptr<Annotation>, SharedComparator>;
-
-using FieldNumberToAnnotations = map<int, AnnotationSet>;
-
-/**
- * The name and type for an atom field.
- */
-struct AtomField {
-    string name;
-    java_type_t javaType;
-
-    // If the field is of type enum, the following map contains the list of enum
-    // values.
-    map<int /* numeric value */, string /* value name */> enumValues;
-
-    inline AtomField() : name(), javaType(JAVA_TYPE_UNKNOWN) {
-    }
-    inline AtomField(const AtomField& that)
-        : name(that.name), javaType(that.javaType), enumValues(that.enumValues) {
-    }
-
-    inline AtomField(string n, java_type_t jt) : name(n), javaType(jt) {
-    }
-    inline ~AtomField() {
-    }
-};
-
-/**
- * The name and code for an atom.
- */
-struct AtomDecl {
-    int code;
-    string name;
-
-    string message;
-    vector<AtomField> fields;
-
-    FieldNumberToAnnotations fieldNumberToAnnotations;
-
-    vector<int> primaryFields;
-    int exclusiveField = 0;
-    int defaultState = INT_MAX;
-    int triggerStateReset = INT_MAX;
-    bool nested = true;
-
-    int uidField = 0;
-
-    AtomDecl();
-    AtomDecl(const AtomDecl& that);
-    AtomDecl(int code, const string& name, const string& message);
-    ~AtomDecl();
-
-    inline bool operator<(const AtomDecl& that) const {
-        return (code == that.code) ? (name < that.name) : (code < that.code);
-    }
-};
-
-using AtomDeclSet = set<shared_ptr<AtomDecl>, SharedComparator>;
-
-// Maps a field number to a set of atoms that have annotation(s) for their field with that field
-// number.
-using FieldNumberToAtomDeclSet = map<int, AtomDeclSet>;
-
-using SignatureInfoMap = map<vector<java_type_t>, FieldNumberToAtomDeclSet>;
-
-struct Atoms {
-    SignatureInfoMap signatureInfoMap;
-    AtomDeclSet decls;
-    AtomDeclSet non_chained_decls;
-    SignatureInfoMap nonChainedSignatureInfoMap;
-};
-
-/**
- * Gather the information about the atoms.  Returns the number of errors.
- */
-int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* atoms);
-int collate_atom(const Descriptor* atom, AtomDecl* atomDecl, vector<java_type_t>* signature);
-
-}  // namespace stats_log_api_gen
-}  // namespace android
-
-#endif  // ANDROID_STATS_LOG_API_GEN_COLLATION_H
diff --git a/tools/stats_log_api_gen/OWNERS b/tools/stats_log_api_gen/OWNERS
deleted file mode 100644
index 41a0c95..0000000
--- a/tools/stats_log_api_gen/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-yro@google.com
diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp
deleted file mode 100644
index f4c937c..0000000
--- a/tools/stats_log_api_gen/java_writer.cpp
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * Copyright (C) 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "java_writer.h"
-
-#include "java_writer_q.h"
-#include "utils.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-static int write_java_q_logger_class(FILE* out, const SignatureInfoMap& signatureInfoMap,
-                                     const AtomDecl& attributionDecl) {
-    fprintf(out, "\n");
-    fprintf(out, "    // Write logging helper methods for statsd in Q and earlier.\n");
-    fprintf(out, "    private static class QLogger {\n");
-
-    write_java_q_logging_constants(out, "        ");
-
-    // Print Q write methods.
-    fprintf(out, "\n");
-    fprintf(out, "        // Write methods.\n");
-    write_java_methods_q_schema(out, signatureInfoMap, attributionDecl, "        ");
-
-    fprintf(out, "    }\n");
-    return 0;
-}
-
-static void write_java_annotation_constants(FILE* out) {
-    fprintf(out, "    // Annotation constants.\n");
-
-    for (const auto& [id, name] : ANNOTATION_ID_CONSTANTS) {
-        fprintf(out, "    public static final byte %s = %hhu;\n", name.c_str(), id);
-    }
-    fprintf(out, "\n");
-}
-
-static void write_annotations(FILE* out, int argIndex,
-                              const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet) {
-    FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt =
-            fieldNumberToAtomDeclSet.find(argIndex);
-    if (fieldNumberToAtomDeclSet.end() == fieldNumberToAtomDeclSetIt) {
-        return;
-    }
-    const AtomDeclSet& atomDeclSet = fieldNumberToAtomDeclSetIt->second;
-    for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) {
-        const string atomConstant = make_constant_name(atomDecl->name);
-        fprintf(out, "        if (%s == code) {\n", atomConstant.c_str());
-        const AnnotationSet& annotations = atomDecl->fieldNumberToAnnotations.at(argIndex);
-        int resetState = -1;
-        int defaultState = -1;
-        for (const shared_ptr<Annotation>& annotation : annotations) {
-            const string& annotationConstant = ANNOTATION_ID_CONSTANTS.at(annotation->annotationId);
-            switch (annotation->type) {
-                case ANNOTATION_TYPE_INT:
-                    if (ANNOTATION_ID_TRIGGER_STATE_RESET == annotation->annotationId) {
-                        resetState = annotation->value.intValue;
-                    } else if (ANNOTATION_ID_DEFAULT_STATE == annotation->annotationId) {
-                        defaultState = annotation->value.intValue;
-                    } else {
-                        fprintf(out, "            builder.addIntAnnotation(%s, %d);\n",
-                                annotationConstant.c_str(), annotation->value.intValue);
-                    }
-                    break;
-                case ANNOTATION_TYPE_BOOL:
-                    fprintf(out, "            builder.addBooleanAnnotation(%s, %s);\n",
-                            annotationConstant.c_str(),
-                            annotation->value.boolValue ? "true" : "false");
-                    break;
-                default:
-                    break;
-            }
-        }
-        if (defaultState != -1 && resetState != -1) {
-            const string& annotationConstant =
-                    ANNOTATION_ID_CONSTANTS.at(ANNOTATION_ID_TRIGGER_STATE_RESET);
-            fprintf(out, "            if (arg%d == %d) {\n", argIndex, resetState);
-            fprintf(out, "                builder.addIntAnnotation(%s, %d);\n",
-                    annotationConstant.c_str(), defaultState);
-            fprintf(out, "            }\n");
-        }
-        fprintf(out, "        }\n");
-    }
-}
-
-static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
-                              const AtomDecl& attributionDecl, const bool supportQ) {
-    for (auto signatureInfoMapIt = signatureInfoMap.begin();
-         signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
-        // Print method signature.
-        fprintf(out, "    public static void write(int code");
-        const vector<java_type_t>& signature = signatureInfoMapIt->first;
-        const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second;
-        int argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
-             arg++) {
-            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-                for (auto chainField : attributionDecl.fields) {
-                    fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
-                            chainField.name.c_str());
-                }
-            } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-                fprintf(out, ", android.util.SparseArray<Object> valueMap");
-            } else {
-                fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
-            }
-            argIndex++;
-        }
-        fprintf(out, ") {\n");
-
-        // Print method body.
-        string indent("");
-        if (supportQ) {
-            fprintf(out, "        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {\n");
-            indent = "    ";
-        }
-
-        // Start StatsEvent.Builder.
-        fprintf(out,
-                "%s        final StatsEvent.Builder builder = "
-                "StatsEvent.newBuilder();\n",
-                indent.c_str());
-
-        // Write atom code.
-        fprintf(out, "%s        builder.setAtomId(code);\n", indent.c_str());
-        write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet);
-
-        // Write the args.
-        argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
-             arg++) {
-            switch (*arg) {
-                case JAVA_TYPE_BOOLEAN:
-                    fprintf(out, "%s        builder.writeBoolean(arg%d);\n", indent.c_str(),
-                            argIndex);
-                    break;
-                case JAVA_TYPE_INT:
-                case JAVA_TYPE_ENUM:
-                    fprintf(out, "%s        builder.writeInt(arg%d);\n", indent.c_str(), argIndex);
-                    break;
-                case JAVA_TYPE_FLOAT:
-                    fprintf(out, "%s        builder.writeFloat(arg%d);\n", indent.c_str(),
-                            argIndex);
-                    break;
-                case JAVA_TYPE_LONG:
-                    fprintf(out, "%s        builder.writeLong(arg%d);\n", indent.c_str(), argIndex);
-                    break;
-                case JAVA_TYPE_STRING:
-                    fprintf(out, "%s        builder.writeString(arg%d);\n", indent.c_str(),
-                            argIndex);
-                    break;
-                case JAVA_TYPE_BYTE_ARRAY:
-                    fprintf(out,
-                            "%s        builder.writeByteArray(null == arg%d ? new byte[0] : "
-                            "arg%d);\n",
-                            indent.c_str(), argIndex, argIndex);
-                    break;
-                case JAVA_TYPE_ATTRIBUTION_CHAIN: {
-                    const char* uidName = attributionDecl.fields.front().name.c_str();
-                    const char* tagName = attributionDecl.fields.back().name.c_str();
-
-                    fprintf(out, "%s        builder.writeAttributionChain(\n", indent.c_str());
-                    fprintf(out, "%s                null == %s ? new int[0] : %s,\n",
-                            indent.c_str(), uidName, uidName);
-                    fprintf(out, "%s                null == %s ? new String[0] : %s);\n",
-                            indent.c_str(), tagName, tagName);
-                    break;
-                }
-                case JAVA_TYPE_KEY_VALUE_PAIR:
-                    fprintf(out, "\n");
-                    fprintf(out, "%s        // Write KeyValuePairs.\n", indent.c_str());
-                    fprintf(out, "%s        final int count = valueMap.size();\n", indent.c_str());
-                    fprintf(out, "%s        android.util.SparseIntArray intMap = null;\n",
-                            indent.c_str());
-                    fprintf(out, "%s        android.util.SparseLongArray longMap = null;\n",
-                            indent.c_str());
-                    fprintf(out, "%s        android.util.SparseArray<String> stringMap = null;\n",
-                            indent.c_str());
-                    fprintf(out, "%s        android.util.SparseArray<Float> floatMap = null;\n",
-                            indent.c_str());
-                    fprintf(out, "%s        for (int i = 0; i < count; i++) {\n", indent.c_str());
-                    fprintf(out, "%s            final int key = valueMap.keyAt(i);\n",
-                            indent.c_str());
-                    fprintf(out, "%s            final Object value = valueMap.valueAt(i);\n",
-                            indent.c_str());
-                    fprintf(out, "%s            if (value instanceof Integer) {\n", indent.c_str());
-                    fprintf(out, "%s                if (null == intMap) {\n", indent.c_str());
-                    fprintf(out,
-                            "%s                    intMap = new "
-                            "android.util.SparseIntArray();\n",
-                            indent.c_str());
-                    fprintf(out, "%s                }\n", indent.c_str());
-                    fprintf(out, "%s                intMap.put(key, (Integer) value);\n",
-                            indent.c_str());
-                    fprintf(out, "%s            } else if (value instanceof Long) {\n",
-                            indent.c_str());
-                    fprintf(out, "%s                if (null == longMap) {\n", indent.c_str());
-                    fprintf(out,
-                            "%s                    longMap = new "
-                            "android.util.SparseLongArray();\n",
-                            indent.c_str());
-                    fprintf(out, "%s                }\n", indent.c_str());
-                    fprintf(out, "%s                longMap.put(key, (Long) value);\n",
-                            indent.c_str());
-                    fprintf(out, "%s            } else if (value instanceof String) {\n",
-                            indent.c_str());
-                    fprintf(out, "%s                if (null == stringMap) {\n", indent.c_str());
-                    fprintf(out,
-                            "%s                    stringMap = new "
-                            "android.util.SparseArray<>();\n",
-                            indent.c_str());
-                    fprintf(out, "%s                }\n", indent.c_str());
-                    fprintf(out, "%s                stringMap.put(key, (String) value);\n",
-                            indent.c_str());
-                    fprintf(out, "%s            } else if (value instanceof Float) {\n",
-                            indent.c_str());
-                    fprintf(out, "%s                if (null == floatMap) {\n", indent.c_str());
-                    fprintf(out,
-                            "%s                    floatMap = new "
-                            "android.util.SparseArray<>();\n",
-                            indent.c_str());
-                    fprintf(out, "%s                }\n", indent.c_str());
-                    fprintf(out, "%s                floatMap.put(key, (Float) value);\n",
-                            indent.c_str());
-                    fprintf(out, "%s            }\n", indent.c_str());
-                    fprintf(out, "%s        }\n", indent.c_str());
-                    fprintf(out,
-                            "%s        builder.writeKeyValuePairs("
-                            "intMap, longMap, stringMap, floatMap);\n",
-                            indent.c_str());
-                    break;
-                default:
-                    // Unsupported types: OBJECT, DOUBLE.
-                    fprintf(stderr, "Encountered unsupported type.");
-                    return 1;
-            }
-            write_annotations(out, argIndex, fieldNumberToAtomDeclSet);
-            argIndex++;
-        }
-
-        fprintf(out, "\n");
-        fprintf(out, "%s        builder.usePooledBuffer();\n", indent.c_str());
-        fprintf(out, "%s        StatsLog.write(builder.build());\n", indent.c_str());
-
-        // Add support for writing using Q schema if this is not the default module.
-        if (supportQ) {
-            fprintf(out, "        } else {\n");
-            fprintf(out, "            QLogger.write(code");
-            argIndex = 1;
-            for (vector<java_type_t>::const_iterator arg = signature.begin();
-                 arg != signature.end(); arg++) {
-                if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-                    const char* uidName = attributionDecl.fields.front().name.c_str();
-                    const char* tagName = attributionDecl.fields.back().name.c_str();
-                    fprintf(out, ", %s, %s", uidName, tagName);
-                } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-                    // Module logging does not yet support key value pair.
-                    fprintf(stderr, "Module logging does not yet support key value pair.\n");
-                    return 1;
-                } else {
-                    fprintf(out, ", arg%d", argIndex);
-                }
-                argIndex++;
-            }
-            fprintf(out, ");\n");
-            fprintf(out, "        }\n");  // if
-        }
-
-        fprintf(out, "    }\n");  // method
-        fprintf(out, "\n");
-    }
-    return 0;
-}
-
-int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
-                         const string& javaClass, const string& javaPackage, const bool supportQ,
-                         const bool supportWorkSource) {
-    // Print prelude
-    fprintf(out, "// This file is autogenerated\n");
-    fprintf(out, "\n");
-    fprintf(out, "package %s;\n", javaPackage.c_str());
-    fprintf(out, "\n");
-    fprintf(out, "\n");
-    if (supportQ) {
-        fprintf(out, "import android.os.Build;\n");
-        fprintf(out, "import android.os.SystemClock;\n");
-    }
-
-    fprintf(out, "import android.util.StatsEvent;\n");
-    fprintf(out, "import android.util.StatsLog;\n");
-
-    fprintf(out, "\n");
-    fprintf(out, "\n");
-    fprintf(out, "/**\n");
-    fprintf(out, " * Utility class for logging statistics events.\n");
-    fprintf(out, " */\n");
-    fprintf(out, "public class %s {\n", javaClass.c_str());
-
-    write_java_atom_codes(out, atoms);
-    write_java_enum_values(out, atoms);
-    write_java_annotation_constants(out);
-
-    int errors = 0;
-
-    // Print write methods.
-    fprintf(out, "    // Write methods\n");
-    errors += write_java_methods(out, atoms.signatureInfoMap, attributionDecl, supportQ);
-    errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap);
-    if (supportWorkSource) {
-        errors += write_java_work_source_methods(out, atoms.signatureInfoMap);
-    }
-
-    if (supportQ) {
-        errors += write_java_q_logger_class(out, atoms.signatureInfoMap, attributionDecl);
-    }
-
-    fprintf(out, "}\n");
-
-    return errors;
-}
-
-}  // namespace stats_log_api_gen
-}  // namespace android
diff --git a/tools/stats_log_api_gen/java_writer.h b/tools/stats_log_api_gen/java_writer.h
deleted file mode 100644
index 8b3b505..0000000
--- a/tools/stats_log_api_gen/java_writer.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2019, 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.
- */
-
-#pragma once
-
-#include <stdio.h>
-#include <string.h>
-
-#include <map>
-#include <set>
-#include <vector>
-
-#include "Collation.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-using namespace std;
-
-int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
-                         const string& javaClass, const string& javaPackage, const bool supportQ,
-                         const bool supportWorkSource);
-
-}  // namespace stats_log_api_gen
-}  // namespace android
diff --git a/tools/stats_log_api_gen/java_writer_q.cpp b/tools/stats_log_api_gen/java_writer_q.cpp
deleted file mode 100644
index d21e270..0000000
--- a/tools/stats_log_api_gen/java_writer_q.cpp
+++ /dev/null
@@ -1,601 +0,0 @@
-/*
- * Copyright (C) 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "java_writer_q.h"
-
-#include "utils.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-void write_java_q_logging_constants(FILE* out, const string& indent) {
-    fprintf(out, "%s// Payload limits.\n", indent.c_str());
-    fprintf(out, "%sprivate static final int LOGGER_ENTRY_MAX_PAYLOAD = 4068;\n", indent.c_str());
-    fprintf(out,
-            "%sprivate static final int MAX_EVENT_PAYLOAD = "
-            "LOGGER_ENTRY_MAX_PAYLOAD - 4;\n",
-            indent.c_str());
-
-    // Value types. Must match with EventLog.java and log.h.
-    fprintf(out, "\n");
-    fprintf(out, "%s// Value types.\n", indent.c_str());
-    fprintf(out, "%sprivate static final byte INT_TYPE = 0;\n", indent.c_str());
-    fprintf(out, "%sprivate static final byte LONG_TYPE = 1;\n", indent.c_str());
-    fprintf(out, "%sprivate static final byte STRING_TYPE = 2;\n", indent.c_str());
-    fprintf(out, "%sprivate static final byte LIST_TYPE = 3;\n", indent.c_str());
-    fprintf(out, "%sprivate static final byte FLOAT_TYPE = 4;\n", indent.c_str());
-
-    // Size of each value type.
-    // Booleans, ints, floats, and enums take 5 bytes, 1 for the type and 4 for
-    // the value.
-    fprintf(out, "\n");
-    fprintf(out, "%s// Size of each value type.\n", indent.c_str());
-    fprintf(out, "%sprivate static final int INT_TYPE_SIZE = 5;\n", indent.c_str());
-    fprintf(out, "%sprivate static final int FLOAT_TYPE_SIZE = 5;\n", indent.c_str());
-    // Longs take 9 bytes, 1 for the type and 8 for the value.
-    fprintf(out, "%sprivate static final int LONG_TYPE_SIZE = 9;\n", indent.c_str());
-    // Strings take 5 metadata bytes: 1 byte is for the type, 4 are for the
-    // length.
-    fprintf(out, "%sprivate static final int STRING_TYPE_OVERHEAD = 5;\n", indent.c_str());
-    fprintf(out, "%sprivate static final int LIST_TYPE_OVERHEAD = 2;\n", indent.c_str());
-}
-
-int write_java_methods_q_schema(FILE* out, const SignatureInfoMap& signatureInfoMap,
-                                const AtomDecl& attributionDecl, const string& indent) {
-    int requiredHelpers = 0;
-    for (auto signatureInfoMapIt = signatureInfoMap.begin();
-         signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
-        // Print method signature.
-        vector<java_type_t> signature = signatureInfoMapIt->first;
-        fprintf(out, "%spublic static void write(int code", indent.c_str());
-        int argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
-             arg++) {
-            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-                for (auto chainField : attributionDecl.fields) {
-                    fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
-                            chainField.name.c_str());
-                }
-            } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-                fprintf(out, ", android.util.SparseArray<Object> valueMap");
-            } else {
-                fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
-            }
-            argIndex++;
-        }
-        fprintf(out, ") {\n");
-
-        // Calculate the size of the buffer.
-        fprintf(out, "%s    // Initial overhead of the list, timestamp, and atom tag.\n",
-                indent.c_str());
-        fprintf(out,
-                "%s    int needed = LIST_TYPE_OVERHEAD + LONG_TYPE_SIZE + "
-                "INT_TYPE_SIZE;\n",
-                indent.c_str());
-        argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
-             arg++) {
-            switch (*arg) {
-                case JAVA_TYPE_BOOLEAN:
-                case JAVA_TYPE_INT:
-                case JAVA_TYPE_FLOAT:
-                case JAVA_TYPE_ENUM:
-                    fprintf(out, "%s    needed += INT_TYPE_SIZE;\n", indent.c_str());
-                    break;
-                case JAVA_TYPE_LONG:
-                    // Longs take 9 bytes, 1 for the type and 8 for the value.
-                    fprintf(out, "%s    needed += LONG_TYPE_SIZE;\n", indent.c_str());
-                    break;
-                case JAVA_TYPE_STRING:
-                    // Strings take 5 metadata bytes + length of byte encoded string.
-                    fprintf(out, "%s    if (arg%d == null) {\n", indent.c_str(), argIndex);
-                    fprintf(out, "%s        arg%d = \"\";\n", indent.c_str(), argIndex);
-                    fprintf(out, "%s    }\n", indent.c_str());
-                    fprintf(out,
-                            "%s    byte[] arg%dBytes = "
-                            "arg%d.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n",
-                            indent.c_str(), argIndex, argIndex);
-                    fprintf(out, "%s    needed += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n",
-                            indent.c_str(), argIndex);
-                    break;
-                case JAVA_TYPE_BYTE_ARRAY:
-                    // Byte arrays take 5 metadata bytes + length of byte array.
-                    fprintf(out, "%s    if (arg%d == null) {\n", indent.c_str(), argIndex);
-                    fprintf(out, "%s        arg%d = new byte[0];\n", indent.c_str(), argIndex);
-                    fprintf(out, "%s    }\n", indent.c_str());
-                    fprintf(out, "%s    needed += STRING_TYPE_OVERHEAD + arg%d.length;\n",
-                            indent.c_str(), argIndex);
-                    break;
-                case JAVA_TYPE_ATTRIBUTION_CHAIN: {
-                    const char* uidName = attributionDecl.fields.front().name.c_str();
-                    const char* tagName = attributionDecl.fields.back().name.c_str();
-                    // Null checks on the params.
-                    fprintf(out, "%s    if (%s == null) {\n", indent.c_str(), uidName);
-                    fprintf(out, "%s        %s = new %s[0];\n", indent.c_str(), uidName,
-                            java_type_name(attributionDecl.fields.front().javaType));
-                    fprintf(out, "%s    }\n", indent.c_str());
-                    fprintf(out, "%s    if (%s == null) {\n", indent.c_str(), tagName);
-                    fprintf(out, "%s        %s = new %s[0];\n", indent.c_str(), tagName,
-                            java_type_name(attributionDecl.fields.back().javaType));
-                    fprintf(out, "%s    }\n", indent.c_str());
-
-                    // First check that the lengths of the uid and tag arrays are the
-                    // same.
-                    fprintf(out, "%s    if (%s.length != %s.length) {\n", indent.c_str(), uidName,
-                            tagName);
-                    fprintf(out, "%s        return;\n", indent.c_str());
-                    fprintf(out, "%s    }\n", indent.c_str());
-                    fprintf(out, "%s    int attrSize = LIST_TYPE_OVERHEAD;\n", indent.c_str());
-                    fprintf(out, "%s    for (int i = 0; i < %s.length; i++) {\n", indent.c_str(),
-                            tagName);
-                    fprintf(out, "%s        String str%d = (%s[i] == null) ? \"\" : %s[i];\n",
-                            indent.c_str(), argIndex, tagName, tagName);
-                    fprintf(out,
-                            "%s        int str%dlen = "
-                            "str%d.getBytes(java.nio.charset.StandardCharsets.UTF_8)."
-                            "length;\n",
-                            indent.c_str(), argIndex, argIndex);
-                    fprintf(out,
-                            "%s        attrSize += "
-                            "LIST_TYPE_OVERHEAD + INT_TYPE_SIZE + STRING_TYPE_OVERHEAD + "
-                            "str%dlen;\n",
-                            indent.c_str(), argIndex);
-                    fprintf(out, "%s    }\n", indent.c_str());
-                    fprintf(out, "%s    needed += attrSize;\n", indent.c_str());
-                    break;
-                }
-                case JAVA_TYPE_KEY_VALUE_PAIR: {
-                    fprintf(out, "%s    // Calculate bytes needed by Key Value Pairs.\n",
-                            indent.c_str());
-                    fprintf(out, "%s    final int count = valueMap.size();\n", indent.c_str());
-                    fprintf(out, "%s    android.util.SparseIntArray intMap = null;\n",
-                            indent.c_str());
-                    fprintf(out, "%s    android.util.SparseLongArray longMap = null;\n",
-                            indent.c_str());
-                    fprintf(out, "%s    android.util.SparseArray<String> stringMap = null;\n",
-                            indent.c_str());
-                    fprintf(out, "%s    android.util.SparseArray<Float> floatMap = null;\n",
-                            indent.c_str());
-                    fprintf(out, "%s    int keyValuePairSize = LIST_TYPE_OVERHEAD;\n",
-                            indent.c_str());
-                    fprintf(out, "%s    for (int i = 0; i < count; i++) {\n", indent.c_str());
-                    fprintf(out, "%s        final int key = valueMap.keyAt(i);\n", indent.c_str());
-                    fprintf(out, "%s        final Object value = valueMap.valueAt(i);\n",
-                            indent.c_str());
-                    fprintf(out, "%s        if (value instanceof Integer) {\n", indent.c_str());
-                    fprintf(out, "%s            keyValuePairSize += LIST_TYPE_OVERHEAD\n",
-                            indent.c_str());
-                    fprintf(out, "%s                    + INT_TYPE_SIZE + INT_TYPE_SIZE;\n",
-                            indent.c_str());
-                    fprintf(out, "%s            if (null == intMap) {\n", indent.c_str());
-                    fprintf(out, "%s                intMap = new android.util.SparseIntArray();\n",
-                            indent.c_str());
-                    fprintf(out, "%s            }\n", indent.c_str());
-                    fprintf(out, "%s            intMap.put(key, (Integer) value);\n",
-                            indent.c_str());
-                    fprintf(out, "%s        } else if (value instanceof Long) {\n", indent.c_str());
-                    fprintf(out, "%s            keyValuePairSize += LIST_TYPE_OVERHEAD\n",
-                            indent.c_str());
-                    fprintf(out, "%s                    + INT_TYPE_SIZE + LONG_TYPE_SIZE;\n",
-                            indent.c_str());
-                    fprintf(out, "%s            if (null == longMap) {\n", indent.c_str());
-                    fprintf(out,
-                            "%s                longMap = new "
-                            "android.util.SparseLongArray();\n",
-                            indent.c_str());
-                    fprintf(out, "%s            }\n", indent.c_str());
-                    fprintf(out, "%s            longMap.put(key, (Long) value);\n", indent.c_str());
-                    fprintf(out, "%s        } else if (value instanceof String) {\n",
-                            indent.c_str());
-                    fprintf(out,
-                            "%s            final String str = (value == null) ? \"\" : "
-                            "(String) value;\n",
-                            indent.c_str());
-                    fprintf(out,
-                            "%s            final int len = "
-                            "str.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;\n",
-                            indent.c_str());
-                    fprintf(out,
-                            "%s            keyValuePairSize += LIST_TYPE_OVERHEAD + "
-                            "INT_TYPE_SIZE\n",
-                            indent.c_str());
-                    fprintf(out, "%s                    + STRING_TYPE_OVERHEAD + len;\n",
-                            indent.c_str());
-                    fprintf(out, "%s            if (null == stringMap) {\n", indent.c_str());
-                    fprintf(out,
-                            "%s                stringMap = new "
-                            "android.util.SparseArray<>();\n",
-                            indent.c_str());
-                    fprintf(out, "%s            }\n", indent.c_str());
-                    fprintf(out, "%s            stringMap.put(key, str);\n", indent.c_str());
-                    fprintf(out, "%s        } else if (value instanceof Float) {\n",
-                            indent.c_str());
-                    fprintf(out, "%s            keyValuePairSize += LIST_TYPE_OVERHEAD\n",
-                            indent.c_str());
-                    fprintf(out, "%s                    + INT_TYPE_SIZE + FLOAT_TYPE_SIZE;\n",
-                            indent.c_str());
-                    fprintf(out, "%s            if (null == floatMap) {\n", indent.c_str());
-                    fprintf(out,
-                            "%s                floatMap = new "
-                            "android.util.SparseArray<>();\n",
-                            indent.c_str());
-                    fprintf(out, "%s            }\n", indent.c_str());
-                    fprintf(out, "%s            floatMap.put(key, (Float) value);\n",
-                            indent.c_str());
-                    fprintf(out, "%s        }\n", indent.c_str());
-                    fprintf(out, "%s    }\n", indent.c_str());
-                    fprintf(out, "%s    needed += keyValuePairSize;\n", indent.c_str());
-                    break;
-                }
-                default:
-                    // Unsupported types: OBJECT, DOUBLE.
-                    fprintf(stderr, "Module logging does not yet support Object and Double.\n");
-                    return 1;
-            }
-            argIndex++;
-        }
-
-        // Now we have the size that is needed. Check for overflow and return if
-        // needed.
-        fprintf(out, "%s    if (needed > MAX_EVENT_PAYLOAD) {\n", indent.c_str());
-        fprintf(out, "%s        return;\n", indent.c_str());
-        fprintf(out, "%s    }\n", indent.c_str());
-
-        // Create new buffer, and associated data types.
-        fprintf(out, "%s    byte[] buff = new byte[needed];\n", indent.c_str());
-        fprintf(out, "%s    int pos = 0;\n", indent.c_str());
-
-        // Initialize the buffer with list data type.
-        fprintf(out, "%s    buff[pos] = LIST_TYPE;\n", indent.c_str());
-        fprintf(out, "%s    buff[pos + 1] = %zu;\n", indent.c_str(), signature.size() + 2);
-        fprintf(out, "%s    pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
-
-        // Write timestamp.
-        fprintf(out, "%s    long elapsedRealtime = SystemClock.elapsedRealtimeNanos();\n",
-                indent.c_str());
-        fprintf(out, "%s    buff[pos] = LONG_TYPE;\n", indent.c_str());
-        fprintf(out, "%s    copyLong(buff, pos + 1, elapsedRealtime);\n", indent.c_str());
-        fprintf(out, "%s    pos += LONG_TYPE_SIZE;\n", indent.c_str());
-
-        // Write atom code.
-        fprintf(out, "%s    buff[pos] = INT_TYPE;\n", indent.c_str());
-        fprintf(out, "%s    copyInt(buff, pos + 1, code);\n", indent.c_str());
-        fprintf(out, "%s    pos += INT_TYPE_SIZE;\n", indent.c_str());
-
-        // Write the args.
-        argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
-             arg++) {
-            switch (*arg) {
-                case JAVA_TYPE_BOOLEAN:
-                    fprintf(out, "%s    buff[pos] = INT_TYPE;\n", indent.c_str());
-                    fprintf(out, "%s    copyInt(buff, pos + 1, arg%d? 1 : 0);\n", indent.c_str(),
-                            argIndex);
-                    fprintf(out, "%s    pos += INT_TYPE_SIZE;\n", indent.c_str());
-                    break;
-                case JAVA_TYPE_INT:
-                case JAVA_TYPE_ENUM:
-                    fprintf(out, "%s    buff[pos] = INT_TYPE;\n", indent.c_str());
-                    fprintf(out, "%s    copyInt(buff, pos + 1, arg%d);\n", indent.c_str(),
-                            argIndex);
-                    fprintf(out, "%s    pos += INT_TYPE_SIZE;\n", indent.c_str());
-                    break;
-                case JAVA_TYPE_FLOAT:
-                    requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT;
-                    fprintf(out, "%s    buff[pos] = FLOAT_TYPE;\n", indent.c_str());
-                    fprintf(out, "%s    copyFloat(buff, pos + 1, arg%d);\n", indent.c_str(),
-                            argIndex);
-                    fprintf(out, "%s    pos += FLOAT_TYPE_SIZE;\n", indent.c_str());
-                    break;
-                case JAVA_TYPE_LONG:
-                    fprintf(out, "%s    buff[pos] = LONG_TYPE;\n", indent.c_str());
-                    fprintf(out, "%s    copyLong(buff, pos + 1, arg%d);\n", indent.c_str(),
-                            argIndex);
-                    fprintf(out, "%s    pos += LONG_TYPE_SIZE;\n", indent.c_str());
-                    break;
-                case JAVA_TYPE_STRING:
-                    fprintf(out, "%s    buff[pos] = STRING_TYPE;\n", indent.c_str());
-                    fprintf(out, "%s    copyInt(buff, pos + 1, arg%dBytes.length);\n",
-                            indent.c_str(), argIndex);
-                    fprintf(out,
-                            "%s    System.arraycopy("
-                            "arg%dBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, "
-                            "arg%dBytes.length);\n",
-                            indent.c_str(), argIndex, argIndex);
-                    fprintf(out, "%s    pos += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n",
-                            indent.c_str(), argIndex);
-                    break;
-                case JAVA_TYPE_BYTE_ARRAY:
-                    fprintf(out, "%s    buff[pos] = STRING_TYPE;\n", indent.c_str());
-                    fprintf(out, "%s    copyInt(buff, pos + 1, arg%d.length);\n", indent.c_str(),
-                            argIndex);
-                    fprintf(out,
-                            "%s    System.arraycopy("
-                            "arg%d, 0, buff, pos + STRING_TYPE_OVERHEAD, arg%d.length);\n",
-                            indent.c_str(), argIndex, argIndex);
-                    fprintf(out, "%s    pos += STRING_TYPE_OVERHEAD + arg%d.length;\n",
-                            indent.c_str(), argIndex);
-                    break;
-                case JAVA_TYPE_ATTRIBUTION_CHAIN: {
-                    requiredHelpers |= JAVA_MODULE_REQUIRES_ATTRIBUTION;
-                    const char* uidName = attributionDecl.fields.front().name.c_str();
-                    const char* tagName = attributionDecl.fields.back().name.c_str();
-
-                    fprintf(out, "%s    writeAttributionChain(buff, pos, %s, %s);\n",
-                            indent.c_str(), uidName, tagName);
-                    fprintf(out, "%s    pos += attrSize;\n", indent.c_str());
-                    break;
-                }
-                case JAVA_TYPE_KEY_VALUE_PAIR:
-                    requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT;
-                    requiredHelpers |= JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS;
-                    fprintf(out,
-                            "%s    writeKeyValuePairs(buff, pos, (byte) count, intMap, "
-                            "longMap, "
-                            "stringMap, floatMap);\n",
-                            indent.c_str());
-                    fprintf(out, "%s    pos += keyValuePairSize;\n", indent.c_str());
-                    break;
-                default:
-                    // Unsupported types: OBJECT, DOUBLE.
-                    fprintf(stderr, "Object and Double are not supported in module logging");
-                    return 1;
-            }
-            argIndex++;
-        }
-
-        fprintf(out, "%s    StatsLog.writeRaw(buff, pos);\n", indent.c_str());
-        fprintf(out, "%s}\n", indent.c_str());
-        fprintf(out, "\n");
-    }
-
-    write_java_helpers_for_q_schema_methods(out, attributionDecl, requiredHelpers, indent);
-
-    return 0;
-}
-
-void write_java_helpers_for_q_schema_methods(FILE* out, const AtomDecl& attributionDecl,
-                                             const int requiredHelpers, const string& indent) {
-    fprintf(out, "\n");
-    fprintf(out, "%s// Helper methods for copying primitives\n", indent.c_str());
-    fprintf(out, "%sprivate static void copyInt(byte[] buff, int pos, int val) {\n",
-            indent.c_str());
-    fprintf(out, "%s    buff[pos] = (byte) (val);\n", indent.c_str());
-    fprintf(out, "%s    buff[pos + 1] = (byte) (val >> 8);\n", indent.c_str());
-    fprintf(out, "%s    buff[pos + 2] = (byte) (val >> 16);\n", indent.c_str());
-    fprintf(out, "%s    buff[pos + 3] = (byte) (val >> 24);\n", indent.c_str());
-    fprintf(out, "%s    return;\n", indent.c_str());
-    fprintf(out, "%s}\n", indent.c_str());
-    fprintf(out, "\n");
-
-    fprintf(out, "%sprivate static void copyLong(byte[] buff, int pos, long val) {\n",
-            indent.c_str());
-    fprintf(out, "%s    buff[pos] = (byte) (val);\n", indent.c_str());
-    fprintf(out, "%s    buff[pos + 1] = (byte) (val >> 8);\n", indent.c_str());
-    fprintf(out, "%s    buff[pos + 2] = (byte) (val >> 16);\n", indent.c_str());
-    fprintf(out, "%s    buff[pos + 3] = (byte) (val >> 24);\n", indent.c_str());
-    fprintf(out, "%s    buff[pos + 4] = (byte) (val >> 32);\n", indent.c_str());
-    fprintf(out, "%s    buff[pos + 5] = (byte) (val >> 40);\n", indent.c_str());
-    fprintf(out, "%s    buff[pos + 6] = (byte) (val >> 48);\n", indent.c_str());
-    fprintf(out, "%s    buff[pos + 7] = (byte) (val >> 56);\n", indent.c_str());
-    fprintf(out, "%s    return;\n", indent.c_str());
-    fprintf(out, "%s}\n", indent.c_str());
-    fprintf(out, "\n");
-
-    if (requiredHelpers & JAVA_MODULE_REQUIRES_FLOAT) {
-        fprintf(out, "%sprivate static void copyFloat(byte[] buff, int pos, float val) {\n",
-                indent.c_str());
-        fprintf(out, "%s    copyInt(buff, pos, Float.floatToIntBits(val));\n", indent.c_str());
-        fprintf(out, "%s    return;\n", indent.c_str());
-        fprintf(out, "%s}\n", indent.c_str());
-        fprintf(out, "\n");
-    }
-
-    if (requiredHelpers & JAVA_MODULE_REQUIRES_ATTRIBUTION) {
-        fprintf(out, "%sprivate static void writeAttributionChain(byte[] buff, int pos",
-                indent.c_str());
-        for (auto chainField : attributionDecl.fields) {
-            fprintf(out, ", %s[] %s", java_type_name(chainField.javaType), chainField.name.c_str());
-        }
-        fprintf(out, ") {\n");
-
-        const char* uidName = attributionDecl.fields.front().name.c_str();
-        const char* tagName = attributionDecl.fields.back().name.c_str();
-
-        // Write the first list begin.
-        fprintf(out, "%s    buff[pos] = LIST_TYPE;\n", indent.c_str());
-        fprintf(out, "%s    buff[pos + 1] = (byte) (%s.length);\n", indent.c_str(), tagName);
-        fprintf(out, "%s    pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
-
-        // Iterate through the attribution chain and write the nodes.
-        fprintf(out, "%s    for (int i = 0; i < %s.length; i++) {\n", indent.c_str(), tagName);
-        // Write the list begin.
-        fprintf(out, "%s        buff[pos] = LIST_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        buff[pos + 1] = %lu;\n", indent.c_str(),
-                attributionDecl.fields.size());
-        fprintf(out, "%s        pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
-
-        // Write the uid.
-        fprintf(out, "%s        buff[pos] = INT_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        copyInt(buff, pos + 1, %s[i]);\n", indent.c_str(), uidName);
-        fprintf(out, "%s        pos += INT_TYPE_SIZE;\n", indent.c_str());
-
-        // Write the tag.
-        fprintf(out, "%s        String %sStr = (%s[i] == null) ? \"\" : %s[i];\n", indent.c_str(),
-                tagName, tagName, tagName);
-        fprintf(out,
-                "%s        byte[] %sByte = "
-                "%sStr.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n",
-                indent.c_str(), tagName, tagName);
-        fprintf(out, "%s        buff[pos] = STRING_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        copyInt(buff, pos + 1, %sByte.length);\n", indent.c_str(), tagName);
-        fprintf(out,
-                "%s        System.arraycopy("
-                "%sByte, 0, buff, pos + STRING_TYPE_OVERHEAD, %sByte.length);\n",
-                indent.c_str(), tagName, tagName);
-        fprintf(out, "%s        pos += STRING_TYPE_OVERHEAD + %sByte.length;\n", indent.c_str(),
-                tagName);
-        fprintf(out, "%s    }\n", indent.c_str());
-        fprintf(out, "%s}\n", indent.c_str());
-        fprintf(out, "\n");
-    }
-
-    if (requiredHelpers & JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS) {
-        fprintf(out,
-                "%sprivate static void writeKeyValuePairs(byte[] buff, int pos, "
-                "byte numPairs,\n",
-                indent.c_str());
-        fprintf(out, "%s        final android.util.SparseIntArray intMap,\n", indent.c_str());
-        fprintf(out, "%s        final android.util.SparseLongArray longMap,\n", indent.c_str());
-        fprintf(out, "%s        final android.util.SparseArray<String> stringMap,\n",
-                indent.c_str());
-        fprintf(out, "%s        final android.util.SparseArray<Float> floatMap) {\n",
-                indent.c_str());
-
-        // Start list of lists.
-        fprintf(out, "%s    buff[pos] = LIST_TYPE;\n", indent.c_str());
-        fprintf(out, "%s    buff[pos + 1] = (byte) numPairs;\n", indent.c_str());
-        fprintf(out, "%s    pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
-
-        // Write integers.
-        fprintf(out, "%s    final int intMapSize = null == intMap ? 0 : intMap.size();\n",
-                indent.c_str());
-        fprintf(out, "%s    for (int i = 0; i < intMapSize; i++) {\n", indent.c_str());
-        fprintf(out, "%s        buff[pos] = LIST_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        buff[pos + 1] = (byte) 2;\n", indent.c_str());
-        fprintf(out, "%s        pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
-        fprintf(out, "%s        final int key = intMap.keyAt(i);\n", indent.c_str());
-        fprintf(out, "%s        final int value = intMap.valueAt(i);\n", indent.c_str());
-        fprintf(out, "%s        buff[pos] = INT_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        copyInt(buff, pos + 1, key);\n", indent.c_str());
-        fprintf(out, "%s        pos += INT_TYPE_SIZE;\n", indent.c_str());
-        fprintf(out, "%s        buff[pos] = INT_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        copyInt(buff, pos + 1, value);\n", indent.c_str());
-        fprintf(out, "%s        pos += INT_TYPE_SIZE;\n", indent.c_str());
-        fprintf(out, "%s    }\n", indent.c_str());
-
-        // Write longs.
-        fprintf(out, "%s    final int longMapSize = null == longMap ? 0 : longMap.size();\n",
-                indent.c_str());
-        fprintf(out, "%s    for (int i = 0; i < longMapSize; i++) {\n", indent.c_str());
-        fprintf(out, "%s        buff[pos] = LIST_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        buff[pos + 1] = (byte) 2;\n", indent.c_str());
-        fprintf(out, "%s        pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
-        fprintf(out, "%s        final int key = longMap.keyAt(i);\n", indent.c_str());
-        fprintf(out, "%s        final long value = longMap.valueAt(i);\n", indent.c_str());
-        fprintf(out, "%s        buff[pos] = INT_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        copyInt(buff, pos + 1, key);\n", indent.c_str());
-        fprintf(out, "%s        pos += INT_TYPE_SIZE;\n", indent.c_str());
-        fprintf(out, "%s        buff[pos] = LONG_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        copyLong(buff, pos + 1, value);\n", indent.c_str());
-        fprintf(out, "%s        pos += LONG_TYPE_SIZE;\n", indent.c_str());
-        fprintf(out, "%s    }\n", indent.c_str());
-
-        // Write Strings.
-        fprintf(out,
-                "%s    final int stringMapSize = null == stringMap ? 0 : "
-                "stringMap.size();\n",
-                indent.c_str());
-        fprintf(out, "%s    for (int i = 0; i < stringMapSize; i++) {\n", indent.c_str());
-        fprintf(out, "%s        buff[pos] = LIST_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        buff[pos + 1] = (byte) 2;\n", indent.c_str());
-        fprintf(out, "%s        pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
-        fprintf(out, "%s        final int key = stringMap.keyAt(i);\n", indent.c_str());
-        fprintf(out, "%s        final String value = stringMap.valueAt(i);\n", indent.c_str());
-        fprintf(out,
-                "%s        final byte[] valueBytes = "
-                "value.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n",
-                indent.c_str());
-        fprintf(out, "%s        buff[pos] = INT_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        copyInt(buff, pos + 1, key);\n", indent.c_str());
-        fprintf(out, "%s        pos += INT_TYPE_SIZE;\n", indent.c_str());
-        fprintf(out, "%s        buff[pos] = STRING_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        copyInt(buff, pos + 1, valueBytes.length);\n", indent.c_str());
-        fprintf(out,
-                "%s        System.arraycopy("
-                "valueBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, "
-                "valueBytes.length);\n",
-                indent.c_str());
-        fprintf(out, "%s        pos += STRING_TYPE_OVERHEAD + valueBytes.length;\n",
-                indent.c_str());
-        fprintf(out, "%s    }\n", indent.c_str());
-
-        // Write floats.
-        fprintf(out,
-                "%s    final int floatMapSize = null == floatMap ? 0 : "
-                "floatMap.size();\n",
-                indent.c_str());
-        fprintf(out, "%s    for (int i = 0; i < floatMapSize; i++) {\n", indent.c_str());
-        fprintf(out, "%s        buff[pos] = LIST_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        buff[pos + 1] = (byte) 2;\n", indent.c_str());
-        fprintf(out, "%s        pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
-        fprintf(out, "%s        final int key = floatMap.keyAt(i);\n", indent.c_str());
-        fprintf(out, "%s        final float value = floatMap.valueAt(i);\n", indent.c_str());
-        fprintf(out, "%s        buff[pos] = INT_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        copyInt(buff, pos + 1, key);\n", indent.c_str());
-        fprintf(out, "%s        pos += INT_TYPE_SIZE;\n", indent.c_str());
-        fprintf(out, "%s        buff[pos] = FLOAT_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        copyFloat(buff, pos + 1, value);\n", indent.c_str());
-        fprintf(out, "%s        pos += FLOAT_TYPE_SIZE;\n", indent.c_str());
-        fprintf(out, "%s    }\n", indent.c_str());
-        fprintf(out, "%s}\n", indent.c_str());
-        fprintf(out, "\n");
-    }
-}
-
-// This method is called in main.cpp to generate StatsLog for modules that's
-// compatible with Q at compile-time.
-int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms,
-                                      const AtomDecl& attributionDecl, const string& javaClass,
-                                      const string& javaPackage, const bool supportWorkSource) {
-    // Print prelude
-    fprintf(out, "// This file is autogenerated\n");
-    fprintf(out, "\n");
-    fprintf(out, "package %s;\n", javaPackage.c_str());
-    fprintf(out, "\n");
-    fprintf(out, "import static java.nio.charset.StandardCharsets.UTF_8;\n");
-    fprintf(out, "\n");
-    fprintf(out, "import android.util.StatsLog;\n");
-    fprintf(out, "import android.os.SystemClock;\n");
-    fprintf(out, "\n");
-    fprintf(out, "\n");
-    fprintf(out, "/**\n");
-    fprintf(out, " * Utility class for logging statistics events.\n");
-    fprintf(out, " */\n");
-    fprintf(out, "public class %s {\n", javaClass.c_str());
-
-    write_java_q_logging_constants(out, "    ");
-
-    write_java_atom_codes(out, atoms);
-
-    write_java_enum_values(out, atoms);
-
-    int errors = 0;
-    // Print write methods
-    fprintf(out, "    // Write methods\n");
-    errors += write_java_methods_q_schema(out, atoms.signatureInfoMap, attributionDecl, "    ");
-    errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap);
-    if (supportWorkSource) {
-        errors += write_java_work_source_methods(out, atoms.signatureInfoMap);
-    }
-
-    fprintf(out, "}\n");
-
-    return errors;
-}
-
-}  // namespace stats_log_api_gen
-}  // namespace android
diff --git a/tools/stats_log_api_gen/java_writer_q.h b/tools/stats_log_api_gen/java_writer_q.h
deleted file mode 100644
index c511a84..0000000
--- a/tools/stats_log_api_gen/java_writer_q.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2019, 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.
- */
-
-#pragma once
-
-#include <stdio.h>
-#include <string.h>
-
-#include <map>
-#include <set>
-#include <vector>
-
-#include "Collation.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-using namespace std;
-
-void write_java_q_logging_constants(FILE* out, const string& indent);
-
-int write_java_methods_q_schema(FILE* out, const SignatureInfoMap& signatureInfoMap,
-                                const AtomDecl& attributionDecl, const string& indent);
-
-void write_java_helpers_for_q_schema_methods(FILE* out, const AtomDecl& attributionDecl,
-                                             const int requiredHelpers, const string& indent);
-
-int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms,
-                                      const AtomDecl& attributionDecl, const string& javaClass,
-                                      const string& javaPackage, const bool supportWorkSource);
-
-}  // namespace stats_log_api_gen
-}  // namespace android
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
deleted file mode 100644
index 416dfdd..0000000
--- a/tools/stats_log_api_gen/main.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-
-#include <getopt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <map>
-#include <set>
-#include <vector>
-
-#include "Collation.h"
-#include "frameworks/proto_logging/stats/atoms.pb.h"
-#include "java_writer.h"
-#include "java_writer_q.h"
-#include "native_writer.h"
-#include "utils.h"
-
-using namespace google::protobuf;
-using namespace std;
-
-namespace android {
-namespace stats_log_api_gen {
-
-using android::os::statsd::Atom;
-
-static void print_usage() {
-    fprintf(stderr, "usage: stats-log-api-gen OPTIONS\n");
-    fprintf(stderr, "\n");
-    fprintf(stderr, "OPTIONS\n");
-    fprintf(stderr, "  --cpp FILENAME       the header file to output for write helpers\n");
-    fprintf(stderr, "  --header FILENAME    the cpp file to output for write helpers\n");
-    fprintf(stderr, "  --help               this message\n");
-    fprintf(stderr, "  --java FILENAME      the java file to output\n");
-    fprintf(stderr, "  --module NAME        optional, module name to generate outputs for\n");
-    fprintf(stderr,
-            "  --namespace COMMA,SEP,NAMESPACE   required for cpp/header with "
-            "module\n");
-    fprintf(stderr,
-            "                                    comma separated namespace of "
-            "the files\n");
-    fprintf(stderr,
-            "  --importHeader NAME  required for cpp/jni to say which header to "
-            "import "
-            "for write helpers\n");
-    fprintf(stderr, "  --javaPackage PACKAGE             the package for the java file.\n");
-    fprintf(stderr, "                                    required for java with module\n");
-    fprintf(stderr, "  --javaClass CLASS    the class name of the java class.\n");
-    fprintf(stderr, "                       Optional for Java with module.\n");
-    fprintf(stderr, "                       Default is \"StatsLogInternal\"\n");
-    fprintf(stderr, "  --supportQ           Include runtime support for Android Q.\n");
-    fprintf(stderr,
-            "  --worksource         Include support for logging WorkSource "
-            "objects.\n");
-    fprintf(stderr,
-            "  --compileQ           Include compile-time support for Android Q "
-            "(Java only).\n");
-}
-
-/**
- * Do the argument parsing and execute the tasks.
- */
-static int run(int argc, char const* const* argv) {
-    string cppFilename;
-    string headerFilename;
-    string javaFilename;
-    string javaPackage;
-    string javaClass;
-
-    string moduleName = DEFAULT_MODULE_NAME;
-    string cppNamespace = DEFAULT_CPP_NAMESPACE;
-    string cppHeaderImport = DEFAULT_CPP_HEADER_IMPORT;
-    bool supportQ = false;
-    bool supportWorkSource = false;
-    bool compileQ = false;
-
-    int index = 1;
-    while (index < argc) {
-        if (0 == strcmp("--help", argv[index])) {
-            print_usage();
-            return 0;
-        } else if (0 == strcmp("--cpp", argv[index])) {
-            index++;
-            if (index >= argc) {
-                print_usage();
-                return 1;
-            }
-            cppFilename = argv[index];
-        } else if (0 == strcmp("--header", argv[index])) {
-            index++;
-            if (index >= argc) {
-                print_usage();
-                return 1;
-            }
-            headerFilename = argv[index];
-        } else if (0 == strcmp("--java", argv[index])) {
-            index++;
-            if (index >= argc) {
-                print_usage();
-                return 1;
-            }
-            javaFilename = argv[index];
-        } else if (0 == strcmp("--module", argv[index])) {
-            index++;
-            if (index >= argc) {
-                print_usage();
-                return 1;
-            }
-            moduleName = argv[index];
-        } else if (0 == strcmp("--namespace", argv[index])) {
-            index++;
-            if (index >= argc) {
-                print_usage();
-                return 1;
-            }
-            cppNamespace = argv[index];
-        } else if (0 == strcmp("--importHeader", argv[index])) {
-            index++;
-            if (index >= argc) {
-                print_usage();
-                return 1;
-            }
-            cppHeaderImport = argv[index];
-        } else if (0 == strcmp("--javaPackage", argv[index])) {
-            index++;
-            if (index >= argc) {
-                print_usage();
-                return 1;
-            }
-            javaPackage = argv[index];
-        } else if (0 == strcmp("--javaClass", argv[index])) {
-            index++;
-            if (index >= argc) {
-                print_usage();
-                return 1;
-            }
-            javaClass = argv[index];
-        } else if (0 == strcmp("--supportQ", argv[index])) {
-            supportQ = true;
-        } else if (0 == strcmp("--worksource", argv[index])) {
-            supportWorkSource = true;
-        } else if (0 == strcmp("--compileQ", argv[index])) {
-            compileQ = true;
-        }
-
-        index++;
-    }
-
-    if (cppFilename.size() == 0 && headerFilename.size() == 0 && javaFilename.size() == 0) {
-        print_usage();
-        return 1;
-    }
-
-    if (DEFAULT_MODULE_NAME == moduleName && (supportQ || compileQ)) {
-        // Support for Q schema is not needed for default module.
-        fprintf(stderr, "%s cannot support Q schema\n", moduleName.c_str());
-        return 1;
-    }
-
-    if (supportQ && compileQ) {
-        // Runtime Q support is redundant if compile-time Q support is required.
-        fprintf(stderr, "Cannot specify compileQ and supportQ simultaneously.\n");
-        return 1;
-    }
-
-    // Collate the parameters
-    Atoms atoms;
-    int errorCount = collate_atoms(Atom::descriptor(), moduleName, &atoms);
-    if (errorCount != 0) {
-        return 1;
-    }
-
-    AtomDecl attributionDecl;
-    vector<java_type_t> attributionSignature;
-    collate_atom(android::os::statsd::AttributionNode::descriptor(), &attributionDecl,
-                 &attributionSignature);
-
-    // Write the .cpp file
-    if (cppFilename.size() != 0) {
-        FILE* out = fopen(cppFilename.c_str(), "w");
-        if (out == NULL) {
-            fprintf(stderr, "Unable to open file for write: %s\n", cppFilename.c_str());
-            return 1;
-        }
-        // If this is for a specific module, the namespace must also be provided.
-        if (moduleName != DEFAULT_MODULE_NAME && cppNamespace == DEFAULT_CPP_NAMESPACE) {
-            fprintf(stderr, "Must supply --namespace if supplying a specific module\n");
-            return 1;
-        }
-        // If this is for a specific module, the header file to import must also be
-        // provided.
-        if (moduleName != DEFAULT_MODULE_NAME && cppHeaderImport == DEFAULT_CPP_HEADER_IMPORT) {
-            fprintf(stderr, "Must supply --headerImport if supplying a specific module\n");
-            return 1;
-        }
-        errorCount = android::stats_log_api_gen::write_stats_log_cpp(
-                out, atoms, attributionDecl, cppNamespace, cppHeaderImport, supportQ);
-        fclose(out);
-    }
-
-    // Write the .h file
-    if (headerFilename.size() != 0) {
-        FILE* out = fopen(headerFilename.c_str(), "w");
-        if (out == NULL) {
-            fprintf(stderr, "Unable to open file for write: %s\n", headerFilename.c_str());
-            return 1;
-        }
-        // If this is for a specific module, the namespace must also be provided.
-        if (moduleName != DEFAULT_MODULE_NAME && cppNamespace == DEFAULT_CPP_NAMESPACE) {
-            fprintf(stderr, "Must supply --namespace if supplying a specific module\n");
-        }
-        errorCount = android::stats_log_api_gen::write_stats_log_header(out, atoms, attributionDecl,
-                                                                        cppNamespace);
-        fclose(out);
-    }
-
-    // Write the .java file
-    if (javaFilename.size() != 0) {
-        if (javaClass.size() == 0) {
-            fprintf(stderr, "Must supply --javaClass if supplying a Java filename");
-            return 1;
-        }
-
-        if (javaPackage.size() == 0) {
-            fprintf(stderr, "Must supply --javaPackage if supplying a Java filename");
-            return 1;
-        }
-
-        if (moduleName.size() == 0) {
-            fprintf(stderr, "Must supply --module if supplying a Java filename");
-            return 1;
-        }
-
-        FILE* out = fopen(javaFilename.c_str(), "w");
-        if (out == NULL) {
-            fprintf(stderr, "Unable to open file for write: %s\n", javaFilename.c_str());
-            return 1;
-        }
-
-        if (compileQ) {
-            errorCount = android::stats_log_api_gen::write_stats_log_java_q_for_module(
-                    out, atoms, attributionDecl, javaClass, javaPackage, supportWorkSource);
-        } else {
-            errorCount = android::stats_log_api_gen::write_stats_log_java(
-                    out, atoms, attributionDecl, javaClass, javaPackage, supportQ,
-                    supportWorkSource);
-        }
-
-        fclose(out);
-    }
-
-    return errorCount;
-}
-
-}  // namespace stats_log_api_gen
-}  // namespace android
-
-/**
- * Main.
- */
-int main(int argc, char const* const* argv) {
-    GOOGLE_PROTOBUF_VERIFY_VERSION;
-
-    return android::stats_log_api_gen::run(argc, argv);
-}
diff --git a/tools/stats_log_api_gen/native_writer.cpp b/tools/stats_log_api_gen/native_writer.cpp
deleted file mode 100644
index 0c6c009..0000000
--- a/tools/stats_log_api_gen/native_writer.cpp
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * Copyright (C) 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "native_writer.h"
-
-#include "utils.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-static void write_native_annotation_constants(FILE* out) {
-    fprintf(out, "// Annotation constants.\n");
-
-    for (const auto& [id, name] : ANNOTATION_ID_CONSTANTS) {
-        fprintf(out, "const uint8_t %s = %hhu;\n", name.c_str(), id);
-    }
-    fprintf(out, "\n");
-}
-
-static void write_annotations(FILE* out, int argIndex,
-                              const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet,
-                              const string& methodPrefix, const string& methodSuffix) {
-    FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt =
-            fieldNumberToAtomDeclSet.find(argIndex);
-    if (fieldNumberToAtomDeclSet.end() == fieldNumberToAtomDeclSetIt) {
-        return;
-    }
-    const AtomDeclSet& atomDeclSet = fieldNumberToAtomDeclSetIt->second;
-    for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) {
-        const string atomConstant = make_constant_name(atomDecl->name);
-        fprintf(out, "    if (%s == code) {\n", atomConstant.c_str());
-        const AnnotationSet& annotations = atomDecl->fieldNumberToAnnotations.at(argIndex);
-        int resetState = -1;
-        int defaultState = -1;
-        for (const shared_ptr<Annotation>& annotation : annotations) {
-            const string& annotationConstant = ANNOTATION_ID_CONSTANTS.at(annotation->annotationId);
-            switch (annotation->type) {
-                case ANNOTATION_TYPE_INT:
-                    if (ANNOTATION_ID_TRIGGER_STATE_RESET == annotation->annotationId) {
-                        resetState = annotation->value.intValue;
-                    } else if (ANNOTATION_ID_DEFAULT_STATE == annotation->annotationId) {
-                        defaultState = annotation->value.intValue;
-                    } else {
-                        fprintf(out, "        %saddInt32Annotation(%s%s, %d);\n",
-                                methodPrefix.c_str(), methodSuffix.c_str(),
-                                annotationConstant.c_str(), annotation->value.intValue);
-                    }
-                    break;
-                case ANNOTATION_TYPE_BOOL:
-                    // TODO(b/151786433): Write annotation constant name instead of
-                    // annotation id literal.
-                    fprintf(out, "        %saddBoolAnnotation(%s%s, %s);\n", methodPrefix.c_str(),
-                            methodSuffix.c_str(), annotationConstant.c_str(),
-                            annotation->value.boolValue ? "true" : "false");
-                    break;
-                default:
-                    break;
-            }
-        }
-        if (defaultState != -1 && resetState != -1) {
-            const string& annotationConstant =
-                    ANNOTATION_ID_CONSTANTS.at(ANNOTATION_ID_TRIGGER_STATE_RESET);
-            fprintf(out, "        if (arg%d == %d) {\n", argIndex, resetState);
-            fprintf(out, "            %saddInt32Annotation(%s%s, %d);\n", methodPrefix.c_str(),
-                    methodSuffix.c_str(), annotationConstant.c_str(), defaultState);
-            fprintf(out, "        }\n");
-        }
-        fprintf(out, "    }\n");
-    }
-}
-
-static int write_native_stats_write_methods(FILE* out, const Atoms& atoms,
-                                            const AtomDecl& attributionDecl, const bool supportQ) {
-    fprintf(out, "\n");
-    for (auto signatureInfoMapIt = atoms.signatureInfoMap.begin();
-         signatureInfoMapIt != atoms.signatureInfoMap.end(); signatureInfoMapIt++) {
-        vector<java_type_t> signature = signatureInfoMapIt->first;
-        const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second;
-        // Key value pairs not supported in native.
-        if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
-            continue;
-        }
-        write_native_method_signature(out, "int stats_write", signature, attributionDecl, " {");
-
-        int argIndex = 1;
-        if (supportQ) {
-            fprintf(out, "    StatsEventCompat event;\n");
-            fprintf(out, "    event.setAtomId(code);\n");
-            write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet, "event.", "");
-            for (vector<java_type_t>::const_iterator arg = signature.begin();
-                 arg != signature.end(); arg++) {
-                switch (*arg) {
-                    case JAVA_TYPE_ATTRIBUTION_CHAIN: {
-                        const char* uidName = attributionDecl.fields.front().name.c_str();
-                        const char* tagName = attributionDecl.fields.back().name.c_str();
-                        fprintf(out, "    event.writeAttributionChain(%s, %s_length, %s);\n",
-                                uidName, uidName, tagName);
-                        break;
-                    }
-                    case JAVA_TYPE_BYTE_ARRAY:
-                        fprintf(out, "    event.writeByteArray(arg%d.arg, arg%d.arg_length);\n",
-                                argIndex, argIndex);
-                        break;
-                    case JAVA_TYPE_BOOLEAN:
-                        fprintf(out, "    event.writeBool(arg%d);\n", argIndex);
-                        break;
-                    case JAVA_TYPE_INT:  // Fall through.
-                    case JAVA_TYPE_ENUM:
-                        fprintf(out, "    event.writeInt32(arg%d);\n", argIndex);
-                        break;
-                    case JAVA_TYPE_FLOAT:
-                        fprintf(out, "    event.writeFloat(arg%d);\n", argIndex);
-                        break;
-                    case JAVA_TYPE_LONG:
-                        fprintf(out, "    event.writeInt64(arg%d);\n", argIndex);
-                        break;
-                    case JAVA_TYPE_STRING:
-                        fprintf(out, "    event.writeString(arg%d);\n", argIndex);
-                        break;
-                    default:
-                        // Unsupported types: OBJECT, DOUBLE, KEY_VALUE_PAIRS.
-                        fprintf(stderr, "Encountered unsupported type.");
-                        return 1;
-                }
-                write_annotations(out, argIndex, fieldNumberToAtomDeclSet, "event.", "");
-                argIndex++;
-            }
-            fprintf(out, "    return event.writeToSocket();\n");
-        } else {
-            fprintf(out, "    AStatsEvent* event = AStatsEvent_obtain();\n");
-            fprintf(out, "    AStatsEvent_setAtomId(event, code);\n");
-            write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet, "AStatsEvent_",
-                              "event, ");
-            for (vector<java_type_t>::const_iterator arg = signature.begin();
-                 arg != signature.end(); arg++) {
-                switch (*arg) {
-                    case JAVA_TYPE_ATTRIBUTION_CHAIN: {
-                        const char* uidName = attributionDecl.fields.front().name.c_str();
-                        const char* tagName = attributionDecl.fields.back().name.c_str();
-                        fprintf(out,
-                                "    AStatsEvent_writeAttributionChain(event, "
-                                "reinterpret_cast<const uint32_t*>(%s), %s.data(), "
-                                "static_cast<uint8_t>(%s_length));\n",
-                                uidName, tagName, uidName);
-                        break;
-                    }
-                    case JAVA_TYPE_BYTE_ARRAY:
-                        fprintf(out,
-                                "    AStatsEvent_writeByteArray(event, "
-                                "reinterpret_cast<const uint8_t*>(arg%d.arg), "
-                                "arg%d.arg_length);\n",
-                                argIndex, argIndex);
-                        break;
-                    case JAVA_TYPE_BOOLEAN:
-                        fprintf(out, "    AStatsEvent_writeBool(event, arg%d);\n", argIndex);
-                        break;
-                    case JAVA_TYPE_INT:  // Fall through.
-                    case JAVA_TYPE_ENUM:
-                        fprintf(out, "    AStatsEvent_writeInt32(event, arg%d);\n", argIndex);
-                        break;
-                    case JAVA_TYPE_FLOAT:
-                        fprintf(out, "    AStatsEvent_writeFloat(event, arg%d);\n", argIndex);
-                        break;
-                    case JAVA_TYPE_LONG:
-                        fprintf(out, "    AStatsEvent_writeInt64(event, arg%d);\n", argIndex);
-                        break;
-                    case JAVA_TYPE_STRING:
-                        fprintf(out, "    AStatsEvent_writeString(event, arg%d);\n", argIndex);
-                        break;
-                    default:
-                        // Unsupported types: OBJECT, DOUBLE, KEY_VALUE_PAIRS
-                        fprintf(stderr, "Encountered unsupported type.");
-                        return 1;
-                }
-                write_annotations(out, argIndex, fieldNumberToAtomDeclSet, "AStatsEvent_",
-                                  "event, ");
-                argIndex++;
-            }
-            fprintf(out, "    const int ret = AStatsEvent_write(event);\n");
-            fprintf(out, "    AStatsEvent_release(event);\n");
-            fprintf(out, "    return ret;\n");
-        }
-        fprintf(out, "}\n\n");
-    }
-    return 0;
-}
-
-static void write_native_stats_write_non_chained_methods(FILE* out, const Atoms& atoms,
-                                                         const AtomDecl& attributionDecl) {
-    fprintf(out, "\n");
-    for (auto signature_it = atoms.nonChainedSignatureInfoMap.begin();
-         signature_it != atoms.nonChainedSignatureInfoMap.end(); signature_it++) {
-        vector<java_type_t> signature = signature_it->first;
-        // Key value pairs not supported in native.
-        if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
-            continue;
-        }
-
-        write_native_method_signature(out, "int stats_write_non_chained", signature,
-                                      attributionDecl, " {");
-
-        vector<java_type_t> newSignature;
-
-        // First two args form the attribution node so size goes down by 1.
-        newSignature.reserve(signature.size() - 1);
-
-        // First arg is Attribution Chain.
-        newSignature.push_back(JAVA_TYPE_ATTRIBUTION_CHAIN);
-
-        // Followed by the originial signature except the first 2 args.
-        newSignature.insert(newSignature.end(), signature.begin() + 2, signature.end());
-
-        const char* uidName = attributionDecl.fields.front().name.c_str();
-        const char* tagName = attributionDecl.fields.back().name.c_str();
-        fprintf(out, "    const int32_t* %s = &arg1;\n", uidName);
-        fprintf(out, "    const size_t %s_length = 1;\n", uidName);
-        fprintf(out, "    const std::vector<char const*> %s(1, arg2);\n", tagName);
-        fprintf(out, "    return ");
-        write_native_method_call(out, "stats_write", newSignature, attributionDecl, 2);
-
-        fprintf(out, "}\n\n");
-    }
-}
-
-static void write_native_method_header(FILE* out, const string& methodName,
-                                       const SignatureInfoMap& signatureInfoMap,
-                                       const AtomDecl& attributionDecl) {
-    for (auto signatureInfoMapIt = signatureInfoMap.begin();
-         signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
-        vector<java_type_t> signature = signatureInfoMapIt->first;
-
-        // Key value pairs not supported in native.
-        if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
-            continue;
-        }
-        write_native_method_signature(out, methodName, signature, attributionDecl, ";");
-    }
-}
-
-int write_stats_log_cpp(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
-                        const string& cppNamespace, const string& importHeader,
-                        const bool supportQ) {
-    // Print prelude
-    fprintf(out, "// This file is autogenerated\n");
-    fprintf(out, "\n");
-
-    fprintf(out, "#include <%s>\n", importHeader.c_str());
-    if (supportQ) {
-        fprintf(out, "#include <StatsEventCompat.h>\n");
-    } else {
-        fprintf(out, "#include <stats_event.h>\n");
-    }
-
-    fprintf(out, "\n");
-    write_namespace(out, cppNamespace);
-
-    write_native_stats_write_methods(out, atoms, attributionDecl, supportQ);
-    write_native_stats_write_non_chained_methods(out, atoms, attributionDecl);
-
-    // Print footer
-    fprintf(out, "\n");
-    write_closing_namespace(out, cppNamespace);
-
-    return 0;
-}
-
-int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
-                           const string& cppNamespace) {
-    // Print prelude
-    fprintf(out, "// This file is autogenerated\n");
-    fprintf(out, "\n");
-    fprintf(out, "#pragma once\n");
-    fprintf(out, "\n");
-    fprintf(out, "#include <stdint.h>\n");
-    fprintf(out, "#include <vector>\n");
-    fprintf(out, "#include <map>\n");
-    fprintf(out, "#include <set>\n");
-    fprintf(out, "\n");
-
-    write_namespace(out, cppNamespace);
-    fprintf(out, "\n");
-    fprintf(out, "/*\n");
-    fprintf(out, " * API For logging statistics events.\n");
-    fprintf(out, " */\n");
-    fprintf(out, "\n");
-
-    write_native_atom_constants(out, atoms, attributionDecl);
-
-    // Print constants for the enum values.
-    fprintf(out, "//\n");
-    fprintf(out, "// Constants for enum values\n");
-    fprintf(out, "//\n\n");
-    for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
-         atomIt++) {
-        for (vector<AtomField>::const_iterator field = (*atomIt)->fields.begin();
-             field != (*atomIt)->fields.end(); field++) {
-            if (field->javaType == JAVA_TYPE_ENUM) {
-                fprintf(out, "// Values for %s.%s\n", (*atomIt)->message.c_str(),
-                        field->name.c_str());
-                for (map<int, string>::const_iterator value = field->enumValues.begin();
-                     value != field->enumValues.end(); value++) {
-                    fprintf(out, "const int32_t %s__%s__%s = %d;\n",
-                            make_constant_name((*atomIt)->message).c_str(),
-                            make_constant_name(field->name).c_str(),
-                            make_constant_name(value->second).c_str(), value->first);
-                }
-                fprintf(out, "\n");
-            }
-        }
-    }
-
-    write_native_annotation_constants(out);
-
-    fprintf(out, "struct BytesField {\n");
-    fprintf(out,
-            "  BytesField(char const* array, size_t len) : arg(array), "
-            "arg_length(len) {}\n");
-    fprintf(out, "  char const* arg;\n");
-    fprintf(out, "  size_t arg_length;\n");
-    fprintf(out, "};\n");
-    fprintf(out, "\n");
-
-    // Print write methods
-    fprintf(out, "//\n");
-    fprintf(out, "// Write methods\n");
-    fprintf(out, "//\n");
-    write_native_method_header(out, "int stats_write", atoms.signatureInfoMap, attributionDecl);
-
-    fprintf(out, "//\n");
-    fprintf(out, "// Write flattened methods\n");
-    fprintf(out, "//\n");
-    write_native_method_header(out, "int stats_write_non_chained", atoms.nonChainedSignatureInfoMap,
-                               attributionDecl);
-
-    fprintf(out, "\n");
-    write_closing_namespace(out, cppNamespace);
-
-    return 0;
-}
-
-}  // namespace stats_log_api_gen
-}  // namespace android
diff --git a/tools/stats_log_api_gen/native_writer.h b/tools/stats_log_api_gen/native_writer.h
deleted file mode 100644
index 264d4db..0000000
--- a/tools/stats_log_api_gen/native_writer.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2019, 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.
- */
-
-#pragma once
-
-#include <stdio.h>
-#include <string.h>
-
-#include "Collation.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-using namespace std;
-
-int write_stats_log_cpp(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
-                        const string& cppNamespace, const string& importHeader,
-                        const bool supportQ);
-
-int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
-                           const string& cppNamespace);
-
-}  // namespace stats_log_api_gen
-}  // namespace android
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
deleted file mode 100644
index a3ea785..0000000
--- a/tools/stats_log_api_gen/test.proto
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-syntax = "proto2";
-
-import "frameworks/proto_logging/stats/atoms.proto";
-import "frameworks/proto_logging/stats/atom_field_options.proto";
-
-package android.stats_log_api_gen;
-
-message IntAtom {
-    optional int32 field1 = 1;
-}
-
-message AnotherIntAtom {
-    optional int32 field1 = 1;
-}
-
-message OutOfOrderAtom {
-    optional int32 field2 = 2;
-    optional int32 field1 = 1;
-}
-
-enum AnEnum {
-    VALUE0 = 0;
-    VALUE1 = 1;
-}
-
-message AllTypesAtom {
-  repeated android.os.statsd.AttributionNode attribution_chain = 1;
-  optional float float_field = 2;
-  optional int64 int64_field = 3;
-  optional uint64 uint64_field = 4;
-  optional int32 int32_field = 5;
-  optional fixed64 fixed64_field = 6;
-  optional fixed32 fixed32_field = 7;
-  optional bool bool_field = 8;
-  optional string string_field = 9;
-  optional uint32 uint32_field = 10;
-  optional AnEnum enum_field = 11;
-  optional sfixed32 sfixed32_field = 12;
-  optional sfixed64 sfixed64_field = 13;
-  optional sint32 sint32_field = 14;
-  optional sint64 sint64_field = 15;
-}
-
-message Event {
-    oneof event {
-        OutOfOrderAtom out_of_order_atom = 2;
-        IntAtom int_atom = 1;
-        AnotherIntAtom another_int_atom = 3;
-        AllTypesAtom all_types_atom = 4;
-    }
-}
-
-message BadTypesAtom {
-    optional IntAtom bad_int_atom = 1;
-    optional bytes bad_bytes = 2;
-    repeated int32 repeated_field = 3;
-    optional double double_field = 4;
-}
-
-message BadTypesEvent {
-    oneof event {
-        BadTypesAtom bad_types_atom = 1;
-    }
-}
-
-message BadSkippedFieldSingleAtom {
-    optional int32 field2 = 2;
-}
-
-message BadSkippedFieldSingle {
-    oneof event {
-        BadSkippedFieldSingleAtom bad = 1;
-    }
-}
-
-message BadSkippedFieldMultipleAtom {
-    optional int32 field1 = 1;
-    optional int32 field3 = 3;
-    optional int32 field5 = 5;
-}
-
-message BadSkippedFieldMultiple {
-    oneof event {
-        BadSkippedFieldMultipleAtom bad = 1;
-    }
-}
-
-message BadAttributionNodePositionAtom {
-  optional int32 field1 = 1;
-  repeated android.os.statsd.AttributionNode attribution = 2;
-}
-
-message BadAttributionNodePosition {
-  oneof event { BadAttributionNodePositionAtom bad = 1; }
-}
-
-message GoodEventWithBinaryFieldAtom {
-    oneof event { GoodBinaryFieldAtom field1 = 1; }
-}
-
-message ComplexField {
-    optional string str = 1;
-}
-
-message GoodBinaryFieldAtom {
-    optional int32 field1 = 1;
-    optional ComplexField bf = 2 [(android.os.statsd.log_mode) = MODE_BYTES];
-}
-
-message BadEventWithBinaryFieldAtom {
-    oneof event { BadBinaryFieldAtom field1 = 1; }
-}
-
-message BadBinaryFieldAtom {
-    optional int32 field1 = 1;
-    optional ComplexField bf = 2;
-}
-
-message BadStateAtoms {
-    oneof event {
-        BadStateAtom1 bad1 = 1;
-        BadStateAtom2 bad2 = 2;
-        BadStateAtom3 bad3 = 3;
-    }
-}
-
-message GoodStateAtoms {
-    oneof event {
-        GoodStateAtom1 good1 = 1;
-        GoodStateAtom2 good2 = 2;
-    }
-}
-
-// The atom has only primary field but no exclusive state field.
-message BadStateAtom1 {
-    optional int32 uid = 1 [(android.os.statsd.state_field_option).primary_field = true];
-}
-
-// Only primative types can be annotated.
-message BadStateAtom2 {
-    repeated android.os.statsd.AttributionNode attribution = 1
-            [(android.os.statsd.state_field_option).primary_field = true];
-    optional int32 state = 2 [(android.os.statsd.state_field_option).exclusive_state = true];
-}
-
-// Having 2 exclusive state field in the atom means the atom is badly designed.
-// E.g., putting bluetooth state and wifi state in the same atom.
-message BadStateAtom3 {
-    optional int32 uid = 1 [(android.os.statsd.state_field_option).primary_field = true];
-    optional int32 state = 2 [(android.os.statsd.state_field_option).exclusive_state = true];
-    optional int32 state2 = 3 [(android.os.statsd.state_field_option).exclusive_state = true];
-}
-
-message GoodStateAtom1 {
-    optional int32 uid = 1 [(android.os.statsd.state_field_option).primary_field = true];
-    optional int32 state = 2 [(android.os.statsd.state_field_option).exclusive_state = true];
-}
-
-// Atoms can have exclusive state field, but no primary field. That means
-// the state is globally exclusive (e.g., DisplayState).
-message GoodStateAtom2 {
-    optional int32 uid = 1;
-    optional int32 state = 2 [(android.os.statsd.state_field_option).exclusive_state = true];
-}
-
-// We can have more than one primary fields. That means their combination is a
-// primary key.
-message GoodStateAtom3 {
-    optional int32 uid = 1 [(android.os.statsd.state_field_option).primary_field = true];
-    optional int32 tid = 2 [(android.os.statsd.state_field_option).primary_field = true];
-    optional int32 state = 3 [(android.os.statsd.state_field_option).exclusive_state = true];
-}
-
-message ModuleOneAtom {
-    optional int32 field = 1 [(android.os.statsd.is_uid) = true];
-}
-
-message ModuleTwoAtom {
-    optional int32 field = 1;
-}
-
-message ModuleOneAndTwoAtom {
-    optional int32 field = 1 [(android.os.statsd.state_field_option).exclusive_state = true];
-}
-
-message NoModuleAtom {
-    optional string field = 1;
-}
-
-message ModuleAtoms {
-    oneof event {
-        ModuleOneAtom module_one_atom = 1 [(android.os.statsd.module) = "module1"];
-        ModuleTwoAtom module_two_atom = 2 [(android.os.statsd.module) = "module2"];
-        ModuleOneAndTwoAtom module_one_and_two_atom = 3 [
-                (android.os.statsd.module) = "module1", (android.os.statsd.module) = "module2"
-        ];
-        NoModuleAtom no_module_atom = 4;
-    }
-}
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
deleted file mode 100644
index dbae588..0000000
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * Copyright (C) 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-#include <stdio.h>
-
-#include "Collation.h"
-#include "frameworks/base/tools/stats_log_api_gen/test.pb.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-using std::map;
-using std::set;
-using std::vector;
-
-/**
- * Return whether the map contains a vector of the elements provided.
- */
-static bool map_contains_vector(const SignatureInfoMap& s, int count, ...) {
-    va_list args;
-    vector<java_type_t> v;
-
-    va_start(args, count);
-    for (int i = 0; i < count; i++) {
-        v.push_back((java_type_t)va_arg(args, int));
-    }
-    va_end(args);
-
-    return s.find(v) != s.end();
-}
-
-/**
- * Expect that the provided map contains the elements provided.
- */
-#define EXPECT_MAP_CONTAINS_SIGNATURE(s, ...)                    \
-    do {                                                         \
-        int count = sizeof((int[]){__VA_ARGS__}) / sizeof(int);  \
-        EXPECT_TRUE(map_contains_vector(s, count, __VA_ARGS__)); \
-    } while (0)
-
-/** Expects that the provided atom has no enum values for any field. */
-#define EXPECT_NO_ENUM_FIELD(atom)                                           \
-    do {                                                                     \
-        for (vector<AtomField>::const_iterator field = atom->fields.begin(); \
-             field != atom->fields.end(); field++) {                         \
-            EXPECT_TRUE(field->enumValues.empty());                          \
-        }                                                                    \
-    } while (0)
-
-/** Expects that exactly one specific field has expected enum values. */
-#define EXPECT_HAS_ENUM_FIELD(atom, field_name, values)                      \
-    do {                                                                     \
-        for (vector<AtomField>::const_iterator field = atom->fields.begin(); \
-             field != atom->fields.end(); field++) {                         \
-            if (field->name == field_name) {                                 \
-                EXPECT_EQ(field->enumValues, values);                        \
-            } else {                                                         \
-                EXPECT_TRUE(field->enumValues.empty());                      \
-            }                                                                \
-        }                                                                    \
-    } while (0)
-
-/**
- * Test a correct collation, with all the types.
- */
-TEST(CollationTest, CollateStats) {
-    Atoms atoms;
-    int errorCount = collate_atoms(Event::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-
-    EXPECT_EQ(0, errorCount);
-    EXPECT_EQ(3ul, atoms.signatureInfoMap.size());
-
-    // IntAtom, AnotherIntAtom
-    EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_INT);
-
-    // OutOfOrderAtom
-    EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_INT, JAVA_TYPE_INT);
-
-    // AllTypesAtom
-    EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap,
-                                  JAVA_TYPE_ATTRIBUTION_CHAIN,  // AttributionChain
-                                  JAVA_TYPE_FLOAT,              // float
-                                  JAVA_TYPE_LONG,               // int64
-                                  JAVA_TYPE_LONG,               // uint64
-                                  JAVA_TYPE_INT,                // int32
-                                  JAVA_TYPE_LONG,               // fixed64
-                                  JAVA_TYPE_INT,                // fixed32
-                                  JAVA_TYPE_BOOLEAN,            // bool
-                                  JAVA_TYPE_STRING,             // string
-                                  JAVA_TYPE_INT,                // uint32
-                                  JAVA_TYPE_INT,                // AnEnum
-                                  JAVA_TYPE_INT,                // sfixed32
-                                  JAVA_TYPE_LONG,               // sfixed64
-                                  JAVA_TYPE_INT,                // sint32
-                                  JAVA_TYPE_LONG                // sint64
-    );
-
-    EXPECT_EQ(4ul, atoms.decls.size());
-
-    AtomDeclSet::const_iterator atomIt = atoms.decls.begin();
-    EXPECT_EQ(1, (*atomIt)->code);
-    EXPECT_EQ("int_atom", (*atomIt)->name);
-    EXPECT_EQ("IntAtom", (*atomIt)->message);
-    EXPECT_NO_ENUM_FIELD((*atomIt));
-    atomIt++;
-
-    EXPECT_EQ(2, (*atomIt)->code);
-    EXPECT_EQ("out_of_order_atom", (*atomIt)->name);
-    EXPECT_EQ("OutOfOrderAtom", (*atomIt)->message);
-    EXPECT_NO_ENUM_FIELD((*atomIt));
-    atomIt++;
-
-    EXPECT_EQ(3, (*atomIt)->code);
-    EXPECT_EQ("another_int_atom", (*atomIt)->name);
-    EXPECT_EQ("AnotherIntAtom", (*atomIt)->message);
-    EXPECT_NO_ENUM_FIELD((*atomIt));
-    atomIt++;
-
-    EXPECT_EQ(4, (*atomIt)->code);
-    EXPECT_EQ("all_types_atom", (*atomIt)->name);
-    EXPECT_EQ("AllTypesAtom", (*atomIt)->message);
-    map<int, string> enumValues;
-    enumValues[0] = "VALUE0";
-    enumValues[1] = "VALUE1";
-    EXPECT_HAS_ENUM_FIELD((*atomIt), "enum_field", enumValues);
-    atomIt++;
-
-    EXPECT_EQ(atoms.decls.end(), atomIt);
-}
-
-/**
- * Test that event class that contains stuff other than the atoms is rejected.
- */
-TEST(CollationTest, NonMessageTypeFails) {
-    Atoms atoms;
-    int errorCount = collate_atoms(IntAtom::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-
-    EXPECT_EQ(1, errorCount);
-}
-
-/**
- * Test that atoms that have non-primitive types or repeated fields are
- * rejected.
- */
-TEST(CollationTest, FailOnBadTypes) {
-    Atoms atoms;
-    int errorCount = collate_atoms(BadTypesEvent::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-
-    EXPECT_EQ(4, errorCount);
-}
-
-/**
- * Test that atoms that skip field numbers (in the first position) are rejected.
- */
-TEST(CollationTest, FailOnSkippedFieldsSingle) {
-    Atoms atoms;
-    int errorCount =
-            collate_atoms(BadSkippedFieldSingle::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-
-    EXPECT_EQ(1, errorCount);
-}
-
-/**
- * Test that atoms that skip field numbers (not in the first position, and
- * multiple times) are rejected.
- */
-TEST(CollationTest, FailOnSkippedFieldsMultiple) {
-    Atoms atoms;
-    int errorCount =
-            collate_atoms(BadSkippedFieldMultiple::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-
-    EXPECT_EQ(2, errorCount);
-}
-
-/**
- * Test that atoms that have an attribution chain not in the first position are
- * rejected.
- */
-TEST(CollationTest, FailBadAttributionNodePosition) {
-    Atoms atoms;
-    int errorCount =
-            collate_atoms(BadAttributionNodePosition::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-
-    EXPECT_EQ(1, errorCount);
-}
-
-TEST(CollationTest, FailOnBadStateAtomOptions) {
-    Atoms atoms;
-    int errorCount = collate_atoms(BadStateAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-
-    EXPECT_EQ(3, errorCount);
-}
-
-TEST(CollationTest, PassOnGoodStateAtomOptions) {
-    Atoms atoms;
-    int errorCount = collate_atoms(GoodStateAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-    EXPECT_EQ(0, errorCount);
-}
-
-TEST(CollationTest, PassOnGoodBinaryFieldAtom) {
-    Atoms atoms;
-    int errorCount =
-            collate_atoms(GoodEventWithBinaryFieldAtom::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-    EXPECT_EQ(0, errorCount);
-}
-
-TEST(CollationTest, FailOnBadBinaryFieldAtom) {
-    Atoms atoms;
-    int errorCount =
-            collate_atoms(BadEventWithBinaryFieldAtom::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-    EXPECT_TRUE(errorCount > 0);
-}
-
-TEST(CollationTest, PassOnLogFromModuleAtom) {
-    Atoms atoms;
-    int errorCount = collate_atoms(ModuleAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-    EXPECT_EQ(errorCount, 0);
-    EXPECT_EQ(atoms.decls.size(), 4ul);
-}
-
-TEST(CollationTest, RecognizeModuleAtom) {
-    Atoms atoms;
-    int errorCount = collate_atoms(ModuleAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-    EXPECT_EQ(errorCount, 0);
-    EXPECT_EQ(atoms.decls.size(), 4ul);
-    EXPECT_EQ(atoms.signatureInfoMap.size(), 2u);
-    EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_INT);
-    EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_STRING);
-
-    SignatureInfoMap::const_iterator signatureInfoMapIt;
-    const vector<java_type_t>* signature;
-    const FieldNumberToAtomDeclSet* fieldNumberToAtomDeclSet;
-    FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt;
-    const AtomDeclSet* atomDeclSet;
-    AtomDeclSet::const_iterator atomDeclSetIt;
-    AtomDecl* atomDecl;
-    FieldNumberToAnnotations* fieldNumberToAnnotations;
-    FieldNumberToAnnotations::const_iterator fieldNumberToAnnotationsIt;
-    const AnnotationSet* annotationSet;
-    AnnotationSet::const_iterator annotationSetIt;
-    Annotation* annotation;
-
-    signatureInfoMapIt = atoms.signatureInfoMap.begin();
-    signature = &(signatureInfoMapIt->first);
-    fieldNumberToAtomDeclSet = &signatureInfoMapIt->second;
-    EXPECT_EQ(1ul, signature->size());
-    EXPECT_EQ(JAVA_TYPE_INT, signature->at(0));
-    EXPECT_EQ(1ul, fieldNumberToAtomDeclSet->size());
-    fieldNumberToAtomDeclSetIt = fieldNumberToAtomDeclSet->begin();
-    EXPECT_EQ(1, fieldNumberToAtomDeclSetIt->first);
-    atomDeclSet = &fieldNumberToAtomDeclSetIt->second;
-    EXPECT_EQ(2ul, atomDeclSet->size());
-    atomDeclSetIt = atomDeclSet->begin();
-    atomDecl = atomDeclSetIt->get();
-    EXPECT_EQ(1, atomDecl->code);
-    fieldNumberToAnnotations = &atomDecl->fieldNumberToAnnotations;
-    fieldNumberToAnnotationsIt = fieldNumberToAnnotations->find(1);
-    EXPECT_NE(fieldNumberToAnnotations->end(), fieldNumberToAnnotationsIt);
-    annotationSet = &fieldNumberToAnnotationsIt->second;
-    EXPECT_EQ(1ul, annotationSet->size());
-    annotationSetIt = annotationSet->begin();
-    annotation = annotationSetIt->get();
-    EXPECT_EQ(ANNOTATION_ID_IS_UID, annotation->annotationId);
-    EXPECT_EQ(1, annotation->atomId);
-    EXPECT_EQ(ANNOTATION_TYPE_BOOL, annotation->type);
-    EXPECT_TRUE(annotation->value.boolValue);
-
-    atomDeclSetIt++;
-    atomDecl = atomDeclSetIt->get();
-    EXPECT_EQ(3, atomDecl->code);
-    fieldNumberToAnnotations = &atomDecl->fieldNumberToAnnotations;
-    fieldNumberToAnnotationsIt = fieldNumberToAnnotations->find(1);
-    EXPECT_NE(fieldNumberToAnnotations->end(), fieldNumberToAnnotationsIt);
-    annotationSet = &fieldNumberToAnnotationsIt->second;
-    EXPECT_EQ(1ul, annotationSet->size());
-    annotationSetIt = annotationSet->begin();
-    annotation = annotationSetIt->get();
-    EXPECT_EQ(ANNOTATION_ID_EXCLUSIVE_STATE, annotation->annotationId);
-    EXPECT_EQ(3, annotation->atomId);
-    EXPECT_EQ(ANNOTATION_TYPE_BOOL, annotation->type);
-    EXPECT_TRUE(annotation->value.boolValue);
-
-    signatureInfoMapIt++;
-    signature = &signatureInfoMapIt->first;
-    fieldNumberToAtomDeclSet = &signatureInfoMapIt->second;
-    EXPECT_EQ(1ul, signature->size());
-    EXPECT_EQ(JAVA_TYPE_STRING, signature->at(0));
-    EXPECT_EQ(0ul, fieldNumberToAtomDeclSet->size());
-}
-
-TEST(CollationTest, RecognizeModule1Atom) {
-    Atoms atoms;
-    const string moduleName = "module1";
-    int errorCount = collate_atoms(ModuleAtoms::descriptor(), moduleName, &atoms);
-    EXPECT_EQ(errorCount, 0);
-    EXPECT_EQ(atoms.decls.size(), 2ul);
-    EXPECT_EQ(atoms.signatureInfoMap.size(), 1u);
-    EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_INT);
-
-    SignatureInfoMap::const_iterator signatureInfoMapIt;
-    const vector<java_type_t>* signature;
-    const FieldNumberToAtomDeclSet* fieldNumberToAtomDeclSet;
-    FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt;
-    const AtomDeclSet* atomDeclSet;
-    AtomDeclSet::const_iterator atomDeclSetIt;
-    AtomDecl* atomDecl;
-    FieldNumberToAnnotations* fieldNumberToAnnotations;
-    FieldNumberToAnnotations::const_iterator fieldNumberToAnnotationsIt;
-    const AnnotationSet* annotationSet;
-    AnnotationSet::const_iterator annotationSetIt;
-    Annotation* annotation;
-
-    signatureInfoMapIt = atoms.signatureInfoMap.begin();
-    signature = &(signatureInfoMapIt->first);
-    fieldNumberToAtomDeclSet = &signatureInfoMapIt->second;
-    EXPECT_EQ(1ul, signature->size());
-    EXPECT_EQ(JAVA_TYPE_INT, signature->at(0));
-    EXPECT_EQ(1ul, fieldNumberToAtomDeclSet->size());
-    fieldNumberToAtomDeclSetIt = fieldNumberToAtomDeclSet->begin();
-    EXPECT_EQ(1, fieldNumberToAtomDeclSetIt->first);
-    atomDeclSet = &fieldNumberToAtomDeclSetIt->second;
-    EXPECT_EQ(2ul, atomDeclSet->size());
-    atomDeclSetIt = atomDeclSet->begin();
-    atomDecl = atomDeclSetIt->get();
-    EXPECT_EQ(1, atomDecl->code);
-    fieldNumberToAnnotations = &atomDecl->fieldNumberToAnnotations;
-    fieldNumberToAnnotationsIt = fieldNumberToAnnotations->find(1);
-    EXPECT_NE(fieldNumberToAnnotations->end(), fieldNumberToAnnotationsIt);
-    annotationSet = &fieldNumberToAnnotationsIt->second;
-    EXPECT_EQ(1ul, annotationSet->size());
-    annotationSetIt = annotationSet->begin();
-    annotation = annotationSetIt->get();
-    EXPECT_EQ(ANNOTATION_ID_IS_UID, annotation->annotationId);
-    EXPECT_EQ(1, annotation->atomId);
-    EXPECT_EQ(ANNOTATION_TYPE_BOOL, annotation->type);
-    EXPECT_TRUE(annotation->value.boolValue);
-
-    atomDeclSetIt++;
-    atomDecl = atomDeclSetIt->get();
-    EXPECT_EQ(3, atomDecl->code);
-    fieldNumberToAnnotations = &atomDecl->fieldNumberToAnnotations;
-    fieldNumberToAnnotationsIt = fieldNumberToAnnotations->find(1);
-    EXPECT_NE(fieldNumberToAnnotations->end(), fieldNumberToAnnotationsIt);
-    annotationSet = &fieldNumberToAnnotationsIt->second;
-    EXPECT_EQ(1ul, annotationSet->size());
-    annotationSetIt = annotationSet->begin();
-    annotation = annotationSetIt->get();
-    EXPECT_EQ(ANNOTATION_ID_EXCLUSIVE_STATE, annotation->annotationId);
-    EXPECT_EQ(3, annotation->atomId);
-    EXPECT_EQ(ANNOTATION_TYPE_BOOL, annotation->type);
-    EXPECT_TRUE(annotation->value.boolValue);
-}
-
-}  // namespace stats_log_api_gen
-}  // namespace android
diff --git a/tools/stats_log_api_gen/utils.cpp b/tools/stats_log_api_gen/utils.cpp
deleted file mode 100644
index abb8913..0000000
--- a/tools/stats_log_api_gen/utils.cpp
+++ /dev/null
@@ -1,434 +0,0 @@
-/*
- * Copyright (C) 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "utils.h"
-
-#include "android-base/strings.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-static void build_non_chained_decl_map(const Atoms& atoms,
-                                       std::map<int, AtomDeclSet::const_iterator>* decl_map) {
-    for (AtomDeclSet::const_iterator atomIt = atoms.non_chained_decls.begin();
-         atomIt != atoms.non_chained_decls.end(); atomIt++) {
-        decl_map->insert(std::make_pair((*atomIt)->code, atomIt));
-    }
-}
-
-/**
- * Turn lower and camel case into upper case with underscores.
- */
-string make_constant_name(const string& str) {
-    string result;
-    const int N = str.size();
-    bool underscore_next = false;
-    for (int i = 0; i < N; i++) {
-        char c = str[i];
-        if (c >= 'A' && c <= 'Z') {
-            if (underscore_next) {
-                result += '_';
-                underscore_next = false;
-            }
-        } else if (c >= 'a' && c <= 'z') {
-            c = 'A' + c - 'a';
-            underscore_next = true;
-        } else if (c == '_') {
-            underscore_next = false;
-        }
-        result += c;
-    }
-    return result;
-}
-
-const char* cpp_type_name(java_type_t type) {
-    switch (type) {
-        case JAVA_TYPE_BOOLEAN:
-            return "bool";
-        case JAVA_TYPE_INT:
-        case JAVA_TYPE_ENUM:
-            return "int32_t";
-        case JAVA_TYPE_LONG:
-            return "int64_t";
-        case JAVA_TYPE_FLOAT:
-            return "float";
-        case JAVA_TYPE_DOUBLE:
-            return "double";
-        case JAVA_TYPE_STRING:
-            return "char const*";
-        case JAVA_TYPE_BYTE_ARRAY:
-            return "const BytesField&";
-        default:
-            return "UNKNOWN";
-    }
-}
-
-const char* java_type_name(java_type_t type) {
-    switch (type) {
-        case JAVA_TYPE_BOOLEAN:
-            return "boolean";
-        case JAVA_TYPE_INT:
-        case JAVA_TYPE_ENUM:
-            return "int";
-        case JAVA_TYPE_LONG:
-            return "long";
-        case JAVA_TYPE_FLOAT:
-            return "float";
-        case JAVA_TYPE_DOUBLE:
-            return "double";
-        case JAVA_TYPE_STRING:
-            return "java.lang.String";
-        case JAVA_TYPE_BYTE_ARRAY:
-            return "byte[]";
-        default:
-            return "UNKNOWN";
-    }
-}
-
-// Native
-// Writes namespaces for the cpp and header files, returning the number of
-// namespaces written.
-void write_namespace(FILE* out, const string& cppNamespaces) {
-    vector<string> cppNamespaceVec = android::base::Split(cppNamespaces, ",");
-    for (string cppNamespace : cppNamespaceVec) {
-        fprintf(out, "namespace %s {\n", cppNamespace.c_str());
-    }
-}
-
-// Writes namespace closing brackets for cpp and header files.
-void write_closing_namespace(FILE* out, const string& cppNamespaces) {
-    vector<string> cppNamespaceVec = android::base::Split(cppNamespaces, ",");
-    for (auto it = cppNamespaceVec.rbegin(); it != cppNamespaceVec.rend(); ++it) {
-        fprintf(out, "} // namespace %s\n", it->c_str());
-    }
-}
-
-static void write_cpp_usage(FILE* out, const string& method_name, const string& atom_code_name,
-                            const shared_ptr<AtomDecl> atom, const AtomDecl& attributionDecl) {
-    fprintf(out, "     * Usage: %s(StatsLog.%s", method_name.c_str(), atom_code_name.c_str());
-
-    for (vector<AtomField>::const_iterator field = atom->fields.begin();
-         field != atom->fields.end(); field++) {
-        if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-            for (auto chainField : attributionDecl.fields) {
-                if (chainField.javaType == JAVA_TYPE_STRING) {
-                    fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType),
-                            chainField.name.c_str());
-                } else {
-                    fprintf(out, ", const %s* %s, size_t %s_length",
-                            cpp_type_name(chainField.javaType), chainField.name.c_str(),
-                            chainField.name.c_str());
-                }
-            }
-        } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
-            fprintf(out,
-                    ", const std::map<int, int32_t>& %s_int"
-                    ", const std::map<int, int64_t>& %s_long"
-                    ", const std::map<int, char const*>& %s_str"
-                    ", const std::map<int, float>& %s_float",
-                    field->name.c_str(), field->name.c_str(), field->name.c_str(),
-                    field->name.c_str());
-        } else {
-            fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
-        }
-    }
-    fprintf(out, ");\n");
-}
-
-void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl) {
-    fprintf(out, "/**\n");
-    fprintf(out, " * Constants for atom codes.\n");
-    fprintf(out, " */\n");
-    fprintf(out, "enum {\n");
-
-    std::map<int, AtomDeclSet::const_iterator> atom_code_to_non_chained_decl_map;
-    build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
-
-    size_t i = 0;
-    // Print atom constants
-    for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
-         atomIt++) {
-        string constant = make_constant_name((*atomIt)->name);
-        fprintf(out, "\n");
-        fprintf(out, "    /**\n");
-        fprintf(out, "     * %s %s\n", (*atomIt)->message.c_str(), (*atomIt)->name.c_str());
-        write_cpp_usage(out, "stats_write", constant, *atomIt, attributionDecl);
-
-        auto non_chained_decl = atom_code_to_non_chained_decl_map.find((*atomIt)->code);
-        if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
-            write_cpp_usage(out, "stats_write_non_chained", constant, *non_chained_decl->second,
-                            attributionDecl);
-        }
-        fprintf(out, "     */\n");
-        char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
-        fprintf(out, "    %s = %d%s\n", constant.c_str(), (*atomIt)->code, comma);
-        i++;
-    }
-    fprintf(out, "\n");
-    fprintf(out, "};\n");
-    fprintf(out, "\n");
-}
-
-void write_native_method_signature(FILE* out, const string& methodName,
-                                   const vector<java_type_t>& signature,
-                                   const AtomDecl& attributionDecl, const string& closer) {
-    fprintf(out, "%s(int32_t code", methodName.c_str());
-    int argIndex = 1;
-    for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
-         arg++) {
-        if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-            for (auto chainField : attributionDecl.fields) {
-                if (chainField.javaType == JAVA_TYPE_STRING) {
-                    fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType),
-                            chainField.name.c_str());
-                } else {
-                    fprintf(out, ", const %s* %s, size_t %s_length",
-                            cpp_type_name(chainField.javaType), chainField.name.c_str(),
-                            chainField.name.c_str());
-                }
-            }
-        } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-            fprintf(out,
-                    ", const std::map<int, int32_t>& arg%d_1, "
-                    "const std::map<int, int64_t>& arg%d_2, "
-                    "const std::map<int, char const*>& arg%d_3, "
-                    "const std::map<int, float>& arg%d_4",
-                    argIndex, argIndex, argIndex, argIndex);
-        } else {
-            fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
-        }
-        argIndex++;
-    }
-    fprintf(out, ")%s\n", closer.c_str());
-}
-
-void write_native_method_call(FILE* out, const string& methodName,
-                              const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
-                              int argIndex) {
-    fprintf(out, "%s(code", methodName.c_str());
-    for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
-         arg++) {
-        if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-            for (auto chainField : attributionDecl.fields) {
-                if (chainField.javaType == JAVA_TYPE_STRING) {
-                    fprintf(out, ", %s", chainField.name.c_str());
-                } else {
-                    fprintf(out, ",  %s,  %s_length", chainField.name.c_str(),
-                            chainField.name.c_str());
-                }
-            }
-        } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-            fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex, argIndex, argIndex,
-                    argIndex);
-        } else {
-            fprintf(out, ", arg%d", argIndex);
-        }
-        argIndex++;
-    }
-    fprintf(out, ");\n");
-}
-
-// Java
-void write_java_atom_codes(FILE* out, const Atoms& atoms) {
-    fprintf(out, "    // Constants for atom codes.\n");
-
-    std::map<int, AtomDeclSet::const_iterator> atom_code_to_non_chained_decl_map;
-    build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
-
-    // Print constants for the atom codes.
-    for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
-         atomIt++) {
-        string constant = make_constant_name((*atomIt)->name);
-        fprintf(out, "\n");
-        fprintf(out, "    /**\n");
-        fprintf(out, "     * %s %s<br>\n", (*atomIt)->message.c_str(), (*atomIt)->name.c_str());
-        write_java_usage(out, "write", constant, **atomIt);
-        auto non_chained_decl = atom_code_to_non_chained_decl_map.find((*atomIt)->code);
-        if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
-            write_java_usage(out, "write_non_chained", constant, **(non_chained_decl->second));
-        }
-        fprintf(out, "     */\n");
-        fprintf(out, "    public static final int %s = %d;\n", constant.c_str(), (*atomIt)->code);
-    }
-    fprintf(out, "\n");
-}
-
-void write_java_enum_values(FILE* out, const Atoms& atoms) {
-    fprintf(out, "    // Constants for enum values.\n\n");
-    for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
-         atomIt++) {
-        for (vector<AtomField>::const_iterator field = (*atomIt)->fields.begin();
-             field != (*atomIt)->fields.end(); field++) {
-            if (field->javaType == JAVA_TYPE_ENUM) {
-                fprintf(out, "    // Values for %s.%s\n", (*atomIt)->message.c_str(),
-                        field->name.c_str());
-                for (map<int, string>::const_iterator value = field->enumValues.begin();
-                     value != field->enumValues.end(); value++) {
-                    fprintf(out, "    public static final int %s__%s__%s = %d;\n",
-                            make_constant_name((*atomIt)->message).c_str(),
-                            make_constant_name(field->name).c_str(),
-                            make_constant_name(value->second).c_str(), value->first);
-                }
-                fprintf(out, "\n");
-            }
-        }
-    }
-}
-
-void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name,
-                      const AtomDecl& atom) {
-    fprintf(out, "     * Usage: StatsLog.%s(StatsLog.%s", method_name.c_str(),
-            atom_code_name.c_str());
-    for (vector<AtomField>::const_iterator field = atom.fields.begin(); field != atom.fields.end();
-         field++) {
-        if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-            fprintf(out, ", android.os.WorkSource workSource");
-        } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
-            fprintf(out, ", android.util.SparseArray<Object> value_map");
-        } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
-            fprintf(out, ", byte[] %s", field->name.c_str());
-        } else {
-            fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
-        }
-    }
-    fprintf(out, ");<br>\n");
-}
-
-int write_java_non_chained_methods(FILE* out, const SignatureInfoMap& signatureInfoMap) {
-    for (auto signatureInfoMapIt = signatureInfoMap.begin();
-         signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
-        // Print method signature.
-        fprintf(out, "    public static void write_non_chained(int code");
-        vector<java_type_t> signature = signatureInfoMapIt->first;
-        int argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
-             arg++) {
-            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-                fprintf(stderr, "Non chained signatures should not have attribution chains.\n");
-                return 1;
-            } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-                fprintf(stderr, "Module logging does not yet support key value pair.\n");
-                return 1;
-            } else {
-                fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
-            }
-            argIndex++;
-        }
-        fprintf(out, ") {\n");
-
-        fprintf(out, "        write(code");
-        argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
-             arg++) {
-            // First two args are uid and tag of attribution chain.
-            if (argIndex == 1) {
-                fprintf(out, ", new int[] {arg%d}", argIndex);
-            } else if (argIndex == 2) {
-                fprintf(out, ", new java.lang.String[] {arg%d}", argIndex);
-            } else {
-                fprintf(out, ", arg%d", argIndex);
-            }
-            argIndex++;
-        }
-        fprintf(out, ");\n");
-        fprintf(out, "    }\n");
-        fprintf(out, "\n");
-    }
-    return 0;
-}
-
-int write_java_work_source_methods(FILE* out, const SignatureInfoMap& signatureInfoMap) {
-    fprintf(out, "    // WorkSource methods.\n");
-    for (auto signatureInfoMapIt = signatureInfoMap.begin();
-         signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
-        vector<java_type_t> signature = signatureInfoMapIt->first;
-        // Determine if there is Attribution in this signature.
-        int attributionArg = -1;
-        int argIndexMax = 0;
-        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
-             arg++) {
-            argIndexMax++;
-            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-                if (attributionArg > -1) {
-                    fprintf(stderr, "An atom contains multiple AttributionNode fields.\n");
-                    fprintf(stderr, "This is not supported. Aborting WorkSource method writing.\n");
-                    fprintf(out,
-                            "\n// Invalid for WorkSource: more than one attribution "
-                            "chain.\n");
-                    return 1;
-                }
-                attributionArg = argIndexMax;
-            }
-        }
-        if (attributionArg < 0) {
-            continue;
-        }
-
-        fprintf(out, "\n");
-        // Method header (signature)
-        fprintf(out, "    public static void write(int code");
-        int argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
-             arg++) {
-            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-                fprintf(out, ", android.os.WorkSource ws");
-            } else {
-                fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
-            }
-            argIndex++;
-        }
-        fprintf(out, ") {\n");
-
-        // write_non_chained() component. TODO: Remove when flat uids are no longer
-        // needed.
-        fprintf(out, "        for (int i = 0; i < ws.size(); ++i) {\n");
-        fprintf(out, "            write_non_chained(code");
-        for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
-            if (argIndex == attributionArg) {
-                fprintf(out, ", ws.getUid(i), ws.getPackageName(i)");
-            } else {
-                fprintf(out, ", arg%d", argIndex);
-            }
-        }
-        fprintf(out, ");\n");
-        fprintf(out, "        }\n");  // close for-loop
-
-        // write() component.
-        fprintf(out,
-                "        java.util.List<android.os.WorkSource.WorkChain> workChains = "
-                "ws.getWorkChains();\n");
-        fprintf(out, "        if (workChains != null) {\n");
-        fprintf(out,
-                "            for (android.os.WorkSource.WorkChain wc : workChains) "
-                "{\n");
-        fprintf(out, "                write(code");
-        for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
-            if (argIndex == attributionArg) {
-                fprintf(out, ", wc.getUids(), wc.getTags()");
-            } else {
-                fprintf(out, ", arg%d", argIndex);
-            }
-        }
-        fprintf(out, ");\n");
-        fprintf(out, "            }\n");  // close for-loop
-        fprintf(out, "        }\n");      // close if
-        fprintf(out, "    }\n");          // close method
-    }
-    return 0;
-}
-
-}  // namespace stats_log_api_gen
-}  // namespace android
diff --git a/tools/stats_log_api_gen/utils.h b/tools/stats_log_api_gen/utils.h
deleted file mode 100644
index 73e0cb8..0000000
--- a/tools/stats_log_api_gen/utils.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2019, 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.
- */
-
-#pragma once
-
-#include <stdio.h>
-#include <string.h>
-
-#include <map>
-#include <set>
-#include <vector>
-
-#include "Collation.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-using namespace std;
-
-const string DEFAULT_CPP_NAMESPACE = "android,util";
-const string DEFAULT_CPP_HEADER_IMPORT = "statslog.h";
-
-const int JAVA_MODULE_REQUIRES_FLOAT = 0x01;
-const int JAVA_MODULE_REQUIRES_ATTRIBUTION = 0x02;
-const int JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS = 0x04;
-
-const map<AnnotationId, string> ANNOTATION_ID_CONSTANTS = {
-        {ANNOTATION_ID_IS_UID, "ANNOTATION_ID_IS_UID"},
-        {ANNOTATION_ID_TRUNCATE_TIMESTAMP, "ANNOTATION_ID_TRUNCATE_TIMESTAMP"},
-        {ANNOTATION_ID_PRIMARY_FIELD, "ANNOTATION_ID_PRIMARY_FIELD"},
-        {ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, "ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID"},
-        {ANNOTATION_ID_EXCLUSIVE_STATE, "ANNOTATION_ID_EXCLUSIVE_STATE"},
-        {ANNOTATION_ID_TRIGGER_STATE_RESET, "ANNOTATION_ID_TRIGGER_STATE_RESET"},
-        {ANNOTATION_ID_STATE_NESTED, "ANNOTATION_ID_STATE_NESTED"}};
-
-string make_constant_name(const string& str);
-
-const char* cpp_type_name(java_type_t type);
-
-const char* java_type_name(java_type_t type);
-
-// Common Native helpers
-void write_namespace(FILE* out, const string& cppNamespaces);
-
-void write_closing_namespace(FILE* out, const string& cppNamespaces);
-
-void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl);
-
-void write_native_method_signature(FILE* out, const string& methodName,
-                                   const vector<java_type_t>& signature,
-                                   const AtomDecl& attributionDecl, const string& closer);
-
-void write_native_method_call(FILE* out, const string& methodName,
-                              const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
-                              int argIndex = 1);
-
-// Common Java helpers.
-void write_java_atom_codes(FILE* out, const Atoms& atoms);
-
-void write_java_enum_values(FILE* out, const Atoms& atoms);
-
-void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name,
-                      const AtomDecl& atom);
-
-int write_java_non_chained_methods(FILE* out, const SignatureInfoMap& signatureInfoMap);
-
-int write_java_work_source_methods(FILE* out, const SignatureInfoMap& signatureInfoMap);
-
-}  // namespace stats_log_api_gen
-}  // namespace android
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
index 75b5e72..4099c27 100644
--- a/wifi/jarjar-rules.txt
+++ b/wifi/jarjar-rules.txt
@@ -21,6 +21,7 @@
 rule android.net.ResolverParamsParcel* com.android.wifi.x.@0
 rule android.net.RouteInfoParcel* com.android.wifi.x.@0
 rule android.net.ScanResultInfoParcelable* com.android.wifi.x.@0
+rule android.net.TcpKeepalivePacketDataParcelable* com.android.wifi.x.@0
 rule android.net.TetherConfigParcel* com.android.wifi.x.@0
 rule android.net.TetherOffloadRuleParcel* com.android.wifi.x.@0
 rule android.net.TetherStatsParcel* com.android.wifi.x.@0
@@ -43,7 +44,6 @@
 rule android.net.InterfaceConfiguration* com.android.wifi.x.@0
 rule android.net.IpMemoryStore* com.android.wifi.x.@0
 rule android.net.NetworkMonitorManager* com.android.wifi.x.@0
-rule android.net.TcpKeepalivePacketData* com.android.wifi.x.@0
 rule android.net.NetworkFactory* com.android.wifi.x.@0
 rule android.net.ip.IpClientCallbacks* com.android.wifi.x.@0
 rule android.net.ip.IpClientManager* com.android.wifi.x.@0