Merge "Revert "Check for data races when reading JIT/DEX entries.""
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 3c07882..e417f05 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1114,7 +1114,7 @@
                 return true;
             }
             return false;
-        });
+        }, true);
         if (!response.empty()) {
             response.resize(response.size() - 1);
         }
@@ -1229,7 +1229,7 @@
         std::string response;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &response, true);
         if (t != nullptr) {
-            kick_transport(t);
+            kick_transport(t, true);
             response =
                     "reconnecting " + t->serial_name() + " [" + t->connection_state_name() + "]\n";
         }
diff --git a/adb/client/usb_dispatch.cpp b/adb/client/usb_dispatch.cpp
index ce57731..f55ae90 100644
--- a/adb/client/usb_dispatch.cpp
+++ b/adb/client/usb_dispatch.cpp
@@ -52,6 +52,11 @@
                                : native::usb_close(reinterpret_cast<native::usb_handle*>(h));
 }
 
+void usb_reset(usb_handle* h) {
+    should_use_libusb() ? libusb::usb_reset(reinterpret_cast<libusb::usb_handle*>(h))
+                        : native::usb_reset(reinterpret_cast<native::usb_handle*>(h));
+}
+
 void usb_kick(usb_handle* h) {
     should_use_libusb() ? libusb::usb_kick(reinterpret_cast<libusb::usb_handle*>(h))
                         : native::usb_kick(reinterpret_cast<native::usb_handle*>(h));
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index f2ca63b..53f01a0 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -622,6 +622,11 @@
     return 0;
 }
 
+void usb_reset(usb_handle* h) {
+    libusb_reset_device(h->device_handle);
+    usb_kick(h);
+}
+
 void usb_kick(usb_handle* h) {
     h->Close();
 }
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 1168958..81b8306 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -458,6 +458,11 @@
     return orig_len - len;
 }
 
+void usb_reset(usb_handle* h) {
+    ioctl(h->fd, USBDEVFS_RESET);
+    usb_kick(h);
+}
+
 void usb_kick(usb_handle* h) {
     std::lock_guard<std::mutex> lock(h->mutex);
     D("[ kicking %p (fd = %d) ]", h, h->fd);
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index e380c84..5c0da47 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -556,6 +556,11 @@
     return 0;
 }
 
+void usb_reset(usb_handle* handle) {
+    // Unimplemented on OS X.
+    usb_kick(handle);
+}
+
 static void usb_kick_locked(usb_handle *handle)
 {
     LOG(INFO) << "Kicking handle";
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index cfa5cf4..f23c3a5 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -448,6 +448,11 @@
     }
 }
 
+void usb_reset(usb_handle* handle) {
+    // Unimplemented on Windows.
+    usb_kick(handle);
+}
+
 static void usb_kick_locked(usb_handle* handle) {
     // The reason the lock must be acquired before calling this function is in
     // case multiple threads are trying to kick the same device at the same time.
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 8a50003..8c33ca5 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -168,7 +168,8 @@
 struct UsbFfsConnection : public Connection {
     UsbFfsConnection(unique_fd control, unique_fd read, unique_fd write,
                      std::promise<void> destruction_notifier)
-        : stopped_(false),
+        : worker_started_(false),
+          stopped_(false),
           destruction_notifier_(std::move(destruction_notifier)),
           control_fd_(std::move(control)),
           read_fd_(std::move(read)),
@@ -194,6 +195,7 @@
 
         // We need to explicitly close our file descriptors before we notify our destruction,
         // because the thread listening on the future will immediately try to reopen the endpoint.
+        aio_context_.reset();
         control_fd_.reset();
         read_fd_.reset();
         write_fd_.reset();
@@ -274,11 +276,16 @@
                   { .fd = control_fd_.get(), .events = POLLIN, .revents = 0 },
                   { .fd = monitor_event_fd_.get(), .events = POLLIN, .revents = 0 },
                 };
-                int rc = TEMP_FAILURE_RETRY(adb_poll(pfd, 2, -1));
+
+                // If we don't see our first bind within a second, try again.
+                int timeout_ms = bound ? -1 : 1000;
+
+                int rc = TEMP_FAILURE_RETRY(adb_poll(pfd, 2, timeout_ms));
                 if (rc == -1) {
                     PLOG(FATAL) << "poll on USB control fd failed";
                 } else if (rc == 0) {
-                    LOG(FATAL) << "poll on USB control fd returned 0";
+                    LOG(WARNING) << "timed out while waiting for FUNCTIONFS_BIND, trying again";
+                    break;
                 }
 
                 if (pfd[1].revents) {
@@ -297,46 +304,70 @@
 
                 switch (event.type) {
                     case FUNCTIONFS_BIND:
-                        CHECK(!bound) << "received FUNCTIONFS_BIND while already bound?";
-                        CHECK(!enabled) << "received FUNCTIONFS_BIND while already enabled?";
-                        bound = true;
+                        if (bound) {
+                            LOG(WARNING) << "received FUNCTIONFS_BIND while already bound?";
+                            running = false;
+                        }
 
+                        if (enabled) {
+                            LOG(WARNING) << "received FUNCTIONFS_BIND while already enabled?";
+                            running = false;
+                        }
+
+                        bound = true;
                         break;
 
                     case FUNCTIONFS_ENABLE:
-                        CHECK(bound) << "received FUNCTIONFS_ENABLE while not bound?";
-                        CHECK(!enabled) << "received FUNCTIONFS_ENABLE while already enabled?";
-                        enabled = true;
+                        if (!bound) {
+                            LOG(WARNING) << "received FUNCTIONFS_ENABLE while not bound?";
+                            running = false;
+                        }
 
+                        if (enabled) {
+                            LOG(WARNING) << "received FUNCTIONFS_ENABLE while already enabled?";
+                            running = false;
+                        }
+
+                        enabled = true;
                         StartWorker();
                         break;
 
                     case FUNCTIONFS_DISABLE:
-                        CHECK(bound) << "received FUNCTIONFS_DISABLE while not bound?";
-                        CHECK(enabled) << "received FUNCTIONFS_DISABLE while not enabled?";
-                        enabled = false;
+                        if (!bound) {
+                            LOG(WARNING) << "received FUNCTIONFS_DISABLE while not bound?";
+                        }
 
+                        if (!enabled) {
+                            LOG(WARNING) << "received FUNCTIONFS_DISABLE while not enabled?";
+                        }
+
+                        enabled = false;
                         running = false;
                         break;
 
                     case FUNCTIONFS_UNBIND:
-                        CHECK(!enabled) << "received FUNCTIONFS_UNBIND while still enabled?";
-                        CHECK(bound) << "received FUNCTIONFS_UNBIND when not bound?";
-                        bound = false;
+                        if (enabled) {
+                            LOG(WARNING) << "received FUNCTIONFS_UNBIND while still enabled?";
+                        }
 
+                        if (!bound) {
+                            LOG(WARNING) << "received FUNCTIONFS_UNBIND when not bound?";
+                        }
+
+                        bound = false;
                         running = false;
                         break;
                 }
             }
 
             StopWorker();
-            aio_context_.reset();
-            read_fd_.reset();
-            write_fd_.reset();
+            HandleError("monitor thread finished");
         });
     }
 
     void StartWorker() {
+        CHECK(!worker_started_);
+        worker_started_ = true;
         worker_thread_ = std::thread([this]() {
             adb_thread_setname("UsbFfs-worker");
             for (size_t i = 0; i < kUsbReadQueueDepth; ++i) {
@@ -361,6 +392,10 @@
     }
 
     void StopWorker() {
+        if (!worker_started_) {
+            return;
+        }
+
         pthread_t worker_thread_handle = worker_thread_.native_handle();
         while (true) {
             int rc = pthread_kill(worker_thread_handle, kInterruptionSignal);
@@ -590,6 +625,8 @@
     }
 
     std::thread monitor_thread_;
+
+    bool worker_started_;
     std::thread worker_thread_;
 
     std::atomic<bool> stopped_;
diff --git a/adb/daemon/usb_dummy.cpp b/adb/daemon/usb_dummy.cpp
index 984bc25..c9bf797 100644
--- a/adb/daemon/usb_dummy.cpp
+++ b/adb/daemon/usb_dummy.cpp
@@ -33,6 +33,10 @@
     return -1;
 }
 
+void usb_reset(usb_handle*) {
+    LOG(FATAL) << "unimplemented";
+}
+
 void usb_kick(usb_handle*) {
     LOG(FATAL) << "unimplemented";
 }
diff --git a/adb/daemon/usb_legacy.cpp b/adb/daemon/usb_legacy.cpp
index 7ace59d..b65727a 100644
--- a/adb/daemon/usb_legacy.cpp
+++ b/adb/daemon/usb_legacy.cpp
@@ -307,6 +307,10 @@
     return 0;
 }
 
+void usb_reset(usb_handle* h) {
+    usb_close(h);
+}
+
 void usb_kick(usb_handle* h) {
     h->kick(h);
 }
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 0b4e084..15c3a9a 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -257,6 +257,11 @@
     return next++;
 }
 
+void Connection::Reset() {
+    LOG(INFO) << "Connection::Reset(): stopping";
+    Stop();
+}
+
 BlockingConnectionAdapter::BlockingConnectionAdapter(std::unique_ptr<BlockingConnection> connection)
     : underlying_(std::move(connection)) {}
 
@@ -312,6 +317,26 @@
     started_ = true;
 }
 
+void BlockingConnectionAdapter::Reset() {
+    {
+        std::lock_guard<std::mutex> lock(mutex_);
+        if (!started_) {
+            LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): not started";
+            return;
+        }
+
+        if (stopped_) {
+            LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_
+                      << "): already stopped";
+            return;
+        }
+    }
+
+    LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): resetting";
+    this->underlying_->Reset();
+    Stop();
+}
+
 void BlockingConnectionAdapter::Stop() {
     {
         std::lock_guard<std::mutex> lock(mutex_);
@@ -424,14 +449,18 @@
     }
 }
 
-void kick_transport(atransport* t) {
+void kick_transport(atransport* t, bool reset) {
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
     // As kick_transport() can be called from threads without guarantee that t is valid,
     // check if the transport is in transport_list first.
     //
     // TODO(jmgao): WTF? Is this actually true?
     if (std::find(transport_list.begin(), transport_list.end(), t) != transport_list.end()) {
-        t->Kick();
+        if (reset) {
+            t->Reset();
+        } else {
+            t->Kick();
+        }
     }
 
 #if ADB_HOST
@@ -942,9 +971,16 @@
     return this->connection()->Write(std::unique_ptr<apacket>(p)) ? 0 : -1;
 }
 
+void atransport::Reset() {
+    if (!kicked_.exchange(true)) {
+        LOG(INFO) << "resetting transport " << this << " " << this->serial;
+        this->connection()->Reset();
+    }
+}
+
 void atransport::Kick() {
     if (!kicked_.exchange(true)) {
-        D("kicking transport %p %s", this, this->serial.c_str());
+        LOG(INFO) << "kicking transport " << this << " " << this->serial;
         this->connection()->Stop();
     }
 }
@@ -1173,18 +1209,22 @@
     return result;
 }
 
-void close_usb_devices(std::function<bool(const atransport*)> predicate) {
+void close_usb_devices(std::function<bool(const atransport*)> predicate, bool reset) {
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (auto& t : transport_list) {
         if (predicate(t)) {
-            t->Kick();
+            if (reset) {
+                t->Reset();
+            } else {
+                t->Kick();
+            }
         }
     }
 }
 
 /* hack for osx */
-void close_usb_devices() {
-    close_usb_devices([](const atransport*) { return true; });
+void close_usb_devices(bool reset) {
+    close_usb_devices([](const atransport*) { return true; }, reset);
 }
 #endif  // ADB_HOST
 
diff --git a/adb/transport.h b/adb/transport.h
index a0174b8..f4490ed 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -99,6 +99,9 @@
     virtual void Start() = 0;
     virtual void Stop() = 0;
 
+    // Stop, and reset the device if it's a USB connection.
+    virtual void Reset();
+
     std::string transport_name_;
     ReadCallback read_callback_;
     ErrorCallback error_callback_;
@@ -124,6 +127,9 @@
     // This method must be thread-safe, and must cause concurrent Reads/Writes to terminate.
     // Formerly known as 'Kick' in atransport.
     virtual void Close() = 0;
+
+    // Terminate a connection, and reset it.
+    virtual void Reset() = 0;
 };
 
 struct BlockingConnectionAdapter : public Connection {
@@ -136,6 +142,8 @@
     virtual void Start() override final;
     virtual void Stop() override final;
 
+    virtual void Reset() override final;
+
     bool started_ GUARDED_BY(mutex_) = false;
     bool stopped_ GUARDED_BY(mutex_) = false;
 
@@ -157,6 +165,7 @@
     bool Write(apacket* packet) override final;
 
     void Close() override;
+    virtual void Reset() override final { Close(); }
 
   private:
     unique_fd fd_;
@@ -170,6 +179,7 @@
     bool Write(apacket* packet) override final;
 
     void Close() override final;
+    virtual void Reset() override final;
 
     usb_handle* handle_;
 };
@@ -235,6 +245,7 @@
     virtual ~atransport();
 
     int Write(apacket* p);
+    void Reset();
     void Kick();
     bool kicked() const { return kicked_; }
 
@@ -364,7 +375,7 @@
 atransport* acquire_one_transport(TransportType type, const char* serial, TransportId transport_id,
                                   bool* is_ambiguous, std::string* error_out,
                                   bool accept_any_state = false);
-void kick_transport(atransport* t);
+void kick_transport(atransport* t, bool reset = false);
 void update_transports(void);
 
 // Iterates across all of the current and pending transports.
@@ -395,8 +406,8 @@
 
 bool check_header(apacket* p, atransport* t);
 
-void close_usb_devices();
-void close_usb_devices(std::function<bool(const atransport*)> predicate);
+void close_usb_devices(bool reset = false);
+void close_usb_devices(std::function<bool(const atransport*)> predicate, bool reset = false);
 
 void send_packet(apacket* p, atransport* t);
 
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 2e5918a..3e87522 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -171,6 +171,11 @@
     return true;
 }
 
+void UsbConnection::Reset() {
+    usb_reset(handle_);
+    usb_kick(handle_);
+}
+
 void UsbConnection::Close() {
     usb_kick(handle_);
 }
diff --git a/adb/types.h b/adb/types.h
index 0090c98..cd1366d 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -216,7 +216,10 @@
     // Add a nonempty block to the chain.
     // The end of the chain must be a complete block (i.e. end_offset_ == 0).
     void append(std::unique_ptr<const block_type> block) {
-        CHECK_NE(0ULL, block->size());
+        if (block->size() == 0) {
+            return;
+        }
+
         CHECK_EQ(0ULL, end_offset_);
         chain_length_ += block->size();
         chain_.emplace_back(std::move(block));
diff --git a/adb/usb.h b/adb/usb.h
index cd83c42..eb8ca6c 100644
--- a/adb/usb.h
+++ b/adb/usb.h
@@ -26,6 +26,7 @@
     int usb_write(handle_ref_type h, const void* data, int len); \
     int usb_read(handle_ref_type h, void* data, int len);        \
     int usb_close(handle_ref_type h);                            \
+    void usb_reset(handle_ref_type h);                           \
     void usb_kick(handle_ref_type h);                            \
     size_t usb_get_max_packet_size(handle_ref_type)
 
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 83213e9..3fa3bea 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -105,6 +105,9 @@
   int get() const { return fd_; }
   operator int() const { return get(); }  // NOLINT
 
+  // Catch bogus error checks (i.e.: "!fd" instead of "fd != -1").
+  bool operator!() const = delete;
+
   int release() __attribute__((warn_unused_result)) {
     tag(fd_, this, nullptr);
     int ret = fd_;
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 827db96..f8f7eb3 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1407,7 +1407,7 @@
 
 int LocalImageSource::OpenFile(const std::string& name) const {
     auto path = find_item_given_name(name);
-    return open(path.c_str(), O_RDONLY);
+    return open(path.c_str(), O_RDONLY | O_BINARY);
 }
 
 static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) {
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index d26f2d5..045bb48 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1609,14 +1609,7 @@
 
     DeviceMapper& dm = DeviceMapper::Instance();
 
-    std::string mount_point;
-    if (entry.mount_point == "/") {
-        // In AVB, the dm device name is vroot instead of system.
-        mount_point = entry.fs_mgr_flags.avb ? "vroot" : "system";
-    } else {
-        mount_point = Basename(entry.mount_point);
-    }
-
+    std::string mount_point = GetVerityDeviceName(entry);
     if (dm.GetState(mount_point) == DmDeviceState::INVALID) {
         return false;
     }
@@ -1639,6 +1632,27 @@
     return false;
 }
 
+bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry) {
+    if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
+        return false;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    std::string device = GetVerityDeviceName(entry);
+
+    std::vector<DeviceMapper::TargetInfo> table;
+    if (dm.GetState(device) == DmDeviceState::INVALID || !dm.GetTableInfo(device, &table)) {
+        return false;
+    }
+    for (const auto& target : table) {
+        if (strcmp(target.spec.target_type, "verity") == 0 &&
+            target.data.find("check_at_most_once") != std::string::npos) {
+            return true;
+        }
+    }
+    return false;
+}
+
 std::string fs_mgr_get_super_partition_name(int slot) {
     // Devices upgrading to dynamic partitions are allowed to specify a super
     // partition name, assumed to be A/B (non-A/B retrofit is not supported).
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 2f1e41f..f6f6f50 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -768,6 +768,17 @@
     return system;
 }
 
+std::string GetVerityDeviceName(const FstabEntry& entry) {
+    std::string base_device;
+    if (entry.mount_point == "/") {
+        // In AVB, the dm device name is vroot instead of system.
+        base_device = entry.fs_mgr_flags.avb ? "vroot" : "system";
+    } else {
+        base_device = android::base::Basename(entry.mount_point);
+    }
+    return base_device + "-verity";
+}
+
 }  // namespace fs_mgr
 }  // namespace android
 
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 24044d8..093d44d 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -340,6 +340,7 @@
                 blk_device = rentry.blk_device;
                 break;
             }
+            // Find overlayfs mount point?
             if ((mount_point == "/") && (rentry.mount_point == "/system")) {
                 blk_device = rentry.blk_device;
                 mount_point = "/system";
@@ -352,6 +353,12 @@
         }
         fs_mgr_set_blk_ro(blk_device, false);
 
+        // Find system-as-root mount point?
+        if ((mount_point == "/system") && !GetEntryForMountPoint(&mounts, mount_point) &&
+            GetEntryForMountPoint(&mounts, "/")) {
+            mount_point = "/";
+        }
+
         // Now remount!
         if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
                     nullptr) == 0) {
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index a3bb852..8abe609 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -77,6 +77,10 @@
 bool fs_mgr_swapon_all(const android::fs_mgr::Fstab& fstab);
 bool fs_mgr_update_logical_partition(android::fs_mgr::FstabEntry* entry);
 
+// Returns true if the given fstab entry has verity enabled, *and* the verity
+// device is in "check_at_most_once" mode.
+bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry);
+
 int fs_mgr_do_format(const android::fs_mgr::FstabEntry& entry, bool reserve_footer);
 
 #define FS_MGR_SETUP_VERITY_SKIPPED  (-3)
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index e811447..88da41d 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -107,5 +107,10 @@
 
 std::set<std::string> GetBootDevices();
 
+// Return the name of the dm-verity device for the given fstab entry. This does
+// not check whether the device is valid or exists; it merely returns the
+// expected name.
+std::string GetVerityDeviceName(const FstabEntry& entry);
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index 85589cc..e3803d5 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -407,9 +407,25 @@
 #define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
 #endif
 
+    // f2fs: export FS_NOCOW_FL flag to user
+    uint32_t flags;
+    int error = ioctl(file_fd, FS_IOC_GETFLAGS, &flags);
+    if (error < 0) {
+        if ((errno == ENOTTY) || (errno == ENOTSUP)) {
+            PLOG(ERROR) << "Failed to get flags, not supported by kernel: " << file_path;
+        } else {
+            PLOG(ERROR) << "Failed to get flags: " << file_path;
+        }
+        return false;
+    }
+    if (!(flags & FS_NOCOW_FL)) {
+        LOG(ERROR) << "It is not pinned: " << file_path;
+        return false;
+    }
+
     // F2FS_IOC_GET_PIN_FILE returns the number of blocks moved.
     uint32_t moved_blocks_nr;
