Merge "General cleanup; add support for key-value pairs"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 73379cd..c84bd24 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -89,3 +89,4 @@
 $(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/adb/Android.bp b/adb/Android.bp
index d14fe56..d605907 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -356,11 +356,12 @@
     generated_headers: ["platform_tools_version"],
 
     static_libs: [
-        "libadbconnection_server",
         "libdiagnose_usb",
     ],
 
     shared_libs: [
+        "libadbconnection_server",
+        "libadbd_auth",
         "libasyncio",
         "libbase",
         "libcrypto",
@@ -408,12 +409,13 @@
     ],
 
     static_libs: [
-        "libadbconnection_server",
         "libadbd_core",
         "libdiagnose_usb",
     ],
 
     shared_libs: [
+        "libadbconnection_server",
+        "libadbd_auth",
         "libasyncio",
         "libbase",
         "libcrypto",
@@ -460,6 +462,8 @@
     ],
 
     shared_libs: [
+        "libadbconnection_server",
+        "libadbd_auth",
         "libadbd_services",
         "libasyncio",
         "libbase",
@@ -492,23 +496,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 +537,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 +583,7 @@
 
     static_libs: [
         "libadbd",
+        "libadbd_auth",
         "libbase",
         "libcutils",
         "libcrypto_utils",
@@ -759,3 +728,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/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/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/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/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 431fea1..c820395 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -154,6 +154,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();
@@ -181,6 +182,15 @@
     //   GetUpdateState will return None, and a new update can begin.
     UpdateState ProcessUpdateState();
 
+  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.
     //
     // |progress| depends on the returned status:
@@ -238,12 +248,13 @@
     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, MergeCannotRemoveCow);
     FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
     friend class SnapshotTest;
     friend class SnapshotUpdateTest;
+    friend class FlashAfterUpdateTest;
     friend struct AutoDeleteCowImage;
     friend struct AutoDeleteSnapshot;
     friend struct PartitionCowCreator;
@@ -341,6 +352,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 +373,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().
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 63d97d0..2c516a2 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -568,6 +568,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)) {
@@ -855,8 +876,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 +905,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 +1135,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 +1153,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) {
@@ -2089,5 +2170,27 @@
     return AutoUnmountDevice::New(device_->GetMetadataDir());
 }
 
+UpdateState SnapshotManager::InitiateMergeAndWait() {
+    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;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 5728582..3c3d9a6 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;
@@ -445,61 +447,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());
 
@@ -680,9 +627,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();
@@ -731,8 +678,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();
     }
@@ -847,8 +798,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"}) {
@@ -1052,8 +1002,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());
@@ -1162,6 +1111,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 {
@@ -1220,6 +1230,121 @@
     EXPECT_FALSE(IsMetadataMounted());
 }
 
+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(, 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/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/liblog/liblog.map.txt b/liblog/liblog.map.txt
index ce4c53c..aea3350 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -51,7 +51,7 @@
 LIBLOG_O {
   global:
     __android_log_is_loggable_len;
-    __android_log_is_debuggable; # vndk
+    __android_log_is_debuggable; # apex vndk
 };
 
 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; # vndk
+    android_log_parser_reset; # vndk
 };
 
 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/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/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
index 7332b41..e4839b4 100644
--- a/libziparchive/unzip.cpp
+++ b/libziparchive/unzip.cpp
@@ -19,6 +19,7 @@
 #include <fnmatch.h>
 #include <getopt.h>
 #include <inttypes.h>
+#include <libgen.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index b6cba90..688d9ad 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -16,6 +16,7 @@
     class core
     socket adbd seqpacket 660 system system
     disabled
+    updatable
     seclabel u:r:adbd:s0
 
 on boot