Merge "libsnapshot: start in background"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index f6ef906..c84bd24 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -88,3 +88,5 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/product_services)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/product_services)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/debug_ramdisk/product_services)
+$(call add-clean-step, find $(PRODUCT_OUT) -type l -name "charger" -print0 | xargs -0 rm -f)
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/bin/adbd)
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 375207b..51d5755 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -22,6 +22,9 @@
       "name": "libpackagelistparser_test"
     },
     {
+      "name": "libcutils_test"
+    },
+    {
       "name": "libprocinfo_test"
     },
     {
diff --git a/adb/Android.bp b/adb/Android.bp
index d14fe56..bd1f124 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -134,6 +134,7 @@
     "transport_fd.cpp",
     "transport_local.cpp",
     "transport_usb.cpp",
+    "types.cpp",
 ]
 
 libadb_posix_srcs = [
@@ -356,11 +357,12 @@
     generated_headers: ["platform_tools_version"],
 
     static_libs: [
-        "libadbconnection_server",
         "libdiagnose_usb",
     ],
 
     shared_libs: [
+        "libadbconnection_server",
+        "libadbd_auth",
         "libasyncio",
         "libbase",
         "libcrypto",
@@ -408,12 +410,13 @@
     ],
 
     static_libs: [
-        "libadbconnection_server",
         "libadbd_core",
         "libdiagnose_usb",
     ],
 
     shared_libs: [
+        "libadbconnection_server",
+        "libadbd_auth",
         "libasyncio",
         "libbase",
         "libcrypto",
@@ -460,6 +463,8 @@
     ],
 
     shared_libs: [
+        "libadbconnection_server",
+        "libadbd_auth",
         "libadbd_services",
         "libasyncio",
         "libbase",
@@ -492,23 +497,34 @@
         keep_symbols: true,
     },
 
-    shared_libs: [
+    stl: "libc++_static",
+    static_libs: [
         "libadbd",
+        "libadbd_auth",
         "libadbd_services",
+        "libasyncio",
         "libbase",
         "libcap",
-        "libcrypto",
+        "libcrypto_utils",
         "libcutils",
+        "libdiagnose_usb",
         "liblog",
+        "libmdnssd",
         "libminijail",
         "libselinux",
     ],
+
+    shared_libs: [
+        "libadbconnection_server",
+        "libcrypto",
+    ],
 }
 
 phony {
     name: "adbd_system_binaries",
     required: [
         "abb",
+        "libadbd_auth",
         "reboot",
         "set-verity-state",
     ]
@@ -522,53 +538,6 @@
 }
 
 cc_binary {
-    name: "static_adbd",
-    defaults: ["adbd_defaults", "host_adbd_supported"],
-
-    recovery_available: false,
-    static_executable: true,
-    host_supported: false,
-
-    srcs: [
-        "daemon/main.cpp",
-    ],
-
-    cflags: [
-        "-D_GNU_SOURCE",
-        "-Wno-deprecated-declarations",
-    ],
-
-    strip: {
-        keep_symbols: true,
-    },
-
-    static_libs: [
-        "libadbconnection_server",
-        "libadbd",
-        "libadbd_services",
-        "libasyncio",
-        "libavb_user",
-        "libbase",
-        "libbootloader_message",
-        "libcap",
-        "libcrypto_static",
-        "libcrypto_utils",
-        "libcutils",
-        "libdiagnose_usb",
-        "libext4_utils",
-        "libfec",
-        "libfec_rs",
-        "libfs_mgr",
-        "liblog",
-        "liblp",
-        "libmdnssd",
-        "libminijail",
-        "libselinux",
-        "libsquashfs_utils",
-    ],
-}
-
-cc_binary {
     name: "abb",
 
     defaults: ["adbd_defaults"],
@@ -615,6 +584,7 @@
 
     static_libs: [
         "libadbd",
+        "libadbd_auth",
         "libbase",
         "libcutils",
         "libcrypto_utils",
@@ -759,3 +729,39 @@
         "fastdeploy/testdata/sample.cd",
     ],
 }
+
+prebuilt_etc {
+    name: "com.android.adbd.ld.config.txt",
+    src: "apex/ld.config.txt",
+    filename: "ld.config.txt",
+    installable: false,
+}
+
+apex {
+    name: "com.android.adbd",
+    manifest: "apex/apex_manifest.json",
+
+    binaries: ["adbd"],
+    prebuilts: ["com.android.adbd.init.rc", "com.android.adbd.ld.config.txt"],
+
+    key: "com.android.adbd.key",
+    certificate: ":com.android.adbd.certificate",
+}
+
+apex_key {
+    name: "com.android.adbd.key",
+    public_key: "apex/com.android.adbd.avbpubkey",
+    private_key: "apex/com.android.adbd.pem",
+}
+
+android_app_certificate {
+    name: "com.android.adbd.certificate",
+    certificate: "apex/com.android.adbd",
+}
+
+prebuilt_etc {
+    name: "com.android.adbd.init.rc",
+    src: "apex/adbd.rc",
+    filename: "init.rc",
+    installable: false,
+}
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 1ec145b..9b663be 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -300,6 +300,7 @@
     handle_online(t);
 #else
     if (!auth_required) {
+        LOG(INFO) << "authentication not required";
         handle_online(t);
         send_connect(t);
     } else {
diff --git a/adb/apex/adbd.rc b/adb/apex/adbd.rc
new file mode 100644
index 0000000..9cb072b
--- /dev/null
+++ b/adb/apex/adbd.rc
@@ -0,0 +1,6 @@
+service adbd /apex/com.android.adbd/bin/adbd --root_seclabel=u:r:su:s0
+    class core
+    socket adbd seqpacket 660 system system
+    disabled
+    override
+    seclabel u:r:adbd:s0
diff --git a/adb/apex/apex_manifest.json b/adb/apex/apex_manifest.json
new file mode 100644
index 0000000..ff2df12
--- /dev/null
+++ b/adb/apex/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.adbd",
+  "version": 1
+}
diff --git a/adb/apex/com.android.adbd.avbpubkey b/adb/apex/com.android.adbd.avbpubkey
new file mode 100644
index 0000000..06235bd
--- /dev/null
+++ b/adb/apex/com.android.adbd.avbpubkey
Binary files differ
diff --git a/adb/apex/com.android.adbd.pem b/adb/apex/com.android.adbd.pem
new file mode 100644
index 0000000..2c9a860
--- /dev/null
+++ b/adb/apex/com.android.adbd.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEAwUmO4l/ZdLhmBcBtpwDjih6z6bC7iZDPAVgnFVnYuYDRlVDA
+9OCDwv02Wwc/YCNzON7vt7JBk3o9wyJZpqY9HR1PUjk2DJa/wHtxbskmLcqsvcoh
+wZxmMkgx1mFyni/vQ0tCjjxYmDcnpoVmSntoPG4LBTZRwbgE2roYSuEi7q88Z9+t
+cFiQ5x7MqVTzUFsi1E+rpsxRaTt6Ly9DO71yR1gMTqONsSgmFm8f2HhUCiQzRh7H
+qLwk8eN5ZLPLVc1JBqo8swuH5pR9whR8HaYyQtK1VANRR9oVj3JpRXmyFUk8QjEn
+91I3sFV1lErdP1uh6xi6ewMBp+mQ+ccNFiNJs8PHVprzbEgX2ah45Tnge95ZwnkR
+V/5G/EwGBsggk/BcZjQyj0PExG6LmygR7lq8q4m9ODJj3cmNLZsZu8ukMBxf4Fim
+4/Y7lyaelW0FL+x3CR27wlIxLyIf/JfUNv/cFO/O2MHrDHYdHtCbvg8vpq1MZtDN
++gJIkYQNUfBEtGS4SkH3WWfNet3bcL5yFx5IVdwCY+n635jPA1fvr1vcIiKnyGUm
+zNE+jMOZkgk6lPPuDwllAX0D8nYTm1eBMCTAWCePO0QlcFHCT9j1/xKbFbjt/xYI
+0pXuOc8/1n61F5ybzH/91cS66gqmYUAekUiP0osTIZ7idVFJMoqpc9m7+rECAwEA
+AQKCAgEAkjg9WU89SCk/NNavnQj1GUXEwOKr3JOppdC0MFi5tQuYgSaH8jfuNZIs
+joxbCzWGMt2j5wl4xkJRes7/lyxnSyEjIoaZNsjL4qb/1tlggn+yUhkZlEfmn98x
+pIYvmS+WBwhmHwfT1cLTwgtkqK/W2PA+cgD3tF6rfXQOcIcEUCBMyB/UKws1A0Kv
+fOIA9ycaoBZtOk+SvtL5ybwtVoIoc4ROOydLR1uiBJKoOrA8kzdzenZKgIFkSYDW
+ErJY/l3AAsTCCoiMlIh84ldw1VUm7JpOBnJECOEYMl5Q+PfpGmU+qqxZGaYe7syX
+mElSOl3tjdY1LF3H4Oi2fd5xLfAgDgQjXcawKRYpImEgbqNfEUHW4BE/uVp0hHn+
+W0tCq9hvWoizhjxVq7oEfpdCXJBH0bTg9h3Ho2nuJMHTrUVbSWPTqNJn1xOi4Oxl
+vWsD5qjOOVw1e0P1dtxQ+6a8+rCL8LDvIthQC9Wpt0yXduEi/vUWiMFx2VbcSpNn
+5PB9HK7vvCpR/k0IocaTKt80D3m2svJCnfrekRx/7n//x8imrvtvaYNpoToTSN0q
+hPOpTNc77R4aARJNXm4sVHzGs6HUXsJfODJdjFtTuaDHjLvRoXZi2wFUVWBvIaFg
+/4+PHXjsfMkY15KULKn3f7Xs7K6rmINAb853zti3Qkllv1EeYoECggEBAP9t1Jxe
+hLKnVrJ5jJ0zCT0/ez6qM5cQG8YvXbVICmoAOQ+/NV6qjPABg5j8FuNhpyr45OuJ
+m1oISLgZPVCbIvYx3oZS4ekWUp9Z7jlDGzsWiBCkEUFLRzDLQRUl4bQMI2SWM+vD
+RL9AAM+NHJQ8LJN7ASNdSQw9ZinNCSByCZ52QjPCfRON0OPY4l1FJKHHymzBNXpe
+R5e9a1o9KEIhd7j+3YX9y8SOVrbUe6U8me5LZ6RY+pLB+cA/UHcSQK23hYAkMcvL
+MQny6B57P6rquzFZDG/OUOZWzWub2FSYTTmiYSHPAuB15FyWShs7h7+wK8y2xrSM
+Lq3FWHxzR1OK2HkCggEBAMG4KsAU/lp9rQhNpdw2NQXqbDLgHy09BFMOOWhyp2/Z
+2lbDo9aP746Q56HAfRRgx5oAAtr3SxeN/R/uEJLYzzDU+SrG4TQO/TZ3DPZOAVYM
+oESWG/HXLN4Hw6j4iWt2NvqpnSVJrvYr6zar/QxRHOMwnUoUV3ugmzUkqFC/Nwmm
+nMGJbTQbEha8OyatfwejmhrCkbQMBiCk0AQmgLybUxs2ckGs5jibau7VqXVxly0f
+WkAsWE/qfybQl4oyBhGCFNObr3Co/PHTaD4ACFQQvaEEF4bTuh6wP+MIgJKxL8IB
+SkrKWO5PFbJWY5lacnNMe7ITrWy60HukLlJe5or5lfkCggEBAP3Rwghw1CRDrR9F
+Mbm0UWYPgwTOVN20ICVcRB40LEURW6KOOxaLG+oTVxXay1PAYkGNes2jvEBHIxvt
+2MQUpTVIcPvBuMPKbufykYtNZ+3bgfInVw4vI9sU3uOI9TPZLAJ0T7vkGpiBnUyh
+yNh0w0b6YDMoK8KB8Ndw67TWHUDd+wM8LNYVgpInnylX4ALzae+QPvgOX84laFwP
+kcXFRBcNDExt2uLDHuAnXYbhJYVqYN8rnDPhlbC4OdlYxfTZ/UtMrD769wwP2SER
+ED9jagirmHQx7Ko3b4GTJ/FINtUiyqqx7wXloLtwjMtq6IZPJfcTWXloI6qCBGAG
+ncYinuECggEAfZeiF8BEm3RpTz3QL3HxdHFkTqOhctnhSNuq+n2C8nBCLwhN21ic
+DkkB84txTFnmboBdWYsEYzQKDL5yflIUGeup00L3VKH3Jm2OuM0f7qLm8TCE04kW
+rKhKAO2JYmNVB7QZjsgzp6QXre1ZdLfNy7mD8Dg584vPtGecvCUMULR1YsBvTV3T
+n2vPyaan+dLmoTzN6/XzrwxLVLWFt0HYYoctEkk/RSn17PwXDm5jfbya7YoSg1Vb
+tFV+Oflul8FHMV35I0hcHYhbR/8LZz0nRBH8EsyIGUdZVB76BKDdfqEJgm2ntHEP
+dvytPAo4s2m9tFkvkZOYgOCTq5GdVDK2OQKCAQAsz+y9rDcqFciCESu4IHzmtckT
+0kwP2W5ds5hzUjbY0Y2AKTx2oHNOFak6WW5vxN0+OIn37SNK3RBStPWJiigut4R4
+rGrZM4pijm53s3cWzd0h8XyLGisl2zORu8gD2IQLkQf79F3lEZHGA+J0mkSHB85N
+IuqReFzL6cfOToNd+8WYjMgJcXmVuKiCV1FRK3jrqNpXO2cLtnhFvQMxRUAYU4j+
+2MIdBFVeMq5ftMMOBS21hM9cNLlOzoSN2HmK+ZkmrlTCK0G9lmF427/lqXTmWLed
+sspOUbOLfBOwxdCCc9JUxz5KggKWcDcTqX25M0mv09rpuCxIEg8lez1Ax0if
+-----END RSA PRIVATE KEY-----
diff --git a/adb/apex/com.android.adbd.pk8 b/adb/apex/com.android.adbd.pk8
new file mode 100644
index 0000000..cdddc3f
--- /dev/null
+++ b/adb/apex/com.android.adbd.pk8
Binary files differ
diff --git a/adb/apex/com.android.adbd.x509.pem b/adb/apex/com.android.adbd.x509.pem
new file mode 100644
index 0000000..bb85c1d
--- /dev/null
+++ b/adb/apex/com.android.adbd.x509.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGHzCCBAegAwIBAgIUW8npFHXBP+wsEAesGMBxaV7TScAwDQYJKoZIhvcNAQEL
+BQAwgZ0xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMRkwFwYDVQQDDBBjb20uYW5kcm9pZC5hZGJkMSIwIAYJKoZIhvcNAQkBFhNh
+bmRyb2lkQGFuZHJvaWQuY29tMCAXDTE5MDgxNTE5MzkxM1oYDzQ3NTcwNzExMTkz
+OTEzWjCBnTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNV
+BAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNVBAsMB0Fu
+ZHJvaWQxGTAXBgNVBAMMEGNvbS5hbmRyb2lkLmFkYmQxIjAgBgkqhkiG9w0BCQEW
+E2FuZHJvaWRAYW5kcm9pZC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
+AoICAQC6zbUeWi5vNA6vCC4FBrJQ9re4UexP6TabsDYvWpFBoCluvMkT2ZRmpXMF
+W7EzQ5VmuUvZgLYVHuJmnvHIV3uaRc2VE1SV+spjWTRt+6DtsAN7irR5K66POWMp
++tr5hASdQBVOJdebimsepy0pH6sXREvanrrFzkSM/2Ho0unlwWJ5Y4jcnvdkVHI5
+Ks0vifLmX4y5mYgv1dcXYWzyYx39f8HyePv0cjRhYXiIEYZ49KWU4MjryvQe/mAu
+MQuMp901BLps2W1+oKyPPA4DV69KUXgF66RFfsjjkJJ/CSeQGzTuez+UWzFk3Duc
+6MmbiL1LTki3vyyVtjW1rYFO2s+M6Pa5NZWHgA55uUxiJ987WPyK9lWnMsY6YeKa
+FDBfS1JUzXGPzVncgM7LLvzAEibLdhjII88NsJvzPoHK0SluSn+E7t7iGO1fTjkD
+Js94iUJAp8OQ4GwkcTVgtEAR+NXzownNjHJ6qpiq6tXRqXdBqSat/glf01AgNDtz
+9AGeW7Mz6FqTdOzg3U4lu77+CGd3SZTuQk8C8PUDNhqhQX5H2qhr90bakGaXuYfE
+rWFzIjrVdJIznV1BimOCay5HyyHab4FWlVhAvslEQb2BpHRyi2lhe0laupOpmN44
+LzfjFM18bi2GashIi2OQuYDyAeT5mGtR2g8mC7g44H6dH+wTfQIDAQABo1MwUTAd
+BgNVHQ4EFgQU7lyyxPO5SOOh9a5O0l4+RjckcgcwHwYDVR0jBBgwFoAU7lyyxPO5
+SOOh9a5O0l4+RjckcgcwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
+AgEAStsOy8bkbZg/Ygx47bPkLSz0cJIvATxTChUGOabkz+brLis88ABVWVP0IXps
+tlLlZR5cjXBJguE7GJXzKPWzQZuB8+YwcGHG6QDFpfdMeGrxPDhwNfGy236ArVnx
+K0v1IIxoZRZ0P7aubk3xwUAPgsmT5ayZCKu+dqlEy5B6ioKEsr7Y2RRT/8ifERNm
+cjS9AhcyWrp4R3cjy2iA/RpdsPFwE5ac3I+GtUB4D2up5aDMsy85i9t2/1kuTUaA
+9UHwGXCpcqP8f8BqeLzuxDzYkAvkntlNxbXn1cbn+dTRIOCBoDbtSeqtxhWooOUH
+RQROeRsB7iicdYJJRge0+WyR+216AKUSQPE6/rT0Ifr06ZRwi22/YyySpwuO3SNA
++yWffh+f4h31Dz+p6pu8wjbMDkq4LnCWyjLwfF/yhvWhwwm5+KPAEhvJABeHQc+3
+cslOC9dlXJm9sPoUC7ghmUiFsCmN2hIzQrr2QoK0Obh0AGexOvOAw9cqtOdZQncB
+bqC8c4sVYScVxwDWkg0lNfRMC5boPjBsl7+M2CC1ukgVpXTyDOEjMWILrBXfYCDX
+unBH3kbKQOfL5RT0nE1Lkt1rn5qAWMJg4mvS4QuIurbRtEoj3QYQadF9md4qJXs0
+TvqvY8iEC4xrWU2SQn1K3PutXgaLP9/b6Cy1SBrhBX+AC5s=
+-----END CERTIFICATE-----
diff --git a/adb/apex/ld.config.txt b/adb/apex/ld.config.txt
new file mode 100644
index 0000000..85f9b29
--- /dev/null
+++ b/adb/apex/ld.config.txt
@@ -0,0 +1,51 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Bionic loader config file for the adbd APEX.
+
+dir.adbd = /apex/com.android.adbd/bin/
+
+[adbd]
+additional.namespaces = platform,art,adbd
+
+namespace.default.isolated = true
+namespace.default.links = art,adbd,platform
+namespace.default.link.art.shared_libs = libadbconnection_server.so
+namespace.default.link.platform.allow_all_shared_libs = true
+namespace.default.link.adbd.allow_all_shared_libs = true
+
+###############################################################################
+# "adbd" APEX namespace
+###############################################################################
+namespace.adbd.isolated = true
+namespace.adbd.search.paths = /apex/com.android.adbd/${LIB}
+namespace.adbd.asan.search.paths = /apex/com.android.adbd/${LIB}
+namespace.adbd.links = platform
+namespace.adbd.link.platform.allow_all_shared_libs = true
+
+###############################################################################
+# "art" APEX namespace: used for libadbdconnection_server
+###############################################################################
+namespace.art.isolated = true
+namespace.art.search.paths = /apex/com.android.art/${LIB}
+namespace.art.asan.search.paths = /apex/com.android.art/${LIB}
+namespace.art.links = platform
+namespace.art.link.platform.allow_all_shared_libs = true
+
+###############################################################################
+# "platform" namespace: used for NDK libraries, and libadbd_auth
+###############################################################################
+namespace.platform.isolated = true
+namespace.platform.search.paths = /system/${LIB}
+namespace.platform.asan.search.paths = /data/asan/system/${LIB}
+
+# /system/lib/libc.so, etc are symlinks to
+# /apex/com.android.runtime/lib/bionic/libc.so, etc. Add the path to the
+# permitted paths because linker uses realpath(3) to check the accessibility
+# of the lib. We could add this to search.paths instead but that makes the
+# resolution of bionic libs be dependent on the order of /system/lib and
+# /apex/.../lib/bionic in search.paths. If the latter is after the former,
+# then the latter is never tried because libc.so is always found in
+# /system/lib but fails to pass the accessibility test because of its realpath.
+# It's better to not depend on the ordering if possible.
+namespace.platform.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
+namespace.platform.asan.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index bdc9e56..5fa0edb 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -101,17 +101,12 @@
 
 static void push_to_device(const void* data, size_t byte_count, const char* dst, bool sync) {
     std::vector<const char*> srcs;
-    {
-        TemporaryFile temp;
-        android::base::WriteFully(temp.fd, data, byte_count);
-        srcs.push_back(temp.path);
-
-        // On Windows, the file needs to be flushed before pushing to device.
-        // closing the file flushes its content, but we still need to remove it after push.
-        // FileDeleter does exactly that.
-        temp.DoNotRemove();
-    }
-    FileDeleter temp_deleter(srcs.back());
+    TemporaryFile tf;
+    android::base::WriteFully(tf.fd, data, byte_count);
+    srcs.push_back(tf.path);
+    // On Windows, the file needs to be flushed before pushing to device,
+    // but can't be removed until after the push.
+    unix_close(tf.release());
 
     if (!do_sync_push(srcs, dst, sync)) {
         error_exit("Failed to push fastdeploy agent to device.");
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 24e722e..343e7b5 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -609,6 +609,7 @@
     while (true) {
         // TODO: Use inotify.
         find_usb_device("/dev/bus/usb", register_device);
+        adb_notify_device_scan_complete();
         kick_disconnected_devices();
         std::this_thread::sleep_for(1s);
     }
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index 5c0da47..7207ca7 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -461,6 +461,7 @@
             std::this_thread::sleep_for(100ms);
         }
 
+        adb_notify_device_scan_complete();
         initialized = true;
     }
 }
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index f23c3a5..197c6fa 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -172,6 +172,7 @@
 
     while (true) {
         find_devices();
+        adb_notify_device_scan_complete();
         std::this_thread::sleep_for(1s);
     }
 }
diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp
index 7a3a4f5..2e84ce6 100644
--- a/adb/daemon/auth.cpp
+++ b/adb/daemon/auth.cpp
@@ -31,6 +31,7 @@
 #include <algorithm>
 #include <memory>
 
+#include <adbd_auth.h>
 #include <android-base/file.h>
 #include <android-base/strings.h>
 #include <crypto_utils/android_pubkey.h>
@@ -38,85 +39,57 @@
 #include <openssl/rsa.h>
 #include <openssl/sha.h>
 
-static fdevent* listener_fde = nullptr;
-static fdevent* framework_fde = nullptr;
-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 AdbdAuthContext* auth_ctx;
 
 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,
-                      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;
-                continue;
-            }
-
-            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';
-
-                // b64_pton requires one additional byte in the target buffer for
-                // decoding to succeed. See http://b/28035006 for details.
-                uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
-                if (b64_pton(line.c_str(), keybuf, sizeof(keybuf)) != ANDROID_PUBKEY_ENCODED_SIZE) {
-                    LOG(ERROR) << "Invalid base64 key " << line.c_str() << " in " << path;
-                    continue;
-                }
-
-                RSA* key = nullptr;
-                if (!android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key)) {
-                    LOG(ERROR) << "Failed to parse key " << line.c_str() << " in " << path;
-                    continue;
-                }
-
-                bool verified =
-                    (RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), token_size,
-                                reinterpret_cast<const uint8_t*>(sig.c_str()), sig.size(),
-                                key) == 1);
-                RSA_free(key);
-                if (verified) return true;
-            }
-        }
-    }
-    auth_key->clear();
-    return false;
+static void IteratePublicKeys(std::function<bool(std::string_view public_key)> f) {
+    adbd_auth_get_public_keys(
+            auth_ctx,
+            [](const char* public_key, size_t len, void* arg) {
+                return (*static_cast<decltype(f)*>(arg))(std::string_view(public_key, len));
+            },
+            &f);
 }
 
-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;
-    }
+bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig,
+                      std::string* auth_key) {
+    bool authorized = false;
+    auth_key->clear();
 
-    LOG(DEBUG) << "Sending '" << msg << "'";
-    if (!WriteFdExactly(framework_fd, msg.c_str(), msg_len)) {
-        PLOG(ERROR) << "Failed to write " << msg_type;
-        return false;
-    }
-    return true;
+    IteratePublicKeys([&](std::string_view public_key) {
+        // TODO: do we really have to support both ' ' and '\t'?
+        std::vector<std::string> split = android::base::Split(std::string(public_key), " \t");
+        uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
+        const std::string& pubkey = split[0];
+        if (b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)) != ANDROID_PUBKEY_ENCODED_SIZE) {
+            LOG(ERROR) << "Invalid base64 key " << pubkey;
+            return true;
+        }
+
+        RSA* key = nullptr;
+        if (!android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key)) {
+            LOG(ERROR) << "Failed to parse key " << pubkey;
+            return true;
+        }
+
+        bool verified =
+                (RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), token_size,
+                            reinterpret_cast<const uint8_t*>(sig.c_str()), sig.size(), key) == 1);
+        RSA_free(key);
+        if (verified) {
+            *auth_key = public_key;
+            authorized = true;
+            return false;
+        }
+
+        return true;
+    });
+
+    return authorized;
 }
 
 static bool adbd_auth_generate_token(void* token, size_t token_size) {
@@ -127,113 +100,6 @@
     return okay;
 }
 
-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);
-        {
-            std::lock_guard<std::mutex> lock(framework_mutex);
-            framework_fd = -1;
-        }
-    }
-}
-
-static void adbd_auth_event(int fd, unsigned events, void*) {
-    if (events & FDE_READ) {
-        char response[2];
-        int ret = unix_read(fd, response, sizeof(response));
-        if (ret <= 0) {
-            framework_disconnected();
-        } else if (ret == 2 && response[0] == 'O' && response[1] == 'K') {
-            if (adb_transport) {
-                adbd_auth_verified(adb_transport);
-            }
-        }
-    }
-}
-
-void adbd_auth_confirm_key(atransport* t) {
-    if (!adb_transport) {
-        adb_transport = t;
-        t->AddDisconnect(&adb_disconnect);
-    }
-
-    {
-        std::lock_guard<std::mutex> lock(framework_mutex);
-        if (framework_fd < 0) {
-            LOG(ERROR) << "Client not connected";
-            needs_retry = true;
-            return;
-        }
-
-        adbd_send_key_message_locked("PK", t->auth_key);
-    }
-}
-
-static void adbd_auth_listener(int fd, unsigned events, void* data) {
-    int s = adb_socket_accept(fd, nullptr, nullptr);
-    if (s < 0) {
-        PLOG(ERROR) << "Failed to accept";
-        return;
-    }
-
-    {
-        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);
-            }
-        }
-    }
-}
-
-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);
-        }
-    }
-}
-
 void adbd_cloexec_auth_socket() {
     int fd = android_get_control_socket("adbd");
     if (fd == -1) {
@@ -243,20 +109,23 @@
     fcntl(fd, F_SETFD, FD_CLOEXEC);
 }
 
+static void adbd_auth_key_authorized(void* arg, uint64_t id) {
+    LOG(INFO) << "adb client authorized";
+    auto* transport = static_cast<atransport*>(arg);
+    transport->auth_id = id;
+    adbd_auth_verified(transport);
+}
+
 void adbd_auth_init(void) {
-    int fd = android_get_control_socket("adbd");
-    if (fd == -1) {
-        PLOG(ERROR) << "Failed to get adbd socket";
-        return;
-    }
-
-    if (listen(fd, 4) == -1) {
-        PLOG(ERROR) << "Failed to listen on '" << fd << "'";
-        return;
-    }
-
-    listener_fde = fdevent_create(fd, adbd_auth_listener, nullptr);
-    fdevent_add(listener_fde, FDE_READ);
+    AdbdAuthCallbacks cb;
+    cb.version = 1;
+    cb.callbacks.v1.key_authorized = adbd_auth_key_authorized;
+    auth_ctx = adbd_auth_new(&cb);
+    std::thread([]() {
+        adb_thread_setname("adbd auth");
+        adbd_auth_run(auth_ctx);
+        LOG(FATAL) << "auth thread terminated";
+    }).detach();
 }
 
 void send_auth_request(atransport* t) {
@@ -280,3 +149,18 @@
     handle_online(t);
     send_connect(t);
 }
+
+static void adb_disconnected(void* unused, atransport* t) {
+    LOG(INFO) << "ADB disconnect";
+    adbd_auth_notify_disconnect(auth_ctx, t->auth_id);
+}
+
+void adbd_auth_confirm_key(atransport* t) {
+    LOG(INFO) << "prompting user to authorize key";
+    t->AddDisconnect(&adb_disconnect);
+    adbd_auth_prompt_user(auth_ctx, t->auth_key.data(), t->auth_key.size(), t);
+}
+
+void adbd_notify_framework_connected_key(atransport* t) {
+    adbd_auth_notify_auth(auth_ctx, t->auth_key.data(), t->auth_key.size());
+}
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 9ebab74..7277cc8 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -205,7 +205,11 @@
     // descriptor will always be open.
     adbd_cloexec_auth_socket();
 
-#if defined(ALLOW_ADBD_NO_AUTH)
+#if defined(__ANDROID_RECOVERY__)
+    if (is_device_unlocked() || __android_log_is_debuggable()) {
+        auth_required = false;
+    }
+#elif defined(ALLOW_ADBD_NO_AUTH)
     // If ro.adb.secure is unset, default to no authentication required.
     auth_required = android::base::GetBoolProperty("ro.adb.secure", false);
 #elif defined(__ANDROID__)
@@ -214,8 +218,6 @@
     }
 #endif
 
-    adbd_auth_init();
-
     // Our external storage path may be different than apps, since
     // we aren't able to bind mount after dropping root.
     const char* adb_external_storage = getenv("ADB_EXTERNAL_STORAGE");
@@ -230,6 +232,9 @@
     drop_privileges(server_port);
 #endif
 
+    // adbd_auth_init will spawn a thread, so we need to defer it until after selinux transitions.
+    adbd_auth_init();
+
     bool is_usb = false;
 
 #if defined(__ANDROID__)
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index a44c10b..4ec90d2 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -39,6 +39,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <cutils/android_reboot.h>
 #include <cutils/sockets.h>
 #include <log/log_properties.h>
 
@@ -140,6 +141,26 @@
     WriteFdExactly(fd.get(), "spinning\n");
 }
 
+[[maybe_unused]] static unique_fd reboot_device(const std::string& name) {
+#if defined(__ANDROID_RECOVERY__)
+    if (!__android_log_is_debuggable()) {
+        auto reboot_service = [name](unique_fd fd) {
+            std::string reboot_string = android::base::StringPrintf("reboot,%s", name.c_str());
+            if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
+                WriteFdFmt(fd.get(), "reboot (%s) failed\n", reboot_string.c_str());
+                return;
+            }
+            while (true) pause();
+        };
+        return create_service_thread("reboot", reboot_service);
+    }
+#endif
+    // Fall through
+    std::string cmd = "/system/bin/reboot ";
+    cmd += name;
+    return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+}
+
 struct ServiceSocket : public asocket {
     ServiceSocket() {
         install_local_socket(this);
@@ -252,9 +273,7 @@
         cmd += name;
         return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
     } else if (android::base::ConsumePrefix(&name, "reboot:")) {
-        std::string cmd = "/system/bin/reboot ";
-        cmd += name;
-        return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+        return reboot_device(std::string(name));
     } else if (name.starts_with("root:")) {
         return create_service_thread("root", restart_root_service);
     } else if (name.starts_with("unroot:")) {
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 1abae87..a9ad805 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -117,14 +117,18 @@
     }
 };
 
+template <class Payload>
 struct IoBlock {
     bool pending = false;
     struct iocb control = {};
-    std::shared_ptr<Block> payload;
+    Payload payload;
 
     TransferId id() const { return TransferId::from_value(control.aio_data); }
 };
 
+using IoReadBlock = IoBlock<Block>;
+using IoWriteBlock = IoBlock<std::shared_ptr<Block>>;
+
 struct ScopedAioContext {
     ScopedAioContext() = default;
     ~ScopedAioContext() { reset(); }
@@ -208,16 +212,17 @@
 
     virtual bool Write(std::unique_ptr<apacket> packet) override final {
         LOG(DEBUG) << "USB write: " << dump_header(&packet->msg);
-        Block header(sizeof(packet->msg));
-        memcpy(header.data(), &packet->msg, sizeof(packet->msg));
+        auto header = std::make_shared<Block>(sizeof(packet->msg));
+        memcpy(header->data(), &packet->msg, sizeof(packet->msg));
 
         std::lock_guard<std::mutex> lock(write_mutex_);
-        write_requests_.push_back(CreateWriteBlock(std::move(header), next_write_id_++));
+        write_requests_.push_back(
+                CreateWriteBlock(std::move(header), 0, sizeof(packet->msg), next_write_id_++));
         if (!packet->payload.empty()) {
             // The kernel attempts to allocate a contiguous block of memory for each write,
             // which can fail if the write is large and the kernel heap is fragmented.
             // Split large writes into smaller chunks to avoid this.
-            std::shared_ptr<Block> payload = std::make_shared<Block>(std::move(packet->payload));
+            auto payload = std::make_shared<Block>(std::move(packet->payload));
             size_t offset = 0;
             size_t len = payload->size();
 
@@ -464,16 +469,20 @@
         worker_thread_.join();
     }
 
-    void PrepareReadBlock(IoBlock* block, uint64_t id) {
+    void PrepareReadBlock(IoReadBlock* block, uint64_t id) {
         block->pending = false;
-        block->payload = std::make_shared<Block>(kUsbReadSize);
+        if (block->payload.capacity() >= kUsbReadSize) {
+            block->payload.resize(kUsbReadSize);
+        } else {
+            block->payload = Block(kUsbReadSize);
+        }
         block->control.aio_data = static_cast<uint64_t>(TransferId::read(id));
-        block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload->data());
-        block->control.aio_nbytes = block->payload->size();
+        block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
+        block->control.aio_nbytes = block->payload.size();
     }
 
-    IoBlock CreateReadBlock(uint64_t id) {
-        IoBlock block;
+    IoReadBlock CreateReadBlock(uint64_t id) {
+        IoReadBlock block;
         PrepareReadBlock(&block, id);
         block.control.aio_rw_flags = 0;
         block.control.aio_lio_opcode = IOCB_CMD_PREAD;
@@ -518,9 +527,9 @@
 
     void HandleRead(TransferId id, int64_t size) {
         uint64_t read_idx = id.id % kUsbReadQueueDepth;
-        IoBlock* block = &read_requests_[read_idx];
+        IoReadBlock* block = &read_requests_[read_idx];
         block->pending = false;
-        block->payload->resize(size);
+        block->payload.resize(size);
 
         // Notification for completed reads can be received out of order.
         if (block->id().id != needed_read_id_) {
@@ -531,7 +540,7 @@
 
         for (uint64_t id = needed_read_id_;; ++id) {
             size_t read_idx = id % kUsbReadQueueDepth;
-            IoBlock* current_block = &read_requests_[read_idx];
+            IoReadBlock* current_block = &read_requests_[read_idx];
             if (current_block->pending) {
                 break;
             }
@@ -540,19 +549,19 @@
         }
     }
 
-    void ProcessRead(IoBlock* block) {
-        if (!block->payload->empty()) {
+    void ProcessRead(IoReadBlock* block) {
+        if (!block->payload.empty()) {
             if (!incoming_header_.has_value()) {
-                CHECK_EQ(sizeof(amessage), block->payload->size());
-                amessage msg;
-                memcpy(&msg, block->payload->data(), sizeof(amessage));
+                CHECK_EQ(sizeof(amessage), block->payload.size());
+                amessage& msg = incoming_header_.emplace();
+                memcpy(&msg, block->payload.data(), sizeof(msg));
                 LOG(DEBUG) << "USB read:" << dump_header(&msg);
                 incoming_header_ = msg;
             } else {
                 size_t bytes_left = incoming_header_->data_length - incoming_payload_.size();
-                Block payload = std::move(*block->payload);
+                Block payload = std::move(block->payload);
                 CHECK_LE(payload.size(), bytes_left);
-                incoming_payload_.append(std::make_unique<Block>(std::move(payload)));
+                incoming_payload_.append(std::move(payload));
             }
 
             if (incoming_header_->data_length == incoming_payload_.size()) {
@@ -560,11 +569,15 @@
                 packet->msg = *incoming_header_;
 
                 // TODO: Make apacket contain an IOVector so we don't have to coalesce.
-                packet->payload = incoming_payload_.coalesce();
+                packet->payload = std::move(incoming_payload_).coalesce();
                 read_callback_(this, std::move(packet));
 
                 incoming_header_.reset();
-                incoming_payload_.clear();
+                // reuse the capacity of the incoming payload while we can.
+                auto free_block = incoming_payload_.clear();
+                if (block->payload.capacity() == 0) {
+                    block->payload = std::move(free_block);
+                }
             }
         }
 
@@ -572,7 +585,7 @@
         SubmitRead(block);
     }
 
-    bool SubmitRead(IoBlock* block) {
+    bool SubmitRead(IoReadBlock* block) {
         block->pending = true;
         struct iocb* iocb = &block->control;
         if (io_submit(aio_context_.get(), 1, &iocb) != 1) {
@@ -594,7 +607,7 @@
         std::lock_guard<std::mutex> lock(write_mutex_);
         auto it =
                 std::find_if(write_requests_.begin(), write_requests_.end(), [id](const auto& req) {
-                    return static_cast<uint64_t>(req->id()) == static_cast<uint64_t>(id);
+                    return static_cast<uint64_t>(req.id()) == static_cast<uint64_t>(id);
                 });
         CHECK(it != write_requests_.end());
 
@@ -605,27 +618,26 @@
         SubmitWrites();
     }
 
-    std::unique_ptr<IoBlock> CreateWriteBlock(std::shared_ptr<Block> payload, size_t offset,
-                                              size_t len, uint64_t id) {
-        auto block = std::make_unique<IoBlock>();
-        block->payload = std::move(payload);
-        block->control.aio_data = static_cast<uint64_t>(TransferId::write(id));
-        block->control.aio_rw_flags = 0;
-        block->control.aio_lio_opcode = IOCB_CMD_PWRITE;
-        block->control.aio_reqprio = 0;
-        block->control.aio_fildes = write_fd_.get();
-        block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload->data() + offset);
-        block->control.aio_nbytes = len;
-        block->control.aio_offset = 0;
-        block->control.aio_flags = IOCB_FLAG_RESFD;
-        block->control.aio_resfd = worker_event_fd_.get();
+    IoWriteBlock CreateWriteBlock(std::shared_ptr<Block> payload, size_t offset, size_t len,
+                                  uint64_t id) {
+        auto block = IoWriteBlock();
+        block.payload = std::move(payload);
+        block.control.aio_data = static_cast<uint64_t>(TransferId::write(id));
+        block.control.aio_rw_flags = 0;
+        block.control.aio_lio_opcode = IOCB_CMD_PWRITE;
+        block.control.aio_reqprio = 0;
+        block.control.aio_fildes = write_fd_.get();
+        block.control.aio_buf = reinterpret_cast<uintptr_t>(block.payload->data() + offset);
+        block.control.aio_nbytes = len;
+        block.control.aio_offset = 0;
+        block.control.aio_flags = IOCB_FLAG_RESFD;
+        block.control.aio_resfd = worker_event_fd_.get();
         return block;
     }
 
-    std::unique_ptr<IoBlock> CreateWriteBlock(Block payload, uint64_t id) {
-        std::shared_ptr<Block> block = std::make_shared<Block>(std::move(payload));
-        size_t len = block->size();
-        return CreateWriteBlock(std::move(block), 0, len, id);
+    IoWriteBlock CreateWriteBlock(Block&& payload, uint64_t id) {
+        size_t len = payload.size();
+        return CreateWriteBlock(std::make_shared<Block>(std::move(payload)), 0, len, id);
     }
 
     void SubmitWrites() REQUIRES(write_mutex_) {
@@ -642,9 +654,9 @@
 
         struct iocb* iocbs[kUsbWriteQueueDepth];
         for (int i = 0; i < writes_to_submit; ++i) {
-            CHECK(!write_requests_[writes_submitted_ + i]->pending);
-            write_requests_[writes_submitted_ + i]->pending = true;
-            iocbs[i] = &write_requests_[writes_submitted_ + i]->control;
+            CHECK(!write_requests_[writes_submitted_ + i].pending);
+            write_requests_[writes_submitted_ + i].pending = true;
+            iocbs[i] = &write_requests_[writes_submitted_ + i].control;
             LOG(VERBOSE) << "submitting write_request " << static_cast<void*>(iocbs[i]);
         }
 
@@ -689,7 +701,7 @@
     std::optional<amessage> incoming_header_;
     IOVector incoming_payload_;
 
-    std::array<IoBlock, kUsbReadQueueDepth> read_requests_;
+    std::array<IoReadBlock, kUsbReadQueueDepth> read_requests_;
     IOVector read_data_;
 
     // ID of the next request that we're going to send out.
@@ -699,7 +711,7 @@
     size_t needed_read_id_ = 0;
 
     std::mutex write_mutex_;
-    std::deque<std::unique_ptr<IoBlock>> write_requests_ GUARDED_BY(write_mutex_);
+    std::deque<IoWriteBlock> write_requests_ GUARDED_BY(write_mutex_);
     size_t next_write_id_ GUARDED_BY(write_mutex_) = 0;
     size_t writes_submitted_ GUARDED_BY(write_mutex_) = 0;
 
diff --git a/adb/fastdeploy/deployagent/test/com/android/fastdeploy/FastDeployTest.java b/adb/fastdeploy/deployagent/test/com/android/fastdeploy/FastDeployTest.java
index ef6ccae..4aa2f79 100644
--- a/adb/fastdeploy/deployagent/test/com/android/fastdeploy/FastDeployTest.java
+++ b/adb/fastdeploy/deployagent/test/com/android/fastdeploy/FastDeployTest.java
@@ -20,7 +20,9 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.ddmlib.Log.LogLevel;
 import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
@@ -68,17 +70,19 @@
     }
 
     private boolean isAppInstalled(String packageName) throws DeviceNotAvailableException {
-        final String commandResult = getDevice().executeShellCommand("pm list packages");
+        final String result = getDevice().executeShellCommand("pm list packages");
+        CLog.logAndDisplay(LogLevel.INFO, result);
         final int prefixLength = "package:".length();
-        return Arrays.stream(commandResult.split("\\r?\\n"))
+        return Arrays.stream(result.split("\\r?\\n"))
                 .anyMatch(line -> line.substring(prefixLength).equals(packageName));
     }
 
     // Mostly copied from PkgInstallSignatureVerificationTest.java.
-    private String fastInstallPackage(String apkPath)
+    private void fastInstallPackage(String apkPath)
             throws IOException, DeviceNotAvailableException {
-        return getDevice().executeAdbCommand("install", "-t", "--fastdeploy", "--force-agent",
+        String result = getDevice().executeAdbCommand("install", "-t", "--fastdeploy", "--force-agent",
                 apkPath);
+        CLog.logAndDisplay(LogLevel.INFO, result);
     }
 }
 
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index 98468b5..27e8c46 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -272,7 +272,9 @@
         if (hostname.empty() && gListenAll) {
             result = network_inaddr_any_server(port, SOCK_STREAM, error);
         } else if (tcp_host_is_local(hostname)) {
-            result = network_loopback_server(port, SOCK_STREAM, error);
+            result = network_loopback_server(port, SOCK_STREAM, error, true);
+        } else if (hostname == "::1") {
+            result = network_loopback_server(port, SOCK_STREAM, error, false);
         } else {
             // TODO: Implement me.
             *error = "listening on specified hostname currently unsupported";
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index e78530c..7d5bf17 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -125,8 +125,7 @@
         if (rc > 0 && static_cast<size_t>(rc) == s->packet_queue.size()) {
             s->packet_queue.clear();
         } else if (rc > 0) {
-            // TODO: Implement a faster drop_front?
-            s->packet_queue.take_front(rc);
+            s->packet_queue.drop_front(rc);
             fdevent_add(s->fde, FDE_WRITE);
             return SocketFlushResult::TryAgain;
         } else if (rc == -1 && errno == EAGAIN) {
diff --git a/adb/sysdeps/network.h b/adb/sysdeps/network.h
index 83ce371..fadd155 100644
--- a/adb/sysdeps/network.h
+++ b/adb/sysdeps/network.h
@@ -19,4 +19,4 @@
 #include <string>
 
 int network_loopback_client(int port, int type, std::string* error);
-int network_loopback_server(int port, int type, std::string* error);
+int network_loopback_server(int port, int type, std::string* error, bool prefer_ipv4);
diff --git a/adb/sysdeps/posix/network.cpp b/adb/sysdeps/posix/network.cpp
index c5c2275..a4d9013 100644
--- a/adb/sysdeps/posix/network.cpp
+++ b/adb/sysdeps/posix/network.cpp
@@ -119,12 +119,15 @@
     return s.release();
 }
 
-int network_loopback_server(int port, int type, std::string* error) {
-    int rc = _network_loopback_server(false, port, type, error);
+int network_loopback_server(int port, int type, std::string* error, bool prefer_ipv4) {
+    int rc = -1;
+    if (prefer_ipv4) {
+        rc = _network_loopback_server(false, port, type, error);
+    }
 
-    // Only attempt to listen on IPv6 if IPv4 is unavailable.
+    // Only attempt to listen on IPv6 if IPv4 is unavailable or prefer_ipv4 is false
     // We don't want to start an IPv6 server if there's already an IPv4 one running.
-    if (rc == -1 && (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT)) {
+    if (rc == -1 && (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT || !prefer_ipv4)) {
         return _network_loopback_server(true, port, type, error);
     }
     return rc;
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 4d6cf3d..d9cc36f 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -920,7 +920,8 @@
     return fd;
 }
 
-int network_loopback_server(int port, int type, std::string* error) {
+int network_loopback_server(int port, int type, std::string* error, bool prefer_ipv4) {
+    // TODO implement IPv6 support on windows
     return _network_server(port, type, INADDR_LOOPBACK, error);
 }
 
@@ -1132,7 +1133,7 @@
     int local_port = -1;
     std::string error;
 
-    server = network_loopback_server(0, SOCK_STREAM, &error);
+    server = network_loopback_server(0, SOCK_STREAM, &error, true);
     if (server < 0) {
         D("adb_socketpair: failed to create server: %s", error.c_str());
         goto fail;
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 8272722..3d6de26 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -281,6 +281,37 @@
             subprocess.check_output(["adb", "-P", str(port), "kill-server"],
                                     stderr=subprocess.STDOUT)
 
+    @unittest.skipUnless(
+        os.name == "posix",
+        "adb doesn't yet support IPv6 on Windows",
+    )
+    def test_starts_on_ipv6_localhost(self):
+        """
+        Tests that the server can start up on ::1 and that it's accessible
+        """
+        server_port = 5037
+        # Kill any existing server on this non-default port.
+        subprocess.check_output(
+            ["adb", "-P", str(server_port), "kill-server"],
+            stderr=subprocess.STDOUT,
+        )
+        try:
+            subprocess.check_output(
+                ["adb", "-L", "tcp:[::1]:{}".format(server_port), "server"],
+                stderr=subprocess.STDOUT,
+            )
+            with fake_adbd() as (port, _):
+                with adb_connect(self, serial="localhost:{}".format(port)):
+                    pass
+        finally:
+            # If we started a server, kill it.
+            subprocess.check_output(
+                ["adb", "-P", str(server_port), "kill-server"],
+                stderr=subprocess.STDOUT,
+            )
+
+
+
 
 class EmulatorTest(unittest.TestCase):
     """Tests for the emulator connection."""
diff --git a/adb/transport.h b/adb/transport.h
index c38cb1d..89d76b8 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -277,8 +277,11 @@
     std::string device;
     std::string devpath;
 
+#if !ADB_HOST
     // Used to provide the key to the framework.
     std::string auth_key;
+    uint64_t auth_id;
+#endif
 
     bool IsTcpDevice() const { return type == kTransportLocal; }
 
diff --git a/adb/transport_fd.cpp b/adb/transport_fd.cpp
index a93e68a..8d2ad66 100644
--- a/adb/transport_fd.cpp
+++ b/adb/transport_fd.cpp
@@ -93,8 +93,8 @@
 
                 if (pfds[0].revents & POLLIN) {
                     // TODO: Should we be getting blocks from a free list?
-                    auto block = std::make_unique<IOVector::block_type>(MAX_PAYLOAD);
-                    rc = adb_read(fd_.get(), &(*block)[0], block->size());
+                    auto block = IOVector::block_type(MAX_PAYLOAD);
+                    rc = adb_read(fd_.get(), &block[0], block.size());
                     if (rc == -1) {
                         *error = std::string("read failed: ") + strerror(errno);
                         return;
@@ -102,7 +102,7 @@
                         *error = "read failed: EOF";
                         return;
                     }
-                    block->resize(rc);
+                    block.resize(rc);
                     read_buffer_.append(std::move(block));
 
                     if (!read_header_ && read_buffer_.size() >= sizeof(amessage)) {
@@ -116,7 +116,7 @@
                         auto data_chain = read_buffer_.take_front(read_header_->data_length);
 
                         // TODO: Make apacket carry around a IOVector instead of coalescing.
-                        auto payload = data_chain.coalesce<apacket::payload_type>();
+                        auto payload = std::move(data_chain).coalesce();
                         auto packet = std::make_unique<apacket>();
                         packet->msg = *read_header_;
                         packet->payload = std::move(payload);
@@ -184,8 +184,7 @@
             return WriteResult::Error;
         }
 
-        // TODO: Implement a more efficient drop_front?
-        write_buffer_.take_front(rc);
+        write_buffer_.drop_front(rc);
         writable_ = write_buffer_.empty();
         if (write_buffer_.empty()) {
             return WriteResult::Completed;
@@ -199,10 +198,10 @@
         std::lock_guard<std::mutex> lock(write_mutex_);
         const char* header_begin = reinterpret_cast<const char*>(&packet->msg);
         const char* header_end = header_begin + sizeof(packet->msg);
-        auto header_block = std::make_unique<IOVector::block_type>(header_begin, header_end);
+        auto header_block = IOVector::block_type(header_begin, header_end);
         write_buffer_.append(std::move(header_block));
         if (!packet->payload.empty()) {
-            write_buffer_.append(std::make_unique<IOVector::block_type>(std::move(packet->payload)));
+            write_buffer_.append(std::move(packet->payload));
         }
 
         WriteResult result = DispatchWrites();
diff --git a/adb/types.cpp b/adb/types.cpp
new file mode 100644
index 0000000..26b77ab
--- /dev/null
+++ b/adb/types.cpp
@@ -0,0 +1,204 @@
+/*
+ * 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 "types.h"
+
+IOVector& IOVector::operator=(IOVector&& move) noexcept {
+    chain_ = std::move(move.chain_);
+    chain_length_ = move.chain_length_;
+    begin_offset_ = move.begin_offset_;
+    start_index_ = move.start_index_;
+
+    move.clear();
+    return *this;
+}
+
+IOVector::block_type IOVector::clear() {
+    chain_length_ = 0;
+    begin_offset_ = 0;
+    start_index_ = 0;
+    block_type res;
+    if (!chain_.empty()) {
+        res = std::move(chain_.back());
+    }
+    chain_.clear();
+    return res;
+}
+
+void IOVector::drop_front(IOVector::size_type len) {
+    if (len == 0) {
+        return;
+    }
+    if (len == size()) {
+        clear();
+        return;
+    }
+    CHECK_LT(len, size());
+
+    auto dropped = 0u;
+    while (dropped < len) {
+        const auto next = chain_[start_index_].size() - begin_offset_;
+        if (dropped + next < len) {
+            pop_front_block();
+            dropped += next;
+        } else {
+            const auto taken = len - dropped;
+            begin_offset_ += taken;
+            break;
+        }
+    }
+}
+
+IOVector IOVector::take_front(IOVector::size_type len) {
+    if (len == 0) {
+        return {};
+    }
+    if (len == size()) {
+        return std::move(*this);
+    }
+
+    CHECK_GE(size(), len);
+    IOVector res;
+    // first iterate over the blocks that completely go into the other vector
+    while (chain_[start_index_].size() - begin_offset_ <= len) {
+        chain_length_ -= chain_[start_index_].size();
+        len -= chain_[start_index_].size() - begin_offset_;
+        if (chain_[start_index_].size() > begin_offset_) {
+            res.append(std::move(chain_[start_index_]));
+            if (begin_offset_) {
+                res.begin_offset_ = std::exchange(begin_offset_, 0);
+            }
+        } else {
+            begin_offset_ = 0;
+        }
+        ++start_index_;
+    }
+
+    if (len > 0) {
+        // what's left is a single buffer that needs to be split between the |res| and |this|
+        // we know that it has to be split - there was a check for the case when it has to
+        // go away as a whole.
+        if (begin_offset_ != 0 || len < chain_[start_index_].size() / 2) {
+            // let's memcpy the data out
+            block_type block(chain_[start_index_].begin() + begin_offset_,
+                             chain_[start_index_].begin() + begin_offset_ + len);
+            res.append(std::move(block));
+            begin_offset_ += len;
+        } else {
+            CHECK_EQ(begin_offset_, 0u);
+            // move out the internal buffer out and copy only the tail of it back in
+            block_type block(chain_[start_index_].begin() + len, chain_[start_index_].end());
+            chain_length_ -= chain_[start_index_].size();
+            chain_[start_index_].resize(len);
+            res.append(std::move(chain_[start_index_]));
+            chain_length_ += block.size();
+            chain_[start_index_] = std::move(block);
+        }
+    }
+    return res;
+}
+
+void IOVector::trim_front() {
+    if ((begin_offset_ == 0 && start_index_ == 0) || chain_.empty()) {
+        return;
+    }
+    block_type& first_block = chain_[start_index_];
+    if (begin_offset_ == first_block.size()) {
+        ++start_index_;
+    } else {
+        memmove(first_block.data(), first_block.data() + begin_offset_,
+                first_block.size() - begin_offset_);
+        first_block.resize(first_block.size() - begin_offset_);
+    }
+    chain_length_ -= begin_offset_;
+    begin_offset_ = 0;
+    trim_chain_front();
+}
+
+void IOVector::trim_chain_front() {
+    if (start_index_) {
+        chain_.erase(chain_.begin(), chain_.begin() + start_index_);
+        start_index_ = 0;
+    }
+}
+
+void IOVector::pop_front_block() {
+    chain_length_ -= chain_[start_index_].size();
+    begin_offset_ = 0;
+    chain_[start_index_].clear();
+    ++start_index_;
+    if (start_index_ > std::max<size_t>(4, chain_.size() / 2)) {
+        trim_chain_front();
+    }
+}
+
+IOVector::block_type IOVector::coalesce() && {
+    // Destructive coalesce() may optimize for several cases when it doesn't need to allocate
+    // new buffer, or even return one of the existing blocks as is. The only guarantee is that
+    // after this call the IOVector is in some valid state. Nothing is guaranteed about the
+    // specifics.
+    if (size() == 0) {
+        return {};
+    }
+    if (begin_offset_ == chain_[start_index_].size() && chain_.size() == start_index_ + 2) {
+        chain_length_ -= chain_.back().size();
+        auto res = std::move(chain_.back());
+        chain_.pop_back();
+        return res;
+    }
+    if (chain_.size() == start_index_ + 1) {
+        chain_length_ -= chain_.back().size();
+        auto res = std::move(chain_.back());
+        chain_.pop_back();
+        if (begin_offset_ != 0) {
+            memmove(res.data(), res.data() + begin_offset_, res.size() - begin_offset_);
+            res.resize(res.size() - begin_offset_);
+            begin_offset_ = 0;
+        }
+        return res;
+    }
+    if (auto& firstBuffer = chain_[start_index_]; firstBuffer.capacity() >= size()) {
+        auto res = std::move(chain_[start_index_]);
+        auto size = res.size();
+        chain_length_ -= size;
+        if (begin_offset_ != 0) {
+            memmove(res.data(), res.data() + begin_offset_, res.size() - begin_offset_);
+            size -= begin_offset_;
+            begin_offset_ = 0;
+        }
+        for (auto i = start_index_ + 1; i < chain_.size(); ++i) {
+            memcpy(res.data() + size, chain_[i].data(), chain_[i].size());
+            size += chain_[i].size();
+        }
+        res.resize(size);
+        ++start_index_;
+        return res;
+    }
+    return const_cast<const IOVector*>(this)->coalesce<>();
+}
+
+std::vector<adb_iovec> IOVector::iovecs() const {
+    std::vector<adb_iovec> result;
+    result.reserve(chain_.size() - start_index_);
+    iterate_blocks([&result](const char* data, size_t len) {
+        adb_iovec iov;
+        iov.iov_base = const_cast<char*>(data);
+        iov.iov_len = len;
+        result.emplace_back(iov);
+    });
+
+    return result;
+}
diff --git a/adb/types.h b/adb/types.h
index 8bd66be..6b00224 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -19,9 +19,7 @@
 #include <string.h>
 
 #include <algorithm>
-#include <deque>
 #include <memory>
-#include <type_traits>
 #include <utility>
 #include <vector>
 
@@ -33,7 +31,7 @@
 struct Block {
     using iterator = char*;
 
-    Block() {}
+    Block() = default;
 
     explicit Block(size_t size) { allocate(size); }
 
@@ -43,24 +41,21 @@
     }
 
     Block(const Block& copy) = delete;
-    Block(Block&& move) noexcept {
-        std::swap(data_, move.data_);
-        std::swap(capacity_, move.capacity_);
-        std::swap(size_, move.size_);
-    }
+    Block(Block&& move) noexcept
+        : data_(std::exchange(move.data_, nullptr)),
+          capacity_(std::exchange(move.capacity_, 0)),
+          size_(std::exchange(move.size_, 0)) {}
 
     Block& operator=(const Block& copy) = delete;
     Block& operator=(Block&& move) noexcept {
         clear();
-
-        std::swap(data_, move.data_);
-        std::swap(capacity_, move.capacity_);
-        std::swap(size_, move.size_);
-
+        data_ = std::exchange(move.data_, nullptr);
+        capacity_ = std::exchange(move.capacity_, 0);
+        size_ = std::exchange(move.size_, 0);
         return *this;
     }
 
-    ~Block() { clear(); }
+    ~Block() = default;
 
     void resize(size_t new_size) {
         if (!data_) {
@@ -144,146 +139,63 @@
     using block_type = Block;
     using size_type = size_t;
 
-    IOVector() {}
+    IOVector() = default;
 
-    explicit IOVector(std::unique_ptr<block_type> block) {
-        append(std::move(block));
-    }
+    explicit IOVector(block_type&& block) { append(std::move(block)); }
 
     IOVector(const IOVector& copy) = delete;
     IOVector(IOVector&& move) noexcept : IOVector() { *this = std::move(move); }
 
     IOVector& operator=(const IOVector& copy) = delete;
-    IOVector& operator=(IOVector&& move) noexcept {
-        chain_ = std::move(move.chain_);
-        chain_length_ = move.chain_length_;
-        begin_offset_ = move.begin_offset_;
-        end_offset_ = move.end_offset_;
+    IOVector& operator=(IOVector&& move) noexcept;
 
-        move.chain_.clear();
-        move.chain_length_ = 0;
-        move.begin_offset_ = 0;
-        move.end_offset_ = 0;
-
-        return *this;
-    }
-
-    size_type size() const { return chain_length_ - begin_offset_ - end_offset_; }
+    size_type size() const { return chain_length_ - begin_offset_; }
     bool empty() const { return size() == 0; }
 
-    void clear() {
-        chain_length_ = 0;
-        begin_offset_ = 0;
-        end_offset_ = 0;
-        chain_.clear();
-    }
+    // Return the last block so the caller can still reuse its allocated capacity
+    // or it can be simply ignored.
+    block_type clear();
+
+    void drop_front(size_type len);
 
     // Split the first |len| bytes out of this chain into its own.
-    IOVector take_front(size_type len) {
-        IOVector head;
-
-        if (len == 0) {
-            return head;
-        }
-        CHECK_GE(size(), len);
-
-        std::shared_ptr<const block_type> first_block = chain_.front();
-        CHECK_GE(first_block->size(), begin_offset_);
-        head.append_shared(std::move(first_block));
-        head.begin_offset_ = begin_offset_;
-
-        while (head.size() < len) {
-            pop_front_block();
-            CHECK(!chain_.empty());
-
-            head.append_shared(chain_.front());
-        }
-
-        if (head.size() == len) {
-            // Head takes full ownership of the last block it took.
-            head.end_offset_ = 0;
-            begin_offset_ = 0;
-            pop_front_block();
-        } else {
-            // Head takes partial ownership of the last block it took.
-            size_t bytes_taken = head.size() - len;
-            head.end_offset_ = bytes_taken;
-            CHECK_GE(chain_.front()->size(), bytes_taken);
-            begin_offset_ = chain_.front()->size() - bytes_taken;
-        }
-
-        return head;
-    }
+    IOVector take_front(size_type len);
 
     // Add a nonempty block to the chain.
-    // The end of the chain must be a complete block (i.e. end_offset_ == 0).
-    void append(std::unique_ptr<const block_type> block) {
-        if (block->size() == 0) {
+    void append(block_type&& block) {
+        if (block.size() == 0) {
             return;
         }
-
-        CHECK_EQ(0ULL, end_offset_);
-        chain_length_ += block->size();
+        CHECK_NE(0ULL, block.size());
+        chain_length_ += block.size();
         chain_.emplace_back(std::move(block));
     }
 
-    void append(block_type&& block) { append(std::make_unique<block_type>(std::move(block))); }
-
-    void trim_front() {
-        if (begin_offset_ == 0) {
-            return;
-        }
-
-        const block_type* first_block = chain_.front().get();
-        auto copy = std::make_unique<block_type>(first_block->size() - begin_offset_);
-        memcpy(copy->data(), first_block->data() + begin_offset_, copy->size());
-        chain_.front() = std::move(copy);
-
-        chain_length_ -= begin_offset_;
-        begin_offset_ = 0;
-    }
+    void trim_front();
 
   private:
-    // append, except takes a shared_ptr.
-    // Private to prevent exterior mutation of blocks.
-    void append_shared(std::shared_ptr<const block_type> block) {
-        CHECK_NE(0ULL, block->size());
-        CHECK_EQ(0ULL, end_offset_);
-        chain_length_ += block->size();
-        chain_.emplace_back(std::move(block));
-    }
+    void trim_chain_front();
 
     // Drop the front block from the chain, and update chain_length_ appropriately.
-    void pop_front_block() {
-        chain_length_ -= chain_.front()->size();
-        begin_offset_ = 0;
-        chain_.pop_front();
-    }
+    void pop_front_block();
 
     // Iterate over the blocks with a callback with an operator()(const char*, size_t).
     template <typename Fn>
     void iterate_blocks(Fn&& callback) const {
-        if (chain_.size() == 0) {
+        if (size() == 0) {
             return;
         }
 
-        for (size_t i = 0; i < chain_.size(); ++i) {
-            const std::shared_ptr<const block_type>& block = chain_.at(i);
-            const char* begin = block->data();
-            size_t length = block->size();
+        for (size_t i = start_index_; i < chain_.size(); ++i) {
+            const auto& block = chain_[i];
+            const char* begin = block.data();
+            size_t length = block.size();
 
-            // Note that both of these conditions can be true if there's only one block.
-            if (i == 0) {
-                CHECK_GE(block->size(), begin_offset_);
+            if (i == start_index_) {
+                CHECK_GE(block.size(), begin_offset_);
                 begin += begin_offset_;
                 length -= begin_offset_;
             }
-
-            if (i == chain_.size() - 1) {
-                CHECK_GE(length, end_offset_);
-                length -= end_offset_;
-            }
-
             callback(begin, length);
         }
     }
@@ -291,7 +203,7 @@
   public:
     // Copy all of the blocks into a single block.
     template <typename CollectionType = block_type>
-    CollectionType coalesce() const {
+    CollectionType coalesce() const& {
         CollectionType result;
         if (size() == 0) {
             return result;
@@ -308,12 +220,13 @@
         return result;
     }
 
+    block_type coalesce() &&;
+
     template <typename FunctionType>
-    auto coalesced(FunctionType&& f) const ->
-        typename std::result_of<FunctionType(const char*, size_t)>::type {
-        if (chain_.size() == 1) {
+    auto coalesced(FunctionType&& f) const {
+        if (chain_.size() == start_index_ + 1) {
             // If we only have one block, we can use it directly.
-            return f(chain_.front()->data() + begin_offset_, size());
+            return f(chain_[start_index_].data() + begin_offset_, size());
         } else {
             // Otherwise, copy to a single block.
             auto data = coalesce();
@@ -322,23 +235,13 @@
     }
 
     // Get a list of iovecs that can be used to write out all of the blocks.
-    std::vector<adb_iovec> iovecs() const {
-        std::vector<adb_iovec> result;
-        iterate_blocks([&result](const char* data, size_t len) {
-            adb_iovec iov;
-            iov.iov_base = const_cast<char*>(data);
-            iov.iov_len = len;
-            result.emplace_back(iov);
-        });
-
-        return result;
-    }
+    std::vector<adb_iovec> iovecs() const;
 
   private:
     // Total length of all of the blocks in the chain.
     size_t chain_length_ = 0;
 
     size_t begin_offset_ = 0;
-    size_t end_offset_ = 0;
-    std::deque<std::shared_ptr<const block_type>> chain_;
+    size_t start_index_ = 0;
+    std::vector<block_type> chain_;
 };
diff --git a/adb/types_test.cpp b/adb/types_test.cpp
index 1fbd2ca..2c99f95 100644
--- a/adb/types_test.cpp
+++ b/adb/types_test.cpp
@@ -19,21 +19,21 @@
 #include <memory>
 #include "types.h"
 
-static std::unique_ptr<IOVector::block_type> create_block(const std::string& string) {
-    return std::make_unique<IOVector::block_type>(string.begin(), string.end());
+static IOVector::block_type create_block(const std::string& string) {
+    return IOVector::block_type(string.begin(), string.end());
 }
 
-static std::unique_ptr<IOVector::block_type> create_block(char value, size_t len) {
-    auto block = std::make_unique<IOVector::block_type>();
-    block->resize(len);
-    memset(&(*block)[0], value, len);
+static IOVector::block_type create_block(char value, size_t len) {
+    auto block = IOVector::block_type();
+    block.resize(len);
+    memset(&(block)[0], value, len);
     return block;
 }
 
 template <typename T>
-static std::unique_ptr<IOVector::block_type> copy_block(T&& block) {
-    auto copy = std::make_unique<IOVector::block_type>();
-    copy->assign(block->begin(), block->end());
+static IOVector::block_type copy_block(const T& block) {
+    auto copy = IOVector::block_type();
+    copy.assign(block.begin(), block.end());
     return copy;
 }
 
@@ -50,7 +50,7 @@
     bc.append(copy_block(block));
     ASSERT_EQ(100ULL, bc.size());
     auto coalesced = bc.coalesce();
-    ASSERT_EQ(*block, coalesced);
+    ASSERT_EQ(block, coalesced);
 }
 
 TEST(IOVector, single_block_split) {
@@ -60,8 +60,8 @@
     IOVector foo = bc.take_front(3);
     ASSERT_EQ(3ULL, foo.size());
     ASSERT_EQ(3ULL, bc.size());
-    ASSERT_EQ(*create_block("foo"), foo.coalesce());
-    ASSERT_EQ(*create_block("bar"), bc.coalesce());
+    ASSERT_EQ(create_block("foo"), foo.coalesce());
+    ASSERT_EQ(create_block("bar"), bc.coalesce());
 }
 
 TEST(IOVector, aligned_split) {
@@ -73,15 +73,15 @@
 
     IOVector foo = bc.take_front(3);
     ASSERT_EQ(3ULL, foo.size());
-    ASSERT_EQ(*create_block("foo"), foo.coalesce());
+    ASSERT_EQ(create_block("foo"), foo.coalesce());
 
     IOVector bar = bc.take_front(3);
     ASSERT_EQ(3ULL, bar.size());
-    ASSERT_EQ(*create_block("bar"), bar.coalesce());
+    ASSERT_EQ(create_block("bar"), bar.coalesce());
 
     IOVector baz = bc.take_front(3);
     ASSERT_EQ(3ULL, baz.size());
-    ASSERT_EQ(*create_block("baz"), baz.coalesce());
+    ASSERT_EQ(create_block("baz"), baz.coalesce());
 
     ASSERT_EQ(0ULL, bc.size());
 }
@@ -97,23 +97,23 @@
     // Aligned left, misaligned right, across multiple blocks.
     IOVector foob = bc.take_front(4);
     ASSERT_EQ(4ULL, foob.size());
-    ASSERT_EQ(*create_block("foob"), foob.coalesce());
+    ASSERT_EQ(create_block("foob"), foob.coalesce());
 
     // Misaligned left, misaligned right, in one block.
     IOVector a = bc.take_front(1);
     ASSERT_EQ(1ULL, a.size());
-    ASSERT_EQ(*create_block("a"), a.coalesce());
+    ASSERT_EQ(create_block("a"), a.coalesce());
 
     // Misaligned left, misaligned right, across two blocks.
     IOVector rba = bc.take_front(3);
     ASSERT_EQ(3ULL, rba.size());
-    ASSERT_EQ(*create_block("rba"), rba.coalesce());
+    ASSERT_EQ(create_block("rba"), rba.coalesce());
 
     // Misaligned left, misaligned right, across three blocks.
     IOVector zquxquu = bc.take_front(7);
     ASSERT_EQ(7ULL, zquxquu.size());
-    ASSERT_EQ(*create_block("zquxquu"), zquxquu.coalesce());
+    ASSERT_EQ(create_block("zquxquu"), zquxquu.coalesce());
 
     ASSERT_EQ(1ULL, bc.size());
-    ASSERT_EQ(*create_block("x"), bc.coalesce());
+    ASSERT_EQ(create_block("x"), bc.coalesce());
 }
diff --git a/base/Android.bp b/base/Android.bp
index 25ae535..aeb8864 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -58,6 +58,7 @@
         "file.cpp",
         "logging.cpp",
         "mapped_file.cpp",
+        "parsebool.cpp",
         "parsenetaddress.cpp",
         "process.cpp",
         "properties.cpp",
@@ -149,6 +150,7 @@
         "macros_test.cpp",
         "mapped_file_test.cpp",
         "parsedouble_test.cpp",
+        "parsebool_test.cpp",
         "parseint_test.cpp",
         "parsenetaddress_test.cpp",
         "process_test.cpp",
diff --git a/base/README.md b/base/README.md
new file mode 100644
index 0000000..2ef5c10
--- /dev/null
+++ b/base/README.md
@@ -0,0 +1,42 @@
+# libbase
+
+## Who is this library for?
+
+This library is a collection of convenience functions to make common tasks
+easier and less error-prone.
+
+In this context, "error-prone" covers both "hard to do correctly" and
+"hard to do with good performance", but as a general purpose library,
+libbase's primary focus is on making it easier to do things easily and
+correctly when a compromise has to be made between "simplest API" on the
+one hand and "fastest implementation" on the other. Though obviously
+the ideal is to have both.
+
+## Should my routine be added?
+
+The intention is to cover the 80% use cases, not be all things to all users.
+
+If you have a routine that's really useful in your project,
+congratulations. But that doesn't mean it should be here rather than
+just in your project.
+
+The question for libbase is "should everyone be doing this?"/"does this
+make everyone's code cleaner/safer?". Historically we've considered the
+bar for inclusion to be "are there at least three *unrelated* projects
+that would be cleaned up by doing so".
+
+If your routine is actually something from a future C++ standard (that
+isn't yet in libc++), or it's widely used in another library, that helps
+show that there's precedent. Being able to say "so-and-so has used this
+API for n years" is a good way to reduce concerns about API choices.
+
+## Any other restrictions?
+
+Unlike most Android code, code in libbase has to build for Mac and
+Windows too.
+
+Code here is also expected to have good test coverage.
+
+By its nature, it's difficult to change libbase API. It's often best
+to start using your routine just in your project, and let it "graduate"
+after you're certain that the API is solid.
diff --git a/base/include/android-base/parsebool.h b/base/include/android-base/parsebool.h
new file mode 100644
index 0000000..b2bd021
--- /dev/null
+++ b/base/include/android-base/parsebool.h
@@ -0,0 +1,58 @@
+/*
+ * 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 <string_view>
+
+namespace android {
+namespace base {
+
+// Parse the given string as yes or no inactivation of some sort. Return one of the
+// ParseBoolResult enumeration values.
+//
+// The following values parse as true:
+//
+//   1
+//   on
+//   true
+//   y
+//   yes
+//
+//
+// The following values parse as false:
+//
+//   0
+//   false
+//   n
+//   no
+//   off
+//
+// Anything else is a parse error.
+//
+// The purpose of this function is to have a single canonical parser for yes-or-no indications
+// throughout the system.
+
+enum class ParseBoolResult {
+  kError,
+  kFalse,
+  kTrue,
+};
+
+ParseBoolResult ParseBool(std::string_view s);
+
+}  // namespace base
+}  // namespace android
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index b1c22c9..14d534a 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -85,5 +85,10 @@
   return true;
 }
 
+// Replaces `from` with `to` in `s`, once if `all == false`, or as many times as
+// there are matches if `all == true`.
+[[nodiscard]] std::string StringReplace(std::string_view s, std::string_view from,
+                                        std::string_view to, bool all);
+
 }  // namespace base
 }  // namespace android
diff --git a/base/parsebool.cpp b/base/parsebool.cpp
new file mode 100644
index 0000000..ff96fe9
--- /dev/null
+++ b/base/parsebool.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/parsebool.h"
+#include <errno.h>
+
+namespace android {
+namespace base {
+
+ParseBoolResult ParseBool(std::string_view s) {
+  if (s == "1" || s == "y" || s == "yes" || s == "on" || s == "true") {
+    return ParseBoolResult::kTrue;
+  }
+  if (s == "0" || s == "n" || s == "no" || s == "off" || s == "false") {
+    return ParseBoolResult::kFalse;
+  }
+  return ParseBoolResult::kError;
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/parsebool_test.cpp b/base/parsebool_test.cpp
new file mode 100644
index 0000000..a081994
--- /dev/null
+++ b/base/parsebool_test.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/parsebool.h"
+
+#include <errno.h>
+
+#include <gtest/gtest.h>
+#include <string_view>
+
+using android::base::ParseBool;
+using android::base::ParseBoolResult;
+
+TEST(parsebool, true_) {
+  static const char* yes[] = {
+      "1", "on", "true", "y", "yes",
+  };
+  for (const char* s : yes) {
+    ASSERT_EQ(ParseBoolResult::kTrue, ParseBool(s));
+  }
+}
+
+TEST(parsebool, false_) {
+  static const char* no[] = {
+      "0", "false", "n", "no", "off",
+  };
+  for (const char* s : no) {
+    ASSERT_EQ(ParseBoolResult::kFalse, ParseBool(s));
+  }
+}
+
+TEST(parsebool, invalid) {
+  ASSERT_EQ(ParseBoolResult::kError, ParseBool("blarg"));
+  ASSERT_EQ(ParseBoolResult::kError, ParseBool(""));
+}
diff --git a/base/properties.cpp b/base/properties.cpp
index d5a5918..4731bf2 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -28,19 +28,22 @@
 #include <map>
 #include <string>
 
+#include <android-base/parsebool.h>
 #include <android-base/parseint.h>
 
 namespace android {
 namespace base {
 
 bool GetBoolProperty(const std::string& key, bool default_value) {
-  std::string value = GetProperty(key, "");
-  if (value == "1" || value == "y" || value == "yes" || value == "on" || value == "true") {
-    return true;
-  } else if (value == "0" || value == "n" || value == "no" || value == "off" || value == "false") {
-    return false;
+  switch (ParseBool(GetProperty(key, ""))) {
+    case ParseBoolResult::kError:
+      return default_value;
+    case ParseBoolResult::kFalse:
+      return false;
+    case ParseBoolResult::kTrue:
+      return true;
   }
-  return default_value;
+  __builtin_unreachable();
 }
 
 template <typename T>
diff --git a/base/strings.cpp b/base/strings.cpp
index bb3167e..40b2bf2 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -116,5 +116,24 @@
   return lhs.size() == rhs.size() && strncasecmp(lhs.data(), rhs.data(), lhs.size()) == 0;
 }
 
+std::string StringReplace(std::string_view s, std::string_view from, std::string_view to,
+                          bool all) {
+  if (from.empty()) return std::string(s);
+
+  std::string result;
+  std::string_view::size_type start_pos = 0;
+  do {
+    std::string_view::size_type pos = s.find(from, start_pos);
+    if (pos == std::string_view::npos) break;
+
+    result.append(s.data() + start_pos, pos - start_pos);
+    result.append(to.data(), to.size());
+
+    start_pos = pos + from.size();
+  } while (all);
+  result.append(s.data() + start_pos, s.size() - start_pos);
+  return result;
+}
+
 }  // namespace base
 }  // namespace android
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index ca3c0b8..5ae3094 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -311,3 +311,46 @@
   ASSERT_TRUE(android::base::ConsumeSuffix(&s, ".bar"));
   ASSERT_EQ("foo", s);
 }
+
+TEST(strings, StringReplace_false) {
+  // No change.
+  ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "z", "Z", false));
+  ASSERT_EQ("", android::base::StringReplace("", "z", "Z", false));
+  ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "", "Z", false));
+
+  // Equal lengths.
+  ASSERT_EQ("Abcabc", android::base::StringReplace("abcabc", "a", "A", false));
+  ASSERT_EQ("aBcabc", android::base::StringReplace("abcabc", "b", "B", false));
+  ASSERT_EQ("abCabc", android::base::StringReplace("abcabc", "c", "C", false));
+
+  // Longer replacement.
+  ASSERT_EQ("foobcabc", android::base::StringReplace("abcabc", "a", "foo", false));
+  ASSERT_EQ("afoocabc", android::base::StringReplace("abcabc", "b", "foo", false));
+  ASSERT_EQ("abfooabc", android::base::StringReplace("abcabc", "c", "foo", false));
+
+  // Shorter replacement.
+  ASSERT_EQ("xxyz", android::base::StringReplace("abcxyz", "abc", "x", false));
+  ASSERT_EQ("axyz", android::base::StringReplace("abcxyz", "bcx", "x", false));
+  ASSERT_EQ("abcx", android::base::StringReplace("abcxyz", "xyz", "x", false));
+}
+
+TEST(strings, StringReplace_true) {
+  // No change.
+  ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "z", "Z", true));
+  ASSERT_EQ("", android::base::StringReplace("", "z", "Z", true));
+  ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "", "Z", true));
+
+  // Equal lengths.
+  ASSERT_EQ("AbcAbc", android::base::StringReplace("abcabc", "a", "A", true));
+  ASSERT_EQ("aBcaBc", android::base::StringReplace("abcabc", "b", "B", true));
+  ASSERT_EQ("abCabC", android::base::StringReplace("abcabc", "c", "C", true));
+
+  // Longer replacement.
+  ASSERT_EQ("foobcfoobc", android::base::StringReplace("abcabc", "a", "foo", true));
+  ASSERT_EQ("afoocafooc", android::base::StringReplace("abcabc", "b", "foo", true));
+  ASSERT_EQ("abfooabfoo", android::base::StringReplace("abcabc", "c", "foo", true));
+
+  // Shorter replacement.
+  ASSERT_EQ("xxyzx", android::base::StringReplace("abcxyzabc", "abc", "x", true));
+  ASSERT_EQ("<xx>", android::base::StringReplace("<abcabc>", "abc", "x", true));
+}
diff --git a/code_coverage/Android.mk b/code_coverage/Android.mk
new file mode 100644
index 0000000..80ab36b
--- /dev/null
+++ b/code_coverage/Android.mk
@@ -0,0 +1,37 @@
+# policies to allow processes inside minijail to dump code coverage information
+#
+
+LOCAL_PATH := $(call my-dir)
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := code_coverage.policy
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MULTILIB := both
+
+ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), arm arm64))
+LOCAL_MODULE_STEM_32 := code_coverage.arm.policy
+LOCAL_MODULE_STEM_64 := code_coverage.arm64.policy
+endif
+
+ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86 x86_64))
+LOCAL_MODULE_STEM_32 := code_coverage.x86.policy
+LOCAL_MODULE_STEM_64 := code_coverage.x86_64.policy
+endif
+
+# different files for different configurations
+ifeq ($(NATIVE_COVERAGE),true)
+LOCAL_SRC_FILES_arm := seccomp_policy/code_coverage.arm.policy
+LOCAL_SRC_FILES_arm64 := seccomp_policy/code_coverage.arm64.policy
+LOCAL_SRC_FILES_x86 := seccomp_policy/code_coverage.x86.policy
+LOCAL_SRC_FILES_x86_64 := seccomp_policy/code_coverage.x86_64.policy
+else
+LOCAL_SRC_FILES_arm := empty_policy/code_coverage.arm.policy
+LOCAL_SRC_FILES_arm64 := empty_policy/code_coverage.arm64.policy
+LOCAL_SRC_FILES_x86 := empty_policy/code_coverage.x86.policy
+LOCAL_SRC_FILES_x86_64 := empty_policy/code_coverage.x86_64.policy
+endif
+
+LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64
+LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy
+include $(BUILD_PREBUILT)
diff --git a/code_coverage/empty_policy/code_coverage.arm.policy b/code_coverage/empty_policy/code_coverage.arm.policy
new file mode 100644
index 0000000..4c9132b
--- /dev/null
+++ b/code_coverage/empty_policy/code_coverage.arm.policy
@@ -0,0 +1,2 @@
+# empty unless code_coverage is enabled.
+# code_coverage.arm.policy
diff --git a/code_coverage/empty_policy/code_coverage.arm64.policy b/code_coverage/empty_policy/code_coverage.arm64.policy
new file mode 100644
index 0000000..dc5c35a
--- /dev/null
+++ b/code_coverage/empty_policy/code_coverage.arm64.policy
@@ -0,0 +1,2 @@
+# empty unless code_coverage is enabled.
+# code_coverage.arm64.policy
diff --git a/code_coverage/empty_policy/code_coverage.x86.policy b/code_coverage/empty_policy/code_coverage.x86.policy
new file mode 100644
index 0000000..044f34c
--- /dev/null
+++ b/code_coverage/empty_policy/code_coverage.x86.policy
@@ -0,0 +1,2 @@
+# empty unless code_coverage is enabled.
+# code_coverage.x86.policy
diff --git a/code_coverage/empty_policy/code_coverage.x86_64.policy b/code_coverage/empty_policy/code_coverage.x86_64.policy
new file mode 100644
index 0000000..6dcd22d
--- /dev/null
+++ b/code_coverage/empty_policy/code_coverage.x86_64.policy
@@ -0,0 +1,2 @@
+# empty unless code_coverage is enabled.
+# code_coverage.x86_64.policy
diff --git a/code_coverage/seccomp_policy/code_coverage.arm.policy b/code_coverage/seccomp_policy/code_coverage.arm.policy
new file mode 100644
index 0000000..d6784e3
--- /dev/null
+++ b/code_coverage/seccomp_policy/code_coverage.arm.policy
@@ -0,0 +1,14 @@
+close: 1
+mkdirat: 1
+msync: 1
+munmap: 1
+openat: 1
+write: 1
+fcntl64: 1
+fstat64: 1
+geteuid32: 1
+_llseek: 1
+mmap2: 1
+sigreturn: 1
+gettimeofday: 1
+prctl: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.arm64.policy b/code_coverage/seccomp_policy/code_coverage.arm64.policy
new file mode 100644
index 0000000..4c3dd26
--- /dev/null
+++ b/code_coverage/seccomp_policy/code_coverage.arm64.policy
@@ -0,0 +1,13 @@
+close: 1
+mkdirat: 1
+msync: 1
+munmap: 1
+openat: 1
+write: 1
+fcntl: 1
+fstat: 1
+geteuid: 1
+lseek: 1
+mmap: 1
+rt_sigreturn: 1
+prctl: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.policy.def b/code_coverage/seccomp_policy/code_coverage.policy.def
new file mode 100644
index 0000000..f136084
--- /dev/null
+++ b/code_coverage/seccomp_policy/code_coverage.policy.def
@@ -0,0 +1,51 @@
+// SECCOMP_MODE_STRICT
+//
+// minijail allowances for code coverage
+// this is processed with generate.sh, so we can use appropriate directives
+// size specific: __LP64__ for 64 bit, else 32 bit
+// arch specific: __arm__, __aarch64__, __i386__, __x86_64__
+
+// includes *all* syscalls used during the coverage dumping
+// no skipping just because they might have been in another policy file.
+
+// coverage tool uses different operations on different passes
+// 1st: uses write() to fill the file
+// 2nd-Nth: uses mmap() to update in place
+
+close: 1
+mkdirat: 1
+msync: 1
+munmap: 1
+openat: 1
+write: 1
+
+#if     defined(__LP64__)
+fcntl: 1
+fstat: 1
+geteuid: 1
+lseek: 1
+mmap: 1
+rt_sigreturn: 1
+#else
+fcntl64: 1
+fstat64: 1
+geteuid32: 1
+_llseek: 1
+mmap2: 1
+sigreturn: 1
+#endif
+
+#if     defined(__arm__)
+gettimeofday: 1
+#endif
+
+#if     defined(__i386__)
+madvise: 1
+#endif
+
+#if     defined(__arm__)
+prctl: 1
+#elif   defined(__aarch64__)
+prctl: 1
+#endif
+
diff --git a/code_coverage/seccomp_policy/code_coverage.x86.policy b/code_coverage/seccomp_policy/code_coverage.x86.policy
new file mode 100644
index 0000000..24ff8b9
--- /dev/null
+++ b/code_coverage/seccomp_policy/code_coverage.x86.policy
@@ -0,0 +1,13 @@
+close: 1
+mkdirat: 1
+msync: 1
+munmap: 1
+openat: 1
+write: 1
+fcntl64: 1
+fstat64: 1
+geteuid32: 1
+_llseek: 1
+mmap2: 1
+sigreturn: 1
+madvise: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.x86_64.policy b/code_coverage/seccomp_policy/code_coverage.x86_64.policy
new file mode 100644
index 0000000..3081036
--- /dev/null
+++ b/code_coverage/seccomp_policy/code_coverage.x86_64.policy
@@ -0,0 +1,12 @@
+close: 1
+mkdirat: 1
+msync: 1
+munmap: 1
+openat: 1
+write: 1
+fcntl: 1
+fstat: 1
+geteuid: 1
+lseek: 1
+mmap: 1
+rt_sigreturn: 1
diff --git a/code_coverage/seccomp_policy/generate.sh b/code_coverage/seccomp_policy/generate.sh
new file mode 100755
index 0000000..ae582c6
--- /dev/null
+++ b/code_coverage/seccomp_policy/generate.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# generate the arch-specific files from the generic one
+
+set -ex
+
+cd "$(dirname "$0")"
+CPP='cpp -undef -E -P code_coverage.policy.def'
+$CPP -D__arm__ -o code_coverage.arm.policy
+$CPP -D__aarch64__ -D__LP64__ -o code_coverage.arm64.policy
+$CPP -D__i386__ -o code_coverage.x86.policy
+$CPP -D__x86_64__ -D__LP64__ -o code_coverage.x86_64.policy
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index f189c45..75bac87 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -20,6 +20,7 @@
 
 #include <inttypes.h>
 #include <signal.h>
+#include <stdarg.h>
 #include <stdbool.h>
 #include <sys/types.h>
 
@@ -71,6 +72,7 @@
 
 // Log information onto the tombstone.
 void _LOG(log_t* log, logtype ltype, const char* fmt, ...) __attribute__((format(printf, 3, 4)));
+void _VLOG(log_t* log, logtype ltype, const char* fmt, va_list ap);
 
 namespace unwindstack {
 class Unwinder;
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 1993840..236fcf7 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -428,7 +428,7 @@
   std::vector<std::pair<std::string, uint64_t>> special_row;
 
 #if defined(__arm__) || defined(__aarch64__)
-  static constexpr const char* special_registers[] = {"ip", "lr", "sp", "pc"};
+  static constexpr const char* special_registers[] = {"ip", "lr", "sp", "pc", "pst"};
 #elif defined(__i386__)
   static constexpr const char* special_registers[] = {"ebp", "esp", "eip"};
 #elif defined(__x86_64__)
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 9b2779a..5ce26fc 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -67,6 +67,14 @@
 
 __attribute__((__weak__, visibility("default")))
 void _LOG(log_t* log, enum logtype ltype, const char* fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  _VLOG(log, ltype, fmt, ap);
+  va_end(ap);
+}
+
+__attribute__((__weak__, visibility("default")))
+void _VLOG(log_t* log, enum logtype ltype, const char* fmt, va_list ap) {
   bool write_to_tombstone = (log->tfd != -1);
   bool write_to_logcat = is_allowed_in_logcat(ltype)
                       && log->crashed_tid != -1
@@ -75,10 +83,7 @@
   static bool write_to_kmsg = should_write_to_kmsg();
 
   std::string msg;
-  va_list ap;
-  va_start(ap, fmt);
   android::base::StringAppendV(&msg, fmt, ap);
-  va_end(ap);
 
   if (msg.empty()) return;
 
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 02a887e..a757d56 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -137,12 +137,14 @@
         "libhidlbase",
         "liblog",
         "liblp",
+        "libprotobuf-cpp-lite",
         "libsparse",
         "libutils",
     ],
 
     static_libs: [
         "libhealthhalutils",
+        "libsnapshot_nobinder",
     ],
 
     header_libs: [
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 7fba67c..5a554a0 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -68,3 +68,4 @@
 #define FB_VAR_BATTERY_SOC_OK "battery-soc-ok"
 #define FB_VAR_SUPER_PARTITION_NAME "super-partition-name"
 #define FB_VAR_SNAPSHOT_UPDATE_STATUS "snapshot-update-status"
+#define FB_VAR_CPU_ABI "cpu-abi"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index dfd5690..1a745ab 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -19,6 +19,8 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 
+#include <unordered_set>
+
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
@@ -33,6 +35,7 @@
 #include <libgsi/libgsi.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
+#include <libsnapshot/snapshot.h>
 #include <uuid/uuid.h>
 
 #include "constants.h"
@@ -48,6 +51,7 @@
 using ::android::hardware::boot::V1_1::MergeStatus;
 using ::android::hardware::fastboot::V1_0::Result;
 using ::android::hardware::fastboot::V1_0::Status;
+using android::snapshot::SnapshotManager;
 using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
 
 struct VariableHandlers {
@@ -57,6 +61,24 @@
     std::function<std::vector<std::vector<std::string>>(FastbootDevice*)> get_all_args;
 };
 
+static bool IsSnapshotUpdateInProgress(FastbootDevice* device) {
+    auto hal = device->boot1_1();
+    if (!hal) {
+        return false;
+    }
+    auto merge_status = hal->getSnapshotMergeStatus();
+    return merge_status == MergeStatus::SNAPSHOTTED || merge_status == MergeStatus::MERGING;
+}
+
+static bool IsProtectedPartitionDuringMerge(FastbootDevice* device, const std::string& name) {
+    static const std::unordered_set<std::string> ProtectedPartitionsDuringMerge = {
+            "userdata", "metadata", "misc"};
+    if (ProtectedPartitionsDuringMerge.count(name) == 0) {
+        return false;
+    }
+    return IsSnapshotUpdateInProgress(device);
+}
+
 static void GetAllVars(FastbootDevice* device, const std::string& name,
                        const VariableHandlers& handlers) {
     if (!handlers.get_all_args) {
@@ -105,7 +127,8 @@
             {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
             {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
             {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
-            {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}}};
+            {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}},
+            {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}}};
 
     if (args.size() < 2) {
         return device->WriteFail("Missing argument");
@@ -142,8 +165,14 @@
         return device->WriteStatus(FastbootResult::FAIL, "Erase is not allowed on locked devices");
     }
 
+    const auto& partition_name = args[1];
+    if (IsProtectedPartitionDuringMerge(device, partition_name)) {
+        auto message = "Cannot erase " + partition_name + " while a snapshot update is in progress";
+        return device->WriteFail(message);
+    }
+
     PartitionHandle handle;
-    if (!OpenPartition(device, args[1], &handle)) {
+    if (!OpenPartition(device, partition_name, &handle)) {
         return device->WriteStatus(FastbootResult::FAIL, "Partition doesn't exist");
     }
     if (wipe_block_device(handle.fd(), get_block_device_size(handle.fd())) == 0) {
@@ -208,9 +237,9 @@
                                    "set_active command is not allowed on locked devices");
     }
 
-    // Slot suffix needs to be between 'a' and 'z'.
     Slot slot;
     if (!GetSlotNumber(args[1], &slot)) {
+        // Slot suffix needs to be between 'a' and 'z'.
         return device->WriteStatus(FastbootResult::FAIL, "Bad slot suffix");
     }
 
@@ -223,6 +252,32 @@
     if (slot >= boot_control_hal->getNumberSlots()) {
         return device->WriteStatus(FastbootResult::FAIL, "Slot out of range");
     }
+
+    // If the slot is not changing, do nothing.
+    if (slot == boot_control_hal->getCurrentSlot()) {
+        return device->WriteOkay("");
+    }
+
+    // Check how to handle the current snapshot state.
+    if (auto hal11 = device->boot1_1()) {
+        auto merge_status = hal11->getSnapshotMergeStatus();
+        if (merge_status == MergeStatus::MERGING) {
+            return device->WriteFail("Cannot change slots while a snapshot update is in progress");
+        }
+        // Note: we allow the slot change if the state is SNAPSHOTTED. First-
+        // stage init does not have access to the HAL, and uses the slot number
+        // and /metadata OTA state to determine whether a slot change occurred.
+        // Booting into the old slot would erase the OTA, and switching A->B->A
+        // would simply resume it if no boots occur in between. Re-flashing
+        // partitions implicitly cancels the OTA, so leaving the state as-is is
+        // safe.
+        if (merge_status == MergeStatus::SNAPSHOTTED) {
+            device->WriteInfo(
+                    "Changing the active slot with a snapshot applied may cancel the"
+                    " update.");
+        }
+    }
+
     CommandResult ret;
     auto cb = [&ret](CommandResult result) { ret = result; };
     auto result = boot_control_hal->setActiveBootSlot(slot, cb);
@@ -466,6 +521,11 @@
     }
 
     const auto& partition_name = args[1];
+    if (IsProtectedPartitionDuringMerge(device, partition_name)) {
+        auto message = "Cannot flash " + partition_name + " while a snapshot update is in progress";
+        return device->WriteFail(message);
+    }
+
     if (LogicalPartitionExists(device, partition_name)) {
         CancelPartitionSnapshot(device, partition_name);
     }
@@ -555,12 +615,9 @@
 bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string>& args) {
     // Note that we use the HAL rather than mounting /metadata, since we want
     // our results to match the bootloader.
-    auto hal = device->boot_control_hal();
+    auto hal = device->boot1_1();
     if (!hal) return device->WriteFail("Not supported");
 
-    android::sp<IBootControl1_1> hal11 = IBootControl1_1::castFrom(hal);
-    if (!hal11) return device->WriteFail("Not supported");
-
     // If no arguments, return the same thing as a getvar. Note that we get the
     // HAL first so we can return "not supported" before we return the less
     // specific error message below.
@@ -573,18 +630,34 @@
         return device->WriteOkay("");
     }
 
-    if (args.size() != 2 || args[1] != "cancel") {
+    MergeStatus status = hal->getSnapshotMergeStatus();
+
+    if (args.size() != 2) {
         return device->WriteFail("Invalid arguments");
     }
+    if (args[1] == "cancel") {
+        switch (status) {
+            case MergeStatus::SNAPSHOTTED:
+            case MergeStatus::MERGING:
+                hal->setSnapshotMergeStatus(MergeStatus::CANCELLED);
+                break;
+            default:
+                break;
+        }
+    } else if (args[1] == "merge") {
+        if (status != MergeStatus::MERGING) {
+            return device->WriteFail("No snapshot merge is in progress");
+        }
 
-    MergeStatus status = hal11->getSnapshotMergeStatus();
-    switch (status) {
-        case MergeStatus::SNAPSHOTTED:
-        case MergeStatus::MERGING:
-            hal11->setSnapshotMergeStatus(MergeStatus::CANCELLED);
-            break;
-        default:
-            break;
+        auto sm = SnapshotManager::NewForFirstStageMount();
+        if (!sm) {
+            return device->WriteFail("Unable to create SnapshotManager");
+        }
+        if (!sm->HandleImminentDataWipe()) {
+            return device->WriteFail("Unable to finish snapshot merge");
+        }
+    } else {
+        return device->WriteFail("Invalid parameter to snapshot-update");
     }
     return device->WriteStatus(FastbootResult::OKAY, "Success");
 }
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index d3c2bda..31fc359 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -60,7 +60,11 @@
       boot_control_hal_(IBootControl::getService()),
       health_hal_(get_health_service()),
       fastboot_hal_(IFastboot::getService()),
-      active_slot_("") {}
+      active_slot_("") {
+    if (boot_control_hal_) {
+        boot1_1_ = android::hardware::boot::V1_1::IBootControl::castFrom(boot_control_hal_);
+    }
+}
 
 FastbootDevice::~FastbootDevice() {
     CloseDevice();
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index 091aadf..bbe8172 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -23,6 +23,7 @@
 #include <vector>
 
 #include <android/hardware/boot/1.0/IBootControl.h>
+#include <android/hardware/boot/1.1/IBootControl.h>
 #include <android/hardware/fastboot/1.0/IFastboot.h>
 #include <android/hardware/health/2.0/IHealth.h>
 
@@ -51,6 +52,7 @@
     android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal() {
         return boot_control_hal_;
     }
+    android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1() { return boot1_1_; }
     android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal() {
         return fastboot_hal_;
     }
@@ -63,6 +65,7 @@
 
     std::unique_ptr<Transport> transport_;
     android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_;
+    android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1_;
     android::sp<android::hardware::health::V2_0::IHealth> health_hal_;
     android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal_;
     std::vector<char> download_data_;
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 6e613d6..10eac01 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -432,19 +432,13 @@
                              std::string* message) {
     // Note that we use the HAL rather than mounting /metadata, since we want
     // our results to match the bootloader.
-    auto hal = device->boot_control_hal();
+    auto hal = device->boot1_1();
     if (!hal) {
         *message = "not supported";
         return false;
     }
 
-    android::sp<IBootControl1_1> hal11 = IBootControl1_1::castFrom(hal);
-    if (!hal11) {
-        *message = "not supported";
-        return false;
-    }
-
-    MergeStatus status = hal11->getSnapshotMergeStatus();
+    MergeStatus status = hal->getSnapshotMergeStatus();
     switch (status) {
         case MergeStatus::SNAPSHOTTED:
             *message = "snapshotted";
@@ -458,3 +452,9 @@
     }
     return true;
 }
+
+bool GetCpuAbi(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+               std::string* message) {
+    *message = android::base::GetProperty("ro.product.cpu.abi", "");
+    return true;
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 4dec10f..90840d6 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -63,6 +63,7 @@
                            std::string* message);
 bool GetSnapshotUpdateStatus(FastbootDevice* device, const std::vector<std::string>& args,
                              std::string* message);
+bool GetCpuAbi(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
 
 // Helpers for getvar all.
 std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 7ce7c7c..cbd42b1 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -399,6 +399,9 @@
             " snapshot-update cancel     On devices that support snapshot-based updates, cancel\n"
             "                            an in-progress update. This may make the device\n"
             "                            unbootable until it is reflashed.\n"
+            " snapshot-update merge      On devices that support snapshot-based updates, finish\n"
+            "                            an in-progress update if it is in the \"merging\"\n"
+            "                            phase.\n"
             "\n"
             "boot image:\n"
             " boot KERNEL [RAMDISK [SECOND]]\n"
@@ -2089,8 +2092,8 @@
             if (!args.empty()) {
                 arg = next_arg(&args);
             }
-            if (!arg.empty() && arg != "cancel") {
-                syntax_error("expected: snapshot-update [cancel]");
+            if (!arg.empty() && (arg != "cancel" && arg != "merge")) {
+                syntax_error("expected: snapshot-update [cancel|merge]");
             }
             fb->SnapshotUpdateCommand(arg);
         } else {
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 6a5ad20..8d534ea 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -124,8 +124,11 @@
 
 RetCode FastBootDriver::SnapshotUpdateCommand(const std::string& command, std::string* response,
                                               std::vector<std::string>* info) {
+    prolog_(StringPrintf("Snapshot %s", command.c_str()));
     std::string raw = FB_CMD_SNAPSHOT_UPDATE ":" + command;
-    return RawCommand(raw, response, info);
+    auto result = RawCommand(raw, response, info);
+    epilog_(result);
+    return result;
 }
 
 RetCode FastBootDriver::FlashPartition(const std::string& partition,
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index a8059b7..cb69037 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1351,38 +1351,9 @@
     return ret;
 }
 
-static std::string GetUserdataBlockDevice() {
-    Fstab fstab;
-    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
-        LERROR << "Failed to read /proc/mounts";
-        return "";
-    }
-    auto entry = GetEntryForMountPoint(&fstab, "/data");
-    if (entry == nullptr) {
-        LERROR << "Didn't find /data mount point in /proc/mounts";
-        return "";
-    }
-    return entry->blk_device;
-}
-
 int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
-    const std::string& block_device = GetUserdataBlockDevice();
-    LINFO << "Userdata is mounted on " << block_device;
-    auto entry = std::find_if(fstab->begin(), fstab->end(), [&block_device](const FstabEntry& e) {
-        if (e.mount_point != "/data") {
-            return false;
-        }
-        if (e.blk_device == block_device) {
-            return true;
-        }
-        DeviceMapper& dm = DeviceMapper::Instance();
-        std::string path;
-        if (!dm.GetDmDevicePathByName("userdata", &path)) {
-            return false;
-        }
-        return path == block_device;
-    });
-    if (entry == fstab->end()) {
+    auto entry = GetMountedEntryForUserdata(fstab);
+    if (entry == nullptr) {
         LERROR << "Can't find /data in fstab";
         return -1;
     }
@@ -1407,9 +1378,8 @@
             return -1;
         }
     } else {
-        // TODO(b/135984674): support remounting for ext4.
-        LERROR << "Remounting in checkpointing mode is not yet supported for ext4";
-        return -1;
+        // STOPSHIP(b/143970043): support remounting for ext4.
+        LWARNING << "Remounting into checkpointing is not supported for ex4. Proceed with caution";
     }
     return 0;
 }
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index d216458..c81a079 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -40,6 +40,7 @@
 using android::base::ParseByteCount;
 using android::base::ParseInt;
 using android::base::ReadFileToString;
+using android::base::Readlink;
 using android::base::Split;
 using android::base::StartsWith;
 
@@ -809,6 +810,89 @@
     return entries;
 }
 
+static std::string ResolveBlockDevice(const std::string& block_device) {
+    if (!StartsWith(block_device, "/dev/block/")) {
+        LWARNING << block_device << " is not a block device";
+        return block_device;
+    }
+    std::string name = block_device.substr(5);
+    if (!StartsWith(name, "block/dm-")) {
+        // Not a dm-device, but might be a symlink. Optimistically try to readlink.
+        std::string result;
+        if (Readlink(block_device, &result)) {
+            return result;
+        } else if (errno == EINVAL) {
+            // After all, it wasn't a symlink.
+            return block_device;
+        } else {
+            LERROR << "Failed to readlink " << block_device;
+            return "";
+        }
+    }
+    // It's a dm-device, let's find what's inside!
+    std::string sys_dir = "/sys/" + name;
+    while (true) {
+        std::string slaves_dir = sys_dir + "/slaves";
+        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(slaves_dir.c_str()), closedir);
+        if (!dir) {
+            LERROR << "Failed to open " << slaves_dir;
+            return "";
+        }
+        std::string sub_device_name = "";
+        for (auto entry = readdir(dir.get()); entry; entry = readdir(dir.get())) {
+            if (entry->d_type != DT_LNK) continue;
+            if (!sub_device_name.empty()) {
+                LERROR << "Too many slaves in " << slaves_dir;
+                return "";
+            }
+            sub_device_name = entry->d_name;
+        }
+        if (sub_device_name.empty()) {
+            LERROR << "No slaves in " << slaves_dir;
+            return "";
+        }
+        if (!StartsWith(sub_device_name, "dm-")) {
+            // Not a dm-device! We can stop now.
+            return "/dev/block/" + sub_device_name;
+        }
+        // Still a dm-device, keep digging.
+        sys_dir = "/sys/block/" + sub_device_name;
+    }
+}
+
+FstabEntry* GetMountedEntryForUserdata(Fstab* fstab) {
+    Fstab mounts;
+    if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
+        LERROR << "Failed to read /proc/mounts";
+        return nullptr;
+    }
+    auto mounted_entry = GetEntryForMountPoint(&mounts, "/data");
+    if (mounted_entry == nullptr) {
+        LWARNING << "/data is not mounted";
+        return nullptr;
+    }
+    std::string resolved_block_device = ResolveBlockDevice(mounted_entry->blk_device);
+    if (resolved_block_device.empty()) {
+        return nullptr;
+    }
+    LINFO << "/data is mounted on " << resolved_block_device;
+    for (auto& entry : *fstab) {
+        if (entry.mount_point != "/data") {
+            continue;
+        }
+        std::string block_device;
+        if (!Readlink(entry.blk_device, &block_device)) {
+            LWARNING << "Failed to readlink " << entry.blk_device;
+            block_device = entry.blk_device;
+        }
+        if (block_device == resolved_block_device) {
+            return &entry;
+        }
+    }
+    LERROR << "Didn't find entry that was used to mount /data";
+    return nullptr;
+}
+
 std::set<std::string> GetBootDevices() {
     // First check the kernel commandline, then try the device tree otherwise
     std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index c6a16e3..80deaef 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -102,6 +102,7 @@
 FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
 // The Fstab can contain multiple entries for the same mount point with different configurations.
 std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path);
+FstabEntry* GetMountedEntryForUserdata(Fstab* fstab);
 
 // This method builds DSU fstab entries and transfer the fstab.
 //
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 414a186..32702ae 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -123,6 +123,7 @@
 cc_test {
     name: "libfs_avb_device_test",
     test_suites: ["device-tests"],
+    require_root: true,
     static_libs: [
         "libavb",
         "libdm",
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 6b842b3..e4d92ca 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -110,7 +110,7 @@
     return ReadFromImageFile(fd);
 }
 
-bool WriteToImageFile(int fd, const LpMetadata& input) {
+bool WriteToImageFile(borrowed_fd fd, const LpMetadata& input) {
     std::string geometry = SerializeGeometry(input.geometry);
     std::string metadata = SerializeMetadata(input);
 
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
index a284d2e..88e5882 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -29,8 +29,6 @@
 // Helper function to serialize geometry and metadata to a normal file, for
 // flashing or debugging.
 std::unique_ptr<LpMetadata> ReadFromImageFile(int fd);
-bool WriteToImageFile(const char* file, const LpMetadata& metadata);
-bool WriteToImageFile(int fd, const LpMetadata& metadata);
 
 // We use an object to build the image file since it requires that data
 // pointers be held alive until the sparse file is destroyed. It's easier
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index cd860cd..04f8987 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -76,12 +76,15 @@
 // 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. If no partition images are specified, the file will be in the
-// empty format.
+// Read/Write logical partition metadata and contents to an image file, for
+// flashing.
 bool WriteToImageFile(const std::string& file, const LpMetadata& metadata, uint32_t block_size,
                       const std::map<std::string, std::string>& images, bool sparsify);
+
+// Read/Write logical partition metadata to an image file, for producing a
+// super_empty.img (for fastboot wipe-super/update-super) or for diagnostics.
 bool WriteToImageFile(const std::string& file, const LpMetadata& metadata);
+bool WriteToImageFile(android::base::borrowed_fd fd, const LpMetadata& metadata);
 std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file);
 std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes);
 
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 8a983ad..bb24069 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -83,8 +83,9 @@
 
 // Perform sanity checks so we don't accidentally overwrite valid metadata
 // with potentially invalid metadata, or random partition data with metadata.
-static bool ValidateAndSerializeMetadata(const IPartitionOpener& opener, const LpMetadata& metadata,
-                                         const std::string& slot_suffix, std::string* blob) {
+static bool ValidateAndSerializeMetadata([[maybe_unused]] const IPartitionOpener& opener,
+                                         const LpMetadata& metadata, const std::string& slot_suffix,
+                                         std::string* blob) {
     const LpMetadataGeometry& geometry = metadata.geometry;
 
     *blob = SerializeMetadata(metadata);
@@ -128,6 +129,10 @@
                    << block_device.first_logical_sector << " for size " << block_device.size;
             return false;
         }
+
+        // When flashing on the device, check partition sizes. Don't do this on
+        // the host since there is no way to verify.
+#if defined(__ANDROID__)
         BlockDeviceInfo info;
         if (!opener.GetInfo(partition_name, &info)) {
             PERROR << partition_name << ": ioctl";
@@ -138,6 +143,7 @@
                    << block_device.size << ", got " << info.size << ")";
             return false;
         }
+#endif
     }
 
     // Make sure all partition entries reference valid extents.
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 9256a16..1d72c70 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -72,6 +72,7 @@
     name: "libsnapshot_sources",
     srcs: [
         "android/snapshot/snapshot.proto",
+        "device_info.cpp",
         "snapshot.cpp",
         "snapshot_metadata_updater.cpp",
         "partition_cow_creator.cpp",
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
new file mode 100644
index 0000000..bacb41c
--- /dev/null
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -0,0 +1,123 @@
+// 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 "device_info.h"
+
+#include <android-base/logging.h>
+#include <fs_mgr.h>
+#include <fs_mgr_overlayfs.h>
+
+namespace android {
+namespace snapshot {
+
+#ifdef LIBSNAPSHOT_USE_HAL
+using android::hardware::boot::V1_0::CommandResult;
+#endif
+
+using namespace std::string_literals;
+
+#ifdef __ANDROID_RECOVERY__
+constexpr bool kIsRecovery = true;
+#else
+constexpr bool kIsRecovery = false;
+#endif
+
+std::string DeviceInfo::GetGsidDir() const {
+    return "ota"s;
+}
+
+std::string DeviceInfo::GetMetadataDir() const {
+    return "/metadata/ota"s;
+}
+
+std::string DeviceInfo::GetSlotSuffix() const {
+    return fs_mgr_get_slot_suffix();
+}
+
+std::string DeviceInfo::GetOtherSlotSuffix() const {
+    return fs_mgr_get_other_slot_suffix();
+}
+
+const android::fs_mgr::IPartitionOpener& DeviceInfo::GetPartitionOpener() const {
+    return opener_;
+}
+
+std::string DeviceInfo::GetSuperDevice(uint32_t slot) const {
+    return fs_mgr_get_super_partition_name(slot);
+}
+
+bool DeviceInfo::IsOverlayfsSetup() const {
+    return fs_mgr_overlayfs_is_setup();
+}
+
+#ifdef LIBSNAPSHOT_USE_HAL
+bool DeviceInfo::EnsureBootHal() {
+    if (!boot_control_) {
+        auto hal = android::hardware::boot::V1_0::IBootControl::getService();
+        if (!hal) {
+            LOG(ERROR) << "Could not find IBootControl HAL";
+            return false;
+        }
+        boot_control_ = android::hardware::boot::V1_1::IBootControl::castFrom(hal);
+        if (!boot_control_) {
+            LOG(ERROR) << "Could not find IBootControl 1.1 HAL";
+            return false;
+        }
+    }
+    return true;
+}
+#endif
+
+bool DeviceInfo::SetBootControlMergeStatus([[maybe_unused]] MergeStatus status) {
+#ifdef LIBSNAPSHOT_USE_HAL
+    if (!EnsureBootHal()) {
+        return false;
+    }
+    if (!boot_control_->setSnapshotMergeStatus(status)) {
+        LOG(ERROR) << "Unable to set the snapshot merge status";
+        return false;
+    }
+    return true;
+#else
+    LOG(ERROR) << "HAL support not enabled.";
+    return false;
+#endif
+}
+
+bool DeviceInfo::IsRecovery() const {
+    return kIsRecovery;
+}
+
+bool DeviceInfo::SetSlotAsUnbootable([[maybe_unused]] unsigned int slot) {
+#ifdef LIBSNAPSHOT_USE_HAL
+    if (!EnsureBootHal()) {
+        return false;
+    }
+
+    CommandResult result = {};
+    auto cb = [&](CommandResult r) -> void { result = r; };
+    boot_control_->setSlotAsUnbootable(slot, cb);
+    if (!result.success) {
+        LOG(ERROR) << "Error setting slot " << slot << " unbootable: " << result.errMsg;
+        return false;
+    }
+    return true;
+#else
+    LOG(ERROR) << "HAL support not enabled.";
+    return false;
+#endif
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
new file mode 100644
index 0000000..d8d3d91
--- /dev/null
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -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.
+
+#pragma once
+
+#include <string>
+
+#ifdef LIBSNAPSHOT_USE_HAL
+#include <android/hardware/boot/1.1/IBootControl.h>
+#endif
+#include <liblp/partition_opener.h>
+#include <libsnapshot/snapshot.h>
+
+namespace android {
+namespace snapshot {
+
+class DeviceInfo final : public SnapshotManager::IDeviceInfo {
+    using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
+
+  public:
+    std::string GetGsidDir() const override;
+    std::string GetMetadataDir() const override;
+    std::string GetSlotSuffix() const override;
+    std::string GetOtherSlotSuffix() const override;
+    const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override;
+    std::string GetSuperDevice(uint32_t slot) const override;
+    bool IsOverlayfsSetup() const override;
+    bool SetBootControlMergeStatus(MergeStatus status) override;
+    bool SetSlotAsUnbootable(unsigned int slot) override;
+    bool IsRecovery() const override;
+
+  private:
+    bool EnsureBootHal();
+
+    android::fs_mgr::PartitionOpener opener_;
+#ifdef LIBSNAPSHOT_USE_HAL
+    android::sp<android::hardware::boot::V1_1::IBootControl> boot_control_;
+#endif
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h b/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
index d5ceb0e..73450db 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
@@ -27,6 +27,8 @@
     virtual ~AutoDevice(){};
     void Release();
 
+    bool HasDevice() const { return !name_.empty(); }
+
   protected:
     AutoDevice(const std::string& name) : name_(name) {}
     std::string name_;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 431fea1..8e3875f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -72,6 +72,8 @@
 
 static constexpr const std::string_view kCowGroupName = "cow";
 
+bool SourceCopyOperationIsClone(const chromeos_update_engine::InstallOperation& operation);
+
 enum class UpdateState : unsigned int {
     // No update or merge is in progress.
     None,
@@ -122,6 +124,7 @@
         virtual const IPartitionOpener& GetPartitionOpener() const = 0;
         virtual bool IsOverlayfsSetup() const = 0;
         virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
+        virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
         virtual bool IsRecovery() const = 0;
     };
 
@@ -133,7 +136,7 @@
     static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);
 
     // This is similar to New(), except designed specifically for first-stage
-    // init.
+    // init or recovery.
     static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);
 
     // Helper function for first-stage init to check whether a SnapshotManager
@@ -154,6 +157,7 @@
     // rebooting or after rolling back), or merge the OTA.
     bool FinishedSnapshotWrites();
 
+  private:
     // Initiate a merge on all snapshot devices. This should only be used after an
     // update has been marked successful after booting.
     bool InitiateMerge();
@@ -179,7 +183,19 @@
     //
     //   MergeCompleted indicates that the update has fully completed.
     //   GetUpdateState will return None, and a new update can begin.
-    UpdateState ProcessUpdateState();
+    //
+    // The optional callback allows the caller to periodically check the
+    // progress with GetUpdateState().
+    UpdateState ProcessUpdateState(const std::function<void()>& callback = {});
+
+  public:
+    // Initiate the merge if necessary, then wait for the merge to finish.
+    // See InitiateMerge() and ProcessUpdateState() for details.
+    // Returns:
+    //   - None if no merge to initiate
+    //   - MergeCompleted if merge is completed
+    //   - other states indicating an error has occurred
+    UpdateState InitiateMergeAndWait();
 
     // Find the status of the current update, if any.
     //
@@ -209,6 +225,19 @@
     // call to CreateLogicalPartitions when snapshots are present.
     bool CreateLogicalAndSnapshotPartitions(const std::string& super_device);
 
+    // This method should be called preceding any wipe or flash of metadata or
+    // userdata. It is only valid in recovery or fastbootd, and it ensures that
+    // a merge has been completed.
+    //
+    // When userdata will be wiped or flashed, it is necessary to clean up any
+    // snapshot state. If a merge is in progress, the merge must be finished.
+    // If a snapshot is present but not yet merged, the slot must be marked as
+    // unbootable.
+    //
+    // Returns true on success (or nothing to do), false on failure. The
+    // optional callback fires periodically to query progress via GetUpdateState.
+    bool HandleImminentDataWipe(const std::function<void()>& callback = {});
+
     // Dump debug information.
     bool Dump(std::ostream& os);
 
@@ -221,7 +250,7 @@
     // is not found in fstab.
     // Note: if this function is called the second time before the AutoDevice returned from the
     // first call is destroyed, the device will be unmounted when any of these AutoDevices is
-    // destroyed. FOr example:
+    // destroyed. For example:
     //   auto a = mgr->EnsureMetadataMounted(); // mounts
     //   auto b = mgr->EnsureMetadataMounted(); // does nothing
     //   b.reset() // unmounts
@@ -238,12 +267,16 @@
     FRIEND_TEST(SnapshotTest, MapPartialSnapshot);
     FRIEND_TEST(SnapshotTest, MapSnapshot);
     FRIEND_TEST(SnapshotTest, Merge);
-    FRIEND_TEST(SnapshotTest, MergeCannotRemoveCow);
     FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
     FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
+    FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
+    FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
+    FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow);
+    FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery);
     FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
     friend class SnapshotTest;
     friend class SnapshotUpdateTest;
+    friend class FlashAfterUpdateTest;
     friend struct AutoDeleteCowImage;
     friend struct AutoDeleteSnapshot;
     friend struct PartitionCowCreator;
@@ -341,6 +374,9 @@
     // condition was detected and handled.
     bool HandleCancelledUpdate(LockedFile* lock);
 
+    // Helper for HandleCancelledUpdate. Assumes booting from new slot.
+    bool HandleCancelledUpdateOnNewSlot(LockedFile* lock);
+
     // Remove artifacts created by the update process, such as snapshots, and
     // set the update state to None.
     bool RemoveAllUpdateState(LockedFile* lock);
@@ -359,7 +395,19 @@
     bool MarkSnapshotMergeCompleted(LockedFile* snapshot_lock, const std::string& snapshot_name);
     void AcknowledgeMergeSuccess(LockedFile* lock);
     void AcknowledgeMergeFailure();
-    bool IsCancelledSnapshot(const std::string& snapshot_name);
+    std::unique_ptr<LpMetadata> ReadCurrentMetadata();
+
+    enum class MetadataPartitionState {
+        // Partition does not exist.
+        None,
+        // Partition is flashed.
+        Flashed,
+        // Partition is created by OTA client.
+        Updated,
+    };
+    // Helper function to check the state of a partition as described in metadata.
+    MetadataPartitionState GetMetadataPartitionState(const LpMetadata& metadata,
+                                                     const std::string& name);
 
     // Note that these require the name of the device containing the snapshot,
     // which may be the "inner" device. Use GetsnapshotDeviecName().
@@ -438,6 +486,10 @@
             const LpMetadata* exported_target_metadata, const std::string& target_suffix,
             const std::map<std::string, SnapshotStatus>& all_snapshot_status);
 
+    // Unmap all partitions that were mapped by CreateLogicalAndSnapshotPartitions.
+    // This should only be called in recovery.
+    bool UnmapAllPartitions();
+
     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
index 77315b4..61f5c0c 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -62,27 +62,52 @@
     return false;
 }
 
+bool SourceCopyOperationIsClone(const InstallOperation& operation) {
+    using ChromeOSExtent = chromeos_update_engine::Extent;
+    if (operation.src_extents().size() != operation.dst_extents().size()) {
+        return false;
+    }
+    return std::equal(operation.src_extents().begin(), operation.src_extents().end(),
+                      operation.dst_extents().begin(),
+                      [](const ChromeOSExtent& src, const ChromeOSExtent& dst) {
+                          return src.start_block() == dst.start_block() &&
+                                 src.num_blocks() == dst.num_blocks();
+                      });
+}
+
+void WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Extent& de,
+                 unsigned int sectors_per_block) {
+    const auto block_boundary = de.start_block() + de.num_blocks();
+    for (auto b = de.start_block(); b < block_boundary; ++b) {
+        for (unsigned int s = 0; s < sectors_per_block; ++s) {
+            const auto sector_id = b * sectors_per_block + s;
+            sc->WriteSector(sector_id);
+        }
+    }
+}
+
 uint64_t PartitionCowCreator::GetCowSize() {
     // WARNING: The origin partition should be READ-ONLY
     const uint64_t logical_block_size = current_metadata->logical_block_size();
     const unsigned int sectors_per_block = logical_block_size / kSectorSize;
     DmSnapCowSizeCalculator sc(kSectorSize, kSnapshotChunkSize);
 
+    // Allocate space for extra extents (if any). These extents are those that can be
+    // used for error corrections or to store verity hash trees.
+    for (const auto& de : extra_extents) {
+        WriteExtent(&sc, de, sectors_per_block);
+    }
+
     if (operations == nullptr) return sc.cow_size_bytes();
 
     for (const auto& iop : *operations) {
-        for (const auto& de : iop.dst_extents()) {
-            // Skip if no blocks are written
-            if (de.num_blocks() == 0) continue;
+        // Do not allocate space for operations that are going to be skipped
+        // during OTA application.
+        if (iop.type() == InstallOperation::SOURCE_COPY && SourceCopyOperationIsClone(iop))
+            continue;
 
-            // Flag all the blocks that were written
-            const auto block_boundary = de.start_block() + de.num_blocks();
-            for (auto b = de.start_block(); b < block_boundary; ++b) {
-                for (unsigned int s = 0; s < sectors_per_block; ++s) {
-                    const auto sector_id = b * sectors_per_block + s;
-                    sc.WriteSector(sector_id);
-                }
-            }
+        for (const auto& de : iop.dst_extents()) {
+            WriteExtent(&sc, de, sectors_per_block);
         }
     }
 
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.h b/fs_mgr/libsnapshot/partition_cow_creator.h
index d3d186b..699f9a1 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.h
+++ b/fs_mgr/libsnapshot/partition_cow_creator.h
@@ -18,6 +18,7 @@
 
 #include <optional>
 #include <string>
+#include <vector>
 
 #include <liblp/builder.h>
 #include <update_engine/update_metadata.pb.h>
@@ -30,6 +31,7 @@
 // Helper class that creates COW for a partition.
 struct PartitionCowCreator {
     using Extent = android::fs_mgr::Extent;
+    using ChromeOSExtent = chromeos_update_engine::Extent;
     using Interval = android::fs_mgr::Interval;
     using MetadataBuilder = android::fs_mgr::MetadataBuilder;
     using Partition = android::fs_mgr::Partition;
@@ -50,6 +52,9 @@
     std::string current_suffix;
     // List of operations to be applied on the partition.
     const RepeatedPtrField<InstallOperation>* operations = nullptr;
+    // Extra extents that are going to be invalidated during the update
+    // process.
+    std::vector<ChromeOSExtent> extra_extents = {};
 
     struct Return {
         SnapshotStatus snapshot_status;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 63d97d0..33c122b 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -29,19 +29,16 @@
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#ifdef LIBSNAPSHOT_USE_HAL
-#include <android/hardware/boot/1.1/IBootControl.h>
-#endif
 #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 <android/snapshot/snapshot.pb.h>
+#include "device_info.h"
 #include "partition_cow_creator.h"
 #include "snapshot_metadata_updater.h"
 #include "utility.h"
@@ -68,6 +65,7 @@
 using android::fs_mgr::SlotNumberForSlotSuffix;
 using android::hardware::boot::V1_1::MergeStatus;
 using chromeos_update_engine::DeltaArchiveManifest;
+using chromeos_update_engine::Extent;
 using chromeos_update_engine::InstallOperation;
 template <typename T>
 using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;
@@ -77,57 +75,6 @@
 
 static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
 
-#ifdef __ANDROID_RECOVERY__
-constexpr bool kIsRecovery = true;
-#else
-constexpr bool kIsRecovery = false;
-#endif
-
-class DeviceInfo final : public SnapshotManager::IDeviceInfo {
-  public:
-    std::string GetGsidDir() const override { return "ota"s; }
-    std::string GetMetadataDir() const override { return "/metadata/ota"s; }
-    std::string GetSlotSuffix() const override { return fs_mgr_get_slot_suffix(); }
-    std::string GetOtherSlotSuffix() const override { return fs_mgr_get_other_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(); }
-    bool SetBootControlMergeStatus(MergeStatus status) override;
-    bool IsRecovery() const override { return kIsRecovery; }
-
-  private:
-    android::fs_mgr::PartitionOpener opener_;
-#ifdef LIBSNAPSHOT_USE_HAL
-    android::sp<android::hardware::boot::V1_1::IBootControl> boot_control_;
-#endif
-};
-
-bool DeviceInfo::SetBootControlMergeStatus([[maybe_unused]] MergeStatus status) {
-#ifdef LIBSNAPSHOT_USE_HAL
-    if (!boot_control_) {
-        auto hal = android::hardware::boot::V1_0::IBootControl::getService();
-        if (!hal) {
-            LOG(ERROR) << "Could not find IBootControl HAL";
-            return false;
-        }
-        boot_control_ = android::hardware::boot::V1_1::IBootControl::castFrom(hal);
-        if (!boot_control_) {
-            LOG(ERROR) << "Could not find IBootControl 1.1 HAL";
-            return false;
-        }
-    }
-    if (!boot_control_->setSnapshotMergeStatus(status)) {
-        LOG(ERROR) << "Unable to set the snapshot merge status";
-        return false;
-    }
-    return true;
-#else
-    return false;
-#endif
-}
-
 // Note: IImageManager is an incomplete type in the header, so the default
 // destructor doesn't work.
 SnapshotManager::~SnapshotManager() {}
@@ -182,6 +129,11 @@
     auto file = LockExclusive();
     if (!file) return false;
 
+    // Purge the ImageManager just in case there is a corrupt lp_metadata file
+    // lying around. (NB: no need to return false on an error, we can let the
+    // update try to progress.)
+    images_->RemoveAllImages();
+
     auto state = ReadUpdateState(file.get());
     if (state != UpdateState::None) {
         LOG(ERROR) << "An update is already in progress, cannot begin a new update";
@@ -354,7 +306,7 @@
     if (!ReadSnapshotStatus(lock, name, &status)) {
         return false;
     }
-    if (status.state() == SnapshotState::MERGE_COMPLETED) {
+    if (status.state() == SnapshotState::NONE || status.state() == SnapshotState::MERGE_COMPLETED) {
         LOG(ERROR) << "Should not create a snapshot device for " << name
                    << " after merging has completed.";
         return false;
@@ -512,6 +464,14 @@
         return false;
     }
 
+    // We can't delete snapshots in recovery. The only way we'd try is it we're
+    // completing or canceling a merge in preparation for a data wipe, in which
+    // case, we don't care if the file sticks around.
+    if (device_->IsRecovery()) {
+        LOG(INFO) << "Skipping delete of snapshot " << name << " in recovery.";
+        return true;
+    }
+
     auto cow_image_name = GetCowImageDeviceName(name);
     if (images_->BackingImageExists(cow_image_name)) {
         if (!images_->DeleteBackingImage(cow_image_name)) {
@@ -568,6 +528,27 @@
         }
     }
 
+    auto metadata = ReadCurrentMetadata();
+    for (auto it = snapshots.begin(); it != snapshots.end();) {
+        switch (GetMetadataPartitionState(*metadata, *it)) {
+            case MetadataPartitionState::Flashed:
+                LOG(WARNING) << "Detected re-flashing for partition " << *it
+                             << ". Skip merging it.";
+                [[fallthrough]];
+            case MetadataPartitionState::None: {
+                LOG(WARNING) << "Deleting snapshot for partition " << *it;
+                if (!DeleteSnapshot(lock.get(), *it)) {
+                    LOG(WARNING) << "Cannot delete snapshot for partition " << *it
+                                 << ". Skip merging it anyways.";
+                }
+                it = snapshots.erase(it);
+            } break;
+            case MetadataPartitionState::Updated: {
+                ++it;
+            } break;
+        }
+    }
+
     // Point of no return - mark that we're starting a merge. From now on every
     // snapshot must be a merge target.
     if (!WriteUpdateState(lock.get(), UpdateState::Merging)) {
@@ -723,7 +704,7 @@
 // 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::ProcessUpdateState() {
+UpdateState SnapshotManager::ProcessUpdateState(const std::function<void()>& callback) {
     while (true) {
         UpdateState state = CheckMergeState();
         if (state == UpdateState::MergeFailed) {
@@ -735,6 +716,10 @@
             return state;
         }
 
+        if (callback) {
+            callback();
+        }
+
         // This wait is not super time sensitive, so we have a relatively
         // low polling frequency.
         std::this_thread::sleep_for(2s);
@@ -855,8 +840,15 @@
 
     std::string dm_name = GetSnapshotDeviceName(name, snapshot_status);
 
+    std::unique_ptr<LpMetadata> current_metadata;
+
     if (!IsSnapshotDevice(dm_name)) {
-        if (IsCancelledSnapshot(name)) {
+        if (!current_metadata) {
+            current_metadata = ReadCurrentMetadata();
+        }
+
+        if (!current_metadata ||
+            GetMetadataPartitionState(*current_metadata, name) != MetadataPartitionState::Updated) {
             DeleteSnapshot(lock, name);
             return UpdateState::Cancelled;
         }
@@ -877,7 +869,8 @@
     }
 
     // This check is expensive so it is only enabled for debugging.
-    DCHECK(!IsCancelledSnapshot(name));
+    DCHECK((current_metadata = ReadCurrentMetadata()) &&
+           GetMetadataPartitionState(*current_metadata, name) == MetadataPartitionState::Updated);
 
     std::string target_type;
     DmTargetSnapshot::Status status;
@@ -1106,13 +1099,17 @@
     if (device_->GetSlotSuffix() != old_slot) {
         // We're booted into the target slot, which means we just rebooted
         // after applying the update.
-        return false;
+        if (!HandleCancelledUpdateOnNewSlot(lock)) {
+            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.
+    //  (4) The device updates to the new slot but re-flashed *all* partitions
+    //      in the new slot.
     //
     // In any case, delete the snapshots. It may be worth using the boot_control
     // HAL to differentiate case (2).
@@ -1120,18 +1117,66 @@
     return true;
 }
 
-bool SnapshotManager::IsCancelledSnapshot(const std::string& snapshot_name) {
+std::unique_ptr<LpMetadata> SnapshotManager::ReadCurrentMetadata() {
     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;
+        return nullptr;
     }
-    auto partition = android::fs_mgr::FindPartition(*metadata.get(), snapshot_name);
-    if (!partition) return false;
-    return (partition->attributes & LP_PARTITION_ATTR_UPDATED) == 0;
+    return metadata;
+}
+
+SnapshotManager::MetadataPartitionState SnapshotManager::GetMetadataPartitionState(
+        const LpMetadata& metadata, const std::string& name) {
+    auto partition = android::fs_mgr::FindPartition(metadata, name);
+    if (!partition) return MetadataPartitionState::None;
+    if (partition->attributes & LP_PARTITION_ATTR_UPDATED) {
+        return MetadataPartitionState::Updated;
+    }
+    return MetadataPartitionState::Flashed;
+}
+
+bool SnapshotManager::HandleCancelledUpdateOnNewSlot(LockedFile* lock) {
+    std::vector<std::string> snapshots;
+    if (!ListSnapshots(lock, &snapshots)) {
+        LOG(WARNING) << "Failed to list snapshots to determine whether device has been flashed "
+                     << "after applying an update. Assuming no snapshots.";
+        // Let HandleCancelledUpdate resets UpdateState.
+        return true;
+    }
+
+    // Attempt to detect re-flashing on each partition.
+    // - If all partitions are re-flashed, we can proceed to cancel the whole update.
+    // - If only some of the partitions are re-flashed, snapshots for re-flashed partitions are
+    //   deleted. Caller is responsible for merging the rest of the snapshots.
+    // - If none of the partitions are re-flashed, caller is responsible for merging the snapshots.
+    auto metadata = ReadCurrentMetadata();
+    if (!metadata) return false;
+    bool all_snapshot_cancelled = true;
+    for (const auto& snapshot_name : snapshots) {
+        if (GetMetadataPartitionState(*metadata, snapshot_name) ==
+            MetadataPartitionState::Updated) {
+            LOG(WARNING) << "Cannot cancel update because snapshot" << snapshot_name
+                         << " is in use.";
+            all_snapshot_cancelled = false;
+            continue;
+        }
+        // Delete snapshots for partitions that are re-flashed after the update.
+        LOG(INFO) << "Detected re-flashing of partition " << snapshot_name << ".";
+        if (!DeleteSnapshot(lock, snapshot_name)) {
+            // This is an error, but it is okay to leave the snapshot in the short term.
+            // However, if all_snapshot_cancelled == false after exiting the loop, caller may
+            // initiate merge for this unused snapshot, which is likely to fail.
+            LOG(WARNING) << "Failed to delete snapshot for re-flashed partition " << snapshot_name;
+        }
+    }
+    if (!all_snapshot_cancelled) return false;
+
+    LOG(INFO) << "All partitions are re-flashed after update, removing all update states.";
+    return true;
 }
 
 bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
@@ -1142,8 +1187,26 @@
     }
 
     bool ok = true;
+    bool has_mapped_cow_images = false;
     for (const auto& name : snapshots) {
-        ok &= (UnmapPartitionWithSnapshot(lock, name) && DeleteSnapshot(lock, name));
+        if (!UnmapPartitionWithSnapshot(lock, name) || !DeleteSnapshot(lock, name)) {
+            // Remember whether or not we were able to unmap the cow image.
+            auto cow_image_device = GetCowImageDeviceName(name);
+            has_mapped_cow_images |= images_->IsImageMapped(cow_image_device);
+
+            ok = false;
+        }
+    }
+
+    if (ok || !has_mapped_cow_images) {
+        // Delete any image artifacts as a precaution, in case an update is
+        // being cancelled due to some corrupted state in an lp_metadata file.
+        // Note that we do not do this if some cow images are still mapped,
+        // since we must not remove backing storage if it's in use.
+        if (!images_->RemoveAllImages()) {
+            LOG(ERROR) << "Could not remove all snapshot artifacts";
+            return false;
+        }
     }
     return ok;
 }
@@ -1337,6 +1400,17 @@
         if (live_snapshot_status->state() == SnapshotState::MERGE_COMPLETED) {
             live_snapshot_status.reset();
         }
+
+        if (live_snapshot_status->state() == SnapshotState::NONE ||
+            live_snapshot_status->cow_partition_size() + live_snapshot_status->cow_file_size() ==
+                    0) {
+            LOG(WARNING) << "Snapshot status for " << params.GetPartitionName()
+                         << " is invalid, ignoring: state = "
+                         << SnapshotState_Name(live_snapshot_status->state())
+                         << ", cow_partition_size = " << live_snapshot_status->cow_partition_size()
+                         << ", cow_file_size = " << live_snapshot_status->cow_file_size();
+            live_snapshot_status.reset();
+        }
     } while (0);
 
     if (live_snapshot_status.has_value()) {
@@ -1624,10 +1698,6 @@
     if (contents.empty()) return false;
 
     if (!Truncate(file)) return false;
-    if (!android::base::WriteStringToFd(contents, file->fd())) {
-        PLOG(ERROR) << "Could not write to state file";
-        return false;
-    }
 
 #ifdef LIBSNAPSHOT_USE_HAL
     auto merge_status = MergeStatus::UNKNOWN;
@@ -1637,9 +1707,9 @@
         case UpdateState::None:
         case UpdateState::MergeNeedsReboot:
         case UpdateState::MergeCompleted:
+        case UpdateState::Initiated:
             merge_status = MergeStatus::NONE;
             break;
-        case UpdateState::Initiated:
         case UpdateState::Unverified:
             merge_status = MergeStatus::SNAPSHOTTED;
             break;
@@ -1653,7 +1723,21 @@
             LOG(ERROR) << "Unexpected update status: " << state;
             break;
     }
-    if (!device_->SetBootControlMergeStatus(merge_status)) {
+
+    bool set_before_write =
+            merge_status == MergeStatus::SNAPSHOTTED || merge_status == MergeStatus::MERGING;
+    if (set_before_write && !device_->SetBootControlMergeStatus(merge_status)) {
+        return false;
+    }
+#endif
+
+    if (!android::base::WriteStringToFd(contents, file->fd())) {
+        PLOG(ERROR) << "Could not write to state file";
+        return false;
+    }
+
+#ifdef LIBSNAPSHOT_USE_HAL
+    if (!set_before_write && !device_->SetBootControlMergeStatus(merge_status)) {
         return false;
     }
 #endif
@@ -1826,12 +1910,15 @@
     // these devices.
     AutoDeviceList created_devices;
 
-    PartitionCowCreator cow_creator{.target_metadata = target_metadata.get(),
-                                    .target_suffix = target_suffix,
-                                    .target_partition = nullptr,
-                                    .current_metadata = current_metadata.get(),
-                                    .current_suffix = current_suffix,
-                                    .operations = nullptr};
+    PartitionCowCreator cow_creator{
+            .target_metadata = target_metadata.get(),
+            .target_suffix = target_suffix,
+            .target_partition = nullptr,
+            .current_metadata = current_metadata.get(),
+            .current_suffix = current_suffix,
+            .operations = nullptr,
+            .extra_extents = {},
+    };
 
     if (!CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
                                        &all_snapshot_status)) {
@@ -1877,15 +1964,24 @@
     }
 
     std::map<std::string, const RepeatedPtrField<InstallOperation>*> install_operation_map;
+    std::map<std::string, std::vector<Extent>> extra_extents_map;
     for (const auto& partition_update : manifest.partitions()) {
         auto suffixed_name = partition_update.partition_name() + target_suffix;
-        auto&& [it, inserted] = install_operation_map.emplace(std::move(suffixed_name),
-                                                              &partition_update.operations());
+        auto&& [it, inserted] =
+                install_operation_map.emplace(suffixed_name, &partition_update.operations());
         if (!inserted) {
             LOG(ERROR) << "Duplicated partition " << partition_update.partition_name()
                        << " in update manifest.";
             return false;
         }
+
+        auto& extra_extents = extra_extents_map[suffixed_name];
+        if (partition_update.has_hash_tree_extent()) {
+            extra_extents.push_back(partition_update.hash_tree_extent());
+        }
+        if (partition_update.has_fec_extent()) {
+            extra_extents.push_back(partition_update.fec_extent());
+        }
     }
 
     for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {
@@ -1896,6 +1992,12 @@
             cow_creator->operations = operations_it->second;
         }
 
+        cow_creator->extra_extents.clear();
+        auto extra_extents_it = extra_extents_map.find(target_partition->name());
+        if (extra_extents_it != extra_extents_map.end()) {
+            cow_creator->extra_extents = std::move(extra_extents_it->second);
+        }
+
         // Compute the device sizes for the partition.
         auto cow_creator_ret = cow_creator->Run();
         if (!cow_creator_ret.has_value()) {
@@ -2038,6 +2140,27 @@
     return UnmapPartitionWithSnapshot(lock.get(), target_partition_name);
 }
 
+bool SnapshotManager::UnmapAllPartitions() {
+    auto lock = LockExclusive();
+    if (!lock) return false;
+
+    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;
+    }
+
+    bool ok = true;
+    for (const auto& partition : metadata->partitions) {
+        auto partition_name = GetPartitionName(partition);
+        ok &= UnmapPartitionWithSnapshot(lock.get(), partition_name);
+    }
+    return ok;
+}
+
 bool SnapshotManager::Dump(std::ostream& os) {
     // Don't actually lock. Dump() is for debugging purposes only, so it is okay
     // if it is racy.
@@ -2089,5 +2212,106 @@
     return AutoUnmountDevice::New(device_->GetMetadataDir());
 }
 
+UpdateState SnapshotManager::InitiateMergeAndWait() {
+    {
+        auto lock = LockExclusive();
+        // Sync update state from file with bootloader.
+        if (!WriteUpdateState(lock.get(), ReadUpdateState(lock.get()))) {
+            LOG(WARNING) << "Unable to sync write update state, fastboot may "
+                         << "reject / accept wipes incorrectly!";
+        }
+    }
+
+    LOG(INFO) << "Waiting for any previous merge request to complete. "
+              << "This can take up to several minutes.";
+    auto state = ProcessUpdateState();
+    if (state == UpdateState::None) {
+        LOG(INFO) << "Can't find any snapshot to merge.";
+        return state;
+    }
+    if (state == UpdateState::Unverified) {
+        if (!InitiateMerge()) {
+            LOG(ERROR) << "Failed to initiate merge.";
+            return state;
+        }
+        // All other states can be handled by ProcessUpdateState.
+        LOG(INFO) << "Waiting for merge to complete. This can take up to several minutes.";
+        state = ProcessUpdateState();
+    }
+
+    LOG(INFO) << "Merge finished with state \"" << state << "\".";
+    return state;
+}
+
+bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callback) {
+    if (!device_->IsRecovery()) {
+        LOG(ERROR) << "Data wipes are only allowed in recovery.";
+        return false;
+    }
+
+    auto mount = EnsureMetadataMounted();
+    if (!mount || !mount->HasDevice()) {
+        // We allow the wipe to continue, because if we can't mount /metadata,
+        // it is unlikely the device would have booted anyway. If there is no
+        // metadata partition, then the device predates Virtual A/B.
+        return true;
+    }
+
+    // Check this early, so we don't accidentally start trying to populate
+    // the state file in recovery. Note we don't call GetUpdateState since
+    // we want errors in acquiring the lock to be propagated, instead of
+    // returning UpdateState::None.
+    auto state_file = GetStateFilePath();
+    if (access(state_file.c_str(), F_OK) != 0 && errno == ENOENT) {
+        return true;
+    }
+
+    auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+    auto super_path = device_->GetSuperDevice(slot_number);
+    if (!CreateLogicalAndSnapshotPartitions(super_path)) {
+        LOG(ERROR) << "Unable to map partitions to complete merge.";
+        return false;
+    }
+
+    UpdateState state = ProcessUpdateState(callback);
+    LOG(INFO) << "Update state in recovery: " << state;
+    switch (state) {
+        case UpdateState::MergeFailed:
+            LOG(ERROR) << "Unrecoverable merge failure detected.";
+            return false;
+        case UpdateState::Unverified: {
+            // If an OTA was just applied but has not yet started merging, we
+            // have no choice but to revert slots, because the current slot will
+            // immediately become unbootable. Rather than wait for the device
+            // to reboot N times until a rollback, we proactively disable the
+            // new slot instead.
+            //
+            // Since the rollback is inevitable, we don't treat a HAL failure
+            // as an error here.
+            std::string old_slot;
+            auto boot_file = GetSnapshotBootIndicatorPath();
+            if (android::base::ReadFileToString(boot_file, &old_slot) &&
+                device_->GetSlotSuffix() != old_slot) {
+                LOG(ERROR) << "Reverting to slot " << old_slot << " since update will be deleted.";
+                device_->SetSlotAsUnbootable(slot_number);
+            }
+            break;
+        }
+        case UpdateState::MergeNeedsReboot:
+            // We shouldn't get here, because nothing is depending on
+            // logical partitions.
+            LOG(ERROR) << "Unexpected merge-needs-reboot state in recovery.";
+            break;
+        default:
+            break;
+    }
+
+    // Nothing should be depending on partitions now, so unmap them all.
+    if (!UnmapAllPartitions()) {
+        LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
+    }
+    return true;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
index 7d96a67..4fd8759 100644
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
@@ -325,7 +325,7 @@
     EXPECT_TRUE(CheckGroupName("product", "another_group"));
 }
 
-INSTANTIATE_TEST_SUITE_P(, SnapshotMetadataUpdaterTest, testing::Values(0, 1));
+INSTANTIATE_TEST_SUITE_P(Snapshot, SnapshotMetadataUpdaterTest, testing::Values(0, 1));
 
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 5728582..1d2a1f1 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -23,6 +23,7 @@
 #include <iostream>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
@@ -56,6 +57,7 @@
 using android::fs_mgr::GetPartitionName;
 using android::fs_mgr::Interval;
 using android::fs_mgr::MetadataBuilder;
+using android::fs_mgr::SlotSuffixForSlotNumber;
 using chromeos_update_engine::DeltaArchiveManifest;
 using chromeos_update_engine::DynamicPartitionGroup;
 using chromeos_update_engine::PartitionUpdate;
@@ -69,6 +71,8 @@
 TestDeviceInfo* test_device = nullptr;
 std::string fake_super;
 
+void MountMetadata();
+
 class SnapshotTest : public ::testing::Test {
   public:
     SnapshotTest() : dm_(DeviceMapper::Instance()) {}
@@ -85,7 +89,7 @@
         InitializeState();
         CleanupTestArtifacts();
         FormatFakeSuper();
-
+        MountMetadata();
         ASSERT_TRUE(sm->BeginUpdate());
     }
 
@@ -445,61 +449,6 @@
     ASSERT_EQ(test_string, buffer);
 }
 
-TEST_F(SnapshotTest, MergeCannotRemoveCow) {
-    ASSERT_TRUE(AcquireLock());
-
-    static const uint64_t kDeviceSize = 1024 * 1024;
-    SnapshotStatus status;
-    status.set_name("test-snapshot");
-    status.set_device_size(kDeviceSize);
-    status.set_snapshot_size(kDeviceSize);
-    status.set_cow_file_size(kDeviceSize);
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
-    ASSERT_TRUE(CreateCowImage("test-snapshot"));
-
-    std::string base_device, cow_device, snap_device;
-    ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_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-img", "");
-    unique_fd fd(open(cow_path.c_str(), O_RDONLY | O_CLOEXEC));
-    ASSERT_GE(fd, 0);
-
-    // Release the lock.
-    lock_ = nullptr;
-
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-
-    test_device->set_slot_suffix("_b");
-    ASSERT_TRUE(sm->InitiateMerge());
-
-    // COW cannot be removed due to open fd, so expect a soft failure.
-    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.
-    ASSERT_TRUE(DeleteSnapshotDevice("test-snapshot"));
-
-    // Map snapshot should fail now, because we're in a merge-complete state.
-    ASSERT_TRUE(AcquireLock());
-    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->ProcessUpdateState(), UpdateState::MergeCompleted);
-}
-
 TEST_F(SnapshotTest, FirstStageMountAndMerge) {
     ASSERT_TRUE(AcquireLock());
 
@@ -628,7 +577,7 @@
     ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);
 
     ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Initiated));
-    ASSERT_EQ(test_device->merge_status(), MergeStatus::SNAPSHOTTED);
+    ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);
 
     ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Unverified));
     ASSERT_EQ(test_device->merge_status(), MergeStatus::SNAPSHOTTED);
@@ -680,9 +629,9 @@
         // Initialize source partition metadata using |manifest_|.
         src_ = MetadataBuilder::New(*opener_, "super", 0);
         ASSERT_TRUE(FillFakeMetadata(src_.get(), manifest_, "_a"));
-        ASSERT_NE(nullptr, src_);
         // Add sys_b which is like system_other.
-        auto partition = src_->AddPartition("sys_b", 0);
+        ASSERT_TRUE(src_->AddGroup("group_b", kGroupSize));
+        auto partition = src_->AddPartition("sys_b", "group_b", 0);
         ASSERT_NE(nullptr, partition);
         ASSERT_TRUE(src_->ResizePartition(partition, 1_MiB));
         auto metadata = src_->Export();
@@ -715,6 +664,7 @@
         if (!image_manager_) {
             InitializeState();
         }
+        MountMetadata();
         for (const auto& suffix : {"_a", "_b"}) {
             test_device->set_slot_suffix(suffix);
             EXPECT_TRUE(sm->CancelUpdate()) << suffix;
@@ -731,8 +681,12 @@
         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";
+        auto it = hashes_.find(name);
+        if (it == hashes_.end()) {
+            return AssertionFailure() << "No existing hash for " << name << ". Bad test code?";
+        }
+        if (it->second != *hash) {
+            return AssertionFailure() << "Content of " << name << " has changed";
         }
         return AssertionSuccess();
     }
@@ -762,6 +716,45 @@
         return AssertionSuccess();
     }
 
+    AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path = nullptr) {
+        std::string real_path;
+        if (!sm->MapUpdateSnapshot(
+                    CreateLogicalPartitionParams{
+                            .block_device = fake_super,
+                            .metadata_slot = 1,
+                            .partition_name = name,
+                            .timeout_ms = 10s,
+                            .partition_opener = opener_.get(),
+                    },
+                    &real_path)) {
+            return AssertionFailure() << "Unable to map snapshot " << name;
+        }
+        if (path) {
+            *path = real_path;
+        }
+        return AssertionSuccess() << "Mapped snapshot " << name << " to " << real_path;
+    }
+
+    AssertionResult WriteSnapshotAndHash(const std::string& name,
+                                         std::optional<size_t> size = std::nullopt) {
+        std::string path;
+        auto res = MapUpdateSnapshot(name, &path);
+        if (!res) {
+            return res;
+        }
+
+        std::string size_string = size ? (std::to_string(*size) + " bytes") : "";
+
+        if (!WriteRandomData(path, size, &hashes_[name])) {
+            return AssertionFailure() << "Unable to write " << size_string << " to " << path
+                                      << " for partition " << name;
+        }
+
+        return AssertionSuccess() << "Written " << size_string << " to " << path
+                                  << " for snapshot partition " << name
+                                  << ", hash: " << hashes_[name];
+    }
+
     std::unique_ptr<TestPartitionOpener> opener_;
     DeltaArchiveManifest manifest_;
     std::unique_ptr<MetadataBuilder> src_;
@@ -808,21 +801,7 @@
 
     // 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_TRUE(WriteSnapshotAndHash(name, partition_size));
     }
 
     // Assert that source partitions aren't affected.
@@ -847,8 +826,7 @@
     }
 
     // Initiate the merge and wait for it to be completed.
-    ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
+    ASSERT_EQ(UpdateState::MergeCompleted, init->InitiateMergeAndWait());
 
     // Check that the target partitions have the same content after the merge.
     for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
@@ -937,17 +915,7 @@
 
     // 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;
+        EXPECT_TRUE(MapUpdateSnapshot(name));
     }
 }
 
@@ -968,21 +936,7 @@
 
     // 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_TRUE(WriteSnapshotAndHash(name));
     }
 
     // Assert that source partitions aren't affected.
@@ -1052,8 +1006,7 @@
 
     // Initiate the merge and wait for it to be completed.
     auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
-    ASSERT_TRUE(new_sm->InitiateMerge());
-    ASSERT_EQ(UpdateState::MergeCompleted, new_sm->ProcessUpdateState());
+    ASSERT_EQ(UpdateState::MergeCompleted, new_sm->InitiateMergeAndWait());
 
     // Execute the second update.
     ASSERT_TRUE(new_sm->BeginUpdate());
@@ -1137,21 +1090,7 @@
 
     // 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_TRUE(WriteSnapshotAndHash(name));
     }
 
     // Assert that source partitions aren't affected.
@@ -1162,6 +1101,67 @@
     ASSERT_TRUE(sm->FinishedSnapshotWrites());
 }
 
+TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) {
+    // Make source partitions as big as possible to force COW image to be created.
+    SetSize(sys_, 5_MiB);
+    SetSize(vnd_, 5_MiB);
+    SetSize(prd_, 5_MiB);
+    src_ = MetadataBuilder::New(*opener_, "super", 0);
+    src_->RemoveGroupAndPartitions(group_->name() + "_a");
+    src_->RemoveGroupAndPartitions(group_->name() + "_b");
+    ASSERT_TRUE(FillFakeMetadata(src_.get(), manifest_, "_a"));
+    auto metadata = src_->Export();
+    ASSERT_NE(nullptr, metadata);
+    ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *metadata.get(), 0));
+
+    // 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));
+    }
+
+    // Add operations for sys. The whole device is written.
+    auto e = sys_->add_operations()->add_dst_extents();
+    e->set_start_block(0);
+    e->set_num_blocks(GetSize(sys_) / manifest_.block_size());
+
+    // Execute the update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // After reboot, init does first stage mount.
+    // Normally we should use NewForFirstStageMount, but if so, "gsid.mapped_image.sys_b-cow-img"
+    // won't be set.
+    auto init = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+    // 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.sys_b-cow-img", "");
+    unique_fd fd(open(cow_path.c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_GE(fd, 0);
+
+    // COW cannot be removed due to open fd, so expect a soft failure.
+    ASSERT_EQ(UpdateState::MergeNeedsReboot, init->InitiateMergeAndWait());
+
+    // Simulate shutting down the device.
+    fd.reset();
+    ASSERT_TRUE(UnmapAll());
+
+    // init does first stage mount again.
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+    // sys_b should be mapped as a dm-linear device directly.
+    ASSERT_FALSE(sm->IsSnapshotDevice("sys_b", nullptr));
+
+    // Merge should be able to complete now.
+    ASSERT_EQ(UpdateState::MergeCompleted, init->InitiateMergeAndWait());
+}
+
 class MetadataMountedTest : public SnapshotUpdateTest {
   public:
     void SetUp() override {
@@ -1196,6 +1196,10 @@
     Fstab fstab_;
 };
 
+void MountMetadata() {
+    MetadataMountedTest().TearDown();
+}
+
 TEST_F(MetadataMountedTest, Android) {
     auto device = sm->EnsureMetadataMounted();
     EXPECT_NE(nullptr, device);
@@ -1220,6 +1224,247 @@
     EXPECT_FALSE(IsMetadataMounted());
 }
 
+// Test that during a merge, we can wipe data in recovery.
+TEST_F(SnapshotUpdateTest, MergeInRecovery) {
+    // Execute the first update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // After reboot, init does first stage mount.
+    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    init = nullptr;
+
+    // Initiate the merge and then immediately stop it to simulate a reboot.
+    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+    ASSERT_TRUE(new_sm->InitiateMerge());
+    ASSERT_TRUE(UnmapAll());
+
+    // Simulate a reboot into recovery.
+    auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+    test_device->set_recovery(true);
+    new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+
+    ASSERT_TRUE(new_sm->HandleImminentDataWipe());
+    ASSERT_EQ(new_sm->GetUpdateState(), UpdateState::None);
+}
+
+// Test that after an OTA, before a merge, we can wipe data in recovery.
+TEST_F(SnapshotUpdateTest, DataWipeRollbackInRecovery) {
+    // Execute the first update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // Simulate a reboot into recovery.
+    auto test_device = new TestDeviceInfo(fake_super, "_b");
+    test_device->set_recovery(true);
+    auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
+
+    ASSERT_TRUE(new_sm->HandleImminentDataWipe());
+    // Manually mount metadata so that we can call GetUpdateState() below.
+    MountMetadata();
+    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
+    EXPECT_TRUE(test_device->IsSlotUnbootable(1));
+    EXPECT_FALSE(test_device->IsSlotUnbootable(0));
+}
+
+// Test that after an OTA and a bootloader rollback with no merge, we can wipe
+// data in recovery.
+TEST_F(SnapshotUpdateTest, DataWipeAfterRollback) {
+    // Execute the first update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // Simulate a rollback, with reboot into recovery.
+    auto test_device = new TestDeviceInfo(fake_super, "_a");
+    test_device->set_recovery(true);
+    auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
+
+    ASSERT_TRUE(new_sm->HandleImminentDataWipe());
+    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
+    EXPECT_FALSE(test_device->IsSlotUnbootable(0));
+    EXPECT_FALSE(test_device->IsSlotUnbootable(0));
+}
+
+TEST_F(SnapshotUpdateTest, Hashtree) {
+    constexpr auto partition_size = 4_MiB;
+    constexpr auto data_size = 3_MiB;
+    constexpr auto hashtree_size = 512_KiB;
+    constexpr auto fec_size = partition_size - data_size - hashtree_size;
+
+    const auto block_size = manifest_.block_size();
+    SetSize(sys_, partition_size);
+
+    auto e = sys_->add_operations()->add_dst_extents();
+    e->set_start_block(0);
+    e->set_num_blocks(data_size / block_size);
+
+    // Set hastree extents.
+    sys_->mutable_hash_tree_data_extent()->set_start_block(0);
+    sys_->mutable_hash_tree_data_extent()->set_num_blocks(data_size / block_size);
+
+    sys_->mutable_hash_tree_extent()->set_start_block(data_size / block_size);
+    sys_->mutable_hash_tree_extent()->set_num_blocks(hashtree_size / block_size);
+
+    // Set FEC extents.
+    sys_->mutable_fec_data_extent()->set_start_block(0);
+    sys_->mutable_fec_data_extent()->set_num_blocks((data_size + hashtree_size) / block_size);
+
+    sys_->mutable_fec_extent()->set_start_block((data_size + hashtree_size) / block_size);
+    sys_->mutable_fec_extent()->set_num_blocks(fec_size / block_size);
+
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+    // Write some data to target partition.
+    ASSERT_TRUE(WriteSnapshotAndHash("sys_b", partition_size));
+
+    // Finish update.
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // After reboot, init does first stage mount.
+    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+    // Check that the target partition have the same content. Hashtree and FEC extents
+    // should be accounted for.
+    ASSERT_TRUE(IsPartitionUnchanged("sys_b"));
+}
+
+class FlashAfterUpdateTest : public SnapshotUpdateTest,
+                             public WithParamInterface<std::tuple<uint32_t, bool>> {
+  public:
+    AssertionResult InitiateMerge(const std::string& slot_suffix) {
+        auto sm = SnapshotManager::New(new TestDeviceInfo(fake_super, slot_suffix));
+        if (!sm->CreateLogicalAndSnapshotPartitions("super")) {
+            return AssertionFailure() << "Cannot CreateLogicalAndSnapshotPartitions";
+        }
+        if (!sm->InitiateMerge()) {
+            return AssertionFailure() << "Cannot initiate merge";
+        }
+        return AssertionSuccess();
+    }
+};
+
+TEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) {
+    // 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));
+    }
+
+    // Execute the update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    if (std::get<1>(GetParam()) /* merge */) {
+        ASSERT_TRUE(InitiateMerge("_b"));
+        // Simulate shutting down the device after merge has initiated.
+        ASSERT_TRUE(UnmapAll());
+    }
+
+    auto flashed_slot = std::get<0>(GetParam());
+    auto flashed_slot_suffix = SlotSuffixForSlotNumber(flashed_slot);
+
+    // Simulate flashing |flashed_slot|. This clears the UPDATED flag.
+    auto flashed_builder = MetadataBuilder::New(*opener_, "super", flashed_slot);
+    flashed_builder->RemoveGroupAndPartitions(group_->name() + flashed_slot_suffix);
+    flashed_builder->RemoveGroupAndPartitions(kCowGroupName);
+    ASSERT_TRUE(FillFakeMetadata(flashed_builder.get(), manifest_, flashed_slot_suffix));
+
+    // Deliberately remove a partition from this build so that
+    // InitiateMerge do not switch state to "merging". This is possible in
+    // practice because the list of dynamic partitions may change.
+    ASSERT_NE(nullptr, flashed_builder->FindPartition("prd" + flashed_slot_suffix));
+    flashed_builder->RemovePartition("prd" + flashed_slot_suffix);
+
+    auto flashed_metadata = flashed_builder->Export();
+    ASSERT_NE(nullptr, flashed_metadata);
+    ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, flashed_slot));
+
+    std::string path;
+    for (const auto& name : {"sys", "vnd"}) {
+        ASSERT_TRUE(CreateLogicalPartition(
+                CreateLogicalPartitionParams{
+                        .block_device = fake_super,
+                        .metadata_slot = flashed_slot,
+                        .partition_name = name + flashed_slot_suffix,
+                        .timeout_ms = 1s,
+                        .partition_opener = opener_.get(),
+                },
+                &path));
+        ASSERT_TRUE(WriteRandomData(path));
+        auto hash = GetHash(path);
+        ASSERT_TRUE(hash.has_value());
+        hashes_[name + flashed_slot_suffix] = *hash;
+    }
+
+    // Simulate shutting down the device after flash.
+    ASSERT_TRUE(UnmapAll());
+
+    // Simulate reboot. After reboot, init does first stage mount.
+    auto init = SnapshotManager::NewForFirstStageMount(
+            new TestDeviceInfo(fake_super, flashed_slot_suffix));
+    ASSERT_NE(init, nullptr);
+    if (init->NeedSnapshotsInFirstStageMount()) {
+        ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    } else {
+        for (const auto& name : {"sys", "vnd"}) {
+            ASSERT_TRUE(CreateLogicalPartition(
+                    CreateLogicalPartitionParams{
+                            .block_device = fake_super,
+                            .metadata_slot = flashed_slot,
+                            .partition_name = name + flashed_slot_suffix,
+                            .timeout_ms = 1s,
+                            .partition_opener = opener_.get(),
+                    },
+                    &path));
+        }
+    }
+
+    // Check that the target partitions have the same content.
+    for (const auto& name : {"sys", "vnd"}) {
+        ASSERT_TRUE(IsPartitionUnchanged(name + flashed_slot_suffix));
+    }
+
+    // There should be no snapshot to merge.
+    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, flashed_slot_suffix));
+    ASSERT_EQ(UpdateState::Cancelled, new_sm->InitiateMergeAndWait());
+
+    // Next OTA calls CancelUpdate no matter what.
+    ASSERT_TRUE(new_sm->CancelUpdate());
+}
+
+INSTANTIATE_TEST_SUITE_P(Snapshot, FlashAfterUpdateTest, Combine(Values(0, 1), Bool()),
+                         [](const TestParamInfo<FlashAfterUpdateTest::ParamType>& info) {
+                             return "Flash"s + (std::get<0>(info.param) ? "New"s : "Old"s) +
+                                    "Slot"s + (std::get<1>(info.param) ? "After"s : "Before"s) +
+                                    "Merge"s;
+                         });
+
 }  // namespace snapshot
 }  // namespace android
 
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index d65320c..1bc0357 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -60,24 +60,11 @@
         android::base::InitLogging(argv, &android::base::StdioLogger);
     }
 
-    auto sm = SnapshotManager::New();
+    auto state = SnapshotManager::New()->InitiateMergeAndWait();
 
-    auto state = sm->GetUpdateState();
     if (state == UpdateState::None) {
-        LOG(INFO) << "Can't find any snapshot to merge.";
         return true;
     }
-    if (state == UpdateState::Unverified) {
-        if (!sm->InitiateMerge()) {
-            LOG(ERROR) << "Failed to initiate merge.";
-            return false;
-        }
-    }
-
-    // All other states can be handled by ProcessUpdateState.
-    LOG(INFO) << "Waiting for any merge to complete. This can take up to 1 minute.";
-    state = SnapshotManager::New()->ProcessUpdateState();
-
     if (state == UpdateState::MergeCompleted) {
         auto end = std::chrono::steady_clock::now();
         auto passed = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 312fa3e..2d62347 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -62,24 +62,6 @@
     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');
@@ -91,6 +73,47 @@
     return out;
 }
 
+bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size,
+                     std::string* hash) {
+    unique_fd rand(open("/dev/urandom", O_RDONLY));
+    unique_fd fd(open(path.c_str(), O_WRONLY));
+
+    SHA256_CTX ctx;
+    if (hash) {
+        SHA256_Init(&ctx);
+    }
+
+    char buf[4096];
+    size_t total_written = 0;
+    while (!expect_size || total_written < *expect_size) {
+        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) {
+                break;
+            }
+            PLOG(ERROR) << "Cannot write " << path;
+            return false;
+        }
+        total_written += n;
+        if (hash) {
+            SHA256_Update(&ctx, buf, n);
+        }
+    }
+
+    if (expect_size && total_written != *expect_size) {
+        PLOG(ERROR) << "Written " << total_written << " bytes, expected " << *expect_size;
+        return false;
+    }
+
+    if (hash) {
+        uint8_t out[32];
+        SHA256_Final(out, &ctx);
+        *hash = ToHexString(out, sizeof(out));
+    }
+    return true;
+}
+
 std::optional<std::string> GetHash(const std::string& path) {
     std::string content;
     if (!android::base::ReadFileToString(path, &content, true)) {
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
index 0f70afe..2bf1b57 100644
--- a/fs_mgr/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -16,6 +16,7 @@
 
 #include <optional>
 #include <string>
+#include <unordered_set>
 
 #include <android/hardware/boot/1.1/IBootControl.h>
 #include <gmock/gmock.h>
@@ -89,6 +90,12 @@
     }
     bool IsOverlayfsSetup() const override { return false; }
     bool IsRecovery() const override { return recovery_; }
+    bool SetSlotAsUnbootable(unsigned int slot) override {
+        unbootable_slots_.insert(slot);
+        return true;
+    }
+
+    bool IsSlotUnbootable(uint32_t slot) { return unbootable_slots_.count(slot) != 0; }
 
     void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
     void set_fake_super(const std::string& path) {
@@ -102,6 +109,7 @@
     std::unique_ptr<TestPartitionOpener> opener_;
     MergeStatus merge_status_;
     bool recovery_ = false;
+    std::unordered_set<uint32_t> unbootable_slots_;
 };
 
 class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {
@@ -129,8 +137,11 @@
 // 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);
+// Write some random data to the given device.
+// If expect_size is not specified, will write until reaching end of the device.
+// Expect space of |path| is multiple of 4K.
+bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt,
+                     std::string* hash = nullptr);
 
 std::optional<std::string> GetHash(const std::string& path);
 
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 1cbaf45..c5adea6 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -969,3 +969,14 @@
     ASSERT_NE(nullptr, GetEntryForMountPoint(&fstab, "/data"))
             << "Default fstab doesn't contain /data entry";
 }
+
+TEST(fs_mgr, UserdataMountedFromDefaultFstab) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
+    Fstab fstab;
+    ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab";
+    ASSERT_NE(nullptr, GetMountedEntryForUserdata(&fstab))
+            << "/data wasn't mounted from default fstab";
+}
diff --git a/healthd/Android.bp b/healthd/Android.bp
index e04f70f..14d46b3 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -89,6 +89,7 @@
 
 cc_library_static {
     name: "libhealthd_charger_nops",
+    recovery_available: true,
 
     srcs: [
         "healthd_mode_charger_nops.cpp",
@@ -104,17 +105,19 @@
     ],
 
     static_libs: [
-        "android.hardware.health@2.0-impl",
+        "libhealthloop",
+        "libhealth2impl",
     ],
 
     shared_libs: [
-        "android.hardware.health@2.0",
+        "android.hardware.health@2.1",
         "libutils",
     ],
 }
 
 sysprop_library {
     name: "charger_sysprop",
+    recovery_available: true,
     srcs: ["charger.sysprop"],
     property_owner: "Platform",
     api_packages: ["android.sysprop"],
@@ -141,16 +144,16 @@
     export_include_dirs: [".", "include"],
 
     static_libs: [
-        "android.hardware.health@2.0-impl",
         "android.hardware.health@1.0-convert",
         "libcharger_sysprop",
-        "libhealthstoragedefault",
         "libhealthd_draw",
+        "libhealthloop",
+        "libhealth2impl",
         "libminui",
     ],
 
     shared_libs: [
-        "android.hardware.health@2.0",
+        "android.hardware.health@2.1",
         "libbase",
         "libcutils",
         "liblog",
@@ -164,3 +167,76 @@
         "AnimationParser.cpp",
     ],
 }
+
+cc_defaults {
+    name: "charger_defaults",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        // common
+        "android.hardware.health@2.0",
+        "android.hardware.health@2.1",
+        "libbase",
+        "libcutils",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+
+        // system charger only
+        "libpng",
+    ],
+
+    static_libs: [
+        // common
+        "android.hardware.health@1.0-convert",
+        "libbatterymonitor",
+        "libcharger_sysprop",
+        "libhealthd_charger_nops",
+        "libhealthloop",
+        "libhealth2impl",
+
+        // system charger only
+        "libhealthd_draw",
+        "libhealthd_charger",
+        "libminui",
+        "libsuspend",
+    ],
+}
+
+cc_binary {
+    name: "charger",
+    defaults: ["charger_defaults"],
+    recovery_available: true,
+    srcs: [
+        "charger.cpp",
+        "charger_utils.cpp",
+    ],
+
+    target: {
+        recovery: {
+            // No UI and libsuspend for recovery charger.
+            cflags: [
+                "-DCHARGER_FORCE_NO_UI=1",
+            ],
+            exclude_shared_libs: [
+                "libpng",
+            ],
+            exclude_static_libs: [
+                "libhealthd_draw",
+                "libhealthd_charger",
+                "libminui",
+                "libsuspend",
+            ],
+        }
+    }
+}
+
+cc_test {
+    name: "charger_test",
+    defaults: ["charger_defaults"],
+    srcs: ["charger_test.cpp"],
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 66ff399..4b09cf8 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -2,112 +2,10 @@
 
 LOCAL_PATH := $(call my-dir)
 
-### charger ###
-include $(CLEAR_VARS)
 ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
 LOCAL_CHARGER_NO_UI := true
 endif
 
-LOCAL_SRC_FILES := \
-    charger.cpp \
-
-LOCAL_MODULE := charger
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
-LOCAL_CFLAGS := -Werror
-
-CHARGER_STATIC_LIBRARIES := \
-    android.hardware.health@2.0-impl \
-    android.hardware.health@1.0-convert \
-    libbinderthreadstate \
-    libcharger_sysprop \
-    libhidlbase \
-    libhealthstoragedefault \
-    libminui \
-    libvndksupport \
-    libhealthd_charger \
-    libhealthd_charger_nops \
-    libhealthd_draw \
-    libbatterymonitor \
-
-CHARGER_SHARED_LIBRARIES := \
-    android.hardware.health@2.0 \
-    libbase \
-    libcutils \
-    libjsoncpp \
-    libpng \
-    libprocessgroup \
-    liblog \
-    libutils \
-
-CHARGER_SHARED_LIBRARIES += libsuspend
-
-LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
-LOCAL_SHARED_LIBRARIES := $(CHARGER_SHARED_LIBRARIES)
-
-LOCAL_HAL_STATIC_LIBRARIES := libhealthd
-
-# Symlink /charger to /system/bin/charger
-LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \
-    && ln -sf /system/bin/charger $(TARGET_ROOT_OUT)/charger
-
-include $(BUILD_EXECUTABLE)
-
-### charger.recovery ###
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-    charger.cpp \
-
-LOCAL_MODULE := charger.recovery
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
-LOCAL_MODULE_STEM := charger
-
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror -DCHARGER_FORCE_NO_UI=1
-
-# charger.recovery doesn't link against libhealthd_{charger,draw} or libminui, since it doesn't need
-# any UI support.
-LOCAL_STATIC_LIBRARIES := \
-    android.hardware.health@2.0-impl \
-    android.hardware.health@1.0-convert \
-    libbinderthreadstate \
-    libcharger_sysprop \
-    libhidlbase \
-    libhealthstoragedefault \
-    libvndksupport \
-    libhealthd_charger_nops \
-    libbatterymonitor \
-
-# These shared libs will be installed to recovery image because of the dependency in `recovery`
-# module.
-LOCAL_SHARED_LIBRARIES := \
-    android.hardware.health@2.0 \
-    libbase \
-    libcutils \
-    liblog \
-    libutils \
-
-# The use of LOCAL_HAL_STATIC_LIBRARIES prevents from building this module with Android.bp.
-LOCAL_HAL_STATIC_LIBRARIES := libhealthd
-
-include $(BUILD_EXECUTABLE)
-
-### charger_test ###
-include $(CLEAR_VARS)
-LOCAL_MODULE := charger_test
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
-LOCAL_SHARED_LIBRARIES := $(CHARGER_SHARED_LIBRARIES)
-LOCAL_SRC_FILES := \
-    charger_test.cpp \
-
-include $(BUILD_EXECUTABLE)
-
-CHARGER_STATIC_LIBRARIES :=
-CHARGER_SHARED_LIBRARIES :=
-
 ### charger_res_images ###
 ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
 define _add-charger-image
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 9e168e9..bdf4aac 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -73,12 +73,25 @@
     return std::nullopt;
 }
 
+static void initHealthInfo(HealthInfo_2_1* health_info_2_1) {
+    *health_info_2_1 = HealthInfo_2_1{};
+
+    // HIDL enum values are zero initialized, so they need to be initialized
+    // properly.
+    health_info_2_1->batteryCapacityLevel = BatteryCapacityLevel::UNKNOWN;
+    auto* props = &health_info_2_1->legacy.legacy;
+    props->batteryStatus = BatteryStatus::UNKNOWN;
+    props->batteryHealth = BatteryHealth::UNKNOWN;
+}
+
 BatteryMonitor::BatteryMonitor()
     : mHealthdConfig(nullptr),
       mBatteryDevicePresent(false),
       mBatteryFixedCapacity(0),
       mBatteryFixedTemperature(0),
-      mHealthInfo(std::make_unique<HealthInfo_2_1>()) {}
+      mHealthInfo(std::make_unique<HealthInfo_2_1>()) {
+    initHealthInfo(mHealthInfo.get());
+}
 
 BatteryMonitor::~BatteryMonitor() {}
 
@@ -168,7 +181,7 @@
         return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
 
     auto ret = mapSysfsString(buf.c_str(), supplyTypeMap);
-    if (ret < 0) {
+    if (!ret) {
         KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
         *ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
     }
@@ -198,7 +211,7 @@
 }
 
 void BatteryMonitor::updateValues(void) {
-    *mHealthInfo = HealthInfo_2_1{};
+    initHealthInfo(mHealthInfo.get());
 
     HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
 
diff --git a/healthd/animation.h b/healthd/animation.h
index 9476c91..d02d7a7 100644
--- a/healthd/animation.h
+++ b/healthd/animation.h
@@ -75,7 +75,7 @@
 
     bool run;
 
-    frame* frames;
+    frame* frames = nullptr;
     int cur_frame;
     int num_frames;
     int first_frame_repeats;  // Number of times to repeat the first frame in the current cycle
@@ -85,6 +85,8 @@
 
     int cur_level;  // current battery level being animated (0-100)
     int cur_status;  // current battery status - see BatteryService.h for BATTERY_STATUS_*
+
+    ~animation() { delete frames; }
 };
 
 }
diff --git a/healthd/charger_test.cpp b/healthd/charger_test.cpp
index a7e2161..e0bde68 100644
--- a/healthd/charger_test.cpp
+++ b/healthd/charger_test.cpp
@@ -21,13 +21,22 @@
 #include <condition_variable>
 #include <fstream>
 #include <iostream>
+#include <memory>
 #include <mutex>
 #include <streambuf>
 #include <string>
 #include <thread>
 #include <vector>
 
-#include <health2/Health.h>
+#include <health/utils.h>
+#include <health2impl/Health.h>
+
+#include "healthd_mode_charger.h"
+
+using android::hardware::health::InitHealthdConfig;
+using android::hardware::health::V2_1::HealthInfo;
+using android::hardware::health::V2_1::IHealth;
+using android::hardware::health::V2_1::implementation::Health;
 
 #define LOG_THIS(fmt, ...)     \
     ALOGE(fmt, ##__VA_ARGS__); \
@@ -129,22 +138,23 @@
     config->screen_on = NULL;
 }
 
-int healthd_board_battery_update(struct android::BatteryProperties*) {
-    getUpdateNotifier().set(true /* updated */);
+class TestHealth : public Health {
+  protected:
+    using Health::Health;
+    void UpdateHealthInfo(HealthInfo*) override { getUpdateNotifier().set(true /* updated */); }
+};
 
-    // return 0 to log periodic polled battery status to kernel log
-    return 0;
-}
-
-extern int healthd_charger_main(int argc, char** argv);
-
-int main(int argc, char** argv) {
-    using android::hardware::health::V2_0::implementation::Health;
-
+int main(int /*argc*/, char** /*argv*/) {
     const char* dumpFile = "/data/local/tmp/dump.txt";
 
+    auto config = std::make_unique<healthd_config>();
+    InitHealthdConfig(config.get());
+    healthd_board_init(config.get());
+    sp<IHealth> passthrough = new TestHealth(std::move(config));
+
     std::thread bgThread([=] {
-        healthd_charger_main(argc, argv);
+        android::Charger charger(passthrough);
+        charger.StartLoop();
     });
 
     // wait for healthd_init to finish
@@ -153,7 +163,7 @@
         exit(1);
     }
 
-    Health::getImplementation()->debug(createHidlHandle(dumpFile), {} /* options */);
+    passthrough->debug(createHidlHandle(dumpFile), {} /* options */);
 
     std::string content = openToString(dumpFile);
     int status = expectContains(content, {
diff --git a/healthd/charger_utils.cpp b/healthd/charger_utils.cpp
new file mode 100644
index 0000000..0cf9df5
--- /dev/null
+++ b/healthd/charger_utils.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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 "charger_utils.h"
+
+#include <android-base/logging.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <health/utils.h>
+#include <health2impl/Health.h>
+#include <hidl/ServiceManagement.h>
+
+using android::hardware::getPassthroughServiceManager;
+using android::hidl::base::V1_0::IBase;
+using android::hidl::manager::V1_0::IServiceManager;
+
+namespace android {
+namespace hardware {
+namespace health {
+sp<V2_1::IHealth> GetPassthroughHealthImpl() {
+    // Not using getService() because there is no hwservicemanager in charger mode.
+    sp<IServiceManager> pm = getPassthroughServiceManager();
+    if (pm == nullptr) {
+        LOG(WARNING) << "Cannot get passthrough service manager.";
+        return nullptr;
+    }
+    sp<IBase> base = pm->get(V2_0::IHealth::descriptor, "default");
+    if (base == nullptr) {
+        LOG(WARNING) << "Cannot find passthrough implementation of health 2.0 HAL for instance "
+                        "'default' on the device.";
+        return nullptr;
+    }
+    sp<V2_1::IHealth> service = V2_1::IHealth::castFrom(base);
+    if (service == nullptr) {
+        LOG(WARNING)
+                << "Cannot cast passthrough implementation of health 2.0 HAL to 2.1 for instance "
+                   "'default' on the device.";
+        return nullptr;
+    }
+    return service;
+}
+
+sp<V2_1::IHealth> GetPassthroughHealth() {
+    auto impl = GetPassthroughHealthImpl();
+    if (impl == nullptr) {
+        LOG(WARNING) << "Charger uses system defaults.";
+        auto config = std::make_unique<healthd_config>();
+        InitHealthdConfig(config.get());
+        impl = new V2_1::implementation::Health(std::move(config));
+    }
+    return impl;
+}
+
+}  // namespace health
+}  // namespace hardware
+}  // namespace android
diff --git a/healthd/charger_utils.h b/healthd/charger_utils.h
new file mode 100644
index 0000000..f96e827
--- /dev/null
+++ b/healthd/charger_utils.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/health/2.1/IHealth.h>
+
+namespace android {
+namespace hardware {
+namespace health {
+sp<V2_1::IHealth> GetPassthroughHealth();
+}  // namespace health
+}  // namespace hardware
+}  // namespace android
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index d676083..7d844c9 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "healthd_mode_charger.h"
+
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -28,7 +30,7 @@
 #include <time.h>
 #include <unistd.h>
 
-#include <functional>
+#include <optional>
 
 #include <android-base/file.h>
 #include <android-base/macros.h>
@@ -47,16 +49,30 @@
 
 #include "AnimationParser.h"
 #include "charger.sysprop.h"
+#include "charger_utils.h"
 #include "healthd_draw.h"
 
-#include <health2/Health.h>
+#include <android/hardware/health/2.0/IHealthInfoCallback.h>
+#include <health/utils.h>
+#include <health2impl/HalHealthLoop.h>
+#include <health2impl/Health.h>
 #include <healthd/healthd.h>
 
 using namespace android;
+using android::hardware::Return;
+using android::hardware::health::GetPassthroughHealth;
+using android::hardware::health::HealthLoop;
+using android::hardware::health::V1_0::BatteryStatus;
+using android::hardware::health::V2_0::Result;
+using android::hardware::health::V2_1::IHealth;
+using IHealth_2_0 = android::hardware::health::V2_0::IHealth;
+using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
+using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
 
 // main healthd loop
 extern int healthd_main(void);
 
+// minui globals
 char* locale;
 
 #ifndef max
@@ -85,6 +101,8 @@
 #define LOGW(x...) KLOG_WARNING("charger", x);
 #define LOGV(x...) KLOG_DEBUG("charger", x);
 
+namespace android {
+
 // Resources in /product/etc/res overrides resources in /res.
 // If the device is using the Generic System Image (GSI), resources may exist in
 // both paths.
@@ -93,28 +111,6 @@
 static constexpr const char* product_animation_root = "/product/etc/res/images/";
 static constexpr const char* animation_desc_path = "/res/values/charger/animation.txt";
 
-struct key_state {
-    bool pending;
-    bool down;
-    int64_t timestamp;
-};
-
-struct charger {
-    bool have_battery_state;
-    bool charger_connected;
-    bool screen_blanked;
-    int64_t next_screen_transition;
-    int64_t next_key_check;
-    int64_t next_pwr_check;
-    int64_t wait_batt_level_timestamp;
-
-    key_state keys[KEY_MAX + 1];
-
-    animation* batt_anim;
-    GRSurface* surf_unknown;
-    int boot_min_cap;
-};
-
 static const animation BASE_ANIMATION = {
     .text_clock =
         {
@@ -153,51 +149,51 @@
     .cur_status = BATTERY_STATUS_UNKNOWN,
 };
 
-static animation::frame default_animation_frames[] = {
-    {
-        .disp_time = 750,
-        .min_level = 0,
-        .max_level = 19,
-        .surface = NULL,
-    },
-    {
-        .disp_time = 750,
-        .min_level = 0,
-        .max_level = 39,
-        .surface = NULL,
-    },
-    {
-        .disp_time = 750,
-        .min_level = 0,
-        .max_level = 59,
-        .surface = NULL,
-    },
-    {
-        .disp_time = 750,
-        .min_level = 0,
-        .max_level = 79,
-        .surface = NULL,
-    },
-    {
-        .disp_time = 750,
-        .min_level = 80,
-        .max_level = 95,
-        .surface = NULL,
-    },
-    {
-        .disp_time = 750,
-        .min_level = 0,
-        .max_level = 100,
-        .surface = NULL,
-    },
-};
+void Charger::InitDefaultAnimationFrames() {
+    owned_frames_ = {
+            {
+                    .disp_time = 750,
+                    .min_level = 0,
+                    .max_level = 19,
+                    .surface = NULL,
+            },
+            {
+                    .disp_time = 750,
+                    .min_level = 0,
+                    .max_level = 39,
+                    .surface = NULL,
+            },
+            {
+                    .disp_time = 750,
+                    .min_level = 0,
+                    .max_level = 59,
+                    .surface = NULL,
+            },
+            {
+                    .disp_time = 750,
+                    .min_level = 0,
+                    .max_level = 79,
+                    .surface = NULL,
+            },
+            {
+                    .disp_time = 750,
+                    .min_level = 80,
+                    .max_level = 95,
+                    .surface = NULL,
+            },
+            {
+                    .disp_time = 750,
+                    .min_level = 0,
+                    .max_level = 100,
+                    .surface = NULL,
+            },
+    };
+}
 
-static animation battery_animation = BASE_ANIMATION;
+Charger::Charger(const sp<IHealth>& service)
+    : HalHealthLoop("charger", service), batt_anim_(BASE_ANIMATION) {}
 
-static charger charger_state;
-static healthd_config* healthd_config;
-static android::BatteryProperties* batt_prop;
-static std::unique_ptr<HealthdDraw> healthd_draw;
+Charger::~Charger() {}
 
 /* current time in milliseconds */
 static int64_t curr_time_ms() {
@@ -284,123 +280,125 @@
     anim->run = false;
 }
 
-static void update_screen_state(charger* charger, int64_t now) {
-    animation* batt_anim = charger->batt_anim;
+void Charger::UpdateScreenState(int64_t now) {
     int disp_time;
 
-    if (!batt_anim->run || now < charger->next_screen_transition) return;
+    if (!batt_anim_.run || now < next_screen_transition_) return;
 
     // If battery level is not ready, keep checking in the defined time
-    if (batt_prop == nullptr ||
-        (batt_prop->batteryLevel == 0 && batt_prop->batteryStatus == BATTERY_STATUS_UNKNOWN)) {
-        if (charger->wait_batt_level_timestamp == 0) {
+    if (health_info_.batteryLevel == 0 && health_info_.batteryStatus == BatteryStatus::UNKNOWN) {
+        if (wait_batt_level_timestamp_ == 0) {
             // Set max delay time and skip drawing screen
-            charger->wait_batt_level_timestamp = now + MAX_BATT_LEVEL_WAIT_TIME;
+            wait_batt_level_timestamp_ = now + MAX_BATT_LEVEL_WAIT_TIME;
             LOGV("[%" PRId64 "] wait for battery capacity ready\n", now);
             return;
-        } else if (now <= charger->wait_batt_level_timestamp) {
+        } else if (now <= wait_batt_level_timestamp_) {
             // Do nothing, keep waiting
             return;
         }
         // If timeout and battery level is still not ready, draw unknown battery
     }
 
-    if (healthd_draw == nullptr) {
-        if (healthd_config && healthd_config->screen_on) {
-            if (!healthd_config->screen_on(batt_prop)) {
+    if (healthd_draw_ == nullptr) {
+        std::optional<bool> out_screen_on;
+        service()->shouldKeepScreenOn([&](Result res, bool screen_on) {
+            if (res == Result::SUCCESS) {
+                *out_screen_on = screen_on;
+            }
+        });
+        if (out_screen_on.has_value()) {
+            if (!*out_screen_on) {
                 LOGV("[%" PRId64 "] leave screen off\n", now);
-                batt_anim->run = false;
-                charger->next_screen_transition = -1;
-                if (charger->charger_connected) request_suspend(true);
+                batt_anim_.run = false;
+                next_screen_transition_ = -1;
+                if (charger_online()) request_suspend(true);
                 return;
             }
         }
 
-        healthd_draw.reset(new HealthdDraw(batt_anim));
+        healthd_draw_.reset(new HealthdDraw(&batt_anim_));
 
         if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
-            healthd_draw->blank_screen(true);
-            charger->screen_blanked = true;
+            healthd_draw_->blank_screen(true);
+            screen_blanked_ = true;
         }
     }
 
     /* animation is over, blank screen and leave */
-    if (batt_anim->num_cycles > 0 && batt_anim->cur_cycle == batt_anim->num_cycles) {
-        reset_animation(batt_anim);
-        charger->next_screen_transition = -1;
-        healthd_draw->blank_screen(true);
-        charger->screen_blanked = true;
+    if (batt_anim_.num_cycles > 0 && batt_anim_.cur_cycle == batt_anim_.num_cycles) {
+        reset_animation(&batt_anim_);
+        next_screen_transition_ = -1;
+        healthd_draw_->blank_screen(true);
+        screen_blanked_ = true;
         LOGV("[%" PRId64 "] animation done\n", now);
-        if (charger->charger_connected) request_suspend(true);
+        if (charger_online()) request_suspend(true);
         return;
     }
 
-    disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time;
+    disp_time = batt_anim_.frames[batt_anim_.cur_frame].disp_time;
 
-    if (charger->screen_blanked) {
-        healthd_draw->blank_screen(false);
-        charger->screen_blanked = false;
+    if (screen_blanked_) {
+        healthd_draw_->blank_screen(false);
+        screen_blanked_ = false;
     }
 
     /* animation starting, set up the animation */
-    if (batt_anim->cur_frame == 0) {
+    if (batt_anim_.cur_frame == 0) {
         LOGV("[%" PRId64 "] animation starting\n", now);
-        if (batt_prop) {
-            batt_anim->cur_level = batt_prop->batteryLevel;
-            batt_anim->cur_status = batt_prop->batteryStatus;
-            if (batt_prop->batteryLevel >= 0 && batt_anim->num_frames != 0) {
-                /* find first frame given current battery level */
-                for (int i = 0; i < batt_anim->num_frames; i++) {
-                    if (batt_anim->cur_level >= batt_anim->frames[i].min_level &&
-                        batt_anim->cur_level <= batt_anim->frames[i].max_level) {
-                        batt_anim->cur_frame = i;
-                        break;
-                    }
+        batt_anim_.cur_level = health_info_.batteryLevel;
+        batt_anim_.cur_status = (int)health_info_.batteryStatus;
+        if (health_info_.batteryLevel >= 0 && batt_anim_.num_frames != 0) {
+            /* find first frame given current battery level */
+            for (int i = 0; i < batt_anim_.num_frames; i++) {
+                if (batt_anim_.cur_level >= batt_anim_.frames[i].min_level &&
+                    batt_anim_.cur_level <= batt_anim_.frames[i].max_level) {
+                    batt_anim_.cur_frame = i;
+                    break;
                 }
-
-                if (charger->charger_connected) {
-                    // repeat the first frame first_frame_repeats times
-                    disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time *
-                                batt_anim->first_frame_repeats;
-                } else {
-                    disp_time = UNPLUGGED_DISPLAY_TIME / batt_anim->num_cycles;
-                }
-
-                LOGV("cur_frame=%d disp_time=%d\n", batt_anim->cur_frame, disp_time);
             }
+
+            if (charger_online()) {
+                // repeat the first frame first_frame_repeats times
+                disp_time = batt_anim_.frames[batt_anim_.cur_frame].disp_time *
+                            batt_anim_.first_frame_repeats;
+            } else {
+                disp_time = UNPLUGGED_DISPLAY_TIME / batt_anim_.num_cycles;
+            }
+
+            LOGV("cur_frame=%d disp_time=%d\n", batt_anim_.cur_frame, disp_time);
         }
     }
 
     /* draw the new frame (@ cur_frame) */
-    healthd_draw->redraw_screen(charger->batt_anim, charger->surf_unknown);
+    healthd_draw_->redraw_screen(&batt_anim_, surf_unknown_);
 
     /* if we don't have anim frames, we only have one image, so just bump
      * the cycle counter and exit
      */
-    if (batt_anim->num_frames == 0 || batt_anim->cur_level < 0) {
+    if (batt_anim_.num_frames == 0 || batt_anim_.cur_level < 0) {
         LOGW("[%" PRId64 "] animation missing or unknown battery status\n", now);
-        charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME;
-        batt_anim->cur_cycle++;
+        next_screen_transition_ = now + BATTERY_UNKNOWN_TIME;
+        batt_anim_.cur_cycle++;
         return;
     }
 
     /* schedule next screen transition */
-    charger->next_screen_transition = curr_time_ms() + disp_time;
+    next_screen_transition_ = curr_time_ms() + disp_time;
 
     /* advance frame cntr to the next valid frame only if we are charging
      * if necessary, advance cycle cntr, and reset frame cntr
      */
-    if (charger->charger_connected) {
-        batt_anim->cur_frame++;
+    if (charger_online()) {
+        batt_anim_.cur_frame++;
 
-        while (batt_anim->cur_frame < batt_anim->num_frames &&
-               (batt_anim->cur_level < batt_anim->frames[batt_anim->cur_frame].min_level ||
-                batt_anim->cur_level > batt_anim->frames[batt_anim->cur_frame].max_level)) {
-            batt_anim->cur_frame++;
+        while (batt_anim_.cur_frame < batt_anim_.num_frames &&
+               (batt_anim_.cur_level < batt_anim_.frames[batt_anim_.cur_frame].min_level ||
+                batt_anim_.cur_level > batt_anim_.frames[batt_anim_.cur_frame].max_level)) {
+            batt_anim_.cur_frame++;
         }
-        if (batt_anim->cur_frame >= batt_anim->num_frames) {
-            batt_anim->cur_cycle++;
-            batt_anim->cur_frame = 0;
+        if (batt_anim_.cur_frame >= batt_anim_.num_frames) {
+            batt_anim_.cur_cycle++;
+            batt_anim_.cur_frame = 0;
 
             /* don't reset the cycle counter, since we use that as a signal
              * in a test above to check if animation is over
@@ -411,29 +409,29 @@
          * If we stop it immediately instead of going through this loop, then
          * the animation would stop somewhere in the middle.
          */
-        batt_anim->cur_frame = 0;
-        batt_anim->cur_cycle++;
+        batt_anim_.cur_frame = 0;
+        batt_anim_.cur_cycle++;
     }
 }
 
-static int set_key_callback(charger* charger, int code, int value) {
+int Charger::SetKeyCallback(int code, int value) {
     int64_t now = curr_time_ms();
     int down = !!value;
 
     if (code > KEY_MAX) return -1;
 
     /* ignore events that don't modify our state */
-    if (charger->keys[code].down == down) return 0;
+    if (keys_[code].down == down) return 0;
 
     /* only record the down even timestamp, as the amount
      * of time the key spent not being pressed is not useful */
-    if (down) charger->keys[code].timestamp = now;
-    charger->keys[code].down = down;
-    charger->keys[code].pending = true;
+    if (down) keys_[code].timestamp = now;
+    keys_[code].down = down;
+    keys_[code].pending = true;
     if (down) {
         LOGV("[%" PRId64 "] key[%d] down\n", now, code);
     } else {
-        int64_t duration = now - charger->keys[code].timestamp;
+        int64_t duration = now - keys_[code].timestamp;
         int64_t secs = duration / 1000;
         int64_t msecs = duration - secs * 1000;
         LOGV("[%" PRId64 "] key[%d] up (was down for %" PRId64 ".%" PRId64 "sec)\n", now, code,
@@ -443,20 +441,19 @@
     return 0;
 }
 
-static void update_input_state(charger* charger, input_event* ev) {
+void Charger::UpdateInputState(input_event* ev) {
     if (ev->type != EV_KEY) return;
-    set_key_callback(charger, ev->code, ev->value);
+    SetKeyCallback(ev->code, ev->value);
 }
 
-static void set_next_key_check(charger* charger, key_state* key, int64_t timeout) {
+void Charger::SetNextKeyCheck(key_state* key, int64_t timeout) {
     int64_t then = key->timestamp + timeout;
 
-    if (charger->next_key_check == -1 || then < charger->next_key_check)
-        charger->next_key_check = then;
+    if (next_key_check_ == -1 || then < next_key_check_) next_key_check_ = then;
 }
 
-static void process_key(charger* charger, int code, int64_t now) {
-    key_state* key = &charger->keys[code];
+void Charger::ProcessKey(int code, int64_t now) {
+    key_state* key = &keys_[code];
 
     if (code == KEY_POWER) {
         if (key->down) {
@@ -469,7 +466,7 @@
                     LOGW("[%" PRId64 "] booting from charger mode\n", now);
                     property_set("sys.boot_from_charger_mode", "1");
                 } else {
-                    if (charger->batt_anim->cur_level >= charger->boot_min_cap) {
+                    if (batt_anim_.cur_level >= boot_min_cap_) {
                         LOGW("[%" PRId64 "] rebooting\n", now);
                         reboot(RB_AUTOBOOT);
                     } else {
@@ -483,18 +480,18 @@
                 /* if the key is pressed but timeout hasn't expired,
                  * make sure we wake up at the right-ish time to check
                  */
-                set_next_key_check(charger, key, POWER_ON_KEY_TIME);
+                SetNextKeyCheck(key, POWER_ON_KEY_TIME);
 
                 /* Turn on the display and kick animation on power-key press
                  * rather than on key release
                  */
-                kick_animation(charger->batt_anim);
+                kick_animation(&batt_anim_);
                 request_suspend(false);
             }
         } else {
             /* if the power key got released, force screen state cycle */
             if (key->pending) {
-                kick_animation(charger->batt_anim);
+                kick_animation(&batt_anim_);
                 request_suspend(false);
             }
         }
@@ -503,36 +500,35 @@
     key->pending = false;
 }
 
-static void handle_input_state(charger* charger, int64_t now) {
-    process_key(charger, KEY_POWER, now);
+void Charger::HandleInputState(int64_t now) {
+    ProcessKey(KEY_POWER, now);
 
-    if (charger->next_key_check != -1 && now > charger->next_key_check)
-        charger->next_key_check = -1;
+    if (next_key_check_ != -1 && now > next_key_check_) next_key_check_ = -1;
 }
 
-static void handle_power_supply_state(charger* charger, int64_t now) {
+void Charger::HandlePowerSupplyState(int64_t now) {
     int timer_shutdown = UNPLUGGED_SHUTDOWN_TIME;
-    if (!charger->have_battery_state) return;
+    if (!have_battery_state_) return;
 
-    if (!charger->charger_connected) {
+    if (!charger_online()) {
         request_suspend(false);
-        if (charger->next_pwr_check == -1) {
+        if (next_pwr_check_ == -1) {
             /* Last cycle would have stopped at the extreme top of battery-icon
              * Need to show the correct level corresponding to capacity.
              *
-             * Reset next_screen_transition to update screen immediately.
+             * Reset next_screen_transition_ to update screen immediately.
              * Reset & kick animation to show complete animation cycles
              * when charger disconnected.
              */
             timer_shutdown =
                     property_get_int32(UNPLUGGED_SHUTDOWN_TIME_PROP, UNPLUGGED_SHUTDOWN_TIME);
-            charger->next_screen_transition = now - 1;
-            reset_animation(charger->batt_anim);
-            kick_animation(charger->batt_anim);
-            charger->next_pwr_check = now + timer_shutdown;
+            next_screen_transition_ = now - 1;
+            reset_animation(&batt_anim_);
+            kick_animation(&batt_anim_);
+            next_pwr_check_ = now + timer_shutdown;
             LOGW("[%" PRId64 "] device unplugged: shutting down in %" PRId64 " (@ %" PRId64 ")\n",
-                 now, (int64_t)timer_shutdown, charger->next_pwr_check);
-        } else if (now >= charger->next_pwr_check) {
+                 now, (int64_t)timer_shutdown, next_pwr_check_);
+        } else if (now >= next_pwr_check_) {
             LOGW("[%" PRId64 "] shutting down\n", now);
             reboot(RB_POWER_OFF);
         } else {
@@ -540,64 +536,60 @@
         }
     } else {
         /* online supply present, reset shutdown timer if set */
-        if (charger->next_pwr_check != -1) {
-            /* Reset next_screen_transition to update screen immediately.
+        if (next_pwr_check_ != -1) {
+            /* Reset next_screen_transition_ to update screen immediately.
              * Reset & kick animation to show complete animation cycles
              * when charger connected again.
              */
             request_suspend(false);
-            charger->next_screen_transition = now - 1;
-            reset_animation(charger->batt_anim);
-            kick_animation(charger->batt_anim);
+            next_screen_transition_ = now - 1;
+            reset_animation(&batt_anim_);
+            kick_animation(&batt_anim_);
             LOGW("[%" PRId64 "] device plugged in: shutdown cancelled\n", now);
         }
-        charger->next_pwr_check = -1;
+        next_pwr_check_ = -1;
     }
 }
 
-void healthd_mode_charger_heartbeat() {
-    charger* charger = &charger_state;
+void Charger::Heartbeat() {
+    // charger* charger = &charger_state;
     int64_t now = curr_time_ms();
 
-    handle_input_state(charger, now);
-    handle_power_supply_state(charger, now);
+    HandleInputState(now);
+    HandlePowerSupplyState(now);
 
     /* do screen update last in case any of the above want to start
      * screen transitions (animations, etc)
      */
-    update_screen_state(charger, now);
+    UpdateScreenState(now);
 }
 
-void healthd_mode_charger_battery_update(android::BatteryProperties* props) {
-    charger* charger = &charger_state;
+void Charger::OnHealthInfoChanged(const HealthInfo_2_1& health_info) {
+    set_charger_online(health_info);
 
-    charger->charger_connected =
-        props->chargerAcOnline || props->chargerUsbOnline || props->chargerWirelessOnline;
-
-    if (!charger->have_battery_state) {
-        charger->have_battery_state = true;
-        charger->next_screen_transition = curr_time_ms() - 1;
+    if (!have_battery_state_) {
+        have_battery_state_ = true;
+        next_screen_transition_ = curr_time_ms() - 1;
         request_suspend(false);
-        reset_animation(charger->batt_anim);
-        kick_animation(charger->batt_anim);
+        reset_animation(&batt_anim_);
+        kick_animation(&batt_anim_);
     }
-    batt_prop = props;
+    health_info_ = health_info.legacy.legacy;
+
+    AdjustWakealarmPeriods(charger_online());
 }
 
-int healthd_mode_charger_preparetowait(void) {
-    charger* charger = &charger_state;
+int Charger::PrepareToWait(void) {
     int64_t now = curr_time_ms();
     int64_t next_event = INT64_MAX;
     int64_t timeout;
 
     LOGV("[%" PRId64 "] next screen: %" PRId64 " next key: %" PRId64 " next pwr: %" PRId64 "\n",
-         now, charger->next_screen_transition, charger->next_key_check, charger->next_pwr_check);
+         now, next_screen_transition_, next_key_check_, next_pwr_check_);
 
-    if (charger->next_screen_transition != -1) next_event = charger->next_screen_transition;
-    if (charger->next_key_check != -1 && charger->next_key_check < next_event)
-        next_event = charger->next_key_check;
-    if (charger->next_pwr_check != -1 && charger->next_pwr_check < next_event)
-        next_event = charger->next_pwr_check;
+    if (next_screen_transition_ != -1) next_event = next_screen_transition_;
+    if (next_key_check_ != -1 && next_key_check_ < next_event) next_event = next_key_check_;
+    if (next_pwr_check_ != -1 && next_pwr_check_ < next_event) next_event = next_pwr_check_;
 
     if (next_event != -1 && next_event != INT64_MAX)
         timeout = max(0, next_event - now);
@@ -607,32 +599,32 @@
     return (int)timeout;
 }
 
-static int input_callback(charger* charger, int fd, unsigned int epevents) {
+int Charger::InputCallback(int fd, unsigned int epevents) {
     input_event ev;
     int ret;
 
     ret = ev_get_input(fd, epevents, &ev);
     if (ret) return -1;
-    update_input_state(charger, &ev);
+    UpdateInputState(&ev);
     return 0;
 }
 
-static void charger_event_handler(uint32_t /*epevents*/) {
+static void charger_event_handler(HealthLoop* /*charger_loop*/, uint32_t /*epevents*/) {
     int ret;
 
     ret = ev_wait(-1);
     if (!ret) ev_dispatch();
 }
 
-animation* init_animation() {
+void Charger::InitAnimation() {
     bool parse_success;
 
     std::string content;
     if (base::ReadFileToString(product_animation_desc_path, &content)) {
-        parse_success = parse_animation_desc(content, &battery_animation);
-        battery_animation.set_resource_root(product_animation_root);
+        parse_success = parse_animation_desc(content, &batt_anim_);
+        batt_anim_.set_resource_root(product_animation_root);
     } else if (base::ReadFileToString(animation_desc_path, &content)) {
-        parse_success = parse_animation_desc(content, &battery_animation);
+        parse_success = parse_animation_desc(content, &batt_anim_);
     } else {
         LOGW("Could not open animation description at %s\n", animation_desc_path);
         parse_success = false;
@@ -640,41 +632,36 @@
 
     if (!parse_success) {
         LOGW("Could not parse animation description. Using default animation.\n");
-        battery_animation = BASE_ANIMATION;
-        battery_animation.animation_file.assign("charger/battery_scale");
-        battery_animation.frames = default_animation_frames;
-        battery_animation.num_frames = ARRAY_SIZE(default_animation_frames);
+        batt_anim_ = BASE_ANIMATION;
+        batt_anim_.animation_file.assign("charger/battery_scale");
+        InitDefaultAnimationFrames();
+        batt_anim_.frames = owned_frames_.data();
+        batt_anim_.num_frames = owned_frames_.size();
     }
-    if (battery_animation.fail_file.empty()) {
-        battery_animation.fail_file.assign("charger/battery_fail");
+    if (batt_anim_.fail_file.empty()) {
+        batt_anim_.fail_file.assign("charger/battery_fail");
     }
 
     LOGV("Animation Description:\n");
-    LOGV("  animation: %d %d '%s' (%d)\n", battery_animation.num_cycles,
-         battery_animation.first_frame_repeats, battery_animation.animation_file.c_str(),
-         battery_animation.num_frames);
-    LOGV("  fail_file: '%s'\n", battery_animation.fail_file.c_str());
-    LOGV("  clock: %d %d %d %d %d %d '%s'\n", battery_animation.text_clock.pos_x,
-         battery_animation.text_clock.pos_y, battery_animation.text_clock.color_r,
-         battery_animation.text_clock.color_g, battery_animation.text_clock.color_b,
-         battery_animation.text_clock.color_a, battery_animation.text_clock.font_file.c_str());
-    LOGV("  percent: %d %d %d %d %d %d '%s'\n", battery_animation.text_percent.pos_x,
-         battery_animation.text_percent.pos_y, battery_animation.text_percent.color_r,
-         battery_animation.text_percent.color_g, battery_animation.text_percent.color_b,
-         battery_animation.text_percent.color_a, battery_animation.text_percent.font_file.c_str());
-    for (int i = 0; i < battery_animation.num_frames; i++) {
-        LOGV("  frame %.2d: %d %d %d\n", i, battery_animation.frames[i].disp_time,
-             battery_animation.frames[i].min_level, battery_animation.frames[i].max_level);
+    LOGV("  animation: %d %d '%s' (%d)\n", batt_anim_.num_cycles, batt_anim_.first_frame_repeats,
+         batt_anim_.animation_file.c_str(), batt_anim_.num_frames);
+    LOGV("  fail_file: '%s'\n", batt_anim_.fail_file.c_str());
+    LOGV("  clock: %d %d %d %d %d %d '%s'\n", batt_anim_.text_clock.pos_x,
+         batt_anim_.text_clock.pos_y, batt_anim_.text_clock.color_r, batt_anim_.text_clock.color_g,
+         batt_anim_.text_clock.color_b, batt_anim_.text_clock.color_a,
+         batt_anim_.text_clock.font_file.c_str());
+    LOGV("  percent: %d %d %d %d %d %d '%s'\n", batt_anim_.text_percent.pos_x,
+         batt_anim_.text_percent.pos_y, batt_anim_.text_percent.color_r,
+         batt_anim_.text_percent.color_g, batt_anim_.text_percent.color_b,
+         batt_anim_.text_percent.color_a, batt_anim_.text_percent.font_file.c_str());
+    for (int i = 0; i < batt_anim_.num_frames; i++) {
+        LOGV("  frame %.2d: %d %d %d\n", i, batt_anim_.frames[i].disp_time,
+             batt_anim_.frames[i].min_level, batt_anim_.frames[i].max_level);
     }
-
-    return &battery_animation;
 }
 
-void healthd_mode_charger_init(struct healthd_config* config) {
-    using android::hardware::health::V2_0::implementation::Health;
-
+void Charger::Init(struct healthd_config* config) {
     int ret;
-    charger* charger = &charger_state;
     int i;
     int epollfd;
 
@@ -682,22 +669,22 @@
 
     LOGW("--------------- STARTING CHARGER MODE ---------------\n");
 
-    ret = ev_init(std::bind(&input_callback, charger, std::placeholders::_1, std::placeholders::_2));
+    ret = ev_init(
+            std::bind(&Charger::InputCallback, this, std::placeholders::_1, std::placeholders::_2));
     if (!ret) {
         epollfd = ev_get_epollfd();
-        healthd_register_event(epollfd, charger_event_handler, EVENT_WAKEUP_FD);
+        RegisterEvent(epollfd, &charger_event_handler, EVENT_WAKEUP_FD);
     }
 
-    animation* anim = init_animation();
-    charger->batt_anim = anim;
+    InitAnimation();
 
-    ret = res_create_display_surface(anim->fail_file.c_str(), &charger->surf_unknown);
+    ret = res_create_display_surface(batt_anim_.fail_file.c_str(), &surf_unknown_);
     if (ret < 0) {
         LOGE("Cannot load custom battery_fail image. Reverting to built in: %d\n", ret);
-        ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown);
+        ret = res_create_display_surface("charger/battery_fail", &surf_unknown_);
         if (ret < 0) {
             LOGE("Cannot load built in battery_fail image\n");
-            charger->surf_unknown = NULL;
+            surf_unknown_ = NULL;
         }
     }
 
@@ -705,49 +692,41 @@
     int scale_count;
     int scale_fps;  // Not in use (charger/battery_scale doesn't have FPS text
                     // chunk). We are using hard-coded frame.disp_time instead.
-    ret = res_create_multi_display_surface(anim->animation_file.c_str(), &scale_count, &scale_fps,
-                                           &scale_frames);
+    ret = res_create_multi_display_surface(batt_anim_.animation_file.c_str(), &scale_count,
+                                           &scale_fps, &scale_frames);
     if (ret < 0) {
         LOGE("Cannot load battery_scale image\n");
-        anim->num_frames = 0;
-        anim->num_cycles = 1;
-    } else if (scale_count != anim->num_frames) {
+        batt_anim_.num_frames = 0;
+        batt_anim_.num_cycles = 1;
+    } else if (scale_count != batt_anim_.num_frames) {
         LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n", scale_count,
-             anim->num_frames);
-        anim->num_frames = 0;
-        anim->num_cycles = 1;
+             batt_anim_.num_frames);
+        batt_anim_.num_frames = 0;
+        batt_anim_.num_cycles = 1;
     } else {
-        for (i = 0; i < anim->num_frames; i++) {
-            anim->frames[i].surface = scale_frames[i];
+        for (i = 0; i < batt_anim_.num_frames; i++) {
+            batt_anim_.frames[i].surface = scale_frames[i];
         }
     }
-    ev_sync_key_state(
-        std::bind(&set_key_callback, charger, std::placeholders::_1, std::placeholders::_2));
+    ev_sync_key_state(std::bind(&Charger::SetKeyCallback, this, std::placeholders::_1,
+                                std::placeholders::_2));
 
-    charger->next_screen_transition = -1;
-    charger->next_key_check = -1;
-    charger->next_pwr_check = -1;
-    charger->wait_batt_level_timestamp = 0;
+    next_screen_transition_ = -1;
+    next_key_check_ = -1;
+    next_pwr_check_ = -1;
+    wait_batt_level_timestamp_ = 0;
 
-    // Initialize Health implementation (which initializes the internal BatteryMonitor).
-    Health::initInstance(config);
+    // Retrieve healthd_config from the existing health HAL.
+    HalHealthLoop::Init(config);
 
-    healthd_config = config;
-    charger->boot_min_cap = config->boot_min_cap;
+    boot_min_cap_ = config->boot_min_cap;
 }
 
-static struct healthd_mode_ops charger_ops = {
-        .init = healthd_mode_charger_init,
-        .preparetowait = healthd_mode_charger_preparetowait,
-        .heartbeat = healthd_mode_charger_heartbeat,
-        .battery_update = healthd_mode_charger_battery_update,
-};
+}  // namespace android
 
 int healthd_charger_main(int argc, char** argv) {
     int ch;
 
-    healthd_mode_ops = &charger_ops;
-
     while ((ch = getopt(argc, argv, "cr")) != -1) {
         switch (ch) {
             case 'c':
@@ -763,5 +742,6 @@
         }
     }
 
-    return healthd_main();
+    Charger charger(GetPassthroughHealth());
+    return charger.StartLoop();
 }
diff --git a/healthd/healthd_mode_charger.h b/healthd/healthd_mode_charger.h
index 2f0c9f2..370ca86 100644
--- a/healthd/healthd_mode_charger.h
+++ b/healthd/healthd_mode_charger.h
@@ -16,4 +16,72 @@
 
 #pragma once
 
+#include <linux/input.h>
+
+#include <memory>
+#include <vector>
+
+#include <android/hardware/health/2.0/IHealthInfoCallback.h>
+#include <android/hardware/health/2.1/IHealth.h>
+#include <health2impl/HalHealthLoop.h>
+
+#include "animation.h"
+
+class GRSurface;
+class HealthdDraw;
+
+namespace android {
+struct key_state {
+    bool pending;
+    bool down;
+    int64_t timestamp;
+};
+
+class Charger : public ::android::hardware::health::V2_1::implementation::HalHealthLoop {
+  public:
+    using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
+    using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
+
+    Charger(const sp<android::hardware::health::V2_1::IHealth>& service);
+    ~Charger();
+
+  protected:
+    // HealthLoop overrides.
+    void Heartbeat() override;
+    int PrepareToWait() override;
+    void Init(struct healthd_config* config) override;
+    // HalHealthLoop overrides
+    void OnHealthInfoChanged(const HealthInfo_2_1& health_info) override;
+
+  private:
+    void InitDefaultAnimationFrames();
+    void UpdateScreenState(int64_t now);
+    int SetKeyCallback(int code, int value);
+    void UpdateInputState(input_event* ev);
+    void SetNextKeyCheck(key_state* key, int64_t timeout);
+    void ProcessKey(int code, int64_t now);
+    void HandleInputState(int64_t now);
+    void HandlePowerSupplyState(int64_t now);
+    int InputCallback(int fd, unsigned int epevents);
+    void InitAnimation();
+
+    bool have_battery_state_ = false;
+    bool screen_blanked_ = false;
+    int64_t next_screen_transition_ = 0;
+    int64_t next_key_check_ = 0;
+    int64_t next_pwr_check_ = 0;
+    int64_t wait_batt_level_timestamp_ = 0;
+
+    key_state keys_[KEY_MAX + 1];
+
+    animation batt_anim_;
+    GRSurface* surf_unknown_ = nullptr;
+    int boot_min_cap_ = 0;
+
+    HealthInfo_1_0 health_info_ = {};
+    std::unique_ptr<HealthdDraw> healthd_draw_;
+    std::vector<animation::frame> owned_frames_;
+};
+}  // namespace android
+
 int healthd_charger_main(int argc, char** argv);
diff --git a/healthd/healthd_mode_charger_nops.cpp b/healthd/healthd_mode_charger_nops.cpp
index bcc04d5..13e7348 100644
--- a/healthd/healthd_mode_charger_nops.cpp
+++ b/healthd/healthd_mode_charger_nops.cpp
@@ -16,45 +16,14 @@
 
 #include "healthd_mode_charger_nops.h"
 
-#include <health2/Health.h>
-#include <healthd/healthd.h>
+#include <health2impl/HalHealthLoop.h>
 
-#include <stdlib.h>
-#include <string.h>
+#include "charger_utils.h"
 
-using namespace android;
-
-// main healthd loop
-extern int healthd_main(void);
-
-// NOPs for modes that need no special action
-
-static void healthd_mode_nop_init(struct healthd_config* config);
-static int healthd_mode_nop_preparetowait(void);
-static void healthd_mode_nop_heartbeat(void);
-static void healthd_mode_nop_battery_update(struct android::BatteryProperties* props);
-
-static struct healthd_mode_ops healthd_nops = {
-        .init = healthd_mode_nop_init,
-        .preparetowait = healthd_mode_nop_preparetowait,
-        .heartbeat = healthd_mode_nop_heartbeat,
-        .battery_update = healthd_mode_nop_battery_update,
-};
-
-static void healthd_mode_nop_init(struct healthd_config* config) {
-    using android::hardware::health::V2_0::implementation::Health;
-    Health::initInstance(config);
-}
-
-static int healthd_mode_nop_preparetowait(void) {
-    return -1;
-}
-
-static void healthd_mode_nop_heartbeat(void) {}
-
-static void healthd_mode_nop_battery_update(struct android::BatteryProperties* /*props*/) {}
+using android::hardware::health::GetPassthroughHealth;
+using android::hardware::health::V2_1::implementation::HalHealthLoop;
 
 int healthd_charger_nops(int /* argc */, char** /* argv */) {
-    healthd_mode_ops = &healthd_nops;
-    return healthd_main();
+    HalHealthLoop charger("charger", GetPassthroughHealth());
+    return charger.StartLoop();
 }
diff --git a/init/Android.bp b/init/Android.bp
index 776a3a6..9529617 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -65,6 +65,7 @@
         "libavb",
         "libc++fs",
         "libcgrouprc_format",
+        "liblmkd_utils",
         "libmodprobe",
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
@@ -118,6 +119,7 @@
         "init.cpp",
         "interface_utils.cpp",
         "keychords.cpp",
+        "lmkd_service.cpp",
         "modalias_handler.cpp",
         "mount_handler.cpp",
         "mount_namespace.cpp",
@@ -150,6 +152,7 @@
     whole_static_libs: [
         "libcap",
         "com.android.sysprop.apex",
+        "com.android.sysprop.init",
     ],
     header_libs: ["bootimg_headers"],
     proto: {
@@ -183,9 +186,11 @@
     static_libs: ["libinit"],
     required: [
         "e2fsdroid",
+        "init.rc",
         "mke2fs",
         "sload_f2fs",
         "make_f2fs",
+        "ueventd.rc",
     ],
     srcs: ["main.cpp"],
     symlinks: ["ueventd"],
@@ -279,6 +284,8 @@
     static_libs: [
         "libbase",
         "libselinux",
+        "libpropertyinfoserializer",
+        "libpropertyinfoparser",
     ],
     whole_static_libs: ["libcap"],
     shared_libs: [
@@ -302,6 +309,7 @@
         "host_import_parser.cpp",
         "host_init_verifier.cpp",
         "parser.cpp",
+        "property_type.cpp",
         "rlimit_parser.cpp",
         "tokenizer.cpp",
         "service.cpp",
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
index 94a02e6..667911d 100644
--- a/init/AndroidTest.xml
+++ b/init/AndroidTest.xml
@@ -17,7 +17,7 @@
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
-    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
diff --git a/init/README.md b/init/README.md
index cdf3487..e8df4ec 100644
--- a/init/README.md
+++ b/init/README.md
@@ -505,12 +505,23 @@
 > Used to mark the point right after /data is mounted. Used to implement the
   `class_reset_post_data` and `class_start_post_data` commands.
 
-`mkdir <path> [mode] [owner] [group]`
+`mkdir <path> [<mode>] [<owner>] [<group>] [encryption=<action>] [key=<key>]`
 > Create a directory at _path_, optionally with the given mode, owner, and
   group. If not provided, the directory is created with permissions 755 and
   owned by the root user and root group. If provided, the mode, owner and group
   will be updated if the directory exists already.
 
+ > _action_ can be one of:
+  * `None`: take no encryption action; directory will be encrypted if parent is.
+  * `Require`: encrypt directory, abort boot process if encryption fails
+  * `Attempt`: try to set an encryption policy, but continue if it fails
+  * `DeleteIfNecessary`: recursively delete directory if necessary to set
+  encryption policy.
+
+  > _key_ can be one of:
+  * `ref`: use the systemwide DE key
+  * `per_boot_ref`: use the key freshly generated on each boot.
+
 `mount_all <fstab> [ <path> ]\* [--<option>]`
 > Calls fs\_mgr\_mount\_all on the given fs\_mgr-format fstab with optional
   options "early" and "late".
diff --git a/init/action.cpp b/init/action.cpp
index 69e40d0..f05fa7c 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -180,21 +180,24 @@
 // It takes an optional (name, value) pair, which if provided must
 // be present in property_triggers_; it skips the check of the current
 // property value for this pair.
-bool Action::CheckPropertyTriggers(const std::string& name,
-                                   const std::string& value) const {
+bool Action::CheckPropertyTriggers(const std::string& name, const std::string& value) const {
     if (property_triggers_.empty()) {
         return true;
     }
 
-    bool found = name.empty();
+    if (!name.empty()) {
+        auto it = property_triggers_.find(name);
+        if (it == property_triggers_.end()) {
+            return false;
+        }
+        const auto& trigger_value = it->second;
+        if (trigger_value != "*" && trigger_value != value) {
+            return false;
+        }
+    }
+
     for (const auto& [trigger_name, trigger_value] : property_triggers_) {
-        if (trigger_name == name) {
-            if (trigger_value != "*" && trigger_value != value) {
-                return false;
-            } else {
-                found = true;
-            }
-        } else {
+        if (trigger_name != name) {
             std::string prop_value = android::base::GetProperty(trigger_name, "");
             if (trigger_value == "*" && !prop_value.empty()) {
                 continue;
@@ -202,7 +205,7 @@
             if (trigger_value != prop_value) return false;
         }
     }
-    return found;
+    return true;
 }
 
 bool Action::CheckEvent(const EventTrigger& event_trigger) const {
diff --git a/init/builtins.cpp b/init/builtins.cpp
index b2c6461..485806b 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -45,6 +45,7 @@
 #include <memory>
 
 #include <ApexProperties.sysprop.h>
+#include <InitProperties.sysprop.h>
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -65,6 +66,7 @@
 
 #include "action_manager.h"
 #include "bootchart.h"
+#include "builtin_arguments.h"
 #include "fscrypt_init_extensions.h"
 #include "init.h"
 #include "mount_namespace.h"
@@ -140,14 +142,7 @@
     if (!write_bootloader_message(options, &err)) {
         return Error() << "Failed to set bootloader message: " << err;
     }
-    // This function should only be reached from init and not from vendor_init, and we want to
-    // immediately trigger reboot instead of relaying through property_service.  Older devices may
-    // still have paths that reach here from vendor_init, so we keep the property_set as a fallback.
-    if (getpid() == 1) {
-        TriggerShutdown("reboot,recovery");
-    } else {
-        property_set("sys.powerctl", "reboot,recovery");
-    }
+    trigger_shutdown("reboot,recovery");
     return {};
 }
 
@@ -364,67 +359,52 @@
     return {};
 }
 
-// mkdir <path> [mode] [owner] [group]
+// mkdir <path> [mode] [owner] [group] [<option> ...]
 static Result<void> do_mkdir(const BuiltinArguments& args) {
-    mode_t mode = 0755;
-    Result<uid_t> uid = -1;
-    Result<gid_t> gid = -1;
-
-    switch (args.size()) {
-        case 5:
-            gid = DecodeUid(args[4]);
-            if (!gid) {
-                return Error() << "Unable to decode GID for '" << args[4] << "': " << gid.error();
-            }
-            FALLTHROUGH_INTENDED;
-        case 4:
-            uid = DecodeUid(args[3]);
-            if (!uid) {
-                return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
-            }
-            FALLTHROUGH_INTENDED;
-        case 3:
-            mode = std::strtoul(args[2].c_str(), 0, 8);
-            FALLTHROUGH_INTENDED;
-        case 2:
-            break;
-        default:
-            return Error() << "Unexpected argument count: " << args.size();
+    auto options = ParseMkdir(args.args);
+    if (!options) return options.error();
+    std::string ref_basename;
+    if (options->ref_option == "ref") {
+        ref_basename = fscrypt_key_ref;
+    } else if (options->ref_option == "per_boot_ref") {
+        ref_basename = fscrypt_key_per_boot_ref;
+    } else {
+        return Error() << "Unknown key option: '" << options->ref_option << "'";
     }
-    std::string target = args[1];
+
     struct stat mstat;
-    if (lstat(target.c_str(), &mstat) != 0) {
+    if (lstat(options->target.c_str(), &mstat) != 0) {
         if (errno != ENOENT) {
-            return ErrnoError() << "lstat() failed on " << target;
+            return ErrnoError() << "lstat() failed on " << options->target;
         }
-        if (!make_dir(target, mode)) {
-            return ErrnoErrorIgnoreEnoent() << "mkdir() failed on " << target;
+        if (!make_dir(options->target, options->mode)) {
+            return ErrnoErrorIgnoreEnoent() << "mkdir() failed on " << options->target;
         }
-        if (lstat(target.c_str(), &mstat) != 0) {
-            return ErrnoError() << "lstat() failed on new " << target;
+        if (lstat(options->target.c_str(), &mstat) != 0) {
+            return ErrnoError() << "lstat() failed on new " << options->target;
         }
     }
     if (!S_ISDIR(mstat.st_mode)) {
-        return Error() << "Not a directory on " << target;
+        return Error() << "Not a directory on " << options->target;
     }
-    bool needs_chmod = (mstat.st_mode & ~S_IFMT) != mode;
-    if ((*uid != static_cast<uid_t>(-1) && *uid != mstat.st_uid) ||
-        (*gid != static_cast<gid_t>(-1) && *gid != mstat.st_gid)) {
-        if (lchown(target.c_str(), *uid, *gid) == -1) {
-            return ErrnoError() << "lchown failed on " << target;
+    bool needs_chmod = (mstat.st_mode & ~S_IFMT) != options->mode;
+    if ((options->uid != static_cast<uid_t>(-1) && options->uid != mstat.st_uid) ||
+        (options->gid != static_cast<gid_t>(-1) && options->gid != mstat.st_gid)) {
+        if (lchown(options->target.c_str(), options->uid, options->gid) == -1) {
+            return ErrnoError() << "lchown failed on " << options->target;
         }
         // chown may have cleared S_ISUID and S_ISGID, chmod again
         needs_chmod = true;
     }
     if (needs_chmod) {
-        if (fchmodat(AT_FDCWD, target.c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
-            return ErrnoError() << "fchmodat() failed on " << target;
+        if (fchmodat(AT_FDCWD, options->target.c_str(), options->mode, AT_SYMLINK_NOFOLLOW) == -1) {
+            return ErrnoError() << "fchmodat() failed on " << options->target;
         }
     }
     if (fscrypt_is_native()) {
-        if (fscrypt_set_directory_policy(target)) {
+        if (!FscryptSetDirectoryPolicy(ref_basename, options->fscrypt_action, options->target)) {
             return reboot_into_recovery(
-                    {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + target});
+                    {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + options->target});
         }
     }
     return {};
@@ -562,11 +542,25 @@
  *
  * return code is processed based on input code
  */
-static Result<void> queue_fs_event(int code) {
+static Result<void> queue_fs_event(int code, bool userdata_remount) {
     if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
+        if (userdata_remount) {
+            // FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION should only happen on FDE devices. Since we don't
+            // support userdata remount on FDE devices, this should never been triggered. Time to
+            // panic!
+            LOG(ERROR) << "Userdata remount is not supported on FDE devices. How did you get here?";
+            trigger_shutdown("reboot,requested-userdata-remount-on-fde-device");
+        }
         ActionManager::GetInstance().QueueEventTrigger("encrypt");
         return {};
     } else if (code == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
+        if (userdata_remount) {
+            // FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED should only happen on FDE devices. Since we
+            // don't support userdata remount on FDE devices, this should never been triggered.
+            // Time to panic!
+            LOG(ERROR) << "Userdata remount is not supported on FDE devices. How did you get here?";
+            trigger_shutdown("reboot,requested-userdata-remount-on-fde-device");
+        }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "block");
         ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
@@ -589,8 +583,8 @@
         return reboot_into_recovery(options);
         /* If reboot worked, there is no return. */
     } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
-        if (fscrypt_install_keyring()) {
-            return Error() << "fscrypt_install_keyring() failed";
+        if (!userdata_remount && !FscryptInstallKeyring()) {
+            return Error() << "FscryptInstallKeyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -600,8 +594,8 @@
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
         return {};
     } else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
-        if (fscrypt_install_keyring()) {
-            return Error() << "fscrypt_install_keyring() failed";
+        if (!userdata_remount && !FscryptInstallKeyring()) {
+            return Error() << "FscryptInstallKeyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -611,8 +605,8 @@
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
         return {};
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
-        if (fscrypt_install_keyring()) {
-            return Error() << "fscrypt_install_keyring() failed";
+        if (!userdata_remount && !FscryptInstallKeyring()) {
+            return Error() << "FscryptInstallKeyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -679,7 +673,7 @@
         /* queue_fs_event will queue event based on mount_fstab return code
          * and return processed return code*/
         initial_mount_fstab_return_code = mount_fstab_return_code;
-        auto queue_fs_result = queue_fs_event(mount_fstab_return_code);
+        auto queue_fs_result = queue_fs_event(mount_fstab_return_code, false);
         if (!queue_fs_result) {
             return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
         }
@@ -1149,9 +1143,9 @@
     }
     // TODO(b/135984674): check that fstab contains /data.
     if (auto rc = fs_mgr_remount_userdata_into_checkpointing(&fstab); rc < 0) {
-        TriggerShutdown("reboot,mount-userdata-failed");
+        trigger_shutdown("reboot,mount-userdata-failed");
     }
-    if (auto result = queue_fs_event(initial_mount_fstab_return_code); !result) {
+    if (auto result = queue_fs_event(initial_mount_fstab_return_code, true); !result) {
         return Error() << "queue_fs_event() failed: " << result.error();
     }
     return {};
@@ -1224,6 +1218,17 @@
     }
 }
 
+static Result<void> do_finish_userspace_reboot(const BuiltinArguments&) {
+    LOG(INFO) << "Userspace reboot successfully finished";
+    boot_clock::time_point now = boot_clock::now();
+    property_set("sys.init.userspace_reboot.last_finished",
+                 std::to_string(now.time_since_epoch().count()));
+    if (!android::sysprop::InitProperties::userspace_reboot_in_progress(false)) {
+        return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
+    }
+    return {};
+}
+
 // Builtin-function-map start
 const BuiltinFunctionMap& GetBuiltinFunctionMap() {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
@@ -1245,6 +1250,7 @@
         {"exec_background",         {1,     kMax, {false,  do_exec_background}}},
         {"exec_start",              {1,     1,    {false,  do_exec_start}}},
         {"export",                  {2,     2,    {false,  do_export}}},
+        {"finish_userspace_reboot", {0,     0,    {false,  do_finish_userspace_reboot}}},
         {"hostname",                {1,     1,    {true,   do_hostname}}},
         {"ifup",                    {1,     1,    {true,   do_ifup}}},
         {"init_user0",              {0,     0,    {false,  do_init_user0}}},
@@ -1257,7 +1263,7 @@
         {"load_system_props",       {0,     0,    {false,  do_load_system_props}}},
         {"loglevel",                {1,     1,    {false,  do_loglevel}}},
         {"mark_post_data",          {0,     0,    {false,  do_mark_post_data}}},
-        {"mkdir",                   {1,     4,    {true,   do_mkdir}}},
+        {"mkdir",                   {1,     6,    {true,   do_mkdir}}},
         // TODO: Do mount operations in vendor_init.
         // mount_all is currently too complex to run in vendor_init as it queues action triggers,
         // imports rc scripts, etc.  It should be simplified and run in vendor_init context.
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
index 2efaeea..bef6966 100644
--- a/init/check_builtins.cpp
+++ b/init/check_builtins.cpp
@@ -29,7 +29,9 @@
 #include <android-base/strings.h>
 
 #include "builtin_arguments.h"
+#include "host_init_verifier.h"
 #include "interface_utils.h"
+#include "property_type.h"
 #include "rlimit_parser.h"
 #include "service.h"
 #include "util.h"
@@ -121,22 +123,10 @@
 }
 
 Result<void> check_mkdir(const BuiltinArguments& args) {
-    if (args.size() >= 4) {
-        if (!args[3].empty()) {
-            auto uid = DecodeUid(args[3]);
-            if (!uid) {
-                return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
-            }
-        }
-
-        if (args.size() == 5 && !args[4].empty()) {
-            auto gid = DecodeUid(args[4]);
-            if (!gid) {
-                return Error() << "Unable to decode GID for '" << args[4] << "': " << gid.error();
-            }
-        }
+    auto options = ParseMkdir(args.args);
+    if (!options) {
+        return options.error();
     }
-
     return {};
 }
 
@@ -183,6 +173,15 @@
                        << "' from init; use the restorecon builtin directly";
     }
 
+    const char* target_context = nullptr;
+    const char* type = nullptr;
+    property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);
+
+    if (!CheckType(type, value)) {
+        return Error() << "Property type check failed, value doesn't match expected type '"
+                       << (type ?: "(null)") << "'";
+    }
+
     return {};
 }
 
diff --git a/init/fscrypt_init_extensions.cpp b/init/fscrypt_init_extensions.cpp
index 5fa07dd..7820f3d 100644
--- a/init/fscrypt_init_extensions.cpp
+++ b/init/fscrypt_init_extensions.cpp
@@ -41,19 +41,15 @@
 
 using namespace android::fscrypt;
 
-static int set_policy_on(const std::string& ref_basename, const std::string& dir);
-
-int fscrypt_install_keyring() {
+bool FscryptInstallKeyring() {
     key_serial_t device_keyring = add_key("keyring", "fscrypt", 0, 0, KEY_SPEC_SESSION_KEYRING);
 
     if (device_keyring == -1) {
         PLOG(ERROR) << "Failed to create keyring";
-        return -1;
+        return false;
     }
-
     LOG(INFO) << "Keyring created with id " << device_keyring << " in process " << getpid();
-
-    return 0;
+    return true;
 }
 
 // TODO(b/139378601): use a single central implementation of this.
@@ -97,102 +93,57 @@
     }
 }
 
-int fscrypt_set_directory_policy(const std::string& dir) {
-    const std::string prefix = "/data/";
-
-    if (!android::base::StartsWith(dir, prefix)) {
-        return 0;
-    }
-
-    // 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_policy_on(fscrypt_key_ref, dir);
-        return 0;
-    }
-
-    // Only set policy on first level /data directories
-    // To make this less restrictive, consider using a policy file.
-    // However this is overkill for as long as the policy is simply
-    // to apply a global policy to all /data folders created via makedir
-    if (dir.find_first_of('/', prefix.size()) != std::string::npos) {
-        return 0;
-    }
-
-    // Special case various directories that must not be encrypted,
-    // often because their subdirectories must be encrypted.
-    // This isn't a nice way to do this, see b/26641735
-    std::vector<std::string> directories_to_exclude = {
-        "lost+found",
-        "system_ce", "system_de",
-        "misc_ce", "misc_de",
-        "vendor_ce", "vendor_de",
-        "media",
-        "data", "user", "user_de",
-        "apex", "preloads", "app-staging",
-        "gsi",
-    };
-    for (const auto& d : directories_to_exclude) {
-        if ((prefix + d) == dir) {
-            LOG(INFO) << "Not setting policy on " << dir;
-            return 0;
-        }
-    }
-    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;
-    }
-    // Empty these directories if policy setting fails.
-    std::vector<std::string> wipe_on_failure = {
-            "rollback", "rollback-observer",  // b/139193659
-    };
-    for (const auto& d : wipe_on_failure) {
-        if ((prefix + d) == dir) {
-            LOG(ERROR) << "Setting policy failed, deleting: " << dir;
-            delete_dir_contents(dir);
-            err = set_policy_on(fscrypt_key_ref, dir);
-            break;
-        }
-    }
-    return err;
-}
-
-// Set an encryption policy on the given directory.  The policy (key reference
+// Look up an encryption policy  The policy (key reference
 // and encryption options) to use is read from files that were written by vold.
-static int set_policy_on(const std::string& ref_basename, const std::string& dir) {
-    EncryptionPolicy policy;
+static bool LookupPolicy(const std::string& ref_basename, EncryptionPolicy* policy) {
     std::string ref_filename = std::string("/data") + ref_basename;
-    if (!android::base::ReadFileToString(ref_filename, &policy.key_raw_ref)) {
-        LOG(ERROR) << "Unable to read system policy to set on " << dir;
-        return -1;
+    if (!android::base::ReadFileToString(ref_filename, &policy->key_raw_ref)) {
+        LOG(ERROR) << "Unable to read system policy with name " << ref_filename;
+        return false;
     }
 
     auto options_filename = std::string("/data") + fscrypt_key_mode;
     std::string options_string;
     if (!android::base::ReadFileToString(options_filename, &options_string)) {
         LOG(ERROR) << "Cannot read encryption options string";
-        return -1;
+        return false;
     }
-    if (!ParseOptions(options_string, &policy.options)) {
+    if (!ParseOptions(options_string, &policy->options)) {
         LOG(ERROR) << "Invalid encryption options string: " << options_string;
-        return -1;
+        return false;
     }
+    return true;
+}
 
+static bool EnsurePolicyOrLog(const EncryptionPolicy& policy, const std::string& dir) {
     if (!EnsurePolicy(policy, dir)) {
         std::string ref_hex;
         BytesToHex(policy.key_raw_ref, &ref_hex);
         LOG(ERROR) << "Setting " << ref_hex << " policy on " << dir << " failed!";
-        return -1;
+        return false;
     }
+    return true;
+}
 
-    return 0;
+static bool SetPolicyOn(const std::string& ref_basename, const std::string& dir) {
+    EncryptionPolicy policy;
+    if (!LookupPolicy(ref_basename, &policy)) return false;
+    if (!EnsurePolicyOrLog(policy, dir)) return false;
+    return true;
+}
+
+bool FscryptSetDirectoryPolicy(const std::string& ref_basename, FscryptAction action,
+                               const std::string& dir) {
+    if (action == FscryptAction::kNone) {
+        return true;
+    }
+    if (SetPolicyOn(ref_basename, dir) || action == FscryptAction::kAttempt) {
+        return true;
+    }
+    if (action == FscryptAction::kDeleteIfNecessary) {
+        LOG(ERROR) << "Setting policy failed, deleting: " << dir;
+        delete_dir_contents(dir);
+        return SetPolicyOn(ref_basename, dir);
+    }
+    return false;
 }
diff --git a/init/fscrypt_init_extensions.h b/init/fscrypt_init_extensions.h
index 2163ef6..d357bb2 100644
--- a/init/fscrypt_init_extensions.h
+++ b/init/fscrypt_init_extensions.h
@@ -18,5 +18,13 @@
 
 #include <string>
 
-int fscrypt_install_keyring();
-int fscrypt_set_directory_policy(const std::string& dir);
+enum class FscryptAction {
+    kNone,
+    kAttempt,
+    kRequire,
+    kDeleteIfNecessary,
+};
+
+bool FscryptInstallKeyring();
+bool FscryptSetDirectoryPolicy(const std::string& ref_basename, FscryptAction action,
+                               const std::string& dir);
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index 9b33a1c..30d3129 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -35,11 +35,6 @@
 namespace android {
 namespace init {
 
-// init.h
-inline void TriggerShutdown(const std::string&) {
-    abort();
-}
-
 // property_service.h
 inline bool CanReadProperty(const std::string&, const std::string&) {
     return true;
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index 522709e..3acc3cc 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -14,6 +14,8 @@
 // limitations under the License.
 //
 
+#include "host_init_verifier.h"
+
 #include <errno.h>
 #include <getopt.h>
 #include <pwd.h>
@@ -31,6 +33,7 @@
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 #include <hidl/metadata.h>
+#include <property_info_serializer/property_info_serializer.h>
 
 #include "action.h"
 #include "action_manager.h"
@@ -53,6 +56,10 @@
 using android::base::ParseInt;
 using android::base::ReadFileToString;
 using android::base::Split;
+using android::properties::BuildTrie;
+using android::properties::ParsePropertyInfoFile;
+using android::properties::PropertyInfoArea;
+using android::properties::PropertyInfoEntry;
 
 static std::vector<std::string> passwd_files;
 
@@ -143,11 +150,12 @@
 #include "generated_stub_builtin_function_map.h"
 
 void PrintUsage() {
-    std::cout << "usage: host_init_verifier [-p FILE] <init rc file>\n"
+    std::cout << "usage: host_init_verifier [options] <init rc file>\n"
                  "\n"
                  "Tests an init script for correctness\n"
                  "\n"
                  "-p FILE\tSearch this passwd file for users and groups\n"
+                 "--property_contexts=FILE\t Use this file for property_contexts\n"
               << std::endl;
 }
 
@@ -172,23 +180,53 @@
     return result;
 }
 
+const PropertyInfoArea* property_info_area;
+
+void HandlePropertyContexts(const std::string& filename,
+                            std::vector<PropertyInfoEntry>* property_infos) {
+    auto file_contents = std::string();
+    if (!ReadFileToString(filename, &file_contents)) {
+        PLOG(ERROR) << "Could not read properties from '" << filename << "'";
+        exit(EXIT_FAILURE);
+    }
+
+    auto errors = std::vector<std::string>{};
+    ParsePropertyInfoFile(file_contents, property_infos, &errors);
+    for (const auto& error : errors) {
+        LOG(ERROR) << "Could not read line from '" << filename << "': " << error;
+    }
+    if (!errors.empty()) {
+        exit(EXIT_FAILURE);
+    }
+}
+
 int main(int argc, char** argv) {
     android::base::InitLogging(argv, &android::base::StdioLogger);
     android::base::SetMinimumLogSeverity(android::base::ERROR);
 
+    auto property_infos = std::vector<PropertyInfoEntry>();
+
     while (true) {
+        static const char kPropertyContexts[] = "property-contexts=";
         static const struct option long_options[] = {
                 {"help", no_argument, nullptr, 'h'},
+                {kPropertyContexts, required_argument, nullptr, 0},
                 {nullptr, 0, nullptr, 0},
         };
 
-        int arg = getopt_long(argc, argv, "p:", long_options, nullptr);
+        int option_index;
+        int arg = getopt_long(argc, argv, "p:", long_options, &option_index);
 
         if (arg == -1) {
             break;
         }
 
         switch (arg) {
+            case 0:
+                if (long_options[option_index].name == kPropertyContexts) {
+                    HandlePropertyContexts(optarg, &property_infos);
+                }
+                break;
             case 'h':
                 PrintUsage();
                 return EXIT_FAILURE;
@@ -216,6 +254,16 @@
     }
     SetKnownInterfaces(*interface_inheritance_hierarchy_map);
 
+    std::string serialized_contexts;
+    std::string trie_error;
+    if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "string", &serialized_contexts,
+                   &trie_error)) {
+        LOG(ERROR) << "Unable to serialize property contexts: " << trie_error;
+        return EXIT_FAILURE;
+    }
+
+    property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_contexts.c_str());
+
     const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
     Action::set_function_map(&function_map);
     ActionManager& am = ActionManager::GetInstance();
diff --git a/init/host_init_verifier.h b/init/host_init_verifier.h
new file mode 100644
index 0000000..5d24f2a
--- /dev/null
+++ b/init/host_init_verifier.h
@@ -0,0 +1,27 @@
+/*
+ * 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 <property_info_parser/property_info_parser.h>
+
+namespace android {
+namespace init {
+
+extern const android::properties::PropertyInfoArea* property_info_area;
+
+}  // namespace init
+}  // namespace android
diff --git a/init/init.cpp b/init/init.cpp
index f775d8f..8e2da59 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -59,6 +59,7 @@
 #include "first_stage_mount.h"
 #include "import_parser.h"
 #include "keychords.h"
+#include "lmkd_service.h"
 #include "mount_handler.h"
 #include "mount_namespace.h"
 #include "property_service.h"
@@ -135,7 +136,7 @@
 
     std::string bootscript = GetProperty("ro.boot.init_rc", "");
     if (bootscript.empty()) {
-        parser.ParseConfig("/init.rc");
+        parser.ParseConfig("/system/etc/init/hw/init.rc");
         if (!parser.ParseConfig("/system/etc/init")) {
             late_import_paths.emplace_back("/system/etc/init");
         }
@@ -179,7 +180,7 @@
     waiting_for_prop.reset();
 }
 
-void TriggerShutdown(const std::string& command) {
+static void TriggerShutdown(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
@@ -680,13 +681,21 @@
 
     boot_clock::time_point start_time = boot_clock::now();
 
+    trigger_shutdown = TriggerShutdown;
+
     SetStdioToDevNull(argv);
     InitKernelLogging(argv);
     LOG(INFO) << "init second stage started!";
 
+    // Will handle EPIPE at the time of write by checking the errno
+    signal(SIGPIPE, SIG_IGN);
+
     // Set init and its forked children's oom_adj.
-    if (auto result = WriteFile("/proc/1/oom_score_adj", "-1000"); !result) {
-        LOG(ERROR) << "Unable to write -1000 to /proc/1/oom_score_adj: " << result.error();
+    if (auto result =
+                WriteFile("/proc/1/oom_score_adj", StringPrintf("%d", DEFAULT_OOM_SCORE_ADJUST));
+        !result) {
+        LOG(ERROR) << "Unable to write " << DEFAULT_OOM_SCORE_ADJUST
+                   << " to /proc/1/oom_score_adj: " << result.error();
     }
 
     // Set up a session keyring that all processes will have access to. It
diff --git a/init/init.h b/init/init.h
index d884a94..0805940 100644
--- a/init/init.h
+++ b/init/init.h
@@ -31,8 +31,6 @@
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
 Parser CreateServiceOnlyParser(ServiceList& service_list);
 
-void TriggerShutdown(const std::string& command);
-
 bool start_waiting_for_property(const char *name, const char *value);
 
 void DumpState();
diff --git a/init/lmkd_service.cpp b/init/lmkd_service.cpp
new file mode 100644
index 0000000..dd1ab4d
--- /dev/null
+++ b/init/lmkd_service.cpp
@@ -0,0 +1,131 @@
+/*
+ * 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 "lmkd_service.h"
+
+#include <errno.h>
+
+#include <android-base/logging.h>
+#include <liblmkd_utils.h>
+
+#include "service_list.h"
+
+namespace android {
+namespace init {
+
+enum LmkdRegistrationResult {
+    LMKD_REG_SUCCESS,
+    LMKD_CONN_FAILED,
+    LMKD_REG_FAILED,
+};
+
+static int lmkd_socket = -1;
+
+static LmkdRegistrationResult RegisterProcess(uid_t uid, pid_t pid, int oom_score_adjust) {
+    // connect to lmkd if not already connected
+    if (lmkd_socket < 0) {
+        lmkd_socket = lmkd_connect();
+        if (lmkd_socket < 0) {
+            return LMKD_CONN_FAILED;
+        }
+    }
+
+    // register service with lmkd
+    struct lmk_procprio params;
+    params.pid = pid;
+    params.uid = uid;
+    params.oomadj = oom_score_adjust;
+    params.ptype = PROC_TYPE_SERVICE;
+    if (lmkd_register_proc(lmkd_socket, &params) != 0) {
+        // data transfer failed, reset the connection
+        close(lmkd_socket);
+        lmkd_socket = -1;
+        return LMKD_REG_FAILED;
+    }
+
+    return LMKD_REG_SUCCESS;
+}
+
+static bool UnregisterProcess(pid_t pid) {
+    if (lmkd_socket < 0) {
+        // no connection or it was lost, no need to unregister
+        return false;
+    }
+
+    // unregister service
+    struct lmk_procremove params;
+    params.pid = pid;
+    if (lmkd_unregister_proc(lmkd_socket, &params) != 0) {
+        // data transfer failed, reset the connection
+        close(lmkd_socket);
+        lmkd_socket = -1;
+        return false;
+    }
+
+    return true;
+}
+
+static void RegisterServices(pid_t exclude_pid) {
+    for (const auto& service : ServiceList::GetInstance().services()) {
+        auto svc = service.get();
+        if (svc->oom_score_adjust() != DEFAULT_OOM_SCORE_ADJUST) {
+            // skip if process is excluded or not yet forked (pid==0)
+            if (svc->pid() == exclude_pid || svc->pid() == 0) {
+                continue;
+            }
+            if (RegisterProcess(svc->uid(), svc->pid(), svc->oom_score_adjust()) !=
+                LMKD_REG_SUCCESS) {
+                // a failure here resets the connection, will retry during next registration
+                break;
+            }
+        }
+    }
+}
+
+void LmkdRegister(const std::string& name, uid_t uid, pid_t pid, int oom_score_adjust) {
+    bool new_connection = lmkd_socket == -1;
+    LmkdRegistrationResult result;
+
+    result = RegisterProcess(uid, pid, oom_score_adjust);
+    if (result == LMKD_REG_FAILED) {
+        // retry one time if connection to lmkd was lost
+        result = RegisterProcess(uid, pid, oom_score_adjust);
+        new_connection = result == LMKD_REG_SUCCESS;
+    }
+    switch (result) {
+        case LMKD_REG_SUCCESS:
+            // register existing services once new connection is established
+            if (new_connection) {
+                RegisterServices(pid);
+            }
+            break;
+        case LMKD_CONN_FAILED:
+            PLOG(ERROR) << "lmkd connection failed when " << name << " process got started";
+            break;
+        case LMKD_REG_FAILED:
+            PLOG(ERROR) << "lmkd failed to register " << name << " process";
+            break;
+    }
+}
+
+void LmkdUnregister(const std::string& name, pid_t pid) {
+    if (!UnregisterProcess(pid)) {
+        PLOG(ERROR) << "lmkd failed to unregister " << name << " process";
+    }
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/lmkd_service.h b/init/lmkd_service.h
new file mode 100644
index 0000000..5b51d52
--- /dev/null
+++ b/init/lmkd_service.h
@@ -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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <string>
+
+namespace android {
+namespace init {
+
+static const int MIN_OOM_SCORE_ADJUST = -1000;
+static const int MAX_OOM_SCORE_ADJUST = 1000;
+// service with default score is unkillable
+static const int DEFAULT_OOM_SCORE_ADJUST = MIN_OOM_SCORE_ADJUST;
+
+#if defined(__ANDROID__)
+
+void LmkdRegister(const std::string& name, uid_t uid, pid_t pid, int oom_score_adjust);
+void LmkdUnregister(const std::string& name, pid_t pid);
+
+#else  // defined(__ANDROID__)
+
+static inline void LmkdRegister(const std::string&, uid_t, pid_t, int) {}
+static inline void LmkdUnregister(const std::string&, pid_t) {}
+
+#endif  // defined(__ANDROID__)
+
+}  // namespace init
+}  // namespace android
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 3baaf7c..7d707cc 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -478,7 +478,7 @@
         return PROP_ERROR_PERMISSION_DENIED;
     }
 
-    if (type == nullptr || !CheckType(type, value)) {
+    if (!CheckType(type, value)) {
         *error = StringPrintf("Property type check failed, value doesn't match expected type '%s'",
                               (type ?: "(null)"));
         return PROP_ERROR_INVALID_VALUE;
diff --git a/init/reboot.cpp b/init/reboot.cpp
index fc18ecb..c9b521a 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -38,6 +38,7 @@
 #include <thread>
 #include <vector>
 
+#include <InitProperties.sysprop.h>
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -57,6 +58,7 @@
 #include "action_manager.h"
 #include "builtin_arguments.h"
 #include "init.h"
+#include "mount_namespace.h"
 #include "property_service.h"
 #include "reboot_utils.h"
 #include "service.h"
@@ -68,10 +70,13 @@
 
 using namespace std::literals;
 
+using android::base::boot_clock;
 using android::base::GetBoolProperty;
+using android::base::SetProperty;
 using android::base::Split;
 using android::base::Timer;
 using android::base::unique_fd;
+using android::base::WaitForProperty;
 using android::base::WriteStringToFile;
 
 namespace android {
@@ -713,18 +718,35 @@
     SendStartSendingMessagesMessage();
 }
 
+static Result<void> UnmountAllApexes() {
+    const char* args[] = {"/system/bin/apexd", "--unmount-all"};
+    int status;
+    if (logwrap_fork_execvp(arraysize(args), args, &status, false, LOG_KLOG, true, nullptr) != 0) {
+        return ErrnoError() << "Failed to call '/system/bin/apexd --unmount-all'";
+    }
+    if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+        return {};
+    }
+    return Error() << "'/system/bin/apexd --unmount-all' failed : " << status;
+}
+
 static Result<void> DoUserspaceReboot() {
     LOG(INFO) << "Userspace reboot initiated";
+    boot_clock::time_point now = boot_clock::now();
+    property_set("sys.init.userspace_reboot.last_started",
+                 std::to_string(now.time_since_epoch().count()));
     auto guard = android::base::make_scope_guard([] {
         // Leave shutdown so that we can handle a full reboot.
         LeaveShutdown();
-        TriggerShutdown("reboot,abort-userspace-reboot");
+        trigger_shutdown("reboot,abort-userspace-reboot");
     });
-    // Triggering userspace-reboot-requested will result in a bunch of set_prop
+    // Triggering userspace-reboot-requested will result in a bunch of setprop
     // actions. We should make sure, that all of them are propagated before
-    // proceeding with userspace reboot.
-    // TODO(b/135984674): implement proper synchronization logic.
-    std::this_thread::sleep_for(500ms);
+    // proceeding with userspace reboot. Synchronously setting kUserspaceRebootInProgress property
+    // is not perfect, but it should do the trick.
+    if (!android::sysprop::InitProperties::userspace_reboot_in_progress(true)) {
+        return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
+    }
     EnterShutdown();
     std::vector<Service*> stop_first;
     // Remember the services that were enabled. We will need to manually enable them again otherwise
@@ -739,6 +761,12 @@
             were_enabled.push_back(s);
         }
     }
+    {
+        Timer sync_timer;
+        LOG(INFO) << "sync() before terminating services...";
+        sync();
+        LOG(INFO) << "sync() took " << sync_timer;
+    }
     // TODO(b/135984674): do we need shutdown animation for userspace reboot?
     // TODO(b/135984674): control userspace timeout via read-only property?
     StopServicesAndLogViolations(stop_first, 10s, true /* SIGTERM */);
@@ -746,30 +774,26 @@
         // TODO(b/135984674): store information about offending services for debugging.
         return Error() << r << " post-data services are still running";
     }
-    // We only really need to restart vold if userdata is ext4 filesystem.
-    // TODO(b/135984674): get userdata fs type here, and do nothing in case of f2fs.
-    // First shutdown volumes managed by vold. They will be recreated by
-    // system_server.
-    Service* vold_service = ServiceList::GetInstance().FindService("vold");
-    if (vold_service != nullptr && vold_service->IsRunning()) {
-        if (auto result = ShutdownVold(); !result) {
-            return result;
-        }
-        LOG(INFO) << "Restarting vold";
-        vold_service->Restart();
-    }
-    // Again, we only need to kill zram backing device in case of ext4 userdata.
-    // TODO(b/135984674): get userdata fs type here, and do nothing in case of f2fs.
-    if (auto result = KillZramBackingDevice(); !result) {
-        return result;
-    }
+    // TODO(b/143970043): in case of ext4 we probably we will need to restart vold and kill zram
+    //  backing device.
     if (int r = StopServicesAndLogViolations(GetDebuggingServices(true /* only_post_data */), 5s,
                                              false /* SIGKILL */);
         r > 0) {
         // TODO(b/135984674): store information about offending services for debugging.
         return Error() << r << " debugging services are still running";
     }
-    // TODO(b/135984674): deactivate APEX modules and switch back to bootstrap namespace.
+    {
+        Timer sync_timer;
+        LOG(INFO) << "sync() after stopping services...";
+        sync();
+        LOG(INFO) << "sync() took " << sync_timer;
+    }
+    if (auto result = UnmountAllApexes(); !result) {
+        return result;
+    }
+    if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
+        return Error() << "Failed to switch to bootstrap namespace";
+    }
     // Re-enable services
     for (const auto& s : were_enabled) {
         LOG(INFO) << "Re-enabling service '" << s->name() << "'";
@@ -781,7 +805,38 @@
     return {};
 }
 
+static void UserspaceRebootWatchdogThread() {
+    if (!WaitForProperty("sys.init.userspace_reboot.in_progress", "1", 20s)) {
+        // TODO(b/135984674): should we reboot instead?
+        LOG(WARNING) << "Userspace reboot didn't start in 20 seconds. Stopping watchdog";
+        return;
+    }
+    LOG(INFO) << "Starting userspace reboot watchdog";
+    // TODO(b/135984674): this should be configured via a read-only sysprop.
+    std::chrono::milliseconds timeout = 60s;
+    if (!WaitForProperty("sys.boot_completed", "1", timeout)) {
+        LOG(ERROR) << "Failed to boot in " << timeout.count() << "ms. Switching to full reboot";
+        // In this case device is in a boot loop. Only way to recover is to do dirty reboot.
+        RebootSystem(ANDROID_RB_RESTART2, "userspace-reboot-watchdog-triggered");
+    }
+    LOG(INFO) << "Device booted, stopping userspace reboot watchdog";
+}
+
 static void HandleUserspaceReboot() {
+    // Spinnig up a separate thread will fail the setns call later in the boot sequence.
+    // Fork a new process to monitor userspace reboot while we are investigating a better solution.
+    pid_t pid = fork();
+    if (pid < 0) {
+        PLOG(ERROR) << "Failed to fork process for userspace reboot watchdog. Switching to full "
+                    << "reboot";
+        trigger_shutdown("reboot,userspace-reboot-failed-to-fork");
+        return;
+    }
+    if (pid == 0) {
+        // Child
+        UserspaceRebootWatchdogThread();
+        _exit(EXIT_SUCCESS);
+    }
     LOG(INFO) << "Clearing queue and starting userspace-reboot-requested trigger";
     auto& am = ActionManager::GetInstance();
     am.ClearQueue();
diff --git a/init/selinux.cpp b/init/selinux.cpp
index a15d136..a9cd290 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -514,9 +514,6 @@
 
 }  // namespace
 
-// The files and directories that were created before initial sepolicy load or
-// files on ramdisk need to have their security context restored to the proper
-// value. This must happen before /dev is populated by ueventd.
 void SelinuxRestoreContext() {
     LOG(INFO) << "Running restorecon...";
     selinux_android_restorecon("/dev", 0);
@@ -560,15 +557,12 @@
     return 0;
 }
 
-// This function sets up SELinux logging to be written to kmsg, to match init's logging.
 void SelinuxSetupKernelLogging() {
     selinux_callback cb;
     cb.func_log = SelinuxKlogCallback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 }
 
-// This function returns the Android version with which the vendor SEPolicy was compiled.
-// It is used for version checks such as whether or not vendor_init should be used
 int SelinuxGetVendorAndroidVersion() {
     static int vendor_android_version = [] {
         if (!IsSplitPolicyDevice()) {
@@ -594,7 +588,6 @@
     return vendor_android_version;
 }
 
-// This function initializes SELinux then execs init to run in the init SELinux context.
 int SetupSelinux(char** argv) {
     SetStdioToDevNull(argv);
     InitKernelLogging(argv);
diff --git a/init/selinux.h b/init/selinux.h
index 63ad470..1a41bfd 100644
--- a/init/selinux.h
+++ b/init/selinux.h
@@ -19,10 +19,19 @@
 namespace android {
 namespace init {
 
+// Initialize SELinux, then exec init to run in the init SELinux context.
 int SetupSelinux(char** argv);
+
+// Restore the proper security context to files and directories on ramdisk, and
+// those that were created before initial sepolicy load.
+// This must happen before /dev is populated by ueventd.
 void SelinuxRestoreContext();
 
+// Set up SELinux logging to be written to kmsg, to match init's logging.
 void SelinuxSetupKernelLogging();
+
+// Return the Android API level with which the vendor SEPolicy was compiled.
+// Used for version checks such as whether or not vendor_init should be used.
 int SelinuxGetVendorAndroidVersion();
 
 static constexpr char kEnvSelinuxStartedAt[] = "SELINUX_STARTED_AT";
diff --git a/init/service.cpp b/init/service.cpp
index c8568a0..574ff52 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -36,13 +36,13 @@
 #include <processgroup/processgroup.h>
 #include <selinux/selinux.h>
 
+#include "lmkd_service.h"
 #include "service_list.h"
 #include "util.h"
 
 #if defined(__ANDROID__)
 #include <ApexProperties.sysprop.h>
 
-#include "init.h"
 #include "mount_namespace.h"
 #include "property_service.h"
 #else
@@ -151,7 +151,7 @@
       seclabel_(seclabel),
       onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
                  "onrestart", {}),
-      oom_score_adjust_(-1000),
+      oom_score_adjust_(DEFAULT_OOM_SCORE_ADJUST),
       start_order_(0),
       args_(args) {}
 
@@ -199,6 +199,10 @@
 
         if (r == 0) process_cgroup_empty_ = true;
     }
+
+    if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
+        LmkdUnregister(name_, pid_);
+    }
 }
 
 void Service::SetProcessAttributesAndCaps() {
@@ -255,7 +259,7 @@
 
     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.";
-        TriggerShutdown(*on_failure_reboot_target_);
+        trigger_shutdown(*on_failure_reboot_target_);
     }
 
     if (flags_ & SVC_EXEC) UnSetExec();
@@ -335,7 +339,7 @@
 Result<void> Service::ExecStart() {
     auto reboot_on_failure = make_scope_guard([this] {
         if (on_failure_reboot_target_) {
-            TriggerShutdown(*on_failure_reboot_target_);
+            trigger_shutdown(*on_failure_reboot_target_);
         }
     });
 
@@ -366,7 +370,7 @@
 Result<void> Service::Start() {
     auto reboot_on_failure = make_scope_guard([this] {
         if (on_failure_reboot_target_) {
-            TriggerShutdown(*on_failure_reboot_target_);
+            trigger_shutdown(*on_failure_reboot_target_);
         }
     });
 
@@ -502,7 +506,7 @@
         return ErrnoError() << "Failed to fork";
     }
 
-    if (oom_score_adjust_ != -1000) {
+    if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
         std::string oom_str = std::to_string(oom_score_adjust_);
         std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
         if (!WriteStringToFile(oom_str, oom_file)) {
@@ -563,6 +567,10 @@
         }
     }
 
+    if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
+        LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
+    }
+
     NotifyStateChange("running");
     reboot_on_failure.Disable();
     return {};
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index e6a341d..154d1dd 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -29,6 +29,7 @@
 #include <hidl-util/FQName.h>
 #include <system/thread_defs.h>
 
+#include "lmkd_service.h"
 #include "rlimit_parser.h"
 #include "service_utils.h"
 #include "util.h"
@@ -261,8 +262,10 @@
 }
 
 Result<void> ServiceParser::ParseOomScoreAdjust(std::vector<std::string>&& args) {
-    if (!ParseInt(args[1], &service_->oom_score_adjust_, -1000, 1000)) {
-        return Error() << "oom_score_adjust value must be in range -1000 - +1000";
+    if (!ParseInt(args[1], &service_->oom_score_adjust_, MIN_OOM_SCORE_ADJUST,
+                  MAX_OOM_SCORE_ADJUST)) {
+        return Error() << "oom_score_adjust value must be in range " << MIN_OOM_SCORE_ADJUST
+                       << " - +" << MAX_OOM_SCORE_ADJUST;
     }
     return {};
 }
diff --git a/init/service_test.cpp b/init/service_test.cpp
index c9cc7bd..c158b0a 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -23,6 +23,7 @@
 
 #include <gtest/gtest.h>
 
+#include "lmkd_service.h"
 #include "util.h"
 
 namespace android {
@@ -49,7 +50,7 @@
     EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory->ioprio_class());
     EXPECT_EQ(0, service_in_old_memory->ioprio_pri());
     EXPECT_EQ(0, service_in_old_memory->priority());
-    EXPECT_EQ(-1000, service_in_old_memory->oom_score_adjust());
+    EXPECT_EQ(DEFAULT_OOM_SCORE_ADJUST, service_in_old_memory->oom_score_adjust());
     EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
 
     for (std::size_t i = 0; i < memory_size; ++i) {
@@ -68,7 +69,7 @@
     EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory2->ioprio_class());
     EXPECT_EQ(0, service_in_old_memory2->ioprio_pri());
     EXPECT_EQ(0, service_in_old_memory2->priority());
-    EXPECT_EQ(-1000, service_in_old_memory2->oom_score_adjust());
+    EXPECT_EQ(DEFAULT_OOM_SCORE_ADJUST, service_in_old_memory2->oom_score_adjust());
     EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
 }
 
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 79fc372..bebcc77 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -51,6 +51,8 @@
 namespace init {
 namespace {
 
+std::string shutdown_command;
+
 class SubcontextProcess {
   public:
     SubcontextProcess(const BuiltinFunctionMap* function_map, std::string context, int init_fd)
@@ -153,6 +155,11 @@
                            << subcontext_command.command_case();
         }
 
+        if (!shutdown_command.empty()) {
+            reply.set_trigger_shutdown(shutdown_command);
+            shutdown_command.clear();
+        }
+
         if (auto result = SendMessage(init_fd_, reply); !result) {
             LOG(FATAL) << "Failed to send message to init: " << result.error();
         }
@@ -174,6 +181,8 @@
         return 0;
     };
 
+    trigger_shutdown = [](const std::string& command) { shutdown_command = command; };
+
     auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
     subcontext_process.MainLoop();
     return 0;
@@ -200,8 +209,12 @@
             PLOG(FATAL) << "Could not dup child_fd";
         }
 
-        if (setexeccon(context_.c_str()) < 0) {
-            PLOG(FATAL) << "Could not set execcon for '" << context_ << "'";
+        // We don't switch contexts if we're running the unit tests.  We don't use std::optional,
+        // since we still need a real context string to pass to the builtin functions.
+        if (context_ != kTestContext) {
+            if (setexeccon(context_.c_str()) < 0) {
+                PLOG(FATAL) << "Could not set execcon for '" << context_ << "'";
+            }
         }
 
         auto init_path = GetExecutablePath();
@@ -254,6 +267,11 @@
         Restart();
         return Error() << "Unable to parse message from subcontext";
     }
+
+    if (subcontext_reply.has_trigger_shutdown()) {
+        trigger_shutdown(subcontext_reply.trigger_shutdown());
+    }
+
     return subcontext_reply;
 }
 
diff --git a/init/subcontext.h b/init/subcontext.h
index bcaad29..5e1d8a8 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -32,6 +32,7 @@
 
 static constexpr const char kInitContext[] = "u:r:init:s0";
 static constexpr const char kVendorContext[] = "u:r:vendor_init:s0";
+static constexpr const char kTestContext[] = "test-test-test";
 
 class Subcontext {
   public:
diff --git a/init/subcontext.proto b/init/subcontext.proto
index e68115e..068c7ce 100644
--- a/init/subcontext.proto
+++ b/init/subcontext.proto
@@ -38,4 +38,6 @@
         Failure failure = 2;
         ExpandArgsReply expand_args_reply = 3;
     }
+
+    optional string trigger_shutdown = 4;
 }
\ No newline at end of file
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index 9cac35e..2e5a256 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -26,6 +26,7 @@
 #include <selinux/selinux.h>
 
 #include "builtin_arguments.h"
+#include "util.h"
 
 using namespace std::literals;
 
@@ -38,24 +39,12 @@
 namespace android {
 namespace init {
 
-// I would use test fixtures, but I cannot skip the test if not root with them, so instead we have
-// this test runner.
 template <typename F>
 void RunTest(F&& test_function) {
-    if (getuid() != 0) {
-        GTEST_SKIP() << "Skipping test, must be run as root.";
-        return;
-    }
-
-    char* context;
-    ASSERT_EQ(0, getcon(&context));
-    auto context_string = std::string(context);
-    free(context);
-
-    auto subcontext = Subcontext({"dummy_path"}, context_string);
+    auto subcontext = Subcontext({"dummy_path"}, kTestContext);
     ASSERT_NE(0, subcontext.pid());
 
-    test_function(subcontext, context_string);
+    test_function(subcontext);
 
     if (subcontext.pid() > 0) {
         kill(subcontext.pid(), SIGTERM);
@@ -64,7 +53,7 @@
 }
 
 TEST(subcontext, CheckDifferentPid) {
-    RunTest([](auto& subcontext, auto& context_string) {
+    RunTest([](auto& subcontext) {
         auto result = subcontext.Execute(std::vector<std::string>{"return_pids_as_error"});
         ASSERT_FALSE(result);
 
@@ -77,7 +66,12 @@
 }
 
 TEST(subcontext, SetProp) {
-    RunTest([](auto& subcontext, auto& context_string) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Skipping test, must be run as root.";
+        return;
+    }
+
+    RunTest([](auto& subcontext) {
         SetProperty("init.test.subcontext", "fail");
         WaitForProperty("init.test.subcontext", "fail");
 
@@ -94,7 +88,7 @@
 }
 
 TEST(subcontext, MultipleCommands) {
-    RunTest([](auto& subcontext, auto& context_string) {
+    RunTest([](auto& subcontext) {
         auto first_pid = subcontext.pid();
 
         auto expected_words = std::vector<std::string>{
@@ -121,7 +115,7 @@
 }
 
 TEST(subcontext, RecoverAfterAbort) {
-    RunTest([](auto& subcontext, auto& context_string) {
+    RunTest([](auto& subcontext) {
         auto first_pid = subcontext.pid();
 
         auto result = subcontext.Execute(std::vector<std::string>{"cause_log_fatal"});
@@ -135,15 +129,27 @@
 }
 
 TEST(subcontext, ContextString) {
-    RunTest([](auto& subcontext, auto& context_string) {
+    RunTest([](auto& subcontext) {
         auto result = subcontext.Execute(std::vector<std::string>{"return_context_as_error"});
         ASSERT_FALSE(result);
-        ASSERT_EQ(context_string, result.error().message());
+        ASSERT_EQ(kTestContext, result.error().message());
     });
 }
 
+TEST(subcontext, TriggerShutdown) {
+    static constexpr const char kTestShutdownCommand[] = "reboot,test-shutdown-command";
+    static std::string trigger_shutdown_command;
+    trigger_shutdown = [](const std::string& command) { trigger_shutdown_command = command; };
+    RunTest([](auto& subcontext) {
+        auto result = subcontext.Execute(
+                std::vector<std::string>{"trigger_shutdown", kTestShutdownCommand});
+        ASSERT_TRUE(result);
+    });
+    EXPECT_EQ(kTestShutdownCommand, trigger_shutdown_command);
+}
+
 TEST(subcontext, ExpandArgs) {
-    RunTest([](auto& subcontext, auto& context_string) {
+    RunTest([](auto& subcontext) {
         auto args = std::vector<std::string>{
             "first",
             "${ro.hardware}",
@@ -159,7 +165,7 @@
 }
 
 TEST(subcontext, ExpandArgsFailure) {
-    RunTest([](auto& subcontext, auto& context_string) {
+    RunTest([](auto& subcontext) {
         auto args = std::vector<std::string>{
             "first",
             "${",
@@ -207,6 +213,11 @@
         return Error() << args.context;
     };
 
+    auto do_trigger_shutdown = [](const BuiltinArguments& args) -> Result<void> {
+        trigger_shutdown(args[1]);
+        return {};
+    };
+
     // clang-format off
     BuiltinFunctionMap test_function_map = {
         {"return_pids_as_error",        {0,     0,      {true,  do_return_pids_as_error}}},
@@ -216,6 +227,7 @@
         {"cause_log_fatal",             {0,     0,      {true,  do_cause_log_fatal}}},
         {"generate_sane_error",         {0,     0,      {true,  do_generate_sane_error}}},
         {"return_context_as_error",     {0,     0,      {true,  do_return_context_as_error}}},
+        {"trigger_shutdown",            {1,     1,      {true,  do_trigger_shutdown}}},
     };
     // clang-format on
     return test_function_map;
diff --git a/init/sysprop/Android.bp b/init/sysprop/Android.bp
new file mode 100644
index 0000000..7582875
--- /dev/null
+++ b/init/sysprop/Android.bp
@@ -0,0 +1,7 @@
+sysprop_library {
+  name: "com.android.sysprop.init",
+  srcs: ["InitProperties.sysprop"],
+  property_owner: "Platform",
+  api_packages: ["android.sysprop"],
+  recovery_available: true,
+}
diff --git a/init/sysprop/InitProperties.sysprop b/init/sysprop/InitProperties.sysprop
new file mode 100644
index 0000000..d6a1ab6
--- /dev/null
+++ b/init/sysprop/InitProperties.sysprop
@@ -0,0 +1,27 @@
+# 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.
+
+owner: Platform
+module: "android.sysprop.InitProperties"
+
+# Serves as a signal to all processes that userspace reboot is happening.
+prop {
+    api_name: "userspace_reboot_in_progress"
+    type: Boolean
+    scope: Public
+    access: ReadWrite
+    prop_name: "sys.init.userspace_reboot.in_progress"
+    integer_as_bool: true
+}
+
diff --git a/init/sysprop/api/com.android.sysprop.init-current.txt b/init/sysprop/api/com.android.sysprop.init-current.txt
new file mode 100644
index 0000000..8da50e0
--- /dev/null
+++ b/init/sysprop/api/com.android.sysprop.init-current.txt
@@ -0,0 +1,9 @@
+props {
+  module: "android.sysprop.InitProperties"
+  prop {
+    api_name: "userspace_reboot_in_progress"
+    access: ReadWrite
+    prop_name: "sys.init.userspace_reboot.in_progress"
+    integer_as_bool: true
+  }
+}
diff --git a/init/sysprop/api/com.android.sysprop.init-latest.txt b/init/sysprop/api/com.android.sysprop.init-latest.txt
new file mode 100644
index 0000000..c835b95
--- /dev/null
+++ b/init/sysprop/api/com.android.sysprop.init-latest.txt
@@ -0,0 +1,9 @@
+props {
+  module: "android.sysprop.InitProperties"
+  prop {
+    api_name: "userspace_reboot_in_progress"
+    scope: Public
+    prop_name: "sys.init.userspace_reboot.in_progress"
+    integer_as_bool: true
+  }
+}
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 416d942..d8d9b36 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -100,7 +100,7 @@
     int n = uevent_kernel_multicast_recv(device_fd_, msg, UEVENT_MSG_LEN);
     if (n <= 0) {
         if (errno != EAGAIN && errno != EWOULDBLOCK) {
-            LOG(ERROR) << "Error reading from Uevent Fd";
+            PLOG(ERROR) << "Error reading from Uevent Fd";
         }
         return false;
     }
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 59f91ee..d2b503b 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -288,7 +288,7 @@
     // TODO: cleanup platform ueventd.rc to remove vendor specific device node entries (b/34968103)
     auto hardware = android::base::GetProperty("ro.hardware", "");
 
-    auto ueventd_configuration = ParseConfig({"/ueventd.rc", "/vendor/ueventd.rc",
+    auto ueventd_configuration = ParseConfig({"/system/etc/ueventd.rc", "/vendor/ueventd.rc",
                                               "/odm/ueventd.rc", "/ueventd." + hardware + ".rc"});
 
     uevent_handlers.emplace_back(std::make_unique<DeviceHandler>(
diff --git a/init/util.cpp b/init/util.cpp
index 40db838..e5254dd 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -61,6 +61,8 @@
 
 const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
 
+void (*trigger_shutdown)(const std::string& command) = nullptr;
+
 // DecodeUid() - decodes and returns the given string, which can be either the
 // numeric or name representation, into the integer uid or gid.
 Result<uid_t> DecodeUid(const std::string& name) {
@@ -434,6 +436,142 @@
     return {};
 }
 
+static FscryptAction FscryptInferAction(const std::string& dir) {
+    const std::string prefix = "/data/";
+
+    if (!android::base::StartsWith(dir, prefix)) {
+        return FscryptAction::kNone;
+    }
+
+    // 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.
+        return FscryptAction::kAttempt;
+    }
+
+    // Only set policy on first level /data directories
+    // To make this less restrictive, consider using a policy file.
+    // However this is overkill for as long as the policy is simply
+    // to apply a global policy to all /data folders created via makedir
+    if (dir.find_first_of('/', prefix.size()) != std::string::npos) {
+        return FscryptAction::kNone;
+    }
+
+    // Special case various directories that must not be encrypted,
+    // often because their subdirectories must be encrypted.
+    // This isn't a nice way to do this, see b/26641735
+    std::vector<std::string> directories_to_exclude = {
+            "lost+found", "system_ce", "system_de", "misc_ce",     "misc_de",
+            "vendor_ce",  "vendor_de", "media",     "data",        "user",
+            "user_de",    "apex",      "preloads",  "app-staging", "gsi",
+    };
+    for (const auto& d : directories_to_exclude) {
+        if ((prefix + d) == dir) {
+            return FscryptAction::kNone;
+        }
+    }
+    // Empty these directories if policy setting fails.
+    std::vector<std::string> wipe_on_failure = {
+            "rollback", "rollback-observer",  // b/139193659
+    };
+    for (const auto& d : wipe_on_failure) {
+        if ((prefix + d) == dir) {
+            return FscryptAction::kDeleteIfNecessary;
+        }
+    }
+    return FscryptAction::kRequire;
+}
+
+Result<MkdirOptions> ParseMkdir(const std::vector<std::string>& args) {
+    mode_t mode = 0755;
+    Result<uid_t> uid = -1;
+    Result<gid_t> gid = -1;
+    FscryptAction fscrypt_inferred_action = FscryptInferAction(args[1]);
+    FscryptAction fscrypt_action = fscrypt_inferred_action;
+    std::string ref_option = "ref";
+    bool set_option_encryption = false;
+    bool set_option_key = false;
+
+    for (size_t i = 2; i < args.size(); i++) {
+        switch (i) {
+            case 2:
+                mode = std::strtoul(args[2].c_str(), 0, 8);
+                break;
+            case 3:
+                uid = DecodeUid(args[3]);
+                if (!uid) {
+                    return Error()
+                           << "Unable to decode UID for '" << args[3] << "': " << uid.error();
+                }
+                break;
+            case 4:
+                gid = DecodeUid(args[4]);
+                if (!gid) {
+                    return Error()
+                           << "Unable to decode GID for '" << args[4] << "': " << gid.error();
+                }
+                break;
+            default:
+                auto parts = android::base::Split(args[i], "=");
+                if (parts.size() != 2) {
+                    return Error() << "Can't parse option: '" << args[i] << "'";
+                }
+                auto optname = parts[0];
+                auto optval = parts[1];
+                if (optname == "encryption") {
+                    if (set_option_encryption) {
+                        return Error() << "Duplicated option: '" << optname << "'";
+                    }
+                    if (optval == "Require") {
+                        fscrypt_action = FscryptAction::kRequire;
+                    } else if (optval == "None") {
+                        fscrypt_action = FscryptAction::kNone;
+                    } else if (optval == "Attempt") {
+                        fscrypt_action = FscryptAction::kAttempt;
+                    } else if (optval == "DeleteIfNecessary") {
+                        fscrypt_action = FscryptAction::kDeleteIfNecessary;
+                    } else {
+                        return Error() << "Unknown encryption option: '" << optval << "'";
+                    }
+                    set_option_encryption = true;
+                } else if (optname == "key") {
+                    if (set_option_key) {
+                        return Error() << "Duplicated option: '" << optname << "'";
+                    }
+                    if (optval == "ref" || optval == "per_boot_ref") {
+                        ref_option = optval;
+                    } else {
+                        return Error() << "Unknown key option: '" << optval << "'";
+                    }
+                    set_option_key = true;
+                } else {
+                    return Error() << "Unknown option: '" << args[i] << "'";
+                }
+        }
+    }
+    if (set_option_key && fscrypt_action == FscryptAction::kNone) {
+        return Error() << "Key option set but encryption action is none";
+    }
+    const std::string prefix = "/data/";
+    if (StartsWith(args[1], prefix) &&
+        args[1].find_first_of('/', prefix.size()) == std::string::npos) {
+        if (!set_option_encryption) {
+            LOG(WARNING) << "Top-level directory needs encryption action, eg mkdir " << args[1]
+                         << " <mode> <uid> <gid> encryption=Require";
+        }
+        if (fscrypt_action == FscryptAction::kNone) {
+            LOG(INFO) << "Not setting encryption policy on: " << args[1];
+        }
+    }
+    if (fscrypt_action != fscrypt_inferred_action) {
+        LOG(WARNING) << "Inferred action different from explicit one, expected "
+                     << static_cast<int>(fscrypt_inferred_action) << " but got "
+                     << static_cast<int>(fscrypt_action);
+    }
+
+    return MkdirOptions{args[1], mode, *uid, *gid, fscrypt_action, ref_option};
+}
+
 Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
         const std::vector<std::string>& args) {
     struct flag_type {
diff --git a/init/util.h b/init/util.h
index 4cccefe..9d89ed7 100644
--- a/init/util.h
+++ b/init/util.h
@@ -25,6 +25,7 @@
 
 #include <android-base/chrono_utils.h>
 
+#include "fscrypt_init_extensions.h"
 #include "result.h"
 
 using android::base::boot_clock;
@@ -34,6 +35,8 @@
 
 static const char kColdBootDoneProp[] = "ro.cold_boot_done";
 
+extern void (*trigger_shutdown)(const std::string& command);
+
 Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
                          gid_t gid, const std::string& socketcon);
 
@@ -60,6 +63,17 @@
 bool IsLegalPropertyName(const std::string& name);
 Result<void> IsLegalPropertyValue(const std::string& name, const std::string& value);
 
+struct MkdirOptions {
+    std::string target;
+    mode_t mode;
+    uid_t uid;
+    gid_t gid;
+    FscryptAction fscrypt_action;
+    std::string ref_option;
+};
+
+Result<MkdirOptions> ParseMkdir(const std::vector<std::string>& args);
+
 Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
         const std::vector<std::string>& args);
 
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index b9420d4..334364e 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -197,7 +197,10 @@
 
 cc_defaults {
     name: "libcutils_test_default",
-    srcs: ["sockets_test.cpp"],
+    srcs: [
+        "native_handle_test.cpp",
+        "sockets_test.cpp",
+    ],
 
     target: {
         android: {
@@ -244,6 +247,7 @@
     defaults: ["libcutils_test_default"],
     host_supported: true,
     shared_libs: test_libraries,
+    require_root: true,
 }
 
 cc_test {
@@ -255,6 +259,7 @@
         "libcgrouprc_format",
     ] + test_libraries,
     stl: "libc++_static",
+    require_root: true,
 
     target: {
         android: {
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 5fb11a5..2b39ca6 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -85,6 +85,7 @@
     { 00755, AID_ROOT,         AID_SHELL,        0, "system/vendor" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system/xbin" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system/apex/*/bin" },
+    { 00751, AID_ROOT,         AID_SHELL,        0, "system_ext/bin" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "vendor/bin" },
     { 00755, AID_ROOT,         AID_SHELL,        0, "vendor" },
     { 00755, AID_ROOT,         AID_ROOT,         0, 0 },
@@ -207,6 +208,7 @@
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/xbin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/apex/*/bin/*" },
+    { 00755, AID_ROOT,      AID_SHELL,     0, "system_ext/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/xbin/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, 0 },
diff --git a/libcutils/fs_config_test.cpp b/libcutils/fs_config_test.cpp
index 9627152..c6684b4 100644
--- a/libcutils/fs_config_test.cpp
+++ b/libcutils/fs_config_test.cpp
@@ -46,7 +46,7 @@
         // clang-format off
     { true,  "system/lib",             "system/lib/hw",           true  },
     { true,  "vendor/lib",             "system/vendor/lib/hw",    true  },
-    { true,  "system/vendor/lib",      "vendor/lib/hw",           true  },
+    { true,  "system/vendor/lib",      "vendor/lib/hw",           false },
     { true,  "system/vendor/lib",      "system/vendor/lib/hw",    true  },
     { true,  "foo/*/bar/*",            "foo/1/bar/2",             true  },
     { true,  "foo/*/bar/*",            "foo/1/bar",               true  },
@@ -56,13 +56,14 @@
     { false, "vendor/bin/wifi",        "system/vendor/bin/wifi",  true  },
     { false, "vendor/bin/wifi",        "system/vendor/bin/wifi2", false },
     { false, "system/vendor/bin/wifi", "system/vendor/bin/wifi",  true, },
-    { false, "odm/bin/wifi",           "system/odm/bin/wifi",     true  },
-    { false, "oem/bin/wifi",           "system/oem/bin/wifi",     true  },
+    { false, "odm/bin/wifi",           "system/odm/bin/wifi",     false },
+    { false, "odm/bin/wifi",           "vendor/odm/bin/wifi",     true  },
+    { false, "oem/bin/wifi",           "system/oem/bin/wifi",     false },
     { false, "data/bin/wifi",          "system/data/bin/wifi",    false },
     { false, "system/bin/*",           "system/bin/wifi",         true  },
     { false, "vendor/bin/*",           "system/vendor/bin/wifi",  true  },
     { false, "system/bin/*",           "system/bin",              false },
-    { false, "system/vendor/bin/*",    "vendor/bin/wifi",         true  },
+    { false, "system/vendor/bin/*",    "vendor/bin/wifi",         false },
     { false, "foo/*/bar/*",            "foo/1/bar/2",             true  },
     { false, "foo/*/bar/*",            "foo/1/bar",               false },
     { false, "foo/*/bar/*",            "foo/1/bar/2/3",           true  },
diff --git a/libcutils/include/cutils/sockets.h b/libcutils/include/cutils/sockets.h
index 285f150..be21a8f 100644
--- a/libcutils/include/cutils/sockets.h
+++ b/libcutils/include/cutils/sockets.h
@@ -103,14 +103,6 @@
 int socket_close(cutils_socket_t sock);
 
 /*
- * Sets socket receive timeout using SO_RCVTIMEO. Setting |timeout_ms| to 0
- * disables receive timeouts.
- *
- * Return 0 on success.
- */
-int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms);
-
-/*
  * Returns the local port the socket is bound to or -1 on error.
  */
 int socket_get_local_port(cutils_socket_t sock);
diff --git a/libcutils/native_handle.cpp b/libcutils/native_handle.cpp
index b409e5b..5804ab1 100644
--- a/libcutils/native_handle.cpp
+++ b/libcutils/native_handle.cpp
@@ -81,6 +81,8 @@
 }
 
 int native_handle_close(const native_handle_t* h) {
+    if (!h) return 0;
+
     if (h->version != sizeof(native_handle_t)) return -EINVAL;
 
     int saved_errno = errno;
diff --git a/libcutils/native_handle_test.cpp b/libcutils/native_handle_test.cpp
new file mode 100644
index 0000000..c1e2f0b
--- /dev/null
+++ b/libcutils/native_handle_test.cpp
@@ -0,0 +1,27 @@
+/*
+ * 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 <cutils/native_handle.h>
+
+#include <gtest/gtest.h>
+
+TEST(native_handle, native_handle_delete) {
+    ASSERT_EQ(0, native_handle_delete(nullptr));
+}
+
+TEST(native_handle, native_handle_close) {
+    ASSERT_EQ(0, native_handle_close(nullptr));
+}
diff --git a/libcutils/sockets_test.cpp b/libcutils/sockets_test.cpp
index b762ac1..1fa40bc 100644
--- a/libcutils/sockets_test.cpp
+++ b/libcutils/sockets_test.cpp
@@ -73,25 +73,6 @@
     EXPECT_EQ(0, socket_close(client));
 }
 
-// Tests receive timeout. The timing verification logic must be very coarse to
-// make sure different systems can all pass these tests.
-void TestReceiveTimeout(cutils_socket_t sock) {
-    time_t start_time;
-    char buffer[32];
-
-    // Make sure a 20ms timeout completes in 1 second or less.
-    EXPECT_EQ(0, socket_set_receive_timeout(sock, 20));
-    start_time = time(nullptr);
-    EXPECT_EQ(-1, recv(sock, buffer, sizeof(buffer), 0));
-    EXPECT_LE(difftime(time(nullptr), start_time), 1.0);
-
-    // Make sure a 1250ms timeout takes 1 second or more.
-    EXPECT_EQ(0, socket_set_receive_timeout(sock, 1250));
-    start_time = time(nullptr);
-    EXPECT_EQ(-1, recv(sock, buffer, sizeof(buffer), 0));
-    EXPECT_LE(1.0, difftime(time(nullptr), start_time));
-}
-
 // Tests socket_get_local_port().
 TEST(SocketsTest, TestGetLocalPort) {
     cutils_socket_t server;
@@ -157,32 +138,6 @@
     TestConnectedSockets(handler, client, SOCK_STREAM);
 }
 
-// Tests setting a receive timeout for UDP sockets.
-TEST(SocketsTest, TestUdpReceiveTimeout) {
-    cutils_socket_t sock = socket_inaddr_any_server(0, SOCK_DGRAM);
-    ASSERT_NE(INVALID_SOCKET, sock);
-
-    TestReceiveTimeout(sock);
-
-    EXPECT_EQ(0, socket_close(sock));
-}
-
-// Tests setting a receive timeout for TCP sockets.
-TEST(SocketsTest, TestTcpReceiveTimeout) {
-    cutils_socket_t server = socket_inaddr_any_server(0, SOCK_STREAM);
-    ASSERT_NE(INVALID_SOCKET, server);
-
-    cutils_socket_t client = socket_network_client(
-            "localhost", socket_get_local_port(server), SOCK_STREAM);
-    cutils_socket_t handler = accept(server, nullptr, nullptr);
-    EXPECT_EQ(0, socket_close(server));
-
-    TestReceiveTimeout(handler);
-
-    EXPECT_EQ(0, socket_close(client));
-    EXPECT_EQ(0, socket_close(handler));
-}
-
 // Tests socket_send_buffers() failure.
 TEST(SocketsTest, TestSocketSendBuffersFailure) {
     EXPECT_EQ(-1, socket_send_buffers(INVALID_SOCKET, nullptr, 0));
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
index 6acdcd8..84663e6 100644
--- a/libcutils/sockets_unix.cpp
+++ b/libcutils/sockets_unix.cpp
@@ -31,13 +31,6 @@
     return close(sock);
 }
 
-int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms) {
-    timeval tv;
-    tv.tv_sec = timeout_ms / 1000;
-    tv.tv_usec = (timeout_ms % 1000) * 1000;
-    return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
-}
-
 ssize_t socket_send_buffers(cutils_socket_t sock,
                             const cutils_socket_buffer_t* buffers,
                             size_t num_buffers) {
diff --git a/libcutils/sockets_windows.cpp b/libcutils/sockets_windows.cpp
index df14712..4adb796 100644
--- a/libcutils/sockets_windows.cpp
+++ b/libcutils/sockets_windows.cpp
@@ -54,11 +54,6 @@
     return closesocket(sock);
 }
 
-int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms) {
-    return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
-                      reinterpret_cast<char*>(&timeout_ms), sizeof(timeout_ms));
-}
-
 ssize_t socket_send_buffers(cutils_socket_t sock,
                             const cutils_socket_buffer_t* buffers,
                             size_t num_buffers) {
diff --git a/libcutils/uevent.cpp b/libcutils/uevent.cpp
index 721de7c..bf244d2 100644
--- a/libcutils/uevent.cpp
+++ b/libcutils/uevent.cpp
@@ -60,7 +60,7 @@
     struct ucred* cred;
 
     *uid = -1;
-    ssize_t n = recvmsg(socket, &hdr, 0);
+    ssize_t n = TEMP_FAILURE_RETRY(recvmsg(socket, &hdr, 0));
     if (n <= 0) {
         return n;
     }
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index d27feb9..ce0c3c8 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -20,12 +20,7 @@
         "-Werror",
     ],
     cppflags: [
-        "-Weverything",
-        "-Wno-c++98-compat-pedantic",
-        // Hide errors in headers we include
-        "-Wno-global-constructors",
-        "-Wno-exit-time-destructors",
-        "-Wno-padded",
+        "-Wextra",
     ],
     srcs: ["GrallocUsageConversion.cpp"],
     export_include_dirs: ["include"],
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 22cf43b..2886289 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -494,7 +494,7 @@
 
 // Cache miss, go to logd to acquire a public reference.
 // Because we lack access to a SHARED PUBLIC /dev/event-log-tags file map?
-static const TagFmt* __getEventTag(EventTagMap* map, unsigned int tag) {
+static const TagFmt* __getEventTag([[maybe_unused]] EventTagMap* map, unsigned int tag) {
   // call event tag service to arrange for a new tag
   char* buf = NULL;
   // Can not use android::base::StringPrintf, asprintf + free instead.
@@ -515,8 +515,9 @@
     } else {
       size = ret;
     }
+#ifdef __ANDROID__
     // Ask event log tag service for an existing entry
-    if (__send_log_msg(buf, size) >= 0) {
+    if (SendLogdControlMessage(buf, size) >= 0) {
       buf[size - 1] = '\0';
       char* ep;
       unsigned long val = strtoul(buf, &ep, 10);  // return size
@@ -529,6 +530,7 @@
         }
       }
     }
+#endif
     free(buf);
   }
   return NULL;
@@ -618,8 +620,9 @@
     } else {
       size = ret;
     }
+#ifdef __ANDROID__
     // Ask event log tag service for an allocation
-    if (__send_log_msg(buf, size) >= 0) {
+    if (SendLogdControlMessage(buf, size) >= 0) {
       buf[size - 1] = '\0';
       unsigned long val = strtoul(buf, &cp, 10);        // return size
       if ((buf != cp) && (val > 0) && (*cp == '\n')) {  // truncation OK
@@ -635,6 +638,7 @@
         }
       }
     }
+#endif
     free(buf);
   }
 
diff --git a/liblog/fake_log_device.cpp b/liblog/fake_log_device.cpp
index 428a482..f61bbdc 100644
--- a/liblog/fake_log_device.cpp
+++ b/liblog/fake_log_device.cpp
@@ -650,10 +650,6 @@
   return fd;
 }
 
-ssize_t __send_log_msg(char*, size_t) {
-  return -ENODEV;
-}
-
 int __android_log_is_loggable(int prio, const char*, int def) {
   int logLevel = def;
   return logLevel >= 0 && prio >= logLevel;
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
index ce54db2..bd2256c 100644
--- a/liblog/fake_log_device.h
+++ b/liblog/fake_log_device.h
@@ -29,7 +29,6 @@
 int fakeLogClose(int fd);
 ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count);
 
-ssize_t __send_log_msg(char*, size_t);
 int __android_log_is_loggable(int prio, const char*, int def);
 int __android_log_is_loggable_len(int prio, const char*, size_t, int def);
 int __android_log_is_debuggable();
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index ce4c53c..2dd8059 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -1,7 +1,7 @@
 LIBLOG {
   global:
-    android_name_to_log_id; # vndk
-    android_log_id_to_name; # vndk
+    android_name_to_log_id; # llndk
+    android_log_id_to_name; # llndk
     __android_log_assert;
     __android_log_buf_print;
     __android_log_buf_write;
@@ -14,44 +14,44 @@
 
 LIBLOG_L {
   global:
-    android_logger_clear; # vndk
-    android_logger_get_id; # vndk
-    android_logger_get_log_readable_size; # vndk
-    android_logger_get_log_version; # vndk
-    android_logger_get_log_size; # vndk
-    android_logger_list_alloc; # apex vndk
-    android_logger_list_alloc_time; # apex vndk
-    android_logger_list_free; # apex vndk
-    android_logger_list_open; # vndk
-    android_logger_list_read; # apex vndk
-    android_logger_open; # apex vndk
-    android_logger_set_log_size; # vndk
+    android_logger_clear; # llndk
+    android_logger_get_id; # llndk
+    android_logger_get_log_readable_size; # llndk
+    android_logger_get_log_version; # llndk
+    android_logger_get_log_size; # llndk
+    android_logger_list_alloc; # apex llndk
+    android_logger_list_alloc_time; # apex llndk
+    android_logger_list_free; # apex llndk
+    android_logger_list_open; # llndk
+    android_logger_list_read; # apex llndk
+    android_logger_open; # apex llndk
+    android_logger_set_log_size; # llndk
 };
 
 LIBLOG_M {
   global:
-    android_logger_get_prune_list; # vndk
-    android_logger_set_prune_list; # vndk
-    android_logger_get_statistics; # vndk
-    __android_log_error_write; # apex vndk
+    android_logger_get_prune_list; # llndk
+    android_logger_set_prune_list; # llndk
+    android_logger_get_statistics; # llndk
+    __android_log_error_write; # apex llndk
     __android_log_is_loggable;
-    create_android_logger; # apex vndk
-    android_log_destroy; # apex vndk
-    android_log_write_list_begin; # apex vndk
-    android_log_write_list_end; # apex vndk
-    android_log_write_int32; # apex vndk
-    android_log_write_int64; # apex vndk
-    android_log_write_string8; # apex vndk
-    android_log_write_string8_len; # apex vndk
-    android_log_write_float32; # apex vndk
-    android_log_write_list; # apex vndk
+    create_android_logger; # apex llndk
+    android_log_destroy; # apex llndk
+    android_log_write_list_begin; # apex llndk
+    android_log_write_list_end; # apex llndk
+    android_log_write_int32; # apex llndk
+    android_log_write_int64; # apex llndk
+    android_log_write_string8; # apex llndk
+    android_log_write_string8_len; # apex llndk
+    android_log_write_float32; # apex llndk
+    android_log_write_list; # apex llndk
 
 };
 
 LIBLOG_O {
   global:
     __android_log_is_loggable_len;
-    __android_log_is_debuggable; # vndk
+    __android_log_is_debuggable; # apex llndk
 };
 
 LIBLOG_Q {
@@ -61,15 +61,15 @@
     __android_log_bwrite; # apex
     __android_log_close; # apex
     __android_log_security; # apex
-    android_log_reset; #vndk
-    android_log_parser_reset; #vndk
+    __android_log_security_bswrite; # apex
+    android_log_reset; # llndk
+    android_log_parser_reset; # llndk
 };
 
 LIBLOG_PRIVATE {
   global:
     __android_log_pmsg_file_read;
     __android_log_pmsg_file_write;
-    __android_log_security_bswrite;
     __android_logger_get_buffer_size;
     __android_logger_property_get_bool;
     android_openEventTagMap;
diff --git a/liblog/log_event_list.cpp b/liblog/log_event_list.cpp
index 7882c96..e9f4a32 100644
--- a/liblog/log_event_list.cpp
+++ b/liblog/log_event_list.cpp
@@ -47,9 +47,6 @@
   uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
 };
 
-// TODO(tomcherry): real C++ structs.
-typedef struct android_log_context_internal android_log_context_internal;
-
 static void init_context(android_log_context_internal* context, uint32_t tag) {
   context->tag = tag;
   context->read_write_flag = kAndroidLoggerWrite;
@@ -110,11 +107,9 @@
   return 0;
 }
 
-int android_log_reset(android_log_context ctx) {
-  android_log_context_internal* context;
+int android_log_reset(android_log_context context) {
   uint32_t tag;
 
-  context = (android_log_context_internal*)ctx;
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -126,10 +121,7 @@
   return 0;
 }
 
-int android_log_parser_reset(android_log_context ctx, const char* msg, size_t len) {
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
+int android_log_parser_reset(android_log_context context, const char* msg, size_t len) {
   if (!context || (kAndroidLoggerRead != context->read_write_flag)) {
     return -EBADF;
   }
@@ -140,10 +132,7 @@
   return 0;
 }
 
-int android_log_write_list_begin(android_log_context ctx) {
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
+int android_log_write_list_begin(android_log_context context) {
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -174,8 +163,7 @@
   return 0;
 }
 
-int android_log_write_int32(android_log_context ctx, int32_t value) {
-  android_log_context_internal* context = (android_log_context_internal*)ctx;
+int android_log_write_int32(android_log_context context, int32_t value) {
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -195,8 +183,7 @@
   return 0;
 }
 
-int android_log_write_int64(android_log_context ctx, int64_t value) {
-  android_log_context_internal* context = (android_log_context_internal*)ctx;
+int android_log_write_int64(android_log_context context, int64_t value) {
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -216,8 +203,7 @@
   return 0;
 }
 
-int android_log_write_string8_len(android_log_context ctx, const char* value, size_t maxlen) {
-  android_log_context_internal* context = (android_log_context_internal*)ctx;
+int android_log_write_string8_len(android_log_context context, const char* value, size_t maxlen) {
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -252,8 +238,7 @@
   return android_log_write_string8_len(ctx, value, MAX_EVENT_PAYLOAD);
 }
 
-int android_log_write_float32(android_log_context ctx, float value) {
-  android_log_context_internal* context = (android_log_context_internal*)ctx;
+int android_log_write_float32(android_log_context context, float value) {
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -273,10 +258,7 @@
   return 0;
 }
 
-int android_log_write_list_end(android_log_context ctx) {
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
+int android_log_write_list_end(android_log_context context) {
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -303,8 +285,7 @@
 /*
  * Logs the list of elements to the event log.
  */
-int android_log_write_list(android_log_context ctx, log_id_t id) {
-  android_log_context_internal* context;
+int android_log_write_list(android_log_context context, log_id_t id) {
   const char* msg;
   ssize_t len;
 
@@ -312,7 +293,6 @@
     return -EINVAL;
   }
 
-  context = (android_log_context_internal*)ctx;
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -337,12 +317,10 @@
                                      : __android_log_security_bwrite(context->tag, msg, len));
 }
 
-int android_log_write_list_buffer(android_log_context ctx, const char** buffer) {
-  android_log_context_internal* context;
+int android_log_write_list_buffer(android_log_context context, const char** buffer) {
   const char* msg;
   ssize_t len;
 
-  context = (android_log_context_internal*)ctx;
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -375,12 +353,10 @@
  * this and continues to call this function, the behavior is undefined
  * (although it won't crash).
  */
-static android_log_list_element android_log_read_next_internal(android_log_context ctx, int peek) {
+static android_log_list_element android_log_read_next_internal(android_log_context context,
+                                                               int peek) {
   android_log_list_element elem;
   unsigned pos;
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
 
   memset(&elem, 0, sizeof(elem));
 
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index 619cf8c..96e7a61 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "logd_reader.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -35,66 +37,8 @@
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
-#include "log_portability.h"
-#include "logd_reader.h"
 #include "logger.h"
 
-static int logdAvailable(log_id_t LogId);
-static int logdVersion(struct android_log_logger* logger,
-                       struct android_log_transport_context* transp);
-static int logdRead(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp, struct log_msg* log_msg);
-static int logdPoll(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp);
-static void logdClose(struct android_log_logger_list* logger_list,
-                      struct android_log_transport_context* transp);
-static int logdClear(struct android_log_logger* logger,
-                     struct android_log_transport_context* transp);
-static ssize_t logdSetSize(struct android_log_logger* logger,
-                           struct android_log_transport_context* transp, size_t size);
-static ssize_t logdGetSize(struct android_log_logger* logger,
-                           struct android_log_transport_context* transp);
-static ssize_t logdGetReadableSize(struct android_log_logger* logger,
-                                   struct android_log_transport_context* transp);
-static ssize_t logdGetPrune(struct android_log_logger_list* logger,
-                            struct android_log_transport_context* transp, char* buf, size_t len);
-static ssize_t logdSetPrune(struct android_log_logger_list* logger,
-                            struct android_log_transport_context* transp, char* buf, size_t len);
-static ssize_t logdGetStats(struct android_log_logger_list* logger,
-                            struct android_log_transport_context* transp, char* buf, size_t len);
-
-struct android_log_transport_read logdLoggerRead = {
-    .name = "logd",
-    .available = logdAvailable,
-    .version = logdVersion,
-    .close = logdClose,
-    .read = logdRead,
-    .poll = logdPoll,
-    .clear = logdClear,
-    .setSize = logdSetSize,
-    .getSize = logdGetSize,
-    .getReadableSize = logdGetReadableSize,
-    .getPrune = logdGetPrune,
-    .setPrune = logdSetPrune,
-    .getStats = logdGetStats,
-};
-
-static int logdAvailable(log_id_t logId) {
-  if (logId >= LOG_ID_MAX) {
-    return -EINVAL;
-  }
-  if (logId == LOG_ID_SECURITY) {
-    uid_t uid = __android_log_uid();
-    if (uid != AID_SYSTEM) {
-      return -EPERM;
-    }
-  }
-  if (access("/dev/socket/logdw", W_OK) == 0) {
-    return 0;
-  }
-  return -EBADF;
-}
-
 // Connects to /dev/socket/<name> and returns the associated fd or returns -1 on error.
 // O_CLOEXEC is always set.
 static int socket_local_client(const std::string& name, int type) {
@@ -120,8 +64,7 @@
 }
 
 /* worker for sending the command to the logger */
-static ssize_t send_log_msg(struct android_log_logger* logger, const char* msg, char* buf,
-                            size_t buf_size) {
+ssize_t SendLogdControlMessage(char* buf, size_t buf_size) {
   ssize_t ret;
   size_t len;
   char* cp;
@@ -131,10 +74,6 @@
     return sock;
   }
 
-  if (msg) {
-    snprintf(buf, buf_size, msg, logger ? logger->logId : (unsigned)-1);
-  }
-
   len = strlen(buf) + 1;
   ret = TEMP_FAILURE_RETRY(write(sock, buf, len));
   if (ret <= 0) {
@@ -185,10 +124,6 @@
   return ret;
 }
 
-ssize_t __send_log_msg(char* buf, size_t buf_size) {
-  return send_log_msg(NULL, NULL, buf, buf_size);
-}
-
 static int check_log_success(char* buf, ssize_t ret) {
   if (ret < 0) {
     return ret;
@@ -202,19 +137,28 @@
   return 0;
 }
 
-static int logdClear(struct android_log_logger* logger,
-                     struct android_log_transport_context* transp __unused) {
+int android_logger_clear(struct logger* logger) {
+  if (!android_logger_is_logd(logger)) {
+    return -EINVAL;
+  }
+  uint32_t log_id = android_logger_get_id(logger);
   char buf[512];
+  snprintf(buf, sizeof(buf), "clear %" PRIu32, log_id);
 
-  return check_log_success(buf, send_log_msg(logger, "clear %d", buf, sizeof(buf)));
+  return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
 }
 
 /* returns the total size of the log's ring buffer */
-static ssize_t logdGetSize(struct android_log_logger* logger,
-                           struct android_log_transport_context* transp __unused) {
-  char buf[512];
+long android_logger_get_log_size(struct logger* logger) {
+  if (!android_logger_is_logd(logger)) {
+    return -EINVAL;
+  }
 
-  ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf));
+  uint32_t log_id = android_logger_get_id(logger);
+  char buf[512];
+  snprintf(buf, sizeof(buf), "getLogSize %" PRIu32, log_id);
+
+  ssize_t ret = SendLogdControlMessage(buf, sizeof(buf));
   if (ret < 0) {
     return ret;
   }
@@ -226,24 +170,32 @@
   return atol(buf);
 }
 
-static ssize_t logdSetSize(struct android_log_logger* logger,
-                           struct android_log_transport_context* transp __unused, size_t size) {
+int android_logger_set_log_size(struct logger* logger, unsigned long size) {
+  if (!android_logger_is_logd(logger)) {
+    return -EINVAL;
+  }
+
+  uint32_t log_id = android_logger_get_id(logger);
   char buf[512];
+  snprintf(buf, sizeof(buf), "setLogSize %" PRIu32 " %lu", log_id, size);
 
-  snprintf(buf, sizeof(buf), "setLogSize %d %zu", logger->logId, size);
-
-  return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf)));
+  return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
 }
 
 /*
  * returns the readable size of the log's ring buffer (that is, amount of the
  * log consumed)
  */
-static ssize_t logdGetReadableSize(struct android_log_logger* logger,
-                                   struct android_log_transport_context* transp __unused) {
-  char buf[512];
+long android_logger_get_log_readable_size(struct logger* logger) {
+  if (!android_logger_is_logd(logger)) {
+    return -EINVAL;
+  }
 
-  ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf));
+  uint32_t log_id = android_logger_get_id(logger);
+  char buf[512];
+  snprintf(buf, sizeof(buf), "getLogSizeUsed %" PRIu32, log_id);
+
+  ssize_t ret = SendLogdControlMessage(buf, sizeof(buf));
   if (ret < 0) {
     return ret;
   }
@@ -255,22 +207,15 @@
   return atol(buf);
 }
 
-/*
- * returns the logger version
- */
-static int logdVersion(struct android_log_logger* logger __unused,
-                       struct android_log_transport_context* transp __unused) {
-  uid_t uid = __android_log_uid();
-  return ((uid != AID_ROOT) && (uid != AID_LOG) && (uid != AID_SYSTEM)) ? 3 : 4;
+int android_logger_get_log_version(struct logger*) {
+  return 4;
 }
 
-/*
- * returns statistics
- */
-static ssize_t logdGetStats(struct android_log_logger_list* logger_list,
-                            struct android_log_transport_context* transp __unused, char* buf,
-                            size_t len) {
-  struct android_log_logger* logger;
+ssize_t android_logger_get_statistics(struct logger_list* logger_list, char* buf, size_t len) {
+  if (logger_list->mode & ANDROID_LOG_PSTORE) {
+    return -EINVAL;
+  }
+
   char* cp = buf;
   size_t remaining = len;
   size_t n;
@@ -280,29 +225,35 @@
   remaining -= n;
   cp += n;
 
-  logger_for_each(logger, logger_list) {
-    n = snprintf(cp, remaining, " %d", logger->logId);
-    n = MIN(n, remaining);
-    remaining -= n;
-    cp += n;
+  for (size_t log_id = 0; log_id < LOG_ID_MAX; ++log_id) {
+    if ((1 << log_id) & logger_list->log_mask) {
+      n = snprintf(cp, remaining, " %zu", log_id);
+      n = MIN(n, remaining);
+      remaining -= n;
+      cp += n;
+    }
   }
 
   if (logger_list->pid) {
     snprintf(cp, remaining, " pid=%u", logger_list->pid);
   }
 
-  return send_log_msg(NULL, NULL, buf, len);
+  return SendLogdControlMessage(buf, len);
+}
+ssize_t android_logger_get_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
+  if (logger_list->mode & ANDROID_LOG_PSTORE) {
+    return -EINVAL;
+  }
+
+  snprintf(buf, len, "getPruneList");
+  return SendLogdControlMessage(buf, len);
 }
 
-static ssize_t logdGetPrune(struct android_log_logger_list* logger_list __unused,
-                            struct android_log_transport_context* transp __unused, char* buf,
-                            size_t len) {
-  return send_log_msg(NULL, "getPruneList", buf, len);
-}
+int android_logger_set_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
+  if (logger_list->mode & ANDROID_LOG_PSTORE) {
+    return -EINVAL;
+  }
 
-static ssize_t logdSetPrune(struct android_log_logger_list* logger_list __unused,
-                            struct android_log_transport_context* transp __unused, char* buf,
-                            size_t len) {
   const char cmd[] = "setPruneList ";
   const size_t cmdlen = sizeof(cmd) - 1;
 
@@ -313,20 +264,14 @@
   buf[len - 1] = '\0';
   memcpy(buf, cmd, cmdlen);
 
-  return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
+  return check_log_success(buf, SendLogdControlMessage(buf, len));
 }
 
-static int logdOpen(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp) {
-  struct android_log_logger* logger;
+static int logdOpen(struct logger_list* logger_list) {
   char buffer[256], *cp, c;
   int ret, remaining, sock;
 
-  if (!logger_list) {
-    return -EINVAL;
-  }
-
-  sock = atomic_load(&transp->context.sock);
+  sock = atomic_load(&logger_list->fd);
   if (sock > 0) {
     return sock;
   }
@@ -346,12 +291,15 @@
   cp += 5;
   c = '=';
   remaining = sizeof(buffer) - (cp - buffer);
-  logger_for_each(logger, logger_list) {
-    ret = snprintf(cp, remaining, "%c%u", c, logger->logId);
-    ret = MIN(ret, remaining);
-    remaining -= ret;
-    cp += ret;
-    c = ',';
+
+  for (size_t log_id = 0; log_id < LOG_ID_MAX; ++log_id) {
+    if ((1 << log_id) & logger_list->log_mask) {
+      ret = snprintf(cp, remaining, "%c%zu", c, log_id);
+      ret = MIN(ret, remaining);
+      remaining -= ret;
+      cp += ret;
+      c = ',';
+    }
   }
 
   if (logger_list->tail) {
@@ -396,7 +344,7 @@
     return ret;
   }
 
-  ret = atomic_exchange(&transp->context.sock, sock);
+  ret = atomic_exchange(&logger_list->fd, sock);
   if ((ret > 0) && (ret != sock)) {
     close(ret);
   }
@@ -404,15 +352,12 @@
 }
 
 /* Read from the selected logs */
-static int logdRead(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp, struct log_msg* log_msg) {
-  int ret = logdOpen(logger_list, transp);
+int LogdRead(struct logger_list* logger_list, struct log_msg* log_msg) {
+  int ret = logdOpen(logger_list);
   if (ret < 0) {
     return ret;
   }
 
-  memset(log_msg, 0, sizeof(*log_msg));
-
   /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
   ret = TEMP_FAILURE_RETRY(recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0));
   if ((logger_list->mode & ANDROID_LOG_NONBLOCK) && ret == 0) {
@@ -425,32 +370,9 @@
   return ret;
 }
 
-static int logdPoll(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp) {
-  struct pollfd p;
-
-  int ret = logdOpen(logger_list, transp);
-  if (ret < 0) {
-    return ret;
-  }
-
-  memset(&p, 0, sizeof(p));
-  p.fd = ret;
-  p.events = POLLIN;
-  ret = poll(&p, 1, 20);
-  if ((ret > 0) && !(p.revents & POLLIN)) {
-    ret = 0;
-  }
-  if ((ret == -1) && errno) {
-    return -errno;
-  }
-  return ret;
-}
-
 /* Close all the logs */
-static void logdClose(struct android_log_logger_list* logger_list __unused,
-                      struct android_log_transport_context* transp) {
-  int sock = atomic_exchange(&transp->context.sock, -1);
+void LogdClose(struct logger_list* logger_list) {
+  int sock = atomic_exchange(&logger_list->fd, -1);
   if (sock > 0) {
     close(sock);
   }
diff --git a/liblog/logd_reader.h b/liblog/logd_reader.h
index 7c53cbb..2d032fa 100644
--- a/liblog/logd_reader.h
+++ b/liblog/logd_reader.h
@@ -18,10 +18,14 @@
 
 #include <unistd.h>
 
+#include "log/log_read.h"
 #include "log_portability.h"
 
 __BEGIN_DECLS
 
-ssize_t __send_log_msg(char* buf, size_t buf_size);
+int LogdRead(struct logger_list* logger_list, struct log_msg* log_msg);
+void LogdClose(struct logger_list* logger_list);
+
+ssize_t SendLogdControlMessage(char* buf, size_t buf_size);
 
 __END_DECLS
diff --git a/liblog/logger.h b/liblog/logger.h
index 02cad22..9d74d29 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -46,84 +46,28 @@
                size_t nr);
 };
 
-struct android_log_logger_list;
-struct android_log_transport_context;
-struct android_log_logger;
-
-struct android_log_transport_read {
-  const char* name; /* human name to describe the transport */
-
-  /* Does not cause resources to be taken */
-  int (*available)(log_id_t logId);
-  int (*version)(struct android_log_logger* logger,
-                 struct android_log_transport_context* transp);
-  /* Release resources taken by the following interfaces */
-  void (*close)(struct android_log_logger_list* logger_list,
-                struct android_log_transport_context* transp);
-  /*
-   * Expect all to instantiate open automagically on any call,
-   * so we do not have an explicit open call.
-   */
-  int (*read)(struct android_log_logger_list* logger_list,
-              struct android_log_transport_context* transp,
-              struct log_msg* log_msg);
-  /* Must only be called if not ANDROID_LOG_NONBLOCK (blocking) */
-  int (*poll)(struct android_log_logger_list* logger_list,
-              struct android_log_transport_context* transp);
-
-  int (*clear)(struct android_log_logger* logger,
-               struct android_log_transport_context* transp);
-  ssize_t (*setSize)(struct android_log_logger* logger,
-                     struct android_log_transport_context* transp, size_t size);
-  ssize_t (*getSize)(struct android_log_logger* logger,
-                     struct android_log_transport_context* transp);
-  ssize_t (*getReadableSize)(struct android_log_logger* logger,
-                             struct android_log_transport_context* transp);
-
-  ssize_t (*getPrune)(struct android_log_logger_list* logger_list,
-                      struct android_log_transport_context* transp, char* buf,
-                      size_t len);
-  ssize_t (*setPrune)(struct android_log_logger_list* logger_list,
-                      struct android_log_transport_context* transp, char* buf,
-                      size_t len);
-  ssize_t (*getStats)(struct android_log_logger_list* logger_list,
-                      struct android_log_transport_context* transp, char* buf,
-                      size_t len);
-};
-
-struct android_log_transport_context {
-  union android_log_context_union context; /* zero init per-transport context */
-
-  struct android_log_transport_read* transport;
-  unsigned logMask;      /* mask of requested log buffers */
-};
-
-struct android_log_logger_list {
-  struct listnode logger;
-  android_log_transport_context transport_context;
-  bool transport_initialized;
+struct logger_list {
+  atomic_int fd;
   int mode;
   unsigned int tail;
   log_time start;
   pid_t pid;
+  uint32_t log_mask;
 };
 
-struct android_log_logger {
-  struct listnode node;
-  struct android_log_logger_list* parent;
+// Format for a 'logger' entry: uintptr_t where only the bottom 32 bits are used.
+// bit 31: Set if this 'logger' is for logd.
+// bit 30: Set if this 'logger' is for pmsg
+// bits 0-2: the decimal value of the log buffer.
+// Other bits are unused.
 
-  log_id_t logId;
-};
+#define LOGGER_LOGD (1U << 31)
+#define LOGGER_PMSG (1U << 30)
+#define LOGGER_LOG_ID_MASK ((1U << 3) - 1)
 
-/* assumes caller has structures read-locked, single threaded, or fenced */
-#define logger_for_each(logp, logger_list)                          \
-  for ((logp) = node_to_item((logger_list)->logger.next,            \
-                             struct android_log_logger, node);      \
-       ((logp) != node_to_item(&(logger_list)->logger,              \
-                               struct android_log_logger, node)) && \
-       ((logp)->parent == (logger_list));                           \
-       (logp) =                                                     \
-           node_to_item((logp)->node.next, struct android_log_logger, node))
+inline bool android_logger_is_logd(struct logger* logger) {
+  return reinterpret_cast<uintptr_t>(logger) & LOGGER_LOGD;
+}
 
 /* OS specific dribs and drabs */
 
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
index 4b4012a..c65501c 100644
--- a/liblog/logger_read.cpp
+++ b/liblog/logger_read.cpp
@@ -21,6 +21,7 @@
 #include <pthread.h>
 #include <sched.h>
 #include <stddef.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -30,223 +31,49 @@
 #include <private/android_filesystem_config.h>
 
 #include "log_portability.h"
+#include "logd_reader.h"
 #include "logger.h"
-
-/* android_logger_alloc unimplemented, no use case */
-/* android_logger_free not exported */
-static void android_logger_free(struct logger* logger) {
-  struct android_log_logger* logger_internal = (struct android_log_logger*)logger;
-
-  if (!logger_internal) {
-    return;
-  }
-
-  list_remove(&logger_internal->node);
-
-  free(logger_internal);
-}
-
-/* android_logger_alloc unimplemented, no use case */
+#include "pmsg_reader.h"
 
 /* method for getting the associated sublog id */
 log_id_t android_logger_get_id(struct logger* logger) {
-  return ((struct android_log_logger*)logger)->logId;
+  return static_cast<log_id_t>(reinterpret_cast<uintptr_t>(logger) & LOGGER_LOG_ID_MASK);
 }
 
-static int init_transport_context(struct android_log_logger_list* logger_list) {
+static struct logger_list* android_logger_list_alloc_internal(int mode, unsigned int tail,
+                                                              log_time start, pid_t pid) {
+  auto* logger_list = static_cast<struct logger_list*>(calloc(1, sizeof(struct logger_list)));
   if (!logger_list) {
-    return -EINVAL;
+    return nullptr;
   }
 
-  if (list_empty(&logger_list->logger)) {
-    return -EINVAL;
-  }
-
-  if (logger_list->transport_initialized) {
-    return 0;
-  }
-
-#if (FAKE_LOG_DEVICE == 0)
-  extern struct android_log_transport_read logdLoggerRead;
-  extern struct android_log_transport_read pmsgLoggerRead;
-
-  struct android_log_transport_read* transport;
-  transport = (logger_list->mode & ANDROID_LOG_PSTORE) ? &pmsgLoggerRead : &logdLoggerRead;
-
-  struct android_log_logger* logger;
-  unsigned logMask = 0;
-
-  logger_for_each(logger, logger_list) {
-    log_id_t logId = logger->logId;
-
-    if (logId == LOG_ID_SECURITY && __android_log_uid() != AID_SYSTEM) {
-      continue;
-    }
-    if (transport->read && (!transport->available || transport->available(logId) >= 0)) {
-      logMask |= 1 << logId;
-    }
-  }
-  if (!logMask) {
-    return -ENODEV;
-  }
-
-  logger_list->transport_context.transport = transport;
-  logger_list->transport_context.logMask = logMask;
-#endif
-  return 0;
-}
-
-#define LOGGER_FUNCTION(logger, def, func, args...)                                               \
-  ssize_t ret = -EINVAL;                                                                          \
-  android_log_logger* logger_internal = reinterpret_cast<android_log_logger*>(logger);            \
-                                                                                                  \
-  if (!logger_internal) {                                                                         \
-    return ret;                                                                                   \
-  }                                                                                               \
-  ret = init_transport_context(logger_internal->parent);                                          \
-  if (ret < 0) {                                                                                  \
-    return ret;                                                                                   \
-  }                                                                                               \
-                                                                                                  \
-  ret = (def);                                                                                    \
-  android_log_transport_context* transport_context = &logger_internal->parent->transport_context; \
-  if (transport_context->logMask & (1 << logger_internal->logId) &&                               \
-      transport_context->transport && transport_context->transport->func) {                       \
-    ssize_t retval =                                                                              \
-        (transport_context->transport->func)(logger_internal, transport_context, ##args);         \
-    if (ret >= 0 || ret == (def)) {                                                               \
-      ret = retval;                                                                               \
-    }                                                                                             \
-  }                                                                                               \
-  return ret
-
-int android_logger_clear(struct logger* logger) {
-  LOGGER_FUNCTION(logger, -ENODEV, clear);
-}
-
-/* returns the total size of the log's ring buffer */
-long android_logger_get_log_size(struct logger* logger) {
-  LOGGER_FUNCTION(logger, -ENODEV, getSize);
-}
-
-int android_logger_set_log_size(struct logger* logger, unsigned long size) {
-  LOGGER_FUNCTION(logger, -ENODEV, setSize, size);
-}
-
-/*
- * returns the readable size of the log's ring buffer (that is, amount of the
- * log consumed)
- */
-long android_logger_get_log_readable_size(struct logger* logger) {
-  LOGGER_FUNCTION(logger, -ENODEV, getReadableSize);
-}
-
-/*
- * returns the logger version
- */
-int android_logger_get_log_version(struct logger* logger) {
-  LOGGER_FUNCTION(logger, 4, version);
-}
-
-#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...)                                  \
-  android_log_logger_list* logger_list_internal =                                              \
-      reinterpret_cast<android_log_logger_list*>(logger_list);                                 \
-                                                                                               \
-  ssize_t ret = init_transport_context(logger_list_internal);                                  \
-  if (ret < 0) {                                                                               \
-    return ret;                                                                                \
-  }                                                                                            \
-                                                                                               \
-  ret = (def);                                                                                 \
-  android_log_transport_context* transport_context = &logger_list_internal->transport_context; \
-  if (transport_context->transport && transport_context->transport->func) {                    \
-    ssize_t retval =                                                                           \
-        (transport_context->transport->func)(logger_list_internal, transport_context, ##args); \
-    if (ret >= 0 || ret == (def)) {                                                            \
-      ret = retval;                                                                            \
-    }                                                                                          \
-  }                                                                                            \
-  return ret
-
-/*
- * returns statistics
- */
-ssize_t android_logger_get_statistics(struct logger_list* logger_list, char* buf, size_t len) {
-  LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getStats, buf, len);
-}
-
-ssize_t android_logger_get_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
-  LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getPrune, buf, len);
-}
-
-int android_logger_set_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
-  LOGGER_LIST_FUNCTION(logger_list, -ENODEV, setPrune, buf, len);
-}
-
-struct logger_list* android_logger_list_alloc(int mode, unsigned int tail, pid_t pid) {
-  struct android_log_logger_list* logger_list;
-
-  logger_list = static_cast<android_log_logger_list*>(calloc(1, sizeof(*logger_list)));
-  if (!logger_list) {
-    return NULL;
-  }
-
-  list_init(&logger_list->logger);
   logger_list->mode = mode;
+  logger_list->start = start;
   logger_list->tail = tail;
   logger_list->pid = pid;
 
-  return (struct logger_list*)logger_list;
+  return logger_list;
+}
+
+struct logger_list* android_logger_list_alloc(int mode, unsigned int tail, pid_t pid) {
+  return android_logger_list_alloc_internal(mode, tail, log_time(0, 0), pid);
 }
 
 struct logger_list* android_logger_list_alloc_time(int mode, log_time start, pid_t pid) {
-  struct android_log_logger_list* logger_list;
-
-  logger_list = static_cast<android_log_logger_list*>(calloc(1, sizeof(*logger_list)));
-  if (!logger_list) {
-    return NULL;
-  }
-
-  list_init(&logger_list->logger);
-  logger_list->mode = mode;
-  logger_list->start = start;
-  logger_list->pid = pid;
-
-  return (struct logger_list*)logger_list;
+  return android_logger_list_alloc_internal(mode, 0, start, pid);
 }
 
-/* android_logger_list_register unimplemented, no use case */
-/* android_logger_list_unregister unimplemented, no use case */
-
 /* Open the named log and add it to the logger list */
 struct logger* android_logger_open(struct logger_list* logger_list, log_id_t logId) {
-  struct android_log_logger_list* logger_list_internal =
-      (struct android_log_logger_list*)logger_list;
-  struct android_log_logger* logger;
-
-  if (!logger_list_internal || (logId >= LOG_ID_MAX)) {
+  if (!logger_list || (logId >= LOG_ID_MAX)) {
     return nullptr;
   }
 
-  logger_for_each(logger, logger_list_internal) {
-    if (logger->logId == logId) {
-      return reinterpret_cast<struct logger*>(logger);
-    }
-  }
+  logger_list->log_mask |= 1 << logId;
 
-  logger = static_cast<android_log_logger*>(calloc(1, sizeof(*logger)));
-  if (!logger) {
-    return nullptr;
-  }
-
-  logger->logId = logId;
-  list_add_tail(&logger_list_internal->logger, &logger->node);
-  logger->parent = logger_list_internal;
-
-  // Reset known transport to re-evaluate, since we added a new logger.
-  logger_list_internal->transport_initialized = false;
-
-  return (struct logger*)logger;
+  uintptr_t logger = logId;
+  logger |= (logger_list->mode & ANDROID_LOG_PSTORE) ? LOGGER_PMSG : LOGGER_LOGD;
+  return reinterpret_cast<struct logger*>(logger);
 }
 
 /* Open the single named log and make it part of a new logger list */
@@ -266,13 +93,22 @@
   return logger_list;
 }
 
-/* Validate log_msg packet, read function has already been null checked */
-static int android_transport_read(struct android_log_logger_list* logger_list,
-                                  struct android_log_transport_context* transp,
-                                  struct log_msg* log_msg) {
-  int ret = (*transp->transport->read)(logger_list, transp, log_msg);
+int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
+  if (logger_list == nullptr || logger_list->log_mask == 0) {
+    return -EINVAL;
+  }
 
-  if (ret < 0) {
+  int ret = 0;
+
+#if (FAKE_LOG_DEVICE == 0)
+  if (logger_list->mode & ANDROID_LOG_PSTORE) {
+    ret = PmsgRead(logger_list, log_msg);
+  } else {
+    ret = LogdRead(logger_list, log_msg);
+  }
+#endif
+
+  if (ret <= 0) {
     return ret;
   }
 
@@ -295,40 +131,19 @@
   return ret;
 }
 
-/* Read from the selected logs */
-int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
-  struct android_log_logger_list* logger_list_internal =
-      (struct android_log_logger_list*)logger_list;
-
-  int ret = init_transport_context(logger_list_internal);
-  if (ret < 0) {
-    return ret;
-  }
-
-  android_log_transport_context* transport_context = &logger_list_internal->transport_context;
-  return android_transport_read(logger_list_internal, transport_context, log_msg);
-}
-
 /* Close all the logs */
 void android_logger_list_free(struct logger_list* logger_list) {
-  struct android_log_logger_list* logger_list_internal =
-      (struct android_log_logger_list*)logger_list;
-
-  if (logger_list_internal == NULL) {
+  if (logger_list == NULL) {
     return;
   }
 
-  android_log_transport_context* transport_context = &logger_list_internal->transport_context;
-
-  if (transport_context->transport && transport_context->transport->close) {
-    (*transport_context->transport->close)(logger_list_internal, transport_context);
+#if (FAKE_LOG_DEVICE == 0)
+  if (logger_list->mode & ANDROID_LOG_PSTORE) {
+    PmsgClose(logger_list);
+  } else {
+    LogdClose(logger_list);
   }
+#endif
 
-  while (!list_empty(&logger_list_internal->logger)) {
-    struct listnode* node = list_head(&logger_list_internal->logger);
-    struct android_log_logger* logger = node_to_item(node, struct android_log_logger, node);
-    android_logger_free((struct logger*)logger);
-  }
-
-  free(logger_list_internal);
+  free(logger_list);
 }
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index f43ce3a..9390fec 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "pmsg_reader.h"
+
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -26,57 +28,7 @@
 
 #include "logger.h"
 
-static int pmsgAvailable(log_id_t logId);
-static int pmsgVersion(struct android_log_logger* logger,
-                       struct android_log_transport_context* transp);
-static int pmsgRead(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp, struct log_msg* log_msg);
-static void pmsgClose(struct android_log_logger_list* logger_list,
-                      struct android_log_transport_context* transp);
-static int pmsgClear(struct android_log_logger* logger,
-                     struct android_log_transport_context* transp);
-
-struct android_log_transport_read pmsgLoggerRead = {
-    .name = "pmsg",
-    .available = pmsgAvailable,
-    .version = pmsgVersion,
-    .close = pmsgClose,
-    .read = pmsgRead,
-    .poll = NULL,
-    .clear = pmsgClear,
-    .setSize = NULL,
-    .getSize = NULL,
-    .getReadableSize = NULL,
-    .getPrune = NULL,
-    .setPrune = NULL,
-    .getStats = NULL,
-};
-
-static int pmsgAvailable(log_id_t logId) {
-  if (logId > LOG_ID_SECURITY) {
-    return -EINVAL;
-  }
-  if (access("/dev/pmsg0", W_OK) == 0) {
-    return 0;
-  }
-  return -EBADF;
-}
-
-static int pmsgClear(struct android_log_logger* logger __unused,
-                     struct android_log_transport_context* transp __unused) {
-  return unlink("/sys/fs/pstore/pmsg-ramoops-0");
-}
-
-/*
- * returns the logger version
- */
-static int pmsgVersion(struct android_log_logger* logger __unused,
-                       struct android_log_transport_context* transp __unused) {
-  return 4;
-}
-
-static int pmsgRead(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp, struct log_msg* log_msg) {
+int PmsgRead(struct logger_list* logger_list, struct log_msg* log_msg) {
   ssize_t ret;
   off_t current, next;
   struct __attribute__((__packed__)) {
@@ -88,7 +40,7 @@
 
   memset(log_msg, 0, sizeof(*log_msg));
 
-  if (atomic_load(&transp->context.fd) <= 0) {
+  if (atomic_load(&logger_list->fd) <= 0) {
     int i, fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC);
 
     if (fd < 0) {
@@ -101,7 +53,7 @@
         return -errno;
       }
     }
-    i = atomic_exchange(&transp->context.fd, fd);
+    i = atomic_exchange(&logger_list->fd, fd);
     if ((i > 0) && (i != fd)) {
       close(i);
     }
@@ -112,7 +64,7 @@
     int fd;
 
     if (preread_count < sizeof(buf)) {
-      fd = atomic_load(&transp->context.fd);
+      fd = atomic_load(&logger_list->fd);
       if (fd <= 0) {
         return -EBADF;
       }
@@ -138,7 +90,7 @@
     }
     preread_count = 0;
 
-    if ((transp->logMask & (1 << buf.l.id)) &&
+    if ((logger_list->log_mask & (1 << buf.l.id)) &&
         ((!logger_list->start.tv_sec && !logger_list->start.tv_nsec) ||
          ((logger_list->start.tv_sec <= buf.l.realtime.tv_sec) &&
           ((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
@@ -146,7 +98,7 @@
         (!logger_list->pid || (logger_list->pid == buf.p.pid))) {
       char* msg = log_msg->entry.msg;
       *msg = buf.prio;
-      fd = atomic_load(&transp->context.fd);
+      fd = atomic_load(&logger_list->fd);
       if (fd <= 0) {
         return -EBADF;
       }
@@ -170,7 +122,7 @@
       return ret + sizeof(buf.prio) + log_msg->entry.hdr_size;
     }
 
-    fd = atomic_load(&transp->context.fd);
+    fd = atomic_load(&logger_list->fd);
     if (fd <= 0) {
       return -EBADF;
     }
@@ -178,7 +130,7 @@
     if (current < 0) {
       return -errno;
     }
-    fd = atomic_load(&transp->context.fd);
+    fd = atomic_load(&logger_list->fd);
     if (fd <= 0) {
       return -EBADF;
     }
@@ -192,9 +144,8 @@
   }
 }
 
-static void pmsgClose(struct android_log_logger_list* logger_list __unused,
-                      struct android_log_transport_context* transp) {
-  int fd = atomic_exchange(&transp->context.fd, 0);
+void PmsgClose(struct logger_list* logger_list) {
+  int fd = atomic_exchange(&logger_list->fd, 0);
   if (fd > 0) {
     close(fd);
   }
@@ -211,8 +162,7 @@
 ssize_t __android_log_pmsg_file_read(log_id_t logId, char prio, const char* prefix,
                                      __android_log_pmsg_file_read_fn fn, void* arg) {
   ssize_t ret;
-  struct android_log_logger_list logger_list;
-  struct android_log_transport_context transp;
+  struct logger_list logger_list;
   struct content {
     struct listnode node;
     struct logger_entry entry;
@@ -234,15 +184,14 @@
 
   /* Add just enough clues in logger_list and transp to make API function */
   memset(&logger_list, 0, sizeof(logger_list));
-  memset(&transp, 0, sizeof(transp));
 
   logger_list.mode = ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK | ANDROID_LOG_RDONLY;
-  transp.logMask = (unsigned)-1;
+  logger_list.log_mask = (unsigned)-1;
   if (logId != LOG_ID_ANY) {
-    transp.logMask = (1 << logId);
+    logger_list.log_mask = (1 << logId);
   }
-  transp.logMask &= ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
-  if (!transp.logMask) {
+  logger_list.log_mask &= ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
+  if (!logger_list.log_mask) {
     return -EINVAL;
   }
 
@@ -268,7 +217,7 @@
 
   /* Read the file content */
   log_msg log_msg;
-  while (pmsgRead(&logger_list, &transp, &log_msg) > 0) {
+  while (PmsgRead(&logger_list, &log_msg) > 0) {
     const char* cp;
     size_t hdr_size = log_msg.entry.hdr_size;
 
@@ -426,7 +375,7 @@
     }
     list_add_head(node, &content->node);
   }
-  pmsgClose(&logger_list, &transp);
+  PmsgClose(&logger_list);
 
   /* Progress through all the collected files */
   list_for_each_safe(node, n, &name_list) {
diff --git a/liblog/pmsg_reader.h b/liblog/pmsg_reader.h
new file mode 100644
index 0000000..53746d8
--- /dev/null
+++ b/liblog/pmsg_reader.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 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 <unistd.h>
+
+#include "log/log_read.h"
+#include "log_portability.h"
+
+__BEGIN_DECLS
+
+int PmsgRead(struct logger_list* logger_list, struct log_msg* log_msg);
+void PmsgClose(struct logger_list* logger_list);
+
+__END_DECLS
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 56892a2..39ac7a5 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -913,7 +913,7 @@
 }
 BENCHMARK(BM_lookupEventTagNum);
 
-// Must be functionally identical to liblog internal __send_log_msg.
+// Must be functionally identical to liblog internal SendLogdControlMessage()
 static void send_to_control(char* buf, size_t len) {
   int sock =
       socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM | SOCK_CLOEXEC);
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index 6af49bb..8212eba 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -113,6 +113,10 @@
     if (ret == 0) {
         memcpy(ss, ai->ai_addr, ai->ai_addrlen);
         freeaddrinfo(ai);
+    } else {
+        // Getaddrinfo has its own error codes. Convert to negative errno.
+        // There, the only thing that can reasonably happen is that the passed-in string is invalid.
+        ret = (ret == EAI_SYSTEM) ? -errno : -EINVAL;
     }
 
     return ret;
@@ -263,19 +267,13 @@
     struct {
         struct nlmsghdr n;
         struct ifaddrmsg r;
-        // Allow for IPv6 address, headers, IPv4 broadcast addr and padding.
-        char attrbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
-                     NLMSG_ALIGN(sizeof(struct rtattr)) +
-                     NLMSG_ALIGN(INET6_ADDRLEN) +
-                     NLMSG_ALIGN(sizeof(struct rtattr)) +
-                     NLMSG_ALIGN(INET_ADDRLEN)];
+        // Allow for IPv4 or IPv6 address, headers, IPv4 broadcast address and padding.
+        char attrbuf[NLMSG_ALIGN(sizeof(struct rtattr)) + NLMSG_ALIGN(INET6_ADDRLEN) +
+                     NLMSG_ALIGN(sizeof(struct rtattr)) + NLMSG_ALIGN(INET_ADDRLEN)];
     } req;
     struct rtattr *rta;
     struct nlmsghdr *nh;
     struct nlmsgerr *err;
-    char buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
-             NLMSG_ALIGN(sizeof(struct nlmsgerr)) +
-             NLMSG_ALIGN(sizeof(struct nlmsghdr))];
 
     // Get interface ID.
     ifindex = if_nametoindex(name);
@@ -344,6 +342,7 @@
         return -saved_errno;
     }
 
+    char buf[NLMSG_ALIGN(sizeof(struct nlmsgerr)) + sizeof(req)];
     len = recv(s, buf, sizeof(buf), 0);
     saved_errno = errno;
     close(s);
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 5871a63..0341902 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -39,6 +39,13 @@
       "Mode": "0755",
       "UID": "system",
       "GID": "system"
+    },
+    {
+      "Controller": "freezer",
+      "Path": "/dev/freezer",
+      "Mode": "0755",
+      "UID": "system",
+      "GID": "system"
     }
   ],
   "Cgroups2": {
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 608f007..3f3dbd7 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -67,6 +67,32 @@
       ]
     },
     {
+      "Name": "Frozen",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "freezer",
+            "Path": "frozen"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "Unfrozen",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "freezer",
+            "Path": ""
+          }
+        }
+      ]
+    },
+    {
       "Name": "NormalPerformance",
       "Actions": [
         {
diff --git a/libprocessgroup/profiles/test.cpp b/libprocessgroup/profiles/test.cpp
index bc9aade..b37e3e6 100644
--- a/libprocessgroup/profiles/test.cpp
+++ b/libprocessgroup/profiles/test.cpp
@@ -33,14 +33,14 @@
 }
 
 // Test suite instantiations
-INSTANTIATE_TEST_SUITE_P(, JsonSchemaTest,
+INSTANTIATE_TEST_SUITE_P(Cgroups, JsonSchemaTest,
                          ::testing::Values(MakeTestParam<Cgroups>("/cgroups.json"),
                                            MakeTestParam<Cgroups>("/cgroups.recovery.json"),
                                            MakeTestParam<TaskProfiles>("/task_profiles.json")));
-INSTANTIATE_TEST_SUITE_P(, CgroupsTest,
+INSTANTIATE_TEST_SUITE_P(Cgroups, CgroupsTest,
                          ::testing::Values(MakeTestParam<Cgroups>("/cgroups.json"),
                                            MakeTestParam<Cgroups>("/cgroups.recovery.json")));
-INSTANTIATE_TEST_SUITE_P(, TaskProfilesTest,
+INSTANTIATE_TEST_SUITE_P(TaskProfiles, TaskProfilesTest,
                          ::testing::Values(MakeTestParam<TaskProfiles>("/task_profiles.json")));
 
 }  // namespace profiles
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index c5c4960..e35cb0d 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -493,6 +493,10 @@
 
 void output_file_close(struct output_file* out) {
   out->sparse_ops->write_end_chunk(out);
+  free(out->zero_buf);
+  free(out->fill_buf);
+  out->zero_buf = nullptr;
+  out->fill_buf = nullptr;
   out->ops->close(out);
 }
 
diff --git a/libsync/libsync.map.txt b/libsync/libsync.map.txt
index 53bb07a..91c3528 100644
--- a/libsync/libsync.map.txt
+++ b/libsync/libsync.map.txt
@@ -19,10 +19,10 @@
     sync_merge; # introduced=26
     sync_file_info; # introduced=26
     sync_file_info_free; # introduced=26
-    sync_wait; # vndk
-    sync_fence_info; # vndk
-    sync_pt_info; # vndk
-    sync_fence_info_free; # vndk
+    sync_wait; # llndk
+    sync_fence_info; # llndk
+    sync_pt_info; # llndk
+    sync_fence_info_free; # llndk
   local:
     *;
 };
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 2573b1c..512c962 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -77,6 +77,7 @@
     ],
 
     cflags: [
+        "-DDEXFILE_SUPPORT",
         "-Wexit-time-destructors",
     ],
 
@@ -89,20 +90,18 @@
             ],
         },
         vendor: {
-            cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+            cflags: ["-UDEXFILE_SUPPORT"],
             exclude_srcs: [
                 "DexFile.cpp",
-                "DexFiles.cpp",
             ],
             exclude_shared_libs: [
                 "libdexfile_support",
             ],
         },
         recovery: {
-            cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+            cflags: ["-UDEXFILE_SUPPORT"],
             exclude_srcs: [
                 "DexFile.cpp",
-                "DexFiles.cpp",
             ],
             exclude_shared_libs: [
                 "libdexfile_support",
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
index 63a77e5..2057fad 100644
--- a/libunwindstack/DexFiles.cpp
+++ b/libunwindstack/DexFiles.cpp
@@ -27,10 +27,21 @@
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 
+#if defined(DEXFILE_SUPPORT)
 #include "DexFile.h"
+#endif
 
 namespace unwindstack {
 
+#if !defined(DEXFILE_SUPPORT)
+// Empty class definition.
+class DexFile {
+ public:
+  DexFile() = default;
+  virtual ~DexFile() = default;
+};
+#endif
+
 struct DEXFileEntry32 {
   uint32_t next;
   uint32_t prev;
@@ -128,6 +139,7 @@
   FindAndReadVariable(maps, "__dex_debug_descriptor");
 }
 
+#if defined(DEXFILE_SUPPORT)
 DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
   // Lock while processing the data.
   DexFile* dex_file;
@@ -141,6 +153,11 @@
   }
   return dex_file;
 }
+#else
+DexFile* DexFiles::GetDexFile(uint64_t, MapInfo*) {
+  return nullptr;
+}
+#endif
 
 bool DexFiles::GetAddr(size_t index, uint64_t* addr) {
   if (index < addrs_.size()) {
@@ -154,6 +171,7 @@
   return false;
 }
 
+#if defined(DEXFILE_SUPPORT)
 void DexFiles::GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc,
                                     std::string* method_name, uint64_t* method_offset) {
   std::lock_guard<std::mutex> guard(lock_);
@@ -175,5 +193,8 @@
     }
   }
 }
+#else
+void DexFiles::GetMethodInformation(Maps*, MapInfo*, uint64_t, std::string*, uint64_t*) {}
+#endif
 
 }  // namespace unwindstack
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index e9787aa..2e8af20 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -103,6 +103,7 @@
   fn("sp", regs_[ARM64_REG_SP]);
   fn("lr", regs_[ARM64_REG_LR]);
   fn("pc", regs_[ARM64_REG_PC]);
+  fn("pst", regs_[ARM64_REG_PSTATE]);
 }
 
 Regs* RegsArm64::Read(void* remote_data) {
@@ -113,6 +114,7 @@
   uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
   reg_data[ARM64_REG_PC] = user->pc;
   reg_data[ARM64_REG_SP] = user->sp;
+  reg_data[ARM64_REG_PSTATE] = user->pstate;
   return regs;
 }
 
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 0b9b85c..1bb0319 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -34,9 +34,7 @@
 #include <unwindstack/Memory.h>
 #include <unwindstack/Unwinder.h>
 
-#if !defined(NO_LIBDEXFILE_SUPPORT)
 #include <unwindstack/DexFiles.h>
-#endif
 
 // Use the demangler from libc++.
 extern "C" char* __cxa_demangle(const char*, char*, size_t*, int* status);
@@ -84,7 +82,7 @@
     return;
   }
 
-#if !defined(NO_LIBDEXFILE_SUPPORT)
+#if defined(DEXFILE_SUPPORT)
   if (dex_files_ == nullptr) {
     return;
   }
@@ -367,12 +365,10 @@
   jit_debug_ = jit_debug;
 }
 
-#if !defined(NO_LIBDEXFILE_SUPPORT)
 void Unwinder::SetDexFiles(DexFiles* dex_files, ArchEnum arch) {
   dex_files->SetArch(arch);
   dex_files_ = dex_files;
 }
-#endif
 
 bool UnwinderFromPid::Init(ArchEnum arch) {
   if (pid_ == getpid()) {
@@ -390,7 +386,7 @@
   jit_debug_ptr_.reset(new JitDebug(process_memory_));
   jit_debug_ = jit_debug_ptr_.get();
   SetJitDebug(jit_debug_, arch);
-#if !defined(NO_LIBDEXFILE_SUPPORT)
+#if defined(DEXFILE_SUPPORT)
   dex_files_ptr_.reset(new DexFiles(process_memory_));
   dex_files_ = dex_files_ptr_.get();
   SetDexFiles(dex_files_, arch);
diff --git a/libunwindstack/include/unwindstack/MachineArm64.h b/libunwindstack/include/unwindstack/MachineArm64.h
index e8b778b..e953335 100644
--- a/libunwindstack/include/unwindstack/MachineArm64.h
+++ b/libunwindstack/include/unwindstack/MachineArm64.h
@@ -55,6 +55,7 @@
   ARM64_REG_R30,
   ARM64_REG_R31,
   ARM64_REG_PC,
+  ARM64_REG_PSTATE,
   ARM64_REG_LAST,
 
   ARM64_REG_SP = ARM64_REG_R31,
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 11ad9de..67762c0 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -107,9 +107,7 @@
 
   void SetDisplayBuildID(bool display_build_id) { display_build_id_ = display_build_id; }
 
-#if !defined(NO_LIBDEXFILE_SUPPORT)
   void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
-#endif
 
   bool elf_from_memory_not_file() { return elf_from_memory_not_file_; }
 
@@ -128,9 +126,7 @@
   std::vector<FrameData> frames_;
   std::shared_ptr<Memory> process_memory_;
   JitDebug* jit_debug_ = nullptr;
-#if !defined(NO_LIBDEXFILE_SUPPORT)
   DexFiles* dex_files_ = nullptr;
-#endif
   bool resolve_names_ = true;
   bool embedded_soname_ = true;
   bool display_build_id_ = false;
@@ -151,9 +147,7 @@
   pid_t pid_;
   std::unique_ptr<Maps> maps_ptr_;
   std::unique_ptr<JitDebug> jit_debug_ptr_;
-#if !defined(NO_LIBDEXFILE_SUPPORT)
   std::unique_ptr<DexFiles> dex_files_ptr_;
-#endif
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 7b1dd92..69a7816 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -1662,7 +1662,7 @@
   ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
 }
 
-INSTANTIATE_TEST_SUITE_P(, ArmExidxDecodeTest,
+INSTANTIATE_TEST_SUITE_P(Unwindstack, ArmExidxDecodeTest,
                          ::testing::Values("logging", "register_logging", "no_logging"));
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
index 7e36953..bc95851 100644
--- a/libunwindstack/tests/RegsIterateTest.cpp
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -114,6 +114,7 @@
   result.push_back({"sp", ARM64_REG_SP});
   result.push_back({"lr", ARM64_REG_LR});
   result.push_back({"pc", ARM64_REG_PC});
+  result.push_back({"pst", ARM64_REG_PSTATE});
   return result;
 }
 
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 98921be..efa4c41 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -125,6 +125,7 @@
     native_bridge_supported: true,
 
     srcs: [
+        "Errors.cpp",
         "FileMap.cpp",
         "JenkinsHash.cpp",
         "NativeHandle.cpp",
diff --git a/libutils/Errors.cpp b/libutils/Errors.cpp
new file mode 100644
index 0000000..2dfd138
--- /dev/null
+++ b/libutils/Errors.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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/Errors.h>
+
+namespace android {
+
+std::string statusToString(status_t s) {
+#define STATUS_CASE(STATUS) \
+    case STATUS:            \
+        return #STATUS
+
+    switch (s) {
+        STATUS_CASE(OK);
+        STATUS_CASE(UNKNOWN_ERROR);
+        STATUS_CASE(NO_MEMORY);
+        STATUS_CASE(INVALID_OPERATION);
+        STATUS_CASE(BAD_VALUE);
+        STATUS_CASE(BAD_TYPE);
+        STATUS_CASE(NAME_NOT_FOUND);
+        STATUS_CASE(PERMISSION_DENIED);
+        STATUS_CASE(NO_INIT);
+        STATUS_CASE(ALREADY_EXISTS);
+        STATUS_CASE(DEAD_OBJECT);
+        STATUS_CASE(FAILED_TRANSACTION);
+        STATUS_CASE(BAD_INDEX);
+        STATUS_CASE(NOT_ENOUGH_DATA);
+        STATUS_CASE(WOULD_BLOCK);
+        STATUS_CASE(TIMED_OUT);
+        STATUS_CASE(UNKNOWN_TRANSACTION);
+        STATUS_CASE(FDS_NOT_ALLOWED);
+        STATUS_CASE(UNEXPECTED_NULL);
+#undef STATUS_CASE
+    }
+
+    return std::to_string(s) + ' ' + strerror(-s);
+}
+
+}  // namespace android
diff --git a/libutils/StrongPointer.cpp b/libutils/StrongPointer.cpp
index ba52502..ef46723 100644
--- a/libutils/StrongPointer.cpp
+++ b/libutils/StrongPointer.cpp
@@ -21,4 +21,7 @@
 namespace android {
 
 void sp_report_race() { LOG_ALWAYS_FATAL("sp<> assignment detected data race"); }
+
+void sp_report_stack_pointer() { LOG_ALWAYS_FATAL("sp<> constructed with stack pointer argument"); }
+
 }
diff --git a/libutils/include/utils/Errors.h b/libutils/include/utils/Errors.h
index 1e03677..d14d223 100644
--- a/libutils/include/utils/Errors.h
+++ b/libutils/include/utils/Errors.h
@@ -19,6 +19,7 @@
 #include <errno.h>
 #include <stdint.h>
 #include <sys/types.h>
+#include <string>
 
 namespace android {
 
@@ -72,6 +73,9 @@
     UNEXPECTED_NULL     = (UNKNOWN_ERROR + 8),
 };
 
+// Human readable name of error
+std::string statusToString(status_t status);
+
 // Restore define; enumeration is in "android" namespace, so the value defined
 // there won't work for Win32 code in a different namespace.
 #ifdef _WIN32
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 9cd7c75..07dd3f1 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -122,26 +122,54 @@
         return o != *this;
     }
 
-private:    
+private:
     template<typename Y> friend class sp;
     template<typename Y> friend class wp;
     void set_pointer(T* ptr);
+    static inline void check_not_on_stack(const void* ptr);
     T* m_ptr;
 };
 
-// For code size reasons, we do not want this inlined or templated.
+// For code size reasons, we do not want these inlined or templated.
 void sp_report_race();
+void sp_report_stack_pointer();
 
 #undef COMPARE
 
 // ---------------------------------------------------------------------------
 // No user serviceable parts below here.
 
+// Check whether address is definitely on the calling stack.  We actually check whether it is on
+// the same 4K page as the frame pointer.
+//
+// Assumptions:
+// - Pages are never smaller than 4K (MIN_PAGE_SIZE)
+// - Malloced memory never shares a page with a stack.
+//
+// It does not appear safe to broaden this check to include adjacent pages; apparently this code
+// is used in environments where there may not be a guard page below (at higher addresses than)
+// the bottom of the stack.
+//
+// TODO: Consider adding make_sp<T>() to allocate an object and wrap the resulting pointer safely
+// without checking overhead.
+template <typename T>
+void sp<T>::check_not_on_stack(const void* ptr) {
+    static constexpr int MIN_PAGE_SIZE = 0x1000;  // 4K. Safer than including sys/user.h.
+    static constexpr uintptr_t MIN_PAGE_MASK = ~static_cast<uintptr_t>(MIN_PAGE_SIZE - 1);
+    uintptr_t my_frame_address =
+            reinterpret_cast<uintptr_t>(__builtin_frame_address(0 /* this frame */));
+    if (((reinterpret_cast<uintptr_t>(ptr) ^ my_frame_address) & MIN_PAGE_MASK) == 0) {
+        sp_report_stack_pointer();
+    }
+}
+
 template<typename T>
 sp<T>::sp(T* other)
         : m_ptr(other) {
-    if (other)
+    if (other) {
+        check_not_on_stack(other);
         other->incStrong(this);
+    }
 }
 
 template<typename T>
@@ -159,8 +187,10 @@
 template<typename T> template<typename U>
 sp<T>::sp(U* other)
         : m_ptr(other) {
-    if (other)
+    if (other) {
+        check_not_on_stack(other);
         (static_cast<T*>(other))->incStrong(this);
+    }
 }
 
 template<typename T> template<typename U>
@@ -207,7 +237,10 @@
 template<typename T>
 sp<T>& sp<T>::operator =(T* other) {
     T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
-    if (other) other->incStrong(this);
+    if (other) {
+        check_not_on_stack(other);
+        other->incStrong(this);
+    }
     if (oldPtr) oldPtr->decStrong(this);
     if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
     m_ptr = other;
diff --git a/libutils/include/utils/Trace.h b/libutils/include/utils/Trace.h
index fec0ffa..9986bf5 100644
--- a/libutils/include/utils/Trace.h
+++ b/libutils/include/utils/Trace.h
@@ -33,7 +33,7 @@
 // ATRACE_NAME traces from its location until the end of its enclosing scope.
 #define _PASTE(x, y) x ## y
 #define PASTE(x, y) _PASTE(x,y)
-#define ATRACE_NAME(name) android::ScopedTrace PASTE(___tracer, __LINE__) (ATRACE_TAG, name)
+#define ATRACE_NAME(name) ::android::ScopedTrace PASTE(___tracer, __LINE__)(ATRACE_TAG, name)
 
 // ATRACE_CALL is an ATRACE_NAME that uses the current function name.
 #define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
diff --git a/libvndksupport/libvndksupport.map.txt b/libvndksupport/libvndksupport.map.txt
index ac9a99c..a44ed18 100644
--- a/libvndksupport/libvndksupport.map.txt
+++ b/libvndksupport/libvndksupport.map.txt
@@ -1,8 +1,8 @@
 LIBVNDKSUPPORT {
   global:
-    android_is_in_vendor_process; # vndk apex
-    android_load_sphal_library; # vndk apex
-    android_unload_sphal_library; # vndk apex
+    android_is_in_vendor_process; # llndk apex
+    android_load_sphal_library; # llndk apex
+    android_unload_sphal_library; # llndk apex
   local:
     *;
 };
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 667bddc..e3bb2ab 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -196,4 +196,5 @@
     srcs: ["libziparchive_fuzzer.cpp"],
     static_libs: ["libziparchive", "libbase", "libz", "liblog"],
     host_supported: true,
+    corpus: ["testdata/*"],
 }
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
index af70f1d..11b575e 100644
--- a/libziparchive/unzip.cpp
+++ b/libziparchive/unzip.cpp
@@ -15,11 +15,12 @@
  */
 
 #include <errno.h>
-#include <error.h>
 #include <fcntl.h>
 #include <fnmatch.h>
 #include <getopt.h>
 #include <inttypes.h>
+#include <libgen.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/stat.h>
@@ -64,6 +65,20 @@
 static uint64_t total_compressed_length = 0;
 static size_t file_count = 0;
 
+static const char* g_progname;
+
+static void die(int error, const char* fmt, ...) {
+  va_list ap;
+
+  va_start(ap, fmt);
+  fprintf(stderr, "%s: ", g_progname);
+  vfprintf(stderr, fmt, ap);
+  if (error != 0) fprintf(stderr, ": %s", strerror(error));
+  fprintf(stderr, "\n");
+  va_end(ap);
+  exit(1);
+}
+
 static bool ShouldInclude(const std::string& name) {
   // Explicitly excluded?
   if (!excludes.empty()) {
@@ -94,9 +109,10 @@
   return (mkdir(path.c_str(), 0777) != -1);
 }
 
-static int CompressionRatio(int64_t uncompressed, int64_t compressed) {
+static float CompressionRatio(int64_t uncompressed, int64_t compressed) {
   if (uncompressed == 0) return 0;
-  return static_cast<int>((100LL * (uncompressed - compressed)) / uncompressed);
+  return static_cast<float>(100LL * (uncompressed - compressed)) /
+         static_cast<float>(uncompressed);
 }
 
 static void MaybeShowHeader(ZipArchiveHandle zah) {
@@ -128,7 +144,7 @@
     if (flag_v) {
       printf(
           "--------          -------  ---                            -------\n"
-          "%8" PRId64 "         %8" PRId64 " %3d%%                            %zu file%s\n",
+          "%8" PRId64 "         %8" PRId64 " %3.0f%%                            %zu file%s\n",
           total_uncompressed_length, total_compressed_length,
           CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
           (file_count == 1) ? "" : "s");
@@ -140,7 +156,7 @@
     }
   } else {
     if (!flag_1 && includes.empty() && excludes.empty()) {
-      printf("%zu files, %" PRId64 " bytes uncompressed, %" PRId64 " bytes compressed: %3d%%\n",
+      printf("%zu files, %" PRId64 " bytes uncompressed, %" PRId64 " bytes compressed:  %.1f%%\n",
              file_count, total_uncompressed_length, total_compressed_length,
              CompressionRatio(total_uncompressed_length, total_compressed_length));
     }
@@ -155,7 +171,7 @@
     char* line = nullptr;
     size_t n;
     if (getline(&line, &n, stdin) == -1) {
-      error(1, 0, "(EOF/read error; assuming [N]one...)");
+      die(0, "(EOF/read error; assuming [N]one...)");
       overwrite_mode = kNever;
       return false;
     }
@@ -183,10 +199,10 @@
   uint8_t* buffer = new uint8_t[entry.uncompressed_length];
   int err = ExtractToMemory(zah, &entry, buffer, entry.uncompressed_length);
   if (err < 0) {
-    error(1, 0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
+    die(0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
   }
   if (!android::base::WriteFully(1, buffer, entry.uncompressed_length)) {
-    error(1, errno, "failed to write %s to stdout", name.c_str());
+    die(errno, "failed to write %s to stdout", name.c_str());
   }
   delete[] buffer;
 }
@@ -194,7 +210,7 @@
 static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
   // Bad filename?
   if (StartsWith(name, "/") || StartsWith(name, "../") || name.find("/../") != std::string::npos) {
-    error(1, 0, "bad filename %s", name.c_str());
+    die(0, "bad filename %s", name.c_str());
   }
 
   // Where are we actually extracting to (for human-readable output)?
@@ -207,7 +223,7 @@
 
   // Ensure the directory hierarchy exists.
   if (!MakeDirectoryHierarchy(android::base::Dirname(name))) {
-    error(1, errno, "couldn't create directory hierarchy for %s", dst.c_str());
+    die(errno, "couldn't create directory hierarchy for %s", dst.c_str());
   }
 
   // An entry in a zip file can just be a directory itself.
@@ -218,7 +234,7 @@
         struct stat sb;
         if (stat(name.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return;
       }
-      error(1, errno, "couldn't extract directory %s", dst.c_str());
+      die(errno, "couldn't extract directory %s", dst.c_str());
     }
     return;
   }
@@ -231,12 +247,12 @@
     // Either overwrite_mode is kAlways or the user consented to this specific case.
     fd = open(name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, entry.unix_mode);
   }
-  if (fd == -1) error(1, errno, "couldn't create file %s", dst.c_str());
+  if (fd == -1) die(errno, "couldn't create file %s", dst.c_str());
 
   // Actually extract into the file.
   if (!flag_q) printf("  inflating: %s\n", dst.c_str());
   int err = ExtractEntryToFile(zah, &entry, fd);
-  if (err < 0) error(1, 0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err));
+  if (err < 0) die(0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err));
   close(fd);
 }
 
@@ -246,7 +262,7 @@
   snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
            t.tm_mday, t.tm_hour, t.tm_min);
   if (flag_v) {
-    printf("%8d  %s  %7d %3d%% %s %08x  %s\n", entry.uncompressed_length,
+    printf("%8d  %s  %7d %3.0f%% %s %08x  %s\n", entry.uncompressed_length,
            (entry.method == kCompressStored) ? "Stored" : "Defl:N", entry.compressed_length,
            CompressionRatio(entry.uncompressed_length, entry.compressed_length), time, entry.crc32,
            name.c_str());
@@ -345,7 +361,7 @@
   void* cookie;
   int err = StartIteration(zah, &cookie);
   if (err != 0) {
-    error(1, 0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
+    die(0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
   }
 
   ZipEntry entry;
@@ -354,7 +370,7 @@
     if (ShouldInclude(name)) ProcessOne(zah, entry, name);
   }
 
-  if (err < -1) error(1, 0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
+  if (err < -1) die(0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
   EndIteration(cookie);
 
   MaybeShowFooter();
@@ -420,18 +436,19 @@
 
 int main(int argc, char* argv[]) {
   // Who am I, and what am I doing?
-  const char* base = basename(argv[0]);
-  if (!strcmp(base, "ziptool") && argc > 1) return main(argc - 1, argv + 1);
-  if (!strcmp(base, "unzip")) {
+  g_progname = basename(argv[0]);
+  if (!strcmp(g_progname, "ziptool") && argc > 1) return main(argc - 1, argv + 1);
+  if (!strcmp(g_progname, "unzip")) {
     role = kUnzip;
-  } else if (!strcmp(base, "zipinfo")) {
+  } else if (!strcmp(g_progname, "zipinfo")) {
     role = kZipinfo;
   } else {
-    error(1, 0, "run as ziptool with unzip or zipinfo as the first argument, or symlink");
+    die(0, "run as ziptool with unzip or zipinfo as the first argument, or symlink");
   }
 
   static const struct option opts[] = {
       {"help", no_argument, 0, 'h'},
+      {},
   };
 
   if (role == kUnzip) {
@@ -484,19 +501,19 @@
     }
   }
 
-  if (!archive_name) error(1, 0, "missing archive filename");
+  if (!archive_name) die(0, "missing archive filename");
 
   // We can't support "-" to unzip from stdin because libziparchive relies on mmap.
   ZipArchiveHandle zah;
   int32_t err;
   if ((err = OpenArchive(archive_name, &zah)) != 0) {
-    error(1, 0, "couldn't open %s: %s", archive_name, ErrorCodeString(err));
+    die(0, "couldn't open %s: %s", archive_name, ErrorCodeString(err));
   }
 
   // Implement -d by changing into that directory.
   // We'll create implicit directories based on paths in the zip file, but we
   // require that the -d directory already exists.
-  if (flag_d && chdir(flag_d) == -1) error(1, errno, "couldn't chdir to %s", flag_d);
+  if (flag_d && chdir(flag_d) == -1) die(errno, "couldn't chdir to %s", flag_d);
 
   ProcessAll(zah);
 
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index e8e125b..deebb8e 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -48,6 +48,7 @@
 cc_library_static {
     name: "liblmkd_utils",
     srcs: ["liblmkd_utils.c"],
+    recovery_available: true,
     shared_libs: [
         "libcutils",
     ],
diff --git a/lmkd/include/liblmkd_utils.h b/lmkd/include/liblmkd_utils.h
index 72e3f4a..92e4d41 100644
--- a/lmkd/include/liblmkd_utils.h
+++ b/lmkd/include/liblmkd_utils.h
@@ -40,6 +40,14 @@
 int lmkd_register_proc(int sock, struct lmk_procprio *params);
 
 /*
+ * Unregisters a process previously registered with lmkd.
+ * On success returns 0.
+ * On error, -1 is returned.
+ * In the case of error errno is set appropriately.
+ */
+int lmkd_unregister_proc(int sock, struct lmk_procremove *params);
+
+/*
  * Creates memcg directory for given process.
  * On success returns 0.
  * -1 is returned if path creation failed.
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
index 59377dd..bd9b80e 100644
--- a/lmkd/include/lmkd.h
+++ b/lmkd/include/lmkd.h
@@ -87,21 +87,33 @@
     return idx * sizeof(int);
 }
 
+/* Process types for lmk_procprio.ptype */
+enum proc_type {
+    PROC_TYPE_FIRST,
+    PROC_TYPE_APP = PROC_TYPE_FIRST,
+    PROC_TYPE_SERVICE,
+    PROC_TYPE_COUNT,
+};
+
 /* LMK_PROCPRIO packet payload */
 struct lmk_procprio {
     pid_t pid;
     uid_t uid;
     int oomadj;
+    enum proc_type ptype;
 };
 
 /*
  * For LMK_PROCPRIO packet get its payload.
  * Warning: no checks performed, caller should ensure valid parameters.
  */
-static inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet, struct lmk_procprio* params) {
+static inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet, int field_count,
+                                          struct lmk_procprio* params) {
     params->pid = (pid_t)ntohl(packet[1]);
     params->uid = (uid_t)ntohl(packet[2]);
     params->oomadj = ntohl(packet[3]);
+    /* if field is missing assume PROC_TYPE_APP for backward compatibility */
+    params->ptype = field_count > 3 ? (enum proc_type)ntohl(packet[4]) : PROC_TYPE_APP;
 }
 
 /*
@@ -113,7 +125,8 @@
     packet[1] = htonl(params->pid);
     packet[2] = htonl(params->uid);
     packet[3] = htonl(params->oomadj);
-    return 4 * sizeof(int);
+    packet[4] = htonl((int)params->ptype);
+    return 5 * sizeof(int);
 }
 
 /* LMK_PROCREMOVE packet payload */
@@ -135,7 +148,7 @@
  * Warning: no checks performed, caller should ensure valid parameters.
  */
 static inline size_t lmkd_pack_set_procremove(LMKD_CTRL_PACKET packet,
-                                              struct lmk_procprio* params) {
+                                              struct lmk_procremove* params) {
     packet[0] = htonl(LMK_PROCREMOVE);
     packet[1] = htonl(params->pid);
     return 2 * sizeof(int);
diff --git a/lmkd/liblmkd_utils.c b/lmkd/liblmkd_utils.c
index fa3b7a9..280c149 100644
--- a/lmkd/liblmkd_utils.c
+++ b/lmkd/liblmkd_utils.c
@@ -28,7 +28,7 @@
 int lmkd_connect() {
     return socket_local_client("lmkd",
                                ANDROID_SOCKET_NAMESPACE_RESERVED,
-                               SOCK_SEQPACKET);
+                               SOCK_SEQPACKET | SOCK_CLOEXEC);
 }
 
 int lmkd_register_proc(int sock, struct lmk_procprio *params) {
@@ -42,6 +42,17 @@
     return (ret < 0) ? -1 : 0;
 }
 
+int lmkd_unregister_proc(int sock, struct lmk_procremove *params) {
+    LMKD_CTRL_PACKET packet;
+    size_t size;
+    int ret;
+
+    size = lmkd_pack_set_procremove(packet, params);
+    ret = TEMP_FAILURE_RETRY(write(sock, packet, size));
+
+    return (ret < 0) ? -1 : 0;
+}
+
 int create_memcg(uid_t uid, pid_t pid) {
     char buf[256];
     int tasks_file;
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 9de7ff7..4352498 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -45,6 +45,7 @@
 #include <log/log.h>
 #include <log/log_event_list.h>
 #include <log/log_time.h>
+#include <private/android_filesystem_config.h>
 #include <psi/psi.h>
 #include <system/thread_defs.h>
 
@@ -240,11 +241,12 @@
 /* data required to handle socket events */
 struct sock_event_handler_info {
     int sock;
+    pid_t pid;
     struct event_handler_info handler_info;
 };
 
-/* max supported number of data connections */
-#define MAX_DATA_CONN 2
+/* max supported number of data connections (AMS, init, tests) */
+#define MAX_DATA_CONN 3
 
 /* socket event handler data */
 static struct sock_event_handler_info ctrl_sock;
@@ -254,7 +256,7 @@
 static struct event_handler_info vmpressure_hinfo[VMPRESS_LEVEL_COUNT];
 
 /*
- * 1 ctrl listen socket, 2 ctrl data socket, 3 memory pressure levels,
+ * 1 ctrl listen socket, 3 ctrl data socket, 3 memory pressure levels,
  * 1 lmk events + 1 fd to wait for process death
  */
 #define MAX_EPOLL_EVENTS (1 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT + 1 + 1)
@@ -490,6 +492,7 @@
     int pidfd;
     uid_t uid;
     int oomadj;
+    pid_t reg_pid; /* PID of the process that registered this record */
     struct proc *pidhash_next;
 };
 
@@ -845,7 +848,35 @@
     return buf;
 }
 
-static void cmd_procprio(LMKD_CTRL_PACKET packet) {
+static bool claim_record(struct proc *procp, pid_t pid) {
+    if (procp->reg_pid == pid) {
+        /* Record already belongs to the registrant */
+        return true;
+    }
+    if (procp->reg_pid == 0) {
+        /* Old registrant is gone, claim the record */
+        procp->reg_pid = pid;
+        return true;
+    }
+    /* The record is owned by another registrant */
+    return false;
+}
+
+static void remove_claims(pid_t pid) {
+    int i;
+
+    for (i = 0; i < PIDHASH_SZ; i++) {
+        struct proc *procp = pidhash[i];
+        while (procp) {
+            if (procp->reg_pid == pid) {
+                procp->reg_pid = 0;
+            }
+            procp = procp->pidhash_next;
+        }
+    }
+}
+
+static void cmd_procprio(LMKD_CTRL_PACKET packet, int field_count, struct ucred *cred) {
     struct proc *procp;
     char path[LINE_MAX];
     char val[20];
@@ -855,7 +886,7 @@
     struct passwd *pwdrec;
     int tgid;
 
-    lmkd_pack_get_procprio(packet, &params);
+    lmkd_pack_get_procprio(packet, field_count, &params);
 
     if (params.oomadj < OOM_SCORE_ADJ_MIN ||
         params.oomadj > OOM_SCORE_ADJ_MAX) {
@@ -863,6 +894,11 @@
         return;
     }
 
+    if (params.ptype < PROC_TYPE_FIRST || params.ptype >= PROC_TYPE_COUNT) {
+        ALOGE("Invalid PROCPRIO process type argument %d", params.ptype);
+        return;
+    }
+
     /* Check if registered process is a thread group leader */
     tgid = proc_get_tgid(params.pid);
     if (tgid >= 0 && tgid != params.pid) {
@@ -889,7 +925,8 @@
         return;
     }
 
-    if (per_app_memcg) {
+    /* lmkd should not change soft limits for services */
+    if (params.ptype == PROC_TYPE_APP && per_app_memcg) {
         if (params.oomadj >= 900) {
             soft_limit_mult = 0;
         } else if (params.oomadj >= 800) {
@@ -954,24 +991,47 @@
         procp->pid = params.pid;
         procp->pidfd = pidfd;
         procp->uid = params.uid;
+        procp->reg_pid = cred->pid;
         procp->oomadj = params.oomadj;
         proc_insert(procp);
     } else {
+        if (!claim_record(procp, cred->pid)) {
+            char buf[LINE_MAX];
+            /* Only registrant of the record can remove it */
+            ALOGE("%s (%d, %d) attempts to modify a process registered by another client",
+                proc_get_name(cred->pid, buf, sizeof(buf)), cred->uid, cred->pid);
+            return;
+        }
         proc_unslot(procp);
         procp->oomadj = params.oomadj;
         proc_slot(procp);
     }
 }
 
-static void cmd_procremove(LMKD_CTRL_PACKET packet) {
+static void cmd_procremove(LMKD_CTRL_PACKET packet, struct ucred *cred) {
     struct lmk_procremove params;
+    struct proc *procp;
 
     lmkd_pack_get_procremove(packet, &params);
+
     if (use_inkernel_interface) {
         stats_remove_taskname(params.pid, kpoll_info.poll_fd);
         return;
     }
 
+    procp = pid_lookup(params.pid);
+    if (!procp) {
+        return;
+    }
+
+    if (!claim_record(procp, cred->pid)) {
+        char buf[LINE_MAX];
+        /* Only registrant of the record can remove it */
+        ALOGE("%s (%d, %d) attempts to unregister a process registered by another client",
+            proc_get_name(cred->pid, buf, sizeof(buf)), cred->uid, cred->pid);
+        return;
+    }
+
     /*
      * WARNING: After pid_remove() procp is freed and can't be used!
      * Therefore placed at the end of the function.
@@ -979,7 +1039,7 @@
     pid_remove(params.pid);
 }
 
-static void cmd_procpurge() {
+static void cmd_procpurge(struct ucred *cred) {
     int i;
     struct proc *procp;
     struct proc *next;
@@ -989,20 +1049,17 @@
         return;
     }
 
-    for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
-        procadjslot_list[i].next = &procadjslot_list[i];
-        procadjslot_list[i].prev = &procadjslot_list[i];
-    }
-
     for (i = 0; i < PIDHASH_SZ; i++) {
         procp = pidhash[i];
         while (procp) {
             next = procp->pidhash_next;
-            free(procp);
+            /* Purge only records created by the requestor */
+            if (claim_record(procp, cred->pid)) {
+                pid_remove(procp->pid);
+            }
             procp = next;
         }
     }
-    memset(&pidhash[0], 0, sizeof(pidhash));
 }
 
 static void inc_killcnt(int oomadj) {
@@ -1153,19 +1210,50 @@
 
     close(data_sock[dsock_idx].sock);
     data_sock[dsock_idx].sock = -1;
+
+    /* Mark all records of the old registrant as unclaimed */
+    remove_claims(data_sock[dsock_idx].pid);
 }
 
-static int ctrl_data_read(int dsock_idx, char *buf, size_t bufsz) {
-    int ret = 0;
+static ssize_t ctrl_data_read(int dsock_idx, char *buf, size_t bufsz, struct ucred *sender_cred) {
+    struct iovec iov = { buf, bufsz };
+    char control[CMSG_SPACE(sizeof(struct ucred))];
+    struct msghdr hdr = {
+        NULL, 0, &iov, 1, control, sizeof(control), 0,
+    };
+    ssize_t ret;
 
-    ret = TEMP_FAILURE_RETRY(read(data_sock[dsock_idx].sock, buf, bufsz));
-
+    ret = TEMP_FAILURE_RETRY(recvmsg(data_sock[dsock_idx].sock, &hdr, 0));
     if (ret == -1) {
-        ALOGE("control data socket read failed; errno=%d", errno);
-    } else if (ret == 0) {
-        ALOGE("Got EOF on control data socket");
-        ret = -1;
+        ALOGE("control data socket read failed; %s", strerror(errno));
+        return -1;
     }
+    if (ret == 0) {
+        ALOGE("Got EOF on control data socket");
+        return -1;
+    }
+
+    struct ucred* cred = NULL;
+    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+    while (cmsg != NULL) {
+        if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
+            cred = (struct ucred*)CMSG_DATA(cmsg);
+            break;
+        }
+        cmsg = CMSG_NXTHDR(&hdr, cmsg);
+    }
+
+    if (cred == NULL) {
+        ALOGE("Failed to retrieve sender credentials");
+        /* Close the connection */
+        ctrl_data_close(dsock_idx);
+        return -1;
+    }
+
+    memcpy(sender_cred, cred, sizeof(struct ucred));
+
+    /* Store PID of the peer */
+    data_sock[dsock_idx].pid = cred->pid;
 
     return ret;
 }
@@ -1187,13 +1275,14 @@
 
 static void ctrl_command_handler(int dsock_idx) {
     LMKD_CTRL_PACKET packet;
+    struct ucred cred;
     int len;
     enum lmk_cmd cmd;
     int nargs;
     int targets;
     int kill_cnt;
 
-    len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE);
+    len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE, &cred);
     if (len <= 0)
         return;
 
@@ -1215,19 +1304,20 @@
         cmd_target(targets, packet);
         break;
     case LMK_PROCPRIO:
-        if (nargs != 3)
+        /* process type field is optional for backward compatibility */
+        if (nargs < 3 || nargs > 4)
             goto wronglen;
-        cmd_procprio(packet);
+        cmd_procprio(packet, nargs, &cred);
         break;
     case LMK_PROCREMOVE:
         if (nargs != 1)
             goto wronglen;
-        cmd_procremove(packet);
+        cmd_procremove(packet, &cred);
         break;
     case LMK_PROCPURGE:
         if (nargs != 0)
             goto wronglen;
-        cmd_procpurge();
+        cmd_procpurge(&cred);
         break;
     case LMK_GETKILLCNT:
         if (nargs != 2)
diff --git a/lmkd/lmkd.rc b/lmkd/lmkd.rc
index 76b6055..982a188 100644
--- a/lmkd/lmkd.rc
+++ b/lmkd/lmkd.rc
@@ -4,5 +4,5 @@
     group lmkd system readproc
     capabilities DAC_OVERRIDE KILL IPC_LOCK SYS_NICE SYS_RESOURCE
     critical
-    socket lmkd seqpacket 0660 system system
+    socket lmkd seqpacket+passcred 0660 system system
     writepid /dev/cpuset/system-background/tasks
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 70ccb80..2d14bf3 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -969,6 +969,16 @@
         }
     }
 
+    if (mode & ANDROID_LOG_PSTORE) {
+        if (clearLog) {
+            unlink("/sys/fs/pstore/pmsg-ramoops-0");
+            return EXIT_SUCCESS;
+        }
+        if (setLogSize || getLogSize || printStatistics || getPruneList || setPruneList) {
+            LogcatPanic(HELP_TRUE, "-L is incompatible with -g/-G, -S, and -p/-P");
+        }
+    }
+
     std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
             nullptr, &android_logger_list_free};
     if (tail_time != log_time::EPOCH) {
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index ba05a06..834b20b 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -638,6 +638,8 @@
     if (stats.sizes(id) > (2 * log_buffer_size(id))) {  // +100%
         // A misbehaving or slow reader has its connection
         // dropped if we hit too much memory pressure.
+        android::prdebug("Kicking blocked reader, pid %d, from LogBuffer::kickMe()\n",
+                         me->mClient->getPid());
         me->release_Locked();
     } else if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
         // Allow a blocked WRAP timeout reader to
@@ -645,6 +647,9 @@
         me->triggerReader_Locked();
     } else {
         // tell slow reader to skip entries to catch up
+        android::prdebug(
+                "Skipping %lu entries from slow reader, pid %d, from LogBuffer::kickMe()\n",
+                pruneRows, me->mClient->getPid());
         me->triggerSkip_Locked(id, pruneRows);
     }
 }
@@ -1051,6 +1056,9 @@
                     LogTimeEntry* entry = times->get();
                     // Killer punch
                     if (entry->isWatching(id)) {
+                        android::prdebug(
+                                "Kicking blocked reader, pid %d, from LogBuffer::clear()\n",
+                                entry->mClient->getPid());
                         entry->release_Locked();
                     }
                     times++;
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index 33da1f1..f4a846f 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -375,9 +375,11 @@
       {"audio_hal.period_size", "u:object_r:default_prop:s0"},
       {"bluetooth.enable_timeout_ms", "u:object_r:bluetooth_prop:s0"},
       {"dalvik.vm.appimageformat", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.boot-dex2oat-cpu-set", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.boot-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.dex2oat-cpu-set", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.dex2oat-threads", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.dexopt.secondary", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.heapgrowthlimit", "u:object_r:dalvik_prop:s0"},
@@ -388,6 +390,7 @@
       {"dalvik.vm.heaptargetutilization", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.image-dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.image-dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.image-dex2oat-cpu-set", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.image-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.isa.arm.features", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.isa.arm.variant", "u:object_r:dalvik_prop:s0"},
diff --git a/rootdir/Android.bp b/rootdir/Android.bp
new file mode 100644
index 0000000..96b5e0d
--- /dev/null
+++ b/rootdir/Android.bp
@@ -0,0 +1,26 @@
+// Copyright 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.
+
+prebuilt_etc {
+    name: "init.rc",
+    src: "init.rc",
+    sub_dir: "init/hw",
+    required: ["fsverity_init"],
+}
+
+prebuilt_etc {
+    name: "ueventd.rc",
+    src: "ueventd.rc",
+    recovery_available: true,
+}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index eac3f06..994d9ae 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -1,22 +1,6 @@
 LOCAL_PATH:= $(call my-dir)
 
 #######################################
-# init.rc
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := init.rc
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
-LOCAL_REQUIRED_MODULES := fsverity_init
-
-# The init symlink must be a post install command of a file that is to TARGET_ROOT_OUT.
-# Since init.rc is required for init and satisfies that requirement, we hijack it to create the symlink.
-LOCAL_POST_INSTALL_CMD := ln -sf /system/bin/init $(TARGET_ROOT_OUT)/init
-
-include $(BUILD_PREBUILT)
-
-#######################################
 # init-debug.rc
 include $(CLEAR_VARS)
 
@@ -148,6 +132,10 @@
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/postinstall
 endif
 
+# The init symlink must be a post install command of a file that is to TARGET_ROOT_OUT.
+# Since init.environ.rc is required for init and satisfies that requirement, we hijack it to create the symlink.
+LOCAL_POST_INSTALL_CMD += ; ln -sf /system/bin/init $(TARGET_ROOT_OUT)/init
+
 include $(BUILD_SYSTEM)/base_rules.mk
 
 $(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in
@@ -294,27 +282,6 @@
 include $(BUILD_PREBUILT)
 
 #######################################
-# {llndk,vndkcore,vndksp,vndkprivate,vndkcorevariant}.libraries.txt
-vndk_libraries_files := \
-  llndk.libraries.txt:$(SOONG_LLNDK_LIBRARIES_FILE)\
-  vndkcore.libraries.txt:$(SOONG_VNDKCORE_LIBRARIES_FILE)\
-  vndksp.libraries.txt:$(SOONG_VNDKSP_LIBRARIES_FILE)\
-  vndkprivate.libraries.txt:$(SOONG_VNDKPRIVATE_LIBRARIES_FILE)\
-  vndkcorevariant.libraries.txt:$(SOONG_VNDKCOREVARIANT_LIBRARIES_FILE)
-
-$(foreach pair,$(vndk_libraries_files),\
-  $(eval _filename := $(call word-colon,1,$(pair)))\
-  $(eval _prebuilt := $(call word-colon,2,$(pair)))\
-  $(eval include $(CLEAR_VARS))\
-  $(eval LOCAL_MODULE := $(_filename))\
-  $(eval LOCAL_MODULE_CLASS := ETC)\
-  $(eval LOCAL_PREBUILT_MODULE_FILE := $(_prebuilt))\
-  $(eval LOCAL_MODULE_PATH := $(TARGET_OUT_ETC))\
-  $(eval LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE)))\
-  $(eval include $(BUILD_PREBUILT)))
-vndk_libraries_files :=
-
-#######################################
 # sanitizer.libraries.txt
 include $(CLEAR_VARS)
 LOCAL_MODULE := sanitizer.libraries.txt
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 9b77ce2..782fb92 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -5,11 +5,11 @@
 #
 
 import /init.environ.rc
-import /init.usb.rc
+import /system/etc/init/hw/init.usb.rc
 import /init.${ro.hardware}.rc
 import /vendor/etc/init/hw/init.${ro.hardware}.rc
-import /init.usb.configfs.rc
-import /init.${ro.zygote}.rc
+import /system/etc/init/hw/init.usb.configfs.rc
+import /system/etc/init/hw/init.${ro.zygote}.rc
 
 # Cgroups are mounted right before early-init using list from /etc/cgroups.json
 on early-init
@@ -283,6 +283,16 @@
     chmod 0664 /dev/cpuset/restricted/tasks
     chmod 0664 /dev/cpuset/tasks
 
+    # freezer cgroup entries
+    mkdir /dev/freezer/frozen
+    write /dev/freezer/frozen/freezer.state FROZEN
+    chown system system /dev/freezer/cgroup.procs
+    chown system system /dev/freezer/frozen
+    chown system system /dev/freezer/frozen/freezer.state
+    chown system system /dev/freezer/frozen/cgroup.procs
+
+    chmod 0444 /dev/freezer/frozen/freezer.state
+
     # make the PSI monitor accessible to others
     chown system system /proc/pressure/memory
     chmod 0664 /proc/pressure/memory
@@ -347,6 +357,8 @@
 
     # Start logd before any other services run to ensure we capture all of their logs.
     start logd
+    # Start lmkd before any other services run so that it can register them
+    start lmkd
 
     # Start essential services.
     start servicemanager
@@ -486,26 +498,26 @@
 
     # Start bootcharting as soon as possible after the data partition is
     # mounted to collect more data.
-    mkdir /data/bootchart 0755 shell shell
+    mkdir /data/bootchart 0755 shell shell encryption=Require
     bootchart start
 
     # Make sure that apexd is started in the default namespace
     enter_default_mount_ns
 
     # /data/apex is now available. Start apexd to scan and activate APEXes.
-    mkdir /data/apex 0750 root system
+    mkdir /data/apex 0750 root system encryption=None
     mkdir /data/apex/active 0750 root system
     mkdir /data/apex/backup 0700 root system
     mkdir /data/apex/hashtree 0700 root system
     mkdir /data/apex/sessions 0700 root system
-    mkdir /data/app-staging 0750 system system
+    mkdir /data/app-staging 0750 system system encryption=None
     start apexd
 
     # Avoid predictable entropy pool. Carry over entropy from previous boot.
     copy /data/system/entropy.dat /dev/urandom
 
     # create basic filesystem structure
-    mkdir /data/misc 01771 system misc
+    mkdir /data/misc 01771 system misc encryption=Require
     mkdir /data/misc/recovery 0770 system log
     copy /data/misc/recovery/ro.build.fingerprint /data/misc/recovery/ro.build.fingerprint.1
     chmod 0440 /data/misc/recovery/ro.build.fingerprint.1
@@ -534,6 +546,7 @@
     mkdir /data/misc/sms 0770 system radio
     mkdir /data/misc/carrierid 0770 system radio
     mkdir /data/misc/apns 0770 system radio
+    mkdir /data/misc/emergencynumberdb 0770 system radio
     mkdir /data/misc/zoneinfo 0775 system system
     mkdir /data/misc/network_watchlist 0774 system system
     mkdir /data/misc/textclassifier 0771 system system
@@ -548,7 +561,7 @@
     mkdir /data/misc/user 0771 root root
     # give system access to wpa_supplicant.conf for backup and restore
     chmod 0660 /data/misc/wifi/wpa_supplicant.conf
-    mkdir /data/local 0751 root root
+    mkdir /data/local 0751 root root encryption=Require
     mkdir /data/misc/media 0700 media media
     mkdir /data/misc/audioserver 0700 audioserver audioserver
     mkdir /data/misc/cameraserver 0700 cameraserver cameraserver
@@ -567,89 +580,91 @@
     mkdir /data/misc/gcov 0770 root root
     mkdir /data/misc/installd 0700 root root
 
-    mkdir /data/preloads 0775 system system
+    mkdir /data/preloads 0775 system system encryption=None
 
-    mkdir /data/vendor 0771 root root
-    mkdir /data/vendor_ce 0771 root root
-    mkdir /data/vendor_de 0771 root root
+    mkdir /data/vendor 0771 root root encryption=Require
+    mkdir /data/vendor_ce 0771 root root encryption=None
+    mkdir /data/vendor_de 0771 root root encryption=None
     mkdir /data/vendor/hardware 0771 root root
 
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp
     mkdir /data/local/tmp 0771 shell shell
     mkdir /data/local/traces 0777 shell shell
-    mkdir /data/data 0771 system system
-    mkdir /data/app-private 0771 system system
-    mkdir /data/app-ephemeral 0771 system system
-    mkdir /data/app-asec 0700 root root
-    mkdir /data/app-lib 0771 system system
-    mkdir /data/app 0771 system system
-    mkdir /data/property 0700 root root
-    mkdir /data/tombstones 0771 system system
+    mkdir /data/data 0771 system system encryption=None
+    mkdir /data/app-private 0771 system system encryption=Require
+    mkdir /data/app-ephemeral 0771 system system encryption=Require
+    mkdir /data/app-asec 0700 root root encryption=Require
+    mkdir /data/app-lib 0771 system system encryption=Require
+    mkdir /data/app 0771 system system encryption=Require
+    mkdir /data/property 0700 root root encryption=Require
+    mkdir /data/tombstones 0771 system system encryption=Require
     mkdir /data/vendor/tombstones 0771 root root
     mkdir /data/vendor/tombstones/wifi 0771 wifi wifi
 
     # create dalvik-cache, so as to enforce our permissions
-    mkdir /data/dalvik-cache 0771 root root
+    mkdir /data/dalvik-cache 0771 root root encryption=Require
     # create the A/B OTA directory, so as to enforce our permissions
-    mkdir /data/ota 0771 root root
+    mkdir /data/ota 0771 root root encryption=Require
 
     # create the OTA package directory. It will be accessed by GmsCore (cache
     # group), update_engine and update_verifier.
-    mkdir /data/ota_package 0770 system cache
+    mkdir /data/ota_package 0770 system cache encryption=Require
 
     # create resource-cache and double-check the perms
-    mkdir /data/resource-cache 0771 system system
+    mkdir /data/resource-cache 0771 system system encryption=Require
     chown system system /data/resource-cache
     chmod 0771 /data/resource-cache
 
     # create the lost+found directories, so as to enforce our permissions
-    mkdir /data/lost+found 0770 root root
+    mkdir /data/lost+found 0770 root root encryption=None
 
     # create directory for DRM plug-ins - give drm the read/write access to
     # the following directory.
-    mkdir /data/drm 0770 drm drm
+    mkdir /data/drm 0770 drm drm encryption=Require
 
     # create directory for MediaDrm plug-ins - give drm the read/write access to
     # the following directory.
-    mkdir /data/mediadrm 0770 mediadrm mediadrm
+    mkdir /data/mediadrm 0770 mediadrm mediadrm encryption=Require
 
-    mkdir /data/anr 0775 system system
+    mkdir /data/anr 0775 system system encryption=Require
 
     # NFC: create data/nfc for nv storage
-    mkdir /data/nfc 0770 nfc nfc
+    mkdir /data/nfc 0770 nfc nfc encryption=Require
     mkdir /data/nfc/param 0770 nfc nfc
 
     # Create all remaining /data root dirs so that they are made through init
     # and get proper encryption policy installed
-    mkdir /data/backup 0700 system system
-    mkdir /data/ss 0700 system system
+    mkdir /data/backup 0700 system system encryption=Require
+    mkdir /data/ss 0700 system system encryption=Require
 
-    mkdir /data/system 0775 system system
+    mkdir /data/system 0775 system system encryption=Require
     mkdir /data/system/dropbox 0700 system system
     mkdir /data/system/heapdump 0700 system system
     mkdir /data/system/users 0775 system system
 
-    mkdir /data/system_de 0770 system system
-    mkdir /data/system_ce 0770 system system
+    mkdir /data/system_de 0770 system system encryption=None
+    mkdir /data/system_ce 0770 system system encryption=None
 
-    mkdir /data/misc_de 01771 system misc
-    mkdir /data/misc_ce 01771 system misc
+    mkdir /data/misc_de 01771 system misc encryption=None
+    mkdir /data/misc_ce 01771 system misc encryption=None
 
-    mkdir /data/user 0711 system system
-    mkdir /data/user_de 0711 system system
+    mkdir /data/user 0711 system system encryption=None
+    mkdir /data/user_de 0711 system system encryption=None
     symlink /data/data /data/user/0
 
-    mkdir /data/media 0770 media_rw media_rw
-    mkdir /data/media/obb 0770 media_rw media_rw
+    # Special-case /data/media/obb per b/64566063
+    mkdir /data/media 0770 media_rw media_rw encryption=None
+    mkdir /data/media/obb 0770 media_rw media_rw encryption=Attempt
 
-    mkdir /data/cache 0770 system cache
+    mkdir /data/cache 0770 system cache encryption=Require
     mkdir /data/cache/recovery 0770 system cache
     mkdir /data/cache/backup_stage 0700 system system
     mkdir /data/cache/backup 0700 system system
 
-    mkdir /data/rollback 0700 system system
-    mkdir /data/rollback-observer 0700 system system
+    # Delete these if need be, per b/139193659
+    mkdir /data/rollback 0700 system system encryption=DeleteIfNecessary
+    mkdir /data/rollback-observer 0700 system system encryption=DeleteIfNecessary
 
     # Wait for apexd to finish activating APEXes before starting more processes.
     wait_for_prop apexd.status ready
@@ -686,8 +701,6 @@
   # Mount default storage into root namespace
   mount none /mnt/user/0 /storage bind rec
   mount none none /storage slave rec
-  # Bootstrap the emulated volume for the pass_through directory for user 0
-  mount none /data/media /mnt/pass_through/0/emulated bind rec
 on zygote-start && property:persist.sys.fuse=false
   # Mount default storage into root namespace
   mount none /mnt/runtime/default /storage bind rec
@@ -867,7 +880,7 @@
     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
+    mkdir /data/per_boot 0700 system system encryption=Require key=per_boot_ref
 
 # system server cannot write to /proc/sys files,
 # and chown/chmod does not work for /proc/sys/ entries.
@@ -919,12 +932,11 @@
 on init && property:ro.debuggable=1
     start console
 
-on userspace-reboot
+on userspace-reboot-requested
   # TODO(b/135984674): reset all necessary properties here.
-  setprop sys.init.userspace_reboot_in_progress 1
   setprop sys.boot_completed 0
   setprop sys.init.updatable_crashing 0
-  setprop apexd.status 0
+  setprop apexd.status ""
 
 on userspace-reboot-fs-remount
   # Make sure that vold is running.
@@ -941,3 +953,6 @@
   trigger zygote-start
   trigger early-boot
   trigger boot
+
+on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
+  finish_userspace_reboot
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index b6cba90..a1888fc 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -9,13 +9,14 @@
     chown system system /sys/class/android_usb/android0/f_rndis/ethaddr
     chmod 0660 /sys/class/android_usb/android0/f_rndis/ethaddr
     mkdir /data/misc/adb 02750 system shell
-    mkdir /data/adb 0700 root root
+    mkdir /data/adb 0700 root root encryption=Require
 
 # 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 seqpacket 660 system system
     disabled
+    updatable
     seclabel u:r:adbd:s0
 
 on boot