-    int error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
+    error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
     if (error < 0) {
         if ((errno == ENOTTY) || (errno == ENOTSUP)) {
             PLOG(ERROR) << "Failed to get file pin status, not supported by kernel: " << file_path;
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index f4e4d4e..d9650f3 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -105,14 +105,15 @@
     table.set_readonly(true);
 
     const std::string mount_point(Basename(fstab_entry->mount_point));
+    const std::string device_name(GetVerityDeviceName(*fstab_entry));
     android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
-    if (!dm.CreateDevice(mount_point, table)) {
+    if (!dm.CreateDevice(device_name, table)) {
         LERROR << "Couldn't create verity device!";
         return false;
     }
 
     std::string dev_path;
-    if (!dm.GetDmDevicePathByName(mount_point, &dev_path)) {
+    if (!dm.GetDmDevicePathByName(device_name, &dev_path)) {
         LERROR << "Couldn't get verity device path!";
         return false;
     }
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 56b5353..db27022 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -98,11 +98,12 @@
     return WriteToImageFile(fd, input);
 }
 
-SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
-                             const std::map<std::string, std::string>& images)
+ImageBuilder::ImageBuilder(const LpMetadata& metadata, uint32_t block_size,
+                           const std::map<std::string, std::string>& images, bool sparsify)
     : metadata_(metadata),
       geometry_(metadata.geometry),
       block_size_(block_size),
+      sparsify_(sparsify),
       images_(images) {
     uint64_t total_size = GetTotalSuperPartitionSize(metadata);
     if (block_size % LP_SECTOR_SIZE != 0) {
@@ -144,11 +145,11 @@
     }
 }
 
-bool SparseBuilder::IsValid() const {
+bool ImageBuilder::IsValid() const {
     return device_images_.size() == metadata_.block_devices.size();
 }
 
-bool SparseBuilder::Export(const char* file) {
+bool ImageBuilder::Export(const char* file) {
     unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
     if (fd < 0) {
         PERROR << "open failed: " << file;
@@ -158,8 +159,8 @@
         LERROR << "Cannot export to a single image on retrofit builds.";
         return false;
     }
-    // No gzip compression; sparseify; no checksum.
-    int ret = sparse_file_write(device_images_[0].get(), fd, false, true, false);
+    // No gzip compression; no checksum.
+    int ret = sparse_file_write(device_images_[0].get(), fd, false, sparsify_, false);
     if (ret != 0) {
         LERROR << "sparse_file_write failed (error code " << ret << ")";
         return false;
@@ -167,7 +168,7 @@
     return true;
 }
 
-bool SparseBuilder::ExportFiles(const std::string& output_dir) {
+bool ImageBuilder::ExportFiles(const std::string& output_dir) {
     for (size_t i = 0; i < device_images_.size(); i++) {
         std::string name = GetBlockDevicePartitionName(metadata_.block_devices[i]);
         std::string file_name = "super_" + name + ".img";
@@ -179,8 +180,8 @@
             PERROR << "open failed: " << file_path;
             return false;
         }
-        // No gzip compression; sparseify; no checksum.
-        int ret = sparse_file_write(device_images_[i].get(), fd, false, true, false);
+        // No gzip compression; no checksum.
+        int ret = sparse_file_write(device_images_[i].get(), fd, false, sparsify_, false);
         if (ret != 0) {
             LERROR << "sparse_file_write failed (error code " << ret << ")";
             return false;
@@ -189,7 +190,7 @@
     return true;
 }
 
-bool SparseBuilder::AddData(sparse_file* file, const std::string& blob, uint64_t sector) {
+bool ImageBuilder::AddData(sparse_file* file, const std::string& blob, uint64_t sector) {
     uint32_t block;
     if (!SectorToBlock(sector, &block)) {
         return false;
@@ -203,7 +204,7 @@
     return true;
 }
 
-bool SparseBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {
+bool ImageBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {
     // The caller must ensure that the metadata has an alignment that is a
     // multiple of the block size. liblp will take care of the rest, ensuring
     // that all partitions are on an aligned boundary. Therefore all writes
@@ -218,11 +219,11 @@
     return true;
 }
 
-uint64_t SparseBuilder::BlockToSector(uint64_t block) const {
+uint64_t ImageBuilder::BlockToSector(uint64_t block) const {
     return (block * block_size_) / LP_SECTOR_SIZE;
 }
 
-bool SparseBuilder::Build() {
+bool ImageBuilder::Build() {
     if (sparse_file_add_fill(device_images_[0].get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
         LERROR << "Could not add initial sparse block for reserved zeroes";
         return false;
@@ -275,8 +276,8 @@
     return true;
 }
 
-bool SparseBuilder::AddPartitionImage(const LpMetadataPartition& partition,
-                                      const std::string& file) {
+bool ImageBuilder::AddPartitionImage(const LpMetadataPartition& partition,
+                                     const std::string& file) {
     // Track which extent we're processing.
     uint32_t extent_index = partition.first_extent_index;
 
@@ -371,7 +372,7 @@
     return true;
 }
 
-uint64_t SparseBuilder::ComputePartitionSize(const LpMetadataPartition& partition) const {
+uint64_t ImageBuilder::ComputePartitionSize(const LpMetadataPartition& partition) const {
     uint64_t sectors = 0;
     for (size_t i = 0; i < partition.num_extents; i++) {
         sectors += metadata_.extents[partition.first_extent_index + i].num_sectors;
@@ -386,7 +387,7 @@
 //
 // Without this, it would be more difficult to find the appropriate extent for
 // an output block. With this guarantee it is a linear walk.
-bool SparseBuilder::CheckExtentOrdering() {
+bool ImageBuilder::CheckExtentOrdering() {
     std::vector<uint64_t> last_sectors(metadata_.block_devices.size());
 
     for (const auto& extent : metadata_.extents) {
@@ -407,7 +408,7 @@
     return true;
 }
 
-int SparseBuilder::OpenImageFile(const std::string& file) {
+int ImageBuilder::OpenImageFile(const std::string& file) {
     android::base::unique_fd source_fd = GetControlFileOrOpen(file.c_str(), O_RDONLY | O_CLOEXEC);
     if (source_fd < 0) {
         PERROR << "open image file failed: " << file;
@@ -437,15 +438,16 @@
     return temp_fds_.back().get();
 }
 
-bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
-                       const std::map<std::string, std::string>& images) {
-    SparseBuilder builder(metadata, block_size, images);
+bool WriteToImageFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
+                      const std::map<std::string, std::string>& images, bool sparsify) {
+    ImageBuilder builder(metadata, block_size, images, sparsify);
     return builder.IsValid() && builder.Build() && builder.Export(file);
 }
 
-bool WriteSplitSparseFiles(const std::string& output_dir, const LpMetadata& metadata,
-                           uint32_t block_size, const std::map<std::string, std::string>& images) {
-    SparseBuilder builder(metadata, block_size, images);
+bool WriteSplitImageFiles(const std::string& output_dir, const LpMetadata& metadata,
+                          uint32_t block_size, const std::map<std::string, std::string>& images,
+                          bool sparsify) {
+    ImageBuilder builder(metadata, block_size, images, sparsify);
     return builder.IsValid() && builder.Build() && builder.ExportFiles(output_dir);
 }
 
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
index 44217a0..75060f9 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -32,13 +32,13 @@
 bool WriteToImageFile(const char* file, const LpMetadata& metadata);
 bool WriteToImageFile(int fd, const LpMetadata& metadata);
 
-// We use an object to build the sparse file since it requires that data
+// We use an object to build the image file since it requires that data
 // pointers be held alive until the sparse file is destroyed. It's easier
 // to do this when the data pointers are all in one place.
-class SparseBuilder {
+class ImageBuilder {
   public:
-    SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
-                  const std::map<std::string, std::string>& images);
+    ImageBuilder(const LpMetadata& metadata, uint32_t block_size,
+                 const std::map<std::string, std::string>& images, bool sparsify);
 
     bool Build();
     bool Export(const char* file);
@@ -60,6 +60,7 @@
     const LpMetadata& metadata_;
     const LpMetadataGeometry& geometry_;
     uint32_t block_size_;
+    bool sparsify_;
 
     std::vector<SparsePtr> device_images_;
     std::string all_metadata_;
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 6348f55..5f782b0 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -72,8 +72,8 @@
 
 // Read/Write logical partition metadata to an image file, for diagnostics or
 // flashing.
-bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
-                       const std::map<std::string, std::string>& images);
+bool WriteToImageFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
+                      const std::map<std::string, std::string>& images, bool sparsify);
 bool WriteToImageFile(const char* file, const LpMetadata& metadata);
 std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file);
 std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes);
@@ -83,8 +83,9 @@
 // is intended for retrofit devices, and will generate one sparse file per
 // block device (each named super_<name>.img) and placed in the specified
 // output folder.
-bool WriteSplitSparseFiles(const std::string& output_dir, const LpMetadata& metadata,
-                           uint32_t block_size, const std::map<std::string, std::string>& images);
+bool WriteSplitImageFiles(const std::string& output_dir, const LpMetadata& metadata,
+                          uint32_t block_size, const std::map<std::string, std::string>& images,
+                          bool sparsify);
 
 // Helper to extract safe C++ strings from partition info.
 std::string GetPartitionName(const LpMetadataPartition& partition);
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 9f3314d..8fc02cb 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -598,7 +598,7 @@
     ASSERT_NE(exported, nullptr);
 
     // Build the sparse file.
-    SparseBuilder sparse(*exported.get(), 512, {});
+    ImageBuilder sparse(*exported.get(), 512, {}, true /* sparsify */);
     ASSERT_TRUE(sparse.IsValid());
     ASSERT_TRUE(sparse.Build());
 
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 19737fe..eb9f525 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -52,6 +52,14 @@
     host_supported: true,
 }
 
+sh_test {
+    name: "adb-remount-sh",
+    src: "adb-remount-test.sh",
+    filename: "adb-remount-test.sh",
+    test_suites: ["general-tests"],
+    test_config: "adb-remount-sh.xml",
+}
+
 java_test_host {
     name: "fs_mgr_vendor_overlay_test",
 
diff --git a/fs_mgr/tests/adb-remount-sh.xml b/fs_mgr/tests/adb-remount-sh.xml
new file mode 100644
index 0000000..716e324
--- /dev/null
+++ b/fs_mgr/tests/adb-remount-sh.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for adb remount test cases">
+    <option name="test-suite-tag" value="adb-remount" />
+    <!-- This test requires a device, so it's not annotated with a null-device -->
+    <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
+        <option name="binary" value="adb-remount-test.sh" />
+    </test>
+</configuration>
+
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index c09dc3d..c22176b 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -1026,7 +1026,7 @@
 elif [ -z "${ANDROID_HOST_OUT}" ]; then
   echo "${ORANGE}[  WARNING ]${NORMAL} please run lunch, skipping"
 else
-  adb reboot-fastboot ||
+  adb reboot fastboot ||
     die "fastbootd not supported (wrong adb in path?)"
   any_wait 2m &&
     inFastboot ||
@@ -1136,7 +1136,7 @@
 
   echo "${GREEN}[ RUN      ]${NORMAL} test fastboot flash to ${scratch_partition} recovery" >&2
 
-  adb reboot-fastboot ||
+  adb reboot fastboot ||
     die "Reboot into fastbootd"
   img=${TMPDIR}/adb-remount-test-${$}.img
   cleanup() {
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 63661f0..9309aad 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -316,7 +316,7 @@
 
     DeviceMapper& dm = DeviceMapper::Instance();
     std::vector<DeviceMapper::TargetInfo> table;
-    if (!dm.GetTableStatus(argv[0], &table)) {
+    if (!dm.GetTableInfo(argv[0], &table)) {
         std::cerr << "Could not query table status of device \"" << argv[0] << "\"." << std::endl;
         return -EINVAL;
     }
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 706dc80..3da8bda 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -64,7 +64,8 @@
     clear_screen();
 
     /* try to display *something* */
-    if (batt_anim->cur_level < 0 || batt_anim->num_frames == 0)
+    if (batt_anim->cur_status == BATTERY_STATUS_UNKNOWN || batt_anim->cur_level < 0 ||
+        batt_anim->num_frames == 0)
         draw_unknown(surf_unknown);
     else
         draw_battery(batt_anim);
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 5fe58ac..0e5aa4f 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -36,6 +36,7 @@
 #include <linux/netlink.h>
 #include <sys/socket.h>
 
+#include <cutils/android_get_control_file.h>
 #include <cutils/klog.h>
 #include <cutils/misc.h>
 #include <cutils/properties.h>
@@ -76,6 +77,7 @@
 #define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC)
 #define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
 #define UNPLUGGED_DISPLAY_TIME (3 * MSEC_PER_SEC)
+#define MAX_BATT_LEVEL_WAIT_TIME (3 * MSEC_PER_SEC)
 
 #define LAST_KMSG_MAX_SZ (32 * 1024)
 
@@ -104,6 +106,7 @@
     int64_t next_screen_transition;
     int64_t next_key_check;
     int64_t next_pwr_check;
+    int64_t wait_batt_level_timestamp;
 
     key_state keys[KEY_MAX + 1];
 
@@ -206,10 +209,9 @@
 #define MAX_KLOG_WRITE_BUF_SZ 256
 
 static void dump_last_kmsg(void) {
-    char* buf;
+    std::string buf;
     char* ptr;
-    unsigned sz = 0;
-    int len;
+    size_t len;
 
     LOGW("\n");
     LOGW("*************** LAST KMSG ***************\n");
@@ -221,21 +223,25 @@
         "/proc/last_kmsg",
         // clang-format on
     };
-    for (size_t i = 0; i < arraysize(kmsg); ++i) {
-        buf = (char*)load_file(kmsg[i], &sz);
-        if (buf && sz) break;
+    for (size_t i = 0; i < arraysize(kmsg) && buf.empty(); ++i) {
+        auto fd = android_get_control_file(kmsg[i]);
+        if (fd >= 0) {
+            android::base::ReadFdToString(fd, &buf);
+        } else {
+            android::base::ReadFileToString(kmsg[i], &buf);
+        }
     }
 
-    if (!buf || !sz) {
+    if (buf.empty()) {
         LOGW("last_kmsg not found. Cold reset?\n");
         goto out;
     }
 
-    len = min(sz, LAST_KMSG_MAX_SZ);
-    ptr = buf + (sz - len);
+    len = min(buf.size(), LAST_KMSG_MAX_SZ);
+    ptr = &buf[buf.size() - len];
 
     while (len > 0) {
-        int cnt = min(len, MAX_KLOG_WRITE_BUF_SZ);
+        size_t cnt = min(len, MAX_KLOG_WRITE_BUF_SZ);
         char yoink;
         char* nl;
 
@@ -251,8 +257,6 @@
         ptr += cnt;
     }
 
-    free(buf);
-
 out:
     LOGW("\n");
     LOGW("************* END LAST KMSG *************\n");
@@ -288,6 +292,21 @@
 
     if (!batt_anim->run || now < charger->next_screen_transition) return;
 
+    // If battery level is not ready, keep checking in the defined time
+    if (batt_prop == nullptr ||
+        (batt_prop->batteryLevel == 0 && batt_prop->batteryStatus == BATTERY_STATUS_UNKNOWN)) {
+        if (charger->wait_batt_level_timestamp == 0) {
+            // Set max delay time and skip drawing screen
+            charger->wait_batt_level_timestamp = now + MAX_BATT_LEVEL_WAIT_TIME;
+            LOGV("[%" PRId64 "] wait for battery capacity ready\n", now);
+            return;
+        } else if (now <= charger->wait_batt_level_timestamp) {
+            // Do nothing, keep waiting
+            return;
+        }
+        // If timeout and battery level is still not ready, draw unknown battery
+    }
+
     if (healthd_draw == nullptr) {
         if (healthd_config && healthd_config->screen_on) {
             if (!healthd_config->screen_on(batt_prop)) {
@@ -707,6 +726,7 @@
     charger->next_screen_transition = -1;
     charger->next_key_check = -1;
     charger->next_pwr_check = -1;
+    charger->wait_batt_level_timestamp = 0;
 
     // Initialize Health implementation (which initializes the internal BatteryMonitor).
     Health::initInstance(config);
diff --git a/init/Android.bp b/init/Android.bp
index 8292aa0..69ee34f 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -61,6 +61,7 @@
     static_libs: [
         "libseccomp_policy",
         "libavb",
+        "libcgrouprc_format",
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
@@ -82,6 +83,7 @@
         "liblogwrap",
         "liblp",
         "libprocessgroup",
+        "libprocessgroup_setup",
         "libselinux",
         "libutils",
     ],
diff --git a/init/devices.cpp b/init/devices.cpp
index 1a77ba1..159c75e 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -21,8 +21,14 @@
 #include <sys/sysmacros.h>
 #include <unistd.h>
 
+#include <chrono>
+#include <map>
 #include <memory>
+#include <string>
+#include <thread>
 
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -37,12 +43,16 @@
 #error "Do not include init.h in files used by ueventd; it will expose init's globals"
 #endif
 
+using namespace std::chrono_literals;
+
 using android::base::Basename;
 using android::base::Dirname;
+using android::base::ReadFileToString;
 using android::base::Readlink;
 using android::base::Realpath;
 using android::base::StartsWith;
 using android::base::StringPrintf;
+using android::base::Trim;
 
 namespace android {
 namespace init {
@@ -101,6 +111,31 @@
     return true;
 }
 
+// Given a path that may start with a virtual dm block device, populate
+// the supplied buffer with the dm module's instantiated name.
+// If it doesn't start with a virtual block device, or there is some
+// error, return false.
+static bool FindDmDevicePartition(const std::string& path, std::string* result) {
+    result->clear();
+    if (!StartsWith(path, "/devices/virtual/block/dm-")) return false;
+    if (getpid() == 1) return false;  // first_stage_init has no sepolicy needs
+
+    static std::map<std::string, std::string> cache;
+    // wait_for_file will not work, the content is also delayed ...
+    for (android::base::Timer t; t.duration() < 200ms; std::this_thread::sleep_for(10ms)) {
+        if (ReadFileToString("/sys" + path + "/dm/name", result) && !result->empty()) {
+            // Got it, set cache with result, when node arrives
+            cache[path] = *result = Trim(*result);
+            return true;
+        }
+    }
+    auto it = cache.find(path);
+    if ((it == cache.end()) || (it->second.empty())) return false;
+    // Return cached results, when node goes away
+    *result = it->second;
+    return true;
+}
+
 Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
     : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
     // Set 'prefix_' or 'wildcard_' based on the below cases:
@@ -293,6 +328,7 @@
 std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const {
     std::string device;
     std::string type;
+    std::string partition;
 
     if (FindPlatformDevice(uevent.path, &device)) {
         // Skip /devices/platform or /devices/ if present
@@ -310,6 +346,8 @@
         type = "pci";
     } else if (FindVbdDevicePrefix(uevent.path, &device)) {
         type = "vbd";
+    } else if (FindDmDevicePartition(uevent.path, &partition)) {
+        return {"/dev/block/mapper/" + partition};
     } else {
         return {};
     }
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 4b0f05d..d458924 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -78,6 +78,7 @@
     ListenerAction HandleBlockDevice(const std::string& name, const Uevent&);
     bool InitRequiredDevices();
     bool InitMappedDevice(const std::string& verity_device);
+    bool InitDeviceMapper();
     bool CreateLogicalPartitions();
     bool MountPartition(const Fstab::iterator& begin, bool erase_used_fstab_entry,
                         Fstab::iterator* end = nullptr);
@@ -97,6 +98,7 @@
     virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0;
 
     bool need_dm_verity_;
+    bool gsi_not_on_userdata_ = false;
 
     Fstab fstab_;
     std::string lp_metadata_partition_;
@@ -267,8 +269,6 @@
     }
 
     required_devices_partition_names_.emplace(super_partition_name_);
-    // When booting from live GSI images, userdata is the super device.
-    required_devices_partition_names_.emplace("userdata");
     return true;
 }
 
@@ -281,25 +281,7 @@
     }
 
     if (IsDmLinearEnabled() || need_dm_verity_) {
-        const std::string dm_path = "/devices/virtual/misc/device-mapper";
-        bool found = false;
-        auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
-            if (uevent.path == dm_path) {
-                device_handler_->HandleUevent(uevent);
-                found = true;
-                return ListenerAction::kStop;
-            }
-            return ListenerAction::kContinue;
-        };
-        uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
-        if (!found) {
-            LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
-            Timer t;
-            uevent_listener_.Poll(dm_callback, 10s);
-            LOG(INFO) << "Wait for device-mapper returned after " << t;
-        }
-        if (!found) {
-            LOG(ERROR) << "device-mapper device not found after polling timeout";
+        if (!InitDeviceMapper()) {
             return false;
         }
     }
@@ -327,11 +309,36 @@
     return true;
 }
 
+bool FirstStageMount::InitDeviceMapper() {
+    const std::string dm_path = "/devices/virtual/misc/device-mapper";
+    bool found = false;
+    auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
+        if (uevent.path == dm_path) {
+            device_handler_->HandleUevent(uevent);
+            found = true;
+            return ListenerAction::kStop;
+        }
+        return ListenerAction::kContinue;
+    };
+    uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
+    if (!found) {
+        LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
+        Timer t;
+        uevent_listener_.Poll(dm_callback, 10s);
+        LOG(INFO) << "Wait for device-mapper returned after " << t;
+    }
+    if (!found) {
+        LOG(ERROR) << "device-mapper device not found after polling timeout";
+        return false;
+    }
+    return true;
+}
+
 bool FirstStageMount::InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata) {
     auto partition_names = android::fs_mgr::GetBlockDevicePartitionNames(metadata);
     for (const auto& partition_name : partition_names) {
-        const auto super_device = android::fs_mgr::GetMetadataSuperBlockDevice(metadata);
-        if (partition_name == android::fs_mgr::GetBlockDevicePartitionName(*super_device)) {
+        // The super partition was found in the earlier pass.
+        if (partition_name == super_partition_name_) {
             continue;
         }
         required_devices_partition_names_.emplace(partition_name);
@@ -499,6 +506,10 @@
     if (system_partition == fstab_.end()) return true;
 
     if (MountPartition(system_partition, false)) {
+        if (gsi_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
+            LOG(ERROR) << "check_most_at_once forbidden on external media";
+            return false;
+        }
         SwitchRoot("/system");
     } else {
         PLOG(ERROR) << "Failed to mount /system";
@@ -612,7 +623,29 @@
         return;
     }
 
-    if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), "/dev/block/by-name/userdata")) {
+    if (!InitDmLinearBackingDevices(*metadata.get())) {
+        return;
+    }
+
+    // Device-mapper might not be ready if the device doesn't use DAP or verity
+    // (for example, hikey).
+    if (access("/dev/device-mapper", F_OK) && !InitDeviceMapper()) {
+        return;
+    }
+
+    // Find the name of the super partition for the GSI. It will either be
+    // "userdata", or a block device such as an sdcard. There are no by-name
+    // partitions other than userdata that we support installing GSIs to.
+    auto super = GetMetadataSuperBlockDevice(*metadata.get());
+    std::string super_name = android::fs_mgr::GetBlockDevicePartitionName(*super);
+    std::string super_path;
+    if (super_name == "userdata") {
+        super_path = "/dev/block/by-name/" + super_name;
+    } else {
+        super_path = "/dev/block/" + super_name;
+    }
+
+    if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path)) {
         LOG(ERROR) << "GSI partition layout could not be instantiated";
         return;
     }
@@ -630,6 +663,7 @@
         fstab_.erase(system_partition);
     }
     fstab_.emplace_back(BuildGsiSystemFstabEntry());
+    gsi_not_on_userdata_ = (super_name != "userdata");
 }
 
 bool FirstStageMountVBootV1::GetDmVerityDevices() {
diff --git a/init/init.cpp b/init/init.cpp
index a5f4549..0f44efd 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -45,6 +45,7 @@
 #include <libavb/libavb.h>
 #include <libgsi/libgsi.h>
 #include <processgroup/processgroup.h>
+#include <processgroup/setup.h>
 #include <selinux/android.h>
 
 #ifndef RECOVERY
@@ -357,8 +358,8 @@
 static Result<Success> SetupCgroupsAction(const BuiltinArguments&) {
     // Have to create <CGROUPS_RC_DIR> using make_dir function
     // for appropriate sepolicy to be set for it
-    make_dir(CGROUPS_RC_DIR, 0711);
-    if (!CgroupSetupCgroups()) {
+    make_dir(android::base::Dirname(CGROUPS_RC_PATH), 0711);
+    if (!CgroupSetup()) {
         return ErrnoError() << "Failed to setup cgroups";
     }
 
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
index 12dfc6d..c8f0e76 100644
--- a/init/mount_handler.cpp
+++ b/init/mount_handler.cpp
@@ -27,6 +27,7 @@
 #include <algorithm>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -34,6 +35,7 @@
 #include <android-base/strings.h>
 #include <fs_mgr.h>
 #include <fstab/fstab.h>
+#include <libdm/dm.h>
 
 #include "epoll.h"
 #include "property_service.h"
@@ -47,8 +49,13 @@
     auto fields = android::base::Split(line, " ");
     while (fields.size() < 3) fields.emplace_back("");
     if (fields[0] == "/dev/root") {
-        if (android::fs_mgr::Fstab fstab; android::fs_mgr::ReadDefaultFstab(&fstab)) {
-            if (auto entry = GetEntryForMountPoint(&fstab, "/")) {
+        auto& dm = dm::DeviceMapper::Instance();
+        std::string path;
+        if (dm.GetDmDevicePathByName("system", &path) || dm.GetDmDevicePathByName("vroot", &path)) {
+            fields[0] = path;
+        } else if (android::fs_mgr::Fstab fstab; android::fs_mgr::ReadDefaultFstab(&fstab)) {
+            auto entry = GetEntryForMountPoint(&fstab, "/");
+            if (entry || (entry = GetEntryForMountPoint(&fstab, "/system"))) {
                 fields[0] = entry->blk_device;
             }
         }
@@ -77,14 +84,19 @@
         struct stat sb;
         if (stat(queue.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
         if (stat(entry.mount_point.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
-        // Skip the noise associated with APEX until there is a need
+        // Clear the noise associated with loopback and APEX.
         if (android::base::StartsWith(value, "loop")) value = "";
+        if (android::base::StartsWith(entry.mount_point, "/apex/")) value = "";
     }
-    std::string property =
-            "dev.mnt.blk" + ((entry.mount_point == "/") ? "/root" : entry.mount_point);
-    std::replace(property.begin(), property.end(), '/', '.');
-    if (value.empty() && android::base::GetProperty(property, "").empty()) return;
-    property_set(property, value);
+    auto mount_prop = entry.mount_point;
+    if (mount_prop == "/") mount_prop = "/root";
+    std::replace(mount_prop.begin(), mount_prop.end(), '/', '.');
+    mount_prop = "dev.mnt.blk" + mount_prop;
+    // Set property even if its value does not change to trigger 'on property:'
+    // handling, except for clearing non-existent or already clear property.
+    // Goal is reduction of empty properties and associated triggers.
+    if (value.empty() && android::base::GetProperty(mount_prop, "").empty()) return;
+    property_set(mount_prop, value);
 }
 
 }  // namespace
@@ -114,25 +126,27 @@
 
 void MountHandler::MountHandlerFunction() {
     rewind(fp_.get());
+    std::vector<MountHandlerEntry> touched;
+    auto untouched = mounts_;
     char* buf = nullptr;
     size_t len = 0;
-    auto untouched = mounts_;
     while (getline(&buf, &len, fp_.get()) != -1) {
-        auto entry = ParseMount(std::string(buf, len));
+        auto entry = ParseMount(std::string(buf));
         auto match = untouched.find(entry);
         if (match == untouched.end()) {
-            SetMountProperty(entry, true);
-            mounts_.emplace(std::move(entry));
+            touched.emplace_back(std::move(entry));
         } else {
             untouched.erase(match);
         }
     }
     free(buf);
     for (auto entry : untouched) {
-        auto match = mounts_.find(entry);
-        if (match == mounts_.end()) continue;
-        mounts_.erase(match);
         SetMountProperty(entry, false);
+        mounts_.erase(entry);
+    }
+    for (auto entry : touched) {
+        SetMountProperty(entry, true);
+        mounts_.emplace(std::move(entry));
     }
 }
 
diff --git a/init/property_service.cpp b/init/property_service.cpp
index fc5538c..467568c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -557,9 +557,8 @@
         uint32_t result =
             HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
         if (result != PROP_SUCCESS) {
-            LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value
-                       << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
-                       << error;
+            LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
+                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
         }
 
         break;
@@ -579,9 +578,8 @@
         std::string error;
         uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
         if (result != PROP_SUCCESS) {
-            LOG(ERROR) << "Unable to set property '" << name << "' to '" << value
-                       << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
-                       << error;
+            LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
+                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
         }
         socket.SendUint32(result);
         break;
diff --git a/init/service.cpp b/init/service.cpp
index 6d08cb1..f5c13b9 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -177,7 +177,7 @@
 Result<Success> Service::EnterNamespaces() const {
     for (const auto& [nstype, path] : namespaces_to_enter_) {
         auto fd = unique_fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
-        if (!fd) {
+        if (fd == -1) {
             return ErrnoError() << "Could not open namespace at " << path;
         }
         if (setns(fd, nstype) == -1) {
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 83a7140..619bc56 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -83,6 +83,8 @@
             ],
         },
         windows: {
+            host_ldlibs: ["-lws2_32"],
+
             srcs: [
                 "socket_inaddr_any_server_windows.cpp",
                 "socket_network_client_windows.cpp",
@@ -91,9 +93,6 @@
             ],
 
             enabled: true,
-            shared: {
-                enabled: false,
-            },
             cflags: [
                 "-D_GNU_SOURCE",
             ],
@@ -236,6 +235,7 @@
     "libbase",
     "libjsoncpp",
     "libprocessgroup",
+    "libcgrouprc",
 ]
 
 cc_test {
@@ -250,7 +250,10 @@
     name: "libcutils_test_static",
     test_suites: ["device-tests"],
     defaults: ["libcutils_test_default"],
-    static_libs: ["libc"] + test_libraries,
+    static_libs: [
+        "libc",
+        "libcgrouprc_format",
+    ] + test_libraries,
     stl: "libc++_static",
 
     target: {
diff --git a/libcutils/android_reboot.cpp b/libcutils/android_reboot.cpp
index ce41cd3..e0def71 100644
--- a/libcutils/android_reboot.cpp
+++ b/libcutils/android_reboot.cpp
@@ -23,12 +23,12 @@
 
 #define TAG "android_reboot"
 
-int android_reboot(int cmd, int /*flags*/, const char* arg) {
+int android_reboot(unsigned cmd, int /*flags*/, const char* arg) {
     int ret;
     const char* restart_cmd = NULL;
     char* prop_value;
 
-    switch (static_cast<unsigned>(cmd)) {
+    switch (cmd) {
         case ANDROID_RB_RESTART:  // deprecated
         case ANDROID_RB_RESTART2:
             restart_cmd = "reboot";
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index e35b91a..e67b458 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -73,6 +73,8 @@
 #ifndef __ANDROID_VNDK__
 using openFdType = int (*)();
 
+static openFdType openFd;
+
 openFdType initOpenAshmemFd() {
     openFdType openFd = nullptr;
     void* handle = dlopen("libashmemd_client.so", RTLD_NOW);
@@ -221,7 +223,10 @@
 
     int fd = -1;
 #ifndef __ANDROID_VNDK__
-    static auto openFd = initOpenAshmemFd();
+    if (!openFd) {
+        openFd = initOpenAshmemFd();
+    }
+
     if (openFd) {
         fd = openFd();
     }
@@ -480,3 +485,11 @@
 
     return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL)));
 }
+
+void ashmem_init() {
+#ifndef __ANDROID_VNDK__
+    pthread_mutex_lock(&__ashmem_lock);
+    openFd = initOpenAshmemFd();
+    pthread_mutex_unlock(&__ashmem_lock);
+#endif  //__ANDROID_VNDK__
+}
diff --git a/libcutils/ashmem-host.cpp b/libcutils/ashmem-host.cpp
index bb990d5..32446d4 100644
--- a/libcutils/ashmem-host.cpp
+++ b/libcutils/ashmem-host.cpp
@@ -82,3 +82,5 @@
 
     return buf.st_size;
 }
+
+void ashmem_init() {}
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index a3df380..6217bc8 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -91,7 +91,7 @@
     { 00750, AID_ROOT,         AID_SHELL,        0, "sbin" },
     { 00777, AID_ROOT,         AID_ROOT,         0, "sdcard" },
     { 00751, AID_ROOT,         AID_SDCARD_R,     0, "storage" },
-    { 00755, AID_ROOT,         AID_SHELL,        0, "system/bin" },
+    { 00751, AID_ROOT,         AID_SHELL,        0, "system/bin" },
     { 00755, AID_ROOT,         AID_ROOT,         0, "system/etc/ppp" },
     { 00755, AID_ROOT,         AID_SHELL,        0, "system/vendor" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system/xbin" },
diff --git a/libcutils/include/cutils/android_reboot.h b/libcutils/include/cutils/android_reboot.h
index 99030ed..cd27eef 100644
--- a/libcutils/include/cutils/android_reboot.h
+++ b/libcutils/include/cutils/android_reboot.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef __CUTILS_ANDROID_REBOOT_H__
-#define __CUTILS_ANDROID_REBOOT_H__
+#pragma once
 
 #include <sys/cdefs.h>
 
@@ -36,10 +35,8 @@
 /* Reboot or shutdown the system.
  * This call uses ANDROID_RB_PROPERTY to request reboot to init process.
  * Due to that, process calling this should have proper selinux permission
- * to write to the property. Otherwise, the call will fail.
+ * to write to the property or the call will fail.
  */
-int android_reboot(int cmd, int flags, const char *arg);
+int android_reboot(unsigned cmd, int flags, const char* arg);
 
 __END_DECLS
-
-#endif /* __CUTILS_ANDROID_REBOOT_H__ */
diff --git a/libcutils/include/cutils/ashmem.h b/libcutils/include/cutils/ashmem.h
index d80caa6..abc5068 100644
--- a/libcutils/include/cutils/ashmem.h
+++ b/libcutils/include/cutils/ashmem.h
@@ -26,6 +26,7 @@
 int ashmem_pin_region(int fd, size_t offset, size_t len);
 int ashmem_unpin_region(int fd, size_t offset, size_t len);
 int ashmem_get_size_region(int fd);
+void ashmem_init();
 
 #ifdef __cplusplus
 }
diff --git a/libkeyutils/mini_keyctl.cpp b/libkeyutils/mini_keyctl.cpp
index e09c864..fe89e62 100644
--- a/libkeyutils/mini_keyctl.cpp
+++ b/libkeyutils/mini_keyctl.cpp
@@ -30,7 +30,6 @@
   fprintf(stderr, "usage: mini-keyctl <action> [args,]\n");
   fprintf(stderr, "       mini-keyctl add <type> <desc> <data> <keyring>\n");
   fprintf(stderr, "       mini-keyctl padd <type> <desc> <keyring>\n");
-  fprintf(stderr, "       mini-keyctl dadd <type> <desc_prefix> <cert_dir> <keyring>\n");
   fprintf(stderr, "       mini-keyctl unlink <key> <keyring>\n");
   fprintf(stderr, "       mini-keyctl restrict_keyring <keyring>\n");
   fprintf(stderr, "       mini-keyctl security <key>\n");
@@ -56,14 +55,6 @@
     std::string data = argv[4];
     std::string keyring = argv[5];
     return Add(type, desc, data, keyring);
-  } else if (action == "dadd") {
-    if (argc != 6) Usage(1);
-    std::string type = argv[2];
-    // The key description contains desc_prefix and an index.
-    std::string desc_prefix = argv[3];
-    std::string cert_dir = argv[4];
-    std::string keyring = argv[5];
-    return AddCertsFromDir(type, desc_prefix, cert_dir, keyring);
   } else if (action == "padd") {
     if (argc != 5) Usage(1);
     std::string type = argv[2];
diff --git a/libkeyutils/mini_keyctl_utils.cpp b/libkeyutils/mini_keyctl_utils.cpp
index 9fe2dfe..56afea4 100644
--- a/libkeyutils/mini_keyctl_utils.cpp
+++ b/libkeyutils/mini_keyctl_utils.cpp
@@ -86,53 +86,6 @@
   return false;
 }
 
-int AddCertsFromDir(const std::string& type, const std::string& desc_prefix,
-                    const std::string& cert_dir, const std::string& keyring) {
-  key_serial_t keyring_id;
-  if (!GetKeyringId(keyring, &keyring_id)) {
-    LOG(ERROR) << "Can not find keyring id";
-    return 1;
-  }
-
-  std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(cert_dir.c_str()), closedir);
-  if (!dir) {
-    PLOG(WARNING) << "Failed to open directory " << cert_dir;
-    return 1;
-  }
-  int keys_added = 0;
-  struct dirent* dp;
-  while ((dp = readdir(dir.get())) != NULL) {
-    if (dp->d_type != DT_REG) {
-      continue;
-    }
-    std::string cert_path = cert_dir + "/" + dp->d_name;
-    std::string cert_buf;
-    if (!android::base::ReadFileToString(cert_path, &cert_buf, false /* follow_symlinks */)) {
-      LOG(ERROR) << "Failed to read " << cert_path;
-      continue;
-    }
-
-    if (cert_buf.size() > kMaxCertSize) {
-      LOG(ERROR) << "Certficate size too large: " << cert_path;
-      continue;
-    }
-
-    // Add key to keyring.
-    int key_desc_index = keys_added;
-    std::string key_desc = desc_prefix + std::to_string(key_desc_index);
-    key_serial_t key =
-        add_key(type.c_str(), key_desc.c_str(), &cert_buf[0], cert_buf.size(), keyring_id);
-    if (key < 0) {
-      PLOG(ERROR) << "Failed to add key to keyring: " << cert_path;
-      continue;
-    }
-    LOG(INFO) << "Key " << cert_path << " added to " << keyring << " with key id 0x" << std::hex
-              << key;
-    keys_added++;
-  }
-  return 0;
-}
-
 int Unlink(key_serial_t key, const std::string& keyring) {
   key_serial_t keyring_id;
   if (!GetKeyringId(keyring, &keyring_id)) {
diff --git a/libkeyutils/mini_keyctl_utils.h b/libkeyutils/mini_keyctl_utils.h
index 804a357..3616831 100644
--- a/libkeyutils/mini_keyctl_utils.h
+++ b/libkeyutils/mini_keyctl_utils.h
@@ -18,11 +18,6 @@
 
 #include <string>
 
-// Add all files in a directory as certificates to a keyring. |keyring| could be the keyring
-// description or keyring id in hex.
-int AddCertsFromDir(const std::string& type, const std::string& desc_prefix,
-                    const std::string& cert_dir, const std::string& keyring);
-
 // Add key to a keyring. Returns non-zero if error happens.
 int Add(const std::string& type, const std::string& desc, const std::string& data,
         const std::string& keyring);
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 9b41ebe..da475cb 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -139,6 +139,5 @@
 llndk_library {
     name: "liblog",
     symbol_file: "liblog.map.txt",
-    unversioned: true,
     export_include_dirs: ["include_vndk"],
 }
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index b9f0dbf..66cb49f 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -24,6 +24,13 @@
         "libnativebridge",
         "libbase",
     ],
+    target: {
+        android: {
+            shared_libs: [
+                "libdl_android",
+            ],
+        },
+    },
     required: [
         "llndk.libraries.txt",
         "vndksp.libraries.txt",
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index d97f09f..78a02e5 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -31,6 +31,7 @@
     },
     shared_libs: [
         "libbase",
+        "libcgrouprc",
         "libjsoncpp",
     ],
     // for cutils/android_filesystem_config.h
@@ -45,5 +46,6 @@
     cflags: [
         "-Wall",
         "-Werror",
+        "-Wexit-time-destructors",
     ],
 }
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index b3b497f..6cd6b6e 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -44,241 +44,42 @@
 using android::base::StringPrintf;
 using android::base::unique_fd;
 
-static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json";
-static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json";
-
 static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
 static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
 static constexpr const char* CGROUP_TASKS_FILE_V2 = "/cgroup.tasks";
 
-static bool Mkdir(const std::string& path, mode_t mode, const std::string& uid,
-                  const std::string& gid) {
-    if (mode == 0) {
-        mode = 0755;
-    }
-
-    if (mkdir(path.c_str(), mode) != 0) {
-        /* chmod in case the directory already exists */
-        if (errno == EEXIST) {
-            if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
-                // /acct is a special case when the directory already exists
-                // TODO: check if file mode is already what we want instead of using EROFS
-                if (errno != EROFS) {
-                    PLOG(ERROR) << "fchmodat() failed for " << path;
-                    return false;
-                }
-            }
-        } else {
-            PLOG(ERROR) << "mkdir() failed for " << path;
-            return false;
-        }
-    }
-
-    if (uid.empty()) {
-        return true;
-    }
-
-    passwd* uid_pwd = getpwnam(uid.c_str());
-    if (!uid_pwd) {
-        PLOG(ERROR) << "Unable to decode UID for '" << uid << "'";
-        return false;
-    }
-
-    uid_t pw_uid = uid_pwd->pw_uid;
-    gid_t gr_gid = -1;
-    if (!gid.empty()) {
-        group* gid_pwd = getgrnam(gid.c_str());
-        if (!gid_pwd) {
-            PLOG(ERROR) << "Unable to decode GID for '" << gid << "'";
-            return false;
-        }
-        gr_gid = gid_pwd->gr_gid;
-    }
-
-    if (lchown(path.c_str(), pw_uid, gr_gid) < 0) {
-        PLOG(ERROR) << "lchown() failed for " << path;
-        return false;
-    }
-
-    /* chown may have cleared S_ISUID and S_ISGID, chmod again */
-    if (mode & (S_ISUID | S_ISGID)) {
-        if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
-            PLOG(ERROR) << "fchmodat() failed for " << path;
-            return false;
-        }
-    }
-
-    return true;
+uint32_t CgroupController::version() const {
+    CHECK(HasValue());
+    return ACgroupController_getVersion(controller_);
 }
 
-static bool ReadDescriptorsFromFile(const std::string& file_name,
-                                    std::map<std::string, CgroupDescriptor>* descriptors) {
-    std::vector<CgroupDescriptor> result;
-    std::string json_doc;
-
-    if (!android::base::ReadFileToString(file_name, &json_doc)) {
-        PLOG(ERROR) << "Failed to read task profiles from " << file_name;
-        return false;
-    }
-
-    Json::Reader reader;
-    Json::Value root;
-    if (!reader.parse(json_doc, root)) {
-        LOG(ERROR) << "Failed to parse cgroups description: " << reader.getFormattedErrorMessages();
-        return false;
-    }
-
-    if (root.isMember("Cgroups")) {
-        const Json::Value& cgroups = root["Cgroups"];
-        for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
-            std::string name = cgroups[i]["Controller"].asString();
-            auto iter = descriptors->find(name);
-            if (iter == descriptors->end()) {
-                descriptors->emplace(name, CgroupDescriptor(1, name, cgroups[i]["Path"].asString(),
-                                     std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
-                                     cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString()));
-            } else {
-                iter->second = CgroupDescriptor(1, name, cgroups[i]["Path"].asString(),
-                                     std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
-                                     cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString());
-            }
-        }
-    }
-
-    if (root.isMember("Cgroups2")) {
-        const Json::Value& cgroups2 = root["Cgroups2"];
-        auto iter = descriptors->find(CGROUPV2_CONTROLLER_NAME);
-        if (iter == descriptors->end()) {
-            descriptors->emplace(CGROUPV2_CONTROLLER_NAME, CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
-                                 std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
-                                 cgroups2["UID"].asString(), cgroups2["GID"].asString()));
-        } else {
-            iter->second = CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
-                                 std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
-                                 cgroups2["UID"].asString(), cgroups2["GID"].asString());
-        }
-    }
-
-    return true;
+const char* CgroupController::name() const {
+    CHECK(HasValue());
+    return ACgroupController_getName(controller_);
 }
 
-static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors) {
-    // load system cgroup descriptors
-    if (!ReadDescriptorsFromFile(CGROUPS_DESC_FILE, descriptors)) {
-        return false;
-    }
-
-    // load vendor cgroup descriptors if the file exists
-    if (!access(CGROUPS_DESC_VENDOR_FILE, F_OK) &&
-        !ReadDescriptorsFromFile(CGROUPS_DESC_VENDOR_FILE, descriptors)) {
-        return false;
-    }
-
-    return true;
+const char* CgroupController::path() const {
+    CHECK(HasValue());
+    return ACgroupController_getPath(controller_);
 }
 
-// To avoid issues in sdk_mac build
-#if defined(__ANDROID__)
-
-static bool SetupCgroup(const CgroupDescriptor& descriptor) {
-    const CgroupController* controller = descriptor.controller();
-
-    // mkdir <path> [mode] [owner] [group]
-    if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
-        LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
-        return false;
-    }
-
-    int result;
-    if (controller->version() == 2) {
-        result = mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
-                       nullptr);
-    } else {
-        // Unfortunately historically cpuset controller was mounted using a mount command
-        // different from all other controllers. This results in controller attributes not
-        // to be prepended with controller name. For example this way instead of
-        // /dev/cpuset/cpuset.cpus the attribute becomes /dev/cpuset/cpus which is what
-        // the system currently expects.
-        if (!strcmp(controller->name(), "cpuset")) {
-            // mount cpuset none /dev/cpuset nodev noexec nosuid
-            result = mount("none", controller->path(), controller->name(),
-                           MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr);
-        } else {
-            // mount cgroup none <path> nodev noexec nosuid <controller>
-            result = mount("none", controller->path(), "cgroup", MS_NODEV | MS_NOEXEC | MS_NOSUID,
-                           controller->name());
-        }
-    }
-
-    if (result < 0) {
-        PLOG(ERROR) << "Failed to mount " << controller->name() << " cgroup";
-        return false;
-    }
-
-    return true;
+bool CgroupController::HasValue() const {
+    return controller_ != nullptr;
 }
 
-#else
+std::string CgroupController::GetTasksFilePath(const std::string& rel_path) const {
+    std::string tasks_path = path();
 
-// Stubs for non-Android targets.
-static bool SetupCgroup(const CgroupDescriptor&) {
-    return false;
+    if (!rel_path.empty()) {
+        tasks_path += "/" + rel_path;
+    }
+    return (version() == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;
 }
 
-#endif
-
-static bool WriteRcFile(const std::map<std::string, CgroupDescriptor>& descriptors) {
-    std::string cgroup_rc_path = StringPrintf("%s/%s", CGROUPS_RC_DIR, CgroupMap::CGROUPS_RC_FILE);
-    unique_fd fd(TEMP_FAILURE_RETRY(open(cgroup_rc_path.c_str(),
-                                         O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
-                                         S_IRUSR | S_IRGRP | S_IROTH)));
-    if (fd < 0) {
-        PLOG(ERROR) << "open() failed for " << cgroup_rc_path;
-        return false;
-    }
-
-    CgroupFile fl;
-    fl.version_ = CgroupFile::FILE_CURR_VERSION;
-    fl.controller_count_ = descriptors.size();
-    int ret = TEMP_FAILURE_RETRY(write(fd, &fl, sizeof(fl)));
-    if (ret < 0) {
-        PLOG(ERROR) << "write() failed for " << cgroup_rc_path;
-        return false;
-    }
-
-    for (const auto& [name, descriptor] : descriptors) {
-        ret = TEMP_FAILURE_RETRY(write(fd, descriptor.controller(), sizeof(CgroupController)));
-        if (ret < 0) {
-            PLOG(ERROR) << "write() failed for " << cgroup_rc_path;
-            return false;
-        }
-    }
-
-    return true;
-}
-
-CgroupController::CgroupController(uint32_t version, const std::string& name,
-                                   const std::string& path) {
-    version_ = version;
-    strncpy(name_, name.c_str(), sizeof(name_) - 1);
-    name_[sizeof(name_) - 1] = '\0';
-    strncpy(path_, path.c_str(), sizeof(path_) - 1);
-    path_[sizeof(path_) - 1] = '\0';
-}
-
-std::string CgroupController::GetTasksFilePath(const std::string& path) const {
-    std::string tasks_path = path_;
-
-    if (!path.empty()) {
-        tasks_path += "/" + path;
-    }
-    return (version_ == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;
-}
-
-std::string CgroupController::GetProcsFilePath(const std::string& path, uid_t uid,
+std::string CgroupController::GetProcsFilePath(const std::string& rel_path, uid_t uid,
                                                pid_t pid) const {
-    std::string proc_path(path_);
-    proc_path.append("/").append(path);
+    std::string proc_path(path());
+    proc_path.append("/").append(rel_path);
     proc_path = regex_replace(proc_path, std::regex("<uid>"), std::to_string(uid));
     proc_path = regex_replace(proc_path, std::regex("<pid>"), std::to_string(pid));
 
@@ -299,7 +100,7 @@
         return true;
     }
 
-    std::string cg_tag = StringPrintf(":%s:", name_);
+    std::string cg_tag = StringPrintf(":%s:", name());
     size_t start_pos = content.find(cg_tag);
     if (start_pos == std::string::npos) {
         return false;
@@ -316,25 +117,12 @@
     return true;
 }
 
-CgroupDescriptor::CgroupDescriptor(uint32_t version, const std::string& name,
-                                   const std::string& path, mode_t mode, const std::string& uid,
-                                   const std::string& gid)
-    : controller_(version, name, path), mode_(mode), uid_(uid), gid_(gid) {}
-
-CgroupMap::CgroupMap() : cg_file_data_(nullptr), cg_file_size_(0) {
+CgroupMap::CgroupMap() {
     if (!LoadRcFile()) {
         LOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
     }
 }
 
-CgroupMap::~CgroupMap() {
-    if (cg_file_data_) {
-        munmap(cg_file_data_, cg_file_size_);
-        cg_file_data_ = nullptr;
-        cg_file_size_ = 0;
-    }
-}
-
 CgroupMap& CgroupMap::GetInstance() {
     // Deliberately leak this object to avoid a race between destruction on
     // process exit and concurrent access from another thread.
@@ -343,129 +131,46 @@
 }
 
 bool CgroupMap::LoadRcFile() {
-    struct stat sb;
-
-    if (cg_file_data_) {
-        // Data already initialized
-        return true;
+    if (!loaded_) {
+        loaded_ = (ACgroupFile_getVersion() != 0);
     }
-
-    std::string cgroup_rc_path = StringPrintf("%s/%s", CGROUPS_RC_DIR, CGROUPS_RC_FILE);
-    unique_fd fd(TEMP_FAILURE_RETRY(open(cgroup_rc_path.c_str(), O_RDONLY | O_CLOEXEC)));
-    if (fd < 0) {
-        PLOG(ERROR) << "open() failed for " << cgroup_rc_path;
-        return false;
-    }
-
-    if (fstat(fd, &sb) < 0) {
-        PLOG(ERROR) << "fstat() failed for " << cgroup_rc_path;
-        return false;
-    }
-
-    size_t file_size = sb.st_size;
-    if (file_size < sizeof(CgroupFile)) {
-        LOG(ERROR) << "Invalid file format " << cgroup_rc_path;
-        return false;
-    }
-
-    CgroupFile* file_data = (CgroupFile*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
-    if (file_data == MAP_FAILED) {
-        PLOG(ERROR) << "Failed to mmap " << cgroup_rc_path;
-        return false;
-    }
-
-    if (file_data->version_ != CgroupFile::FILE_CURR_VERSION) {
-        LOG(ERROR) << cgroup_rc_path << " file version mismatch";
-        munmap(file_data, file_size);
-        return false;
-    }
-
-    if (file_size != sizeof(CgroupFile) + file_data->controller_count_ * sizeof(CgroupController)) {
-        LOG(ERROR) << cgroup_rc_path << " file has invalid size";
-        munmap(file_data, file_size);
-        return false;
-    }
-
-    cg_file_data_ = file_data;
-    cg_file_size_ = file_size;
-
-    return true;
+    return loaded_;
 }
 
 void CgroupMap::Print() const {
-    if (!cg_file_data_) {
+    if (!loaded_) {
         LOG(ERROR) << "CgroupMap::Print called for [" << getpid()
                    << "] failed, RC file was not initialized properly";
         return;
     }
-    LOG(INFO) << "File version = " << cg_file_data_->version_;
-    LOG(INFO) << "File controller count = " << cg_file_data_->controller_count_;
+    LOG(INFO) << "File version = " << ACgroupFile_getVersion();
+    LOG(INFO) << "File controller count = " << ACgroupFile_getControllerCount();
 
     LOG(INFO) << "Mounted cgroups:";
-    CgroupController* controller = (CgroupController*)(cg_file_data_ + 1);
-    for (int i = 0; i < cg_file_data_->controller_count_; i++, controller++) {
-        LOG(INFO) << "\t" << controller->name() << " ver " << controller->version() << " path "
-                  << controller->path();
+
+    auto controller_count = ACgroupFile_getControllerCount();
+    for (uint32_t i = 0; i < controller_count; ++i) {
+        const ACgroupController* controller = ACgroupFile_getController(i);
+        LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver "
+                  << ACgroupController_getVersion(controller) << " path "
+                  << ACgroupController_getPath(controller);
     }
 }
 
-bool CgroupMap::SetupCgroups() {
-    std::map<std::string, CgroupDescriptor> descriptors;
-
-    // load cgroups.json file
-    if (!ReadDescriptors(&descriptors)) {
-        LOG(ERROR) << "Failed to load cgroup description file";
-        return false;
-    }
-
-    // setup cgroups
-    for (const auto& [name, descriptor] : descriptors) {
-        if (!SetupCgroup(descriptor)) {
-            // issue a warning and proceed with the next cgroup
-            // TODO: mark the descriptor as invalid and skip it in WriteRcFile()
-            LOG(WARNING) << "Failed to setup " << name << " cgroup";
-        }
-    }
-
-    // mkdir <CGROUPS_RC_DIR> 0711 system system
-    if (!Mkdir(CGROUPS_RC_DIR, 0711, "system", "system")) {
-        LOG(ERROR) << "Failed to create directory for <CGROUPS_RC_FILE> file";
-        return false;
-    }
-
-    // Generate <CGROUPS_RC_FILE> file which can be directly mmapped into
-    // process memory. This optimizes performance, memory usage
-    // and limits infrormation shared with unprivileged processes
-    // to the minimum subset of information from cgroups.json
-    if (!WriteRcFile(descriptors)) {
-        LOG(ERROR) << "Failed to write " << CGROUPS_RC_FILE << " file";
-        return false;
-    }
-
-    std::string cgroup_rc_path = StringPrintf("%s/%s", CGROUPS_RC_DIR, CGROUPS_RC_FILE);
-    // chmod 0644 <cgroup_rc_path>
-    if (fchmodat(AT_FDCWD, cgroup_rc_path.c_str(), 0644, AT_SYMLINK_NOFOLLOW) < 0) {
-        PLOG(ERROR) << "fchmodat() failed";
-        return false;
-    }
-
-    return true;
-}
-
-const CgroupController* CgroupMap::FindController(const std::string& name) const {
-    if (!cg_file_data_) {
+CgroupController CgroupMap::FindController(const std::string& name) const {
+    if (!loaded_) {
         LOG(ERROR) << "CgroupMap::FindController called for [" << getpid()
                    << "] failed, RC file was not initialized properly";
-        return nullptr;
+        return CgroupController(nullptr);
     }
 
-    // skip the file header to get to the first controller
-    CgroupController* controller = (CgroupController*)(cg_file_data_ + 1);
-    for (int i = 0; i < cg_file_data_->controller_count_; i++, controller++) {
-        if (name == controller->name()) {
-            return controller;
+    auto controller_count = ACgroupFile_getControllerCount();
+    for (uint32_t i = 0; i < controller_count; ++i) {
+        const ACgroupController* controller = ACgroupFile_getController(i);
+        if (name == ACgroupController_getName(controller)) {
+            return CgroupController(controller);
         }
     }
 
-    return nullptr;
+    return CgroupController(nullptr);
 }
diff --git a/libprocessgroup/cgroup_map.h b/libprocessgroup/cgroup_map.h
index 1c355cd..d765e60 100644
--- a/libprocessgroup/cgroup_map.h
+++ b/libprocessgroup/cgroup_map.h
@@ -20,77 +20,43 @@
 #include <sys/types.h>
 
 #include <map>
+#include <memory>
 #include <mutex>
 #include <string>
+#include <vector>
 
-// Minimal controller description to be mmapped into process address space
+#include <android/cgrouprc.h>
+
+// Convenient wrapper of an ACgroupController pointer.
 class CgroupController {
   public:
-    CgroupController() {}
-    CgroupController(uint32_t version, const std::string& name, const std::string& path);
+    // Does not own controller
+    explicit CgroupController(const ACgroupController* controller) : controller_(controller) {}
 
-    uint32_t version() const { return version_; }
-    const char* name() const { return name_; }
-    const char* path() const { return path_; }
+    uint32_t version() const;
+    const char* name() const;
+    const char* path() const;
+
+    bool HasValue() const;
 
     std::string GetTasksFilePath(const std::string& path) const;
     std::string GetProcsFilePath(const std::string& path, uid_t uid, pid_t pid) const;
     bool GetTaskGroup(int tid, std::string* group) const;
-
   private:
-    static constexpr size_t CGROUP_NAME_BUF_SZ = 16;
-    static constexpr size_t CGROUP_PATH_BUF_SZ = 32;
-
-    uint32_t version_;
-    char name_[CGROUP_NAME_BUF_SZ];
-    char path_[CGROUP_PATH_BUF_SZ];
-};
-
-// Complete controller description for mounting cgroups
-class CgroupDescriptor {
-  public:
-    CgroupDescriptor(uint32_t version, const std::string& name, const std::string& path,
-                     mode_t mode, const std::string& uid, const std::string& gid);
-
-    const CgroupController* controller() const { return &controller_; }
-    mode_t mode() const { return mode_; }
-    std::string uid() const { return uid_; }
-    std::string gid() const { return gid_; }
-
-  private:
-    CgroupController controller_;
-    mode_t mode_;
-    std::string uid_;
-    std::string gid_;
-};
-
-struct CgroupFile {
-    static constexpr uint32_t FILE_VERSION_1 = 1;
-    static constexpr uint32_t FILE_CURR_VERSION = FILE_VERSION_1;
-
-    uint32_t version_;
-    uint32_t controller_count_;
-    CgroupController controllers_[];
+    const ACgroupController* controller_ = nullptr;
 };
 
 class CgroupMap {
   public:
-    static constexpr const char* CGROUPS_RC_FILE = "cgroup.rc";
-
     // Selinux policy ensures only init process can successfully use this function
     static bool SetupCgroups();
 
     static CgroupMap& GetInstance();
-
-    const CgroupController* FindController(const std::string& name) const;
+    CgroupController FindController(const std::string& name) const;
 
   private:
-    struct CgroupFile* cg_file_data_;
-    size_t cg_file_size_;
-
+    bool loaded_ = false;
     CgroupMap();
-    ~CgroupMap();
-
     bool LoadRcFile();
     void Print() const;
 };
diff --git a/libprocessgroup/cgrouprc/Android.bp b/libprocessgroup/cgrouprc/Android.bp
new file mode 100644
index 0000000..6848620
--- /dev/null
+++ b/libprocessgroup/cgrouprc/Android.bp
@@ -0,0 +1,61 @@
+// 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.
+
+cc_library {
+    name: "libcgrouprc",
+    host_supported: true,
+    recovery_available: true,
+    // Do not ever mark this as vendor_available; otherwise, vendor modules
+    // that links to the static library will behave unexpectedly. All on-device
+    // modules should use libprocessgroup which links to the LL-NDK library
+    // defined below. The static library is built for tests.
+    vendor_available: false,
+    srcs: [
+        "cgroup_controller.cpp",
+        "cgroup_file.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    header_libs: [
+        "libprocessgroup_headers",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+    static_libs: [
+        "libcgrouprc_format",
+    ],
+    stubs: {
+        symbol_file: "libcgrouprc.map.txt",
+        versions: ["29"],
+    },
+    target: {
+        linux: {
+            version_script: "libcgrouprc.map.txt",
+        },
+    },
+}
+
+llndk_library {
+    name: "libcgrouprc",
+    symbol_file: "libcgrouprc.map.txt",
+    export_include_dirs: [
+        "include",
+    ],
+}
diff --git a/libprocessgroup/cgrouprc/cgroup_controller.cpp b/libprocessgroup/cgrouprc/cgroup_controller.cpp
new file mode 100644
index 0000000..d064d31
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgroup_controller.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 <android-base/logging.h>
+#include <android/cgrouprc.h>
+
+#include "cgrouprc_internal.h"
+
+// All ACgroupController_* functions implicitly convert the pointer back
+// to the original CgroupController pointer before invoking the member functions.
+
+uint32_t ACgroupController_getVersion(const ACgroupController* controller) {
+    CHECK(controller != nullptr);
+    return controller->version();
+}
+
+const char* ACgroupController_getName(const ACgroupController* controller) {
+    CHECK(controller != nullptr);
+    return controller->name();
+}
+
+const char* ACgroupController_getPath(const ACgroupController* controller) {
+    CHECK(controller != nullptr);
+    return controller->path();
+}
diff --git a/libprocessgroup/cgrouprc/cgroup_file.cpp b/libprocessgroup/cgrouprc/cgroup_file.cpp
new file mode 100644
index 0000000..e26d841
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgroup_file.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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 <sys/mman.h>
+#include <sys/stat.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <android/cgrouprc.h>
+#include <processgroup/processgroup.h>
+
+#include "cgrouprc_internal.h"
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+using android::cgrouprc::format::CgroupController;
+using android::cgrouprc::format::CgroupFile;
+
+static CgroupFile* LoadRcFile() {
+    struct stat sb;
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) {
+        PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
+        return nullptr;
+    }
+
+    if (fstat(fd, &sb) < 0) {
+        PLOG(ERROR) << "fstat() failed for " << CGROUPS_RC_PATH;
+        return nullptr;
+    }
+
+    size_t file_size = sb.st_size;
+    if (file_size < sizeof(CgroupFile)) {
+        LOG(ERROR) << "Invalid file format " << CGROUPS_RC_PATH;
+        return nullptr;
+    }
+
+    CgroupFile* file_data = (CgroupFile*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
+    if (file_data == MAP_FAILED) {
+        PLOG(ERROR) << "Failed to mmap " << CGROUPS_RC_PATH;
+        return nullptr;
+    }
+
+    if (file_data->version_ != CgroupFile::FILE_CURR_VERSION) {
+        LOG(ERROR) << CGROUPS_RC_PATH << " file version mismatch";
+        munmap(file_data, file_size);
+        return nullptr;
+    }
+
+    auto expected = sizeof(CgroupFile) + file_data->controller_count_ * sizeof(CgroupController);
+    if (file_size != expected) {
+        LOG(ERROR) << CGROUPS_RC_PATH << " file has invalid size, expected " << expected
+                   << ", actual " << file_size;
+        munmap(file_data, file_size);
+        return nullptr;
+    }
+
+    return file_data;
+}
+
+static CgroupFile* GetInstance() {
+    // Deliberately leak this object (not munmap) to avoid a race between destruction on
+    // process exit and concurrent access from another thread.
+    static auto* file = LoadRcFile();
+    return file;
+}
+
+uint32_t ACgroupFile_getVersion() {
+    auto file = GetInstance();
+    if (file == nullptr) return 0;
+    return file->version_;
+}
+
+uint32_t ACgroupFile_getControllerCount() {
+    auto file = GetInstance();
+    if (file == nullptr) return 0;
+    return file->controller_count_;
+}
+
+const ACgroupController* ACgroupFile_getController(uint32_t index) {
+    auto file = GetInstance();
+    if (file == nullptr) return nullptr;
+    CHECK(index < file->controller_count_);
+    // Although the object is not actually an ACgroupController object, all ACgroupController_*
+    // functions implicitly convert ACgroupController* back to CgroupController* before invoking
+    // member functions.
+    return static_cast<ACgroupController*>(&file->controllers_[index]);
+}
diff --git a/libprocessgroup/cgrouprc/cgrouprc_internal.h b/libprocessgroup/cgrouprc/cgrouprc_internal.h
new file mode 100644
index 0000000..cd02f03
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgrouprc_internal.h
@@ -0,0 +1,24 @@
+/*
+ * 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 <android/cgrouprc.h>
+
+#include <processgroup/format/cgroup_controller.h>
+#include <processgroup/format/cgroup_file.h>
+
+struct ACgroupController : android::cgrouprc::format::CgroupController {};
diff --git a/libprocessgroup/cgrouprc/include/android/cgrouprc.h b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
new file mode 100644
index 0000000..4edd239
--- /dev/null
+++ b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
@@ -0,0 +1,84 @@
+/*
+ * 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 <stdint.h>
+
+__BEGIN_DECLS
+
+// For host builds, __INTRODUCED_IN is not defined.
+#ifndef __INTRODUCED_IN
+#define __INTRODUCED_IN(x)
+#endif
+
+struct ACgroupController;
+typedef struct ACgroupController ACgroupController;
+
+#if __ANDROID_API__ >= __ANDROID_API_Q__
+
+// ACgroupFile
+
+/**
+ * Returns file version. See android::cgrouprc::format::CgroupFile for a list of valid versions
+ * for the file.
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupFile_getVersion() __INTRODUCED_IN(29);
+
+/**
+ * Returns the number of controllers.
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupFile_getControllerCount() __INTRODUCED_IN(29);
+
+/**
+ * Returns the controller at the given index.
+ * Returnss nullptr if the given index exceeds getControllerCount().
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) const ACgroupController* ACgroupFile_getController(
+        uint32_t index) __INTRODUCED_IN(29);
+
+// ACgroupController
+
+/**
+ * Returns the version of the given controller.
+ * If the given controller is null, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupController_getVersion(const ACgroupController*)
+        __INTRODUCED_IN(29);
+
+/**
+ * Returns the name of the given controller.
+ * If the given controller is null, return nullptr.
+ */
+__attribute__((warn_unused_result)) const char* ACgroupController_getName(const ACgroupController*)
+        __INTRODUCED_IN(29);
+
+/**
+ * Returns the path of the given controller.
+ * If the given controller is null, return nullptr.
+ */
+__attribute__((warn_unused_result)) const char* ACgroupController_getPath(const ACgroupController*)
+        __INTRODUCED_IN(29);
+
+__END_DECLS
+
+#endif
diff --git a/libprocessgroup/cgrouprc/libcgrouprc.map.txt b/libprocessgroup/cgrouprc/libcgrouprc.map.txt
new file mode 100644
index 0000000..91df392
--- /dev/null
+++ b/libprocessgroup/cgrouprc/libcgrouprc.map.txt
@@ -0,0 +1,11 @@
+LIBCGROUPRC { # introduced=29
+  global:
+    ACgroupFile_getVersion;
+    ACgroupFile_getControllerCount;
+    ACgroupFile_getController;
+    ACgroupController_getVersion;
+    ACgroupController_getName;
+    ACgroupController_getPath;
+  local:
+    *;
+};
diff --git a/libprocessgroup/cgrouprc_format/Android.bp b/libprocessgroup/cgrouprc_format/Android.bp
new file mode 100644
index 0000000..dfbeed7
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/Android.bp
@@ -0,0 +1,32 @@
+// 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.
+
+cc_library_static {
+    name: "libcgrouprc_format",
+    host_supported: true,
+    recovery_available: true,
+    srcs: [
+        "cgroup_controller.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+}
diff --git a/libprocessgroup/cgrouprc_format/cgroup_controller.cpp b/libprocessgroup/cgrouprc_format/cgroup_controller.cpp
new file mode 100644
index 0000000..877eed8
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/cgroup_controller.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+CgroupController::CgroupController(uint32_t version, const std::string& name,
+                                   const std::string& path) {
+    // strlcpy isn't available on host. Although there is an implementation
+    // in licutils, libcutils itself depends on libcgrouprc_format, causing
+    // a circular dependency.
+    version_ = version;
+    strncpy(name_, name.c_str(), sizeof(name_) - 1);
+    name_[sizeof(name_) - 1] = '\0';
+    strncpy(path_, path.c_str(), sizeof(path_) - 1);
+    path_[sizeof(path_) - 1] = '\0';
+}
+
+uint32_t CgroupController::version() const {
+    return version_;
+}
+
+const char* CgroupController::name() const {
+    return name_;
+}
+
+const char* CgroupController::path() const {
+    return path_;
+}
+
+}  // namespace format
+}  // namespace cgrouprc
+}  // namespace android
diff --git a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h
new file mode 100644
index 0000000..64c7532
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h
@@ -0,0 +1,47 @@
+/*
+ * 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 <stdint.h>
+#include <string>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+// Minimal controller description to be mmapped into process address space
+struct CgroupController {
+  public:
+    CgroupController() {}
+    CgroupController(uint32_t version, const std::string& name, const std::string& path);
+
+    uint32_t version() const;
+    const char* name() const;
+    const char* path() const;
+
+  private:
+    static constexpr size_t CGROUP_NAME_BUF_SZ = 16;
+    static constexpr size_t CGROUP_PATH_BUF_SZ = 32;
+
+    uint32_t version_;
+    char name_[CGROUP_NAME_BUF_SZ];
+    char path_[CGROUP_PATH_BUF_SZ];
+};
+
+}  // namespace format
+}  // namespace cgrouprc
+}  // namespace android
diff --git a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h
new file mode 100644
index 0000000..f1678a1
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h
@@ -0,0 +1,36 @@
+/*
+ * 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 <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+struct CgroupFile {
+    uint32_t version_;
+    uint32_t controller_count_;
+    CgroupController controllers_[];
+
+    static constexpr uint32_t FILE_VERSION_1 = 1;
+    static constexpr uint32_t FILE_CURR_VERSION = FILE_VERSION_1;
+};
+
+}  // namespace format
+}  // namespace cgrouprc
+}  // namespace android
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 6f973b8..86e6035 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -24,9 +24,8 @@
 __BEGIN_DECLS
 
 static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
-static constexpr const char* CGROUPS_RC_DIR = "/dev/cgroup_info";
+static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc";
 
-bool CgroupSetupCgroups();
 bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path);
 bool CgroupGetAttributePath(const std::string& attr_name, std::string* path);
 bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path);
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 8884650..abe63dd 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -55,19 +55,15 @@
 
 #define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
 
-bool CgroupSetupCgroups() {
-    return CgroupMap::SetupCgroups();
-}
-
 bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path) {
-    const CgroupController* controller = CgroupMap::GetInstance().FindController(cgroup_name);
+    auto controller = CgroupMap::GetInstance().FindController(cgroup_name);
 
-    if (controller == nullptr) {
+    if (!controller.HasValue()) {
         return false;
     }
 
     if (path) {
-        *path = controller->path();
+        *path = controller.path();
     }
 
     return true;
@@ -111,7 +107,7 @@
 
 static bool isMemoryCgroupSupported() {
     std::string cgroup_name;
-    static bool memcg_supported = (CgroupMap::GetInstance().FindController("memory") != nullptr);
+    static bool memcg_supported = CgroupMap::GetInstance().FindController("memory").HasValue();
 
     return memcg_supported;
 }
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 1eefada..c7d0cca 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -152,21 +152,21 @@
 }
 
 bool cpusets_enabled() {
-    static bool enabled = (CgroupMap::GetInstance().FindController("cpuset") != nullptr);
+    static bool enabled = (CgroupMap::GetInstance().FindController("cpuset").HasValue());
     return enabled;
 }
 
 bool schedboost_enabled() {
-    static bool enabled = (CgroupMap::GetInstance().FindController("schedtune") != nullptr);
+    static bool enabled = (CgroupMap::GetInstance().FindController("schedtune").HasValue());
     return enabled;
 }
 
 static int getCGroupSubsys(int tid, const char* subsys, std::string& subgroup) {
-    const CgroupController* controller = CgroupMap::GetInstance().FindController(subsys);
+    auto controller = CgroupMap::GetInstance().FindController(subsys);
 
-    if (!controller) return -1;
+    if (!controller.HasValue()) return -1;
 
-    if (!controller->GetTaskGroup(tid, &subgroup)) {
+    if (!controller.GetTaskGroup(tid, &subgroup)) {
         LOG(ERROR) << "Failed to find cgroup for tid " << tid;
         return -1;
     }
diff --git a/libprocessgroup/setup/Android.bp b/libprocessgroup/setup/Android.bp
new file mode 100644
index 0000000..f6fc066
--- /dev/null
+++ b/libprocessgroup/setup/Android.bp
@@ -0,0 +1,44 @@
+//
+// 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.
+//
+
+cc_library_shared {
+    name: "libprocessgroup_setup",
+    recovery_available: true,
+    srcs: [
+        "cgroup_map_write.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcgrouprc",
+        "libjsoncpp",
+    ],
+    static_libs: [
+        "libcgrouprc_format",
+    ],
+    header_libs: [
+        "libprocessgroup_headers",
+    ],
+    export_header_lib_headers: [
+        "libprocessgroup_headers",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/libprocessgroup/setup/cgroup_descriptor.h b/libprocessgroup/setup/cgroup_descriptor.h
new file mode 100644
index 0000000..597060e
--- /dev/null
+++ b/libprocessgroup/setup/cgroup_descriptor.h
@@ -0,0 +1,43 @@
+/*
+ * 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 <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+
+// Complete controller description for mounting cgroups
+class CgroupDescriptor {
+  public:
+    CgroupDescriptor(uint32_t version, const std::string& name, const std::string& path,
+                     mode_t mode, const std::string& uid, const std::string& gid);
+
+    const format::CgroupController* controller() const { return &controller_; }
+    mode_t mode() const { return mode_; }
+    std::string uid() const { return uid_; }
+    std::string gid() const { return gid_; }
+
+  private:
+    format::CgroupController controller_;
+    mode_t mode_ = 0;
+    std::string uid_;
+    std::string gid_;
+};
+
+}  // namespace cgrouprc
+}  // namespace android
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
new file mode 100644
index 0000000..da60948
--- /dev/null
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -0,0 +1,329 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "libprocessgroup"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <regex>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <json/reader.h>
+#include <json/value.h>
+#include <processgroup/format/cgroup_file.h>
+#include <processgroup/processgroup.h>
+#include <processgroup/setup.h>
+
+#include "cgroup_descriptor.h"
+
+using android::base::GetBoolProperty;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+namespace android {
+namespace cgrouprc {
+
+static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json";
+static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json";
+
+static bool Mkdir(const std::string& path, mode_t mode, const std::string& uid,
+                  const std::string& gid) {
+    if (mode == 0) {
+        mode = 0755;
+    }
+
+    if (mkdir(path.c_str(), mode) != 0) {
+        /* chmod in case the directory already exists */
+        if (errno == EEXIST) {
+            if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
+                // /acct is a special case when the directory already exists
+                // TODO: check if file mode is already what we want instead of using EROFS
+                if (errno != EROFS) {
+                    PLOG(ERROR) << "fchmodat() failed for " << path;
+                    return false;
+                }
+            }
+        } else {
+            PLOG(ERROR) << "mkdir() failed for " << path;
+            return false;
+        }
+    }
+
+    if (uid.empty()) {
+        return true;
+    }
+
+    passwd* uid_pwd = getpwnam(uid.c_str());
+    if (!uid_pwd) {
+        PLOG(ERROR) << "Unable to decode UID for '" << uid << "'";
+        return false;
+    }
+
+    uid_t pw_uid = uid_pwd->pw_uid;
+    gid_t gr_gid = -1;
+    if (!gid.empty()) {
+        group* gid_pwd = getgrnam(gid.c_str());
+        if (!gid_pwd) {
+            PLOG(ERROR) << "Unable to decode GID for '" << gid << "'";
+            return false;
+        }
+        gr_gid = gid_pwd->gr_gid;
+    }
+
+    if (lchown(path.c_str(), pw_uid, gr_gid) < 0) {
+        PLOG(ERROR) << "lchown() failed for " << path;
+        return false;
+    }
+
+    /* chown may have cleared S_ISUID and S_ISGID, chmod again */
+    if (mode & (S_ISUID | S_ISGID)) {
+        if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
+            PLOG(ERROR) << "fchmodat() failed for " << path;
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool ReadDescriptorsFromFile(const std::string& file_name,
+                                    std::map<std::string, CgroupDescriptor>* descriptors) {
+    std::vector<CgroupDescriptor> result;
+    std::string json_doc;
+
+    if (!android::base::ReadFileToString(file_name, &json_doc)) {
+        PLOG(ERROR) << "Failed to read task profiles from " << file_name;
+        return false;
+    }
+
+    Json::Reader reader;
+    Json::Value root;
+    if (!reader.parse(json_doc, root)) {
+        LOG(ERROR) << "Failed to parse cgroups description: " << reader.getFormattedErrorMessages();
+        return false;
+    }
+
+    if (root.isMember("Cgroups")) {
+        const Json::Value& cgroups = root["Cgroups"];
+        for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
+            std::string name = cgroups[i]["Controller"].asString();
+            auto iter = descriptors->find(name);
+            if (iter == descriptors->end()) {
+                descriptors->emplace(
+                        name, CgroupDescriptor(
+                                      1, name, cgroups[i]["Path"].asString(),
+                                      std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
+                                      cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString()));
+            } else {
+                iter->second = CgroupDescriptor(
+                        1, name, cgroups[i]["Path"].asString(),
+                        std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
+                        cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString());
+            }
+        }
+    }
+
+    if (root.isMember("Cgroups2")) {
+        const Json::Value& cgroups2 = root["Cgroups2"];
+        auto iter = descriptors->find(CGROUPV2_CONTROLLER_NAME);
+        if (iter == descriptors->end()) {
+            descriptors->emplace(
+                    CGROUPV2_CONTROLLER_NAME,
+                    CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+                                     std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
+                                     cgroups2["UID"].asString(), cgroups2["GID"].asString()));
+        } else {
+            iter->second =
+                    CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+                                     std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
+                                     cgroups2["UID"].asString(), cgroups2["GID"].asString());
+        }
+    }
+
+    return true;
+}
+
+static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors) {
+    // load system cgroup descriptors
+    if (!ReadDescriptorsFromFile(CGROUPS_DESC_FILE, descriptors)) {
+        return false;
+    }
+
+    // load vendor cgroup descriptors if the file exists
+    if (!access(CGROUPS_DESC_VENDOR_FILE, F_OK) &&
+        !ReadDescriptorsFromFile(CGROUPS_DESC_VENDOR_FILE, descriptors)) {
+        return false;
+    }
+
+    return true;
+}
+
+// To avoid issues in sdk_mac build
+#if defined(__ANDROID__)
+
+static bool SetupCgroup(const CgroupDescriptor& descriptor) {
+    const format::CgroupController* controller = descriptor.controller();
+
+    // mkdir <path> [mode] [owner] [group]
+    if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
+        LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
+        return false;
+    }
+
+    int result;
+    if (controller->version() == 2) {
+        result = mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+                       nullptr);
+    } else {
+        // Unfortunately historically cpuset controller was mounted using a mount command
+        // different from all other controllers. This results in controller attributes not
+        // to be prepended with controller name. For example this way instead of
+        // /dev/cpuset/cpuset.cpus the attribute becomes /dev/cpuset/cpus which is what
+        // the system currently expects.
+        if (!strcmp(controller->name(), "cpuset")) {
+            // mount cpuset none /dev/cpuset nodev noexec nosuid
+            result = mount("none", controller->path(), controller->name(),
+                           MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr);
+        } else {
+            // mount cgroup none <path> nodev noexec nosuid <controller>
+            result = mount("none", controller->path(), "cgroup", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+                           controller->name());
+        }
+    }
+
+    if (result < 0) {
+        PLOG(ERROR) << "Failed to mount " << controller->name() << " cgroup";
+        return false;
+    }
+
+    return true;
+}
+
+#else
+
+// Stubs for non-Android targets.
+static bool SetupCgroup(const CgroupDescriptor&) {
+    return false;
+}
+
+#endif
+
+static bool WriteRcFile(const std::map<std::string, CgroupDescriptor>& descriptors) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
+                                         S_IRUSR | S_IRGRP | S_IROTH)));
+    if (fd < 0) {
+        PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
+        return false;
+    }
+
+    format::CgroupFile fl;
+    fl.version_ = format::CgroupFile::FILE_CURR_VERSION;
+    fl.controller_count_ = descriptors.size();
+    int ret = TEMP_FAILURE_RETRY(write(fd, &fl, sizeof(fl)));
+    if (ret < 0) {
+        PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH;
+        return false;
+    }
+
+    for (const auto& [name, descriptor] : descriptors) {
+        ret = TEMP_FAILURE_RETRY(
+                write(fd, descriptor.controller(), sizeof(format::CgroupController)));
+        if (ret < 0) {
+            PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH;
+            return false;
+        }
+    }
+
+    return true;
+}
+
+CgroupDescriptor::CgroupDescriptor(uint32_t version, const std::string& name,
+                                   const std::string& path, mode_t mode, const std::string& uid,
+                                   const std::string& gid)
+    : controller_(version, name, path), mode_(mode), uid_(uid), gid_(gid) {}
+
+}  // namespace cgrouprc
+}  // namespace android
+
+bool CgroupSetup() {
+    using namespace android::cgrouprc;
+
+    std::map<std::string, CgroupDescriptor> descriptors;
+
+    if (getpid() != 1) {
+        LOG(ERROR) << "Cgroup setup can be done only by init process";
+        return false;
+    }
+
+    // Make sure we do this only one time. No need for std::call_once because
+    // init is a single-threaded process
+    if (access(CGROUPS_RC_PATH, F_OK) == 0) {
+        LOG(WARNING) << "Attempt to call SetupCgroups more than once";
+        return true;
+    }
+
+    // load cgroups.json file
+    if (!ReadDescriptors(&descriptors)) {
+        LOG(ERROR) << "Failed to load cgroup description file";
+        return false;
+    }
+
+    // setup cgroups
+    for (const auto& [name, descriptor] : descriptors) {
+        if (!SetupCgroup(descriptor)) {
+            // issue a warning and proceed with the next cgroup
+            // TODO: mark the descriptor as invalid and skip it in WriteRcFile()
+            LOG(WARNING) << "Failed to setup " << name << " cgroup";
+        }
+    }
+
+    // mkdir <CGROUPS_RC_DIR> 0711 system system
+    if (!Mkdir(android::base::Dirname(CGROUPS_RC_PATH), 0711, "system", "system")) {
+        LOG(ERROR) << "Failed to create directory for " << CGROUPS_RC_PATH << " file";
+        return false;
+    }
+
+    // Generate <CGROUPS_RC_FILE> file which can be directly mmapped into
+    // process memory. This optimizes performance, memory usage
+    // and limits infrormation shared with unprivileged processes
+    // to the minimum subset of information from cgroups.json
+    if (!WriteRcFile(descriptors)) {
+        LOG(ERROR) << "Failed to write " << CGROUPS_RC_PATH << " file";
+        return false;
+    }
+
+    // chmod 0644 <CGROUPS_RC_PATH>
+    if (fchmodat(AT_FDCWD, CGROUPS_RC_PATH, 0644, AT_SYMLINK_NOFOLLOW) < 0) {
+        PLOG(ERROR) << "fchmodat() failed";
+        return false;
+    }
+
+    return true;
+}
diff --git a/libprocessgroup/setup/include/processgroup/setup.h b/libprocessgroup/setup/include/processgroup/setup.h
new file mode 100644
index 0000000..6ea1979
--- /dev/null
+++ b/libprocessgroup/setup/include/processgroup/setup.h
@@ -0,0 +1,19 @@
+/*
+ * 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
+
+bool CgroupSetup();
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 9362c03..4b45c87 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -46,7 +46,7 @@
 
 bool ProfileAttribute::GetPathForTask(int tid, std::string* path) const {
     std::string subgroup;
-    if (!controller_->GetTaskGroup(tid, &subgroup)) {
+    if (!controller()->GetTaskGroup(tid, &subgroup)) {
         return false;
     }
 
@@ -55,9 +55,10 @@
     }
 
     if (subgroup.empty()) {
-        *path = StringPrintf("%s/%s", controller_->path(), file_name_.c_str());
+        *path = StringPrintf("%s/%s", controller()->path(), file_name_.c_str());
     } else {
-        *path = StringPrintf("%s/%s/%s", controller_->path(), subgroup.c_str(), file_name_.c_str());
+        *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(),
+                             file_name_.c_str());
     }
     return true;
 }
@@ -135,7 +136,7 @@
     return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
 }
 
-SetCgroupAction::SetCgroupAction(const CgroupController* c, const std::string& p)
+SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
     : controller_(c), path_(p) {
 #ifdef CACHE_FILE_DESCRIPTORS
     // cache file descriptor only if path is app independent
@@ -145,7 +146,7 @@
         return;
     }
 
-    std::string tasks_path = c->GetTasksFilePath(p.c_str());
+    std::string tasks_path = c.GetTasksFilePath(p);
 
     if (access(tasks_path.c_str(), W_OK) != 0) {
         // file is not accessible
@@ -199,7 +200,7 @@
     }
 
     // this is app-dependent path, file descriptor is not cached
-    std::string procs_path = controller_->GetProcsFilePath(path_, uid, pid);
+    std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
         PLOG(WARNING) << "Failed to open " << procs_path;
@@ -212,7 +213,7 @@
 
     return true;
 #else
-    std::string procs_path = controller_->GetProcsFilePath(path_, uid, pid);
+    std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
         // no permissions to access the file, ignore
@@ -247,7 +248,7 @@
     LOG(ERROR) << "Application profile can't be applied to a thread";
     return false;
 #else
-    std::string tasks_path = controller_->GetTasksFilePath(path_);
+    std::string tasks_path = controller()->GetTasksFilePath(path_);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
         // no permissions to access the file, ignore
@@ -326,8 +327,8 @@
         std::string file_attr = attr[i]["File"].asString();
 
         if (attributes_.find(name) == attributes_.end()) {
-            const CgroupController* controller = cg_map.FindController(controller_name);
-            if (controller) {
+            auto controller = cg_map.FindController(controller_name);
+            if (controller.HasValue()) {
                 attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_attr);
             } else {
                 LOG(WARNING) << "Controller " << controller_name << " is not found";
@@ -355,8 +356,8 @@
                 std::string controller_name = params_val["Controller"].asString();
                 std::string path = params_val["Path"].asString();
 
-                const CgroupController* controller = cg_map.FindController(controller_name);
-                if (controller) {
+                auto controller = cg_map.FindController(controller_name);
+                if (controller.HasValue()) {
                     profile->Add(std::make_unique<SetCgroupAction>(controller, path));
                 } else {
                     LOG(WARNING) << "JoinCgroup: controller " << controller_name << " is not found";
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 9ee81c1..37cc305 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -27,16 +27,16 @@
 
 class ProfileAttribute {
   public:
-    ProfileAttribute(const CgroupController* controller, const std::string& file_name)
+    ProfileAttribute(const CgroupController& controller, const std::string& file_name)
         : controller_(controller), file_name_(file_name) {}
 
-    const CgroupController* controller() const { return controller_; }
+    const CgroupController* controller() const { return &controller_; }
     const std::string& file_name() const { return file_name_; }
 
     bool GetPathForTask(int tid, std::string* path) const;
 
   private:
-    const CgroupController* controller_;
+    CgroupController controller_;
     std::string file_name_;
 };
 
@@ -106,16 +106,16 @@
 // Set cgroup profile element
 class SetCgroupAction : public ProfileAction {
   public:
-    SetCgroupAction(const CgroupController* c, const std::string& p);
+    SetCgroupAction(const CgroupController& c, const std::string& p);
 
     virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     virtual bool ExecuteForTask(int tid) const;
 
-    const CgroupController* controller() const { return controller_; }
+    const CgroupController* controller() const { return &controller_; }
     std::string path() const { return path_; }
 
   private:
-    const CgroupController* controller_;
+    CgroupController controller_;
     std::string path_;
 #ifdef CACHE_FILE_DESCRIPTORS
     android::base::unique_fd fd_;
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
index b5bc5af..845a197 100644
--- a/libstats/include/stats_event_list.h
+++ b/libstats/include/stats_event_list.h
@@ -18,15 +18,18 @@
 #define ANDROID_STATS_LOG_STATS_EVENT_LIST_H
 
 #include <log/log_event_list.h>
+#include <sys/uio.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 void reset_log_context(android_log_context ctx);
 int write_to_logger(android_log_context context, log_id_t id);
-void note_log_drop(int error);
+void note_log_drop(int error, int atom_tag);
 void stats_log_close();
 int android_log_write_char_array(android_log_context ctx, const char* value, size_t len);
+extern int (*write_to_statsd)(struct iovec* vec, size_t nr);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
index 5b90361..ae12cbe 100644
--- a/libstats/stats_event_list.c
+++ b/libstats/stats_event_list.c
@@ -41,7 +41,7 @@
 extern struct android_log_transport_write statsdLoggerWrite;
 
 static int __write_to_statsd_init(struct iovec* vec, size_t nr);
-static int (*write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
+int (*write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
 
 // Similar to create_android_logger(), but instead of allocation a new buffer,
 // this function resets the buffer for resuse.
@@ -120,8 +120,8 @@
     return retValue;
 }
 
-void note_log_drop(int error) {
-    statsdLoggerWrite.noteDrop(error);
+void note_log_drop(int error, int tag) {
+    statsdLoggerWrite.noteDrop(error, tag);
 }
 
 void stats_log_close() {
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index f5be95c..b778f92 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -47,9 +47,18 @@
 #endif
 #endif
 
+#ifndef htole64
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htole64(x) (x)
+#else
+#define htole64(x) __bswap_64(x)
+#endif
+#endif
+
 static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
 static atomic_int dropped = 0;
 static atomic_int log_error = 0;
+static atomic_int atom_tag = 0;
 
 void statsd_writer_init_lock() {
     /*
@@ -152,9 +161,10 @@
     return 1;
 }
 
-static void statsdNoteDrop(int error) {
+static void statsdNoteDrop(int error, int tag) {
     atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
     atomic_exchange_explicit(&log_error, error, memory_order_relaxed);
+    atomic_exchange_explicit(&atom_tag, tag, memory_order_relaxed);
 }
 
 static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
@@ -203,12 +213,17 @@
     if (sock >= 0) {
         int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
         if (snapshot) {
-            android_log_event_int_t buffer;
+            android_log_event_long_t buffer;
             header.id = LOG_ID_STATS;
             // store the last log error in the tag field. This tag field is not used by statsd.
             buffer.header.tag = htole32(atomic_load(&log_error));
-            buffer.payload.type = EVENT_TYPE_INT;
-            buffer.payload.data = htole32(snapshot);
+            buffer.payload.type = EVENT_TYPE_LONG;
+            // format:
+            // |atom_tag|dropped_count|
+            int64_t composed_long = atomic_load(&atom_tag);
+            // Send 2 int32's via an int64.
+            composed_long = ((composed_long << 32) | ((int64_t)snapshot));
+            buffer.payload.data = htole64(composed_long);
 
             newVec[headerLength].iov_base = &buffer;
             newVec[headerLength].iov_len = sizeof(buffer);
diff --git a/libstats/statsd_writer.h b/libstats/statsd_writer.h
index 4fc3f8b..fe2d37c 100644
--- a/libstats/statsd_writer.h
+++ b/libstats/statsd_writer.h
@@ -39,7 +39,7 @@
     /* write log to transport, returns number of bytes propagated, or -errno */
     int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
     /* note one log drop */
-    void (*noteDrop)(int error);
+    void (*noteDrop)(int error, int tag);
 };
 
 #endif  // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libunwindstack/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
index 668527a..802beca 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -61,6 +61,14 @@
   table_encoding_ = data[3];
   table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
 
+  // If we can't perform a binary search on the entries, it's not worth
+  // using this object. The calling code will fall back to the DwarfEhFrame
+  // object in this case.
+  if (table_entry_size_ == 0) {
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+
   memory_.set_pc_offset(memory_.cur_offset());
   if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
     last_error_.code = DWARF_ERROR_MEMORY_INVALID;
@@ -137,13 +145,13 @@
 }
 
 template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
-                                                          uint64_t total_entries) {
-  CHECK(fde_count_ > 0);
-  CHECK(total_entries <= fde_count_);
+bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+  if (fde_count_ == 0) {
+    return false;
+  }
 
   size_t first = 0;
-  size_t last = total_entries;
+  size_t last = fde_count_;
   while (first < last) {
     size_t current = (first + last) / 2;
     const FdeInfo* info = GetFdeInfoFromIndex(current);
@@ -172,87 +180,6 @@
 }
 
 template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
-  CHECK(fde_count_ != 0);
-  last_error_.code = DWARF_ERROR_NONE;
-  last_error_.address = 0;
-
-  // We can do a binary search if the pc is in the range of the elements
-  // that have already been cached.
-  if (!fde_info_.empty()) {
-    const FdeInfo* info = &fde_info_[fde_info_.size() - 1];
-    if (pc >= info->pc) {
-      *fde_offset = info->offset;
-      return true;
-    }
-    if (pc < info->pc) {
-      return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
-    }
-  }
-
-  if (cur_entries_offset_ == 0) {
-    // All entries read, or error encountered.
-    return false;
-  }
-
-  memory_.set_data_offset(entries_data_offset_);
-  memory_.set_cur_offset(cur_entries_offset_);
-  memory_.set_pc_offset(0);
-  cur_entries_offset_ = 0;
-
-  FdeInfo* prev_info = nullptr;
-  for (size_t current = fde_info_.size();
-       current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
-    FdeInfo* info = &fde_info_[current];
-    uint64_t value;
-    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
-        !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
-      fde_info_.erase(current);
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-
-    // Relative encodings require adding in the load bias.
-    if (IsEncodingRelative(table_encoding_)) {
-      value += load_bias_;
-    }
-    info->pc = value;
-
-    if (pc < info->pc) {
-      if (prev_info == nullptr) {
-        return false;
-      }
-      cur_entries_offset_ = memory_.cur_offset();
-      *fde_offset = prev_info->offset;
-      return true;
-    }
-    prev_info = info;
-  }
-
-  if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) {
-    *fde_offset = prev_info->offset;
-    return true;
-  }
-  return false;
-}
-
-template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
-  if (fde_count_ == 0) {
-    return false;
-  }
-
-  if (table_entry_size_ > 0) {
-    // Do a binary search since the size of each table entry is fixed.
-    return GetFdeOffsetBinary(pc, fde_offset, fde_count_);
-  } else {
-    // Do a sequential search since each table entry size is variable.
-    return GetFdeOffsetSequential(pc, fde_offset);
-  }
-}
-
-template <typename AddressType>
 void DwarfEhFrameWithHdr<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
   for (size_t i = 0; i < fde_count_; i++) {
     const FdeInfo* info = GetFdeInfoFromIndex(i);
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
index e3e9ca8..0e5eef7 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.h
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -69,10 +69,6 @@
 
   const FdeInfo* GetFdeInfoFromIndex(size_t index);
 
-  bool GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset);
-
-  bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries);
-
   void GetFdes(std::vector<const DwarfFde*>* fdes) override;
 
  protected:
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 57a780e..849a31a 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -138,7 +138,7 @@
     return false;
   }
 
-  if (cie->version != 1 && cie->version != 3 && cie->version != 4) {
+  if (cie->version != 1 && cie->version != 3 && cie->version != 4 && cie->version != 5) {
     // Unrecognized version.
     last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
     return false;
@@ -155,7 +155,7 @@
     cie->augmentation_string.push_back(aug_value);
   } while (aug_value != '\0');
 
-  if (cie->version == 4) {
+  if (cie->version == 4 || cie->version == 5) {
     // Skip the Address Size field since we only use it for validation.
     memory_.set_cur_offset(memory_.cur_offset() + 1);
 
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 12efb94..dee8eb3 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -276,7 +276,7 @@
         if (gnu_build_id_size_ - offset < hdr.n_descsz || hdr.n_descsz == 0) {
           return "";
         }
-        std::string build_id(hdr.n_descsz - 1, '\0');
+        std::string build_id(hdr.n_descsz, '\0');
         if (memory_->ReadFully(gnu_build_id_offset_ + offset, &build_id[0], hdr.n_descsz)) {
           return build_id;
         }
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index d620934..120bd73 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -550,6 +550,22 @@
   VerifyCieVersion(cie, 4, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
 }
 
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version5) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 0, 10, 4, 8, 0x81, 3});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 5, 10, DW_EH_PE_sdata4, 0x181, 0x10, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version5) {
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 0, 10, 4, 8, 0x81, 3});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 5, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
+}
+
 TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset_version_invalid) {
   SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{0, '\0', 1, 2, 3, 4, 5, 6, 7});
   ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x5000) == nullptr);
@@ -558,10 +574,10 @@
   ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x6000) == nullptr);
   EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
 
-  SetCie32(&this->memory_, 0x7000, 0x100, std::vector<uint8_t>{5, '\0', 1, 2, 3, 4, 5, 6, 7});
+  SetCie32(&this->memory_, 0x7000, 0x100, std::vector<uint8_t>{6, '\0', 1, 2, 3, 4, 5, 6, 7});
   ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x7000) == nullptr);
   EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
-  SetCie64(&this->memory_, 0x8000, 0x100, std::vector<uint8_t>{5, '\0', 1, 2, 3, 4, 5, 6, 7});
+  SetCie64(&this->memory_, 0x8000, 0x100, std::vector<uint8_t>{6, '\0', 1, 2, 3, 4, 5, 6, 7});
   ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x8000) == nullptr);
   EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
 }
@@ -803,9 +819,10 @@
     GetFdeFromPc64_not_in_section, GetCieFde32, GetCieFde64, GetCieFromOffset32_cie_cached,
     GetCieFromOffset64_cie_cached, GetCieFromOffset32_version1, GetCieFromOffset64_version1,
     GetCieFromOffset32_version3, GetCieFromOffset64_version3, GetCieFromOffset32_version4,
-    GetCieFromOffset64_version4, GetCieFromOffset_version_invalid, GetCieFromOffset32_augment,
-    GetCieFromOffset64_augment, GetFdeFromOffset32_augment, GetFdeFromOffset64_augment,
-    GetFdeFromOffset32_lsda_address, GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved);
+    GetCieFromOffset64_version4, GetCieFromOffset32_version5, GetCieFromOffset64_version5,
+    GetCieFromOffset_version_invalid, GetCieFromOffset32_augment, GetCieFromOffset64_augment,
+    GetFdeFromOffset32_augment, GetFdeFromOffset64_augment, GetFdeFromOffset32_lsda_address,
+    GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 910ae36..be9e721 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -95,6 +95,13 @@
   EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
   EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
 
+  // Verify a zero table entry size fails to init.
+  this->memory_.SetData8(0x1003, 0x1);
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
+  // Reset the value back to the original.
+  this->memory_.SetData8(0x1003, DW_EH_PE_sdata4);
+
   // Verify a zero fde count fails to init.
   this->memory_.SetData32(0x1006, 0);
   ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
@@ -276,9 +283,8 @@
   EXPECT_EQ(0x500U, info->offset);
 }
 
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetBinary_verify) {
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_verify) {
   this->eh_frame_->TestSetTableEntrySize(0x10);
-  this->eh_frame_->TestSetFdeCount(10);
 
   typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
   for (size_t i = 0; i < 10; i++) {
@@ -288,105 +294,42 @@
   }
 
   uint64_t fde_offset;
-  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10));
+  this->eh_frame_->TestSetFdeCount(10);
+  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
   // Not an error, just not found.
   ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
   // Even number of elements.
   for (size_t i = 0; i < 10; i++) {
+    SCOPED_TRACE(testing::Message() << "Failed at index " << i);
     TypeParam pc = 0x1000 * (i + 1);
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 10)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 10))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 10))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset));
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset));
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset));
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
   }
