Merge "Remove the global seccomp option."
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 7dff1b8..1ec145b 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -337,9 +337,12 @@
             case ADB_AUTH_SIGNATURE: {
                 // TODO: Switch to string_view.
                 std::string signature(p->payload.begin(), p->payload.end());
-                if (adbd_auth_verify(t->token, sizeof(t->token), signature)) {
+                std::string auth_key;
+                if (adbd_auth_verify(t->token, sizeof(t->token), signature, &auth_key)) {
                     adbd_auth_verified(t);
                     t->failed_auth_attempts = 0;
+                    t->auth_key = auth_key;
+                    adbd_notify_framework_connected_key(t);
                 } else {
                     if (t->failed_auth_attempts++ > 256) std::this_thread::sleep_for(1s);
                     send_auth_request(t);
@@ -348,7 +351,8 @@
             }
 
             case ADB_AUTH_RSAPUBLICKEY:
-                adbd_auth_confirm_key(p->payload.data(), p->msg.data_length, t);
+                t->auth_key = std::string(p->payload.data());
+                adbd_auth_confirm_key(t);
                 break;
 #endif
             default:
@@ -1127,7 +1131,9 @@
 
     if (service == "features") {
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
+        atransport* t =
+                s->transport ? s->transport
+                             : acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t != nullptr) {
             SendOkay(reply_fd, FeatureSetToString(t->features()));
         } else {
@@ -1186,7 +1192,9 @@
     // These always report "unknown" rather than the actual error, for scripts.
     if (service == "get-serialno") {
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
+        atransport* t =
+                s->transport ? s->transport
+                             : acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
             SendOkay(reply_fd, !t->serial.empty() ? t->serial : "unknown");
         } else {
@@ -1196,7 +1204,9 @@
     }
     if (service == "get-devpath") {
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
+        atransport* t =
+                s->transport ? s->transport
+                             : acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
             SendOkay(reply_fd, !t->devpath.empty() ? t->devpath : "unknown");
         } else {
@@ -1206,7 +1216,9 @@
     }
     if (service == "get-state") {
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
+        atransport* t =
+                s->transport ? s->transport
+                             : acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
             SendOkay(reply_fd, t->connection_state_name());
         } else {
@@ -1230,7 +1242,9 @@
 
     if (service == "reconnect") {
         std::string response;
-        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &response, true);
+        atransport* t = s->transport ? s->transport
+                                     : acquire_one_transport(type, serial, transport_id, nullptr,
+                                                             &response, true);
         if (t != nullptr) {
             kick_transport(t, true);
             response =
@@ -1242,8 +1256,15 @@
 
     // TODO: Switch handle_forward_request to string_view.
     std::string service_str(service);
-    if (handle_forward_request(
-                service_str.c_str(), [=](std::string* error) { return s->transport; }, reply_fd)) {
+    auto transport_acquirer = [=](std::string* error) {
+        if (s->transport) {
+            return s->transport;
+        } else {
+            std::string error;
+            return acquire_one_transport(type, serial, transport_id, nullptr, &error);
+        }
+    };
+    if (handle_forward_request(service_str.c_str(), transport_acquirer, reply_fd)) {
         return HostRequestResult::Handled;
     }
 
diff --git a/adb/adb.h b/adb/adb.h
index 352b2fe..c6cb06a 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -33,6 +33,7 @@
 
 constexpr size_t MAX_PAYLOAD_V1 = 4 * 1024;
 constexpr size_t MAX_PAYLOAD = 1024 * 1024;
+constexpr size_t MAX_FRAMEWORK_PAYLOAD = 64 * 1024;
 
 constexpr size_t LINUX_MAX_SOCKET_SIZE = 4194304;
 
diff --git a/adb/adb_auth.h b/adb/adb_auth.h
index 2fc8478..2be9a76 100644
--- a/adb/adb_auth.h
+++ b/adb/adb_auth.h
@@ -50,8 +50,10 @@
 void adbd_auth_verified(atransport *t);
 
 void adbd_cloexec_auth_socket();
-bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig);
-void adbd_auth_confirm_key(const char* data, size_t len, atransport* t);
+bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig,
+                      std::string* auth_key);
+void adbd_auth_confirm_key(atransport* t);
+void adbd_notify_framework_connected_key(atransport* t);
 
 void send_auth_request(atransport *t);
 
diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp
index 2b8f461..7a3a4f5 100644
--- a/adb/daemon/auth.cpp
+++ b/adb/daemon/auth.cpp
@@ -26,7 +26,9 @@
 #include <resolv.h>
 #include <stdio.h>
 #include <string.h>
+#include <iomanip>
 
+#include <algorithm>
 #include <memory>
 
 #include <android-base/file.h>
@@ -38,22 +40,24 @@
 
 static fdevent* listener_fde = nullptr;
 static fdevent* framework_fde = nullptr;
-static int framework_fd = -1;
+static auto& framework_mutex = *new std::mutex();
+static int framework_fd GUARDED_BY(framework_mutex) = -1;
+static auto& connected_keys GUARDED_BY(framework_mutex) = *new std::vector<std::string>;
 
-static void usb_disconnected(void* unused, atransport* t);
-static struct adisconnect usb_disconnect = { usb_disconnected, nullptr};
-static atransport* usb_transport;
+static void adb_disconnected(void* unused, atransport* t);
+static struct adisconnect adb_disconnect = {adb_disconnected, nullptr};
+static atransport* adb_transport;
 static bool needs_retry = false;
 
 bool auth_required = true;
 
-bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig) {
+bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig,
+                      std::string* auth_key) {
     static constexpr const char* key_paths[] = { "/adb_keys", "/data/misc/adb/adb_keys", nullptr };
 
     for (const auto& path : key_paths) {
         if (access(path, R_OK) == 0) {
             LOG(INFO) << "Loading keys from " << path;
-
             std::string content;
             if (!android::base::ReadFileToString(path, &content)) {
                 PLOG(ERROR) << "Couldn't read " << path;
@@ -61,6 +65,8 @@
             }
 
             for (const auto& line : android::base::Split(content, "\n")) {
+                if (line.empty()) continue;
+                *auth_key = line;
                 // TODO: do we really have to support both ' ' and '\t'?
                 char* sep = strpbrk(const_cast<char*>(line.c_str()), " \t");
                 if (sep) *sep = '\0';
@@ -88,9 +94,31 @@
             }
         }
     }
+    auth_key->clear();
     return false;
 }
 
+static bool adbd_send_key_message_locked(std::string_view msg_type, std::string_view key)
+        REQUIRES(framework_mutex) {
+    if (framework_fd < 0) {
+        LOG(ERROR) << "Client not connected to send msg_type " << msg_type;
+        return false;
+    }
+    std::string msg = std::string(msg_type) + std::string(key);
+    int msg_len = msg.length();
+    if (msg_len >= static_cast<int>(MAX_FRAMEWORK_PAYLOAD)) {
+        LOG(ERROR) << "Key too long (" << msg_len << ")";
+        return false;
+    }
+
+    LOG(DEBUG) << "Sending '" << msg << "'";
+    if (!WriteFdExactly(framework_fd, msg.c_str(), msg_len)) {
+        PLOG(ERROR) << "Failed to write " << msg_type;
+        return false;
+    }
+    return true;
+}
+
 static bool adbd_auth_generate_token(void* token, size_t token_size) {
     FILE* fp = fopen("/dev/urandom", "re");
     if (!fp) return false;
@@ -99,17 +127,28 @@
     return okay;
 }
 
-static void usb_disconnected(void* unused, atransport* t) {
-    LOG(INFO) << "USB disconnect";
-    usb_transport = nullptr;
+static void adb_disconnected(void* unused, atransport* t) {
+    LOG(INFO) << "ADB disconnect";
+    adb_transport = nullptr;
     needs_retry = false;
+    {
+        std::lock_guard<std::mutex> lock(framework_mutex);
+        if (framework_fd >= 0) {
+            adbd_send_key_message_locked("DC", t->auth_key);
+        }
+        connected_keys.erase(std::remove(connected_keys.begin(), connected_keys.end(), t->auth_key),
+                             connected_keys.end());
+    }
 }
 
 static void framework_disconnected() {
     LOG(INFO) << "Framework disconnect";
     if (framework_fde) {
         fdevent_destroy(framework_fde);
-        framework_fd = -1;
+        {
+            std::lock_guard<std::mutex> lock(framework_mutex);
+            framework_fd = -1;
+        }
     }
 }
 
@@ -120,41 +159,28 @@
         if (ret <= 0) {
             framework_disconnected();
         } else if (ret == 2 && response[0] == 'O' && response[1] == 'K') {
-            if (usb_transport) {
-                adbd_auth_verified(usb_transport);
+            if (adb_transport) {
+                adbd_auth_verified(adb_transport);
             }
         }
     }
 }
 
-void adbd_auth_confirm_key(const char* key, size_t len, atransport* t) {
-    if (!usb_transport) {
-        usb_transport = t;
-        t->AddDisconnect(&usb_disconnect);
+void adbd_auth_confirm_key(atransport* t) {
+    if (!adb_transport) {
+        adb_transport = t;
+        t->AddDisconnect(&adb_disconnect);
     }
 
-    if (framework_fd < 0) {
-        LOG(ERROR) << "Client not connected";
-        needs_retry = true;
-        return;
-    }
+    {
+        std::lock_guard<std::mutex> lock(framework_mutex);
+        if (framework_fd < 0) {
+            LOG(ERROR) << "Client not connected";
+            needs_retry = true;
+            return;
+        }
 
-    if (key[len - 1] != '\0') {
-        LOG(ERROR) << "Key must be a null-terminated string";
-        return;
-    }
-
-    char msg[MAX_PAYLOAD_V1];
-    int msg_len = snprintf(msg, sizeof(msg), "PK%s", key);
-    if (msg_len >= static_cast<int>(sizeof(msg))) {
-        LOG(ERROR) << "Key too long (" << msg_len << ")";
-        return;
-    }
-    LOG(DEBUG) << "Sending '" << msg << "'";
-
-    if (unix_write(framework_fd, msg, msg_len) == -1) {
-        PLOG(ERROR) << "Failed to write PK";
-        return;
+        adbd_send_key_message_locked("PK", t->auth_key);
     }
 }
 
@@ -165,18 +191,46 @@
         return;
     }
 
-    if (framework_fd >= 0) {
-        LOG(WARNING) << "adb received framework auth socket connection again";
-        framework_disconnected();
+    {
+        std::lock_guard<std::mutex> lock(framework_mutex);
+        if (framework_fd >= 0) {
+            LOG(WARNING) << "adb received framework auth socket connection again";
+            framework_disconnected();
+        }
+
+        framework_fd = s;
+        framework_fde = fdevent_create(framework_fd, adbd_auth_event, nullptr);
+        fdevent_add(framework_fde, FDE_READ);
+
+        if (needs_retry) {
+            needs_retry = false;
+            send_auth_request(adb_transport);
+        }
+
+        // if a client connected before the framework was available notify the framework of the
+        // connected key now.
+        if (!connected_keys.empty()) {
+            for (const auto& key : connected_keys) {
+                adbd_send_key_message_locked("CK", key);
+            }
+        }
     }
+}
 
-    framework_fd = s;
-    framework_fde = fdevent_create(framework_fd, adbd_auth_event, nullptr);
-    fdevent_add(framework_fde, FDE_READ);
-
-    if (needs_retry) {
-        needs_retry = false;
-        send_auth_request(usb_transport);
+void adbd_notify_framework_connected_key(atransport* t) {
+    if (!adb_transport) {
+        adb_transport = t;
+        t->AddDisconnect(&adb_disconnect);
+    }
+    {
+        std::lock_guard<std::mutex> lock(framework_mutex);
+        if (std::find(connected_keys.begin(), connected_keys.end(), t->auth_key) ==
+            connected_keys.end()) {
+            connected_keys.push_back(t->auth_key);
+        }
+        if (framework_fd >= 0) {
+            adbd_send_key_message_locked("CK", t->auth_key);
+        }
     }
 }
 
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index f4aa9fb..1abae87 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -509,16 +509,14 @@
             }
 
             if (id.direction == TransferDirection::READ) {
-                if (!HandleRead(id, event.res)) {
-                    return;
-                }
+                HandleRead(id, event.res);
             } else {
                 HandleWrite(id);
             }
         }
     }
 
-    bool HandleRead(TransferId id, int64_t size) {
+    void HandleRead(TransferId id, int64_t size) {
         uint64_t read_idx = id.id % kUsbReadQueueDepth;
         IoBlock* block = &read_requests_[read_idx];
         block->pending = false;
@@ -528,7 +526,7 @@
         if (block->id().id != needed_read_id_) {
             LOG(VERBOSE) << "read " << block->id().id << " completed while waiting for "
                          << needed_read_id_;
-            return true;
+            return;
         }
 
         for (uint64_t id = needed_read_id_;; ++id) {
@@ -537,22 +535,15 @@
             if (current_block->pending) {
                 break;
             }
-            if (!ProcessRead(current_block)) {
-                return false;
-            }
+            ProcessRead(current_block);
             ++needed_read_id_;
         }
-
-        return true;
     }
 
-    bool ProcessRead(IoBlock* block) {
+    void ProcessRead(IoBlock* block) {
         if (!block->payload->empty()) {
             if (!incoming_header_.has_value()) {
-                if (block->payload->size() != sizeof(amessage)) {
-                    HandleError("received packet of unexpected length while reading header");
-                    return false;
-                }
+                CHECK_EQ(sizeof(amessage), block->payload->size());
                 amessage msg;
                 memcpy(&msg, block->payload->data(), sizeof(amessage));
                 LOG(DEBUG) << "USB read:" << dump_header(&msg);
@@ -560,10 +551,7 @@
             } else {
                 size_t bytes_left = incoming_header_->data_length - incoming_payload_.size();
                 Block payload = std::move(*block->payload);
-                if (block->payload->size() > bytes_left) {
-                    HandleError("received too many bytes while waiting for payload");
-                    return false;
-                }
+                CHECK_LE(payload.size(), bytes_left);
                 incoming_payload_.append(std::make_unique<Block>(std::move(payload)));
             }
 
@@ -582,7 +570,6 @@
 
         PrepareReadBlock(block, block->id().id + kUsbReadQueueDepth);
         SubmitRead(block);
-        return true;
     }
 
     bool SubmitRead(IoBlock* block) {
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp
index 22c9243..154c9b9 100644
--- a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp
+++ b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp
@@ -46,10 +46,10 @@
 
 void DeployPatchGenerator::APKEntryToLog(const APKEntry& entry) {
     Log("Filename: %s", entry.filename().c_str());
-    Log("CRC32: 0x%08llX", entry.crc32());
-    Log("Data Offset: %lld", entry.dataoffset());
-    Log("Compressed Size: %lld", entry.compressedsize());
-    Log("Uncompressed Size: %lld", entry.uncompressedsize());
+    Log("CRC32: 0x%08" PRIX64, entry.crc32());
+    Log("Data Offset: %" PRId64, entry.dataoffset());
+    Log("Compressed Size: %" PRId64, entry.compressedsize());
+    Log("Uncompressed Size: %" PRId64, entry.uncompressedsize());
 }
 
 void DeployPatchGenerator::APKMetaDataToLog(const char* file, const APKMetaData& metadata) {
@@ -164,4 +164,4 @@
                   return lhs.localEntry->dataoffset() < rhs.localEntry->dataoffset();
               });
     return totalSize;
-}
\ No newline at end of file
+}
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 75993b3..e78530c 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -757,6 +757,8 @@
 
 #if ADB_HOST
     service = std::string_view(s->smart_socket_data).substr(4);
+
+    // TODO: These should be handled in handle_host_request.
     if (android::base::ConsumePrefix(&service, "host-serial:")) {
         // serial number should follow "host:" and could be a host:port string.
         if (!internal::parse_host_service(&serial, &service, service)) {
diff --git a/adb/test_device.py b/adb/test_device.py
index f95a5b3..dbd80ed 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -139,6 +139,25 @@
         msg = self.device.forward_list()
         self.assertEqual('', msg.strip())
 
+    def test_forward_old_protocol(self):
+        serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
+
+        msg = self.device.forward_list()
+        self.assertEqual('', msg.strip(),
+                         'Forwarding list must be empty to run this test.')
+
+        s = socket.create_connection(("localhost", 5037))
+        service = b"host-serial:%s:forward:tcp:5566;tcp:6655" % serialno
+        cmd = b"%04x%s" % (len(service), service)
+        s.sendall(cmd)
+
+        msg = self.device.forward_list()
+        self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
+
+        self.device.forward_remove_all()
+        msg = self.device.forward_list()
+        self.assertEqual('', msg.strip())
+
     def test_forward_tcp_port_0(self):
         self.assertEqual('', self.device.forward_list().strip(),
                          'Forwarding list must be empty to run this test.')
diff --git a/adb/tools/Android.bp b/adb/tools/Android.bp
new file mode 100644
index 0000000..71e32b7
--- /dev/null
+++ b/adb/tools/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary_host {
+    name: "check_ms_os_desc",
+
+    defaults: ["adb_defaults"],
+
+    srcs: [
+        "check_ms_os_desc.cpp",
+    ],
+
+    static_libs: [
+        "libbase",
+        "libusb",
+    ],
+
+    stl: "libc++_static",
+
+    dist: {
+        targets: [
+            "sdk",
+        ],
+    },
+}
diff --git a/adb/tools/check_ms_os_desc.cpp b/adb/tools/check_ms_os_desc.cpp
new file mode 100644
index 0000000..8743ff7
--- /dev/null
+++ b/adb/tools/check_ms_os_desc.cpp
@@ -0,0 +1,266 @@
+/*
+ * 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 <err.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <libusb/libusb.h>
+
+static bool is_adb_device(libusb_device* device) {
+    libusb_device_descriptor device_desc;
+    libusb_get_device_descriptor(device, &device_desc);
+    if (device_desc.bDeviceClass != 0) {
+        return false;
+    }
+
+    libusb_config_descriptor* config_desc;
+    int rc = libusb_get_active_config_descriptor(device, &config_desc);
+    if (rc != 0) {
+        fprintf(stderr, "failed to get config descriptor for device %u:%u: %s\n",
+                libusb_get_bus_number(device), libusb_get_port_number(device),
+                libusb_error_name(rc));
+        return false;
+    }
+
+    for (size_t i = 0; i < config_desc->bNumInterfaces; ++i) {
+        const libusb_interface* interface = &config_desc->interface[i];
+        for (int j = 0; j < interface->num_altsetting; ++j) {
+            const libusb_interface_descriptor* interface_descriptor = &interface->altsetting[j];
+            if (interface_descriptor->bInterfaceClass == 0xff &&
+                interface_descriptor->bInterfaceSubClass == 0x42 &&
+                interface_descriptor->bInterfaceProtocol == 1) {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+static std::optional<std::vector<uint8_t>> get_descriptor(libusb_device_handle* handle,
+                                                          uint8_t type, uint8_t index,
+                                                          uint16_t length) {
+    std::vector<uint8_t> result;
+    result.resize(length);
+    int rc = libusb_get_descriptor(handle, type, index, result.data(), result.size());
+    if (rc < 0) {
+        fprintf(stderr, "libusb_get_descriptor failed: %s\n", libusb_error_name(rc));
+        return std::nullopt;
+    }
+    result.resize(rc);
+    return result;
+}
+
+static std::optional<std::string> get_string_descriptor(libusb_device_handle* handle,
+                                                        uint8_t index) {
+    std::string result;
+    result.resize(4096);
+    int rc = libusb_get_string_descriptor_ascii(
+            handle, index, reinterpret_cast<uint8_t*>(result.data()), result.size());
+    if (rc < 0) {
+        fprintf(stderr, "libusb_get_string_descriptor_ascii failed: %s\n", libusb_error_name(rc));
+        return std::nullopt;
+    }
+    result.resize(rc);
+    return result;
+}
+
+static void check_ms_os_desc_v1(libusb_device_handle* device_handle, const std::string& serial) {
+    auto os_desc = get_descriptor(device_handle, 0x03, 0xEE, 0x12);
+    if (!os_desc) {
+        errx(1, "failed to retrieve MS OS descriptor");
+    }
+
+    if (os_desc->size() != 0x12) {
+        errx(1, "os descriptor size mismatch");
+    }
+
+    if (memcmp(os_desc->data() + 2, u"MSFT100\0", 14) != 0) {
+        errx(1, "os descriptor signature mismatch");
+    }
+
+    uint8_t vendor_code = (*os_desc)[16];
+    uint8_t pad = (*os_desc)[17];
+
+    if (pad != 0) {
+        errx(1, "os descriptor padding non-zero");
+    }
+
+    std::vector<uint8_t> data;
+    data.resize(0x10);
+    int rc = libusb_control_transfer(device_handle, 0xC0, vendor_code, 0x00, 0x04, data.data(),
+                                     data.size(), 0);
+    if (rc != 0x10) {
+        errx(1, "failed to retrieve MS OS v1 compat descriptor header: %s", libusb_error_name(rc));
+    }
+
+    struct __attribute__((packed)) ms_os_desc_v1_header {
+        uint32_t dwLength;
+        uint16_t bcdVersion;
+        uint16_t wIndex;
+        uint8_t bCount;
+        uint8_t reserved[7];
+    };
+    static_assert(sizeof(ms_os_desc_v1_header) == 0x10);
+
+    ms_os_desc_v1_header hdr;
+    memcpy(&hdr, data.data(), data.size());
+
+    data.resize(hdr.dwLength);
+    rc = libusb_control_transfer(device_handle, 0xC0, vendor_code, 0x00, 0x04, data.data(),
+                                 data.size(), 0);
+    if (static_cast<size_t>(rc) != data.size()) {
+        errx(1, "failed to retrieve MS OS v1 compat descriptor: %s", libusb_error_name(rc));
+    }
+
+    memcpy(&hdr, data.data(), data.size());
+
+    struct __attribute__((packed)) ms_os_desc_v1_function {
+        uint8_t bFirstInterfaceNumber;
+        uint8_t reserved1;
+        uint8_t compatibleID[8];
+        uint8_t subCompatibleID[8];
+        uint8_t reserved2[6];
+    };
+
+    if (sizeof(ms_os_desc_v1_header) + hdr.bCount * sizeof(ms_os_desc_v1_function) != data.size()) {
+        errx(1, "MS OS v1 compat descriptor size mismatch");
+    }
+
+    for (int i = 0; i < hdr.bCount; ++i) {
+        ms_os_desc_v1_function function;
+        memcpy(&function,
+               data.data() + sizeof(ms_os_desc_v1_header) + i * sizeof(ms_os_desc_v1_function),
+               sizeof(function));
+        if (memcmp("WINUSB\0\0", function.compatibleID, 8) == 0) {
+            return;
+        }
+    }
+
+    errx(1, "failed to find v1 MS OS descriptor specifying WinUSB for device %s", serial.c_str());
+}
+
+static void check_ms_os_desc_v2(libusb_device_handle* device_handle, const std::string& serial) {
+    libusb_bos_descriptor* bos;
+    int rc = libusb_get_bos_descriptor(device_handle, &bos);
+
+    if (rc != 0) {
+        fprintf(stderr, "failed to get bos descriptor for device %s\n", serial.c_str());
+        return;
+    }
+
+    for (size_t i = 0; i < bos->bNumDeviceCaps; ++i) {
+        libusb_bos_dev_capability_descriptor* desc = bos->dev_capability[i];
+        if (desc->bDescriptorType != LIBUSB_DT_DEVICE_CAPABILITY) {
+            errx(1, "invalid BOS descriptor type: %d", desc->bDescriptorType);
+        }
+
+        if (desc->bDevCapabilityType != 0x05 /* PLATFORM */) {
+            fprintf(stderr, "skipping non-platform dev capability: %#02x\n",
+                    desc->bDevCapabilityType);
+            continue;
+        }
+
+        if (desc->bLength < sizeof(*desc) + 16) {
+            errx(1, "received device capability descriptor not long enough to contain a UUID?");
+        }
+
+        char uuid[16];
+        memcpy(uuid, desc->dev_capability_data, 16);
+
+        constexpr uint8_t ms_os_uuid[16] = {0xD8, 0xDD, 0x60, 0xDF, 0x45, 0x89, 0x4C, 0xC7,
+                                            0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F};
+        if (memcmp(uuid, ms_os_uuid, 16) != 0) {
+            fprintf(stderr, "skipping unknown UUID\n");
+            continue;
+        }
+
+        size_t data_length = desc->bLength - sizeof(*desc) - 16;
+        fprintf(stderr, "found MS OS 2.0 descriptor, length = %zu\n", data_length);
+
+        // Linux does not appear to support MS OS 2.0 Descriptors.
+        // TODO: If and when it does, verify that we're emitting them properly.
+    }
+}
+
+int main(int argc, char** argv) {
+    libusb_context* ctx;
+    if (libusb_init(&ctx) != 0) {
+        errx(1, "failed to initialize libusb context");
+    }
+
+    libusb_device** device_list = nullptr;
+    ssize_t device_count = libusb_get_device_list(ctx, &device_list);
+    if (device_count < 0) {
+        errx(1, "libusb_get_device_list failed");
+    }
+
+    const char* expected_serial = getenv("ANDROID_SERIAL");
+    bool found = false;
+
+    for (ssize_t i = 0; i < device_count; ++i) {
+        libusb_device* device = device_list[i];
+        if (!is_adb_device(device)) {
+            continue;
+        }
+
+        libusb_device_handle* device_handle = nullptr;
+        int rc = libusb_open(device, &device_handle);
+        if (rc != 0) {
+            fprintf(stderr, "failed to open device %u:%u: %s\n", libusb_get_bus_number(device),
+                    libusb_get_port_number(device), libusb_error_name(rc));
+            continue;
+        }
+
+        libusb_device_descriptor device_desc;
+        libusb_get_device_descriptor(device, &device_desc);
+
+        std::optional<std::string> serial =
+                get_string_descriptor(device_handle, device_desc.iSerialNumber);
+        if (!serial) {
+            errx(1, "failed to get serial for device %u:%u", libusb_get_bus_number(device),
+                 libusb_get_port_number(device));
+        }
+
+        if (expected_serial && *serial != expected_serial) {
+            fprintf(stderr, "skipping %s (wanted %s)\n", serial->c_str(), expected_serial);
+            continue;
+        }
+
+        // Check for MS OS Descriptor v1.
+        // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeusb/c2f351f9-84d2-4a1b-9fe3-a6ca195f84d0
+        fprintf(stderr, "fetching v1 OS descriptor from device %s\n", serial->c_str());
+        check_ms_os_desc_v1(device_handle, *serial);
+        fprintf(stderr, "found v1 OS descriptor for device %s\n", serial->c_str());
+
+        // Read BOS for MS OS Descriptor 2.0 descriptors:
+        // http://download.microsoft.com/download/3/5/6/3563ED4A-F318-4B66-A181-AB1D8F6FD42D/MS_OS_2_0_desc.docx
+        fprintf(stderr, "fetching v2 OS descriptor from device %s\n", serial->c_str());
+        check_ms_os_desc_v2(device_handle, *serial);
+
+        found = true;
+    }
+
+    if (expected_serial && !found) {
+        errx(1, "failed to find device with serial %s", expected_serial);
+    }
+    return 0;
+}
diff --git a/adb/transport.h b/adb/transport.h
index 245037e..61a3d9a 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -275,6 +275,9 @@
     std::string device;
     std::string devpath;
 
+    // Used to provide the key to the framework.
+    std::string auth_key;
+
     bool IsTcpDevice() const { return type == kTransportLocal; }
 
 #if ADB_HOST
diff --git a/base/Android.bp b/base/Android.bp
index 357ce01..f5000c1 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -20,8 +20,14 @@
         "-Wall",
         "-Werror",
         "-Wextra",
-        "-D_FILE_OFFSET_BITS=64",
     ],
+    target: {
+        android: {
+            cflags: [
+                "-D_FILE_OFFSET_BITS=64",
+            ],
+        },
+    },
 }
 
 cc_library_headers {
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 6e11b4e..1605daf 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -120,7 +120,7 @@
   // Catch bogus error checks (i.e.: "!fd" instead of "fd != -1").
   bool operator!() const = delete;
 
-  bool ok() const { return get() != -1; }
+  bool ok() const { return get() >= 0; }
 
   int release() __attribute__((warn_unused_result)) {
     tag(fd_, this, nullptr);
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index cd9fda3..dd24aac 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -1093,8 +1093,8 @@
 void LogBootInfoToStatsd(std::chrono::milliseconds end_time,
                          std::chrono::milliseconds total_duration, int32_t bootloader_duration_ms,
                          double time_since_last_boot_sec) {
-  const auto reason = android::base::GetProperty(bootloader_reboot_reason_property, "<EMPTY>");
-  const auto system_reason = android::base::GetProperty(system_reboot_reason_property, "<EMPTY>");
+  auto reason = android::base::GetProperty(bootloader_reboot_reason_property, "<EMPTY>");
+  auto system_reason = android::base::GetProperty(system_reboot_reason_property, "<EMPTY>");
   android::util::stats_write(android::util::BOOT_SEQUENCE_REPORTED, reason.c_str(),
                              system_reason.c_str(), end_time.count(), total_duration.count(),
                              (int64_t)bootloader_duration_ms,
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 716fe95..546bce2 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -134,8 +134,6 @@
         "libfs_mgr",
         "libgsi",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "liblog",
         "liblp",
         "libsparse",
@@ -145,6 +143,10 @@
     static_libs: [
         "libhealthhalutils",
     ],
+
+    header_libs: [
+        "libsnapshot_headers",
+    ]
 }
 
 cc_defaults {
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 409ef70..4c77c75 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -194,23 +194,6 @@
     return device->WriteStatus(FastbootResult::FAIL, "Couldn't download data");
 }
 
-bool FlashHandler(FastbootDevice* device, const std::vector<std::string>& args) {
-    if (args.size() < 2) {
-        return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
-    }
-
-    if (GetDeviceLockStatus()) {
-        return device->WriteStatus(FastbootResult::FAIL,
-                                   "Flashing is not allowed on locked devices");
-    }
-
-    int ret = Flash(device, args[1]);
-    if (ret < 0) {
-        return device->WriteStatus(FastbootResult::FAIL, strerror(-ret));
-    }
-    return device->WriteStatus(FastbootResult::OKAY, "Flashing succeeded");
-}
-
 bool SetActiveHandler(FastbootDevice* device, const std::vector<std::string>& args) {
     if (args.size() < 2) {
         return device->WriteStatus(FastbootResult::FAIL, "Missing slot argument");
@@ -440,6 +423,11 @@
     if (!partition) {
         return device->WriteFail("Partition does not exist");
     }
+
+    // Remove the updated flag to cancel any snapshots.
+    uint32_t attrs = partition->attributes();
+    partition->set_attributes(attrs & ~LP_PARTITION_ATTR_UPDATED);
+
     if (!builder->ResizePartition(partition, partition_size)) {
         return device->WriteFail("Not enough space to resize partition");
     }
@@ -449,6 +437,42 @@
     return device->WriteOkay("Partition resized");
 }
 
+void CancelPartitionSnapshot(FastbootDevice* device, const std::string& partition_name) {
+    PartitionBuilder builder(device, partition_name);
+    if (!builder.Valid()) return;
+
+    auto partition = builder->FindPartition(partition_name);
+    if (!partition) return;
+
+    // Remove the updated flag to cancel any snapshots.
+    uint32_t attrs = partition->attributes();
+    partition->set_attributes(attrs & ~LP_PARTITION_ATTR_UPDATED);
+
+    builder.Write();
+}
+
+bool FlashHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
+    }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL,
+                                   "Flashing is not allowed on locked devices");
+    }
+
+    const auto& partition_name = args[1];
+    if (LogicalPartitionExists(device, partition_name)) {
+        CancelPartitionSnapshot(device, partition_name);
+    }
+
+    int ret = Flash(device, partition_name);
+    if (ret < 0) {
+        return device->WriteStatus(FastbootResult::FAIL, strerror(-ret));
+    }
+    return device->WriteStatus(FastbootResult::OKAY, "Flashing succeeded");
+}
+
 bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args) {
     if (args.size() < 2) {
         return device->WriteFail("Invalid arguments");
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 99854c9..102ebdb 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -32,6 +32,7 @@
 #include <fstab/fstab.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
+#include <libsnapshot/snapshot.h>
 #include <sparse/sparse.h>
 
 #include "fastboot_device.h"
@@ -171,6 +172,11 @@
         if (!slot_suffix.empty() && GetPartitionSlotSuffix(partition_name) == slot_suffix) {
             continue;
         }
+        std::string group_name = GetPartitionGroupName(old_metadata->groups[partition.group_index]);
+        // Skip partitions in the COW group
+        if (group_name == android::snapshot::kCowGroupName) {
+            continue;
+        }
         partitions_to_keep.emplace(partition_name);
     }
 
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 8923f40..4737ae4 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -160,6 +160,10 @@
                                                       "vbmeta_system",
                                                                   true,  ImageType::BootCritical },
     { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  ImageType::Normal },
+    { "vendor_boot",
+                  "vendor_boot.img",  "vendor_boot.sig",
+                                                      "vendor_boot",
+                                                                  true,  ImageType::BootCritical },
     { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  ImageType::Normal },
         // clang-format on
 };
@@ -2054,10 +2058,10 @@
             if (partition == "userdata" && set_fbe_marker) {
                 fprintf(stderr, "setting FBE marker on initial userdata...\n");
                 std::string initial_userdata_dir = create_fbemarker_tmpdir();
-                fb_perform_format(partition, 1, "", "", initial_userdata_dir);
+                fb_perform_format(partition, 1, partition_type, "", initial_userdata_dir);
                 delete_fbemarker_tmpdir(initial_userdata_dir);
             } else {
-                fb_perform_format(partition, 1, "", "", "");
+                fb_perform_format(partition, 1, partition_type, "", "");
             }
         }
     }
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index fea0a77..b897182 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -291,7 +291,7 @@
 RetCode FastBootDriver::UploadInner(const std::string& outfile, std::string* response,
                                     std::vector<std::string>* info) {
     RetCode ret;
-    int dsize;
+    int dsize = 0;
     if ((ret = RawCommand(FB_CMD_UPLOAD, response, info, &dsize))) {
         error_ = "Upload request failed: " + error_;
         return ret;
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index fe2e052..bb63df8 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -1,33 +1,27 @@
-Android Overlayfs integration with adb remount
+Android OverlayFS Integration with adb Remount
 ==============================================
 
 Introduction
 ------------
 
-Users working with userdebug or eng builds expect to be able to
-remount the system partition as read-write and then add or modify
-any number of files without reflashing the system image, which is
-understandably efficient for a development cycle.
-Limited memory systems that chose to use readonly filesystems like
-*squashfs*, or *Logical Resizable Android Partitions* which land
-system partition images right-sized, and with filesystem that have
-been deduped on the block level to compress the content; means that
-either a remount is not possible directly, or when done offers
-little or no utility because of remaining space limitations or
-support logistics.
+Users working with userdebug or eng builds expect to be able to remount the
+system partition as read-write and then add or modify any number of files
+without reflashing the system image, which is efficient for a development cycle.
 
-*Overlayfs* comes to the rescue for these debug scenarios, and logic
-will _automatically_ setup backing storage for a writable filesystem
-as an upper reference, and mount overtop the lower.  These actions
-will be performed in the **adb disable-verity** and **adb remount**
-requests.
+Limited memory systems use read-only types of file systems or logical resizable
+Android partitions (LRAPs). These file systems land system partition images
+right-sized, and have been deduped at the block level to compress the content.
+This means that a remount either isn’t possible, or isn't useful because of
+space limitations or support logistics.
 
-Operations
-----------
+OverlayFS resolves these debug scenarios with the _adb disable-verity_ and
+_adb remount_ commands, which set up backing storage for a writable file
+system as an upper reference, and mount the lower reference on top.
 
-### Cookbook
+Performing a remount
+--------------------
 
-The typical action to utilize the remount facility is:
+Use the following sequence to perform the remount.
 
     $ adb root
     $ adb disable-verity
@@ -36,7 +30,7 @@
     $ adb root
     $ adb remount
 
-Followed by one of the following:
+Then enter one of the following sequences:
 
     $ adb stop
     $ adb sync
@@ -48,75 +42,67 @@
     $ adb push <source> <destination>
     $ adb reboot
 
-Note that the sequence above:
+Note that you can replace these two lines:
 
     $ adb disable-verity
     $ adb reboot
 
-*or*
-
-    $ adb remount
-
-can be replaced in both places with:
+with this line:
 
     $ adb remount -R
 
-which will not reboot if everything is already prepared and ready
-to go.
+**Note:** _adb reboot -R_ won’t reboot if the device is already in the adb remount state.
 
-None of this changes if *overlayfs* needs to be engaged.
-The decisions whether to use traditional direct filesystem remount,
-or one wrapped by *overlayfs* is automatically determined based on
-a probe of the filesystem types and space remaining.
+None of this changes if OverlayFS needs to be engaged.
+The decisions whether to use traditional direct file-system remount,
+or one wrapped by OverlayFS is automatically determined based on
+a probe of the file-system types and space remaining.
 
 ### Backing Storage
 
-When *overlayfs* logic is feasible, it will use either the
+When *OverlayFS* logic is feasible, it uses either the
 **/cache/overlay/** directory for non-A/B devices, or the
 **/mnt/scratch/overlay** directory for A/B devices that have
-access to *Logical Resizable Android Partitions*.
+access to *LRAP*.
+It is also possible for an A/B device to use the system_<other> partition
+for backing storage. eg: if booting off system_a+vendor_a, use system_b.
 The backing store is used as soon as possible in the boot
-process and can occur at first stage init, or at the
-mount_all init rc commands.
+process and can occur at first stage init, or when the
+*mount_all* commands are run in init RC scripts.
 
-This early as possible attachment of *overlayfs* means that
-*sepolicy* or *init* itself can also be pushed and used after
-the exec phases that accompany each stage.
+By attaching OverlayFS early, SEpolicy or init can be pushed and used after the exec phases of each stage.
 
 Caveats
 -------
 
-- Space used in the backing storage is on a file by file basis
-  and will require more space than if updated in place.  As such
-  it is important to be mindful of any wasted space, for instance
-  **BOARD_<partition>IMAGE_PARTITION_RESERVED_SIZE** being defined
-  will have a negative impact on the overall right-sizing of images
-  and thus free dynamic partition space.
-- Kernel must have CONFIG_OVERLAY_FS=y and will need to be patched
-  with "*overlayfs: override_creds=off option bypass creator_cred*"
-  if kernel is 4.4 or higher.
+- Backing storage requires more space than immutable storage, as backing is
+  done file by file. Be mindful of wasted space. For example, defining
+  **BOARD_IMAGE_PARTITION_RESERVED_SIZE** has a negative impact on the
+  right-sizing of images and requires more free dynamic partition space.
+- The kernel requires **CONFIG_OVERLAY_FS=y**. If the kernel version is higher
+  than 4.4, it requires source to be in line with android-common kernels. 
   The patch series is available on the upstream mailing list and the latest as
-  of Jul 24 2019 is https://lore.kernel.org/patchwork/patch/1104577/.
-  This patch adds an override_creds _mount_ option to overlayfs that
+  of Sep 5 2019 is https://www.spinics.net/lists/linux-mtd/msg08331.html
+  This patch adds an override_creds _mount_ option to OverlayFS that
   permits legacy behavior for systems that do not have overlapping
   sepolicy rules, principals of least privilege, which is how Android behaves.
-- *adb enable-verity* will free up overlayfs and as a bonus the
-  device will be reverted pristine to before any content was updated.
-  Update engine does not take advantage of this, will perform a full OTA.
-- Update engine may not run if *fs_mgr_overlayfs_is_setup*() reports
-  true as adb remount overrides are incompatible with an OTA resources.
+  For 4.19 and higher a rework of the xattr handling to deal with recursion
+  is required. https://patchwork.kernel.org/patch/11117145/ is a start of that
+  adjustment.
+- _adb enable-verity_ frees up OverlayFS and reverts the device to the state
+  prior to content updates. The update engine performs a full OTA.
+- _adb remount_ overrides are incompatible with OTA resources, so the update
+  engine may not run if fs_mgr_overlayfs_is_setup() returns true.
+- If a dynamic partition runs out of space, making a logical partition larger
+  may fail because of the scratch partition. If this happens, clear the scratch
+  storage by running either either _fastboot flashall_ or _adb enable-verity_.
+  Then reinstate the overrides and continue.
 - For implementation simplicity on retrofit dynamic partition devices,
   take the whole alternate super (eg: if "*a*" slot, then the whole of
   "*system_b*").
   Since landing a filesystem on the alternate super physical device
   without differentiating if it is setup to support logical or physical,
   the alternate slot metadata and previous content will be lost.
-- If dynamic partitions runs out of space, resizing a logical
-  partition larger may fail because of the scratch partition.
-  If this happens, either fastboot flashall or adb enable-verity can
-  be used to clear scratch storage to permit the flash.
-  Then reinstate the overrides and continue.
-- File bugs or submit fixes for review.
 - There are other subtle caveats requiring complex logic to solve.
   Have evaluated them as too complex or not worth the trouble, please
   File a bug if a use case needs to be covered.
@@ -125,7 +111,7 @@
     out and we reserve the right to not inform, if the layering
     does not prevent any messaging.
   - Space remaining threshold is hard coded.  If 1% or more space
-    still remains, overlayfs will not be used, yet that amount of
+    still remains, OverlayFS will not be used, yet that amount of
     space remaining is problematic.
   - Flashing a partition via bootloader fastboot, as opposed to user
     space fastbootd, is not detected, thus a partition may have
@@ -139,3 +125,4 @@
     to confusion.  When debugging using **adb remount** it is
     currently advised to confirm update is present after a reboot
     to develop confidence.
+- File bugs or submit fixes for review.
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index fa756a0..0dcb9fe 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -79,22 +79,22 @@
     return true;
 }
 
-static bool CreateDmTable(const IPartitionOpener& opener, const LpMetadata& metadata,
-                          const LpMetadataPartition& partition, const std::string& super_device,
-                          DmTable* table) {
+bool CreateDmTableInternal(const CreateLogicalPartitionParams& params, DmTable* table) {
+    const auto& super_device = params.block_device;
+
     uint64_t sector = 0;
-    for (size_t i = 0; i < partition.num_extents; i++) {
-        const auto& extent = metadata.extents[partition.first_extent_index + i];
+    for (size_t i = 0; i < params.partition->num_extents; i++) {
+        const auto& extent = params.metadata->extents[params.partition->first_extent_index + i];
         std::unique_ptr<DmTarget> target;
         switch (extent.target_type) {
             case LP_TARGET_TYPE_ZERO:
                 target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
                 break;
             case LP_TARGET_TYPE_LINEAR: {
-                const auto& block_device = metadata.block_devices[extent.target_source];
+                const auto& block_device = params.metadata->block_devices[extent.target_source];
                 std::string dev_string;
-                if (!GetPhysicalPartitionDevicePath(opener, metadata, block_device, super_device,
-                                                    &dev_string)) {
+                if (!GetPhysicalPartitionDevicePath(*params.partition_opener, *params.metadata,
+                                                    block_device, super_device, &dev_string)) {
                     LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
                     return false;
                 }
@@ -111,12 +111,21 @@
         }
         sector += extent.num_sectors;
     }
-    if (partition.attributes & LP_PARTITION_ATTR_READONLY) {
+    if (params.partition->attributes & LP_PARTITION_ATTR_READONLY) {
         table->set_readonly(true);
     }
+    if (params.force_writable) {
+        table->set_readonly(false);
+    }
     return true;
 }
 
+bool CreateDmTable(CreateLogicalPartitionParams params, DmTable* table) {
+    CreateLogicalPartitionParams::OwnedData owned_data;
+    if (!params.InitDefaults(&owned_data)) return false;
+    return CreateDmTableInternal(params, table);
+}
+
 bool CreateLogicalPartitions(const std::string& block_device) {
     uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
     auto metadata = ReadMetadata(block_device.c_str(), slot);
@@ -154,61 +163,74 @@
     return true;
 }
 
-bool CreateLogicalPartition(const CreateLogicalPartitionParams& params, std::string* path) {
-    const LpMetadata* metadata = params.metadata;
+bool CreateLogicalPartitionParams::InitDefaults(CreateLogicalPartitionParams::OwnedData* owned) {
+    if (block_device.empty()) {
+        LOG(ERROR) << "block_device is required for CreateLogicalPartition";
+        return false;
+    }
+
+    if (!partition_opener) {
+        owned->partition_opener = std::make_unique<PartitionOpener>();
+        partition_opener = owned->partition_opener.get();
+    }
 
     // Read metadata if needed.
-    std::unique_ptr<LpMetadata> local_metadata;
     if (!metadata) {
-        if (!params.metadata_slot) {
+        if (!metadata_slot) {
             LOG(ERROR) << "Either metadata or a metadata slot must be specified.";
             return false;
         }
-        auto slot = *params.metadata_slot;
-        if (local_metadata = ReadMetadata(params.block_device, slot); !local_metadata) {
-            LOG(ERROR) << "Could not read partition table for: " << params.block_device;
+        auto slot = *metadata_slot;
+        if (owned->metadata = ReadMetadata(*partition_opener, block_device, slot);
+            !owned->metadata) {
+            LOG(ERROR) << "Could not read partition table for: " << block_device;
             return false;
         }
-        metadata = local_metadata.get();
+        metadata = owned->metadata.get();
     }
 
     // Find the partition by name if needed.
-    const LpMetadataPartition* partition = params.partition;
     if (!partition) {
-        for (const auto& iter : metadata->partitions) {
-            if (GetPartitionName(iter) == params.partition_name) {
-                partition = &iter;
+        for (const auto& metadata_partition : metadata->partitions) {
+            if (android::fs_mgr::GetPartitionName(metadata_partition) == partition_name) {
+                partition = &metadata_partition;
                 break;
             }
         }
-        if (!partition) {
-            LERROR << "Could not find any partition with name: " << params.partition_name;
-            return false;
-        }
     }
-
-    PartitionOpener default_opener;
-    const IPartitionOpener* opener =
-            params.partition_opener ? params.partition_opener : &default_opener;
-
-    DmTable table;
-    if (!CreateDmTable(*opener, *metadata, *partition, params.block_device, &table)) {
+    if (!partition) {
+        LERROR << "Could not find any partition with name: " << partition_name;
         return false;
     }
-    if (params.force_writable) {
-        table.set_readonly(false);
+    if (partition_name.empty()) {
+        partition_name = android::fs_mgr::GetPartitionName(*partition);
+    } else if (partition_name != android::fs_mgr::GetPartitionName(*partition)) {
+        LERROR << "Inconsistent partition_name " << partition_name << " with partition "
+               << android::fs_mgr::GetPartitionName(*partition);
+        return false;
     }
 
-    std::string device_name = params.device_name;
     if (device_name.empty()) {
-        device_name = GetPartitionName(*partition);
+        device_name = partition_name;
+    }
+
+    return true;
+}
+
+bool CreateLogicalPartition(CreateLogicalPartitionParams params, std::string* path) {
+    CreateLogicalPartitionParams::OwnedData owned_data;
+    if (!params.InitDefaults(&owned_data)) return false;
+
+    DmTable table;
+    if (!CreateDmTableInternal(params, &table)) {
+        return false;
     }
 
     DeviceMapper& dm = DeviceMapper::Instance();
-    if (!dm.CreateDevice(device_name, table, path, params.timeout_ms)) {
+    if (!dm.CreateDevice(params.device_name, table, path, params.timeout_ms)) {
         return false;
     }
-    LINFO << "Created logical partition " << device_name << " on device " << *path;
+    LINFO << "Created logical partition " << params.device_name << " on device " << *path;
     return true;
 }
 
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index bc197cd..4dbacd7 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -706,10 +706,12 @@
 
 // For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
 // between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
-// /product and /system_ext. When they're skipped here, /system/product and /system/system_ext in
-// GSI will be used.
+// device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because
+// only common files for all targets can be put into system partition. It is under
+// /system/system_ext because GSI is a single system.img that includes the contents of system_ext
+// partition and product partition under /system/system_ext and /system/product, respectively.
 bool SkipMountingPartitions(Fstab* fstab) {
-    constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg";
+    constexpr const char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg";
 
     std::string skip_config;
     auto save_errno = errno;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index d7ea81d..358c980 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -713,7 +713,7 @@
     }
     report = report + ")=";
 
-    auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
+    auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
                      options.c_str());
     if (ret) {
         retval = false;
@@ -776,7 +776,7 @@
     entry.fs_type = mnt_type;
     if ((mnt_type == "f2fs") && !f2fs) entry.fs_type = "ext4";
     if ((mnt_type == "ext4") && !ext4) entry.fs_type = "f2fs";
-    entry.flags = MS_RELATIME;
+    entry.flags = MS_NOATIME;
     if (readonly) {
         entry.flags |= MS_RDONLY;
     } else {
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index 2054fa1..f3d09fb 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -81,14 +81,32 @@
     // Helpers for determining the effective partition and device name.
     std::string GetPartitionName() const;
     std::string GetDeviceName() const;
+
+    // Specify ownership of fields. The ownership of these fields are managed
+    // by the caller of InitDefaults().
+    // These are not declared in CreateLogicalPartitionParams so that the
+    // copy constructor is not deleted.
+    struct OwnedData {
+        std::unique_ptr<LpMetadata> metadata;
+        std::unique_ptr<IPartitionOpener> partition_opener;
+    };
+
+    // Fill in default values for |params| that CreateLogicalPartition assumes. Caller does
+    // not need to call this before calling CreateLogicalPartition; CreateLogicalPartition sets
+    // values when they are missing.
+    // Caller is responsible for destroying owned_data when |this| is not used.
+    bool InitDefaults(OwnedData* owned);
 };
 
-bool CreateLogicalPartition(const CreateLogicalPartitionParams& params, std::string* path);
+bool CreateLogicalPartition(CreateLogicalPartitionParams params, std::string* path);
 
 // Destroy the block device for a logical partition, by name. If |timeout_ms|
 // is non-zero, then this will block until the device path has been unlinked.
 bool DestroyLogicalPartition(const std::string& name);
 
+// Helper for populating a DmTable for a logical partition.
+bool CreateDmTable(CreateLogicalPartitionParams params, android::dm::DmTable* table);
+
 }  // namespace fs_mgr
 }  // namespace android
 
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index f0142bb..b2572f6 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -67,6 +67,9 @@
         "libfs_mgr",
         "liblp",
     ] + liblp_lib_deps,
+    header_libs: [
+        "libstorage_literals_headers",
+    ],
     stl: "libc++_static",
     srcs: [
         "builder_test.cpp",
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 8a74745..96068b1 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -40,12 +40,16 @@
     return true;
 }
 
+Interval LinearExtent::AsInterval() const {
+    return Interval(device_index(), physical_sector(), end_sector());
+}
+
 bool ZeroExtent::AddTo(LpMetadata* out) const {
     out->extents.emplace_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0, 0});
     return true;
 }
 
-Partition::Partition(const std::string& name, const std::string& group_name, uint32_t attributes)
+Partition::Partition(std::string_view name, std::string_view group_name, uint32_t attributes)
     : name_(name), group_name_(group_name), attributes_(attributes), size_(0) {}
 
 void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
@@ -96,6 +100,20 @@
     DCHECK(size_ == aligned_size);
 }
 
+Partition Partition::GetBeginningExtents(uint64_t aligned_size) const {
+    Partition p(name_, group_name_, attributes_);
+    for (const auto& extent : extents_) {
+        auto le = extent->AsLinearExtent();
+        if (le) {
+            p.AddExtent(std::make_unique<LinearExtent>(*le));
+        } else {
+            p.AddExtent(std::make_unique<ZeroExtent>(extent->num_sectors()));
+        }
+    }
+    p.ShrinkTo(aligned_size);
+    return p;
+}
+
 uint64_t Partition::BytesOnDisk() const {
     uint64_t sectors = 0;
     for (const auto& extent : extents_) {
@@ -426,7 +444,7 @@
     return true;
 }
 
-bool MetadataBuilder::AddGroup(const std::string& group_name, uint64_t maximum_size) {
+bool MetadataBuilder::AddGroup(std::string_view group_name, uint64_t maximum_size) {
     if (FindGroup(group_name)) {
         LERROR << "Group already exists: " << group_name;
         return false;
@@ -436,10 +454,10 @@
 }
 
 Partition* MetadataBuilder::AddPartition(const std::string& name, uint32_t attributes) {
-    return AddPartition(name, std::string(kDefaultGroup), attributes);
+    return AddPartition(name, kDefaultGroup, attributes);
 }
 
-Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& group_name,
+Partition* MetadataBuilder::AddPartition(std::string_view name, std::string_view group_name,
                                          uint32_t attributes) {
     if (name.empty()) {
         LERROR << "Partition must have a non-empty name.";
@@ -457,7 +475,7 @@
     return partitions_.back().get();
 }
 
-Partition* MetadataBuilder::FindPartition(const std::string& name) {
+Partition* MetadataBuilder::FindPartition(std::string_view name) {
     for (const auto& partition : partitions_) {
         if (partition->name() == name) {
             return partition.get();
@@ -486,7 +504,7 @@
     return total;
 }
 
-void MetadataBuilder::RemovePartition(const std::string& name) {
+void MetadataBuilder::RemovePartition(std::string_view name) {
     for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
         if ((*iter)->name() == name) {
             partitions_.erase(iter);
@@ -579,12 +597,42 @@
     return true;
 }
 
-bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
+Interval Interval::Intersect(const Interval& a, const Interval& b) {
+    Interval ret = a;
+    if (a.device_index != b.device_index) {
+        ret.start = ret.end = a.start;  // set length to 0 to indicate no intersection.
+        return ret;
+    }
+    ret.start = std::max(a.start, b.start);
+    ret.end = std::max(ret.start, std::min(a.end, b.end));
+    return ret;
+}
+
+std::vector<Interval> Interval::Intersect(const std::vector<Interval>& a,
+                                          const std::vector<Interval>& b) {
+    std::vector<Interval> ret;
+    for (const Interval& a_interval : a) {
+        for (const Interval& b_interval : b) {
+            auto intersect = Intersect(a_interval, b_interval);
+            if (intersect.length() > 0) ret.emplace_back(std::move(intersect));
+        }
+    }
+    return ret;
+}
+
+std::unique_ptr<Extent> Interval::AsExtent() const {
+    return std::make_unique<LinearExtent>(length(), device_index, start);
+}
+
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size,
+                                    const std::vector<Interval>& free_region_hint) {
     uint64_t space_needed = aligned_size - partition->size();
     uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
     DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
 
     std::vector<Interval> free_regions = GetFreeRegions();
+    if (!free_region_hint.empty())
+        free_regions = Interval::Intersect(free_regions, free_region_hint);
 
     const uint64_t sectors_per_block = geometry_.logical_block_size / LP_SECTOR_SIZE;
     CHECK_NE(sectors_per_block, 0);
@@ -650,7 +698,7 @@
     return true;
 }
 
-std::vector<MetadataBuilder::Interval> MetadataBuilder::PrioritizeSecondHalfOfSuper(
+std::vector<Interval> MetadataBuilder::PrioritizeSecondHalfOfSuper(
         const std::vector<Interval>& free_list) {
     const auto& super = block_devices_[0];
     uint64_t first_sector = super.first_logical_sector;
@@ -926,7 +974,8 @@
     return true;
 }
 
-bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size) {
+bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size,
+                                      const std::vector<Interval>& free_region_hint) {
     // Align the space needed up to the nearest sector.
     uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
     uint64_t old_size = partition->size();
@@ -936,7 +985,7 @@
     }
 
     if (aligned_size > old_size) {
-        if (!GrowPartition(partition, aligned_size)) {
+        if (!GrowPartition(partition, aligned_size, free_region_hint)) {
             return false;
         }
     } else if (aligned_size < partition->size()) {
@@ -958,7 +1007,7 @@
     return names;
 }
 
-void MetadataBuilder::RemoveGroupAndPartitions(const std::string& group_name) {
+void MetadataBuilder::RemoveGroupAndPartitions(std::string_view group_name) {
     if (group_name == kDefaultGroup) {
         // Cannot remove the default group.
         return;
@@ -1141,5 +1190,9 @@
                    : "";
 }
 
+uint64_t MetadataBuilder::logical_block_size() const {
+    return geometry_.logical_block_size;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index c5b4047..a67ffa7 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -17,11 +17,13 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
+#include <storage_literals/storage_literals.h>
 
 #include "liblp_test.h"
 #include "utility.h"
 
 using namespace std;
+using namespace android::storage_literals;
 using namespace android::fs_mgr;
 using namespace android::fs_mgr::testing;
 using ::testing::_;
@@ -591,13 +593,6 @@
     ASSERT_NE(builder->Export(), nullptr);
 }
 
-constexpr unsigned long long operator"" _GiB(unsigned long long x) {  // NOLINT
-    return x << 30;
-}
-constexpr unsigned long long operator"" _MiB(unsigned long long x) {  // NOLINT
-    return x << 20;
-}
-
 TEST_F(BuilderTest, RemoveAndAddFirstPartition) {
     auto builder = MetadataBuilder::New(10_GiB, 65536, 2);
     ASSERT_NE(nullptr, builder);
@@ -887,3 +882,38 @@
     std::set<std::string> partitions_to_keep{"system_a", "vendor_a", "product_a"};
     ASSERT_TRUE(builder->ImportPartitions(*on_disk.get(), partitions_to_keep));
 }
+
+// Interval has operator< defined; it is not appropriate to re-define Interval::operator== that
+// compares device index.
+namespace android {
+namespace fs_mgr {
+bool operator==(const Interval& a, const Interval& b) {
+    return a.device_index == b.device_index && a.start == b.start && a.end == b.end;
+}
+}  // namespace fs_mgr
+}  // namespace android
+
+TEST_F(BuilderTest, Interval) {
+    EXPECT_EQ(0u, Interval::Intersect(Interval(0, 100, 200), Interval(0, 50, 100)).length());
+    EXPECT_EQ(Interval(0, 100, 150),
+              Interval::Intersect(Interval(0, 100, 200), Interval(0, 50, 150)));
+    EXPECT_EQ(Interval(0, 100, 200),
+              Interval::Intersect(Interval(0, 100, 200), Interval(0, 50, 200)));
+    EXPECT_EQ(Interval(0, 100, 200),
+              Interval::Intersect(Interval(0, 100, 200), Interval(0, 50, 250)));
+    EXPECT_EQ(Interval(0, 100, 200),
+              Interval::Intersect(Interval(0, 100, 200), Interval(0, 100, 200)));
+    EXPECT_EQ(Interval(0, 150, 200),
+              Interval::Intersect(Interval(0, 100, 200), Interval(0, 150, 250)));
+    EXPECT_EQ(0u, Interval::Intersect(Interval(0, 100, 200), Interval(0, 200, 250)).length());
+
+    auto v = Interval::Intersect(std::vector<Interval>{Interval(0, 0, 50), Interval(0, 100, 150)},
+                                 std::vector<Interval>{Interval(0, 25, 125)});
+    ASSERT_EQ(2, v.size());
+    EXPECT_EQ(Interval(0, 25, 50), v[0]);
+    EXPECT_EQ(Interval(0, 100, 125), v[1]);
+
+    EXPECT_EQ(0u, Interval::Intersect(std::vector<Interval>{Interval(0, 0, 50)},
+                                      std::vector<Interval>{Interval(0, 100, 150)})
+                          .size());
+}
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 58a88b5..6b842b3 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -17,6 +17,7 @@
 #include "images.h"
 
 #include <limits.h>
+#include <sys/stat.h>
 
 #include <android-base/file.h>
 
@@ -27,12 +28,45 @@
 namespace android {
 namespace fs_mgr {
 
+using android::base::borrowed_fd;
 using android::base::unique_fd;
 
 #if defined(_WIN32)
 static const int O_NOFOLLOW = 0;
 #endif
 
+static bool IsEmptySuperImage(borrowed_fd fd) {
+    struct stat s;
+    if (fstat(fd.get(), &s) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " fstat failed";
+        return false;
+    }
+    if (s.st_size < LP_METADATA_GEOMETRY_SIZE) {
+        return false;
+    }
+
+    // Rewind back to the start, read the geometry struct.
+    LpMetadataGeometry geometry = {};
+    if (SeekFile64(fd.get(), 0, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed";
+        return false;
+    }
+    if (!android::base::ReadFully(fd, &geometry, sizeof(geometry))) {
+        PERROR << __PRETTY_FUNCTION__ << " read failed";
+        return false;
+    }
+    return geometry.magic == LP_METADATA_GEOMETRY_MAGIC;
+}
+
+bool IsEmptySuperImage(const std::string& file) {
+    unique_fd fd = GetControlFileOrOpen(file, O_RDONLY | O_CLOEXEC);
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " open failed";
+        return false;
+    }
+    return IsEmptySuperImage(fd);
+}
+
 std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {
     std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
     if (SeekFile64(fd, 0, SEEK_SET) < 0) {
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index c3f6e91..4d7f81d 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -33,10 +33,11 @@
 namespace fs_mgr {
 
 class LinearExtent;
+struct Interval;
 
 // By default, partitions are aligned on a 1MiB boundary.
-static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
-static const uint32_t kDefaultBlockSize = 4096;
+static constexpr uint32_t kDefaultPartitionAlignment = 1024 * 1024;
+static constexpr uint32_t kDefaultBlockSize = 4096;
 
 // Name of the default group in a metadata.
 static constexpr std::string_view kDefaultGroup = "default";
@@ -74,6 +75,8 @@
         return sector >= physical_sector_ && sector < end_sector();
     }
 
+    Interval AsInterval() const;
+
   private:
     uint32_t device_index_;
     uint64_t physical_sector_;
@@ -91,7 +94,7 @@
     friend class MetadataBuilder;
 
   public:
-    explicit PartitionGroup(const std::string& name, uint64_t maximum_size)
+    explicit PartitionGroup(std::string_view name, uint64_t maximum_size)
         : name_(name), maximum_size_(maximum_size) {}
 
     const std::string& name() const { return name_; }
@@ -108,7 +111,7 @@
     friend class MetadataBuilder;
 
   public:
-    Partition(const std::string& name, const std::string& group_name, uint32_t attributes);
+    Partition(std::string_view name, std::string_view group_name, uint32_t attributes);
 
     // Add a raw extent.
     void AddExtent(std::unique_ptr<Extent>&& extent);
@@ -123,9 +126,16 @@
     const std::string& name() const { return name_; }
     const std::string& group_name() const { return group_name_; }
     uint32_t attributes() const { return attributes_; }
+    void set_attributes(uint32_t attributes) { attributes_ = attributes; }
     const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
     uint64_t size() const { return size_; }
 
+    // Return a copy of *this, but with extents that includes only the first
+    // |aligned_size| bytes. |aligned_size| should be aligned to
+    // logical_block_size() of the MetadataBuilder that this partition belongs
+    // to.
+    Partition GetBeginningExtents(uint64_t aligned_size) const;
+
   private:
     void ShrinkTo(uint64_t aligned_size);
     void set_group_name(std::string_view group_name) { group_name_ = group_name; }
@@ -137,6 +147,35 @@
     uint64_t size_;
 };
 
+// An interval in the metadata. This is similar to a LinearExtent with one difference.
+// LinearExtent represents a "used" region in the metadata, while Interval can also represent
+// an "unused" region.
+struct Interval {
+    uint32_t device_index;
+    uint64_t start;
+    uint64_t end;
+
+    Interval(uint32_t device_index, uint64_t start, uint64_t end)
+        : device_index(device_index), start(start), end(end) {}
+    uint64_t length() const { return end - start; }
+
+    // Note: the device index is not included in sorting (intervals are
+    // sorted in per-device lists).
+    bool operator<(const Interval& other) const {
+        return (start == other.start) ? end < other.end : start < other.start;
+    }
+
+    std::unique_ptr<Extent> AsExtent() const;
+
+    // Intersect |a| with |b|.
+    // If no intersection, result has 0 length().
+    static Interval Intersect(const Interval& a, const Interval& b);
+
+    // Intersect two lists of intervals, and store result to |a|.
+    static std::vector<Interval> Intersect(const std::vector<Interval>& a,
+                                           const std::vector<Interval>& b);
+};
+
 class MetadataBuilder {
   public:
     // Construct an empty logical partition table builder given the specified
@@ -205,7 +244,7 @@
     // total space used by all partitions in the group.
     //
     // This can fail and return false if the group already exists.
-    bool AddGroup(const std::string& group_name, uint64_t maximum_size);
+    bool AddGroup(std::string_view group_name, uint64_t maximum_size);
 
     // Export metadata so it can be serialized to an image, to disk, or mounted
     // via device-mapper.
@@ -213,7 +252,7 @@
 
     // Add a partition, returning a handle so it can be sized as needed. If a
     // partition with the given name already exists, nullptr is returned.
-    Partition* AddPartition(const std::string& name, const std::string& group_name,
+    Partition* AddPartition(std::string_view name, std::string_view group_name,
                             uint32_t attributes);
 
     // Same as AddPartition above, but uses the default partition group which
@@ -221,10 +260,10 @@
     Partition* AddPartition(const std::string& name, uint32_t attributes);
 
     // Delete a partition by name if it exists.
-    void RemovePartition(const std::string& name);
+    void RemovePartition(std::string_view name);
 
     // Find a partition by name. If no partition is found, nullptr is returned.
-    Partition* FindPartition(const std::string& name);
+    Partition* FindPartition(std::string_view name);
 
     // Find a group by name. If no group is found, nullptr is returned.
     PartitionGroup* FindGroup(std::string_view name);
@@ -243,7 +282,11 @@
     //
     // Note, this is an in-memory operation, and it does not alter the
     // underlying filesystem or contents of the partition on disk.
-    bool ResizePartition(Partition* partition, uint64_t requested_size);
+    //
+    // If |free_region_hint| is not empty, it will only try to allocate extents
+    // in regions within the list.
+    bool ResizePartition(Partition* partition, uint64_t requested_size,
+                         const std::vector<Interval>& free_region_hint = {});
 
     // Return the list of partitions belonging to a group.
     std::vector<Partition*> ListPartitionsInGroup(const std::string& group_name);
@@ -268,7 +311,7 @@
     std::vector<std::string> ListGroups() const;
 
     // Remove all partitions belonging to a group, then remove the group.
-    void RemoveGroupAndPartitions(const std::string& group_name);
+    void RemoveGroupAndPartitions(std::string_view group_name);
 
     // Set the LP_METADATA_AUTO_SLOT_SUFFIXING flag.
     void SetAutoSlotSuffixing();
@@ -290,6 +333,11 @@
     // Return the name of the block device at |index|.
     std::string GetBlockDevicePartitionName(uint64_t index) const;
 
+    // Return the list of free regions not occupied by extents in the metadata.
+    std::vector<Interval> GetFreeRegions() const;
+
+    uint64_t logical_block_size() const;
+
   private:
     MetadataBuilder();
     MetadataBuilder(const MetadataBuilder&) = delete;
@@ -299,7 +347,8 @@
     bool Init(const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,
               uint32_t metadata_max_size, uint32_t metadata_slot_count);
     bool Init(const LpMetadata& metadata);
-    bool GrowPartition(Partition* partition, uint64_t aligned_size);
+    bool GrowPartition(Partition* partition, uint64_t aligned_size,
+                       const std::vector<Interval>& free_region_hint);
     void ShrinkPartition(Partition* partition, uint64_t aligned_size);
     uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const;
     uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
@@ -322,22 +371,6 @@
 
     bool ValidatePartitionGroups() const;
 
-    struct Interval {
-        uint32_t device_index;
-        uint64_t start;
-        uint64_t end;
-
-        Interval(uint32_t device_index, uint64_t start, uint64_t end)
-            : device_index(device_index), start(start), end(end) {}
-        uint64_t length() const { return end - start; }
-
-        // Note: the device index is not included in sorting (intervals are
-        // sorted in per-device lists).
-        bool operator<(const Interval& other) const {
-            return (start == other.start) ? end < other.end : start < other.start;
-        }
-    };
-    std::vector<Interval> GetFreeRegions() const;
     bool IsAnyRegionCovered(const std::vector<Interval>& regions,
                             const LinearExtent& candidate) const;
     bool IsAnyRegionAllocated(const LinearExtent& candidate) const;
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 135a1b3..cd860cd 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -70,8 +70,15 @@
                           uint32_t slot_number);
 std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number);
 
+// Returns whether an image is an "empty" image or not. An empty image contains
+// only metadata. Unlike a flashed block device, there are no reserved bytes or
+// backup sections, and only one slot is stored (even if multiple slots are
+// supported). It is a format specifically for storing only metadata.
+bool IsEmptySuperImage(const std::string& file);
+
 // Read/Write logical partition metadata to an image file, for diagnostics or
-// flashing.
+// flashing. If no partition images are specified, the file will be in the
+// empty format.
 bool WriteToImageFile(const std::string& file, const LpMetadata& metadata, uint32_t block_size,
                       const std::map<std::string, std::string>& images, bool sparsify);
 bool WriteToImageFile(const std::string& file, const LpMetadata& metadata);
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index afcce8f..48c5c83 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -205,9 +205,9 @@
 #endif
 }
 
-base::unique_fd GetControlFileOrOpen(const char* path, int flags) {
+base::unique_fd GetControlFileOrOpen(std::string_view path, int flags) {
 #if defined(__ANDROID__)
-    int fd = android_get_control_file(path);
+    int fd = android_get_control_file(path.data());
     if (fd >= 0) {
         int newfd = TEMP_FAILURE_RETRY(dup(fd));
         if (newfd >= 0) {
@@ -216,7 +216,7 @@
         PERROR << "Cannot dup fd for already controlled file: " << path << ", reopening...";
     }
 #endif
-    return base::unique_fd(open(path, flags));
+    return base::unique_fd(open(path.data(), flags));
 }
 
 bool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 25ab66b..0661769 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -21,6 +21,9 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <string>
+#include <string_view>
+
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 
@@ -94,7 +97,7 @@
 // Call BLKROSET ioctl on fd so that fd is readonly / read-writable.
 bool SetBlockReadonly(int fd, bool readonly);
 
-::android::base::unique_fd GetControlFileOrOpen(const char* path, int flags);
+::android::base::unique_fd GetControlFileOrOpen(std::string_view path, int flags);
 
 // For Virtual A/B updates, modify |metadata| so that it can be written to |target_slot_number|.
 bool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index a54db58..f73b189 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -25,10 +25,12 @@
     shared_libs: [
         "libbase",
         "liblog",
+        "liblp",
     ],
     static_libs: [
         "libdm",
         "libfs_mgr",
+        "libfstab",
         "liblp",
     ],
     whole_static_libs: [
@@ -49,9 +51,17 @@
     name: "libsnapshot_sources",
     srcs: [
         "snapshot.cpp",
+        "partition_cow_creator.cpp",
+        "utility.cpp",
     ],
 }
 
+cc_library_headers {
+    name: "libsnapshot_headers",
+    recovery_available: true,
+    defaults: ["libsnapshot_defaults"],
+}
+
 cc_library_static {
     name: "libsnapshot",
     defaults: ["libsnapshot_defaults"],
@@ -76,6 +86,7 @@
     defaults: ["libsnapshot_defaults"],
     srcs: [
         "snapshot_test.cpp",
+        "partition_cow_creator_test.cpp",
         "test_helpers.cpp",
     ],
     shared_libs: [
@@ -89,5 +100,10 @@
         "libgmock",
         "liblp",
         "libsnapshot",
+        "libsparse",
+        "libz",
+    ],
+    header_libs: [
+        "libstorage_literals_headers",
     ],
 }
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index b982b4b..6bc09a1 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -17,13 +17,17 @@
 #include <stdint.h>
 
 #include <chrono>
+#include <map>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include <android-base/unique_fd.h>
+#include <fs_mgr_dm_linear.h>
 #include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
+#include <liblp/builder.h>
 #include <liblp/liblp.h>
 
 #ifndef FRIEND_TEST
@@ -45,6 +49,13 @@
 
 namespace snapshot {
 
+struct AutoDeleteCowImage;
+struct AutoDeleteSnapshot;
+struct PartitionCowCreator;
+struct AutoDeviceList;
+
+static constexpr const std::string_view kCowGroupName = "cow";
+
 enum class UpdateState : unsigned int {
     // No update or merge is in progress.
     None,
@@ -66,13 +77,18 @@
     MergeCompleted,
 
     // Merging failed due to an unrecoverable error.
-    MergeFailed
+    MergeFailed,
+
+    // The update was implicitly cancelled, either by a rollback or a flash
+    // operation via fastboot. This state can only be returned by WaitForMerge.
+    Cancelled
 };
 
 class SnapshotManager final {
     using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
-    using LpMetadata = android::fs_mgr::LpMetadata;
     using IPartitionOpener = android::fs_mgr::IPartitionOpener;
+    using LpMetadata = android::fs_mgr::LpMetadata;
+    using MetadataBuilder = android::fs_mgr::MetadataBuilder;
 
   public:
     // Dependency injection for testing.
@@ -82,7 +98,9 @@
         virtual std::string GetGsidDir() const = 0;
         virtual std::string GetMetadataDir() const = 0;
         virtual std::string GetSlotSuffix() const = 0;
+        virtual std::string GetSuperDevice(uint32_t slot) const = 0;
         virtual const IPartitionOpener& GetPartitionOpener() const = 0;
+        virtual bool IsOverlayfsSetup() const = 0;
     };
 
     ~SnapshotManager();
@@ -117,12 +135,15 @@
     // update has been marked successful after booting.
     bool InitiateMerge();
 
-    // Wait for the current merge to finish, then perform cleanup when it
-    // completes. It is necessary to call this after InitiateMerge(), or when
-    // a merge state is detected during boot.
+    // Perform any necessary post-boot actions. This should be run soon after
+    // /data is mounted.
     //
-    // Note that after calling WaitForMerge(), GetUpdateState() may still return
-    // that a merge is in progress:
+    // If a merge is in progress, this function will block until the merge is
+    // completed. If a merge or update was cancelled, this will clean up any
+    // update artifacts and return.
+    //
+    // Note that after calling this, GetUpdateState() may still return that a
+    // merge is in progress:
     //   MergeFailed indicates that a fatal error occurred. WaitForMerge() may
     //   called any number of times again to attempt to make more progress, but
     //   we do not expect it to succeed if a catastrophic error occurred.
@@ -135,7 +156,7 @@
     //
     //   MergeCompleted indicates that the update has fully completed.
     //   GetUpdateState will return None, and a new update can begin.
-    UpdateState WaitForMerge();
+    UpdateState ProcessUpdateState();
 
     // Find the status of the current update, if any.
     //
@@ -145,6 +166,20 @@
     //   Other: 0
     UpdateState GetUpdateState(double* progress = nullptr);
 
+    // Create necessary COW device / files for OTA clients. New logical partitions will be added to
+    // group "cow" in target_metadata. Regions of partitions of current_metadata will be
+    // "write-protected" and snapshotted.
+    bool CreateUpdateSnapshots(MetadataBuilder* target_metadata, const std::string& target_suffix,
+                               MetadataBuilder* current_metadata, const std::string& current_suffix,
+                               const std::map<std::string, uint64_t>& cow_sizes);
+
+    // Map a snapshotted partition for OTA clients to write to. Write-protected regions are
+    // determined previously in CreateSnapshots.
+    bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params, std::string* snapshot_path);
+
+    // Unmap a snapshot device that's previously mapped with MapUpdateSnapshot.
+    bool UnmapUpdateSnapshot(const std::string& target_partition_name);
+
     // If this returns true, first-stage mount must call
     // CreateLogicalAndSnapshotPartitions rather than CreateLogicalPartitions.
     bool NeedSnapshotsInFirstStageMount();
@@ -158,13 +193,19 @@
     FRIEND_TEST(SnapshotTest, CreateSnapshot);
     FRIEND_TEST(SnapshotTest, FirstStageMountAfterRollback);
     FRIEND_TEST(SnapshotTest, FirstStageMountAndMerge);
+    FRIEND_TEST(SnapshotTest, FlashSuperDuringMerge);
     FRIEND_TEST(SnapshotTest, FlashSuperDuringUpdate);
     FRIEND_TEST(SnapshotTest, MapPartialSnapshot);
     FRIEND_TEST(SnapshotTest, MapSnapshot);
     FRIEND_TEST(SnapshotTest, Merge);
     FRIEND_TEST(SnapshotTest, MergeCannotRemoveCow);
     FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
+    FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
     friend class SnapshotTest;
+    friend class SnapshotUpdateTest;
+    friend struct AutoDeleteCowImage;
+    friend struct AutoDeleteSnapshot;
+    friend struct PartitionCowCreator;
 
     using DmTargetSnapshot = android::dm::DmTargetSnapshot;
     using IImageManager = android::fiemap::IImageManager;
@@ -202,25 +243,43 @@
     std::unique_ptr<LockedFile> OpenFile(const std::string& file, int open_flags, int lock_flags);
     bool Truncate(LockedFile* file);
 
+    enum class SnapshotState : int { None, Created, Merging, MergeCompleted };
+    static std::string to_string(SnapshotState state);
+
+    // This state is persisted per-snapshot in /metadata/ota/snapshots/.
+    struct SnapshotStatus {
+        SnapshotState state = SnapshotState::None;
+        uint64_t device_size = 0;
+        uint64_t snapshot_size = 0;
+        uint64_t cow_partition_size = 0;
+        uint64_t cow_file_size = 0;
+
+        // These are non-zero when merging.
+        uint64_t sectors_allocated = 0;
+        uint64_t metadata_sectors = 0;
+    };
+
     // Create a new snapshot record. This creates the backing COW store and
     // persists information needed to map the device. The device can be mapped
     // with MapSnapshot().
     //
-    // |device_size| should be the size of the base_device that will be passed
-    // via MapDevice(). |snapshot_size| should be the number of bytes in the
-    // base device, starting from 0, that will be snapshotted. The cow_size
+    // |status|.device_size should be the size of the base_device that will be passed
+    // via MapDevice(). |status|.snapshot_size should be the number of bytes in the
+    // base device, starting from 0, that will be snapshotted. |status|.cow_file_size
     // should be the amount of space that will be allocated to store snapshot
     // deltas.
     //
-    // If |snapshot_size| < device_size, then the device will always
+    // If |status|.snapshot_size < |status|.device_size, then the device will always
     // be mapped with two table entries: a dm-snapshot range covering
     // snapshot_size, and a dm-linear range covering the remainder.
     //
-    // All sizes are specified in bytes, and the device and snapshot sizes
-    // must be a multiple of the sector size (512 bytes). |cow_size| will
-    // be rounded up to the nearest sector.
-    bool CreateSnapshot(LockedFile* lock, const std::string& name, uint64_t device_size,
-                        uint64_t snapshot_size, uint64_t cow_size);
+    // All sizes are specified in bytes, and the device, snapshot, COW partition and COW file sizes
+    // must be a multiple of the sector size (512 bytes).
+    bool CreateSnapshot(LockedFile* lock, const std::string& name, SnapshotStatus status);
+
+    // |name| should be the base partition name (e.g. "system_a"). Create the
+    // backing COW image using the size previously passed to CreateSnapshot().
+    bool CreateCowImage(LockedFile* lock, const std::string& name);
 
     // Map a snapshot device that was previously created with CreateSnapshot.
     // If a merge was previously initiated, the device-mapper table will have a
@@ -230,21 +289,36 @@
     // timeout_ms is 0, then no wait will occur and |dev_path| may not yet
     // exist on return.
     bool MapSnapshot(LockedFile* lock, const std::string& name, const std::string& base_device,
-                     const std::chrono::milliseconds& timeout_ms, std::string* dev_path);
+                     const std::string& cow_device, const std::chrono::milliseconds& timeout_ms,
+                     std::string* dev_path);
 
-    // Remove the backing copy-on-write image for the named snapshot. The
+    // Map a COW image that was previous created with CreateCowImage.
+    bool MapCowImage(const std::string& name, const std::chrono::milliseconds& timeout_ms);
+
+    // Remove the backing copy-on-write image and snapshot states for the named snapshot. The
     // caller is responsible for ensuring that the snapshot is unmapped.
     bool DeleteSnapshot(LockedFile* lock, const std::string& name);
 
     // Unmap a snapshot device previously mapped with MapSnapshotDevice().
     bool UnmapSnapshot(LockedFile* lock, const std::string& name);
 
+    // Unmap a COW image device previously mapped with MapCowImage().
+    bool UnmapCowImage(const std::string& name);
+
     // Unmap and remove all known snapshots.
     bool RemoveAllSnapshots(LockedFile* lock);
 
     // List the known snapshot names.
     bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots);
 
+    // Check for a cancelled or rolled back merge, returning true if such a
+    // condition was detected and handled.
+    bool HandleCancelledUpdate(LockedFile* lock);
+
+    // Remove artifacts created by the update process, such as snapshots, and
+    // set the update state to None.
+    bool RemoveAllUpdateState(LockedFile* lock);
+
     // Interact with /metadata/ota/state.
     std::unique_ptr<LockedFile> OpenStateFile(int open_flags, int lock_flags);
     std::unique_ptr<LockedFile> LockShared();
@@ -253,25 +327,13 @@
     bool WriteUpdateState(LockedFile* file, UpdateState state);
     std::string GetStateFilePath() const;
 
-    enum class SnapshotState : int { Created, Merging, MergeCompleted };
-    static std::string to_string(SnapshotState state);
-
-    // This state is persisted per-snapshot in /metadata/ota/snapshots/.
-    struct SnapshotStatus {
-        SnapshotState state;
-        uint64_t device_size;
-        uint64_t snapshot_size;
-        // These are non-zero when merging.
-        uint64_t sectors_allocated = 0;
-        uint64_t metadata_sectors = 0;
-    };
-
     // Helpers for merging.
     bool SwitchSnapshotToMerge(LockedFile* lock, const std::string& name);
     bool RewriteSnapshotDeviceTable(const std::string& dm_name);
     bool MarkSnapshotMergeCompleted(LockedFile* snapshot_lock, const std::string& snapshot_name);
     void AcknowledgeMergeSuccess(LockedFile* lock);
     void AcknowledgeMergeFailure();
+    bool IsCancelledSnapshot(const std::string& snapshot_name);
 
     // Note that these require the name of the device containing the snapshot,
     // which may be the "inner" device. Use GetsnapshotDeviecName().
@@ -308,6 +370,28 @@
     std::string GetSnapshotDeviceName(const std::string& snapshot_name,
                                       const SnapshotStatus& status);
 
+    // Map the base device, COW devices, and snapshot device.
+    bool MapPartitionWithSnapshot(LockedFile* lock, CreateLogicalPartitionParams params,
+                                  std::string* path);
+
+    // Map the COW devices, including the partition in super and the images.
+    // |params|:
+    //    - |partition_name| should be the name of the top-level partition (e.g. system_b),
+    //            not system_b-cow-img
+    //    - |device_name| and |partition| is ignored
+    //    - |timeout_ms| and the rest is respected
+    // Return the path in |cow_device_path| (e.g. /dev/block/dm-1) and major:minor in
+    // |cow_device_string|
+    bool MapCowDevices(LockedFile* lock, const CreateLogicalPartitionParams& params,
+                       const SnapshotStatus& snapshot_status, AutoDeviceList* created_devices,
+                       std::string* cow_name);
+
+    // The reverse of MapCowDevices.
+    bool UnmapCowDevices(LockedFile* lock, const std::string& name);
+
+    // The reverse of MapPartitionWithSnapshot.
+    bool UnmapPartitionWithSnapshot(LockedFile* lock, const std::string& target_partition_name);
+
     std::string gsid_dir_;
     std::string metadata_dir_;
     std::unique_ptr<IDeviceInfo> device_;
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
new file mode 100644
index 0000000..3163e61
--- /dev/null
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -0,0 +1,176 @@
+// 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 "partition_cow_creator.h"
+
+#include <math.h>
+
+#include <android-base/logging.h>
+
+#include "utility.h"
+
+using android::dm::kSectorSize;
+using android::fs_mgr::Extent;
+using android::fs_mgr::Interval;
+using android::fs_mgr::kDefaultBlockSize;
+using android::fs_mgr::Partition;
+
+namespace android {
+namespace snapshot {
+
+// Round |d| up to a multiple of |block_size|.
+static uint64_t RoundUp(double d, uint64_t block_size) {
+    uint64_t ret = ((uint64_t)ceil(d) + block_size - 1) / block_size * block_size;
+    CHECK(ret >= d) << "Can't round " << d << " up to a multiple of " << block_size;
+    return ret;
+}
+
+// Intersect two linear extents. If no intersection, return an extent with length 0.
+static std::unique_ptr<Extent> Intersect(Extent* target_extent, Extent* existing_extent) {
+    // Convert target_extent and existing_extent to linear extents. Zero extents
+    // doesn't matter and doesn't result in any intersection.
+    auto existing_linear_extent = existing_extent->AsLinearExtent();
+    if (!existing_linear_extent) return nullptr;
+
+    auto target_linear_extent = target_extent->AsLinearExtent();
+    if (!target_linear_extent) return nullptr;
+
+    return Interval::Intersect(target_linear_extent->AsInterval(),
+                               existing_linear_extent->AsInterval())
+            .AsExtent();
+}
+
+// Check that partition |p| contains |e| fully. Both of them should
+// be from |target_metadata|.
+// Returns true as long as |e| is a subrange of any extent of |p|.
+bool PartitionCowCreator::HasExtent(Partition* p, Extent* e) {
+    for (auto& partition_extent : p->extents()) {
+        auto intersection = Intersect(partition_extent.get(), e);
+        if (intersection != nullptr && intersection->num_sectors() == e->num_sectors()) {
+            return true;
+        }
+    }
+    return false;
+}
+
+// Return the number of sectors, N, where |target_partition|[0..N] (from
+// |target_metadata|) are the sectors that should be snapshotted. N is computed
+// so that this range of sectors are used by partitions in |current_metadata|.
+//
+// The client code (update_engine) should have computed target_metadata by
+// resizing partitions of current_metadata, so only the first N sectors should
+// be snapshotted, not a range with start index != 0.
+//
+// Note that if partition A has shrunk and partition B has grown, the new
+// extents of partition B may use the empty space that was used by partition A.
+// In this case, that new extent cannot be written directly, as it may be used
+// by the running system. Hence, all extents of the new partition B must be
+// intersected with all old partitions (including old partition A and B) to get
+// the region that needs to be snapshotted.
+std::optional<uint64_t> PartitionCowCreator::GetSnapshotSize() {
+    // Compute the number of sectors that needs to be snapshotted.
+    uint64_t snapshot_sectors = 0;
+    std::vector<std::unique_ptr<Extent>> intersections;
+    for (const auto& extent : target_partition->extents()) {
+        for (auto* existing_partition :
+             ListPartitionsWithSuffix(current_metadata, current_suffix)) {
+            for (const auto& existing_extent : existing_partition->extents()) {
+                auto intersection = Intersect(extent.get(), existing_extent.get());
+                if (intersection != nullptr && intersection->num_sectors() > 0) {
+                    snapshot_sectors += intersection->num_sectors();
+                    intersections.emplace_back(std::move(intersection));
+                }
+            }
+        }
+    }
+    uint64_t snapshot_size = snapshot_sectors * kSectorSize;
+
+    // Sanity check that all recorded intersections are indeed within
+    // target_partition[0..snapshot_sectors].
+    Partition target_partition_snapshot = target_partition->GetBeginningExtents(snapshot_size);
+    for (const auto& intersection : intersections) {
+        if (!HasExtent(&target_partition_snapshot, intersection.get())) {
+            auto linear_intersection = intersection->AsLinearExtent();
+            LOG(ERROR) << "Extent "
+                       << (linear_intersection
+                                   ? (std::to_string(linear_intersection->physical_sector()) + "," +
+                                      std::to_string(linear_intersection->end_sector()))
+                                   : "")
+                       << " is not part of Partition " << target_partition->name() << "[0.."
+                       << snapshot_size
+                       << "]. The metadata wasn't constructed correctly. This should not happen.";
+            return std::nullopt;
+        }
+    }
+
+    return snapshot_size;
+}
+
+std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
+    static constexpr double kCowEstimateFactor = 1.05;
+
+    CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
+          target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);
+
+    uint64_t logical_block_size = current_metadata->logical_block_size();
+    CHECK(logical_block_size != 0 && !(logical_block_size & (logical_block_size - 1)))
+            << "logical_block_size is not power of 2";
+
+    Return ret;
+    ret.snapshot_status.device_size = target_partition->size();
+
+    auto snapshot_size = GetSnapshotSize();
+    if (!snapshot_size.has_value()) return std::nullopt;
+
+    ret.snapshot_status.snapshot_size = *snapshot_size;
+
+    // TODO: always read from cow_size when the COW size is written in
+    // update package. kCowEstimateFactor is good for prototyping but
+    // we can't use that in production.
+    if (!cow_size.has_value()) {
+        cow_size =
+                RoundUp(ret.snapshot_status.snapshot_size * kCowEstimateFactor, kDefaultBlockSize);
+    }
+
+    // Compute regions that are free in both current and target metadata. These are the regions
+    // we can use for COW partition.
+    auto target_free_regions = target_metadata->GetFreeRegions();
+    auto current_free_regions = current_metadata->GetFreeRegions();
+    auto free_regions = Interval::Intersect(target_free_regions, current_free_regions);
+    uint64_t free_region_length = 0;
+    for (const auto& interval : free_regions) {
+        free_region_length += interval.length() * kSectorSize;
+    }
+
+    LOG(INFO) << "Remaining free space for COW: " << free_region_length << " bytes";
+
+    // Compute the COW partition size.
+    ret.snapshot_status.cow_partition_size = std::min(*cow_size, free_region_length);
+    // Round it down to the nearest logical block. Logical partitions must be a multiple
+    // of logical blocks.
+    ret.snapshot_status.cow_partition_size &= ~(logical_block_size - 1);
+    // Assign cow_partition_usable_regions to indicate what regions should the COW partition uses.
+    ret.cow_partition_usable_regions = std::move(free_regions);
+
+    // The rest of the COW space is allocated on ImageManager.
+    ret.snapshot_status.cow_file_size = (*cow_size) - ret.snapshot_status.cow_partition_size;
+    // Round it up to the nearest sector.
+    ret.snapshot_status.cow_file_size += kSectorSize - 1;
+    ret.snapshot_status.cow_file_size &= ~(kSectorSize - 1);
+
+    return ret;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.h b/fs_mgr/libsnapshot/partition_cow_creator.h
new file mode 100644
index 0000000..a235914
--- /dev/null
+++ b/fs_mgr/libsnapshot/partition_cow_creator.h
@@ -0,0 +1,63 @@
+// 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 <optional>
+#include <string>
+
+#include <liblp/builder.h>
+
+#include <libsnapshot/snapshot.h>
+
+namespace android {
+namespace snapshot {
+
+// Helper class that creates COW for a partition.
+struct PartitionCowCreator {
+    using Extent = android::fs_mgr::Extent;
+    using Interval = android::fs_mgr::Interval;
+    using MetadataBuilder = android::fs_mgr::MetadataBuilder;
+    using Partition = android::fs_mgr::Partition;
+
+    // The metadata that will be written to target metadata slot.
+    MetadataBuilder* target_metadata;
+    // The suffix of the target slot.
+    std::string target_suffix;
+    // The partition in target_metadata that needs to be snapshotted.
+    Partition* target_partition;
+    // The metadata at the current slot (that would be used if the device boots
+    // normally). This is used to determine which extents are being used.
+    MetadataBuilder* current_metadata;
+    // The suffix of the current slot.
+    std::string current_suffix;
+    // The COW size given by client code.
+    std::optional<uint64_t> cow_size;
+
+    struct Return {
+        SnapshotManager::SnapshotStatus snapshot_status;
+        std::vector<Interval> cow_partition_usable_regions;
+    };
+
+    std::optional<Return> Run();
+
+  private:
+    bool HasExtent(Partition* p, Extent* e);
+    std::optional<uint64_t> GetSnapshotSize();
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
new file mode 100644
index 0000000..e308943
--- /dev/null
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -0,0 +1,90 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+#include <liblp/property_fetcher.h>
+
+#include "partition_cow_creator.h"
+
+using ::android::fs_mgr::MetadataBuilder;
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::Return;
+
+namespace android {
+namespace snapshot {
+
+class MockPropertyFetcher : public fs_mgr::IPropertyFetcher {
+  public:
+    MOCK_METHOD2(GetProperty, std::string(const std::string&, const std::string&));
+    MOCK_METHOD2(GetBoolProperty, bool(const std::string&, bool));
+};
+
+class PartitionCowCreatorTest : ::testing::Test {
+  public:
+    void SetUp() override {
+        fs_mgr::IPropertyFetcher::OverrideForTesting(std::make_unique<MockPropertyFetcher>());
+
+        EXPECT_CALL(fetcher(), GetProperty("ro.boot.slot_suffix", _))
+                .Times(AnyNumber())
+                .WillRepeatedly(Return("_a"));
+        EXPECT_CALL(fetcher(), GetBoolProperty("ro.boot.dynamic_partitions", _))
+                .Times(AnyNumber())
+                .WillRepeatedly(Return(true));
+        EXPECT_CALL(fetcher(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
+                .Times(AnyNumber())
+                .WillRepeatedly(Return(false));
+        EXPECT_CALL(fetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+                .Times(AnyNumber())
+                .WillRepeatedly(Return(true));
+    }
+    void TearDown() override {
+        fs_mgr::IPropertyFetcher::OverrideForTesting(std::make_unique<MockPropertyFetcher>());
+    }
+    MockPropertyFetcher& fetcher() {
+        return *static_cast<MockPropertyFetcher*>(fs_mgr::IPropertyFetcher::GetInstance());
+    }
+};
+
+TEST(PartitionCowCreator, IntersectSelf) {
+    auto builder_a = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder_a, nullptr);
+    auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system_a, nullptr);
+    ASSERT_TRUE(builder_a->ResizePartition(system_a, 40 * 1024));
+
+    auto builder_b = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder_b, nullptr);
+    auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system_b, nullptr);
+    ASSERT_TRUE(builder_b->ResizePartition(system_b, 40 * 1024));
+
+    PartitionCowCreator creator{.target_metadata = builder_b.get(),
+                                .target_suffix = "_b",
+                                .target_partition = system_b,
+                                .current_metadata = builder_a.get(),
+                                .current_suffix = "_a",
+                                .cow_size = 20 * 1024};
+    auto ret = creator.Run();
+    ASSERT_TRUE(ret.has_value());
+    ASSERT_EQ(40 * 1024, ret->snapshot_status.device_size);
+    ASSERT_EQ(40 * 1024, ret->snapshot_status.snapshot_size);
+    ASSERT_EQ(20 * 1024,
+              ret->snapshot_status.cow_file_size + ret->snapshot_status.cow_partition_size);
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index ab1157b..15d52f1 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -15,10 +15,12 @@
 #include <libsnapshot/snapshot.h>
 
 #include <dirent.h>
+#include <math.h>
 #include <sys/file.h>
 #include <sys/types.h>
 #include <sys/unistd.h>
 
+#include <optional>
 #include <thread>
 #include <unordered_set>
 
@@ -28,12 +30,17 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
+#include <fs_mgr_overlayfs.h>
 #include <fstab/fstab.h>
 #include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
 #include <liblp/liblp.h>
 
+#include "partition_cow_creator.h"
+#include "utility.h"
+
 namespace android {
 namespace snapshot {
 
@@ -46,11 +53,15 @@
 using android::dm::kSectorSize;
 using android::dm::SnapshotStorageMode;
 using android::fiemap::IImageManager;
+using android::fs_mgr::CreateDmTable;
 using android::fs_mgr::CreateLogicalPartition;
 using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::GetPartitionGroupName;
 using android::fs_mgr::GetPartitionName;
 using android::fs_mgr::LpMetadata;
+using android::fs_mgr::MetadataBuilder;
 using android::fs_mgr::SlotNumberForSlotSuffix;
+using std::chrono::duration_cast;
 using namespace std::chrono_literals;
 using namespace std::string_literals;
 
@@ -64,6 +75,10 @@
     std::string GetMetadataDir() const override { return "/metadata/ota"s; }
     std::string GetSlotSuffix() const override { return fs_mgr_get_slot_suffix(); }
     const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const { return opener_; }
+    std::string GetSuperDevice(uint32_t slot) const override {
+        return fs_mgr_get_super_partition_name(slot);
+    }
+    bool IsOverlayfsSetup() const override { return fs_mgr_overlayfs_is_setup(); }
 
   private:
     android::fs_mgr::PartitionOpener opener_;
@@ -97,10 +112,18 @@
     return snapshot_name + "-cow";
 }
 
+static std::string GetCowImageDeviceName(const std::string& snapshot_name) {
+    return snapshot_name + "-cow-img";
+}
+
 static std::string GetBaseDeviceName(const std::string& partition_name) {
     return partition_name + "-base";
 }
 
+static std::string GetSnapshotExtraDeviceName(const std::string& snapshot_name) {
+    return snapshot_name + "-inner";
+}
+
 bool SnapshotManager::BeginUpdate() {
     auto file = LockExclusive();
     if (!file) return false;
@@ -119,28 +142,53 @@
 
     UpdateState state = ReadUpdateState(file.get());
     if (state == UpdateState::None) return true;
-    if (state != UpdateState::Initiated) {
-        LOG(ERROR) << "Cannot cancel update after it has completed or started merging";
-        return false;
+
+    if (state == UpdateState::Initiated) {
+        LOG(INFO) << "Update has been initiated, now canceling";
+        return RemoveAllUpdateState(file.get());
     }
 
-    if (!RemoveAllSnapshots(file.get())) {
+    if (state == UpdateState::Unverified) {
+        // We completed an update, but it can still be canceled if we haven't booted into it.
+        auto boot_file = GetSnapshotBootIndicatorPath();
+        std::string contents;
+        if (!android::base::ReadFileToString(boot_file, &contents)) {
+            PLOG(WARNING) << "Cannot read " << boot_file << ", proceed to canceling the update:";
+            return RemoveAllUpdateState(file.get());
+        }
+        if (device_->GetSlotSuffix() == contents) {
+            LOG(INFO) << "Canceling a previously completed update";
+            return RemoveAllUpdateState(file.get());
+        }
+    }
+    LOG(ERROR) << "Cannot cancel update after it has completed or started merging";
+    return false;
+}
+
+bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) {
+    if (!RemoveAllSnapshots(lock)) {
         LOG(ERROR) << "Could not remove all snapshots";
         return false;
     }
 
-    if (!WriteUpdateState(file.get(), UpdateState::None)) {
-        LOG(ERROR) << "Could not write new update state";
-        return false;
-    }
-    return true;
+    RemoveSnapshotBootIndicator();
+
+    // If this fails, we'll keep trying to remove the update state (as the
+    // device reboots or starts a new update) until it finally succeeds.
+    return WriteUpdateState(lock, UpdateState::None);
 }
 
 bool SnapshotManager::FinishedSnapshotWrites() {
     auto lock = LockExclusive();
     if (!lock) return false;
 
-    if (ReadUpdateState(lock.get()) != UpdateState::Initiated) {
+    auto update_state = ReadUpdateState(lock.get());
+    if (update_state == UpdateState::Unverified) {
+        LOG(INFO) << "FinishedSnapshotWrites already called before. Ignored.";
+        return true;
+    }
+
+    if (update_state != UpdateState::Initiated) {
         LOG(ERROR) << "Can only transition to the Unverified state from the Initiated state.";
         return false;
     }
@@ -158,52 +206,71 @@
 }
 
 bool SnapshotManager::CreateSnapshot(LockedFile* lock, const std::string& name,
-                                     uint64_t device_size, uint64_t snapshot_size,
-                                     uint64_t cow_size) {
+                                     SnapshotManager::SnapshotStatus status) {
     CHECK(lock);
-    if (!EnsureImageManager()) return false;
-
+    CHECK(lock->lock_mode() == LOCK_EX);
     // Sanity check these sizes. Like liblp, we guarantee the partition size
     // is respected, which means it has to be sector-aligned. (This guarantee
-    // is useful for locating avb footers correctly). The COW size, however,
+    // is useful for locating avb footers correctly). The COW file size, however,
     // can be arbitrarily larger than specified, so we can safely round it up.
-    if (device_size % kSectorSize != 0) {
+    if (status.device_size % kSectorSize != 0) {
         LOG(ERROR) << "Snapshot " << name
-                   << " device size is not a multiple of the sector size: " << device_size;
+                   << " device size is not a multiple of the sector size: " << status.device_size;
         return false;
     }
-    if (snapshot_size % kSectorSize != 0) {
+    if (status.snapshot_size % kSectorSize != 0) {
+        LOG(ERROR) << "Snapshot " << name << " snapshot size is not a multiple of the sector size: "
+                   << status.snapshot_size;
+        return false;
+    }
+    if (status.cow_partition_size % kSectorSize != 0) {
         LOG(ERROR) << "Snapshot " << name
-                   << " snapshot size is not a multiple of the sector size: " << snapshot_size;
+                   << " cow partition size is not a multiple of the sector size: "
+                   << status.cow_partition_size;
+        return false;
+    }
+    if (status.cow_file_size % kSectorSize != 0) {
+        LOG(ERROR) << "Snapshot " << name << " cow file size is not a multiple of the sector size: "
+                   << status.cow_partition_size;
         return false;
     }
 
-    // Round the COW size up to the nearest sector.
-    cow_size += kSectorSize - 1;
-    cow_size &= ~(kSectorSize - 1);
+    status.state = SnapshotState::Created;
+    status.sectors_allocated = 0;
+    status.metadata_sectors = 0;
 
-    LOG(INFO) << "Snapshot " << name << " will have COW size " << cow_size;
-
-    // Note, we leave the status file hanging around if we fail to create the
-    // actual backing image. This is harmless, since it'll get removed when
-    // CancelUpdate is called.
-    SnapshotStatus status = {
-            .state = SnapshotState::Created,
-            .device_size = device_size,
-            .snapshot_size = snapshot_size,
-    };
     if (!WriteSnapshotStatus(lock, name, status)) {
         PLOG(ERROR) << "Could not write snapshot status: " << name;
         return false;
     }
+    return true;
+}
 
-    auto cow_name = GetCowName(name);
-    int cow_flags = IImageManager::CREATE_IMAGE_ZERO_FILL;
-    return images_->CreateBackingImage(cow_name, cow_size, cow_flags);
+bool SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name) {
+    CHECK(lock);
+    CHECK(lock->lock_mode() == LOCK_EX);
+    if (!EnsureImageManager()) return false;
+
+    SnapshotStatus status;
+    if (!ReadSnapshotStatus(lock, name, &status)) {
+        return false;
+    }
+
+    // The COW file size should have been rounded up to the nearest sector in CreateSnapshot.
+    // Sanity check this.
+    if (status.cow_file_size % kSectorSize != 0) {
+        LOG(ERROR) << "Snapshot " << name << " COW file size is not a multiple of the sector size: "
+                   << status.cow_file_size;
+        return false;
+    }
+
+    std::string cow_image_name = GetCowImageDeviceName(name);
+    int cow_flags = IImageManager::CREATE_IMAGE_DEFAULT;
+    return images_->CreateBackingImage(cow_image_name, status.cow_file_size, cow_flags);
 }
 
 bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
-                                  const std::string& base_device,
+                                  const std::string& base_device, const std::string& cow_device,
                                   const std::chrono::milliseconds& timeout_ms,
                                   std::string* dev_path) {
     CHECK(lock);
@@ -249,22 +316,7 @@
     uint64_t snapshot_sectors = status.snapshot_size / kSectorSize;
     uint64_t linear_sectors = (status.device_size - status.snapshot_size) / kSectorSize;
 
-    auto cow_name = GetCowName(name);
 
-    bool ok;
-    std::string cow_dev;
-    if (has_local_image_manager_) {
-        // If we forced a local image manager, it means we don't have binder,
-        // which means first-stage init. We must use device-mapper.
-        const auto& opener = device_->GetPartitionOpener();
-        ok = images_->MapImageWithDeviceMapper(opener, cow_name, &cow_dev);
-    } else {
-        ok = images_->MapImageDevice(cow_name, timeout_ms, &cow_dev);
-    }
-    if (!ok) {
-        LOG(ERROR) << "Could not map image device: " << cow_name;
-        return false;
-    }
 
     auto& dm = DeviceMapper::Instance();
 
@@ -293,30 +345,34 @@
     // and a linear target in the same table. Instead, we stack them, and give the
     // snapshot device a different name. It is not exposed to the caller in this
     // case.
-    auto snap_name = (linear_sectors > 0) ? name + "-inner" : name;
+    auto snap_name = (linear_sectors > 0) ? GetSnapshotExtraDeviceName(name) : name;
 
     DmTable table;
-    table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_dev, mode,
+    table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_device, mode,
                                     kSnapshotChunkSize);
     if (!dm.CreateDevice(snap_name, table, dev_path, timeout_ms)) {
         LOG(ERROR) << "Could not create snapshot device: " << snap_name;
-        images_->UnmapImageDevice(cow_name);
         return false;
     }
 
     if (linear_sectors) {
+        std::string snap_dev;
+        if (!dm.GetDeviceString(snap_name, &snap_dev)) {
+            LOG(ERROR) << "Cannot determine major/minor for: " << snap_name;
+            return false;
+        }
+
         // Our stacking will looks like this:
         //     [linear, linear] ; to snapshot, and non-snapshot region of base device
         //     [snapshot-inner]
         //     [base device]   [cow]
         DmTable table;
-        table.Emplace<DmTargetLinear>(0, snapshot_sectors, *dev_path, 0);
+        table.Emplace<DmTargetLinear>(0, snapshot_sectors, snap_dev, 0);
         table.Emplace<DmTargetLinear>(snapshot_sectors, linear_sectors, base_device,
                                       snapshot_sectors);
         if (!dm.CreateDevice(name, table, dev_path, timeout_ms)) {
             LOG(ERROR) << "Could not create outer snapshot device: " << name;
             dm.DeleteDevice(snap_name);
-            images_->UnmapImageDevice(cow_name);
             return false;
         }
     }
@@ -327,14 +383,32 @@
     return true;
 }
 
+bool SnapshotManager::MapCowImage(const std::string& name,
+                                  const std::chrono::milliseconds& timeout_ms) {
+    if (!EnsureImageManager()) return false;
+    auto cow_image_name = GetCowImageDeviceName(name);
+
+    bool ok;
+    std::string cow_dev;
+    if (has_local_image_manager_) {
+        // If we forced a local image manager, it means we don't have binder,
+        // which means first-stage init. We must use device-mapper.
+        const auto& opener = device_->GetPartitionOpener();
+        ok = images_->MapImageWithDeviceMapper(opener, cow_image_name, &cow_dev);
+    } else {
+        ok = images_->MapImageDevice(cow_image_name, timeout_ms, &cow_dev);
+    }
+
+    if (ok) {
+        LOG(INFO) << "Mapped " << cow_image_name << " to " << cow_dev;
+    } else {
+        LOG(ERROR) << "Could not map image device: " << cow_image_name;
+    }
+    return ok;
+}
+
 bool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {
     CHECK(lock);
-    if (!EnsureImageManager()) return false;
-
-    SnapshotStatus status;
-    if (!ReadSnapshotStatus(lock, name, &status)) {
-        return false;
-    }
 
     auto& dm = DeviceMapper::Instance();
     if (!dm.DeleteDeviceIfExists(name)) {
@@ -342,34 +416,34 @@
         return false;
     }
 
-    // There may be an extra device, since the kernel doesn't let us have a
-    // snapshot and linear target in the same table.
-    auto dm_name = GetSnapshotDeviceName(name, status);
-    if (name != dm_name && !dm.DeleteDeviceIfExists(dm_name)) {
-        LOG(ERROR) << "Could not delete inner snapshot device: " << dm_name;
+    auto snapshot_extra_device = GetSnapshotExtraDeviceName(name);
+    if (!dm.DeleteDeviceIfExists(snapshot_extra_device)) {
+        LOG(ERROR) << "Could not delete snapshot inner device: " << snapshot_extra_device;
         return false;
     }
 
-    auto cow_name = GetCowName(name);
-    if (images_->IsImageMapped(cow_name) && !images_->UnmapImageDevice(cow_name)) {
-        return false;
-    }
     return true;
 }
 
+bool SnapshotManager::UnmapCowImage(const std::string& name) {
+    if (!EnsureImageManager()) return false;
+    return images_->UnmapImageIfExists(GetCowImageDeviceName(name));
+}
+
 bool SnapshotManager::DeleteSnapshot(LockedFile* lock, const std::string& name) {
     CHECK(lock);
+    CHECK(lock->lock_mode() == LOCK_EX);
     if (!EnsureImageManager()) return false;
 
-    auto cow_name = GetCowName(name);
-    if (!images_->BackingImageExists(cow_name)) {
-        return true;
-    }
-    if (images_->IsImageMapped(cow_name) && !images_->UnmapImageDevice(cow_name)) {
+    if (!UnmapCowDevices(lock, name)) {
         return false;
     }
-    if (!images_->DeleteBackingImage(cow_name)) {
-        return false;
+
+    auto cow_image_name = GetCowImageDeviceName(name);
+    if (images_->BackingImageExists(cow_image_name)) {
+        if (!images_->DeleteBackingImage(cow_image_name)) {
+            return false;
+        }
     }
 
     std::string error;
@@ -575,9 +649,12 @@
 // Note that when a merge fails, we will *always* try again to complete the
 // merge each time the device boots. There is no harm in doing so, and if
 // the problem was transient, we might manage to get a new outcome.
-UpdateState SnapshotManager::WaitForMerge() {
+UpdateState SnapshotManager::ProcessUpdateState() {
     while (true) {
         UpdateState state = CheckMergeState();
+        if (state == UpdateState::MergeFailed) {
+            AcknowledgeMergeFailure();
+        }
         if (state != UpdateState::Merging) {
             // Either there is no merge, or the merge was finished, so no need
             // to keep waiting.
@@ -593,15 +670,16 @@
 UpdateState SnapshotManager::CheckMergeState() {
     auto lock = LockExclusive();
     if (!lock) {
-        AcknowledgeMergeFailure();
         return UpdateState::MergeFailed;
     }
 
-    auto state = CheckMergeState(lock.get());
+    UpdateState state = CheckMergeState(lock.get());
     if (state == UpdateState::MergeCompleted) {
+        // Do this inside the same lock. Failures get acknowledged without the
+        // lock, because flock() might have failed.
         AcknowledgeMergeSuccess(lock.get());
-    } else if (state == UpdateState::MergeFailed) {
-        AcknowledgeMergeFailure();
+    } else if (state == UpdateState::Cancelled) {
+        RemoveAllUpdateState(lock.get());
     }
     return state;
 }
@@ -623,10 +701,17 @@
             // run.
             break;
 
+        case UpdateState::Unverified:
+            // This is an edge case. Normally cancelled updates are detected
+            // via the merge poll below, but if we never started a merge, we
+            // need to also check here.
+            if (HandleCancelledUpdate(lock)) {
+                return UpdateState::Cancelled;
+            }
+            return state;
+
         default:
-            LOG(ERROR) << "No merge exists, cannot wait. Update state: "
-                       << static_cast<uint32_t>(state);
-            return UpdateState::None;
+            return state;
     }
 
     std::vector<std::string> snapshots;
@@ -634,6 +719,7 @@
         return UpdateState::MergeFailed;
     }
 
+    bool cancelled = false;
     bool failed = false;
     bool merging = false;
     bool needs_reboot = false;
@@ -651,6 +737,9 @@
                 break;
             case UpdateState::MergeCompleted:
                 break;
+            case UpdateState::Cancelled:
+                cancelled = true;
+                break;
             default:
                 LOG(ERROR) << "Unknown merge status: " << static_cast<uint32_t>(snapshot_state);
                 failed = true;
@@ -673,6 +762,14 @@
         WriteUpdateState(lock, UpdateState::MergeNeedsReboot);
         return UpdateState::MergeNeedsReboot;
     }
+    if (cancelled) {
+        // This is an edge case, that we handle as correctly as we sensibly can.
+        // The underlying partition has changed behind update_engine, and we've
+        // removed the snapshot as a result. The exact state of the update is
+        // undefined now, but this can only happen on an unlocked device where
+        // partitions can be flashed without wiping userdata.
+        return UpdateState::Cancelled;
+    }
     return UpdateState::MergeCompleted;
 }
 
@@ -684,17 +781,30 @@
 
     std::string dm_name = GetSnapshotDeviceName(name, snapshot_status);
 
-    // During a check, we decided the merge was complete, but we were unable to
-    // collapse the device-mapper stack and perform COW cleanup. If we haven't
-    // rebooted after this check, the device will still be a snapshot-merge
-    // target. If the have rebooted, the device will now be a linear target,
-    // and we can try cleanup again.
-    if (snapshot_status.state == SnapshotState::MergeCompleted && !IsSnapshotDevice(dm_name)) {
-        // NB: It's okay if this fails now, we gave cleanup our best effort.
-        OnSnapshotMergeComplete(lock, name, snapshot_status);
-        return UpdateState::MergeCompleted;
+    if (!IsSnapshotDevice(dm_name)) {
+        if (IsCancelledSnapshot(name)) {
+            DeleteSnapshot(lock, name);
+            return UpdateState::Cancelled;
+        }
+
+        // During a check, we decided the merge was complete, but we were unable to
+        // collapse the device-mapper stack and perform COW cleanup. If we haven't
+        // rebooted after this check, the device will still be a snapshot-merge
+        // target. If the have rebooted, the device will now be a linear target,
+        // and we can try cleanup again.
+        if (snapshot_status.state == SnapshotState::MergeCompleted) {
+            // NB: It's okay if this fails now, we gave cleanup our best effort.
+            OnSnapshotMergeComplete(lock, name, snapshot_status);
+            return UpdateState::MergeCompleted;
+        }
+
+        LOG(ERROR) << "Expected snapshot or snapshot-merge for device: " << dm_name;
+        return UpdateState::MergeFailed;
     }
 
+    // This check is expensive so it is only enabled for debugging.
+    DCHECK(!IsCancelledSnapshot(name));
+
     std::string target_type;
     DmTargetSnapshot::Status status;
     if (!QuerySnapshotStatus(dm_name, &target_type, &status)) {
@@ -750,12 +860,7 @@
 }
 
 void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
-    RemoveSnapshotBootIndicator();
-
-    if (!WriteUpdateState(lock, UpdateState::None)) {
-        // We'll try again next reboot, ad infinitum.
-        return;
-    }
+    RemoveAllUpdateState(lock);
 }
 
 void SnapshotManager::AcknowledgeMergeFailure() {
@@ -814,25 +919,16 @@
 
 bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
                                              const SnapshotStatus& status) {
-    // Ideally, we would complete the following steps to collapse the device:
-    //  (1) Rewrite the snapshot table to be identical to the base device table.
-    //  (2) Rewrite the verity table to use the "snapshot" (now linear) device.
-    //  (3) Delete the base device.
-    //
-    // This should be possible once libsnapshot understands LpMetadata. In the
-    // meantime, we implement a simpler solution: rewriting the snapshot table
-    // to be a single dm-linear segment against the base device. While not as
-    // ideal, it still lets us remove the COW device. We can remove this
-    // implementation once the new method has been tested.
     auto& dm = DeviceMapper::Instance();
     auto dm_name = GetSnapshotDeviceName(name, status);
 
+    // Verify we have a snapshot-merge device.
     DeviceMapper::TargetInfo target;
     if (!GetSingleTarget(dm_name, TableQuery::Table, &target)) {
         return false;
     }
     if (DeviceMapper::GetTargetType(target.spec) != "snapshot-merge") {
-        // This should be impossible, it was checked above.
+        // This should be impossible, it was checked earlier.
         LOG(ERROR) << "Snapshot device has invalid target type: " << dm_name;
         return false;
     }
@@ -844,8 +940,8 @@
         return false;
     }
 
-    uint64_t num_sectors = status.snapshot_size / kSectorSize;
-    if (num_sectors * kSectorSize != status.snapshot_size) {
+    uint64_t snapshot_sectors = status.snapshot_size / kSectorSize;
+    if (snapshot_sectors * kSectorSize != status.snapshot_size) {
         LOG(ERROR) << "Snapshot " << name
                    << " size is not sector aligned: " << status.snapshot_size;
         return false;
@@ -861,7 +957,7 @@
             return false;
         }
         if (outer_table.size() != 2) {
-            LOG(ERROR) << "Expected 2 dm-linear targets for tabble " << name
+            LOG(ERROR) << "Expected 2 dm-linear targets for table " << name
                        << ", got: " << outer_table.size();
             return false;
         }
@@ -873,39 +969,97 @@
                 return false;
             }
         }
-        uint64_t sectors = outer_table[0].spec.length + outer_table[1].spec.length;
-        if (sectors != num_sectors) {
-            LOG(ERROR) << "Outer snapshot " << name << " should have " << num_sectors
-                       << ", got: " << sectors;
+        if (outer_table[0].spec.length != snapshot_sectors) {
+            LOG(ERROR) << "dm-snapshot " << name << " should have " << snapshot_sectors
+                       << " sectors, got: " << outer_table[0].spec.length;
+            return false;
+        }
+        uint64_t expected_device_sectors = status.device_size / kSectorSize;
+        uint64_t actual_device_sectors = outer_table[0].spec.length + outer_table[1].spec.length;
+        if (expected_device_sectors != actual_device_sectors) {
+            LOG(ERROR) << "Outer device " << name << " should have " << expected_device_sectors
+                       << " sectors, got: " << actual_device_sectors;
             return false;
         }
     }
 
-    // Note: we are replacing the OUTER table here, so we do not use dm_name.
-    DmTargetLinear new_target(0, num_sectors, base_device, 0);
-    LOG(INFO) << "Replacing snapshot device " << name
-              << " table with: " << new_target.GetParameterString();
-
+    uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+    // Create a DmTable that is identical to the base device.
+    CreateLogicalPartitionParams base_device_params{
+            .block_device = device_->GetSuperDevice(slot),
+            .metadata_slot = slot,
+            .partition_name = name,
+            .partition_opener = &device_->GetPartitionOpener(),
+    };
     DmTable table;
-    table.Emplace<DmTargetLinear>(new_target);
+    if (!CreateDmTable(base_device_params, &table)) {
+        LOG(ERROR) << "Could not create a DmTable for partition: " << name;
+        return false;
+    }
+
+    // Note: we are replacing the *outer* table here, so we do not use dm_name.
     if (!dm.LoadTableAndActivate(name, table)) {
         return false;
     }
 
-    if (dm_name != name) {
-        // Attempt to delete the snapshot device. Nothing should be depending on
-        // the device, and device-mapper should have flushed remaining I/O. We
-        // could in theory replace with dm-zero (or re-use the table above), but
-        // for now it's better to know why this would fail.
-        if (!dm.DeleteDeviceIfExists(dm_name)) {
-            LOG(ERROR) << "Unable to delete snapshot device " << dm_name << ", COW cannot be "
-                       << "reclaimed until after reboot.";
-            return false;
-        }
+    // Attempt to delete the snapshot device if one still exists. Nothing
+    // should be depending on the device, and device-mapper should have
+    // flushed remaining I/O. We could in theory replace with dm-zero (or
+    // re-use the table above), but for now it's better to know why this
+    // would fail.
+    if (dm_name != name && !dm.DeleteDeviceIfExists(dm_name)) {
+        LOG(ERROR) << "Unable to delete snapshot device " << dm_name << ", COW cannot be "
+                   << "reclaimed until after reboot.";
+        return false;
+    }
+
+    // Cleanup the base device as well, since it is no longer used. This does
+    // not block cleanup.
+    auto base_name = GetBaseDeviceName(name);
+    if (!dm.DeleteDeviceIfExists(base_name)) {
+        LOG(ERROR) << "Unable to delete base device for snapshot: " << base_name;
     }
     return true;
 }
 
+bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock) {
+    std::string old_slot;
+    auto boot_file = GetSnapshotBootIndicatorPath();
+    if (!android::base::ReadFileToString(boot_file, &old_slot)) {
+        PLOG(ERROR) << "Unable to read the snapshot indicator file: " << boot_file;
+        return false;
+    }
+    if (device_->GetSlotSuffix() != old_slot) {
+        // We're booted into the target slot, which means we just rebooted
+        // after applying the update.
+        return false;
+    }
+
+    // The only way we can get here is if:
+    //  (1) The device rolled back to the previous slot.
+    //  (2) This function was called prematurely before rebooting the device.
+    //  (3) fastboot set_active was used.
+    //
+    // In any case, delete the snapshots. It may be worth using the boot_control
+    // HAL to differentiate case (2).
+    RemoveAllUpdateState(lock);
+    return true;
+}
+
+bool SnapshotManager::IsCancelledSnapshot(const std::string& snapshot_name) {
+    const auto& opener = device_->GetPartitionOpener();
+    uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+    auto super_device = device_->GetSuperDevice(slot);
+    auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
+    if (!metadata) {
+        LOG(ERROR) << "Could not read dynamic partition metadata for device: " << super_device;
+        return false;
+    }
+    auto partition = android::fs_mgr::FindPartition(*metadata.get(), snapshot_name);
+    if (!partition) return false;
+    return (partition->attributes & LP_PARTITION_ATTR_UPDATED) == 0;
+}
+
 bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
     std::vector<std::string> snapshots;
     if (!ListSnapshots(lock, &snapshots)) {
@@ -915,7 +1069,7 @@
 
     bool ok = true;
     for (const auto& name : snapshots) {
-        ok &= DeleteSnapshot(lock, name);
+        ok &= (UnmapPartitionWithSnapshot(lock, name) && DeleteSnapshot(lock, name));
     }
     return ok;
 }
@@ -1008,22 +1162,6 @@
     auto lock = LockExclusive();
     if (!lock) return false;
 
-    std::vector<std::string> snapshot_list;
-    if (!ListSnapshots(lock.get(), &snapshot_list)) {
-        return false;
-    }
-
-    std::unordered_set<std::string> live_snapshots;
-    for (const auto& snapshot : snapshot_list) {
-        SnapshotStatus status;
-        if (!ReadSnapshotStatus(lock.get(), snapshot, &status)) {
-            return false;
-        }
-        if (status.state != SnapshotState::MergeCompleted) {
-            live_snapshots.emplace(snapshot);
-        }
-    }
-
     const auto& opener = device_->GetPartitionOpener();
     uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
     auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
@@ -1032,58 +1170,278 @@
         return false;
     }
 
-    // Map logical partitions.
-    auto& dm = DeviceMapper::Instance();
     for (const auto& partition : metadata->partitions) {
-        auto partition_name = GetPartitionName(partition);
-        if (!partition.num_extents) {
-            LOG(INFO) << "Skipping zero-length logical partition: " << partition_name;
+        if (GetPartitionGroupName(metadata->groups[partition.group_index]) == kCowGroupName) {
+            LOG(INFO) << "Skip mapping partition " << GetPartitionName(partition) << " in group "
+                      << kCowGroupName;
             continue;
         }
 
-        if (!(partition.attributes & LP_PARTITION_ATTR_UPDATED)) {
-            LOG(INFO) << "Detected re-flashing of partition, will skip snapshot: "
-                      << partition_name;
-            live_snapshots.erase(partition_name);
-        }
-
         CreateLogicalPartitionParams params = {
                 .block_device = super_device,
                 .metadata = metadata.get(),
                 .partition = &partition,
                 .partition_opener = &opener,
         };
-
-        if (auto iter = live_snapshots.find(partition_name); iter != live_snapshots.end()) {
-            // If the device has a snapshot, it'll need to be writable, and
-            // we'll need to create the logical partition with a marked-up name
-            // (since the snapshot will use the partition name).
-            params.force_writable = true;
-            params.device_name = GetBaseDeviceName(partition_name);
-        }
-
         std::string ignore_path;
-        if (!CreateLogicalPartition(params, &ignore_path)) {
-            LOG(ERROR) << "Could not create logical partition " << partition_name << " as device "
-                       << params.GetDeviceName();
+        if (!MapPartitionWithSnapshot(lock.get(), std::move(params), &ignore_path)) {
             return false;
         }
-        if (!params.force_writable) {
-            // No snapshot.
-            continue;
-        }
+    }
 
-        // We don't have ueventd in first-stage init, so use device major:minor
-        // strings instead.
-        std::string base_device;
-        if (!dm.GetDeviceString(params.GetDeviceName(), &base_device)) {
-            LOG(ERROR) << "Could not determine major/minor for: " << params.GetDeviceName();
+    LOG(INFO) << "Created logical partitions with snapshot.";
+    return true;
+}
+
+static std::chrono::milliseconds GetRemainingTime(
+        const std::chrono::milliseconds& timeout,
+        const std::chrono::time_point<std::chrono::steady_clock>& begin) {
+    // If no timeout is specified, execute all commands without specifying any timeout.
+    if (timeout.count() == 0) return std::chrono::milliseconds(0);
+    auto passed_time = std::chrono::steady_clock::now() - begin;
+    auto remaining_time = timeout - duration_cast<std::chrono::milliseconds>(passed_time);
+    if (remaining_time.count() <= 0) {
+        LOG(ERROR) << "MapPartitionWithSnapshot has reached timeout " << timeout.count() << "ms ("
+                   << remaining_time.count() << "ms remaining)";
+        // Return min() instead of remaining_time here because 0 is treated as a special value for
+        // no timeout, where the rest of the commands will still be executed.
+        return std::chrono::milliseconds::min();
+    }
+    return remaining_time;
+}
+
+bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
+                                               CreateLogicalPartitionParams params,
+                                               std::string* path) {
+    auto begin = std::chrono::steady_clock::now();
+
+    CHECK(lock);
+    path->clear();
+
+    if (params.GetPartitionName() != params.GetDeviceName()) {
+        LOG(ERROR) << "Mapping snapshot with a different name is unsupported: partition_name = "
+                   << params.GetPartitionName() << ", device_name = " << params.GetDeviceName();
+        return false;
+    }
+
+    // Fill out fields in CreateLogicalPartitionParams so that we have more information (e.g. by
+    // reading super partition metadata).
+    CreateLogicalPartitionParams::OwnedData params_owned_data;
+    if (!params.InitDefaults(&params_owned_data)) {
+        return false;
+    }
+
+    if (!params.partition->num_extents) {
+        LOG(INFO) << "Skipping zero-length logical partition: " << params.GetPartitionName();
+        return true;  // leave path empty to indicate that nothing is mapped.
+    }
+
+    // Determine if there is a live snapshot for the SnapshotStatus of the partition; i.e. if the
+    // partition still has a snapshot that needs to be mapped.  If no live snapshot or merge
+    // completed, live_snapshot_status is set to nullopt.
+    std::optional<SnapshotStatus> live_snapshot_status;
+    do {
+        if (!(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {
+            LOG(INFO) << "Detected re-flashing of partition, will skip snapshot: "
+                      << params.GetPartitionName();
+            break;
+        }
+        auto file_path = GetSnapshotStatusFilePath(params.GetPartitionName());
+        if (access(file_path.c_str(), F_OK) != 0) {
+            if (errno != ENOENT) {
+                PLOG(INFO) << "Can't map snapshot for " << params.GetPartitionName()
+                           << ": Can't access " << file_path;
+                return false;
+            }
+            break;
+        }
+        live_snapshot_status = std::make_optional<SnapshotStatus>();
+        if (!ReadSnapshotStatus(lock, params.GetPartitionName(), &*live_snapshot_status)) {
             return false;
         }
-        if (!MapSnapshot(lock.get(), partition_name, base_device, {}, &ignore_path)) {
-            LOG(ERROR) << "Could not map snapshot for partition: " << partition_name;
+        // No live snapshot if merge is completed.
+        if (live_snapshot_status->state == SnapshotState::MergeCompleted) {
+            live_snapshot_status.reset();
+        }
+    } while (0);
+
+    if (live_snapshot_status.has_value()) {
+        // dm-snapshot requires the base device to be writable.
+        params.force_writable = true;
+        // Map the base device with a different name to avoid collision.
+        params.device_name = GetBaseDeviceName(params.GetPartitionName());
+    }
+
+    AutoDeviceList created_devices;
+
+    // Create the base device for the snapshot, or if there is no snapshot, the
+    // device itself. This device consists of the real blocks in the super
+    // partition that this logical partition occupies.
+    auto& dm = DeviceMapper::Instance();
+    std::string ignore_path;
+    if (!CreateLogicalPartition(params, &ignore_path)) {
+        LOG(ERROR) << "Could not create logical partition " << params.GetPartitionName()
+                   << " as device " << params.GetDeviceName();
+        return false;
+    }
+    created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName());
+
+    if (!live_snapshot_status.has_value()) {
+        created_devices.Release();
+        return true;
+    }
+
+    // We don't have ueventd in first-stage init, so use device major:minor
+    // strings instead.
+    std::string base_device;
+    if (!dm.GetDeviceString(params.GetDeviceName(), &base_device)) {
+        LOG(ERROR) << "Could not determine major/minor for: " << params.GetDeviceName();
+        return false;
+    }
+
+    auto remaining_time = GetRemainingTime(params.timeout_ms, begin);
+    if (remaining_time.count() < 0) return false;
+
+    std::string cow_name;
+    CreateLogicalPartitionParams cow_params = params;
+    cow_params.timeout_ms = remaining_time;
+    if (!MapCowDevices(lock, cow_params, *live_snapshot_status, &created_devices, &cow_name)) {
+        return false;
+    }
+    std::string cow_device;
+    if (!dm.GetDeviceString(cow_name, &cow_device)) {
+        LOG(ERROR) << "Could not determine major/minor for: " << cow_name;
+        return false;
+    }
+
+    remaining_time = GetRemainingTime(params.timeout_ms, begin);
+    if (remaining_time.count() < 0) return false;
+
+    if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,
+                     path)) {
+        LOG(ERROR) << "Could not map snapshot for partition: " << params.GetPartitionName();
+        return false;
+    }
+    // No need to add params.GetPartitionName() to created_devices since it is immediately released.
+
+    created_devices.Release();
+
+    LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at " << *path;
+
+    return true;
+}
+
+bool SnapshotManager::UnmapPartitionWithSnapshot(LockedFile* lock,
+                                                 const std::string& target_partition_name) {
+    CHECK(lock);
+
+    if (!UnmapSnapshot(lock, target_partition_name)) {
+        return false;
+    }
+
+    if (!UnmapCowDevices(lock, target_partition_name)) {
+        return false;
+    }
+
+    auto& dm = DeviceMapper::Instance();
+    std::string base_name = GetBaseDeviceName(target_partition_name);
+    if (!dm.DeleteDeviceIfExists(base_name)) {
+        LOG(ERROR) << "Cannot delete base device: " << base_name;
+        return false;
+    }
+
+    LOG(INFO) << "Successfully unmapped snapshot " << target_partition_name;
+
+    return true;
+}
+
+bool SnapshotManager::MapCowDevices(LockedFile* lock, const CreateLogicalPartitionParams& params,
+                                    const SnapshotStatus& snapshot_status,
+                                    AutoDeviceList* created_devices, std::string* cow_name) {
+    CHECK(lock);
+    if (!EnsureImageManager()) return false;
+    CHECK(snapshot_status.cow_partition_size + snapshot_status.cow_file_size > 0);
+    auto begin = std::chrono::steady_clock::now();
+
+    std::string partition_name = params.GetPartitionName();
+    std::string cow_image_name = GetCowImageDeviceName(partition_name);
+    *cow_name = GetCowName(partition_name);
+
+    auto& dm = DeviceMapper::Instance();
+
+    // Map COW image if necessary.
+    if (snapshot_status.cow_file_size > 0) {
+        auto remaining_time = GetRemainingTime(params.timeout_ms, begin);
+        if (remaining_time.count() < 0) return false;
+
+        if (!MapCowImage(partition_name, remaining_time)) {
+            LOG(ERROR) << "Could not map cow image for partition: " << partition_name;
             return false;
         }
+        created_devices->EmplaceBack<AutoUnmapImage>(images_.get(), cow_image_name);
+
+        // If no COW partition exists, just return the image alone.
+        if (snapshot_status.cow_partition_size == 0) {
+            *cow_name = std::move(cow_image_name);
+            LOG(INFO) << "Mapped COW image for " << partition_name << " at " << *cow_name;
+            return true;
+        }
+    }
+
+    auto remaining_time = GetRemainingTime(params.timeout_ms, begin);
+    if (remaining_time.count() < 0) return false;
+
+    CHECK(snapshot_status.cow_partition_size > 0);
+
+    // Create the DmTable for the COW device. It is the DmTable of the COW partition plus
+    // COW image device as the last extent.
+    CreateLogicalPartitionParams cow_partition_params = params;
+    cow_partition_params.partition = nullptr;
+    cow_partition_params.partition_name = *cow_name;
+    cow_partition_params.device_name.clear();
+    DmTable table;
+    if (!CreateDmTable(cow_partition_params, &table)) {
+        return false;
+    }
+    // If the COW image exists, append it as the last extent.
+    if (snapshot_status.cow_file_size > 0) {
+        std::string cow_image_device;
+        if (!dm.GetDeviceString(cow_image_name, &cow_image_device)) {
+            LOG(ERROR) << "Cannot determine major/minor for: " << cow_image_name;
+            return false;
+        }
+        auto cow_partition_sectors = snapshot_status.cow_partition_size / kSectorSize;
+        auto cow_image_sectors = snapshot_status.cow_file_size / kSectorSize;
+        table.Emplace<DmTargetLinear>(cow_partition_sectors, cow_image_sectors, cow_image_device,
+                                      0);
+    }
+
+    // We have created the DmTable now. Map it.
+    std::string cow_path;
+    if (!dm.CreateDevice(*cow_name, table, &cow_path, remaining_time)) {
+        LOG(ERROR) << "Could not create COW device: " << *cow_name;
+        return false;
+    }
+    created_devices->EmplaceBack<AutoUnmapDevice>(&dm, *cow_name);
+    LOG(INFO) << "Mapped COW device for " << params.GetPartitionName() << " at " << cow_path;
+    return true;
+}
+
+bool SnapshotManager::UnmapCowDevices(LockedFile* lock, const std::string& name) {
+    CHECK(lock);
+    if (!EnsureImageManager()) return false;
+
+    auto& dm = DeviceMapper::Instance();
+    auto cow_name = GetCowName(name);
+    if (!dm.DeleteDeviceIfExists(cow_name)) {
+        LOG(ERROR) << "Cannot unmap " << cow_name;
+        return false;
+    }
+
+    std::string cow_image_name = GetCowImageDeviceName(name);
+    if (!images_->UnmapImageIfExists(cow_image_name)) {
+        LOG(ERROR) << "Cannot unmap image " << cow_image_name;
+        return false;
     }
     return true;
 }
@@ -1221,12 +1579,14 @@
         return false;
     }
     auto pieces = android::base::Split(contents, " ");
-    if (pieces.size() != 5) {
+    if (pieces.size() != 7) {
         LOG(ERROR) << "Invalid status line for snapshot: " << path;
         return false;
     }
 
-    if (pieces[0] == "created") {
+    if (pieces[0] == "none") {
+        status->state = SnapshotState::None;
+    } else if (pieces[0] == "created") {
         status->state = SnapshotState::Created;
     } else if (pieces[0] == "merging") {
         status->state = SnapshotState::Merging;
@@ -1234,6 +1594,7 @@
         status->state = SnapshotState::MergeCompleted;
     } else {
         LOG(ERROR) << "Unrecognized state " << pieces[0] << " for snapshot: " << name;
+        return false;
     }
 
     if (!android::base::ParseUint(pieces[1], &status->device_size)) {
@@ -1244,11 +1605,19 @@
         LOG(ERROR) << "Invalid snapshot size in status line for: " << path;
         return false;
     }
-    if (!android::base::ParseUint(pieces[3], &status->sectors_allocated)) {
+    if (!android::base::ParseUint(pieces[3], &status->cow_partition_size)) {
+        LOG(ERROR) << "Invalid cow linear size in status line for: " << path;
+        return false;
+    }
+    if (!android::base::ParseUint(pieces[4], &status->cow_file_size)) {
+        LOG(ERROR) << "Invalid cow file size in status line for: " << path;
+        return false;
+    }
+    if (!android::base::ParseUint(pieces[5], &status->sectors_allocated)) {
         LOG(ERROR) << "Invalid snapshot size in status line for: " << path;
         return false;
     }
-    if (!android::base::ParseUint(pieces[4], &status->metadata_sectors)) {
+    if (!android::base::ParseUint(pieces[6], &status->metadata_sectors)) {
         LOG(ERROR) << "Invalid snapshot size in status line for: " << path;
         return false;
     }
@@ -1257,6 +1626,8 @@
 
 std::string SnapshotManager::to_string(SnapshotState state) {
     switch (state) {
+        case SnapshotState::None:
+            return "none";
         case SnapshotState::Created:
             return "created";
         case SnapshotState::Merging:
@@ -1286,6 +1657,8 @@
             to_string(status.state),
             std::to_string(status.device_size),
             std::to_string(status.snapshot_size),
+            std::to_string(status.cow_partition_size),
+            std::to_string(status.cow_file_size),
             std::to_string(status.sectors_allocated),
             std::to_string(status.metadata_sectors),
     };
@@ -1313,7 +1686,7 @@
 std::string SnapshotManager::GetSnapshotDeviceName(const std::string& snapshot_name,
                                                    const SnapshotStatus& status) {
     if (status.device_size != status.snapshot_size) {
-        return snapshot_name + "-inner";
+        return GetSnapshotExtraDeviceName(snapshot_name);
     }
     return snapshot_name;
 }
@@ -1340,5 +1713,200 @@
     return true;
 }
 
+bool SnapshotManager::CreateUpdateSnapshots(MetadataBuilder* target_metadata,
+                                            const std::string& target_suffix,
+                                            MetadataBuilder* current_metadata,
+                                            const std::string& current_suffix,
+                                            const std::map<std::string, uint64_t>& cow_sizes) {
+    auto lock = LockExclusive();
+    if (!lock) return false;
+
+    // Add _{target_suffix} to COW size map.
+    std::map<std::string, uint64_t> suffixed_cow_sizes;
+    for (const auto& [name, size] : cow_sizes) {
+        suffixed_cow_sizes[name + target_suffix] = size;
+    }
+
+    target_metadata->RemoveGroupAndPartitions(kCowGroupName);
+    if (!target_metadata->AddGroup(kCowGroupName, 0)) {
+        LOG(ERROR) << "Cannot add group " << kCowGroupName;
+        return false;
+    }
+
+    // TODO(b/134949511): remove this check. Right now, with overlayfs mounted, the scratch
+    // partition takes up a big chunk of space in super, causing COW images to be created on
+    // retrofit Virtual A/B devices.
+    if (device_->IsOverlayfsSetup()) {
+        LOG(ERROR) << "Cannot create update snapshots with overlayfs setup. Run `adb enable-verity`"
+                   << ", reboot, then try again.";
+        return false;
+    }
+
+    // Check that all these metadata is not retrofit dynamic partitions. Snapshots on
+    // devices with retrofit dynamic partitions does not make sense.
+    // This ensures that current_metadata->GetFreeRegions() uses the same device
+    // indices as target_metadata (i.e. 0 -> "super").
+    // This is also assumed in MapCowDevices() call below.
+    CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
+          target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);
+
+    std::map<std::string, SnapshotStatus> all_snapshot_status;
+
+    // In case of error, automatically delete devices that are created along the way.
+    // Note that "lock" is destroyed after "created_devices", so it is safe to use |lock| for
+    // these devices.
+    AutoDeviceList created_devices;
+
+    for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {
+        std::optional<uint64_t> cow_size = std::nullopt;
+        auto it = suffixed_cow_sizes.find(target_partition->name());
+        if (it != suffixed_cow_sizes.end()) {
+            cow_size = it->second;
+            LOG(INFO) << "Using provided COW size " << *cow_size << " for partition "
+                      << target_partition->name();
+        }
+
+        // Compute the device sizes for the partition.
+        PartitionCowCreator cow_creator{target_metadata,  target_suffix,  target_partition,
+                                        current_metadata, current_suffix, cow_size};
+        auto cow_creator_ret = cow_creator.Run();
+        if (!cow_creator_ret.has_value()) {
+            return false;
+        }
+
+        LOG(INFO) << "For partition " << target_partition->name()
+                  << ", device size = " << cow_creator_ret->snapshot_status.device_size
+                  << ", snapshot size = " << cow_creator_ret->snapshot_status.snapshot_size
+                  << ", cow partition size = "
+                  << cow_creator_ret->snapshot_status.cow_partition_size
+                  << ", cow file size = " << cow_creator_ret->snapshot_status.cow_file_size;
+
+        // Delete any existing snapshot before re-creating one.
+        if (!DeleteSnapshot(lock.get(), target_partition->name())) {
+            LOG(ERROR) << "Cannot delete existing snapshot before creating a new one for partition "
+                       << target_partition->name();
+            return false;
+        }
+
+        // It is possible that the whole partition uses free space in super, and snapshot / COW
+        // would not be needed. In this case, skip the partition.
+        bool needs_snapshot = cow_creator_ret->snapshot_status.snapshot_size > 0;
+        bool needs_cow = (cow_creator_ret->snapshot_status.cow_partition_size +
+                          cow_creator_ret->snapshot_status.cow_file_size) > 0;
+        CHECK(needs_snapshot == needs_cow);
+
+        if (!needs_snapshot) {
+            LOG(INFO) << "Skip creating snapshot for partition " << target_partition->name()
+                      << "because nothing needs to be snapshotted.";
+            continue;
+        }
+
+        // Store these device sizes to snapshot status file.
+        if (!CreateSnapshot(lock.get(), target_partition->name(),
+                            cow_creator_ret->snapshot_status)) {
+            return false;
+        }
+        created_devices.EmplaceBack<AutoDeleteSnapshot>(this, lock.get(), target_partition->name());
+
+        // Create the COW partition. That is, use any remaining free space in super partition before
+        // creating the COW images.
+        if (cow_creator_ret->snapshot_status.cow_partition_size > 0) {
+            CHECK(cow_creator_ret->snapshot_status.cow_partition_size % kSectorSize == 0)
+                    << "cow_partition_size == "
+                    << cow_creator_ret->snapshot_status.cow_partition_size
+                    << " is not a multiple of sector size " << kSectorSize;
+            auto cow_partition = target_metadata->AddPartition(GetCowName(target_partition->name()),
+                                                               kCowGroupName, 0 /* flags */);
+            if (cow_partition == nullptr) {
+                return false;
+            }
+
+            if (!target_metadata->ResizePartition(
+                        cow_partition, cow_creator_ret->snapshot_status.cow_partition_size,
+                        cow_creator_ret->cow_partition_usable_regions)) {
+                LOG(ERROR) << "Cannot create COW partition on metadata with size "
+                           << cow_creator_ret->snapshot_status.cow_partition_size;
+                return false;
+            }
+            // Only the in-memory target_metadata is modified; nothing to clean up if there is an
+            // error in the future.
+        }
+
+        // Create the backing COW image if necessary.
+        if (cow_creator_ret->snapshot_status.cow_file_size > 0) {
+            if (!CreateCowImage(lock.get(), target_partition->name())) {
+                return false;
+            }
+        }
+
+        all_snapshot_status[target_partition->name()] = std::move(cow_creator_ret->snapshot_status);
+
+        LOG(INFO) << "Successfully created snapshot for " << target_partition->name();
+    }
+
+    auto& dm = DeviceMapper::Instance();
+    auto exported_target_metadata = target_metadata->Export();
+    CreateLogicalPartitionParams cow_params{
+            .block_device = LP_METADATA_DEFAULT_PARTITION_NAME,
+            .metadata = exported_target_metadata.get(),
+            .timeout_ms = std::chrono::milliseconds::max(),
+            .partition_opener = &device_->GetPartitionOpener(),
+    };
+    for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {
+        AutoDeviceList created_devices_for_cow;
+
+        if (!UnmapPartitionWithSnapshot(lock.get(), target_partition->name())) {
+            LOG(ERROR) << "Cannot unmap existing COW devices before re-mapping them for zero-fill: "
+                       << target_partition->name();
+            return false;
+        }
+
+        auto it = all_snapshot_status.find(target_partition->name());
+        CHECK(it != all_snapshot_status.end()) << target_partition->name();
+        cow_params.partition_name = target_partition->name();
+        std::string cow_name;
+        if (!MapCowDevices(lock.get(), cow_params, it->second, &created_devices_for_cow,
+                           &cow_name)) {
+            return false;
+        }
+
+        std::string cow_path;
+        if (!dm.GetDmDevicePathByName(cow_name, &cow_path)) {
+            LOG(ERROR) << "Cannot determine path for " << cow_name;
+            return false;
+        }
+
+        if (!InitializeCow(cow_path)) {
+            LOG(ERROR) << "Can't zero-fill COW device for " << target_partition->name() << ": "
+                       << cow_path;
+            return false;
+        }
+        // Let destructor of created_devices_for_cow to unmap the COW devices.
+    };
+
+    created_devices.Release();
+    LOG(INFO) << "Successfully created all snapshots for target slot " << target_suffix;
+
+    return true;
+}
+
+bool SnapshotManager::MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
+                                        std::string* snapshot_path) {
+    auto lock = LockShared();
+    if (!lock) return false;
+    if (!UnmapPartitionWithSnapshot(lock.get(), params.GetPartitionName())) {
+        LOG(ERROR) << "Cannot unmap existing snapshot before re-mapping it: "
+                   << params.GetPartitionName();
+        return false;
+    }
+    return MapPartitionWithSnapshot(lock.get(), params, snapshot_path);
+}
+
+bool SnapshotManager::UnmapUpdateSnapshot(const std::string& target_partition_name) {
+    auto lock = LockShared();
+    if (!lock) return false;
+    return UnmapPartitionWithSnapshot(lock.get(), target_partition_name);
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index acffe8c..e9835d0 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -32,8 +32,10 @@
 #include <libfiemap/image_manager.h>
 #include <liblp/builder.h>
 #include <liblp/mock_property_fetcher.h>
+#include <storage_literals/storage_literals.h>
 
 #include "test_helpers.h"
+#include "utility.h"
 
 namespace android {
 namespace snapshot {
@@ -45,9 +47,12 @@
 using android::fs_mgr::BlockDeviceInfo;
 using android::fs_mgr::CreateLogicalPartitionParams;
 using android::fs_mgr::DestroyLogicalPartition;
+using android::fs_mgr::GetPartitionGroupName;
+using android::fs_mgr::GetPartitionName;
 using android::fs_mgr::MetadataBuilder;
 using namespace ::testing;
 using namespace android::fs_mgr::testing;
+using namespace android::storage_literals;
 using namespace std::chrono_literals;
 using namespace std::string_literals;
 
@@ -57,7 +62,7 @@
 TestDeviceInfo* test_device = nullptr;
 std::string fake_super;
 
-static constexpr uint64_t kSuperSize = 16 * 1024 * 1024;
+static constexpr uint64_t kSuperSize = 16_MiB;
 
 class SnapshotTest : public ::testing::Test {
   public:
@@ -104,8 +109,8 @@
         std::vector<std::string> snapshots = {"test-snapshot", "test_partition_a",
                                               "test_partition_b"};
         for (const auto& snapshot : snapshots) {
-            DeleteSnapshotDevice(snapshot);
-            DeleteBackingImage(image_manager_, snapshot + "-cow");
+            ASSERT_TRUE(DeleteSnapshotDevice(snapshot));
+            DeleteBackingImage(image_manager_, snapshot + "-cow-img");
 
             auto status_file = sm->GetSnapshotStatusFilePath(snapshot);
             android::base::RemoveFileIfExists(status_file);
@@ -198,6 +203,7 @@
                     .block_device = fake_super,
                     .metadata = metadata.get(),
                     .partition = &partition,
+                    .device_name = GetPartitionName(partition) + "-base",
                     .force_writable = true,
                     .timeout_ms = 10s,
             };
@@ -209,14 +215,52 @@
         return true;
     }
 
-    void DeleteSnapshotDevice(const std::string& snapshot) {
-        DeleteDevice(snapshot);
-        DeleteDevice(snapshot + "-inner");
-    }
-    void DeleteDevice(const std::string& device) {
-        if (dm_.GetState(device) != DmDeviceState::INVALID) {
-            ASSERT_TRUE(dm_.DeleteDevice(device));
+    AssertionResult DeleteSnapshotDevice(const std::string& snapshot) {
+        AssertionResult res = AssertionSuccess();
+        if (!(res = DeleteDevice(snapshot))) return res;
+        if (!(res = DeleteDevice(snapshot + "-inner"))) return res;
+        if (!(res = DeleteDevice(snapshot + "-cow"))) return res;
+        if (!image_manager_->UnmapImageIfExists(snapshot + "-cow-img")) {
+            return AssertionFailure() << "Cannot unmap image " << snapshot << "-cow-img";
         }
+        if (!(res = DeleteDevice(snapshot + "-base"))) return res;
+        return AssertionSuccess();
+    }
+
+    AssertionResult DeleteDevice(const std::string& device) {
+        if (!dm_.DeleteDeviceIfExists(device)) {
+            return AssertionFailure() << "Can't delete " << device;
+        }
+        return AssertionSuccess();
+    }
+
+    AssertionResult CreateCowImage(const std::string& name) {
+        if (!sm->CreateCowImage(lock_.get(), name)) {
+            return AssertionFailure() << "Cannot create COW image " << name;
+        }
+        std::string cow_device;
+        auto map_res = MapCowImage(name, 10s, &cow_device);
+        if (!map_res) {
+            return map_res;
+        }
+        if (!InitializeCow(cow_device)) {
+            return AssertionFailure() << "Cannot zero fill " << cow_device;
+        }
+        if (!sm->UnmapCowImage(name)) {
+            return AssertionFailure() << "Cannot unmap " << name << " after zero filling it";
+        }
+        return AssertionSuccess();
+    }
+
+    AssertionResult MapCowImage(const std::string& name,
+                                const std::chrono::milliseconds& timeout_ms, std::string* path) {
+        if (!sm->MapCowImage(name, timeout_ms)) {
+            return AssertionFailure() << "Cannot map cow image " << name;
+        }
+        if (!dm_.GetDmDevicePathByName(name + "-cow-img"s, path)) {
+            return AssertionFailure() << "No path for " << name << "-cow-img";
+        }
+        return AssertionSuccess();
     }
 
     DeviceMapper& dm_;
@@ -229,8 +273,11 @@
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize,
-                                   kDeviceSize));
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot",
+                                   {.device_size = kDeviceSize,
+                                    .snapshot_size = kDeviceSize,
+                                    .cow_file_size = kDeviceSize}));
+    ASSERT_TRUE(CreateCowImage("test-snapshot"));
 
     std::vector<std::string> snapshots;
     ASSERT_TRUE(sm->ListSnapshots(lock_.get(), &snapshots));
@@ -247,6 +294,7 @@
     }
 
     ASSERT_TRUE(sm->UnmapSnapshot(lock_.get(), "test-snapshot"));
+    ASSERT_TRUE(sm->UnmapCowImage("test-snapshot"));
     ASSERT_TRUE(sm->DeleteSnapshot(lock_.get(), "test-snapshot"));
 }
 
@@ -254,14 +302,21 @@
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize,
-                                   kDeviceSize));
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot",
+                                   {.device_size = kDeviceSize,
+                                    .snapshot_size = kDeviceSize,
+                                    .cow_file_size = kDeviceSize}));
+    ASSERT_TRUE(CreateCowImage("test-snapshot"));
 
     std::string base_device;
     ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
 
+    std::string cow_device;
+    ASSERT_TRUE(MapCowImage("test-snapshot", 10s, &cow_device));
+
     std::string snap_device;
-    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
+                                &snap_device));
     ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
 }
 
@@ -270,14 +325,21 @@
 
     static const uint64_t kSnapshotSize = 1024 * 1024;
     static const uint64_t kDeviceSize = 1024 * 1024 * 2;
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kSnapshotSize,
-                                   kSnapshotSize));
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot",
+                                   {.device_size = kDeviceSize,
+                                    .snapshot_size = kSnapshotSize,
+                                    .cow_file_size = kSnapshotSize}));
+    ASSERT_TRUE(CreateCowImage("test-snapshot"));
 
     std::string base_device;
     ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
 
+    std::string cow_device;
+    ASSERT_TRUE(MapCowImage("test-snapshot", 10s, &cow_device));
+
     std::string snap_device;
-    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
+                                &snap_device));
     ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
 }
 
@@ -308,15 +370,25 @@
 }
 
 TEST_F(SnapshotTest, Merge) {
+    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+            .WillByDefault(Return(true));
+
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize,
-                                   kDeviceSize));
 
-    std::string base_device, snap_device;
-    ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
-    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+    std::string base_device, cow_device, snap_device;
+    ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
+    ASSERT_TRUE(MapUpdatePartitions());
+    ASSERT_TRUE(dm_.GetDmDevicePathByName("test_partition_b-base", &base_device));
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b",
+                                   {.device_size = kDeviceSize,
+                                    .snapshot_size = kDeviceSize,
+                                    .cow_file_size = kDeviceSize}));
+    ASSERT_TRUE(CreateCowImage("test_partition_b"));
+    ASSERT_TRUE(MapCowImage("test_partition_b", 10s, &cow_device));
+    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test_partition_b", base_device, cow_device, 10s,
+                                &snap_device));
 
     std::string test_string = "This is a test string.";
     {
@@ -325,10 +397,10 @@
         ASSERT_TRUE(android::base::WriteFully(fd, test_string.data(), test_string.size()));
     }
 
-    // Note: we know the name of the device is test-snapshot because we didn't
-    // request a linear segment.
+    // Note: we know there is no inner/outer dm device since we didn't request
+    // a linear segment.
     DeviceMapper::TargetInfo target;
-    ASSERT_TRUE(sm->IsSnapshotDevice("test-snapshot", &target));
+    ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
     ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
 
     // Release the lock.
@@ -341,20 +413,22 @@
     ASSERT_TRUE(sm->InitiateMerge());
 
     // The device should have been switched to a snapshot-merge target.
-    ASSERT_TRUE(sm->IsSnapshotDevice("test-snapshot", &target));
+    ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
     ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
 
     // We should not be able to cancel an update now.
     ASSERT_FALSE(sm->CancelUpdate());
 
-    ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeCompleted);
+    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeCompleted);
     ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);
 
     // The device should no longer be a snapshot or snapshot-merge.
-    ASSERT_FALSE(sm->IsSnapshotDevice("test-snapshot"));
+    ASSERT_FALSE(sm->IsSnapshotDevice("test_partition_b"));
 
-    // Test that we can read back the string we wrote to the snapshot.
-    unique_fd fd(open(base_device.c_str(), O_RDONLY | O_CLOEXEC));
+    // Test that we can read back the string we wrote to the snapshot. Note
+    // that the base device is gone now. |snap_device| contains the correct
+    // partition.
+    unique_fd fd(open(snap_device.c_str(), O_RDONLY | O_CLOEXEC));
     ASSERT_GE(fd, 0);
 
     std::string buffer(test_string.size(), '\0');
@@ -366,16 +440,21 @@
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize,
-                                   kDeviceSize));
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot",
+                                   {.device_size = kDeviceSize,
+                                    .snapshot_size = kDeviceSize,
+                                    .cow_file_size = kDeviceSize}));
+    ASSERT_TRUE(CreateCowImage("test-snapshot"));
 
-    std::string base_device, snap_device;
+    std::string base_device, cow_device, snap_device;
     ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
-    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+    ASSERT_TRUE(MapCowImage("test-snapshot", 10s, &cow_device));
+    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
+                                &snap_device));
 
     // Keep an open handle to the cow device. This should cause the merge to
     // be incomplete.
-    auto cow_path = android::base::GetProperty("gsid.mapped_image.test-snapshot-cow", "");
+    auto cow_path = android::base::GetProperty("gsid.mapped_image.test-snapshot-cow-img", "");
     unique_fd fd(open(cow_path.c_str(), O_RDONLY | O_CLOEXEC));
     ASSERT_GE(fd, 0);
 
@@ -388,20 +467,26 @@
     ASSERT_TRUE(sm->InitiateMerge());
 
     // COW cannot be removed due to open fd, so expect a soft failure.
-    ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeNeedsReboot);
+    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeNeedsReboot);
 
+    // Release the handle to the COW device to fake a reboot.
+    fd.reset();
+    // Wait 1s, otherwise DeleteSnapshotDevice may fail with EBUSY.
+    sleep(1);
     // Forcefully delete the snapshot device, so it looks like we just rebooted.
-    DeleteSnapshotDevice("test-snapshot");
+    ASSERT_TRUE(DeleteSnapshotDevice("test-snapshot"));
 
     // Map snapshot should fail now, because we're in a merge-complete state.
     ASSERT_TRUE(AcquireLock());
-    ASSERT_FALSE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+    ASSERT_TRUE(MapCowImage("test-snapshot", 10s, &cow_device));
+    ASSERT_FALSE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
+                                 &snap_device));
 
     // Release everything and now the merge should complete.
     fd = {};
     lock_ = nullptr;
 
-    ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeCompleted);
+    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeCompleted);
 }
 
 TEST_F(SnapshotTest, FirstStageMountAndMerge) {
@@ -414,13 +499,16 @@
 
     ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
     ASSERT_TRUE(MapUpdatePartitions());
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b", kDeviceSize, kDeviceSize,
-                                   kDeviceSize));
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b",
+                                   {.device_size = kDeviceSize,
+                                    .snapshot_size = kDeviceSize,
+                                    .cow_file_size = kDeviceSize}));
+    ASSERT_TRUE(CreateCowImage("test_partition_b"));
 
     // Simulate a reboot into the new slot.
     lock_ = nullptr;
     ASSERT_TRUE(sm->FinishedSnapshotWrites());
-    ASSERT_TRUE(DestroyLogicalPartition("test_partition_b"));
+    ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
 
     auto rebooted = new TestDeviceInfo(fake_super);
     rebooted->set_slot_suffix("_b");
@@ -453,13 +541,16 @@
 
     ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
     ASSERT_TRUE(MapUpdatePartitions());
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b", kDeviceSize, kDeviceSize,
-                                   kDeviceSize));
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b",
+                                   {.device_size = kDeviceSize,
+                                    .snapshot_size = kDeviceSize,
+                                    .cow_file_size = kDeviceSize}));
+    ASSERT_TRUE(CreateCowImage("test_partition_b"));
 
     // Simulate a reboot into the new slot.
     lock_ = nullptr;
     ASSERT_TRUE(sm->FinishedSnapshotWrites());
-    ASSERT_TRUE(DestroyLogicalPartition("test_partition_b"));
+    ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
 
     // Reflash the super partition.
     FormatFakeSuper();
@@ -482,6 +573,468 @@
     DeviceMapper::TargetInfo target;
     auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
     ASSERT_FALSE(init->IsSnapshotDevice(dm_name, &target));
+
+    // We should see a cancelled update as well.
+    lock_ = nullptr;
+    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::Cancelled);
+}
+
+TEST_F(SnapshotTest, FlashSuperDuringMerge) {
+    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+            .WillByDefault(Return(true));
+
+    ASSERT_TRUE(AcquireLock());
+
+    static const uint64_t kDeviceSize = 1024 * 1024;
+
+    ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
+    ASSERT_TRUE(MapUpdatePartitions());
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b",
+                                   {.device_size = kDeviceSize,
+                                    .snapshot_size = kDeviceSize,
+                                    .cow_file_size = kDeviceSize}));
+    ASSERT_TRUE(CreateCowImage("test_partition_b"));
+
+    // Simulate a reboot into the new slot.
+    lock_ = nullptr;
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
+
+    auto rebooted = new TestDeviceInfo(fake_super);
+    rebooted->set_slot_suffix("_b");
+
+    auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    ASSERT_TRUE(init->InitiateMerge());
+
+    // Now, reflash super. Note that we haven't called ProcessUpdateState, so the
+    // status is still Merging.
+    ASSERT_TRUE(DeleteSnapshotDevice("test_partition_b"));
+    ASSERT_TRUE(init->image_manager()->UnmapImageIfExists("test_partition_b-cow-img"));
+    FormatFakeSuper();
+    ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+    // Because the status is Merging, we must call ProcessUpdateState, which should
+    // detect a cancelled update.
+    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::Cancelled);
+    ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);
+}
+
+class SnapshotUpdateTest : public SnapshotTest {
+  public:
+    void SetUp() override {
+        SnapshotTest::SetUp();
+        Cleanup();
+
+        // Cleanup() changes slot suffix, so initialize it again.
+        test_device->set_slot_suffix("_a");
+
+        ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+                .WillByDefault(Return(true));
+
+        opener = std::make_unique<TestPartitionOpener>(fake_super);
+
+        // Initialize source partition metadata. sys_b is similar to system_other.
+        // Not using full name "system", "vendor", "product" because these names collide with the
+        // mapped partitions on the running device.
+        src = MetadataBuilder::New(*opener, "super", 0);
+        ASSERT_NE(nullptr, src);
+        auto partition = src->AddPartition("sys_a", 0);
+        ASSERT_NE(nullptr, partition);
+        ASSERT_TRUE(src->ResizePartition(partition, 3_MiB));
+        partition = src->AddPartition("vnd_a", 0);
+        ASSERT_NE(nullptr, partition);
+        ASSERT_TRUE(src->ResizePartition(partition, 3_MiB));
+        partition = src->AddPartition("prd_a", 0);
+        ASSERT_NE(nullptr, partition);
+        ASSERT_TRUE(src->ResizePartition(partition, 3_MiB));
+        partition = src->AddPartition("sys_b", 0);
+        ASSERT_NE(nullptr, partition);
+        ASSERT_TRUE(src->ResizePartition(partition, 1_MiB));
+        auto metadata = src->Export();
+        ASSERT_NE(nullptr, metadata);
+        ASSERT_TRUE(UpdatePartitionTable(*opener, "super", *metadata.get(), 0));
+
+        // Map source partitions. Additionally, map sys_b to simulate system_other after flashing.
+        std::string path;
+        for (const auto& name : {"sys_a", "vnd_a", "prd_a", "sys_b"}) {
+            ASSERT_TRUE(CreateLogicalPartition(
+                    CreateLogicalPartitionParams{
+                            .block_device = fake_super,
+                            .metadata_slot = 0,
+                            .partition_name = name,
+                            .timeout_ms = 1s,
+                            .partition_opener = opener.get(),
+                    },
+                    &path));
+            ASSERT_TRUE(WriteRandomData(path));
+            auto hash = GetHash(path);
+            ASSERT_TRUE(hash.has_value());
+            hashes_[name] = *hash;
+        }
+    }
+    void TearDown() override {
+        Cleanup();
+        SnapshotTest::TearDown();
+    }
+    void Cleanup() {
+        if (!image_manager_) {
+            InitializeState();
+        }
+        for (const auto& suffix : {"_a", "_b"}) {
+            test_device->set_slot_suffix(suffix);
+            EXPECT_TRUE(sm->CancelUpdate()) << suffix;
+        }
+        EXPECT_TRUE(UnmapAll());
+    }
+
+    static AssertionResult ResizePartitions(
+            MetadataBuilder* builder,
+            const std::vector<std::pair<std::string, uint64_t>>& partition_sizes) {
+        for (auto&& [name, size] : partition_sizes) {
+            auto partition = builder->FindPartition(name);
+            if (!partition) {
+                return AssertionFailure() << "Cannot find partition in metadata " << name;
+            }
+            if (!builder->ResizePartition(partition, size)) {
+                return AssertionFailure()
+                       << "Cannot resize partition " << name << " to " << size << " bytes";
+            }
+        }
+        return AssertionSuccess();
+    }
+
+    AssertionResult IsPartitionUnchanged(const std::string& name) {
+        std::string path;
+        if (!dm_.GetDmDevicePathByName(name, &path)) {
+            return AssertionFailure() << "Path of " << name << " cannot be determined";
+        }
+        auto hash = GetHash(path);
+        if (!hash.has_value()) {
+            return AssertionFailure() << "Cannot read partition " << name << ": " << path;
+        }
+        if (hashes_[name] != *hash) {
+            return AssertionFailure() << "Content of " << name << " has changed after the merge";
+        }
+        return AssertionSuccess();
+    }
+
+    std::optional<uint64_t> GetSnapshotSize(const std::string& name) {
+        if (!AcquireLock()) {
+            return std::nullopt;
+        }
+        auto local_lock = std::move(lock_);
+
+        SnapshotManager::SnapshotStatus status;
+        if (!sm->ReadSnapshotStatus(local_lock.get(), name, &status)) {
+            return std::nullopt;
+        }
+        return status.snapshot_size;
+    }
+
+    AssertionResult UnmapAll() {
+        for (const auto& name : {"sys", "vnd", "prd"}) {
+            if (!dm_.DeleteDeviceIfExists(name + "_a"s)) {
+                return AssertionFailure() << "Cannot unmap " << name << "_a";
+            }
+            if (!DeleteSnapshotDevice(name + "_b"s)) {
+                return AssertionFailure() << "Cannot delete snapshot " << name << "_b";
+            }
+        }
+        return AssertionSuccess();
+    }
+
+    std::unique_ptr<TestPartitionOpener> opener;
+    std::unique_ptr<MetadataBuilder> src;
+    std::map<std::string, std::string> hashes_;
+};
+
+// Test full update flow executed by update_engine. Some partitions uses super empty space,
+// some uses images, and some uses both.
+// Also test UnmapUpdateSnapshot unmaps everything.
+// Also test first stage mount and merge after this.
+TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
+    // OTA client calls CancelUpdate then BeginUpdate before doing anything.
+    ASSERT_TRUE(sm->CancelUpdate());
+    ASSERT_TRUE(sm->BeginUpdate());
+
+    // OTA client blindly unmaps all partitions that are possibly mapped.
+    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+        ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
+    }
+
+    // OTA client adjusts the partition sizes before giving it to CreateUpdateSnapshots.
+    auto tgt = MetadataBuilder::NewForUpdate(*opener, "super", 0, 1);
+    ASSERT_NE(nullptr, tgt);
+    // clang-format off
+    ASSERT_TRUE(ResizePartitions(tgt.get(), {
+            {"sys_b", 4_MiB}, // grows
+            {"vnd_b", 4_MiB}, // grows
+            {"prd_b", 4_MiB}, // grows
+    }));
+    // clang-format on
+
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(tgt.get(), "_b", src.get(), "_a", {}));
+
+    // Test that partitions prioritize using space in super.
+    ASSERT_NE(nullptr, tgt->FindPartition("sys_b-cow"));
+    ASSERT_NE(nullptr, tgt->FindPartition("vnd_b-cow"));
+    ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
+
+    // The metadata slot 1 is now updated.
+    auto metadata = tgt->Export();
+    ASSERT_NE(nullptr, metadata);
+    ASSERT_TRUE(UpdatePartitionTable(*opener, "super", *metadata.get(), 1));
+
+    // Write some data to target partitions.
+    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+        std::string path;
+        ASSERT_TRUE(sm->MapUpdateSnapshot(
+                CreateLogicalPartitionParams{
+                        .block_device = fake_super,
+                        .metadata_slot = 1,
+                        .partition_name = name,
+                        .timeout_ms = 10s,
+                        .partition_opener = opener.get(),
+                },
+                &path))
+                << name;
+        ASSERT_TRUE(WriteRandomData(path));
+        auto hash = GetHash(path);
+        ASSERT_TRUE(hash.has_value());
+        hashes_[name] = *hash;
+    }
+
+    // Assert that source partitions aren't affected.
+    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+        ASSERT_TRUE(IsPartitionUnchanged(name));
+    }
+
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // After reboot, init does first stage mount.
+    auto rebooted = new TestDeviceInfo(fake_super);
+    rebooted->set_slot_suffix("_b");
+    auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+    // Check that the target partitions have the same content.
+    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+        ASSERT_TRUE(IsPartitionUnchanged(name));
+    }
+
+    // Initiate the merge and wait for it to be completed.
+    ASSERT_TRUE(init->InitiateMerge());
+    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
+
+    // Check that the target partitions have the same content after the merge.
+    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+        ASSERT_TRUE(IsPartitionUnchanged(name))
+                << "Content of " << name << " changes after the merge";
+    }
+}
+
+// Test that if new system partitions uses empty space in super, that region is not snapshotted.
+TEST_F(SnapshotUpdateTest, DirectWriteEmptySpace) {
+    auto tgt = MetadataBuilder::NewForUpdate(*opener, "super", 0, 1);
+    ASSERT_NE(nullptr, tgt);
+    // clang-format off
+    ASSERT_TRUE(ResizePartitions(tgt.get(), {
+            {"sys_b", 4_MiB}, // grows
+            // vnd_b and prd_b are unchanged
+    }));
+    // clang-format on
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(tgt.get(), "_b", src.get(), "_a", {}));
+
+    ASSERT_EQ(3_MiB, GetSnapshotSize("sys_b").value_or(0));
+}
+
+// Test that if new system partitions uses space of old vendor partition, that region is
+// snapshotted.
+TEST_F(SnapshotUpdateTest, SnapshotOldPartitions) {
+    auto tgt = MetadataBuilder::NewForUpdate(*opener, "super", 0, 1);
+    ASSERT_NE(nullptr, tgt);
+    // clang-format off
+    ASSERT_TRUE(ResizePartitions(tgt.get(), {
+            {"vnd_b", 2_MiB}, // shrinks
+            {"sys_b", 4_MiB}, // grows
+            // prd_b is unchanged
+    }));
+    // clang-format on
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(tgt.get(), "_b", src.get(), "_a", {}));
+
+    ASSERT_EQ(4_MiB, GetSnapshotSize("sys_b").value_or(0));
+}
+
+// Test that even if there seem to be empty space in target metadata, COW partition won't take
+// it because they are used by old partitions.
+TEST_F(SnapshotUpdateTest, CowPartitionDoNotTakeOldPartitions) {
+    auto tgt = MetadataBuilder::NewForUpdate(*opener, "super", 0, 1);
+    ASSERT_NE(nullptr, tgt);
+    // clang-format off
+    ASSERT_TRUE(ResizePartitions(tgt.get(), {
+            {"sys_b", 2_MiB}, // shrinks
+            // vnd_b and prd_b are unchanged
+    }));
+    // clang-format on
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(tgt.get(), "_b", src.get(), "_a", {}));
+
+    auto metadata = tgt->Export();
+    ASSERT_NE(nullptr, metadata);
+    std::vector<std::string> written;
+    // Write random data to all COW partitions in super
+    for (auto p : metadata->partitions) {
+        if (GetPartitionGroupName(metadata->groups[p.group_index]) != kCowGroupName) {
+            continue;
+        }
+        std::string path;
+        ASSERT_TRUE(CreateLogicalPartition(
+                CreateLogicalPartitionParams{
+                        .block_device = fake_super,
+                        .metadata = metadata.get(),
+                        .partition = &p,
+                        .timeout_ms = 1s,
+                        .partition_opener = opener.get(),
+                },
+                &path));
+        ASSERT_TRUE(WriteRandomData(path));
+        written.push_back(GetPartitionName(p));
+    }
+    ASSERT_FALSE(written.empty())
+            << "No COW partitions are created even if there are empty space in super partition";
+
+    // Make sure source partitions aren't affected.
+    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+        ASSERT_TRUE(IsPartitionUnchanged(name));
+    }
+}
+
+// Test that it crashes after creating snapshot status file but before creating COW image, then
+// calling CreateUpdateSnapshots again works.
+TEST_F(SnapshotUpdateTest, SnapshotStatusFileWithoutCow) {
+    // Write some trash snapshot files to simulate leftovers from previous runs.
+    {
+        ASSERT_TRUE(AcquireLock());
+        auto local_lock = std::move(lock_);
+        ASSERT_TRUE(sm->WriteSnapshotStatus(local_lock.get(), "sys_b",
+                                            SnapshotManager::SnapshotStatus{}));
+        ASSERT_TRUE(image_manager_->CreateBackingImage("sys_b-cow-img", 1_MiB,
+                                                       IImageManager::CREATE_IMAGE_DEFAULT));
+    }
+
+    // Redo the update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->UnmapUpdateSnapshot("sys_b"));
+    auto tgt = MetadataBuilder::NewForUpdate(*opener, "super", 0, 1);
+    ASSERT_NE(nullptr, tgt);
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(tgt.get(), "_b", src.get(), "_a", {}));
+
+    // The metadata slot 1 is now updated.
+    auto metadata = tgt->Export();
+    ASSERT_NE(nullptr, metadata);
+    ASSERT_TRUE(UpdatePartitionTable(*opener, "super", *metadata.get(), 1));
+
+    // Check that target partitions can be mapped.
+    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+        std::string path;
+        EXPECT_TRUE(sm->MapUpdateSnapshot(
+                CreateLogicalPartitionParams{
+                        .block_device = fake_super,
+                        .metadata_slot = 1,
+                        .partition_name = name,
+                        .timeout_ms = 10s,
+                        .partition_opener = opener.get(),
+                },
+                &path))
+                << name;
+    }
+}
+
+// Test that the old partitions are not modified.
+TEST_F(SnapshotUpdateTest, TestRollback) {
+    // Execute the update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->UnmapUpdateSnapshot("sys_b"));
+    auto tgt = MetadataBuilder::NewForUpdate(*opener, "super", 0, 1);
+    ASSERT_NE(nullptr, tgt);
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(tgt.get(), "_b", src.get(), "_a", {}));
+
+    // The metadata slot 1 is now updated.
+    auto metadata = tgt->Export();
+    ASSERT_NE(nullptr, metadata);
+    ASSERT_TRUE(UpdatePartitionTable(*opener, "super", *metadata.get(), 1));
+
+    // Write some data to target partitions.
+    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+        std::string path;
+        ASSERT_TRUE(sm->MapUpdateSnapshot(
+                CreateLogicalPartitionParams{
+                        .block_device = fake_super,
+                        .metadata_slot = 1,
+                        .partition_name = name,
+                        .timeout_ms = 10s,
+                        .partition_opener = opener.get(),
+                },
+                &path))
+                << name;
+        ASSERT_TRUE(WriteRandomData(path));
+        auto hash = GetHash(path);
+        ASSERT_TRUE(hash.has_value());
+        hashes_[name] = *hash;
+    }
+
+    // Assert that source partitions aren't affected.
+    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+        ASSERT_TRUE(IsPartitionUnchanged(name));
+    }
+
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // After reboot, init does first stage mount.
+    auto rebooted = new TestDeviceInfo(fake_super);
+    rebooted->set_slot_suffix("_b");
+    auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+    // Check that the target partitions have the same content.
+    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+        ASSERT_TRUE(IsPartitionUnchanged(name));
+    }
+
+    // Simulate shutting down the device again.
+    ASSERT_TRUE(UnmapAll());
+    rebooted = new TestDeviceInfo(fake_super);
+    rebooted->set_slot_suffix("_a");
+    init = SnapshotManager::NewForFirstStageMount(rebooted);
+    ASSERT_NE(init, nullptr);
+    ASSERT_FALSE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+    // Assert that the source partitions aren't affected.
+    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+        ASSERT_TRUE(IsPartitionUnchanged(name));
+    }
+}
+
+// Test that if an update is applied but not booted into, it can be canceled.
+TEST_F(SnapshotUpdateTest, CancelAfterApply) {
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(sm->CancelUpdate());
 }
 
 }  // namespace snapshot
@@ -525,6 +1078,7 @@
     }
 
     // Clean up previous run.
+    SnapshotUpdateTest().Cleanup();
     SnapshotTest().Cleanup();
 
     // Use a separate image manager for our fake super partition.
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index f67dd21..e12ec77 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -14,11 +14,18 @@
 
 #include "test_helpers.h"
 
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
+#include <openssl/sha.h>
 
 namespace android {
 namespace snapshot {
 
+using android::base::ReadFully;
+using android::base::unique_fd;
+using android::base::WriteFully;
 using android::fiemap::IImageManager;
 
 void DeleteBackingImage(IImageManager* manager, const std::string& name) {
@@ -53,5 +60,55 @@
     return PartitionOpener::GetDeviceString(partition_name);
 }
 
+bool WriteRandomData(const std::string& path) {
+    unique_fd rand(open("/dev/urandom", O_RDONLY));
+    unique_fd fd(open(path.c_str(), O_WRONLY));
+
+    char buf[4096];
+    while (true) {
+        ssize_t n = TEMP_FAILURE_RETRY(read(rand.get(), buf, sizeof(buf)));
+        if (n <= 0) return false;
+        if (!WriteFully(fd.get(), buf, n)) {
+            if (errno == ENOSPC) {
+                return true;
+            }
+            PLOG(ERROR) << "Cannot write " << path;
+            return false;
+        }
+    }
+}
+
+std::string ToHexString(const uint8_t* buf, size_t len) {
+    char lookup[] = "0123456789abcdef";
+    std::string out(len * 2 + 1, '\0');
+    char* outp = out.data();
+    for (; len > 0; len--, buf++) {
+        *outp++ = (char)lookup[*buf >> 4];
+        *outp++ = (char)lookup[*buf & 0xf];
+    }
+    return out;
+}
+
+std::optional<std::string> GetHash(const std::string& path) {
+    unique_fd fd(open(path.c_str(), O_RDONLY));
+    char buf[4096];
+    SHA256_CTX ctx;
+    SHA256_Init(&ctx);
+    while (true) {
+        ssize_t n = TEMP_FAILURE_RETRY(read(fd.get(), buf, sizeof(buf)));
+        if (n < 0) {
+            PLOG(ERROR) << "Cannot read " << path;
+            return std::nullopt;
+        }
+        if (n == 0) {
+            break;
+        }
+        SHA256_Update(&ctx, buf, n);
+    }
+    uint8_t out[32];
+    SHA256_Final(out, &ctx);
+    return ToHexString(out, sizeof(out));
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
index c87f118..d917e35 100644
--- a/fs_mgr/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -14,6 +14,7 @@
 
 #pragma once
 
+#include <optional>
 #include <string>
 
 #include <libfiemap/image_manager.h>
@@ -47,9 +48,11 @@
     std::string GetGsidDir() const override { return "ota/test"s; }
     std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
     std::string GetSlotSuffix() const override { return slot_suffix_; }
+    std::string GetSuperDevice([[maybe_unused]] uint32_t slot) const override { return "super"; }
     const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override {
         return *opener_.get();
     }
+    bool IsOverlayfsSetup() const override { return false; }
 
     void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
     void set_fake_super(const std::string& path) {
@@ -64,5 +67,10 @@
 // Helper for error-spam-free cleanup.
 void DeleteBackingImage(android::fiemap::IImageManager* manager, const std::string& name);
 
+// Write some random data to the given device. Will write until reaching end of the device.
+bool WriteRandomData(const std::string& device);
+
+std::optional<std::string> GetHash(const std::string& path);
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
new file mode 100644
index 0000000..66629e8
--- /dev/null
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -0,0 +1,113 @@
+// 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 "utility.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+using android::fs_mgr::MetadataBuilder;
+using android::fs_mgr::Partition;
+
+namespace android {
+namespace snapshot {
+
+void AutoDevice::Release() {
+    name_.clear();
+}
+
+AutoDeviceList::~AutoDeviceList() {
+    // Destroy devices in the reverse order because newer devices may have dependencies
+    // on older devices.
+    for (auto it = devices_.rbegin(); it != devices_.rend(); ++it) {
+        it->reset();
+    }
+}
+
+void AutoDeviceList::Release() {
+    for (auto&& p : devices_) {
+        p->Release();
+    }
+}
+
+AutoUnmapDevice::~AutoUnmapDevice() {
+    if (name_.empty()) return;
+    if (!dm_->DeleteDeviceIfExists(name_)) {
+        LOG(ERROR) << "Failed to auto unmap device " << name_;
+    }
+}
+
+AutoUnmapImage::~AutoUnmapImage() {
+    if (name_.empty()) return;
+    if (!images_->UnmapImageIfExists(name_)) {
+        LOG(ERROR) << "Failed to auto unmap cow image " << name_;
+    }
+}
+
+std::vector<Partition*> ListPartitionsWithSuffix(MetadataBuilder* builder,
+                                                 const std::string& suffix) {
+    std::vector<Partition*> ret;
+    for (const auto& group : builder->ListGroups()) {
+        for (auto* partition : builder->ListPartitionsInGroup(group)) {
+            if (!base::EndsWith(partition->name(), suffix)) {
+                continue;
+            }
+            ret.push_back(partition);
+        }
+    }
+    return ret;
+}
+
+AutoDeleteSnapshot::~AutoDeleteSnapshot() {
+    if (!name_.empty() && !manager_->DeleteSnapshot(lock_, name_)) {
+        LOG(ERROR) << "Failed to auto delete snapshot " << name_;
+    }
+}
+
+bool InitializeCow(const std::string& device) {
+    // When the kernel creates a persistent dm-snapshot, it requires a CoW file
+    // to store the modifications. The kernel interface does not specify how
+    // the CoW is used, and there is no standard associated.
+    // By looking at the current implementation, the CoW file is treated as:
+    // - a _NEW_ snapshot if its first 32 bits are zero, so the newly created
+    // dm-snapshot device will look like a perfect copy of the origin device;
+    // - an _EXISTING_ snapshot if the first 32 bits are equal to a
+    // kernel-specified magic number and the CoW file metadata is set as valid,
+    // so it can be used to resume the last state of a snapshot device;
+    // - an _INVALID_ snapshot otherwise.
+    // To avoid zero-filling the whole CoW file when a new dm-snapshot is
+    // created, here we zero-fill only the first 32 bits. This is a temporary
+    // workaround that will be discussed again when the kernel API gets
+    // consolidated.
+    // TODO(b/139202197): Remove this hack once the kernel API is consolidated.
+    constexpr ssize_t kDmSnapZeroFillSize = 4;  // 32-bit
+
+    char zeros[kDmSnapZeroFillSize] = {0};
+    android::base::unique_fd fd(open(device.c_str(), O_WRONLY | O_BINARY));
+    if (fd < 0) {
+        PLOG(ERROR) << "Can't open COW device: " << device;
+        return false;
+    }
+
+    LOG(INFO) << "Zero-filling COW device: " << device;
+    if (!android::base::WriteFully(fd, zeros, kDmSnapZeroFillSize)) {
+        PLOG(ERROR) << "Can't zero-fill COW device for " << device;
+        return false;
+    }
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
new file mode 100644
index 0000000..a700c46
--- /dev/null
+++ b/fs_mgr/libsnapshot/utility.h
@@ -0,0 +1,110 @@
+// 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 <functional>
+#include <string>
+
+#include <android-base/macros.h>
+#include <libdm/dm.h>
+#include <libfiemap/image_manager.h>
+#include <liblp/builder.h>
+#include <libsnapshot/snapshot.h>
+
+namespace android {
+namespace snapshot {
+
+struct AutoDevice {
+    virtual ~AutoDevice(){};
+    void Release();
+
+  protected:
+    AutoDevice(const std::string& name) : name_(name) {}
+    std::string name_;
+
+  private:
+    DISALLOW_COPY_AND_ASSIGN(AutoDevice);
+    AutoDevice(AutoDevice&& other) = delete;
+};
+
+// A list of devices we created along the way.
+// - Whenever a device is created that is subject to GC'ed at the end of
+//   this function, add it to this list.
+// - If any error has occurred, the list is destroyed, and all these devices
+//   are cleaned up.
+// - Upon success, Release() should be called so that the created devices
+//   are kept.
+struct AutoDeviceList {
+    ~AutoDeviceList();
+    template <typename T, typename... Args>
+    void EmplaceBack(Args&&... args) {
+        devices_.emplace_back(std::make_unique<T>(std::forward<Args>(args)...));
+    }
+    void Release();
+
+  private:
+    std::vector<std::unique_ptr<AutoDevice>> devices_;
+};
+
+// Automatically unmap a device upon deletion.
+struct AutoUnmapDevice : AutoDevice {
+    // On destruct, delete |name| from device mapper.
+    AutoUnmapDevice(android::dm::DeviceMapper* dm, const std::string& name)
+        : AutoDevice(name), dm_(dm) {}
+    AutoUnmapDevice(AutoUnmapDevice&& other) = default;
+    ~AutoUnmapDevice();
+
+  private:
+    DISALLOW_COPY_AND_ASSIGN(AutoUnmapDevice);
+    android::dm::DeviceMapper* dm_ = nullptr;
+};
+
+// Automatically unmap an image upon deletion.
+struct AutoUnmapImage : AutoDevice {
+    // On destruct, delete |name| from image manager.
+    AutoUnmapImage(android::fiemap::IImageManager* images, const std::string& name)
+        : AutoDevice(name), images_(images) {}
+    AutoUnmapImage(AutoUnmapImage&& other) = default;
+    ~AutoUnmapImage();
+
+  private:
+    DISALLOW_COPY_AND_ASSIGN(AutoUnmapImage);
+    android::fiemap::IImageManager* images_ = nullptr;
+};
+
+// Automatically deletes a snapshot. |name| should be the name of the partition, e.g. "system_a".
+// Client is responsible for maintaining the lifetime of |manager| and |lock|.
+struct AutoDeleteSnapshot : AutoDevice {
+    AutoDeleteSnapshot(SnapshotManager* manager, SnapshotManager::LockedFile* lock,
+                       const std::string& name)
+        : AutoDevice(name), manager_(manager), lock_(lock) {}
+    AutoDeleteSnapshot(AutoDeleteSnapshot&& other);
+    ~AutoDeleteSnapshot();
+
+  private:
+    DISALLOW_COPY_AND_ASSIGN(AutoDeleteSnapshot);
+    SnapshotManager* manager_ = nullptr;
+    SnapshotManager::LockedFile* lock_ = nullptr;
+};
+
+// Return a list of partitions in |builder| with the name ending in |suffix|.
+std::vector<android::fs_mgr::Partition*> ListPartitionsWithSuffix(
+        android::fs_mgr::MetadataBuilder* builder, const std::string& suffix);
+
+// Initialize a device before using it as the COW device for a dm-snapshot device.
+bool InitializeCow(const std::string& device);
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libstorage_literals/Android.bp b/fs_mgr/libstorage_literals/Android.bp
new file mode 100644
index 0000000..11611dd
--- /dev/null
+++ b/fs_mgr/libstorage_literals/Android.bp
@@ -0,0 +1,6 @@
+
+cc_library_headers {
+    name: "libstorage_literals_headers",
+    host_supported: true,
+    export_include_dirs: ["."],
+}
diff --git a/fs_mgr/libstorage_literals/storage_literals/storage_literals.h b/fs_mgr/libstorage_literals/storage_literals/storage_literals.h
new file mode 100644
index 0000000..ac0dfbd
--- /dev/null
+++ b/fs_mgr/libstorage_literals/storage_literals/storage_literals.h
@@ -0,0 +1,77 @@
+// 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 <stdlib.h>
+
+namespace android {
+namespace storage_literals {
+
+template <size_t Power>
+struct Size {
+    static constexpr size_t power = Power;
+    explicit constexpr Size(uint64_t count) : value_(count) {}
+
+    constexpr uint64_t bytes() const { return value_ << power; }
+    constexpr uint64_t count() const { return value_; }
+    constexpr operator uint64_t() const { return bytes(); }
+
+  private:
+    uint64_t value_;
+};
+
+using B = Size<0>;
+using KiB = Size<10>;
+using MiB = Size<20>;
+using GiB = Size<30>;
+
+constexpr B operator""_B(unsigned long long v) {  // NOLINT
+    return B{v};
+}
+
+constexpr KiB operator""_KiB(unsigned long long v) {  // NOLINT
+    return KiB{v};
+}
+
+constexpr MiB operator""_MiB(unsigned long long v) {  // NOLINT
+    return MiB{v};
+}
+
+constexpr GiB operator""_GiB(unsigned long long v) {  // NOLINT
+    return GiB{v};
+}
+
+template <typename Dest, typename Src>
+constexpr Dest size_cast(Src src) {
+    if (Src::power < Dest::power) {
+        return Dest(src.count() >> (Dest::power - Src::power));
+    }
+    if (Src::power > Dest::power) {
+        return Dest(src.count() << (Src::power - Dest::power));
+    }
+    return Dest(src.count());
+}
+
+static_assert(1_B == 1);
+static_assert(1_KiB == 1 << 10);
+static_assert(1_MiB == 1 << 20);
+static_assert(1_GiB == 1 << 30);
+static_assert(size_cast<KiB>(1_B).count() == 0);
+static_assert(size_cast<KiB>(1024_B).count() == 1);
+static_assert(size_cast<KiB>(1_MiB).count() == 1024);
+
+}  // namespace storage_literals
+}  // namespace android
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index ed6d0e3..f445703 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -1190,6 +1190,11 @@
     skip_unrelated_mounts |
     grep " overlay ro,") ||
     die "remount overlayfs missed a spot (ro)"
+  !(adb_sh grep -v noatime /proc/mounts </dev/null |
+    skip_administrative_mounts data |
+    skip_unrelated_mounts |
+    grep -v ' ro,') ||
+    die "mounts are not noatime"
   D=`adb_sh grep " rw," /proc/mounts </dev/null |
      skip_administrative_mounts data`
   if echo "${D}" | grep /dev/root >/dev/null; then
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 778e08c..27a6452 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -38,8 +38,6 @@
         "libkeystore_aidl",
         "libkeystore_binder",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "android.hardware.gatekeeper@1.0",
         "libgatekeeper_aidl",
     ],
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 53be526..4f89bfb 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -42,8 +42,6 @@
         "libbase",
         "libcutils",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "liblog",
         "libutils",
         "android.hardware.health@2.0",
diff --git a/healthd/Android.mk b/healthd/Android.mk
index b87f3c7..66ff399 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -21,9 +21,7 @@
     android.hardware.health@1.0-convert \
     libbinderthreadstate \
     libcharger_sysprop \
-    libhidltransport \
     libhidlbase \
-    libhwbinder_noltopgo \
     libhealthstoragedefault \
     libminui \
     libvndksupport \
@@ -75,9 +73,7 @@
     android.hardware.health@1.0-convert \
     libbinderthreadstate \
     libcharger_sysprop \
-    libhidltransport \
     libhidlbase \
-    libhwbinder_noltopgo \
     libhealthstoragedefault \
     libvndksupport \
     libhealthd_charger_nops \
diff --git a/init/Android.bp b/init/Android.bp
index db8cdcb..b601075 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -108,7 +108,6 @@
         "action.cpp",
         "action_manager.cpp",
         "action_parser.cpp",
-        "boringssl_self_test.cpp",
         "bootchart.cpp",
         "builtins.cpp",
         "capabilities.cpp",
@@ -224,6 +223,7 @@
 
     srcs: [
         "devices_test.cpp",
+        "firmware_handler_test.cpp",
         "init_test.cpp",
         "keychords_test.cpp",
         "persistent_properties_test.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index 4e73898..8f58437 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -118,6 +118,7 @@
 LOCAL_SANITIZE := signed-integer-overflow
 # First stage init is weird: it may start without stdout/stderr, and no /proc.
 LOCAL_NOSANITIZE := hwaddress
+LOCAL_INJECT_BSSL_HASH := true
 include $(BUILD_EXECUTABLE)
 endif
 
diff --git a/init/README.md b/init/README.md
index 2de76a9..423c6d1 100644
--- a/init/README.md
+++ b/init/README.md
@@ -263,6 +263,12 @@
 > Scheduling priority of the service process. This value has to be in range
   -20 to 19. Default priority is 0. Priority is set via setpriority().
 
+`reboot_on_failure <target>`
+> If this process cannot be started or if the process terminates with an exit code other than
+  CLD_EXITED or an status other than '0', reboot the system with the target specified in
+  _target_. _target_ takes the same format as the parameter to sys.powerctl. This is particularly
+  intended to be used with the `exec_start` builtin for any must-have checks during boot.
+
 `restart_period <seconds>`
 > If a non-oneshot service exits, it will be restarted at its start time plus
   this period. It defaults to 5s to rate limit crashing services.
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
index c592c37..053ebf8 100644
--- a/init/README.ueventd.md
+++ b/init/README.ueventd.md
@@ -82,7 +82,7 @@
 
 ## Firmware loading
 ----------------
-Ueventd automatically serves firmware requests by searching through a list of firmware directories
+Ueventd by default serves firmware requests by searching through a list of firmware directories
 for a file matching the uevent `FIRMWARE`. It then forks a process to serve this firmware to the
 kernel.
 
@@ -100,6 +100,26 @@
 Ueventd will wait until after `post-fs` in init, to keep retrying before believing the firmwares are
 not present.
 
+The exact firmware file to be served can be customized by running an external program by a
+`external_firmware_handler` line in a ueventd.rc file. This line takes the format of
+
+    external_firmware_handler <devpath> <user name to run as> <path to external program>
+For example
+
+    external_firmware_handler /devices/leds/red/firmware/coeffs.bin system /vendor/bin/led_coeffs.bin
+Will launch `/vendor/bin/led_coeffs.bin` as the system user instead of serving the default firmware
+for `/devices/leds/red/firmware/coeffs.bin`.
+
+Ueventd will provide the uevent `DEVPATH` and `FIRMWARE` to this external program on the environment
+via environment variables with the same names. Ueventd will use the string written to stdout as the
+new name of the firmware to load. It will still look for the new firmware in the list of firmware
+directories stated above. It will also reject file names with `..` in them, to prevent leaving these
+directories. If stdout cannot be read, or the program returns with any exit code other than
+`EXIT_SUCCESS`, or the program crashes, the default firmware from the uevent will be loaded.
+
+Ueventd will additionally log all messages sent to stderr from the external program to the serial
+console after the external program has exited.
+
 ## Coldboot
 --------
 Ueventd must create devices in `/dev` for all devices that have already sent their uevents before
@@ -110,3 +130,9 @@
 
 For boot time purposes, this is done in parallel across a set of child processes. `ueventd.cpp` in
 this directory contains documentation on how the parallelization is done.
+
+There is an option to parallelize the restorecon function during cold boot as well. This should only
+be done for devices that do not use genfscon, which is the recommended method for labeling sysfs
+nodes. To enable this option, use the below line in a ueventd.rc script:
+
+    parallel_restorecon enabled
diff --git a/init/boringssl_self_test.cpp b/init/boringssl_self_test.cpp
deleted file mode 100644
index 759eb43..0000000
--- a/init/boringssl_self_test.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "boringssl_self_test.h"
-
-#include <android-base/logging.h>
-#include <cutils/android_reboot.h>
-#include <openssl/crypto.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-namespace android {
-namespace init {
-
-Result<void> StartBoringSslSelfTest(const BuiltinArguments&) {
-    pid_t id = fork();
-
-    if (id == 0) {
-        if (BORINGSSL_self_test() != 1) {
-            LOG(INFO) << "BoringSSL crypto self tests failed";
-
-            // This check has failed, so the device should refuse
-            // to boot. Rebooting to bootloader to wait for
-            // further action from the user.
-
-            int result = android_reboot(ANDROID_RB_RESTART2, 0,
-                                        "bootloader,boringssl-self-check-failed");
-            if (result != 0) {
-                LOG(ERROR) << "Failed to reboot into bootloader";
-            }
-        }
-
-        _exit(0);
-    } else if (id == -1) {
-        // Failed to fork, so cannot run the test. Refuse to continue.
-        PLOG(FATAL) << "Failed to fork for BoringSSL self test";
-    }
-
-    return {};
-}
-
-}  // namespace init
-}  // namespace android
diff --git a/init/boringssl_self_test.h b/init/boringssl_self_test.h
deleted file mode 100644
index 9e717d0..0000000
--- a/init/boringssl_self_test.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "builtin_arguments.h"
-#include "result.h"
-
-namespace android {
-namespace init {
-
-Result<void> StartBoringSslSelfTest(const BuiltinArguments&);
-
-}  // namespace init
-}  // namespace android
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 8c4d989..42211b2 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -81,6 +81,7 @@
 
 using android::base::Basename;
 using android::base::StartsWith;
+using android::base::StringPrintf;
 using android::base::unique_fd;
 using android::fs_mgr::Fstab;
 using android::fs_mgr::ReadFstabFromFile;
@@ -702,11 +703,11 @@
 
 static Result<void> do_setprop(const BuiltinArguments& args) {
     if (StartsWith(args[1], "ctl.")) {
-        return Error() << "InitPropertySet: Do not set ctl. properties from init; call the Service "
-                          "functions directly";
+        return Error()
+               << "Cannot set ctl. properties from init; call the Service functions directly";
     }
     if (args[1] == kRestoreconProperty) {
-        return Error() << "InitPropertySet: Do not set '" << kRestoreconProperty
+        return Error() << "Cannot set '" << kRestoreconProperty
                        << "' from init; use the restorecon builtin directly";
     }
 
@@ -1025,7 +1026,20 @@
 }
 
 static Result<void> do_load_persist_props(const BuiltinArguments& args) {
-    load_persist_props();
+    // Devices with FDE have load_persist_props called twice; the first time when the temporary
+    // /data partition is mounted and then again once /data is truly mounted.  We do not want to
+    // read persistent properties from the temporary /data partition or mark persistent properties
+    // as having been loaded during the first call, so we return in that case.
+    std::string crypto_state = android::base::GetProperty("ro.crypto.state", "");
+    std::string crypto_type = android::base::GetProperty("ro.crypto.type", "");
+    if (crypto_state == "encrypted" && crypto_type == "block") {
+        static size_t num_calls = 0;
+        if (++num_calls == 1) return {};
+    }
+
+    SendLoadPersistentPropertiesMessage();
+
+    start_waiting_for_property("ro.persistent_properties.ready", "true");
     return {};
 }
 
@@ -1072,34 +1086,45 @@
     return android::base::GetProperty("ro.crypto.type", "") == "file";
 }
 
-static Result<void> ExecWithRebootOnFailure(const std::string& reboot_reason,
-                                            const BuiltinArguments& args) {
-    auto service = Service::MakeTemporaryOneshotService(args.args);
+static Result<void> ExecWithFunctionOnFailure(const std::vector<std::string>& args,
+                                              std::function<void(const std::string&)> function) {
+    auto service = Service::MakeTemporaryOneshotService(args);
     if (!service) {
-        return Error() << "Could not create exec service: " << service.error();
+        function("MakeTemporaryOneshotService failed: " + service.error().message());
     }
-    (*service)->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
+    (*service)->AddReapCallback([function](const siginfo_t& siginfo) {
         if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
-            // TODO (b/122850122): support this in gsi
-            if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
-                LOG(ERROR) << "Rebooting into recovery, reason: " << reboot_reason;
-                if (auto result = reboot_into_recovery(
-                            {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
-                    !result) {
-                    LOG(FATAL) << "Could not reboot into recovery: " << result.error();
-                }
-            } else {
-                LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
-            }
+            function(StringPrintf("Exec service failed, status %d", siginfo.si_status));
         }
     });
     if (auto result = (*service)->ExecStart(); !result) {
-        return Error() << "Could not start exec service: " << result.error();
+        function("ExecStart failed: " + result.error().message());
     }
     ServiceList::GetInstance().AddService(std::move(*service));
     return {};
 }
 
+static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
+    auto reboot_reason = vdc_arg + "_failed";
+
+    auto reboot = [reboot_reason](const std::string& message) {
+        // TODO (b/122850122): support this in gsi
+        if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
+            LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
+            if (auto result = reboot_into_recovery(
+                        {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
+                !result) {
+                LOG(FATAL) << "Could not reboot into recovery: " << result.error();
+            }
+        } else {
+            LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
+        }
+    };
+
+    std::vector<std::string> args = {"exec", "/system/bin/vdc", "--wait", "cryptfs", vdc_arg};
+    return ExecWithFunctionOnFailure(args, reboot);
+}
+
 static Result<void> do_installkey(const BuiltinArguments& args) {
     if (!is_file_crypto()) return {};
 
@@ -1107,15 +1132,11 @@
     if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
         return ErrnoError() << "Failed to create " << unencrypted_dir;
     }
-    return ExecWithRebootOnFailure(
-        "enablefilecrypto_failed",
-        {{"exec", "/system/bin/vdc", "--wait", "cryptfs", "enablefilecrypto"}, args.context});
+    return ExecVdcRebootOnFailure("enablefilecrypto");
 }
 
 static Result<void> do_init_user0(const BuiltinArguments& args) {
-    return ExecWithRebootOnFailure(
-        "init_user0_failed",
-        {{"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"}, args.context});
+    return ExecVdcRebootOnFailure("init_user0");
 }
 
 static Result<void> do_mark_post_data(const BuiltinArguments& args) {
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
index 771f1d7..2efaeea 100644
--- a/init/check_builtins.cpp
+++ b/init/check_builtins.cpp
@@ -81,6 +81,15 @@
     return check_exec(std::move(args));
 }
 
+Result<void> check_exec_reboot_on_failure(const BuiltinArguments& args) {
+    BuiltinArguments remaining_args(args.context);
+
+    remaining_args.args = std::vector<std::string>(args.begin() + 1, args.end());
+    remaining_args.args[0] = args[0];
+
+    return check_exec(remaining_args);
+}
+
 Result<void> check_interface_restart(const BuiltinArguments& args) {
     if (auto result = IsKnownInterface(args[1]); !result) {
         return result.error();
diff --git a/init/check_builtins.h b/init/check_builtins.h
index 4ff0d0c..fb34556 100644
--- a/init/check_builtins.h
+++ b/init/check_builtins.h
@@ -25,6 +25,7 @@
 Result<void> check_chown(const BuiltinArguments& args);
 Result<void> check_exec(const BuiltinArguments& args);
 Result<void> check_exec_background(const BuiltinArguments& args);
+Result<void> check_exec_reboot_on_failure(const BuiltinArguments& args);
 Result<void> check_interface_restart(const BuiltinArguments& args);
 Result<void> check_interface_start(const BuiltinArguments& args);
 Result<void> check_interface_stop(const BuiltinArguments& args);
diff --git a/init/epoll.cpp b/init/epoll.cpp
index 01d8867..17d63fa 100644
--- a/init/epoll.cpp
+++ b/init/epoll.cpp
@@ -69,19 +69,24 @@
     return {};
 }
 
-Result<void> Epoll::Wait(std::optional<std::chrono::milliseconds> timeout) {
+Result<std::vector<std::function<void()>*>> Epoll::Wait(
+        std::optional<std::chrono::milliseconds> timeout) {
     int timeout_ms = -1;
     if (timeout && timeout->count() < INT_MAX) {
         timeout_ms = timeout->count();
     }
-    epoll_event ev;
-    auto nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_, &ev, 1, timeout_ms));
-    if (nr == -1) {
+    const auto max_events = epoll_handlers_.size();
+    epoll_event ev[max_events];
+    auto num_events = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_, ev, max_events, timeout_ms));
+    if (num_events == -1) {
         return ErrnoError() << "epoll_wait failed";
-    } else if (nr == 1) {
-        std::invoke(*reinterpret_cast<std::function<void()>*>(ev.data.ptr));
     }
-    return {};
+    std::vector<std::function<void()>*> pending_functions;
+    for (int i = 0; i < num_events; ++i) {
+        pending_functions.emplace_back(reinterpret_cast<std::function<void()>*>(ev[i].data.ptr));
+    }
+
+    return pending_functions;
 }
 
 }  // namespace init
diff --git a/init/epoll.h b/init/epoll.h
index ca84266..c32a661 100644
--- a/init/epoll.h
+++ b/init/epoll.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_EPOLL_H
-#define _INIT_EPOLL_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/epoll.h>
@@ -24,6 +23,7 @@
 #include <functional>
 #include <map>
 #include <optional>
+#include <vector>
 
 #include <android-base/unique_fd.h>
 
@@ -39,7 +39,8 @@
     Result<void> Open();
     Result<void> RegisterHandler(int fd, std::function<void()> handler, uint32_t events = EPOLLIN);
     Result<void> UnregisterHandler(int fd);
-    Result<void> Wait(std::optional<std::chrono::milliseconds> timeout);
+    Result<std::vector<std::function<void()>*>> Wait(
+            std::optional<std::chrono::milliseconds> timeout);
 
   private:
     android::base::unique_fd epoll_fd_;
@@ -48,5 +49,3 @@
 
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index c067f6f..1dce2d5 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -17,6 +17,10 @@
 #include "firmware_handler.h"
 
 #include <fcntl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/sendfile.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -26,25 +30,29 @@
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 
+using android::base::ReadFdToString;
+using android::base::Socketpair;
+using android::base::Split;
 using android::base::Timer;
+using android::base::Trim;
 using android::base::unique_fd;
 using android::base::WriteFully;
 
 namespace android {
 namespace init {
 
-static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
-                         int loading_fd, int data_fd) {
+static void LoadFirmware(const std::string& firmware, const std::string& root, int fw_fd,
+                         size_t fw_size, int loading_fd, int data_fd) {
     // Start transfer.
     WriteFully(loading_fd, "1", 1);
 
     // Copy the firmware.
     int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
     if (rc == -1) {
-        PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent.firmware
-                    << "' }";
+        PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << firmware << "' }";
     }
 
     // Tell the firmware whether to abort or commit.
@@ -56,36 +64,151 @@
     return access("/dev/.booting", F_OK) == 0;
 }
 
-FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories)
-    : firmware_directories_(std::move(firmware_directories)) {}
+FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories,
+                                 std::vector<ExternalFirmwareHandler> external_firmware_handlers)
+    : firmware_directories_(std::move(firmware_directories)),
+      external_firmware_handlers_(std::move(external_firmware_handlers)) {}
 
-void FirmwareHandler::ProcessFirmwareEvent(const Uevent& uevent) {
-    int booting = IsBooting();
+Result<std::string> FirmwareHandler::RunExternalHandler(const std::string& handler, uid_t uid,
+                                                        const Uevent& uevent) const {
+    unique_fd child_stdout;
+    unique_fd parent_stdout;
+    if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &child_stdout, &parent_stdout)) {
+        return ErrnoError() << "Socketpair() for stdout failed";
+    }
 
+    unique_fd child_stderr;
+    unique_fd parent_stderr;
+    if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &child_stderr, &parent_stderr)) {
+        return ErrnoError() << "Socketpair() for stderr failed";
+    }
+
+    signal(SIGCHLD, SIG_DFL);
+
+    auto pid = fork();
+    if (pid < 0) {
+        return ErrnoError() << "fork() failed";
+    }
+
+    if (pid == 0) {
+        setenv("FIRMWARE", uevent.firmware.c_str(), 1);
+        setenv("DEVPATH", uevent.path.c_str(), 1);
+        parent_stdout.reset();
+        parent_stderr.reset();
+        close(STDOUT_FILENO);
+        close(STDERR_FILENO);
+        dup2(child_stdout.get(), STDOUT_FILENO);
+        dup2(child_stderr.get(), STDERR_FILENO);
+
+        auto args = Split(handler, " ");
+        std::vector<char*> c_args;
+        for (auto& arg : args) {
+            c_args.emplace_back(arg.data());
+        }
+        c_args.emplace_back(nullptr);
+
+        if (setuid(uid) != 0) {
+            fprintf(stderr, "setuid() failed: %s", strerror(errno));
+            _exit(EXIT_FAILURE);
+        }
+
+        execv(c_args[0], c_args.data());
+        fprintf(stderr, "exec() failed: %s", strerror(errno));
+        _exit(EXIT_FAILURE);
+    }
+
+    child_stdout.reset();
+    child_stderr.reset();
+
+    int status;
+    pid_t waited_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+    if (waited_pid == -1) {
+        return ErrnoError() << "waitpid() failed";
+    }
+
+    std::string stdout_content;
+    if (!ReadFdToString(parent_stdout.get(), &stdout_content)) {
+        return ErrnoError() << "ReadFdToString() for stdout failed";
+    }
+
+    std::string stderr_content;
+    if (ReadFdToString(parent_stderr.get(), &stderr_content)) {
+        auto messages = Split(stderr_content, "\n");
+        for (const auto& message : messages) {
+            if (!message.empty()) {
+                LOG(ERROR) << "External Firmware Handler: " << message;
+            }
+        }
+    } else {
+        LOG(ERROR) << "ReadFdToString() for stderr failed";
+    }
+
+    if (WIFEXITED(status)) {
+        if (WEXITSTATUS(status) == EXIT_SUCCESS) {
+            return Trim(stdout_content);
+        } else {
+            return Error() << "exited with status " << WEXITSTATUS(status);
+        }
+    } else if (WIFSIGNALED(status)) {
+        return Error() << "killed by signal " << WTERMSIG(status);
+    }
+
+    return Error() << "unexpected exit status " << status;
+}
+
+std::string FirmwareHandler::GetFirmwarePath(const Uevent& uevent) const {
+    for (const auto& external_handler : external_firmware_handlers_) {
+        if (external_handler.devpath == uevent.path) {
+            LOG(INFO) << "Launching external firmware handler '" << external_handler.handler_path
+                      << "' for devpath: '" << uevent.path << "' firmware: '" << uevent.firmware
+                      << "'";
+
+            auto result =
+                    RunExternalHandler(external_handler.handler_path, external_handler.uid, uevent);
+            if (!result) {
+                LOG(ERROR) << "Using default firmware; External firmware handler failed: "
+                           << result.error();
+                return uevent.firmware;
+            }
+            if (result->find("..") != std::string::npos) {
+                LOG(ERROR) << "Using default firmware; External firmware handler provided an "
+                              "invalid path, '"
+                           << *result << "'";
+                return uevent.firmware;
+            }
+            LOG(INFO) << "Loading firmware '" << *result << "' in place of '" << uevent.firmware
+                      << "'";
+            return *result;
+        }
+    }
     LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'";
+    return uevent.firmware;
+}
 
-    std::string root = "/sys" + uevent.path;
+void FirmwareHandler::ProcessFirmwareEvent(const std::string& root,
+                                           const std::string& firmware) const {
     std::string loading = root + "/loading";
     std::string data = root + "/data";
 
     unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC));
     if (loading_fd == -1) {
-        PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent.firmware;
+        PLOG(ERROR) << "couldn't open firmware loading fd for " << firmware;
         return;
     }
 
     unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC));
     if (data_fd == -1) {
-        PLOG(ERROR) << "couldn't open firmware data fd for " << uevent.firmware;
+        PLOG(ERROR) << "couldn't open firmware data fd for " << firmware;
         return;
     }
 
     std::vector<std::string> attempted_paths_and_errors;
 
+    int booting = IsBooting();
 try_loading_again:
     attempted_paths_and_errors.clear();
     for (const auto& firmware_directory : firmware_directories_) {
-        std::string file = firmware_directory + uevent.firmware;
+        std::string file = firmware_directory + firmware;
         unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
         if (fw_fd == -1) {
             attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
@@ -98,7 +221,7 @@
                                                     ", fstat failed: " + strerror(errno));
             continue;
         }
-        LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
+        LoadFirmware(firmware, root, fw_fd, sb.st_size, loading_fd, data_fd);
         return;
     }
 
@@ -110,7 +233,7 @@
         goto try_loading_again;
     }
 
-    LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware;
+    LOG(ERROR) << "firmware: could not find firmware for " << firmware;
     for (const auto& message : attempted_paths_and_errors) {
         LOG(ERROR) << message;
     }
@@ -129,7 +252,8 @@
     }
     if (pid == 0) {
         Timer t;
-        ProcessFirmwareEvent(uevent);
+        auto firmware = GetFirmwarePath(uevent);
+        ProcessFirmwareEvent("/sys" + uevent.path, firmware);
         LOG(INFO) << "loading " << uevent.path << " took " << t;
         _exit(EXIT_SUCCESS);
     }
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index 3996096..b4138f1 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -14,32 +14,48 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_FIRMWARE_HANDLER_H
-#define _INIT_FIRMWARE_HANDLER_H
+#pragma once
+
+#include <pwd.h>
 
 #include <string>
 #include <vector>
 
+#include "result.h"
 #include "uevent.h"
 #include "uevent_handler.h"
 
 namespace android {
 namespace init {
 
+struct ExternalFirmwareHandler {
+    ExternalFirmwareHandler(std::string devpath, uid_t uid, std::string handler_path)
+        : devpath(std::move(devpath)), uid(uid), handler_path(std::move(handler_path)) {}
+    std::string devpath;
+    uid_t uid;
+    std::string handler_path;
+};
+
 class FirmwareHandler : public UeventHandler {
   public:
-    explicit FirmwareHandler(std::vector<std::string> firmware_directories);
+    FirmwareHandler(std::vector<std::string> firmware_directories,
+                    std::vector<ExternalFirmwareHandler> external_firmware_handlers);
     virtual ~FirmwareHandler() = default;
 
     void HandleUevent(const Uevent& uevent) override;
 
   private:
-    void ProcessFirmwareEvent(const Uevent& uevent);
+    friend void FirmwareTestWithExternalHandler(const std::string& test_name,
+                                                bool expect_new_firmware);
+
+    Result<std::string> RunExternalHandler(const std::string& handler, uid_t uid,
+                                           const Uevent& uevent) const;
+    std::string GetFirmwarePath(const Uevent& uevent) const;
+    void ProcessFirmwareEvent(const std::string& root, const std::string& firmware) const;
 
     std::vector<std::string> firmware_directories_;
+    std::vector<ExternalFirmwareHandler> external_firmware_handlers_;
 };
 
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/init/firmware_handler_test.cpp b/init/firmware_handler_test.cpp
new file mode 100644
index 0000000..7bb603c
--- /dev/null
+++ b/init/firmware_handler_test.cpp
@@ -0,0 +1,125 @@
+/*
+ * 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 "firmware_handler.h"
+
+#include <stdlib.h>
+#include <iostream>
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include "uevent.h"
+
+using android::base::GetExecutablePath;
+using namespace std::literals;
+
+namespace android {
+namespace init {
+
+void FirmwareTestWithExternalHandler(const std::string& test_name, bool expect_new_firmware) {
+    auto test_path = GetExecutablePath() + " firmware " + test_name;
+    auto external_firmware_handler = ExternalFirmwareHandler(
+            "/devices/led/firmware/test_firmware001.bin", getuid(), test_path);
+
+    auto firmware_handler = FirmwareHandler({"/test"}, {external_firmware_handler});
+
+    auto uevent = Uevent{
+            .path = "/devices/led/firmware/test_firmware001.bin",
+            .firmware = "test_firmware001.bin",
+    };
+
+    if (expect_new_firmware) {
+        EXPECT_EQ("other_firmware001.bin", firmware_handler.GetFirmwarePath(uevent));
+    } else {
+        EXPECT_EQ("test_firmware001.bin", firmware_handler.GetFirmwarePath(uevent));
+    }
+
+    // Always test the base case that the handler isn't invoked if the devpath doesn't match.
+    auto uevent_different_path = Uevent{
+            .path = "/devices/led/not/mine",
+            .firmware = "test_firmware001.bin",
+    };
+    EXPECT_EQ("test_firmware001.bin", firmware_handler.GetFirmwarePath(uevent_different_path));
+}
+
+TEST(firmware_handler, HandleChange) {
+    FirmwareTestWithExternalHandler("HandleChange", true);
+}
+
+int HandleChange(int argc, char** argv) {
+    // Assert that the environment is set up correctly.
+    if (getenv("DEVPATH") != "/devices/led/firmware/test_firmware001.bin"s) {
+        std::cerr << "$DEVPATH not set correctly" << std::endl;
+        return EXIT_FAILURE;
+    }
+    if (getenv("FIRMWARE") != "test_firmware001.bin"s) {
+        std::cerr << "$FIRMWARE not set correctly" << std::endl;
+        return EXIT_FAILURE;
+    }
+    std::cout << "other_firmware001.bin" << std::endl;
+    return 0;
+}
+
+TEST(firmware_handler, HandleAbort) {
+    FirmwareTestWithExternalHandler("HandleAbort", false);
+}
+
+int HandleAbort(int argc, char** argv) {
+    abort();
+    return 0;
+}
+
+TEST(firmware_handler, HandleFailure) {
+    FirmwareTestWithExternalHandler("HandleFailure", false);
+}
+
+int HandleFailure(int argc, char** argv) {
+    std::cerr << "Failed" << std::endl;
+    return EXIT_FAILURE;
+}
+
+TEST(firmware_handler, HandleBadPath) {
+    FirmwareTestWithExternalHandler("HandleBadPath", false);
+}
+
+int HandleBadPath(int argc, char** argv) {
+    std::cout << "../firmware.bin";
+    return 0;
+}
+
+}  // namespace init
+}  // namespace android
+
+// init_test.cpp contains the main entry point for all init tests.
+int FirmwareTestChildMain(int argc, char** argv) {
+    if (argc < 3) {
+        return 1;
+    }
+
+#define RunTest(testname)                           \
+    if (argv[2] == std::string(#testname)) {        \
+        return android::init::testname(argc, argv); \
+    }
+
+    RunTest(HandleChange);
+    RunTest(HandleAbort);
+    RunTest(HandleFailure);
+    RunTest(HandleBadPath);
+
+#undef RunTest
+    return 1;
+}
diff --git a/init/fscrypt_init_extensions.cpp b/init/fscrypt_init_extensions.cpp
index 0f5a864..bd23e31 100644
--- a/init/fscrypt_init_extensions.cpp
+++ b/init/fscrypt_init_extensions.cpp
@@ -38,7 +38,7 @@
 
 #define TAG "fscrypt"
 
-static int set_system_de_policy_on(const std::string& dir);
+static int set_policy_on(const std::string& ref_basename, const std::string& dir);
 
 int fscrypt_install_keyring() {
     key_serial_t device_keyring = add_key("keyring", "fscrypt", 0, 0, KEY_SPEC_SESSION_KEYRING);
@@ -104,7 +104,7 @@
     // Special-case /data/media/obb per b/64566063
     if (dir == "/data/media/obb") {
         // Try to set policy on this directory, but if it is non-empty this may fail.
-        set_system_de_policy_on(dir);
+        set_policy_on(fscrypt_key_ref, dir);
         return 0;
     }
 
@@ -135,7 +135,16 @@
             return 0;
         }
     }
-    int err = set_system_de_policy_on(dir);
+    std::vector<std::string> per_boot_directories = {
+            "per_boot",
+    };
+    for (const auto& d : per_boot_directories) {
+        if ((prefix + d) == dir) {
+            LOG(INFO) << "Setting per_boot key on " << dir;
+            return set_policy_on(fscrypt_key_per_boot_ref, dir);
+        }
+    }
+    int err = set_policy_on(fscrypt_key_ref, dir);
     if (err == 0) {
         return 0;
     }
@@ -147,15 +156,15 @@
         if ((prefix + d) == dir) {
             LOG(ERROR) << "Setting policy failed, deleting: " << dir;
             delete_dir_contents(dir);
-            err = set_system_de_policy_on(dir);
+            err = set_policy_on(fscrypt_key_ref, dir);
             break;
         }
     }
     return err;
 }
 
-static int set_system_de_policy_on(const std::string& dir) {
-    std::string ref_filename = std::string("/data") + fscrypt_key_ref;
+static int set_policy_on(const std::string& ref_basename, const std::string& dir) {
+    std::string ref_filename = std::string("/data") + ref_basename;
     std::string policy;
     if (!android::base::ReadFileToString(ref_filename, &policy)) {
         LOG(ERROR) << "Unable to read system policy to set on " << dir;
@@ -175,7 +184,6 @@
         return -1;
     }
 
-    LOG(INFO) << "Setting policy on " << dir;
     int result =
             fscrypt_policy_ensure(dir.c_str(), policy.c_str(), policy.length(), modes[0].c_str(),
                                   modes.size() >= 2 ? modes[1].c_str() : "aes-256-cts");
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index f9a08a5..e3068b2 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -34,6 +34,11 @@
 namespace android {
 namespace init {
 
+// init.h
+inline void EnterShutdown(const std::string&) {
+    abort();
+}
+
 // property_service.h
 inline bool CanReadProperty(const std::string&, const std::string&) {
     return true;
diff --git a/init/init.cpp b/init/init.cpp
index 4e5f2c1..8326466 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -53,7 +53,6 @@
 #include <selinux/android.h>
 
 #include "action_parser.h"
-#include "boringssl_self_test.h"
 #include "builtins.h"
 #include "epoll.h"
 #include "first_stage_init.h"
@@ -180,6 +179,16 @@
     waiting_for_prop.reset();
 }
 
+void EnterShutdown(const std::string& command) {
+    // We can't call HandlePowerctlMessage() directly in this function,
+    // because it modifies the contents of the action queue, which can cause the action queue
+    // to get into a bad state if this function is called from a command being executed by the
+    // action queue.  Instead we set this flag and ensure that shutdown happens before the next
+    // command is run in the main init loop.
+    shutdown_command = command;
+    do_shutdown = true;
+}
+
 void property_changed(const std::string& name, const std::string& value) {
     // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
     // This is to ensure that init will always and immediately shutdown/reboot, regardless of
@@ -188,16 +197,7 @@
     // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
     // commands to be executed.
     if (name == "sys.powerctl") {
-        // Despite the above comment, we can't call HandlePowerctlMessage() in this function,
-        // because it modifies the contents of the action queue, which can cause the action queue
-        // to get into a bad state if this function is called from a command being executed by the
-        // action queue.  Instead we set this flag and ensure that shutdown happens before the next
-        // command is run in the main init loop.
-        // TODO: once property service is removed from init, this will never happen from a builtin,
-        // but rather from a callback from the property service socket, in which case this hack can
-        // go away.
-        shutdown_command = value;
-        do_shutdown = true;
+        EnterShutdown(value);
     }
 
     if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
@@ -310,9 +310,6 @@
         process_cmdline = "unknown process";
     }
 
-    LOG(INFO) << "Received control message '" << msg << "' for '" << name << "' from pid: " << pid
-              << " (" << process_cmdline << ")";
-
     const ControlMessageFunction& function = it->second;
 
     Service* svc = nullptr;
@@ -325,20 +322,25 @@
             svc = ServiceList::GetInstance().FindInterface(name);
             break;
         default:
-            LOG(ERROR) << "Invalid function target from static map key '" << msg << "': "
+            LOG(ERROR) << "Invalid function target from static map key ctl." << msg << ": "
                        << static_cast<std::underlying_type<ControlTarget>::type>(function.target);
             return false;
     }
 
     if (svc == nullptr) {
-        LOG(ERROR) << "Could not find '" << name << "' for ctl." << msg;
+        LOG(ERROR) << "Control message: Could not find '" << name << "' for ctl." << msg
+                   << " from pid: " << pid << " (" << process_cmdline << ")";
         return false;
     }
 
     if (auto result = function.action(svc); !result) {
-        LOG(ERROR) << "Could not ctl." << msg << " for '" << name << "': " << result.error();
+        LOG(ERROR) << "Control message: Could not ctl." << msg << " for '" << name
+                   << "' from pid: " << pid << " (" << process_cmdline << "): " << result.error();
         return false;
     }
+
+    LOG(INFO) << "Control message: Processed ctl." << msg << " for '" << name
+              << "' from pid: " << pid << " (" << process_cmdline << ")";
     return true;
 }
 
@@ -609,6 +611,22 @@
                                 selinux_start_time_ns));
 }
 
+void SendLoadPersistentPropertiesMessage() {
+    auto init_message = InitMessage{};
+    init_message.set_load_persistent_properties(true);
+    if (auto result = SendMessage(property_fd, init_message); !result) {
+        LOG(ERROR) << "Failed to send load persistent properties message: " << result.error();
+    }
+}
+
+void SendStopSendingMessagesMessage() {
+    auto init_message = InitMessage{};
+    init_message.set_stop_sending_messages(true);
+    if (auto result = SendMessage(property_fd, init_message); !result) {
+        LOG(ERROR) << "Failed to send load persistent properties message: " << result.error();
+    }
+}
+
 static void HandlePropertyFd() {
     auto message = ReadMessage(property_fd);
     if (!message) {
@@ -751,6 +769,7 @@
 
     am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
 
+    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
     am.QueueEventTrigger("early-init");
 
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
@@ -758,7 +777,6 @@
     // ... so that we can start queuing up actions that require stuff from /dev.
     am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
     am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
-    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
     Keychords keychords;
     am.QueueBuiltinAction(
             [&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
@@ -773,9 +791,6 @@
     // Trigger all the boot actions to get us started.
     am.QueueEventTrigger("init");
 
-    // Starting the BoringSSL self test, for NIAP certification compliance.
-    am.QueueBuiltinAction(StartBoringSslSelfTest, "StartBoringSslSelfTest");
-
     // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
     // wasn't ready immediately after wait_for_coldboot_done
     am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
@@ -821,8 +836,17 @@
             if (am.HasMoreCommands()) epoll_timeout = 0ms;
         }
 
-        if (auto result = epoll.Wait(epoll_timeout); !result) {
-            LOG(ERROR) << result.error();
+        auto pending_functions = epoll.Wait(epoll_timeout);
+        if (!pending_functions) {
+            LOG(ERROR) << pending_functions.error();
+        } else if (!pending_functions->empty()) {
+            // We always reap children before responding to the other pending functions. This is to
+            // prevent a race where other daemons see that a service has exited and ask init to
+            // start it again via ctl.start before init has reaped it.
+            ReapAnyOutstandingChildren();
+            for (const auto& function : *pending_functions) {
+                (*function)();
+            }
         }
     }
 
diff --git a/init/init.h b/init/init.h
index 624a3d4..61fb110 100644
--- a/init/init.h
+++ b/init/init.h
@@ -31,12 +31,17 @@
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
 Parser CreateServiceOnlyParser(ServiceList& service_list);
 
+void EnterShutdown(const std::string& command);
+
 bool start_waiting_for_property(const char *name, const char *value);
 
 void DumpState();
 
 void ResetWaitForProp();
 
+void SendLoadPersistentPropertiesMessage();
+void SendStopSendingMessagesMessage();
+
 int SecondStageMain(int argc, char** argv);
 
 }  // namespace init
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 0411214..315d584 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -221,3 +221,19 @@
 
 }  // namespace init
 }  // namespace android
+
+int SubcontextTestChildMain(int, char**);
+int FirmwareTestChildMain(int, char**);
+
+int main(int argc, char** argv) {
+    if (argc > 1 && !strcmp(argv[1], "subcontext")) {
+        return SubcontextTestChildMain(argc, argv);
+    }
+
+    if (argc > 1 && !strcmp(argv[1], "firmware")) {
+        return FirmwareTestChildMain(argc, argv);
+    }
+
+    testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/init/interface_utils.cpp b/init/interface_utils.cpp
index a54860f..ddbacd7 100644
--- a/init/interface_utils.cpp
+++ b/init/interface_utils.cpp
@@ -77,6 +77,12 @@
                                                 const InterfaceInheritanceHierarchyMap& hierarchy) {
     std::set<FQName> interface_fqnames;
     for (const std::string& instance : instances) {
+        // There is insufficient build-time information on AIDL interfaces to check them here
+        // TODO(b/139307527): Rework how services store interfaces to avoid excess string parsing
+        if (base::Split(instance, "/")[0] == "aidl") {
+            continue;
+        }
+
         FqInstance fqinstance;
         if (!fqinstance.setTo(instance)) {
             return Error() << "Unable to parse interface instance '" << instance << "'";
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
index 33373d4..6e9b337 100644
--- a/init/keychords_test.cpp
+++ b/init/keychords_test.cpp
@@ -212,7 +212,11 @@
 }
 
 void TestFrame::RelaxForMs(std::chrono::milliseconds wait) {
-    epoll_.Wait(wait);
+    auto pending_functions = epoll_.Wait(wait);
+    ASSERT_TRUE(pending_functions) << pending_functions.error();
+    for (const auto& function : *pending_functions) {
+        (*function)();
+    }
 }
 
 void TestFrame::SetChord(int key, bool value) {
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 12144c1..0745148 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -91,22 +91,22 @@
         return false;
     }
 
-    // Special casing for the runtime APEX
-    constexpr const char kRuntimeApexMountPath[] = "/system/apex/com.android.runtime";
-    static const std::vector<std::string> kRuntimeApexDirNames = {"com.android.runtime.release",
-                                                                  "com.android.runtime.debug"};
+    // Special casing for the ART APEX
+    constexpr const char kArtApexMountPath[] = "/system/apex/com.android.art";
+    static const std::vector<std::string> kArtApexDirNames = {"com.android.art.release",
+                                                              "com.android.art.debug"};
     bool success = false;
-    for (const auto& name : kRuntimeApexDirNames) {
+    for (const auto& name : kArtApexDirNames) {
         std::string path = std::string(kSystemApex) + "/" + name;
         if (access(path.c_str(), F_OK) == 0) {
-            if (mount(path.c_str(), kRuntimeApexMountPath, nullptr, MS_BIND, nullptr) == 0) {
+            if (mount(path.c_str(), kArtApexMountPath, nullptr, MS_BIND, nullptr) == 0) {
                 success = true;
                 break;
             }
         }
     }
     if (!success) {
-        PLOG(ERROR) << "Failed to bind mount the runtime APEX to " << kRuntimeApexMountPath;
+        PLOG(ERROR) << "Failed to bind mount the ART APEX to " << kArtApexMountPath;
     }
     return success;
 }
diff --git a/init/property_service.cpp b/init/property_service.cpp
index c78b81b..c18decc 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -209,11 +209,6 @@
     if (persistent_properties_loaded && StartsWith(name, "persist.")) {
         WritePersistentProperty(name, value);
     }
-    // If init sets ro.persistent_properties.ready to true, then it has finished writing persistent
-    // properties, and we should write future persistent properties to disk.
-    if (name == "ro.persistent_properties.ready" && value == "true") {
-        persistent_properties_loaded = true;
-    }
     // If init hasn't started its main loop, then it won't be handling property changed messages
     // anyway, so there's no need to try to send them.
     if (init_socket != -1) {
@@ -299,7 +294,7 @@
 
     bool SendUint32(uint32_t value) {
         if (!socket_.ok()) {
-            return false;
+            return true;
         }
         int result = TEMP_FAILURE_RETRY(send(socket_, &value, sizeof(value), 0));
         return result == sizeof(value);
@@ -315,9 +310,7 @@
         return true;
     }
 
-    int Release() { return socket_.release(); }
-
-    int socket() { return socket_.get(); }
+    [[nodiscard]] int Release() { return socket_.release(); }
 
     const ucred& cred() { return cred_; }
 
@@ -390,29 +383,41 @@
 
     unique_fd socket_;
     ucred cred_;
+
+    DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
 };
 
 static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid,
                                    SocketConnection* socket, std::string* error) {
+    if (init_socket == -1) {
+        *error = "Received control message after shutdown, ignoring";
+        return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+    }
+
     auto property_msg = PropertyMessage{};
     auto* control_message = property_msg.mutable_control_message();
     control_message->set_msg(msg);
     control_message->set_name(name);
     control_message->set_pid(pid);
+
+    // We must release the fd before sending it to init, otherwise there will be a race with init.
+    // If init calls close() before Release(), then fdsan will see the wrong tag and abort().
+    int fd = -1;
     if (socket != nullptr) {
-        control_message->set_fd(socket->socket());
+        fd = socket->Release();
+        control_message->set_fd(fd);
     }
 
     if (auto result = SendMessage(init_socket, property_msg); !result) {
+        // We've already released the fd above, so if we fail to send the message to init, we need
+        // to manually free it here.
+        if (fd != -1) {
+            close(fd);
+        }
         *error = "Failed to send control message: " + result.error().message();
         return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
     }
 
-    if (socket != nullptr) {
-        // We've successfully sent the fd to init, so release it here.
-        socket->Release();
-    }
-
     return PROP_SUCCESS;
 }
 
@@ -617,7 +622,6 @@
             LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
                        << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
         }
-
         socket.SendUint32(result);
         break;
       }
@@ -777,32 +781,6 @@
     }
 }
 
-/* When booting an encrypted system, /data is not mounted when the
- * property service is started, so any properties stored there are
- * not loaded.  Vold triggers init to load these properties once it
- * has mounted /data.
- */
-void load_persist_props(void) {
-    // Devices with FDE have load_persist_props called twice; the first time when the temporary
-    // /data partition is mounted and then again once /data is truly mounted.  We do not want to
-    // read persistent properties from the temporary /data partition or mark persistent properties
-    // as having been loaded during the first call, so we return in that case.
-    std::string crypto_state = android::base::GetProperty("ro.crypto.state", "");
-    std::string crypto_type = android::base::GetProperty("ro.crypto.type", "");
-    if (crypto_state == "encrypted" && crypto_type == "block") {
-        static size_t num_calls = 0;
-        if (++num_calls == 1) return;
-    }
-
-    load_override_properties();
-    /* Read persistent properties after all default values have been loaded. */
-    auto persistent_properties = LoadPersistentProperties();
-    for (const auto& persistent_property_record : persistent_properties.properties()) {
-        property_set(persistent_property_record.name(), persistent_property_record.value());
-    }
-    property_set("ro.persistent_properties.ready", "true");
-}
-
 // If the ro.product.[brand|device|manufacturer|model|name] properties have not been explicitly
 // set, derive them from ro.product.${partition}.* properties
 static void property_initialize_ro_product_props() {
@@ -1020,9 +998,64 @@
     selinux_android_restorecon(kPropertyInfosPath, 0);
 }
 
+static void HandleInitSocket() {
+    auto message = ReadMessage(init_socket);
+    if (!message) {
+        LOG(ERROR) << "Could not read message from init_dedicated_recv_socket: " << message.error();
+        return;
+    }
+
+    auto init_message = InitMessage{};
+    if (!init_message.ParseFromString(*message)) {
+        LOG(ERROR) << "Could not parse message from init";
+        return;
+    }
+
+    switch (init_message.msg_case()) {
+        case InitMessage::kLoadPersistentProperties: {
+            load_override_properties();
+            // Read persistent properties after all default values have been loaded.
+            auto persistent_properties = LoadPersistentProperties();
+            for (const auto& persistent_property_record : persistent_properties.properties()) {
+                InitPropertySet(persistent_property_record.name(),
+                                persistent_property_record.value());
+            }
+            InitPropertySet("ro.persistent_properties.ready", "true");
+            persistent_properties_loaded = true;
+            break;
+        }
+        case InitMessage::kStopSendingMessages: {
+            init_socket = -1;
+            break;
+        }
+        default:
+            LOG(ERROR) << "Unknown message type from init: " << init_message.msg_case();
+    }
+}
+
 static void PropertyServiceThread() {
+    Epoll epoll;
+    if (auto result = epoll.Open(); !result) {
+        LOG(FATAL) << result.error();
+    }
+
+    if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
+        LOG(FATAL) << result.error();
+    }
+
+    if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result) {
+        LOG(FATAL) << result.error();
+    }
+
     while (true) {
-        handle_property_set_fd();
+        auto pending_functions = epoll.Wait(std::nullopt);
+        if (!pending_functions) {
+            LOG(ERROR) << pending_functions.error();
+        } else {
+            for (const auto& function : *pending_functions) {
+                (*function)();
+            }
+        }
     }
 }
 
@@ -1036,8 +1069,8 @@
     *epoll_socket = sockets[0];
     init_socket = sockets[1];
 
-    if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC, false, 0666, 0, 0,
-                                   {})) {
+    if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+                                   false, 0666, 0, 0, {})) {
         property_set_fd = *result;
     } else {
         LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
diff --git a/init/property_service.h b/init/property_service.h
index 2dc92a5..8f7d8d9 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -33,7 +33,6 @@
 
 void property_init();
 void property_load_boot_defaults(bool load_debug_prop);
-void load_persist_props();
 void StartPropertyService(int* epoll_socket);
 
 }  // namespace init
diff --git a/init/property_service.proto b/init/property_service.proto
index f711526..ea454d4 100644
--- a/init/property_service.proto
+++ b/init/property_service.proto
@@ -35,3 +35,10 @@
         ChangedMessage changed_message = 2;
     };
 }
+
+message InitMessage {
+    oneof msg {
+        bool load_persistent_properties = 1;
+        bool stop_sending_messages = 2;
+    };
+}
diff --git a/init/reboot.cpp b/init/reboot.cpp
index b0b5b54..786a084 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -730,6 +730,12 @@
         s->UnSetExec();
     }
 
+    // We no longer process messages about properties changing coming from property service, so we
+    // need to tell property service to stop sending us these messages, otherwise it'll fill the
+    // buffers and block indefinitely, causing future property sets, including those that init makes
+    // during shutdown in Service::NotifyStateChange() to also block indefinitely.
+    SendStopSendingMessagesMessage();
+
     return true;
 }
 
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index d1a712f..de085cc 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -21,11 +21,12 @@
 
 #include <string>
 
-#include "android-base/file.h"
-#include "android-base/logging.h"
-#include "android-base/strings.h"
-#include "backtrace/Backtrace.h"
-#include "cutils/android_reboot.h"
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <backtrace/Backtrace.h>
+#include <cutils/android_reboot.h>
 
 #include "capabilities.h"
 
@@ -93,7 +94,14 @@
             break;
 
         case ANDROID_RB_THERMOFF:
-            reboot(RB_POWER_OFF);
+            if (android::base::GetBoolProperty("ro.thermal_warmreset", false)) {
+                LOG(INFO) << "Try to trigger a warm reset for thermal shutdown";
+                static constexpr const char kThermalShutdownTarget[] = "shutdown,thermal";
+                syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
+                        LINUX_REBOOT_CMD_RESTART2, kThermalShutdownTarget);
+            } else {
+                reboot(RB_POWER_OFF);
+            }
             break;
     }
     // In normal case, reboot should not return.
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 6842820..4852cd0 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -439,7 +439,8 @@
     bool is_enforcing = IsEnforcing();
     if (kernel_enforcing != is_enforcing) {
         if (security_setenforce(is_enforcing)) {
-            PLOG(FATAL) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
+            PLOG(FATAL) << "security_setenforce(" << (is_enforcing ? "true" : "false")
+                        << ") failed";
         }
     }
 
diff --git a/init/service.cpp b/init/service.cpp
index 9537843..793a2b2 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -29,6 +29,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <cutils/sockets.h>
@@ -41,6 +42,7 @@
 #if defined(__ANDROID__)
 #include <ApexProperties.sysprop.h>
 
+#include "init.h"
 #include "mount_namespace.h"
 #include "property_service.h"
 #else
@@ -50,6 +52,7 @@
 using android::base::boot_clock;
 using android::base::GetProperty;
 using android::base::Join;
+using android::base::make_scope_guard;
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
@@ -116,9 +119,10 @@
     return execv(c_strings[0], c_strings.data()) == 0;
 }
 
-static bool IsRuntimeApexReady() {
+static bool AreRuntimeApexesReady() {
     struct stat buf;
-    return stat("/apex/com.android.runtime/", &buf) == 0;
+    return stat("/apex/com.android.art/", &buf) == 0 &&
+           stat("/apex/com.android.runtime/", &buf) == 0;
 }
 
 unsigned long Service::next_start_order_ = 1;
@@ -249,6 +253,11 @@
         f(siginfo);
     }
 
+    if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_) {
+        LOG(ERROR) << "Service with 'reboot_on_failure' option failed, shutting down system.";
+        EnterShutdown(*on_failure_reboot_target_);
+    }
+
     if (flags_ & SVC_EXEC) UnSetExec();
 
     if (flags_ & SVC_TEMPORARY) return;
@@ -324,6 +333,12 @@
 
 
 Result<void> Service::ExecStart() {
+    auto reboot_on_failure = make_scope_guard([this] {
+        if (on_failure_reboot_target_) {
+            EnterShutdown(*on_failure_reboot_target_);
+        }
+    });
+
     if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
         // Don't delay the service for ExecStart() as the semantic is that
         // the caller might depend on the side effect of the execution.
@@ -344,10 +359,17 @@
               << " gid " << proc_attr_.gid << "+" << proc_attr_.supp_gids.size() << " context "
               << (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
 
+    reboot_on_failure.Disable();
     return {};
 }
 
 Result<void> Service::Start() {
+    auto reboot_on_failure = make_scope_guard([this] {
+        if (on_failure_reboot_target_) {
+            EnterShutdown(*on_failure_reboot_target_);
+        }
+    });
+
     if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
         ServiceList::GetInstance().DelayService(*this);
         return Error() << "Cannot start an updatable service '" << name_
@@ -370,6 +392,7 @@
             flags_ |= SVC_RESTART;
         }
         // It is not an error to try to start a service that is already running.
+        reboot_on_failure.Disable();
         return {};
     }
 
@@ -406,11 +429,11 @@
         scon = *result;
     }
 
-    if (!IsRuntimeApexReady() && !pre_apexd_) {
-        // If this service is started before the runtime APEX gets available,
-        // mark it as pre-apexd one. Note that this marking is permanent. So
-        // for example, if the service is re-launched (e.g., due to crash),
-        // it is still recognized as pre-apexd... for consistency.
+    if (!AreRuntimeApexesReady() && !pre_apexd_) {
+        // If this service is started before the Runtime and ART APEXes get
+        // available, mark it as pre-apexd one. Note that this marking is
+        // permanent. So for example, if the service is re-launched (e.g., due
+        // to crash), it is still recognized as pre-apexd... for consistency.
         pre_apexd_ = true;
     }
 
@@ -531,6 +554,7 @@
     }
 
     NotifyStateChange("running");
+    reboot_on_failure.Disable();
     return {};
 }
 
diff --git a/init/service.h b/init/service.h
index ccefc8e..788f792 100644
--- a/init/service.h
+++ b/init/service.h
@@ -196,6 +196,8 @@
     bool post_data_ = false;
 
     bool running_at_post_data_reset_ = false;
+
+    std::optional<std::string> on_failure_reboot_target_;
 };
 
 }  // namespace init
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index dd552fb..4322dc7 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -145,17 +145,21 @@
     const std::string& interface_name = args[1];
     const std::string& instance_name = args[2];
 
-    FQName fq_name;
-    if (!FQName::parse(interface_name, &fq_name)) {
-        return Error() << "Invalid fully-qualified name for interface '" << interface_name << "'";
-    }
+    // AIDL services don't use fully qualified names and instead just use "interface aidl <name>"
+    if (interface_name != "aidl") {
+        FQName fq_name;
+        if (!FQName::parse(interface_name, &fq_name)) {
+            return Error() << "Invalid fully-qualified name for interface '" << interface_name
+                           << "'";
+        }
 
-    if (!fq_name.isFullyQualified()) {
-        return Error() << "Interface name not fully-qualified '" << interface_name << "'";
-    }
+        if (!fq_name.isFullyQualified()) {
+            return Error() << "Interface name not fully-qualified '" << interface_name << "'";
+        }
 
-    if (fq_name.isValidValueName()) {
-        return Error() << "Interface name must not be a value name '" << interface_name << "'";
+        if (fq_name.isValidValueName()) {
+            return Error() << "Interface name must not be a value name '" << interface_name << "'";
+        }
     }
 
     const std::string fullname = interface_name + "/" + instance_name;
@@ -306,6 +310,18 @@
     return {};
 }
 
+Result<void> ServiceParser::ParseRebootOnFailure(std::vector<std::string>&& args) {
+    if (service_->on_failure_reboot_target_) {
+        return Error() << "Only one reboot_on_failure command may be specified";
+    }
+    if (!StartsWith(args[1], "shutdown") && !StartsWith(args[1], "reboot")) {
+        return Error()
+               << "reboot_on_failure commands must begin with either 'shutdown' or 'reboot'";
+    }
+    service_->on_failure_reboot_target_ = std::move(args[1]);
+    return {};
+}
+
 Result<void> ServiceParser::ParseRestartPeriod(std::vector<std::string>&& args) {
     int period;
     if (!ParseInt(args[1], &period, 5)) {
@@ -467,49 +483,41 @@
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     // clang-format off
     static const KeywordMap<ServiceParser::OptionParser> parser_map = {
-        {"capabilities",
-                        {0,     kMax, &ServiceParser::ParseCapabilities}},
-        {"class",       {1,     kMax, &ServiceParser::ParseClass}},
-        {"console",     {0,     1,    &ServiceParser::ParseConsole}},
-        {"critical",    {0,     0,    &ServiceParser::ParseCritical}},
-        {"disabled",    {0,     0,    &ServiceParser::ParseDisabled}},
-        {"enter_namespace",
-                        {2,     2,    &ServiceParser::ParseEnterNamespace}},
-        {"file",        {2,     2,    &ServiceParser::ParseFile}},
-        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &ServiceParser::ParseGroup}},
-        {"interface",   {2,     2,    &ServiceParser::ParseInterface}},
-        {"ioprio",      {2,     2,    &ServiceParser::ParseIoprio}},
-        {"keycodes",    {1,     kMax, &ServiceParser::ParseKeycodes}},
-        {"memcg.limit_in_bytes",
-                        {1,     1,    &ServiceParser::ParseMemcgLimitInBytes}},
-        {"memcg.limit_percent",
-                        {1,     1,    &ServiceParser::ParseMemcgLimitPercent}},
-        {"memcg.limit_property",
-                        {1,     1,    &ServiceParser::ParseMemcgLimitProperty}},
+        {"capabilities",            {0,     kMax, &ServiceParser::ParseCapabilities}},
+        {"class",                   {1,     kMax, &ServiceParser::ParseClass}},
+        {"console",                 {0,     1,    &ServiceParser::ParseConsole}},
+        {"critical",                {0,     0,    &ServiceParser::ParseCritical}},
+        {"disabled",                {0,     0,    &ServiceParser::ParseDisabled}},
+        {"enter_namespace",         {2,     2,    &ServiceParser::ParseEnterNamespace}},
+        {"file",                    {2,     2,    &ServiceParser::ParseFile}},
+        {"group",                   {1,     NR_SVC_SUPP_GIDS + 1, &ServiceParser::ParseGroup}},
+        {"interface",               {2,     2,    &ServiceParser::ParseInterface}},
+        {"ioprio",                  {2,     2,    &ServiceParser::ParseIoprio}},
+        {"keycodes",                {1,     kMax, &ServiceParser::ParseKeycodes}},
+        {"memcg.limit_in_bytes",    {1,     1,    &ServiceParser::ParseMemcgLimitInBytes}},
+        {"memcg.limit_percent",     {1,     1,    &ServiceParser::ParseMemcgLimitPercent}},
+        {"memcg.limit_property",    {1,     1,    &ServiceParser::ParseMemcgLimitProperty}},
         {"memcg.soft_limit_in_bytes",
-                        {1,     1,    &ServiceParser::ParseMemcgSoftLimitInBytes}},
-        {"memcg.swappiness",
-                        {1,     1,    &ServiceParser::ParseMemcgSwappiness}},
-        {"namespace",   {1,     2,    &ServiceParser::ParseNamespace}},
-        {"oneshot",     {0,     0,    &ServiceParser::ParseOneshot}},
-        {"onrestart",   {1,     kMax, &ServiceParser::ParseOnrestart}},
-        {"oom_score_adjust",
-                        {1,     1,    &ServiceParser::ParseOomScoreAdjust}},
-        {"override",    {0,     0,    &ServiceParser::ParseOverride}},
-        {"priority",    {1,     1,    &ServiceParser::ParsePriority}},
-        {"restart_period",
-                        {1,     1,    &ServiceParser::ParseRestartPeriod}},
-        {"rlimit",      {3,     3,    &ServiceParser::ParseProcessRlimit}},
-        {"seclabel",    {1,     1,    &ServiceParser::ParseSeclabel}},
-        {"setenv",      {2,     2,    &ServiceParser::ParseSetenv}},
-        {"shutdown",    {1,     1,    &ServiceParser::ParseShutdown}},
-        {"sigstop",     {0,     0,    &ServiceParser::ParseSigstop}},
-        {"socket",      {3,     6,    &ServiceParser::ParseSocket}},
-        {"timeout_period",
-                        {1,     1,    &ServiceParser::ParseTimeoutPeriod}},
-        {"updatable",   {0,     0,    &ServiceParser::ParseUpdatable}},
-        {"user",        {1,     1,    &ServiceParser::ParseUser}},
-        {"writepid",    {1,     kMax, &ServiceParser::ParseWritepid}},
+                                    {1,     1,    &ServiceParser::ParseMemcgSoftLimitInBytes}},
+        {"memcg.swappiness",        {1,     1,    &ServiceParser::ParseMemcgSwappiness}},
+        {"namespace",               {1,     2,    &ServiceParser::ParseNamespace}},
+        {"oneshot",                 {0,     0,    &ServiceParser::ParseOneshot}},
+        {"onrestart",               {1,     kMax, &ServiceParser::ParseOnrestart}},
+        {"oom_score_adjust",        {1,     1,    &ServiceParser::ParseOomScoreAdjust}},
+        {"override",                {0,     0,    &ServiceParser::ParseOverride}},
+        {"priority",                {1,     1,    &ServiceParser::ParsePriority}},
+        {"reboot_on_failure",       {1,     1,    &ServiceParser::ParseRebootOnFailure}},
+        {"restart_period",          {1,     1,    &ServiceParser::ParseRestartPeriod}},
+        {"rlimit",                  {3,     3,    &ServiceParser::ParseProcessRlimit}},
+        {"seclabel",                {1,     1,    &ServiceParser::ParseSeclabel}},
+        {"setenv",                  {2,     2,    &ServiceParser::ParseSetenv}},
+        {"shutdown",                {1,     1,    &ServiceParser::ParseShutdown}},
+        {"sigstop",                 {0,     0,    &ServiceParser::ParseSigstop}},
+        {"socket",                  {3,     6,    &ServiceParser::ParseSocket}},
+        {"timeout_period",          {1,     1,    &ServiceParser::ParseTimeoutPeriod}},
+        {"updatable",               {0,     0,    &ServiceParser::ParseUpdatable}},
+        {"user",                    {1,     1,    &ServiceParser::ParseUser}},
+        {"writepid",                {1,     kMax, &ServiceParser::ParseWritepid}},
     };
     // clang-format on
     return parser_map;
diff --git a/init/service_parser.h b/init/service_parser.h
index 4729874..ace4d70 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -68,6 +68,7 @@
     Result<void> ParseMemcgSwappiness(std::vector<std::string>&& args);
     Result<void> ParseNamespace(std::vector<std::string>&& args);
     Result<void> ParseProcessRlimit(std::vector<std::string>&& args);
+    Result<void> ParseRebootOnFailure(std::vector<std::string>&& args);
     Result<void> ParseRestartPeriod(std::vector<std::string>&& args);
     Result<void> ParseSeclabel(std::vector<std::string>&& args);
     Result<void> ParseSetenv(std::vector<std::string>&& args);
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index dcbff82..7565eb6 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -224,12 +224,8 @@
 }  // namespace init
 }  // namespace android
 
-int main(int argc, char** argv) {
-    if (argc > 1 && !strcmp(basename(argv[1]), "subcontext")) {
-        auto test_function_map = android::init::BuildTestFunctionMap();
-        return android::init::SubcontextMain(argc, argv, &test_function_map);
-    }
-
-    testing::InitGoogleTest(&argc, argv);
-    return RUN_ALL_TESTS();
+// init_test.cpp contains the main entry point for all init tests.
+int SubcontextTestChildMain(int argc, char** argv) {
+    auto test_function_map = android::init::BuildTestFunctionMap();
+    return android::init::SubcontextMain(argc, argv, &test_function_map);
 }
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index cffc1b9..59f91ee 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -113,10 +113,12 @@
 class ColdBoot {
   public:
     ColdBoot(UeventListener& uevent_listener,
-             std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers)
+             std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers,
+             bool enable_parallel_restorecon)
         : uevent_listener_(uevent_listener),
           uevent_handlers_(uevent_handlers),
-          num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {}
+          num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4),
+          enable_parallel_restorecon_(enable_parallel_restorecon) {}
 
     void Run();
 
@@ -132,6 +134,8 @@
     std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers_;
 
     unsigned int num_handler_subprocesses_;
+    bool enable_parallel_restorecon_;
+
     std::vector<Uevent> uevent_queue_;
 
     std::set<pid_t> subprocess_pids_;
@@ -155,7 +159,6 @@
 
         selinux_android_restorecon(dir.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
     }
-    _exit(EXIT_SUCCESS);
 }
 
 void ColdBoot::GenerateRestoreCon(const std::string& directory) {
@@ -195,7 +198,10 @@
 
         if (pid == 0) {
             UeventHandlerMain(i, num_handler_subprocesses_);
-            RestoreConHandler(i, num_handler_subprocesses_);
+            if (enable_parallel_restorecon_) {
+                RestoreConHandler(i, num_handler_subprocesses_);
+            }
+            _exit(EXIT_SUCCESS);
         }
 
         subprocess_pids_.emplace(pid);
@@ -240,14 +246,20 @@
 
     RegenerateUevents();
 
-    selinux_android_restorecon("/sys", 0);
-    selinux_android_restorecon("/sys/devices", 0);
-    GenerateRestoreCon("/sys");
-    // takes long time for /sys/devices, parallelize it
-    GenerateRestoreCon("/sys/devices");
+    if (enable_parallel_restorecon_) {
+        selinux_android_restorecon("/sys", 0);
+        selinux_android_restorecon("/sys/devices", 0);
+        GenerateRestoreCon("/sys");
+        // takes long time for /sys/devices, parallelize it
+        GenerateRestoreCon("/sys/devices");
+    }
 
     ForkSubProcesses();
 
+    if (!enable_parallel_restorecon_) {
+        selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
+    }
+
     WaitForSubProcesses();
 
     android::base::SetProperty(kColdBootDoneProp, "true");
@@ -284,7 +296,8 @@
             std::move(ueventd_configuration.sysfs_permissions),
             std::move(ueventd_configuration.subsystems), android::fs_mgr::GetBootDevices(), true));
     uevent_handlers.emplace_back(std::make_unique<FirmwareHandler>(
-            std::move(ueventd_configuration.firmware_directories)));
+            std::move(ueventd_configuration.firmware_directories),
+            std::move(ueventd_configuration.external_firmware_handlers)));
 
     if (ueventd_configuration.enable_modalias_handling) {
         std::vector<std::string> base_paths = {"/odm/lib/modules", "/vendor/lib/modules"};
@@ -293,7 +306,8 @@
     UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);
 
     if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {
-        ColdBoot cold_boot(uevent_listener, uevent_handlers);
+        ColdBoot cold_boot(uevent_listener, uevent_handlers,
+                           ueventd_configuration.enable_parallel_restorecon);
         cold_boot.Run();
     }
 
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 8ee0cce..a74b247 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -88,18 +88,42 @@
     return {};
 }
 
-Result<void> ParseModaliasHandlingLine(std::vector<std::string>&& args,
-                                       bool* enable_modalias_handling) {
+Result<void> ParseExternalFirmwareHandlerLine(
+        std::vector<std::string>&& args,
+        std::vector<ExternalFirmwareHandler>* external_firmware_handlers) {
+    if (args.size() != 4) {
+        return Error() << "external_firmware_handler lines must have exactly 3 parameters";
+    }
+
+    if (std::find_if(external_firmware_handlers->begin(), external_firmware_handlers->end(),
+                     [&args](const auto& other) { return other.devpath == args[2]; }) !=
+        external_firmware_handlers->end()) {
+        return Error() << "found a previous external_firmware_handler with the same devpath, '"
+                       << args[2] << "'";
+    }
+
+    passwd* pwd = getpwnam(args[2].c_str());
+    if (!pwd) {
+        return ErrnoError() << "invalid handler uid'" << args[2] << "'";
+    }
+
+    ExternalFirmwareHandler handler(std::move(args[1]), pwd->pw_uid, std::move(args[3]));
+    external_firmware_handlers->emplace_back(std::move(handler));
+
+    return {};
+}
+
+Result<void> ParseEnabledDisabledLine(std::vector<std::string>&& args, bool* feature) {
     if (args.size() != 2) {
-        return Error() << "modalias_handling lines take exactly one parameter";
+        return Error() << args[0] << " lines take exactly one parameter";
     }
 
     if (args[1] == "enabled") {
-        *enable_modalias_handling = true;
+        *feature = true;
     } else if (args[1] == "disabled") {
-        *enable_modalias_handling = false;
+        *feature = false;
     } else {
-        return Error() << "modalias_handling takes either 'enabled' or 'disabled' as a parameter";
+        return Error() << args[0] << " takes either 'enabled' or 'disabled' as a parameter";
     }
 
     return {};
@@ -212,12 +236,18 @@
     parser.AddSingleLineParser("firmware_directories",
                                std::bind(ParseFirmwareDirectoriesLine, _1,
                                          &ueventd_configuration.firmware_directories));
+    parser.AddSingleLineParser("external_firmware_handler",
+                               std::bind(ParseExternalFirmwareHandlerLine, _1,
+                                         &ueventd_configuration.external_firmware_handlers));
     parser.AddSingleLineParser("modalias_handling",
-                               std::bind(ParseModaliasHandlingLine, _1,
+                               std::bind(ParseEnabledDisabledLine, _1,
                                          &ueventd_configuration.enable_modalias_handling));
     parser.AddSingleLineParser("uevent_socket_rcvbuf_size",
                                std::bind(ParseUeventSocketRcvbufSizeLine, _1,
                                          &ueventd_configuration.uevent_socket_rcvbuf_size));
+    parser.AddSingleLineParser("parallel_restorecon",
+                               std::bind(ParseEnabledDisabledLine, _1,
+                                         &ueventd_configuration.enable_parallel_restorecon));
 
     for (const auto& config : configs) {
         parser.ParseConfig(config);
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index d476dec..eaafa5a 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_UEVENTD_PARSER_H
-#define _INIT_UEVENTD_PARSER_H
+#pragma once
 
 #include <string>
 #include <vector>
 
 #include "devices.h"
+#include "firmware_handler.h"
 
 namespace android {
 namespace init {
@@ -30,13 +30,13 @@
     std::vector<SysfsPermissions> sysfs_permissions;
     std::vector<Permissions> dev_permissions;
     std::vector<std::string> firmware_directories;
+    std::vector<ExternalFirmwareHandler> external_firmware_handlers;
     bool enable_modalias_handling = false;
     size_t uevent_socket_rcvbuf_size = 0;
+    bool enable_parallel_restorecon = false;
 };
 
 UeventdConfiguration ParseConfig(const std::vector<std::string>& configs);
 
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
index 9c1cedf..172ba0b 100644
--- a/init/ueventd_parser_test.cpp
+++ b/init/ueventd_parser_test.cpp
@@ -20,6 +20,8 @@
 #include <gtest/gtest.h>
 #include <private/android_filesystem_config.h>
 
+#include "firmware_handler.h"
+
 namespace android {
 namespace init {
 
@@ -93,7 +95,7 @@
             {"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
             {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
 
-    TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}});
+    TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}, {}});
 }
 
 TEST(ueventd_parser, Permissions) {
@@ -119,7 +121,7 @@
             {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT},
     };
 
-    TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}});
+    TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}, {}});
 }
 
 TEST(ueventd_parser, FirmwareDirectories) {
@@ -135,7 +137,52 @@
             "/more",
     };
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories, {}});
+}
+
+TEST(ueventd_parser, ExternalFirmwareHandlers) {
+    auto ueventd_file = R"(
+external_firmware_handler devpath root handler_path
+external_firmware_handler /devices/path/firmware/something001.bin system /vendor/bin/firmware_handler.sh
+external_firmware_handler /devices/path/firmware/something001.bin radio "/vendor/bin/firmware_handler.sh --has --arguments"
+)";
+
+    auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
+            {
+                    "devpath",
+                    AID_ROOT,
+                    "handler_path",
+            },
+            {
+                    "/devices/path/firmware/something001.bin",
+                    AID_SYSTEM,
+                    "/vendor/bin/firmware_handler.sh",
+            },
+            {
+                    "/devices/path/firmware/something001.bin",
+                    AID_RADIO,
+                    "/vendor/bin/firmware_handler.sh --has --arguments",
+            },
+    };
+
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers});
+}
+
+TEST(ueventd_parser, ExternalFirmwareHandlersDuplicate) {
+    auto ueventd_file = R"(
+external_firmware_handler devpath root handler_path
+external_firmware_handler devpath root handler_path2
+)";
+
+    auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
+            {
+                    "devpath",
+                    AID_ROOT,
+                    "handler_path",
+            },
+    };
+
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers});
 }
 
 TEST(ueventd_parser, UeventSocketRcvbufSize) {
@@ -144,7 +191,25 @@
 uevent_socket_rcvbuf_size 8M
 )";
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, false, 8 * 1024 * 1024});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, false, 8 * 1024 * 1024});
+}
+
+TEST(ueventd_parser, EnabledDisabledLines) {
+    auto ueventd_file = R"(
+modalias_handling enabled
+parallel_restorecon enabled
+modalias_handling disabled
+)";
+
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, false, 0, true});
+
+    auto ueventd_file2 = R"(
+parallel_restorecon enabled
+modalias_handling enabled
+parallel_restorecon disabled
+)";
+
+    TestUeventdFile(ueventd_file2, {{}, {}, {}, {}, {}, true, 0, false});
 }
 
 TEST(ueventd_parser, AllTogether) {
@@ -178,7 +243,11 @@
 /sys/devices/virtual/*/input   poll_delay  0660  root   input
 firmware_directories /more
 
+external_firmware_handler /devices/path/firmware/firmware001.bin root /vendor/bin/touch.sh
+
 uevent_socket_rcvbuf_size 6M
+modalias_handling enabled
+parallel_restorecon enabled
 
 #ending comment
 )";
@@ -208,10 +277,15 @@
             "/more",
     };
 
+    auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
+            {"/devices/path/firmware/firmware001.bin", AID_ROOT, "/vendor/bin/touch.sh"},
+    };
+
     size_t uevent_socket_rcvbuf_size = 6 * 1024 * 1024;
 
-    TestUeventdFile(ueventd_file, {subsystems, sysfs_permissions, permissions, firmware_directories,
-                                   false, uevent_socket_rcvbuf_size});
+    TestUeventdFile(ueventd_file,
+                    {subsystems, sysfs_permissions, permissions, firmware_directories,
+                     external_firmware_handlers, true, uevent_socket_rcvbuf_size, true});
 }
 
 // All of these lines are ill-formed, so test that there is 0 output.
@@ -230,6 +304,18 @@
 
 subsystem #no name
 
+modalias_handling
+modalias_handling enabled enabled
+modalias_handling blah
+
+parallel_restorecon
+parallel_restorecon enabled enabled
+parallel_restorecon blah
+
+external_firmware_handler
+external_firmware_handler blah blah
+external_firmware_handler blah blah blah blah
+
 )";
 
     TestUeventdFile(ueventd_file, {});
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc
index f1ca446..f71d0c3 100644
--- a/libappfuse/FuseBridgeLoop.cc
+++ b/libappfuse/FuseBridgeLoop.cc
@@ -86,6 +86,7 @@
         const bool proxy_read_ready = last_proxy_events_.events & EPOLLIN;
         const bool proxy_write_ready = last_proxy_events_.events & EPOLLOUT;
 
+        last_state_ = state_;
         last_device_events_.events = 0;
         last_proxy_events_.events = 0;
 
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index f8d5058..e000a00 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -79,7 +79,7 @@
       index_++;
       return *this;
     }
-    iterator& operator++(int increment) {
+    const iterator operator++(int increment) {
       index_ += increment;
       return *this;
     }
@@ -87,7 +87,7 @@
       index_--;
       return *this;
     }
-    iterator& operator--(int decrement) {
+    const iterator operator--(int decrement) {
       index_ -= decrement;
       return *this;
     }
diff --git a/libcutils/ashmem-host.cpp b/libcutils/ashmem-host.cpp
index 32446d4..6c7655a 100644
--- a/libcutils/ashmem-host.cpp
+++ b/libcutils/ashmem-host.cpp
@@ -34,6 +34,29 @@
 
 #include <utils/Compat.h>
 
+static bool ashmem_validate_stat(int fd, struct stat* buf) {
+    int result = fstat(fd, buf);
+    if (result == -1) {
+        return false;
+    }
+
+    /*
+     * Check if this is an "ashmem" region.
+     * TODO: This is very hacky, and can easily break.
+     * We need some reliable indicator.
+     */
+    if (!(buf->st_nlink == 0 && S_ISREG(buf->st_mode))) {
+        errno = ENOTTY;
+        return false;
+    }
+    return true;
+}
+
+int ashmem_valid(int fd) {
+    struct stat buf;
+    return ashmem_validate_stat(fd, &buf);
+}
+
 int ashmem_create_region(const char* /*ignored*/, size_t size) {
     char pattern[PATH_MAX];
     snprintf(pattern, sizeof(pattern), "/tmp/android-ashmem-%d-XXXXXXXXX", getpid());
@@ -65,18 +88,7 @@
 int ashmem_get_size_region(int fd)
 {
     struct stat buf;
-    int result = fstat(fd, &buf);
-    if (result == -1) {
-        return -1;
-    }
-
-    /*
-     * Check if this is an "ashmem" region.
-     * TODO: This is very hacky, and can easily break.
-     * We need some reliable indicator.
-     */
-    if (!(buf.st_nlink == 0 && S_ISREG(buf.st_mode))) {
-        errno = ENOTTY;
+    if (!ashmem_validate_stat(fd, &buf)) {
         return -1;
     }
 
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index b29638c..d0d83de 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -84,7 +84,7 @@
     { 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" },
-    { 00755, AID_ROOT,         AID_SHELL,        0, "system/apex/*/bin" },
+    { 00751, AID_ROOT,         AID_SHELL,        0, "system/apex/*/bin" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "vendor/bin" },
     { 00755, AID_ROOT,         AID_SHELL,        0, "vendor" },
     { 00755, AID_ROOT,         AID_ROOT,         0, 0 },
diff --git a/libcutils/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
index f6cae36..4f07456 100644
--- a/libcutils/include/cutils/native_handle.h
+++ b/libcutils/include/cutils/native_handle.h
@@ -69,10 +69,11 @@
 
 /*
  * native_handle_create
- * 
+ *
  * creates a native_handle_t and initializes it. must be destroyed with
- * native_handle_delete().
- * 
+ * native_handle_delete(). Note that numFds must be <= NATIVE_HANDLE_MAX_FDS,
+ * numInts must be <= NATIVE_HANDLE_MAX_INTS, and both must be >= 0.
+ *
  */
 native_handle_t* native_handle_create(int numFds, int numInts);
 
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index ae9dab5..e1e8230 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -128,6 +128,7 @@
 #define AID_GPU_SERVICE 1072     /* GPU service daemon */
 #define AID_NETWORK_STACK 1073   /* network stack service */
 #define AID_GSID 1074            /* GSI service daemon */
+#define AID_FSVERITY_CERT 1075   /* fs-verity key ownership in keystore */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libion/tests/Android.bp b/libion/tests/Android.bp
index d3b4688..5600702 100644
--- a/libion/tests/Android.bp
+++ b/libion/tests/Android.bp
@@ -24,7 +24,8 @@
     srcs: [
         "allocate_test.cpp",
         "exit_test.cpp",
-    	"heap_query.cpp",
+        "heap_query.cpp",
+        "system_heap.cpp",
         "invalid_values_test.cpp",
         "ion_test_fixture.cpp",
         "map_test.cpp",
diff --git a/libion/tests/heap_query.cpp b/libion/tests/heap_query.cpp
index bad3bbf..fed8030 100644
--- a/libion/tests/heap_query.cpp
+++ b/libion/tests/heap_query.cpp
@@ -15,6 +15,8 @@
  */
 
 #include <gtest/gtest.h>
+
+#include <ion/ion.h>
 #include "ion_test_fixture.h"
 
 class HeapQuery : public IonTest {};
@@ -23,5 +25,24 @@
     ASSERT_GT(ion_heaps.size(), 0);
 }
 
-// TODO: Check if we expect some of the default
-// heap types to be present on all devices.
+// TODO: Adjust this test to account for the range of valid carveout and DMA heap ids.
+TEST_F(HeapQuery, HeapIdVerify) {
+    for (const auto& heap : ion_heaps) {
+        SCOPED_TRACE(::testing::Message() << "Invalid id for heap:" << heap.name << ":" << heap.type
+                                          << ":" << heap.heap_id);
+        switch (heap.type) {
+            case ION_HEAP_TYPE_SYSTEM:
+                ASSERT_TRUE((1 << heap.heap_id) & ION_HEAP_SYSTEM_MASK);
+                break;
+            case ION_HEAP_TYPE_SYSTEM_CONTIG:
+                ASSERT_TRUE((1 << heap.heap_id) & ION_HEAP_SYSTEM_CONTIG_MASK);
+                break;
+            case ION_HEAP_TYPE_CARVEOUT:
+                ASSERT_TRUE((1 << heap.heap_id) & ION_HEAP_CARVEOUT_MASK);
+                break;
+            case ION_HEAP_TYPE_DMA:
+                ASSERT_TRUE((1 << heap.heap_id) & ION_HEAP_TYPE_DMA_MASK);
+                break;
+        }
+    }
+}
diff --git a/libion/tests/system_heap.cpp b/libion/tests/system_heap.cpp
new file mode 100644
index 0000000..fb63888
--- /dev/null
+++ b/libion/tests/system_heap.cpp
@@ -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.
+ */
+
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+#include <iostream>
+
+#include <ion/ion.h>
+#include "ion_test_fixture.h"
+
+class SystemHeap : public IonTest {};
+
+TEST_F(SystemHeap, Presence) {
+    bool system_heap_found = false;
+    for (const auto& heap : ion_heaps) {
+        if (heap.type == ION_HEAP_TYPE_SYSTEM) {
+            system_heap_found = true;
+            EXPECT_TRUE((1 << heap.heap_id) & ION_HEAP_SYSTEM_MASK);
+        }
+    }
+    // We now expect the system heap to exist from Android
+    ASSERT_TRUE(system_heap_found);
+}
+
+TEST_F(SystemHeap, Allocate) {
+    int fd;
+    ASSERT_EQ(0, ion_alloc_fd(ionfd, getpagesize(), 0, ION_HEAP_SYSTEM_MASK, 0, &fd));
+    ASSERT_TRUE(fd != 0);
+    ASSERT_EQ(close(fd), 0);  // free the buffer
+}
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 45a9bc9..5a73dc0 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1747,22 +1747,21 @@
 
   return count;
 }
-
-// meant to be handed to ASSERT_TRUE / EXPECT_TRUE only to expand the message
-static testing::AssertionResult IsOk(bool ok, std::string& message) {
-  return ok ? testing::AssertionSuccess()
-            : (testing::AssertionFailure() << message);
-}
 #endif  // TEST_PREFIX
 
 TEST(liblog, enoent) {
 #ifdef TEST_PREFIX
+  if (getuid() != 0) {
+    GTEST_SKIP() << "Skipping test, must be run as root.";
+    return;
+  }
+
   TEST_PREFIX
   log_time ts(CLOCK_MONOTONIC);
   EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
   EXPECT_EQ(SUPPORTS_END_TO_END, count_matching_ts(ts));
 
-  // This call will fail if we are setuid(AID_SYSTEM), beware of any
+  // This call will fail unless we are root, beware of any
   // test prior to this one playing with setuid and causing interference.
   // We need to run before these tests so that they do not interfere with
   // this test.
@@ -1774,20 +1773,7 @@
   // liblog.android_logger_get_ is one of those tests that has no recourse
   // and that would be adversely affected by emptying the log if it was run
   // right after this test.
-  if (getuid() != AID_ROOT) {
-    fprintf(
-        stderr,
-        "WARNING: test conditions request being run as root and not AID=%d\n",
-        getuid());
-    if (!__android_log_is_debuggable()) {
-      fprintf(
-          stderr,
-          "WARNING: can not run test on a \"user\" build, bypassing test\n");
-      return;
-    }
-  }
-
-  system((getuid() == AID_ROOT) ? "stop logd" : "su 0 stop logd");
+  system("stop logd");
   usleep(1000000);
 
   // A clean stop like we are testing returns -ENOENT, but in the _real_
@@ -1799,19 +1785,15 @@
   std::string content = android::base::StringPrintf(
       "__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) = %d %s\n",
       ret, (ret <= 0) ? strerror(-ret) : "(content sent)");
-  EXPECT_TRUE(
-      IsOk((ret == -ENOENT) || (ret == -ENOTCONN) || (ret == -ECONNREFUSED),
-           content));
+  EXPECT_TRUE(ret == -ENOENT || ret == -ENOTCONN || ret == -ECONNREFUSED) << content;
   ret = __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts));
   content = android::base::StringPrintf(
       "__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) = %d %s\n",
       ret, (ret <= 0) ? strerror(-ret) : "(content sent)");
-  EXPECT_TRUE(
-      IsOk((ret == -ENOENT) || (ret == -ENOTCONN) || (ret == -ECONNREFUSED),
-           content));
+  EXPECT_TRUE(ret == -ENOENT || ret == -ENOTCONN || ret == -ECONNREFUSED) << content;
   EXPECT_EQ(0, count_matching_ts(ts));
 
-  system((getuid() == AID_ROOT) ? "start logd" : "su 0 start logd");
+  system("start logd");
   usleep(1000000);
 
   EXPECT_EQ(0, count_matching_ts(ts));
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 4e4554a..c7dff5a 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -15,8 +15,6 @@
         "liblog",
         "libbase",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "libutils",
         "android.hardware.memtrack@1.0",
     ],
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index d864d1b..f20df19 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -113,7 +113,7 @@
     static_libs: ["libmemunreachable"],
     shared_libs: [
         "libbinder",
-        "libhwbinder",
+        "libhidlbase",
         "libutils",
     ],
     test_suites: ["device-tests"],
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 6d3c057..60d462f 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -63,6 +63,10 @@
     LOG_ALWAYS_FATAL_IF((dot_index == std::string::npos),
                         "Error finding namespace of apex: no dot in apex name %s", caller_location);
     std::string name = location.substr(dot_index + 1, slash_index - dot_index - 1);
+    // TODO(b/139408016): Rename the runtime namespace to "art".
+    if (name == "art") {
+      name = "runtime";
+    }
     android_namespace_t* boot_namespace = android_get_exported_namespace(name.c_str());
     LOG_ALWAYS_FATAL_IF((boot_namespace == nullptr),
                         "Error finding namespace of apex: no namespace called %s", name.c_str());
diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp
index 3694360..93df1d0 100644
--- a/libnativeloader/public_libraries.cpp
+++ b/libnativeloader/public_libraries.cpp
@@ -49,12 +49,12 @@
 constexpr const char* kLlndkLibrariesFile = "/system/etc/llndk.libraries.txt";
 constexpr const char* kVndkLibrariesFile = "/system/etc/vndksp.libraries.txt";
 
-const std::vector<const std::string> kRuntimePublicLibraries = {
+const std::vector<const std::string> kArtApexPublicLibraries = {
     "libicuuc.so",
     "libicui18n.so",
 };
 
-constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/" LIB;
+constexpr const char* kArtApexLibPath = "/apex/com.android.art/" LIB;
 
 constexpr const char* kNeuralNetworksApexPublicLibrary = "libneuralnetworks.so";
 
@@ -182,8 +182,8 @@
   // For example, libicuuc.so is exposed to classloader namespace from runtime namespace.
   // Unfortunately, it does not have stable C symbols, and default namespace should only use
   // stable symbols in libandroidicu.so. http://b/120786417
-  for (const std::string& lib_name : kRuntimePublicLibraries) {
-    std::string path(kRuntimeApexLibPath);
+  for (const std::string& lib_name : kArtApexPublicLibraries) {
+    std::string path(kArtApexLibPath);
     path.append("/").append(lib_name);
 
     struct stat s;
@@ -207,9 +207,9 @@
   return android::base::Join(*sonames, ':');
 }
 
-static std::string InitRuntimePublicLibraries() {
-  CHECK(sizeof(kRuntimePublicLibraries) > 0);
-  std::string list = android::base::Join(kRuntimePublicLibraries, ":");
+static std::string InitArtPublicLibraries() {
+  CHECK(sizeof(kArtApexPublicLibraries) > 0);
+  std::string list = android::base::Join(kArtApexPublicLibraries, ":");
 
   std::string additional_libs = additional_public_libraries();
   if (!additional_libs.empty()) {
@@ -277,7 +277,7 @@
 }
 
 const std::string& runtime_public_libraries() {
-  static std::string list = InitRuntimePublicLibraries();
+  static std::string list = InitArtPublicLibraries();
   return list;
 }
 
diff --git a/libprocessgroup/OWNERS b/libprocessgroup/OWNERS
index bfa730a..27b9a03 100644
--- a/libprocessgroup/OWNERS
+++ b/libprocessgroup/OWNERS
@@ -1,2 +1,3 @@
 ccross@google.com
+surenb@google.com
 tomcherry@google.com
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 15f8139..c83df1a 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -134,15 +134,27 @@
 
     switch (policy) {
         case SP_BACKGROUND:
-            return SetTaskProfiles(tid, {"HighEnergySaving", "TimerSlackHigh"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid, {"HighEnergySaving", "LowIoPriority", "TimerSlackHigh"},
+                                   true)
+                           ? 0
+                           : -1;
         case SP_FOREGROUND:
         case SP_AUDIO_APP:
         case SP_AUDIO_SYS:
-            return SetTaskProfiles(tid, {"HighPerformance", "TimerSlackNormal"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid, {"HighPerformance", "HighIoPriority", "TimerSlackNormal"},
+                                   true)
+                           ? 0
+                           : -1;
         case SP_TOP_APP:
-            return SetTaskProfiles(tid, {"MaxPerformance", "TimerSlackNormal"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid, {"MaxPerformance", "MaxIoPriority", "TimerSlackNormal"},
+                                   true)
+                           ? 0
+                           : -1;
         case SP_RT_APP:
-            return SetTaskProfiles(tid, {"RealtimePerformance", "TimerSlackNormal"}, true) ? 0 : -1;
+            return SetTaskProfiles(
+                           tid, {"RealtimePerformance", "MaxIoPriority", "TimerSlackNormal"}, true)
+                           ? 0
+                           : -1;
         default:
             return SetTaskProfiles(tid, {"TimerSlackNormal"}, true) ? 0 : -1;
     }
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index b778f92..b1c05ea 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -109,6 +109,11 @@
         if (sock < 0) {
             ret = -errno;
         } else {
+            int sndbuf = 1 * 1024 * 1024;  // set max send buffer size 1MB
+            socklen_t bufLen = sizeof(sndbuf);
+            // SO_RCVBUF does not have an effect on unix domain socket, but SO_SNDBUF does.
+            // Proceed to connect even setsockopt fails.
+            setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, bufLen);
             struct sockaddr_un un;
             memset(&un, 0, sizeof(struct sockaddr_un));
             un.sun_family = AF_UNIX;
diff --git a/libsystem/include/system/graphics-base-v1.2.h b/libsystem/include/system/graphics-base-v1.2.h
new file mode 100644
index 0000000..2194f5e
--- /dev/null
+++ b/libsystem/include/system/graphics-base-v1.2.h
@@ -0,0 +1,31 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+// Source: android.hardware.graphics.common@1.2
+// Location: hardware/interfaces/graphics/common/1.2/
+
+#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_2_EXPORTED_CONSTANTS_H_
+#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_2_EXPORTED_CONSTANTS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    HAL_HDR_HDR10_PLUS = 4,
+} android_hdr_v1_2_t;
+
+typedef enum {
+    HAL_DATASPACE_DISPLAY_BT2020 = 142999552 /* ((STANDARD_BT2020 | TRANSFER_SRGB) | RANGE_FULL) */,
+    HAL_DATASPACE_DYNAMIC_DEPTH = 4098 /* 0x1002 */,
+    HAL_DATASPACE_JPEG_APP_SEGMENTS = 4099 /* 0x1003 */,
+    HAL_DATASPACE_HEIF = 4100 /* 0x1004 */,
+} android_dataspace_v1_2_t;
+
+typedef enum {
+    HAL_PIXEL_FORMAT_HSV_888 = 55 /* 0x37 */,
+} android_pixel_format_v1_2_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_2_EXPORTED_CONSTANTS_H_
diff --git a/libsystem/include/system/graphics-base.h b/libsystem/include/system/graphics-base.h
index ea92007..92ee077 100644
--- a/libsystem/include/system/graphics-base.h
+++ b/libsystem/include/system/graphics-base.h
@@ -3,5 +3,6 @@
 
 #include "graphics-base-v1.0.h"
 #include "graphics-base-v1.1.h"
+#include "graphics-base-v1.2.h"
 
 #endif  // SYSTEM_CORE_GRAPHICS_BASE_H_
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 73237e6..8d2299f 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -153,12 +153,12 @@
     shared_libs: [
         "libunwindstack",
     ],
+    relative_install_path: "libunwindstack_test",
 }
 
-cc_test {
-    name: "libunwindstack_test",
+cc_defaults {
+    name: "libunwindstack_testlib_flags",
     defaults: ["libunwindstack_flags"],
-    isolated: true,
 
     srcs: [
         "tests/ArmExidxDecodeTest.cpp",
@@ -183,7 +183,7 @@
         "tests/ElfTestUtils.cpp",
         "tests/IsolatedSettings.cpp",
         "tests/JitDebugTest.cpp",
-        "tests/LocalUnwinderTest.cpp",
+        "tests/LocalUpdatableMapsTest.cpp",
         "tests/LogFake.cpp",
         "tests/MapInfoCreateMemoryTest.cpp",
         "tests/MapInfoGetBuildIDTest.cpp",
@@ -211,6 +211,7 @@
         "tests/UnwindOfflineTest.cpp",
         "tests/UnwindTest.cpp",
         "tests/UnwinderTest.cpp",
+        "tests/VerifyBionicTerminationTest.cpp",
     ],
 
     cflags: [
@@ -252,11 +253,28 @@
         "tests/files/offline/straddle_arm/*",
         "tests/files/offline/straddle_arm64/*",
     ],
+}
+
+cc_test {
+    name: "libunwindstack_test",
+    defaults: ["libunwindstack_testlib_flags"],
+    isolated: true,
+
+    srcs: [
+        "tests/LocalUnwinderTest.cpp",
+    ],
     required: [
         "libunwindstack_local",
     ],
 }
 
+// Skip LocalUnwinderTest until atest understands required properly.
+cc_test {
+    name: "libunwindstack_unit_test",
+    defaults: ["libunwindstack_testlib_flags"],
+    isolated: true,
+}
+
 //-------------------------------------------------------------------------
 // Tools
 //-------------------------------------------------------------------------
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 5da73e4..250e600 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -149,9 +149,10 @@
       }
 
       // Never delete these maps, they may be in use. The assumption is
-      // that there will only every be a handfull of these so waiting
+      // that there will only every be a handful of these so waiting
       // to destroy them is not too expensive.
       saved_maps_.emplace_back(std::move(info));
+      search_map_idx = old_map_idx + 1;
       maps_[old_map_idx] = nullptr;
       total_entries--;
     }
diff --git a/libunwindstack/TEST_MAPPING b/libunwindstack/TEST_MAPPING
new file mode 100644
index 0000000..55771c0
--- /dev/null
+++ b/libunwindstack/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libunwindstack_unit_test"
+    }
+  ]
+}
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 7556482..0b9b85c 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -309,7 +309,7 @@
   }
 }
 
-std::string Unwinder::FormatFrame(const FrameData& frame) {
+std::string Unwinder::FormatFrame(const FrameData& frame) const {
   std::string data;
   if (regs_->Is32Bit()) {
     data += android::base::StringPrintf("  #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
@@ -355,7 +355,7 @@
   return data;
 }
 
-std::string Unwinder::FormatFrame(size_t frame_num) {
+std::string Unwinder::FormatFrame(size_t frame_num) const {
   if (frame_num >= frames_.size()) {
     return "";
   }
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 1784394..e53f367 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -105,7 +105,7 @@
 
   const std::string GetMapsFile() const override;
 
- private:
+ protected:
   std::vector<std::unique_ptr<MapInfo>> saved_maps_;
 };
 
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 52b3578..11ad9de 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -77,7 +77,7 @@
   void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
               const std::vector<std::string>* map_suffixes_to_ignore = nullptr);
 
-  size_t NumFrames() { return frames_.size(); }
+  size_t NumFrames() const { return frames_.size(); }
 
   const std::vector<FrameData>& frames() { return frames_; }
 
@@ -87,8 +87,8 @@
     return frames;
   }
 
-  std::string FormatFrame(size_t frame_num);
-  std::string FormatFrame(const FrameData& frame);
+  std::string FormatFrame(size_t frame_num) const;
+  std::string FormatFrame(const FrameData& frame) const;
 
   void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
 
diff --git a/libunwindstack/tests/LocalUnwinderTest.cpp b/libunwindstack/tests/LocalUnwinderTest.cpp
index 56a18cd..9936f7a 100644
--- a/libunwindstack/tests/LocalUnwinderTest.cpp
+++ b/libunwindstack/tests/LocalUnwinderTest.cpp
@@ -170,10 +170,10 @@
 
   std::string testlib(testing::internal::GetArgvs()[0]);
   auto const value = testlib.find_last_of('/');
-  if (value == std::string::npos) {
-    testlib = "../";
+  if (value != std::string::npos) {
+    testlib = testlib.substr(0, value + 1);
   } else {
-    testlib = testlib.substr(0, value + 1) + "../";
+    testlib = "";
   }
   testlib += "libunwindstack_local.so";
 
diff --git a/libunwindstack/tests/LocalUpdatableMapsTest.cpp b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
new file mode 100644
index 0000000..b816b9a
--- /dev/null
+++ b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
@@ -0,0 +1,274 @@
+/*
+ * 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 <stdint.h>
+#include <sys/mman.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/file.h>
+#include <unwindstack/Maps.h>
+
+namespace unwindstack {
+
+class TestUpdatableMaps : public LocalUpdatableMaps {
+ public:
+  TestUpdatableMaps() : LocalUpdatableMaps() {}
+  virtual ~TestUpdatableMaps() = default;
+
+  const std::string GetMapsFile() const override { return maps_file_; }
+
+  void TestSetMapsFile(const std::string& maps_file) { maps_file_ = maps_file; }
+
+  const std::vector<std::unique_ptr<MapInfo>>& TestGetSavedMaps() { return saved_maps_; }
+
+ private:
+  std::string maps_file_;
+};
+
+class LocalUpdatableMapsTest : public ::testing::Test {
+ protected:
+  static const std::string GetDefaultMapString() {
+    return "3000-4000 r-xp 00000 00:00 0\n8000-9000 r-xp 00000 00:00 0\n";
+  }
+
+  void SetUp() override {
+    TemporaryFile tf;
+    ASSERT_TRUE(android::base::WriteStringToFile(GetDefaultMapString(), tf.path));
+
+    maps_.TestSetMapsFile(tf.path);
+    ASSERT_TRUE(maps_.Parse());
+    ASSERT_EQ(2U, maps_.Total());
+
+    MapInfo* map_info = maps_.Get(0);
+    ASSERT_TRUE(map_info != nullptr);
+    EXPECT_EQ(0x3000U, map_info->start);
+    EXPECT_EQ(0x4000U, map_info->end);
+    EXPECT_EQ(0U, map_info->offset);
+    EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+    EXPECT_TRUE(map_info->name.empty());
+
+    map_info = maps_.Get(1);
+    ASSERT_TRUE(map_info != nullptr);
+    EXPECT_EQ(0x8000U, map_info->start);
+    EXPECT_EQ(0x9000U, map_info->end);
+    EXPECT_EQ(0U, map_info->offset);
+    EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+    EXPECT_TRUE(map_info->name.empty());
+  }
+
+  TestUpdatableMaps maps_;
+};
+
+TEST_F(LocalUpdatableMapsTest, same_map) {
+  TemporaryFile tf;
+  ASSERT_TRUE(android::base::WriteStringToFile(GetDefaultMapString(), tf.path));
+
+  maps_.TestSetMapsFile(tf.path);
+  ASSERT_TRUE(maps_.Reparse());
+  ASSERT_EQ(2U, maps_.Total());
+  EXPECT_EQ(0U, maps_.TestGetSavedMaps().size());
+
+  MapInfo* map_info = maps_.Get(0);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x3000U, map_info->start);
+  EXPECT_EQ(0x4000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_TRUE(map_info->name.empty());
+
+  map_info = maps_.Get(1);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x8000U, map_info->start);
+  EXPECT_EQ(0x9000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_TRUE(map_info->name.empty());
+}
+
+TEST_F(LocalUpdatableMapsTest, same_map_new_perms) {
+  TemporaryFile tf;
+  ASSERT_TRUE(
+      android::base::WriteStringToFile("3000-4000 rwxp 00000 00:00 0\n"
+                                       "8000-9000 r-xp 00000 00:00 0\n",
+                                       tf.path));
+
+  maps_.TestSetMapsFile(tf.path);
+  ASSERT_TRUE(maps_.Reparse());
+  ASSERT_EQ(2U, maps_.Total());
+
+  MapInfo* map_info = maps_.Get(0);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x3000U, map_info->start);
+  EXPECT_EQ(0x4000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, map_info->flags);
+  EXPECT_TRUE(map_info->name.empty());
+
+  map_info = maps_.Get(1);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x8000U, map_info->start);
+  EXPECT_EQ(0x9000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_TRUE(map_info->name.empty());
+
+  auto& saved_maps = maps_.TestGetSavedMaps();
+  ASSERT_EQ(1U, saved_maps.size());
+  map_info = saved_maps[0].get();
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x3000U, map_info->start);
+  EXPECT_EQ(0x4000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_TRUE(map_info->name.empty());
+}
+
+TEST_F(LocalUpdatableMapsTest, same_map_new_name) {
+  TemporaryFile tf;
+  ASSERT_TRUE(
+      android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
+                                       "8000-9000 r-xp 00000 00:00 0\n",
+                                       tf.path));
+
+  maps_.TestSetMapsFile(tf.path);
+  ASSERT_TRUE(maps_.Reparse());
+  ASSERT_EQ(2U, maps_.Total());
+
+  MapInfo* map_info = maps_.Get(0);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x3000U, map_info->start);
+  EXPECT_EQ(0x4000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_EQ("/fake/lib.so", map_info->name);
+
+  map_info = maps_.Get(1);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x8000U, map_info->start);
+  EXPECT_EQ(0x9000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_TRUE(map_info->name.empty());
+
+  auto& saved_maps = maps_.TestGetSavedMaps();
+  ASSERT_EQ(1U, saved_maps.size());
+  map_info = saved_maps[0].get();
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x3000U, map_info->start);
+  EXPECT_EQ(0x4000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_TRUE(map_info->name.empty());
+}
+
+TEST_F(LocalUpdatableMapsTest, only_add_maps) {
+  TemporaryFile tf;
+  ASSERT_TRUE(
+      android::base::WriteStringToFile("1000-2000 r-xp 00000 00:00 0\n"
+                                       "3000-4000 r-xp 00000 00:00 0\n"
+                                       "8000-9000 r-xp 00000 00:00 0\n"
+                                       "a000-f000 r-xp 00000 00:00 0\n",
+                                       tf.path));
+
+  maps_.TestSetMapsFile(tf.path);
+  ASSERT_TRUE(maps_.Reparse());
+  ASSERT_EQ(4U, maps_.Total());
+  EXPECT_EQ(0U, maps_.TestGetSavedMaps().size());
+
+  MapInfo* map_info = maps_.Get(0);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x1000U, map_info->start);
+  EXPECT_EQ(0x2000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_TRUE(map_info->name.empty());
+
+  map_info = maps_.Get(1);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x3000U, map_info->start);
+  EXPECT_EQ(0x4000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_TRUE(map_info->name.empty());
+
+  map_info = maps_.Get(2);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x8000U, map_info->start);
+  EXPECT_EQ(0x9000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_TRUE(map_info->name.empty());
+
+  map_info = maps_.Get(3);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0xa000U, map_info->start);
+  EXPECT_EQ(0xf000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_TRUE(map_info->name.empty());
+}
+
+TEST_F(LocalUpdatableMapsTest, all_new_maps) {
+  TemporaryFile tf;
+  ASSERT_TRUE(
+      android::base::WriteStringToFile("1000-2000 r-xp 00000 00:00 0\n"
+                                       "a000-f000 r-xp 00000 00:00 0\n",
+                                       tf.path));
+
+  maps_.TestSetMapsFile(tf.path);
+  ASSERT_TRUE(maps_.Reparse());
+  ASSERT_EQ(2U, maps_.Total());
+
+  MapInfo* map_info = maps_.Get(0);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x1000U, map_info->start);
+  EXPECT_EQ(0x2000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_TRUE(map_info->name.empty());
+
+  map_info = maps_.Get(1);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0xa000U, map_info->start);
+  EXPECT_EQ(0xf000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_TRUE(map_info->name.empty());
+
+  auto& saved_maps = maps_.TestGetSavedMaps();
+  ASSERT_EQ(2U, saved_maps.size());
+  map_info = saved_maps[0].get();
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x3000U, map_info->start);
+  EXPECT_EQ(0x4000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_TRUE(map_info->name.empty());
+
+  map_info = saved_maps[1].get();
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x8000U, map_info->start);
+  EXPECT_EQ(0x9000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_TRUE(map_info->name.empty());
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/VerifyBionicTerminationTest.cpp b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
new file mode 100644
index 0000000..6a3e91a
--- /dev/null
+++ b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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 _GNU_SOURCE 1
+#include <stdint.h>
+#include <string.h>
+
+#include <string>
+
+#if defined(__BIONIC__)
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
+// This test is specific to bionic to verify that __libc_init is
+// properly setting the return address to undefined so that the
+// unwind properly terminates.
+
+namespace unwindstack {
+
+static std::string DumpFrames(const UnwinderFromPid& unwinder) {
+  std::string unwind;
+  for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+    unwind += unwinder.FormatFrame(i) + '\n';
+  }
+  return unwind;
+}
+
+static DwarfLocationEnum GetReturnAddressLocation(uint64_t rel_pc, DwarfSection* section) {
+  if (section == nullptr) {
+    return DWARF_LOCATION_INVALID;
+  }
+
+  const DwarfFde* fde = section->GetFdeFromPc(rel_pc);
+  if (fde == nullptr || fde->cie == nullptr) {
+    return DWARF_LOCATION_INVALID;
+  }
+  dwarf_loc_regs_t regs;
+  if (!section->GetCfaLocationInfo(rel_pc, fde, &regs)) {
+    return DWARF_LOCATION_INVALID;
+  }
+
+  auto reg_entry = regs.find(fde->cie->return_address_register);
+  if (reg_entry == regs.end()) {
+    return DWARF_LOCATION_INVALID;
+  }
+  return reg_entry->second.type;
+}
+
+static void VerifyReturnAddress(const FrameData& frame) {
+  // Now go and find information about the register data and verify that the relative pc results in
+  // an undefined register.
+  Elf elf(Memory::CreateFileMemory(frame.map_name, 0).release());
+  ASSERT_TRUE(elf.Init()) << "Failed to init elf object from " << frame.map_name;
+  ASSERT_TRUE(elf.valid()) << "Elf " << frame.map_name << " is not valid.";
+  ElfInterface* interface = elf.interface();
+
+  // Only check the eh_frame and the debug_frame since the undefined register
+  // is set using a cfi directive.
+  // Check debug_frame first, then eh_frame since debug_frame always
+  // contains the most specific data.
+  DwarfLocationEnum location = GetReturnAddressLocation(frame.rel_pc, interface->debug_frame());
+  if (location == DWARF_LOCATION_UNDEFINED) {
+    return;
+  }
+
+  location = GetReturnAddressLocation(frame.rel_pc, interface->eh_frame());
+  ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location);
+}
+
+// This test assumes that it starts from the main thread, and that the
+// libc.so on device will include symbols so that function names can
+// be resolved.
+TEST(VerifyBionicTermination, local_terminate) {
+  std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+
+  UnwinderFromPid unwinder(512, getpid());
+  ASSERT_TRUE(unwinder.Init(regs->Arch()));
+  unwinder.SetRegs(regs.get());
+
+  RegsGetLocal(regs.get());
+  unwinder.Unwind();
+  ASSERT_LT(0U, unwinder.NumFrames());
+
+  SCOPED_TRACE(DumpFrames(unwinder));
+
+  // Look for the frame that includes __libc_init, there should only
+  // be one and it should be the last.
+  bool found = false;
+  const std::vector<FrameData>& frames = unwinder.frames();
+  for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+    const FrameData& frame = frames[i];
+    if (frame.function_name == "__libc_init" && !frame.map_name.empty() &&
+        std::string("libc.so") == basename(frame.map_name.c_str())) {
+      ASSERT_EQ(unwinder.NumFrames(), i + 1) << "__libc_init is not last frame.";
+      ASSERT_NO_FATAL_FAILURE(VerifyReturnAddress(frame));
+      found = true;
+    }
+  }
+  ASSERT_TRUE(found) << "Unable to find libc.so:__libc_init frame\n";
+}
+
+}  // namespace unwindstack
+
+#endif
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 8be4dd0..98921be 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -205,6 +205,7 @@
         "Mutex_test.cpp",
         "SharedBuffer_test.cpp",
         "String8_test.cpp",
+        "String16_test.cpp",
         "StrongPointer_test.cpp",
         "Unicode_test.cpp",
         "Vector_test.cpp",
@@ -289,3 +290,9 @@
     ],
     shared_libs: ["libutils_test_singleton1"],
 }
+
+cc_benchmark {
+    name: "libutils_benchmark",
+    srcs: ["Vector_benchmark.cpp"],
+    shared_libs: ["libutils"],
+}
diff --git a/libutils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp
index 7910c6e..3e703db 100644
--- a/libutils/SharedBuffer.cpp
+++ b/libutils/SharedBuffer.cpp
@@ -41,6 +41,7 @@
         // The following is OK on Android-supported platforms.
         sb->mRefs.store(1, std::memory_order_relaxed);
         sb->mSize = size;
+        sb->mClientMetadata = 0;
     }
     return sb;
 }
diff --git a/libutils/SharedBuffer.h b/libutils/SharedBuffer.h
index fdf13a9..476c842 100644
--- a/libutils/SharedBuffer.h
+++ b/libutils/SharedBuffer.h
@@ -102,7 +102,12 @@
         // Must be sized to preserve correct alignment.
         mutable std::atomic<int32_t>        mRefs;
                 size_t                      mSize;
-                uint32_t                    mReserved[2];
+                uint32_t                    mReserved;
+public:
+        // mClientMetadata is reserved for client use.  It is initialized to 0
+        // and the clients can do whatever they want with it.  Note that this is
+        // placed last so that it is adjcent to the buffer allocated.
+                uint32_t                    mClientMetadata;
 };
 
 static_assert(sizeof(SharedBuffer) % 8 == 0
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 818b171..5c3cf32 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -24,21 +24,21 @@
 
 namespace android {
 
+static const StaticString16 emptyString(u"");
 static inline char16_t* getEmptyString() {
-    static SharedBuffer* gEmptyStringBuf = [] {
-        SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t));
-        char16_t* str = static_cast<char16_t*>(buf->data());
-        *str = 0;
-        return buf;
-    }();
-
-    gEmptyStringBuf->acquire();
-    return static_cast<char16_t*>(gEmptyStringBuf->data());
+    return const_cast<char16_t*>(emptyString.string());
 }
 
 // ---------------------------------------------------------------------------
 
-static char16_t* allocFromUTF8(const char* u8str, size_t u8len)
+void* String16::alloc(size_t size)
+{
+    SharedBuffer* buf = SharedBuffer::alloc(size);
+    buf->mClientMetadata = kIsSharedBufferAllocated;
+    return buf;
+}
+
+char16_t* String16::allocFromUTF8(const char* u8str, size_t u8len)
 {
     if (u8len == 0) return getEmptyString();
 
@@ -49,7 +49,7 @@
         return getEmptyString();
     }
 
-    SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1));
+    SharedBuffer* buf = static_cast<SharedBuffer*>(alloc(sizeof(char16_t) * (u16len + 1)));
     if (buf) {
         u8cur = (const uint8_t*) u8str;
         char16_t* u16str = (char16_t*)buf->data();
@@ -66,13 +66,13 @@
     return getEmptyString();
 }
 
-static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len) {
+char16_t* String16::allocFromUTF16(const char16_t* u16str, size_t u16len) {
     if (u16len >= SIZE_MAX / sizeof(char16_t)) {
         android_errorWriteLog(0x534e4554, "73826242");
         abort();
     }
 
-    SharedBuffer* buf = SharedBuffer::alloc((u16len + 1) * sizeof(char16_t));
+    SharedBuffer* buf = static_cast<SharedBuffer*>(alloc((u16len + 1) * sizeof(char16_t)));
     ALOG_ASSERT(buf, "Unable to allocate shared buffer");
     if (buf) {
         char16_t* str = (char16_t*)buf->data();
@@ -97,8 +97,8 @@
     // having run. In this case we always allocate an empty string. It's less
     // efficient than using getEmptyString(), but we assume it's uncommon.
 
-    char16_t* data = static_cast<char16_t*>(
-            SharedBuffer::alloc(sizeof(char16_t))->data());
+    SharedBuffer* buf = static_cast<SharedBuffer*>(alloc(sizeof(char16_t)));
+    char16_t* data = static_cast<char16_t*>(buf->data());
     data[0] = 0;
     mString = data;
 }
@@ -106,7 +106,7 @@
 String16::String16(const String16& o)
     : mString(o.mString)
 {
-    SharedBuffer::bufferFromData(mString)->acquire();
+    acquire();
 }
 
 String16::String16(const String16& o, size_t len, size_t begin)
@@ -136,26 +136,30 @@
 
 String16::~String16()
 {
-    SharedBuffer::bufferFromData(mString)->release();
+    release();
 }
 
 size_t String16::size() const
 {
-    return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1;
+    if (isStaticString()) {
+        return staticStringSize();
+    } else {
+        return SharedBuffer::sizeFromData(mString) / sizeof(char16_t) - 1;
+    }
 }
 
 void String16::setTo(const String16& other)
 {
-    SharedBuffer::bufferFromData(other.mString)->acquire();
-    SharedBuffer::bufferFromData(mString)->release();
+    release();
     mString = other.mString;
+    acquire();
 }
 
 status_t String16::setTo(const String16& other, size_t len, size_t begin)
 {
     const size_t N = other.size();
     if (begin >= N) {
-        SharedBuffer::bufferFromData(mString)->release();
+        release();
         mString = getEmptyString();
         return OK;
     }
@@ -184,8 +188,7 @@
         abort();
     }
 
-    SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
-        ->editResize((len+1)*sizeof(char16_t));
+    SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((len + 1) * sizeof(char16_t)));
     if (buf) {
         char16_t* str = (char16_t*)buf->data();
         memmove(str, other, len*sizeof(char16_t));
@@ -212,8 +215,8 @@
         abort();
     }
 
-    SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
-        ->editResize((myLen+otherLen+1)*sizeof(char16_t));
+    SharedBuffer* buf =
+            static_cast<SharedBuffer*>(editResize((myLen + otherLen + 1) * sizeof(char16_t)));
     if (buf) {
         char16_t* str = (char16_t*)buf->data();
         memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t));
@@ -238,8 +241,8 @@
         abort();
     }
 
-    SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
-        ->editResize((myLen+otherLen+1)*sizeof(char16_t));
+    SharedBuffer* buf =
+            static_cast<SharedBuffer*>(editResize((myLen + otherLen + 1) * sizeof(char16_t)));
     if (buf) {
         char16_t* str = (char16_t*)buf->data();
         memcpy(str+myLen, chrs, otherLen*sizeof(char16_t));
@@ -273,8 +276,8 @@
            len, myLen, String8(chrs, len).string());
     #endif
 
-    SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
-        ->editResize((myLen+len+1)*sizeof(char16_t));
+    SharedBuffer* buf =
+            static_cast<SharedBuffer*>(editResize((myLen + len + 1) * sizeof(char16_t)));
     if (buf) {
         char16_t* str = (char16_t*)buf->data();
         if (pos < myLen) {
@@ -338,23 +341,87 @@
     return strstr16(mString, chrs) != nullptr;
 }
 
+void* String16::edit() {
+    SharedBuffer* buf;
+    if (isStaticString()) {
+        buf = static_cast<SharedBuffer*>(alloc((size() + 1) * sizeof(char16_t)));
+        if (buf) {
+            buf->acquire();
+            memcpy(buf->data(), mString, (size() + 1) * sizeof(char16_t));
+        }
+    } else {
+        buf = SharedBuffer::bufferFromData(mString)->edit();
+        buf->mClientMetadata = kIsSharedBufferAllocated;
+    }
+    return buf;
+}
+
+void* String16::editResize(size_t newSize) {
+    SharedBuffer* buf;
+    if (isStaticString()) {
+        size_t copySize = (size() + 1) * sizeof(char16_t);
+        if (newSize < copySize) {
+            copySize = newSize;
+        }
+        buf = static_cast<SharedBuffer*>(alloc(newSize));
+        if (buf) {
+            buf->acquire();
+            memcpy(buf->data(), mString, copySize);
+        }
+    } else {
+        buf = SharedBuffer::bufferFromData(mString)->editResize(newSize);
+        buf->mClientMetadata = kIsSharedBufferAllocated;
+    }
+    return buf;
+}
+
+void String16::acquire()
+{
+    if (!isStaticString()) {
+        SharedBuffer::bufferFromData(mString)->acquire();
+    }
+}
+
+void String16::release()
+{
+    if (!isStaticString()) {
+        SharedBuffer::bufferFromData(mString)->release();
+    }
+}
+
+bool String16::isStaticString() const {
+    // See String16.h for notes on the memory layout of String16::StaticData and
+    // SharedBuffer.
+    static_assert(sizeof(SharedBuffer) - offsetof(SharedBuffer, mClientMetadata) == 4);
+    const uint32_t* p = reinterpret_cast<const uint32_t*>(mString);
+    return (*(p - 1) & kIsSharedBufferAllocated) == 0;
+}
+
+size_t String16::staticStringSize() const {
+    // See String16.h for notes on the memory layout of String16::StaticData and
+    // SharedBuffer.
+    static_assert(sizeof(SharedBuffer) - offsetof(SharedBuffer, mClientMetadata) == 4);
+    const uint32_t* p = reinterpret_cast<const uint32_t*>(mString);
+    return static_cast<size_t>(*(p - 1));
+}
+
 status_t String16::makeLower()
 {
     const size_t N = size();
     const char16_t* str = string();
-    char16_t* edit = nullptr;
+    char16_t* edited = nullptr;
     for (size_t i=0; i<N; i++) {
         const char16_t v = str[i];
         if (v >= 'A' && v <= 'Z') {
-            if (!edit) {
-                SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
+            if (!edited) {
+                SharedBuffer* buf = static_cast<SharedBuffer*>(edit());
                 if (!buf) {
                     return NO_MEMORY;
                 }
-                edit = (char16_t*)buf->data();
-                mString = str = edit;
+                edited = (char16_t*)buf->data();
+                mString = str = edited;
             }
-            edit[i] = tolower((char)v);
+            edited[i] = tolower((char)v);
         }
     }
     return OK;
@@ -364,18 +431,18 @@
 {
     const size_t N = size();
     const char16_t* str = string();
-    char16_t* edit = nullptr;
+    char16_t* edited = nullptr;
     for (size_t i=0; i<N; i++) {
         if (str[i] == replaceThis) {
-            if (!edit) {
-                SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
+            if (!edited) {
+                SharedBuffer* buf = static_cast<SharedBuffer*>(edit());
                 if (!buf) {
                     return NO_MEMORY;
                 }
-                edit = (char16_t*)buf->data();
-                mString = str = edit;
+                edited = (char16_t*)buf->data();
+                mString = str = edited;
             }
-            edit[i] = withThis;
+            edited[i] = withThis;
         }
     }
     return OK;
@@ -385,7 +452,7 @@
 {
     const size_t N = size();
     if (begin >= N) {
-        SharedBuffer::bufferFromData(mString)->release();
+        release();
         mString = getEmptyString();
         return OK;
     }
@@ -395,8 +462,7 @@
     }
 
     if (begin > 0) {
-        SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
-            ->editResize((N+1)*sizeof(char16_t));
+        SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((N + 1) * sizeof(char16_t)));
         if (!buf) {
             return NO_MEMORY;
         }
@@ -404,8 +470,7 @@
         memmove(str, str+begin, (N-begin+1)*sizeof(char16_t));
         mString = str;
     }
-    SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
-        ->editResize((len+1)*sizeof(char16_t));
+    SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((len + 1) * sizeof(char16_t)));
     if (buf) {
         char16_t* str = (char16_t*)buf->data();
         str[len] = 0;
diff --git a/libutils/String16_test.cpp b/libutils/String16_test.cpp
new file mode 100644
index 0000000..f1f24c3
--- /dev/null
+++ b/libutils/String16_test.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/String16.h>
+#include <utils/String8.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+::testing::AssertionResult Char16_tStringEquals(const char16_t* a, const char16_t* b) {
+    if (strcmp16(a, b) != 0) {
+        return ::testing::AssertionFailure()
+               << "\"" << String8(a).c_str() << "\" not equal to \"" << String8(b).c_str() << "\"";
+    }
+    return ::testing::AssertionSuccess();
+}
+
+#define EXPECT_STR16EQ(a, b) EXPECT_TRUE(Char16_tStringEquals(a, b))
+
+TEST(String16Test, FromChar16_t) {
+    String16 tmp(u"Verify me");
+    EXPECT_STR16EQ(u"Verify me", tmp);
+}
+
+TEST(String16Test, FromChar16_tSized) {
+    String16 tmp(u"Verify me", 7);
+    EXPECT_STR16EQ(u"Verify ", tmp);
+}
+
+TEST(String16Test, FromChar) {
+    String16 tmp("Verify me");
+    EXPECT_STR16EQ(u"Verify me", tmp);
+}
+
+TEST(String16Test, FromCharSized) {
+    String16 tmp("Verify me", 7);
+    EXPECT_STR16EQ(u"Verify ", tmp);
+}
+
+TEST(String16Test, Copy) {
+    String16 tmp("Verify me");
+    String16 another = tmp;
+    EXPECT_STR16EQ(u"Verify me", tmp);
+    EXPECT_STR16EQ(u"Verify me", another);
+}
+
+TEST(String16Test, Move) {
+    String16 tmp("Verify me");
+    String16 another(std::move(tmp));
+    EXPECT_STR16EQ(u"Verify me", another);
+}
+
+TEST(String16Test, Size) {
+    String16 tmp("Verify me");
+    EXPECT_EQ(9U, tmp.size());
+}
+
+TEST(String16Test, setTo) {
+    String16 tmp("Verify me");
+    tmp.setTo(u"New content");
+    EXPECT_EQ(11U, tmp.size());
+    EXPECT_STR16EQ(u"New content", tmp);
+}
+
+TEST(String16Test, Append) {
+    String16 tmp("Verify me");
+    tmp.append(String16("Hello"));
+    EXPECT_EQ(14U, tmp.size());
+    EXPECT_STR16EQ(u"Verify meHello", tmp);
+}
+
+TEST(String16Test, Insert) {
+    String16 tmp("Verify me");
+    tmp.insert(6, u"Insert");
+    EXPECT_EQ(15U, tmp.size());
+    EXPECT_STR16EQ(u"VerifyInsert me", tmp);
+}
+
+TEST(String16Test, Remove) {
+    String16 tmp("Verify me");
+    tmp.remove(2, 6);
+    EXPECT_EQ(2U, tmp.size());
+    EXPECT_STR16EQ(u" m", tmp);
+}
+
+TEST(String16Test, MakeLower) {
+    String16 tmp("Verify Me!");
+    tmp.makeLower();
+    EXPECT_EQ(10U, tmp.size());
+    EXPECT_STR16EQ(u"verify me!", tmp);
+}
+
+TEST(String16Test, ReplaceAll) {
+    String16 tmp("Verify verify Verify");
+    tmp.replaceAll(u'r', u'!');
+    EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp);
+}
+
+TEST(String16Test, Compare) {
+    String16 tmp("Verify me");
+    EXPECT_EQ(String16(u"Verify me"), tmp);
+}
+
+TEST(String16Test, StaticString) {
+    String16 nonStaticString("NonStatic");
+    StaticString16 staticString(u"Static");
+
+    EXPECT_TRUE(staticString.isStaticString());
+    EXPECT_FALSE(nonStaticString.isStaticString());
+}
+
+TEST(String16Test, StaticStringCopy) {
+    StaticString16 tmp(u"Verify me");
+    String16 another = tmp;
+    EXPECT_STR16EQ(u"Verify me", tmp);
+    EXPECT_STR16EQ(u"Verify me", another);
+    EXPECT_TRUE(tmp.isStaticString());
+    EXPECT_TRUE(another.isStaticString());
+}
+
+TEST(String16Test, StaticStringMove) {
+    StaticString16 tmp(u"Verify me");
+    String16 another(std::move(tmp));
+    EXPECT_STR16EQ(u"Verify me", another);
+    EXPECT_TRUE(another.isStaticString());
+}
+
+TEST(String16Test, StaticStringSize) {
+    StaticString16 tmp(u"Verify me");
+    EXPECT_EQ(9U, tmp.size());
+}
+
+TEST(String16Test, StaticStringSetTo) {
+    StaticString16 tmp(u"Verify me");
+    tmp.setTo(u"New content");
+    EXPECT_EQ(11U, tmp.size());
+    EXPECT_STR16EQ(u"New content", tmp);
+    EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringAppend) {
+    StaticString16 tmp(u"Verify me");
+    tmp.append(String16("Hello"));
+    EXPECT_EQ(14U, tmp.size());
+    EXPECT_STR16EQ(u"Verify meHello", tmp);
+    EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringInsert) {
+    StaticString16 tmp(u"Verify me");
+    tmp.insert(6, u"Insert");
+    EXPECT_EQ(15U, tmp.size());
+    EXPECT_STR16EQ(u"VerifyInsert me", tmp);
+    EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringRemove) {
+    StaticString16 tmp(u"Verify me");
+    tmp.remove(2, 6);
+    EXPECT_EQ(2U, tmp.size());
+    EXPECT_STR16EQ(u" m", tmp);
+    EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringMakeLower) {
+    StaticString16 tmp(u"Verify me!");
+    tmp.makeLower();
+    EXPECT_EQ(10U, tmp.size());
+    EXPECT_STR16EQ(u"verify me!", tmp);
+    EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringReplaceAll) {
+    StaticString16 tmp(u"Verify verify Verify");
+    tmp.replaceAll(u'r', u'!');
+    EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp);
+    EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringCompare) {
+    StaticString16 tmp(u"Verify me");
+    EXPECT_EQ(String16(u"Verify me"), tmp);
+}
+
+TEST(String16Test, StringSetToStaticString) {
+    StaticString16 tmp(u"Verify me");
+    String16 another(u"nonstatic");
+    another = tmp;
+    EXPECT_STR16EQ(u"Verify me", tmp);
+    EXPECT_STR16EQ(u"Verify me", another);
+}
+
+TEST(String16Test, StringMoveFromStaticString) {
+    StaticString16 tmp(u"Verify me");
+    String16 another(std::move(tmp));
+    EXPECT_STR16EQ(u"Verify me", another);
+}
+
+TEST(String16Test, EmptyStringIsStatic) {
+    String16 tmp("");
+    EXPECT_TRUE(tmp.isStaticString());
+}
+
+}  // namespace android
diff --git a/libutils/Vector_benchmark.cpp b/libutils/Vector_benchmark.cpp
new file mode 100644
index 0000000..c23d499
--- /dev/null
+++ b/libutils/Vector_benchmark.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 <benchmark/benchmark.h>
+#include <utils/Vector.h>
+#include <vector>
+
+void BM_fill_android_vector(benchmark::State& state) {
+    android::Vector<char> v;
+    while (state.KeepRunning()) {
+        v.push('A');
+    }
+}
+BENCHMARK(BM_fill_android_vector);
+
+void BM_fill_std_vector(benchmark::State& state) {
+    std::vector<char> v;
+    while (state.KeepRunning()) {
+        v.push_back('A');
+    }
+}
+BENCHMARK(BM_fill_std_vector);
+
+void BM_prepend_android_vector(benchmark::State& state) {
+    android::Vector<char> v;
+    while (state.KeepRunning()) {
+        v.insertAt('A', 0);
+    }
+}
+BENCHMARK(BM_prepend_android_vector);
+
+void BM_prepend_std_vector(benchmark::State& state) {
+    std::vector<char> v;
+    while (state.KeepRunning()) {
+        v.insert(v.begin(), 'A');
+    }
+}
+BENCHMARK(BM_prepend_std_vector);
+
+BENCHMARK_MAIN();
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index afbc2ed..adc3e7d 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -37,13 +37,17 @@
 
 class String8;
 
+template <size_t N>
+class StaticString16;
+
 // DO NOT USE: please use std::u16string
 
 //! This is a string holding UTF-16 characters.
 class String16
 {
 public:
-    /* use String16(StaticLinkage) if you're statically linking against
+    /*
+     * Use String16(StaticLinkage) if you're statically linking against
      * libutils and declaring an empty static String16, e.g.:
      *
      *   static String16 sAStaticEmptyString(String16::kEmptyString);
@@ -123,8 +127,76 @@
 
     inline                      operator const char16_t*() const;
 
-private:
-            const char16_t*     mString;
+    // Static and non-static String16 behave the same for the users, so
+    // this method isn't of much use for the users. It is public for testing.
+            bool                isStaticString() const;
+
+  private:
+    /*
+     * A flag indicating the type of underlying buffer.
+     */
+    static constexpr uint32_t kIsSharedBufferAllocated = 0x80000000;
+
+    /*
+     * alloc() returns void* so that SharedBuffer class is not exposed.
+     */
+    static void* alloc(size_t size);
+    static char16_t* allocFromUTF8(const char* u8str, size_t u8len);
+    static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len);
+
+    /*
+     * edit() and editResize() return void* so that SharedBuffer class
+     * is not exposed.
+     */
+    void* edit();
+    void* editResize(size_t new_size);
+
+    void acquire();
+    void release();
+
+    size_t staticStringSize() const;
+
+    const char16_t* mString;
+
+protected:
+    /*
+     * Data structure used to allocate static storage for static String16.
+     *
+     * Note that this data structure and SharedBuffer are used interchangably
+     * as the underlying data structure for a String16.  Therefore, the layout
+     * of this data structure must match the part in SharedBuffer that is
+     * visible to String16.
+     */
+    template <size_t N>
+    struct StaticData {
+        // The high bit of 'size' is used as a flag.
+        static_assert(N - 1 < kIsSharedBufferAllocated, "StaticString16 too long!");
+        constexpr StaticData() : size(N - 1), data{0} {}
+        const uint32_t size;
+        char16_t data[N];
+
+        constexpr StaticData(const StaticData<N>&) = default;
+    };
+
+    /*
+     * Helper function for constructing a StaticData object.
+     */
+    template <size_t N>
+    static constexpr const StaticData<N> makeStaticData(const char16_t (&s)[N]) {
+        StaticData<N> r;
+        // The 'size' field is at the same location where mClientMetadata would
+        // be for a SharedBuffer.  We do NOT set kIsSharedBufferAllocated flag
+        // here.
+        for (size_t i = 0; i < N - 1; ++i) r.data[i] = s[i];
+        return r;
+    }
+
+    template <size_t N>
+    explicit constexpr String16(const StaticData<N>& s) : mString(s.data) {}
+
+public:
+    template <size_t N>
+    explicit constexpr String16(const StaticString16<N>& s) : mString(s.mString) {}
 };
 
 // String16 can be trivially moved using memcpy() because moving does not
@@ -132,6 +204,42 @@
 ANDROID_TRIVIAL_MOVE_TRAIT(String16)
 
 // ---------------------------------------------------------------------------
+
+/*
+ * A StaticString16 object is a specialized String16 object.  Instead of holding
+ * the string data in a ref counted SharedBuffer object, it holds data in a
+ * buffer within StaticString16 itself.  Note that this buffer is NOT ref
+ * counted and is assumed to be available for as long as there is at least a
+ * String16 object using it.  Therefore, one must be extra careful to NEVER
+ * assign a StaticString16 to a String16 that outlives the StaticString16
+ * object.
+ *
+ * THE SAFEST APPROACH IS TO USE StaticString16 ONLY AS GLOBAL VARIABLES.
+ *
+ * A StaticString16 SHOULD NEVER APPEAR IN APIs.  USE String16 INSTEAD.
+ */
+template <size_t N>
+class StaticString16 : public String16 {
+public:
+    constexpr StaticString16(const char16_t (&s)[N]) : String16(mData), mData(makeStaticData(s)) {}
+
+    constexpr StaticString16(const StaticString16<N>& other)
+        : String16(mData), mData(other.mData) {}
+
+    constexpr StaticString16(const StaticString16<N>&&) = delete;
+
+    // There is no reason why one would want to 'new' a StaticString16.  Delete
+    // it to discourage misuse.
+    static void* operator new(std::size_t) = delete;
+
+private:
+    const StaticData<N> mData;
+};
+
+template <typename F>
+StaticString16(const F&)->StaticString16<sizeof(F) / sizeof(char16_t)>;
+
+// ---------------------------------------------------------------------------
 // No user servicable parts below.
 
 inline int compare_type(const String16& lhs, const String16& rhs)
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 42af751..d17da12 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -1433,8 +1433,8 @@
     set_process_group_and_prio(pid, SP_FOREGROUND, ANDROID_PRIORITY_HIGHEST);
 
     inc_killcnt(procp->oomadj);
-    ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB",
-        taskname, pid, uid, procp->oomadj, tasksize * page_k);
+    ALOGE("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB", taskname, pid, uid, procp->oomadj,
+          tasksize * page_k);
 
     TRACE_KILL_END();
 
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index e2711af..4dcb338 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -32,6 +32,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/cdefs.h>
+#include <sys/ioctl.h>
 #include <sys/resource.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
@@ -158,8 +159,22 @@
                          enum helpType showHelp, const char* fmt, ...)
     __printflike(3, 4);
 
-static int openLogFile(const char* pathname) {
-    return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
+#ifndef F2FS_IOC_SET_PIN_FILE
+#define F2FS_IOCTL_MAGIC       0xf5
+#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
+#endif
+
+static int openLogFile(const char* pathname, size_t sizeKB) {
+    int fd = open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
+    if (fd < 0) {
+        return fd;
+    }
+
+    // no need to check errors
+    __u32 set = 1;
+    ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set);
+    fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, (sizeKB << 10));
+    return fd;
 }
 
 static void close_output(android_logcat_context_internal* context) {
@@ -192,6 +207,7 @@
             if (context->fds[1] == context->output_fd) {
                 context->fds[1] = -1;
             }
+            posix_fadvise(context->output_fd, 0, 0, POSIX_FADV_DONTNEED);
             close(context->output_fd);
         }
         context->output_fd = -1;
@@ -276,7 +292,7 @@
         }
     }
 
-    context->output_fd = openLogFile(context->outputFileName);
+    context->output_fd = openLogFile(context->outputFileName, context->logRotateSizeKBytes);
 
     if (context->output_fd < 0) {
         logcat_panic(context, HELP_FALSE, "couldn't open output file");
@@ -398,7 +414,7 @@
 
     close_output(context);
 
-    context->output_fd = openLogFile(context->outputFileName);
+    context->output_fd = openLogFile(context->outputFileName, context->logRotateSizeKBytes);
 
     if (context->output_fd < 0) {
         logcat_panic(context, HELP_FALSE, "couldn't open output file");
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index 26c9de3..e986184 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -52,7 +52,7 @@
     stop logcatd
 
 # logcatd service
-service logcatd /system/bin/logcatd -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r ${logd.logpersistd.rotate_kbytes:-1024} -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
+service logcatd /system/bin/logcatd -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r ${logd.logpersistd.rotate_kbytes:-2048} -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
     class late_start
     disabled
     # logd for write to /data/misc/logd, log group for read from log daemon
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 4559050..df1d929 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -214,7 +214,7 @@
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 
-# Start of runtime APEX compatibility.
+# Start of i18n and ART APEX compatibility.
 #
 # Meta-comment:
 # The placing of this section is somewhat arbitrary. The LOCAL_POST_INSTALL_CMD
@@ -226,32 +226,21 @@
 # come to rely on them.
 
 # http://b/121248172 - create a link from /system/usr/icu to
-# /apex/com.android.runtime/etc/icu so that apps can find the ICU .dat file.
+# /apex/com.android.i18n/etc/icu so that apps can find the ICU .dat file.
 # A symlink can't overwrite a directory and the /system/usr/icu directory once
 # existed so the required structure must be created whatever we find.
 LOCAL_POST_INSTALL_CMD = mkdir -p $(TARGET_OUT)/usr && rm -rf $(TARGET_OUT)/usr/icu
 LOCAL_POST_INSTALL_CMD += && ln -sf /apex/com.android.i18n/etc/icu $(TARGET_OUT)/usr/icu
 
 # TODO(b/124106384): Clean up compat symlinks for ART binaries.
-ART_BINARIES := \
-  dalvikvm \
-  dalvikvm32 \
-  dalvikvm64 \
-  dex2oat \
-  dexdiag \
-  dexdump \
-  dexlist \
-  dexoptanalyzer \
-  oatdump \
-  profman \
-
+ART_BINARIES := dalvikvm dex2oat
 LOCAL_POST_INSTALL_CMD += && mkdir -p $(TARGET_OUT)/bin
 $(foreach b,$(ART_BINARIES), \
   $(eval LOCAL_POST_INSTALL_CMD += \
-    && ln -sf /apex/com.android.runtime/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
+    && ln -sf /apex/com.android.art/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
 )
 
-# End of runtime APEX compatibilty.
+# End of i18n and ART APEX compatibilty.
 
 ifeq ($(_enforce_vndk_at_runtime),true)
 
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index f0b1fd2..e598f05 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -85,8 +85,11 @@
 # android_link_namespaces in libnativeloader.
 namespace.runtime.visible = true
 
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
+namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
+namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
 namespace.runtime.links = default
 # Need allow_all_shared_libs because libart.so can dlopen oat files in
 # /system/framework and /data.
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index a639592..e1da587 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -166,8 +166,11 @@
 # android_link_namespaces in libnativeloader.
 namespace.runtime.visible = true
 
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
+namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
+namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
 namespace.runtime.links = default
 # Need allow_all_shared_libs because libart.so can dlopen oat files in
 # /system/framework and /data.
@@ -187,6 +190,7 @@
 namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
 
 namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+namespace.media.asan.permitted.paths = /apex/com.android.media/${LIB}/extractors
 
 namespace.media.links = default,neuralnetworks
 namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
@@ -328,7 +332,7 @@
 namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
 namespace.rs.asan.permitted.paths += /data
 
-namespace.rs.links = default,vndk,neuralnetworks
+namespace.rs.links = default,neuralnetworks
 
 namespace.rs.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
@@ -336,8 +340,6 @@
 # namespace because RS framework libs are using them.
 namespace.rs.link.default.shared_libs += %PRIVATE_LLNDK_LIBRARIES%
 
-namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
-
 # LLNDK library moved into apex
 namespace.rs.link.neuralnetworks.shared_libs = libneuralnetworks.so
 
@@ -490,8 +492,11 @@
 ###############################################################################
 namespace.runtime.isolated = true
 
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
+namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
+namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
 namespace.runtime.links = system
 # TODO(b/130340935): Use a dynamically created linker namespace similar to
 # classloader-namespace for oat files, and tighten this up.
@@ -697,8 +702,11 @@
 # android_link_namespaces in libnativeloader.
 namespace.runtime.visible = true
 
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
+namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
+namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
 namespace.runtime.links = default
 # TODO(b/130340935): Use a dynamically created linker namespace similar to
 # classloader-namespace for oat files, and tighten this up.
@@ -716,6 +724,7 @@
 namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
 
 namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+namespace.media.asan.permitted.paths = /apex/com.android.media/${LIB}/extractors
 
 namespace.media.links = default,neuralnetworks
 namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 0bb60ab..4beabd6 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -105,8 +105,11 @@
 # android_link_namespaces in libnativeloader.
 namespace.runtime.visible = true
 
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
+namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
+namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
 namespace.runtime.links = default
 # Need allow_all_shared_libs because libart.so can dlopen oat files in
 # /system/framework and /data.
@@ -265,7 +268,7 @@
 namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
 namespace.rs.asan.permitted.paths += /data
 
-namespace.rs.links = default,vndk,neuralnetworks
+namespace.rs.links = default,neuralnetworks
 
 namespace.rs.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
@@ -273,8 +276,6 @@
 # namespace because RS framework libs are using them.
 namespace.rs.link.default.shared_libs += %PRIVATE_LLNDK_LIBRARIES%
 
-namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
-
 # LLNDK library moved into apex
 namespace.rs.link.neuralnetworks.shared_libs = libneuralnetworks.so
 
@@ -421,8 +422,11 @@
 ###############################################################################
 namespace.runtime.isolated = true
 
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
+namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
+namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
 namespace.runtime.links = default
 # TODO(b/130340935): Use a dynamically created linker namespace similar to
 # classloader-namespace for oat files, and tighten this up.
@@ -505,8 +509,11 @@
 # android_link_namespaces in libnativeloader.
 namespace.runtime.visible = true
 
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
+namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
+namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
 namespace.runtime.links = default
 # TODO(b/130340935): Use a dynamically created linker namespace similar to
 # classloader-namespace for oat files, and tighten this up.
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index d8f6095..27e855f 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -1,6 +1,7 @@
 # See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
 libandroid.so
 libaaudio.so
+libamidi.so
 libbinder_ndk.so
 libc.so
 libcamera2ndk.so
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
index 20905bf..b565340 100644
--- a/rootdir/etc/public.libraries.iot.txt
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -2,6 +2,7 @@
 libandroid.so
 libandroidthings.so
 libaaudio.so
+libamidi.so
 libbinder_ndk.so
 libc.so
 libcamera2ndk.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 4ece5b5..7cbda08 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -1,6 +1,7 @@
 # See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
 libandroid.so
 libaaudio.so
+libamidi.so
 libbinder_ndk.so
 libc.so
 libcamera2ndk.so
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 93b7f43..17f6596 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -5,7 +5,7 @@
     export ANDROID_ASSETS /system/app
     export ANDROID_DATA /data
     export ANDROID_STORAGE /storage
-    export ANDROID_RUNTIME_ROOT /apex/com.android.runtime
+    export ANDROID_RUNTIME_ROOT /apex/com.android.art
     export ANDROID_I18N_ROOT /apex/com.android.i18n
     export ANDROID_TZDATA_ROOT /apex/com.android.tzdata
     export EXTERNAL_STORAGE /sdcard
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 0f61a61..a15b501 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -52,6 +52,36 @@
     # the libraries are available to the processes started after this statement.
     exec_start apexd-bootstrap
 
+    # These must already exist by the time boringssl_self_test32 / boringssl_self_test64 run.
+    mkdir /dev/boringssl 0755 root root
+    mkdir /dev/boringssl/selftest 0755 root root
+
+# Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
+on early-init && property:ro.product.cpu.abilist32=*
+    exec_start boringssl_self_test32
+on early-init && property:ro.product.cpu.abilist64=*
+    exec_start boringssl_self_test64
+on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
+    exec_start boringssl_self_test_apex32
+on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
+    exec_start boringssl_self_test_apex64
+
+service boringssl_self_test32 /system/bin/boringssl_self_test32
+    setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
+    reboot_on_failure reboot,bootloader,boringssl-self-check-failed
+
+service boringssl_self_test64 /system/bin/boringssl_self_test64
+    setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
+    reboot_on_failure reboot,bootloader,boringssl-self-check-failed
+
+service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
+    setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
+    reboot_on_failure reboot,bootloader,boringssl-self-check-failed
+
+service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
+    setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
+    reboot_on_failure reboot,bootloader,boringssl-self-check-failed
+
 on init
     sysclktz 0
 
@@ -127,10 +157,6 @@
     mkdir /mnt/expand 0771 system system
     mkdir /mnt/appfuse 0711 root root
 
-    # tmpfs place for BORINGSSL_self_test() to remember whether it has run
-    mkdir /dev/boringssl 0755 root root
-    mkdir /dev/boringssl/selftest 0755 root root
-
     # Storage views to support runtime permissions
     mkdir /mnt/runtime 0700 root root
     mkdir /mnt/runtime/default 0755 root root
@@ -431,15 +457,10 @@
     # HALs required before storage encryption can get unlocked (FBE/FDE)
     class_start early_hal
 
-    # Check and mark a successful boot, before mounting userdata with mount_all.
-    # No-op for non-A/B device.
-    exec_start update_verifier_nonencrypted
-
 on post-fs-data
     mark_post_data
 
     # Start checkpoint before we touch data
-    start vold
     exec - system system -- /system/bin/vdc checkpoint prepareCheckpoint
 
     # We chown/chmod /data again so because mount is run as root + defaults
@@ -536,6 +557,7 @@
     mkdir /data/misc/profiles/ref 0771 system system
     mkdir /data/misc/profman 0770 system shell
     mkdir /data/misc/gcov 0770 root root
+    mkdir /data/misc/installd 0700 root root
 
     mkdir /data/preloads 0775 system system
 
@@ -668,16 +690,22 @@
 # It is recommended to put unnecessary data/ initialization from post-fs-data
 # to start-zygote in device's init.rc to unblock zygote start.
 on zygote-start && property:ro.crypto.state=unencrypted
+    # A/B update verifier that marks a successful boot.
+    exec_start update_verifier_nonencrypted
     start netd
     start zygote
     start zygote_secondary
 
 on zygote-start && property:ro.crypto.state=unsupported
+    # A/B update verifier that marks a successful boot.
+    exec_start update_verifier_nonencrypted
     start netd
     start zygote
     start zygote_secondary
 
 on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
+    # A/B update verifier that marks a successful boot.
+    exec_start update_verifier_nonencrypted
     start netd
     start zygote
     start zygote_secondary
@@ -701,6 +729,12 @@
     chown root system /sys/module/lowmemorykiller/parameters/minfree
     chmod 0664 /sys/module/lowmemorykiller/parameters/minfree
 
+    # System server manages zram writeback
+    chown root system /sys/block/zram0/idle
+    chmod 0664 /sys/block/zram0/idle
+    chown root system /sys/block/zram0/writeback
+    chmod 0664 /sys/block/zram0/writeback
+
     # Tweak background writeout
     write /proc/sys/vm/dirty_expire_centisecs 200
     write /proc/sys/vm/dirty_background_ratio  5
@@ -800,6 +834,8 @@
     trigger zygote-start
 
 on property:vold.decrypt=trigger_restart_min_framework
+    # A/B update verifier that marks a successful boot.
+    exec_start update_verifier
     class_start main
 
 on property:vold.decrypt=trigger_restart_framework
@@ -820,6 +856,9 @@
 
 on property:sys.boot_completed=1
     bootchart stop
+    # Setup per_boot directory so other .rc could start to use it on boot_completed
+    exec - system system -- /bin/rm -rf /data/per_boot
+    mkdir /data/per_boot 0700 system system
 
 # system server cannot write to /proc/sys files,
 # and chown/chmod does not work for /proc/sys/ entries.
@@ -867,6 +906,8 @@
     chmod 0773 /data/misc/trace
     # Give reads to anyone for the window trace folder on debug builds.
     chmod 0775 /data/misc/wmtrace
+
+on init && property:ro.debuggable=1
     start console
 
 service flash_recovery /system/bin/install-recovery.sh
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index f0681d2..b6cba90 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -14,7 +14,7 @@
 # adbd is controlled via property triggers in init.<platform>.usb.rc
 service adbd /system/bin/adbd --root_seclabel=u:r:su:s0
     class core
-    socket adbd stream 660 system system
+    socket adbd seqpacket 660 system system
     disabled
     seclabel u:r:adbd:s0
 
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index f8e680d..bf3fb42 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -4,7 +4,7 @@
     user root
     group root readproc reserved_disk
     socket zygote stream 660 root system
-    socket blastula_pool stream 660 root system
+    socket usap_pool_primary stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 0235370..1bab588 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -4,7 +4,7 @@
     user root
     group root readproc reserved_disk
     socket zygote stream 660 root system
-    socket blastula_pool stream 660 root system
+    socket usap_pool_primary stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
@@ -20,6 +20,6 @@
     user root
     group root readproc reserved_disk
     socket zygote_secondary stream 660 root system
-    socket blastula_pool_secondary stream 660 root system
+    socket usap_pool_secondary stream 660 root system
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 3f3cc15..6fa210a 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -4,7 +4,7 @@
     user root
     group root readproc reserved_disk
     socket zygote stream 660 root system
-    socket blastula_pool stream 660 root system
+    socket usap_pool_primary stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index fae38c9..48461ec 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -4,7 +4,7 @@
     user root
     group root readproc reserved_disk
     socket zygote stream 660 root system
-    socket blastula_pool stream 660 root system
+    socket usap_pool_primary stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
@@ -20,6 +20,6 @@
     user root
     group root readproc reserved_disk
     socket zygote_secondary stream 660 root system
-    socket blastula_pool_secondary stream 660 root system
+    socket usap_pool_secondary stream 660 root system
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index dbe60e5..9d131dc 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -70,19 +70,30 @@
 # /system image.
 llndk_libraries_moved_to_apex_list:=$(LLNDK_MOVED_TO_APEX_LIBRARIES)
 
+# Returns the unique installed basenames of a module, or module.so if there are
+# none.  The guess is to handle cases like libc, where the module itself is
+# marked uninstallable but a symlink is installed with the name libc.so.
 # $(1): list of libraries
-# $(2): output file to write the list of libraries to
-define write-libs-to-file
-$(2): PRIVATE_LIBRARIES := $(1)
-$(2):
-	echo -n > $$@ && $$(foreach lib,$$(PRIVATE_LIBRARIES),echo $$(lib).so >> $$@;)
+# $(2): suffix to to add to each library (not used for guess)
+define module-installed-files-or-guess
+$(foreach lib,$(1),$(or $(strip $(sort $(notdir $(call module-installed-files,$(lib)$(2))))),$(lib).so))
 endef
-$(eval $(call write-libs-to-file,$(llndk_libraries_list),$(llndk_libraries_file)))
-$(eval $(call write-libs-to-file,$(vndksp_libraries_list),$(vndksp_libraries_file)))
-$(eval $(call write-libs-to-file,$(VNDK_CORE_LIBRARIES),$(vndkcore_libraries_file)))
-$(eval $(call write-libs-to-file,$(VNDK_PRIVATE_LIBRARIES),$(vndkprivate_libraries_file)))
+
+# $(1): list of libraries
+# $(2): suffix to add to each library
+# $(3): output file to write the list of libraries to
+define write-libs-to-file
+$(3): PRIVATE_LIBRARIES := $(1)
+$(3): PRIVATE_SUFFIX := $(2)
+$(3):
+	echo -n > $$@ && $$(foreach so,$$(call module-installed-files-or-guess,$$(PRIVATE_LIBRARIES),$$(PRIVATE_SUFFIX)),echo $$(so) >> $$@;)
+endef
+$(eval $(call write-libs-to-file,$(llndk_libraries_list),,$(llndk_libraries_file)))
+$(eval $(call write-libs-to-file,$(vndksp_libraries_list),.vendor,$(vndksp_libraries_file)))
+$(eval $(call write-libs-to-file,$(VNDK_CORE_LIBRARIES),.vendor,$(vndkcore_libraries_file)))
+$(eval $(call write-libs-to-file,$(VNDK_PRIVATE_LIBRARIES),.vendor,$(vndkprivate_libraries_file)))
 ifeq ($(my_vndk_use_core_variant),true)
-$(eval $(call write-libs-to-file,$(VNDK_USING_CORE_VARIANT_LIBRARIES),$(vndk_using_core_variant_libraries_file)))
+$(eval $(call write-libs-to-file,$(VNDK_USING_CORE_VARIANT_LIBRARIES),,$(vndk_using_core_variant_libraries_file)))
 endif
 endif # ifneq ($(lib_list_from_prebuilts),true)
 
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index 2b35819..622de5b 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -315,7 +315,8 @@
         PLOG(ERROR) << "setting RLIMIT_NOFILE failed";
     }
 
-    while ((fs_read_atomic_int("/data/.layout_version", &fs_version) == -1) || (fs_version < 3)) {
+    while ((fs_read_atomic_int("/data/misc/installd/layout_version", &fs_version) == -1) ||
+           (fs_version < 3)) {
         LOG(ERROR) << "installd fs upgrade not yet complete; waiting...";
         sleep(1);
     }
diff --git a/storaged/Android.bp b/storaged/Android.bp
index 733b60f..cc19481 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -24,8 +24,6 @@
         "libbinder",
         "libcutils",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "liblog",
         "libprotobuf-cpp-lite",
         "libsysutils",
diff --git a/trusty/gatekeeper/Android.bp b/trusty/gatekeeper/Android.bp
index 1666cfb..e553af1 100644
--- a/trusty/gatekeeper/Android.bp
+++ b/trusty/gatekeeper/Android.bp
@@ -42,7 +42,6 @@
         "android.hardware.gatekeeper@1.0",
         "libbase",
         "libhidlbase",
-        "libhidltransport",
         "libgatekeeper",
         "libutils",
         "liblog",
diff --git a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
index b5fc6bf..ec2ba12 100644
--- a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
+++ b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
@@ -17,6 +17,7 @@
 
 #define LOG_TAG "android.hardware.keymaster@4.0-impl.trusty"
 
+#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
 #include <authorization_set.h>
 #include <cutils/log.h>
 #include <keymaster/android_keymaster_messages.h>
@@ -46,6 +47,9 @@
 using ::keymaster::UpdateOperationResponse;
 using ::keymaster::ng::Tag;
 
+typedef ::android::hardware::keymaster::V3_0::Tag Tag3;
+using ::android::hardware::keymaster::V4_0::Constants;
+
 namespace keymaster {
 namespace V4_0 {
 namespace {
@@ -79,6 +83,45 @@
     return keymaster_tag_get_type(tag);
 }
 
+/*
+ * injectAuthToken translates a KM4 authToken into a legacy AUTH_TOKEN tag
+ *
+ * Currently, system/keymaster's reference implementation only accepts this
+ * method for passing an auth token, so until that changes we need to
+ * translate to the old format.
+ */
+inline hidl_vec<KeyParameter> injectAuthToken(const hidl_vec<KeyParameter>& keyParamsBase,
+                                              const HardwareAuthToken& authToken) {
+    std::vector<KeyParameter> keyParams(keyParamsBase);
+    const size_t mac_len = static_cast<size_t>(Constants::AUTH_TOKEN_MAC_LENGTH);
+    /*
+     * mac.size() == 0 indicates no token provided, so we should not copy.
+     * mac.size() != mac_len means it is incompatible with the old
+     *   hw_auth_token_t structure. This is forbidden by spec, but to be safe
+     *   we only copy if mac.size() == mac_len, e.g. there is an authToken
+     *   with a hw_auth_token_t compatible MAC.
+     */
+    if (authToken.mac.size() == mac_len) {
+        KeyParameter p;
+        p.tag = static_cast<Tag>(Tag3::AUTH_TOKEN);
+        p.blob.resize(sizeof(hw_auth_token_t));
+
+        hw_auth_token_t* auth_token = reinterpret_cast<hw_auth_token_t*>(p.blob.data());
+        auth_token->version = 0;
+        auth_token->challenge = authToken.challenge;
+        auth_token->user_id = authToken.userId;
+        auth_token->authenticator_id = authToken.authenticatorId;
+        auth_token->authenticator_type =
+                htobe32(static_cast<uint32_t>(authToken.authenticatorType));
+        auth_token->timestamp = htobe64(authToken.timestamp);
+        static_assert(mac_len == sizeof(auth_token->hmac));
+        memcpy(auth_token->hmac, authToken.mac.data(), mac_len);
+        keyParams.push_back(p);
+    }
+
+    return hidl_vec<KeyParameter>(std::move(keyParams));
+}
+
 class KmParamSet : public keymaster_key_param_set_t {
   public:
     KmParamSet(const hidl_vec<KeyParameter>& keyParams) {
@@ -472,11 +515,11 @@
 Return<void> TrustyKeymaster4Device::begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
                                            const hidl_vec<KeyParameter>& inParams,
                                            const HardwareAuthToken& authToken, begin_cb _hidl_cb) {
-    (void)authToken;
+    hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
     BeginOperationRequest request;
     request.purpose = legacy_enum_conversion(purpose);
     request.SetKeyMaterial(key.data(), key.size());
-    request.additional_params.Reinitialize(KmParamSet(inParams));
+    request.additional_params.Reinitialize(KmParamSet(extendedParams));
 
     BeginOperationResponse response;
     impl_->BeginOperation(request, &response);
@@ -496,16 +539,16 @@
                                             const HardwareAuthToken& authToken,
                                             const VerificationToken& verificationToken,
                                             update_cb _hidl_cb) {
-    (void)authToken;
     (void)verificationToken;
     UpdateOperationRequest request;
     UpdateOperationResponse response;
     hidl_vec<KeyParameter> resultParams;
     hidl_vec<uint8_t> resultBlob;
+    hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
     uint32_t resultConsumed = 0;
 
     request.op_handle = operationHandle;
-    request.additional_params.Reinitialize(KmParamSet(inParams));
+    request.additional_params.Reinitialize(KmParamSet(extendedParams));
 
     size_t inp_size = input.size();
     size_t ser_size = request.SerializedSize();
@@ -537,13 +580,13 @@
                                             const HardwareAuthToken& authToken,
                                             const VerificationToken& verificationToken,
                                             finish_cb _hidl_cb) {
-    (void)authToken;
     (void)verificationToken;
     FinishOperationRequest request;
+    hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
     request.op_handle = operationHandle;
     request.input.Reinitialize(input.data(), input.size());
     request.signature.Reinitialize(signature.data(), signature.size());
-    request.additional_params.Reinitialize(KmParamSet(inParams));
+    request.additional_params.Reinitialize(KmParamSet(extendedParams));
 
     FinishOperationResponse response;
     impl_->FinishOperation(request, &response);
diff --git a/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
new file mode 100644
index 0000000..aa30707
--- /dev/null
+++ b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.keymaster</name>
+        <transport>hwbinder</transport>
+        <version>4.0</version>
+        <interface>
+        <name>IKeymasterDevice</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index d107b78..f32a69e 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -101,7 +101,6 @@
         "libutils",
         "libhardware",
         "libhidlbase",
-        "libhidltransport",
         "libtrusty",
         "libkeymaster_messages",
         "libkeymaster3device",
@@ -132,10 +131,11 @@
         "libutils",
         "libhardware",
         "libhidlbase",
-        "libhidltransport",
         "libtrusty",
         "libkeymaster_messages",
         "libkeymaster4",
         "android.hardware.keymaster@4.0"
     ],
+
+    vintf_fragments: ["4.0/android.hardware.keymaster@4.0-service.trusty.xml"],
 }
diff --git a/usbd/Android.bp b/usbd/Android.bp
index 3afa7a9..6a339a1 100644
--- a/usbd/Android.bp
+++ b/usbd/Android.bp
@@ -5,7 +5,6 @@
     shared_libs: [
         "libbase",
         "libhidlbase",
-        "libhidltransport",
         "liblog",
         "libutils",
         "libhardware",