+
   // Odd number of elements.
+  this->eh_frame_->TestSetFdeCount(9);
   for (size_t i = 0; i < 9; i++) {
+    SCOPED_TRACE(testing::Message() << "Failed at index " << i);
     TypeParam pc = 0x1000 * (i + 1);
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 9)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 9))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 9))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset));
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset));
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset));
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
   }
 }
 
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetBinary_index_fail) {
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_index_fail) {
   this->eh_frame_->TestSetTableEntrySize(0x10);
   this->eh_frame_->TestSetFdeCount(10);
 
   uint64_t fde_offset;
-  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x1000, &fde_offset, 10));
-}
-
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetSequential) {
-  this->eh_frame_->TestSetFdeCount(10);
-  this->eh_frame_->TestSetEntriesDataOffset(0x100);
-  this->eh_frame_->TestSetEntriesEnd(0x2000);
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
-
-  this->memory_.SetData32(0x1048, 0x440);
-  this->memory_.SetData32(0x104c, 0x600);
-
-  // Verify that if entries is zero, that it fails.
-  uint64_t fde_offset;
-  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
-  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
-
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
-  EXPECT_EQ(0x500U, fde_offset);
-
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
-  EXPECT_EQ(0x600U, fde_offset);
-
-  // Expect that the data is cached so no more memory reads will occur.
-  this->memory_.Clear();
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
-  EXPECT_EQ(0x600U, fde_offset);
-}
-
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetSequential_last_element) {
-  this->eh_frame_->TestSetFdeCount(2);
-  this->eh_frame_->TestSetEntriesDataOffset(0x100);
-  this->eh_frame_->TestSetEntriesEnd(0x2000);
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
-
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
-
-  this->memory_.SetData32(0x1048, 0x440);
-  this->memory_.SetData32(0x104c, 0x600);
-
-  uint64_t fde_offset;
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
-  EXPECT_EQ(0x600U, fde_offset);
-}
-
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetSequential_end_check) {
-  this->eh_frame_->TestSetFdeCount(2);
-  this->eh_frame_->TestSetEntriesDataOffset(0x100);
-  this->eh_frame_->TestSetEntriesEnd(0x1048);
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
-
-  this->memory_.SetData32(0x1048, 0x440);
-  this->memory_.SetData32(0x104c, 0x600);
-
-  uint64_t fde_offset;
-  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
+  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
 }
 
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_fail_fde_count) {
@@ -397,7 +340,7 @@
   ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
 }
 
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_binary_search) {
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_search) {
   this->eh_frame_->TestSetTableEntrySize(16);
   this->eh_frame_->TestSetFdeCount(10);
 
@@ -417,26 +360,6 @@
   EXPECT_EQ(0x10700U, fde_offset);
 }
 
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_sequential_search) {
-  this->eh_frame_->TestSetFdeCount(10);
-  this->eh_frame_->TestSetTableEntrySize(0);
-
-  typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
-  info.pc = 0x50;
-  info.offset = 0x10000;
-  this->eh_frame_->TestSetFdeInfo(0, info);
-  info.pc = 0x150;
-  info.offset = 0x10100;
-  this->eh_frame_->TestSetFdeInfo(1, info);
-  info.pc = 0x250;
-  info.offset = 0x10200;
-  this->eh_frame_->TestSetFdeInfo(2, info);
-
-  uint64_t fde_offset;
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x200, &fde_offset));
-  EXPECT_EQ(0x10100U, fde_offset);
-}
-
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetCieFde32) {
   // CIE 32 information.
   this->memory_.SetData32(0xf000, 0x100);
@@ -526,10 +449,8 @@
 REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias, GetFdes,
                            GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
                            GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
-                           GetFdeOffsetBinary_verify, GetFdeOffsetBinary_index_fail,
-                           GetFdeOffsetSequential, GetFdeOffsetSequential_last_element,
-                           GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count,
-                           GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search,
+                           GetFdeOffsetFromPc_verify, GetFdeOffsetFromPc_index_fail,
+                           GetFdeOffsetFromPc_fail_fde_count, GetFdeOffsetFromPc_search,
                            GetCieFde32, GetCieFde64, GetFdeFromPc_fde_not_found);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameWithHdrTestTypes;
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index d895863..cdc927a 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -1192,14 +1192,16 @@
   char note_section[128];
   Nhdr note_header = {};
   note_header.n_namesz = 4;  // "GNU"
-  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_descsz = 7;  // "BUILDID"
   note_header.n_type = NT_GNU_BUILD_ID;
   memcpy(&note_section, &note_header, sizeof(note_header));
   size_t note_offset = sizeof(note_header);
+  // The note information contains the GNU and trailing '\0'.
   memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
   note_offset += sizeof("GNU");
-  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
-  note_offset += sizeof("BUILDID");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
 
   Shdr shdr = {};
   shdr.sh_type = SHT_NOTE;
@@ -1244,24 +1246,27 @@
   char note_section[128];
   Nhdr note_header = {};
   note_header.n_namesz = 8;  // "WRONG" aligned to 4
-  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_descsz = 7;  // "BUILDID"
   note_header.n_type = NT_GNU_BUILD_ID;
   memcpy(&note_section, &note_header, sizeof(note_header));
   size_t note_offset = sizeof(note_header);
   memcpy(&note_section[note_offset], "WRONG", sizeof("WRONG"));
   note_offset += 8;
-  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
-  note_offset += sizeof("BUILDID");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
 
   note_header.n_namesz = 4;  // "GNU"
-  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_descsz = 7;  // "BUILDID"
   note_header.n_type = NT_GNU_BUILD_ID;
   memcpy(&note_section[note_offset], &note_header, sizeof(note_header));
   note_offset += sizeof(note_header);
+  // The note information contains the GNU and trailing '\0'.
   memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
   note_offset += sizeof("GNU");
-  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
-  note_offset += sizeof("BUILDID");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
 
   Shdr shdr = {};
   shdr.sh_type = SHT_NOTE;
@@ -1306,14 +1311,16 @@
   char note_section[128];
   Nhdr note_header = {};
   note_header.n_namesz = 4;  // "GNU"
-  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_descsz = 7;  // "BUILDID"
   note_header.n_type = NT_GNU_BUILD_ID;
   memcpy(&note_section, &note_header, sizeof(note_header));
   size_t note_offset = sizeof(note_header);
+  // The note information contains the GNU and trailing '\0'.
   memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
   note_offset += sizeof("GNU");
-  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
-  note_offset += sizeof("BUILDID");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
 
   Shdr shdr = {};
   shdr.sh_type = SHT_NOTE;
@@ -1358,14 +1365,16 @@
   char note_section[128];
   Nhdr note_header = {};
   note_header.n_namesz = 4;  // "GNU"
-  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_descsz = 7;  // "BUILDID"
   note_header.n_type = NT_GNU_BUILD_ID;
   memcpy(&note_section, &note_header, sizeof(note_header));
   size_t note_offset = sizeof(note_header);
+  // The note information contains the GNU and trailing '\0'.
   memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
   note_offset += sizeof("GNU");
-  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
-  note_offset += sizeof("BUILDID");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
 
   Shdr shdr = {};
   shdr.sh_type = SHT_NOTE;
@@ -1410,14 +1419,16 @@
   char note_section[128];
   Nhdr note_header = {};
   note_header.n_namesz = 4;  // "GNU"
-  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_descsz = 7;  // "BUILDID"
   note_header.n_type = NT_GNU_BUILD_ID;
   memcpy(&note_section, &note_header, sizeof(note_header));
   size_t note_offset = sizeof(note_header);
+  // The note information contains the GNU and trailing '\0'.
   memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
   note_offset += sizeof("GNU");
-  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
-  note_offset += sizeof("BUILDID");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
 
   Shdr shdr = {};
   shdr.sh_type = SHT_NOTE;
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 655579e..02ba9c8 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -204,6 +204,7 @@
 TEST_F(UnwindOfflineTest, pc_straddle_arm) {
   ASSERT_NO_FATAL_FAILURE(Init("straddle_arm/", ARCH_ARM));
 
+  std::unique_ptr<Regs> regs_copy(regs_->Clone());
   Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
 
@@ -223,6 +224,22 @@
   EXPECT_EQ(0xe9c86730U, unwinder.frames()[2].sp);
   EXPECT_EQ(0xf3367147U, unwinder.frames()[3].pc);
   EXPECT_EQ(0xe9c86778U, unwinder.frames()[3].sp);
+
+  // Display build ids now.
+  unwinder.SetRegs(regs_copy.get());
+  unwinder.SetDisplayBuildID(true);
+  unwinder.Unwind();
+
+  frame_info = DumpFrames(unwinder);
+  ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0001a9f8  libc.so (abort+64) (BuildId: 2dd0d4ba881322a0edabeed94808048c)\n"
+      "  #01 pc 00006a1b  libbase.so (android::base::DefaultAborter(char const*)+6) (BuildId: "
+      "ed43842c239cac1a618e600ea91c4cbd)\n"
+      "  #02 pc 00007441  libbase.so (android::base::LogMessage::~LogMessage()+748) (BuildId: "
+      "ed43842c239cac1a618e600ea91c4cbd)\n"
+      "  #03 pc 00015147  /does/not/exist/libhidlbase.so\n",
+      frame_info);
 }
 
 TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
diff --git a/libutils/Android.bp b/libutils/Android.bp
index c67ff8f..4f194c7 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -69,6 +69,7 @@
     ],
 
     shared_libs: [
+        "libcutils",
         "liblog",
     ],
 
@@ -83,7 +84,6 @@
             cflags: ["-fvisibility=protected"],
 
             shared_libs: [
-                "libcutils",
                 "libprocessgroup",
                 "libdl",
                 "libvndksupport",
@@ -98,14 +98,6 @@
             exclude_shared_libs: ["libvndksupport"],
         },
 
-        host: {
-            cflags: ["-DLIBUTILS_NATIVE=1"],
-
-            shared: {
-                enabled: false,
-            },
-        },
-
         linux_bionic: {
             enabled: true,
         },
@@ -180,18 +172,23 @@
         },
     },
 
+    shared_libs: [
+         "libutils",
+         "libbacktrace",
+    ],
+
     target: {
-        android: {
-            shared_libs: [
-                "libutils",
-                "libbacktrace",
-            ],
-        },
         linux: {
             srcs: [
                 "ProcessCallStack.cpp",
             ],
         },
+        darwin: {
+            enabled: false,
+        },
+        windows: {
+            enabled: false,
+        },
     },
 }
 
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index b3f943d..2d696eb 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -76,8 +76,8 @@
 }
 
 void Looper::initTLSKey() {
-    int result = pthread_key_create(& gTLSKey, threadDestructor);
-    LOG_ALWAYS_FATAL_IF(result != 0, "Could not allocate TLS key.");
+    int error = pthread_key_create(&gTLSKey, threadDestructor);
+    LOG_ALWAYS_FATAL_IF(error != 0, "Could not allocate TLS key: %s", strerror(error));
 }
 
 void Looper::threadDestructor(void *st) {
@@ -399,8 +399,8 @@
     ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
     if (nWrite != sizeof(uint64_t)) {
         if (errno != EAGAIN) {
-            LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s", mWakeEventFd.get(),
-                             strerror(errno));
+            LOG_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",
+                             mWakeEventFd.get(), nWrite, strerror(errno));
         }
     }
 }
diff --git a/libutils/Mutex_test.cpp b/libutils/Mutex_test.cpp
index 8a1805f..79f4302 100644
--- a/libutils/Mutex_test.cpp
+++ b/libutils/Mutex_test.cpp
@@ -29,4 +29,20 @@
     android::Mutex::Autolock _l(mLock);
     i = 0;
     modifyLockedVariable();
-}
\ No newline at end of file
+}
+
+TEST(Mutex, tryLock) {
+    if (mLock.tryLock() != 0) {
+        return;
+    }
+    mLock.unlock();
+}
+
+#if defined(__ANDROID__)
+TEST(Mutex, timedLock) {
+    if (mLock.timedLock(1) != 0) {
+        return;
+    }
+    mLock.unlock();
+}
+#endif
diff --git a/libutils/include/utils/Mutex.h b/libutils/include/utils/Mutex.h
index 29c2e8c..1325bf3 100644
--- a/libutils/include/utils/Mutex.h
+++ b/libutils/include/utils/Mutex.h
@@ -108,7 +108,7 @@
     void unlock() RELEASE();
 
     // lock if possible; returns 0 on success, error otherwise
-    status_t tryLock() TRY_ACQUIRE(true);
+    status_t tryLock() TRY_ACQUIRE(0);
 
 #if defined(__ANDROID__)
     // Lock the mutex, but don't wait longer than timeoutNs (relative time).
@@ -122,7 +122,7 @@
     // which is subject to NTP adjustments, and includes time during suspend,
     // so a timeout may occur even though no processes could run.
     // Not holding a partial wakelock may lead to a system suspend.
-    status_t timedLock(nsecs_t timeoutNs) TRY_ACQUIRE(true);
+    status_t timedLock(nsecs_t timeoutNs) TRY_ACQUIRE(0);
 #endif
 
     // Manages the mutex automatically. It'll be locked when Autolock is
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index bfa2508..546c15c 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -9,7 +9,10 @@
     ],
     local_include_dirs: ["include/vndksupport"],
     export_include_dirs: ["include"],
-    shared_libs: ["liblog"],
+    shared_libs: [
+        "libdl_android",
+        "liblog",
+    ],
     version_script: "libvndksupport.map.txt",
     stubs: {
         symbol_file: "libvndksupport.map.txt",
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 9538bba..bc1543b 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -38,6 +38,19 @@
             ],
         },
     },
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+            "shift",
+            "integer-divide-by-zero",
+            "implicit-signed-integer-truncation",
+            // TODO: Fix crash when we enable this option
+            // "implicit-unsigned-integer-truncation",
+            // TODO: not tested yet.
+            // "implicit-integer-sign-change",
+        ],
+    },
 }
 
 cc_defaults {
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 0710d0a..96dbba1 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -102,21 +102,9 @@
 }
 
 static uint32_t ComputeHash(const ZipString& name) {
-#if !defined(_WIN32)
   return std::hash<std::string_view>{}(
-      std::string_view(reinterpret_cast<const char*>(name.name), name.name_length));
-#else
-  // Remove this code path once the windows compiler knows how to compile the above statement.
-  uint32_t hash = 0;
-  uint16_t len = name.name_length;
-  const uint8_t* str = name.name;
-
-  while (len--) {
-    hash = hash * 31 + *str++;
-  }
-
-  return hash;
-#endif
+             std::string_view(reinterpret_cast<const char*>(name.name), name.name_length)) &
+         UINT32_MAX;
 }
 
 static bool isZipStringEqual(const uint8_t* start, const ZipString& zip_string,
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 981df3a..0df0fa5 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -30,9 +30,13 @@
 #include "entry_name_utils-inl.h"
 #include "zip_archive_common.h"
 
-#if !defined(powerof2)
-#define powerof2(x) ((((x)-1) & (x)) == 0)
-#endif
+#undef powerof2
+#define powerof2(x)                                               \
+  ({                                                              \
+    __typeof__(x) _x = (x);                                       \
+    __typeof__(x) _x2;                                            \
+    __builtin_add_overflow(_x, -1, &_x2) ? 1 : ((_x2 & _x) == 0); \
+  })
 
 /* Zip compression methods we support */
 enum {
diff --git a/logcat/event.logtags b/logcat/event.logtags
index da8d2d4..3a1d36f 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -116,8 +116,8 @@
 # audio
 # 61000 - 61199 reserved for audioserver
 
-# 0 for screen off, 1 for screen on, 2 for key-guard done
-70000 screen_toggled (screen_state|1|5)
+# com.android.server.policy
+# 70000 - 70199 reserved for PhoneWindowManager and other policies
 
 # aggregation service
 70200 aggregation (aggregation time|2|3)
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 54b019e..462ae8b 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -8,6 +8,7 @@
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+LOCAL_REQUIRED_MODULES := fsverity_init
 
 # The init symlink must be a post install command of a file that is to TARGET_ROOT_OUT.
 # Since init.rc is required for init and satisfies that requirement, we hijack it to create the symlink.
@@ -57,6 +58,15 @@
 endif
 
 #######################################
+# fsverity_init
+
+include $(CLEAR_VARS)
+LOCAL_MODULE:= fsverity_init
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_SRC_FILES := fsverity_init.sh
+include $(BUILD_PREBUILT)
+
+#######################################
 # init.environ.rc
 
 include $(CLEAR_VARS)
@@ -79,7 +89,7 @@
 
 EXPORT_GLOBAL_GCOV_OPTIONS :=
 ifeq ($(NATIVE_COVERAGE),true)
-  EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/gcov
+  EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/trace
 endif
 
 # Put it here instead of in init.rc module definition,
@@ -366,3 +376,5 @@
 	$(hide) echo -n > $@
 	$(hide) $(foreach lib,$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES), \
 		echo $(lib).so >> $@;)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
new file mode 100644
index 0000000..5dc019c
--- /dev/null
+++ b/rootdir/avb/Android.mk
@@ -0,0 +1,46 @@
+LOCAL_PATH:= $(call my-dir)
+
+#######################################
+# q-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := q-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
+
+#######################################
+# r-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := r-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
+
+#######################################
+# s-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := s-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
diff --git a/rootdir/avb/q-gsi.avbpubkey b/rootdir/avb/q-gsi.avbpubkey
new file mode 100644
index 0000000..5ed7543
--- /dev/null
+++ b/rootdir/avb/q-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/avb/r-gsi.avbpubkey b/rootdir/avb/r-gsi.avbpubkey
new file mode 100644
index 0000000..2609b30
--- /dev/null
+++ b/rootdir/avb/r-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/avb/s-gsi.avbpubkey b/rootdir/avb/s-gsi.avbpubkey
new file mode 100644
index 0000000..9065fb8
--- /dev/null
+++ b/rootdir/avb/s-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index 9cf93ca..7324ba9 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -11,14 +11,13 @@
 dir.legacy = /odm
 dir.legacy = /sbin
 
-dir.legacy = /data/nativetest
-dir.legacy = /data/nativetest64
-dir.legacy = /data/benchmarktest
-dir.legacy = /data/benchmarktest64
-
 # Except for /postinstall, where only /system and /product are searched
 dir.postinstall = /postinstall
 
+# Fallback entry to provide APEX namespace lookups for binaries anywhere else.
+# This must be last.
+dir.legacy = /data
+
 [legacy]
 namespace.default.isolated = false
 
@@ -54,8 +53,7 @@
 # Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
 # libart.
 namespace.default.visible = true
-namespace.default.link.runtime.shared_libs  = libart.so:libartd.so
-namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs  = libdexfile_external.so
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
@@ -102,6 +100,7 @@
 namespace.media.link.default.shared_libs  = libandroid.so
 namespace.media.link.default.shared_libs += libbinder_ndk.so
 namespace.media.link.default.shared_libs += libc.so
+namespace.media.link.default.shared_libs += libcgrouprc.so
 namespace.media.link.default.shared_libs += libdl.so
 namespace.media.link.default.shared_libs += liblog.so
 namespace.media.link.default.shared_libs += libmediametrics.so
@@ -145,6 +144,7 @@
 namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
 namespace.resolv.links = default
 namespace.resolv.link.default.shared_libs  = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
 namespace.resolv.link.default.shared_libs += libm.so
 namespace.resolv.link.default.shared_libs += libdl.so
 namespace.resolv.link.default.shared_libs += libbinder_ndk.so
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 85deacf..45e80e1 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -27,13 +27,12 @@
 # necessary) the unrestricted subdirs above. Then clean this up.
 dir.unrestricted = /data/local/tmp
 
-dir.system = /data/nativetest
-dir.system = /data/nativetest64
-dir.system = /data/benchmarktest
-dir.system = /data/benchmarktest64
-
 dir.postinstall = /postinstall
 
+# Fallback entry to provide APEX namespace lookups for binaries anywhere else.
+# This must be last.
+dir.system = /data
+
 [system]
 additional.namespaces = runtime,conscrypt,media,resolv,sphal,vndk,rs
 
@@ -133,8 +132,7 @@
 # Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
 # libart.
 namespace.default.visible = true
-namespace.default.link.runtime.shared_libs  = libart.so:libartd.so
-namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs  = libdexfile_external.so
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
@@ -214,6 +212,7 @@
 namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
 namespace.resolv.links = default
 namespace.resolv.link.default.shared_libs  = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
 namespace.resolv.link.default.shared_libs += libm.so
 namespace.resolv.link.default.shared_libs += libdl.so
 namespace.resolv.link.default.shared_libs += libbinder_ndk.so
@@ -255,17 +254,19 @@
 namespace.sphal.asan.permitted.paths +=           /vendor/${LIB}
 
 # Once in this namespace, access to libraries in /system/lib is restricted. Only
-# libs listed here can be used.
-namespace.sphal.links = default,vndk,rs
+# libs listed here can be used. Order is important here as the namespaces are
+# tried in this order. rs should be before vndk because both are capable
+# of loading libRS_internal.so
+namespace.sphal.links = rs,default,vndk
+
+# Renderscript gets separate namespace
+namespace.sphal.link.rs.shared_libs = libRS_internal.so
 
 namespace.sphal.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
 namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
 
-# Renderscript gets separate namespace
-namespace.sphal.link.rs.shared_libs = libRS_internal.so
-
 ###############################################################################
 # "rs" namespace
 #
@@ -577,8 +578,7 @@
 namespace.default.links = runtime,resolv
 namespace.default.visible = true
 
-namespace.default.link.runtime.shared_libs  = libart.so:libartd.so
-namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs  = libdexfile_external.so
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 26e1dd9..a762ba8 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -27,13 +27,12 @@
 # necessary) the unrestricted subdirs above. Then clean this up.
 dir.unrestricted = /data/local/tmp
 
-dir.system = /data/nativetest
-dir.system = /data/nativetest64
-dir.system = /data/benchmarktest
-dir.system = /data/benchmarktest64
-
 dir.postinstall = /postinstall
 
+# Fallback entry to provide APEX namespace lookups for binaries anywhere else.
+# This must be last.
+dir.system = /data
+
 [system]
 additional.namespaces = runtime,conscrypt,media,resolv,sphal,vndk,rs
 
@@ -74,8 +73,7 @@
 # Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
 # libart.
 namespace.default.visible = true
-namespace.default.link.runtime.shared_libs  = libart.so:libartd.so
-namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs  = libdexfile_external.so
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
@@ -156,6 +154,7 @@
 namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
 namespace.resolv.links = default
 namespace.resolv.link.default.shared_libs  = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
 namespace.resolv.link.default.shared_libs += libm.so
 namespace.resolv.link.default.shared_libs += libdl.so
 namespace.resolv.link.default.shared_libs += libbinder_ndk.so
@@ -197,17 +196,19 @@
 namespace.sphal.asan.permitted.paths +=           /vendor/${LIB}
 
 # Once in this namespace, access to libraries in /system/lib is restricted. Only
-# libs listed here can be used.
-namespace.sphal.links = default,vndk,rs
+# libs listed here can be used. Order is important here as the namespaces are
+# tried in this order. rs should be before vndk because both are capable
+# of loading libRS_internal.so
+namespace.sphal.links = rs,default,vndk
+
+# Renderscript gets separate namespace
+namespace.sphal.link.rs.shared_libs = libRS_internal.so
 
 namespace.sphal.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
 namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
 
-# Renderscript gets separate namespace
-namespace.sphal.link.rs.shared_libs = libRS_internal.so
-
 ###############################################################################
 # "rs" namespace
 #
@@ -400,8 +401,7 @@
 namespace.default.links = runtime,resolv
 namespace.default.visible = true
 
-namespace.default.link.runtime.shared_libs  = libart.so:libartd.so
-namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs  = libdexfile_external.so
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
@@ -477,6 +477,7 @@
 namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
 namespace.resolv.links = default
 namespace.resolv.link.default.shared_libs  = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
 namespace.resolv.link.default.shared_libs += libm.so
 namespace.resolv.link.default.shared_libs += libdl.so
 namespace.resolv.link.default.shared_libs += libbinder_ndk.so
diff --git a/rootdir/fsverity_init.sh b/rootdir/fsverity_init.sh
new file mode 100644
index 0000000..4fee15f
--- /dev/null
+++ b/rootdir/fsverity_init.sh
@@ -0,0 +1,32 @@
+#!/system/bin/sh
+#
+# 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.
+#
+
+# Enforce fsverity signature checking
+echo 1 > /proc/sys/fs/verity/require_signatures
+
+# Load all keys
+for cert in /product/etc/security/fsverity/*.der; do
+  /system/bin/mini-keyctl padd asymmetric fsv_product .fs-verity < "$cert" ||
+    log -p e -t fsverity_init "Failed to load $cert"
+done
+
+DEBUGGABLE=$(getprop ro.debuggable)
+if [ $DEBUGGABLE != "1" ]; then
+  # Prevent future key links to .fs-verity keyring
+  /system/bin/mini-keyctl restrict_keyring .fs-verity ||
+    log -p e -t fsverity_init "Failed to restrict .fs-verity keyring"
+fi
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 8e63a81..473cd8d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -222,6 +222,9 @@
     chmod 0664 /dev/cpuset/restricted/tasks
     chmod 0664 /dev/cpuset/tasks
 
+    # make the PSI monitor accessible to others
+    chown system system /proc/pressure/memory
+    chmod 0664 /proc/pressure/memory
 
     # qtaguid will limit access to specific data based on group memberships.
     #   net_bw_acct grants impersonation of socket owners.
@@ -420,12 +423,7 @@
 
     # Load fsverity keys. This needs to happen before apexd, as post-install of
     # APEXes may rely on keys.
-    exec -- /system/bin/mini-keyctl dadd asymmetric product_cert /product/etc/security/cacerts_fsverity .fs-verity
-    exec -- /system/bin/mini-keyctl dadd asymmetric vendor_cert /vendor/etc/security/cacerts_fsverity .fs-verity
-    # Prevent future key links to fsverity keyring
-    exec -- /system/bin/mini-keyctl restrict_keyring .fs-verity
-    # Enforce fsverity signature checking
-    write /proc/sys/fs/verity/require_signatures 1
+    exec -- /system/bin/fsverity_init
 
     # Make sure that apexd is started in the default namespace
     enter_default_mount_ns
@@ -654,6 +652,12 @@
     write /proc/sys/vm/dirty_expire_centisecs 200
     write /proc/sys/vm/dirty_background_ratio  5
 
+    # F2FS tuning. Set cp_interval larger than dirty_expire_centisecs
+    # to avoid power consumption when system becomes mostly idle. Be careful
+    # to make it too large, since it may bring userdata loss, if they
+    # are not aware of using fsync()/sync() to prepare sudden power-cut.
+    write /sys/fs/f2fs/${dev.mnt.blk.data}/cp_interval 200
+
     # Permissions for System Server and daemons.
     chown radio system /sys/android_power/state
     chown radio system /sys/android_power/request_state
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 7ad6f1c..5289976 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -7,6 +7,8 @@
         "-Wno-unused-const-variable",
         "-D_FILE_OFFSET_BITS=64",
         "-DWITHOUT_NLS",
+        "-DWITHOUT_BZ2",
+        "-DWITHOUT_GZIP",
     ],
 }
 
@@ -54,14 +56,6 @@
     defaults: ["toolbox_binary_defaults"],
 }
 
-// We only want 'r' on userdebug and eng builds.
-cc_binary {
-    name: "r",
-    defaults: ["toolbox_defaults"],
-    srcs: ["r.c"],
-    vendor_available: true,
-}
-
 // We build BSD grep separately (but see http://b/111849261).
 cc_defaults {
     name: "grep_common",
diff --git a/toolbox/r.c b/toolbox/r.c
deleted file mode 100644
index b96cdb2..0000000
--- a/toolbox/r.c
+++ /dev/null
@@ -1,102 +0,0 @@
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <unistd.h>
-
-#if __LP64__
-#define strtoptr strtoull
-#else
-#define strtoptr strtoul
-#endif
-
-static int usage()
-{
-    fprintf(stderr,"r [-b|-s] <address> [<value>]\n");
-    return -1;
-}
-
-int main(int argc, char *argv[])
-{
-    if(argc < 2) return usage();
-
-    int width = 4;
-    if(!strcmp(argv[1], "-b")) {
-        width = 1;
-        argc--;
-        argv++;
-    } else if(!strcmp(argv[1], "-s")) {
-        width = 2;
-        argc--;
-        argv++;
-    }
-
-    if(argc < 2) return usage();
-    uintptr_t addr = strtoptr(argv[1], 0, 16);
-
-    uintptr_t endaddr = 0;
-    char* end = strchr(argv[1], '-');
-    if (end)
-        endaddr = strtoptr(end + 1, 0, 16);
-
-    if (!endaddr)
-        endaddr = addr + width - 1;
-
-    if (endaddr <= addr) {
-        fprintf(stderr, "end address <= start address\n");
-        return -1;
-    }
-
-    bool set = false;
-    uint32_t value = 0;
-    if(argc > 2) {
-        set = true;
-        value = strtoul(argv[2], 0, 16);
-    }
-
-    int fd = open("/dev/mem", O_RDWR | O_SYNC);
-    if(fd < 0) {
-        fprintf(stderr,"cannot open /dev/mem\n");
-        return -1;
-    }
-
-    off64_t mmap_start = addr & ~(PAGE_SIZE - 1);
-    size_t mmap_size = endaddr - mmap_start + 1;
-    mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
-
-    void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE,
-                        MAP_SHARED, fd, mmap_start);
-
-    if(page == MAP_FAILED){
-        fprintf(stderr,"cannot mmap region\n");
-        return -1;
-    }
-
-    while (addr <= endaddr) {
-        switch(width){
-        case 4: {
-            uint32_t* x = (uint32_t*) (((uintptr_t) page) + (addr & 4095));
-            if(set) *x = value;
-            fprintf(stderr,"%08"PRIxPTR": %08x\n", addr, *x);
-            break;
-        }
-        case 2: {
-            uint16_t* x = (uint16_t*) (((uintptr_t) page) + (addr & 4095));
-            if(set) *x = value;
-            fprintf(stderr,"%08"PRIxPTR": %04x\n", addr, *x);
-            break;
-        }
-        case 1: {
-            uint8_t* x = (uint8_t*) (((uintptr_t) page) + (addr & 4095));
-            if(set) *x = value;
-            fprintf(stderr,"%08"PRIxPTR": %02x\n", addr, *x);
-            break;
-        }
-        }
-        addr += width;
-    }
-    return 0;
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/file.c b/toolbox/upstream-netbsd/usr.bin/grep/file.c
index cf4a0fa..428bf58 100644
--- a/toolbox/upstream-netbsd/usr.bin/grep/file.c
+++ b/toolbox/upstream-netbsd/usr.bin/grep/file.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $	*/
+/*	$NetBSD: file.c,v 1.10 2018/08/12 09:03:21 christos Exp $	*/
 /*	$FreeBSD: head/usr.bin/grep/file.c 211496 2010-08-19 09:28:59Z des $	*/
 /*	$OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $	*/
 
@@ -35,15 +35,12 @@
 #endif
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $");
+__RCSID("$NetBSD: file.c,v 1.10 2018/08/12 09:03:21 christos Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
-#ifndef __ANDROID__
-#include <bzlib.h>
-#endif
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -53,21 +50,20 @@
 #include <unistd.h>
 #include <wchar.h>
 #include <wctype.h>
-#ifndef __ANDROID__
-#include <zlib.h>
-#endif
 
 #include "grep.h"
 
 #define	MAXBUFSIZ	(32 * 1024)
 #define	LNBUFBUMP	80
 
-#ifndef __ANDROID__
+#ifndef WITHOUT_GZIP
 static gzFile gzbufdesc;
+#endif
+#ifndef WITHOUT_BZ2
 static BZFILE* bzbufdesc;
 #endif
 
-static unsigned char buffer[MAXBUFSIZ];
+static unsigned char buffer[MAXBUFSIZ + 1];
 static unsigned char *bufpos;
 static size_t bufrem;
 
@@ -77,18 +73,21 @@
 static inline int
 grep_refill(struct file *f)
 {
-	ssize_t nr;
-#ifndef __ANDROID__
+	ssize_t nr = -1;
 	int bzerr;
-#endif
 
 	bufpos = buffer;
 	bufrem = 0;
 
-#ifndef __ANDROID__
-	if (filebehave == FILE_GZIP)
+#ifndef WITHOUT_GZIP
+	if (filebehave == FILE_GZIP) {
 		nr = gzread(gzbufdesc, buffer, MAXBUFSIZ);
-	else if (filebehave == FILE_BZIP && bzbufdesc != NULL) {
+		if (nr == -1)
+			return -1;
+	}
+#endif
+#ifndef WITHOUT_BZ2
+	if (filebehave == FILE_BZIP && bzbufdesc != NULL) {
 		nr = BZ2_bzRead(&bzerr, bzbufdesc, buffer, MAXBUFSIZ);
 		switch (bzerr) {
 		case BZ_OK:
@@ -114,9 +113,13 @@
 			/* Make sure we exit with an error */
 			nr = -1;
 		}
-	} else
+		if (nr == -1)
+			return -1;
+	}
 #endif
+	if (nr == -1) {
 		nr = read(f->fd, buffer, MAXBUFSIZ);
+	}
 
 	if (nr < 0)
 		return (-1);
@@ -125,7 +128,7 @@
 	return (0);
 }
 
-static inline int
+static inline void
 grep_lnbufgrow(size_t newlen)
 {
 
@@ -133,8 +136,6 @@
 		lnbuf = grep_realloc(lnbuf, newlen);
 		lnbuflen = newlen;
 	}
-
-	return (0);
 }
 
 char *
@@ -159,20 +160,22 @@
 	/* Look for a newline in the remaining part of the buffer */
 	if ((p = memchr(bufpos, line_sep, bufrem)) != NULL) {
 		++p; /* advance over newline */
-		ret = (char *)bufpos;
 		len = p - bufpos;
+		grep_lnbufgrow(len + 1);
+		memcpy(lnbuf, bufpos, len);
+		lnbuf[len] = '\0';
+		*lenp = len;
 		bufrem -= len;
 		bufpos = p;
-		*lenp = len;
-		return (ret);
+		return ((char *)lnbuf);
 	}
 
 	/* We have to copy the current buffered data to the line buffer */
 	for (len = bufrem, off = 0; ; len += bufrem) {
 		/* Make sure there is room for more data */
-		if (grep_lnbufgrow(len + LNBUFBUMP))
-			goto error;
+		grep_lnbufgrow(len + LNBUFBUMP);
 		memcpy(lnbuf + off, bufpos, len - off);
+		lnbuf[len] = '\0';
 		off = len;
 		if (grep_refill(f) != 0)
 			goto error;
@@ -185,9 +188,9 @@
 		++p;
 		diff = p - bufpos;
 		len += diff;
-		if (grep_lnbufgrow(len))
-		    goto error;
+		grep_lnbufgrow(len + 1);
 		memcpy(lnbuf + off, bufpos, diff);
+		lnbuf[off + diff] = '\0';
 		bufrem -= diff;
 		bufpos = p;
 		break;
@@ -204,11 +207,13 @@
 grep_file_init(struct file *f)
 {
 
-#ifndef __ANDROID__
+#ifndef WITHOUT_GZIP
 	if (filebehave == FILE_GZIP &&
 	    (gzbufdesc = gzdopen(f->fd, "r")) == NULL)
 		goto error;
+#endif
 
+#ifndef WITHOUT_BZ2
 	if (filebehave == FILE_BZIP &&
 	    (bzbufdesc = BZ2_bzdopen(f->fd, "r")) == NULL)
 		goto error;
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.c b/toolbox/upstream-netbsd/usr.bin/grep/grep.c
index 1ea6ed3..bad2a73 100644
--- a/toolbox/upstream-netbsd/usr.bin/grep/grep.c
+++ b/toolbox/upstream-netbsd/usr.bin/grep/grep.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: grep.c,v 1.12 2014/07/11 16:30:45 christos Exp $	*/
+/*	$NetBSD: grep.c,v 1.15 2018/08/12 09:03:21 christos Exp $	*/
 /* 	$FreeBSD: head/usr.bin/grep/grep.c 211519 2010-08-19 22:55:17Z delphij $	*/
 /*	$OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $	*/
 
@@ -34,7 +34,7 @@
 #endif
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: grep.c,v 1.12 2014/07/11 16:30:45 christos Exp $");
+__RCSID("$NetBSD: grep.c,v 1.15 2018/08/12 09:03:21 christos Exp $");
 
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -170,7 +170,9 @@
 struct option long_options[] =
 {
 	{"binary-files",	required_argument,	NULL, BIN_OPT},
+#ifndef WITHOUT_GZIP
 	{"decompress",          no_argument,            NULL, DECOMPRESS_OPT},
+#endif
 	{"help",		no_argument,		NULL, HELP_OPT},
 	{"mmap",		no_argument,		NULL, MMAP_OPT},
 	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
@@ -197,7 +199,9 @@
 	{"no-filename",		no_argument,		NULL, 'h'},
 	{"with-filename",	no_argument,		NULL, 'H'},
 	{"ignore-case",		no_argument,		NULL, 'i'},
+#ifndef WITHOUT_BZ2
 	{"bz2decompress",	no_argument,		NULL, 'J'},
+#endif
 	{"files-with-matches",	no_argument,		NULL, 'l'},
 	{"files-without-match", no_argument,            NULL, 'L'},
 	{"max-count",		required_argument,	NULL, 'm'},
@@ -338,6 +342,7 @@
 	case 'g':
 		grepbehave = GREP_BASIC;
 		break;
+#ifndef WITHOUT_GZIP
 	case 'z':
 		filebehave = FILE_GZIP;
 		switch(__progname[1]) {
@@ -352,6 +357,7 @@
 			break;
 		}
 		break;
+#endif
 	}
 
 	lastc = '\0';
@@ -491,9 +497,11 @@
 			iflag =  true;
 			cflags |= REG_ICASE;
 			break;
+#ifndef WITHOUT_BZ2
 		case 'J':
 			filebehave = FILE_BZIP;
 			break;
+#endif
 		case 'L':
 			lflag = false;
 			Lflag = true;
@@ -596,9 +604,11 @@
 			    strcasecmp("no", optarg) != 0)
 				errx(2, getstr(3), "--color");
 			break;
+#ifndef WITHOUT_GZIP
 		case DECOMPRESS_OPT:
 			filebehave = FILE_GZIP;
 			break;
+#endif
 		case LABEL_OPT:
 			label = optarg;
 			break;
@@ -679,8 +689,13 @@
 		}
 	}
 
-	if (lbflag)
+	if (lbflag) {
+#ifdef _IOLBF
+		setvbuf(stdout, NULL, _IOLBF, 0);
+#else
 		setlinebuf(stdout);
+#endif
+	}
 
 	if ((aargc == 0 || aargc == 1) && !Hflag)
 		hflag = true;
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.h b/toolbox/upstream-netbsd/usr.bin/grep/grep.h
index fa2a3e3..b7ef7fa 100644
--- a/toolbox/upstream-netbsd/usr.bin/grep/grep.h
+++ b/toolbox/upstream-netbsd/usr.bin/grep/grep.h
@@ -1,4 +1,4 @@
-/*	$NetBSD: grep.h,v 1.8 2012/05/06 22:27:00 joerg Exp $	*/
+/*	$NetBSD: grep.h,v 1.10 2018/08/12 09:03:21 christos Exp $	*/
 /*	$OpenBSD: grep.h,v 1.15 2010/04/05 03:03:55 tedu Exp $	*/
 /*	$FreeBSD: head/usr.bin/grep/grep.h 211496 2010-08-19 09:28:59Z des $	*/
 
@@ -29,14 +29,14 @@
  * SUCH DAMAGE.
  */
 
-#ifndef __ANDROID__
+#ifndef WITHOUT_BZ2
 #include <bzlib.h>
 #endif
 #include <limits.h>
 #include <regex.h>
 #include <stdbool.h>
 #include <stdio.h>
-#ifndef __ANDROID__
+#ifndef WITHOUT_GZIP
 #include <zlib.h>
 #endif
 
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/util.c b/toolbox/upstream-netbsd/usr.bin/grep/util.c
index ecd948d..a3c9e4c 100644
--- a/toolbox/upstream-netbsd/usr.bin/grep/util.c
+++ b/toolbox/upstream-netbsd/usr.bin/grep/util.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: util.c,v 1.17 2013/01/21 03:24:43 msaitoh Exp $	*/
+/*	$NetBSD: util.c,v 1.19 2018/02/05 22:14:26 mrg Exp $	*/
 /*	$FreeBSD: head/usr.bin/grep/util.c 211496 2010-08-19 09:28:59Z des $	*/
 /*	$OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $	*/
 
@@ -34,7 +34,7 @@
 #endif
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: util.c,v 1.17 2013/01/21 03:24:43 msaitoh Exp $");
+__RCSID("$NetBSD: util.c,v 1.19 2018/02/05 22:14:26 mrg Exp $");
 
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -478,9 +478,10 @@
 			if (color) 
 				fprintf(stdout, "\33[%sm\33[K", color);
 
-				fwrite(line->dat + matches[i].rm_so, 
-				    matches[i].rm_eo - matches[i].rm_so, 1,
-				    stdout);
+			fwrite(line->dat + matches[i].rm_so, 
+			    matches[i].rm_eo - matches[i].rm_so, 1,
+			    stdout);
+
 			if (color) 
 				fprintf(stdout, "\33[m\33[K");
 			a = matches[i].rm_eo;
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index 0a0ecec..00e3dbc 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -19,8 +19,13 @@
 # to pull in the baseline set of Trusty specific modules.
 #
 
+# For gatekeeper, we include the generic -service and -impl to use legacy
+# HAL loading of gatekeeper.trusty.
+
 PRODUCT_PACKAGES += \
 	android.hardware.keymaster@3.0-service.trusty \
+	android.hardware.gatekeeper@1.0-service \
+	android.hardware.gatekeeper@1.0-impl \
 	gatekeeper.trusty
 
 PRODUCT_PROPERTY_OVERRIDES += \