Merge "Don't include map name if the consumer doesn't want names."
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
index 49e0363..0fcf89b 100644
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -98,13 +98,22 @@
     return android::base::GetProperty("ro.boot.slot_suffix", "");
 }
 
+static bool is_avb_device_locked() {
+    return android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked";
+}
+
 /* Use AVB to turn verity on/off */
 static bool set_avb_verity_enabled_state(int fd, AvbOps* ops, bool enable_verity) {
     std::string ab_suffix = get_ab_suffix();
-
     bool verity_enabled;
+
+    if (is_avb_device_locked()) {
+        WriteFdFmt(fd, "Device is locked. Please unlock the device first\n");
+        return false;
+    }
+
     if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
-        WriteFdFmt(fd, "Error getting verity state\n");
+        WriteFdFmt(fd, "Error getting verity state. Try adb root first?\n");
         return false;
     }
 
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 20fb6a3..c58c67a 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -58,10 +58,6 @@
 #define cpu_to_le16(x) htole16(x)
 #define cpu_to_le32(x) htole32(x)
 
-#define FUNCTIONFS_ENDPOINT_ALLOC       _IOR('g', 231, __u32)
-
-static constexpr size_t ENDPOINT_ALLOC_RETRIES = 10;
-
 static int dummy_fd = -1;
 
 struct func_desc {
@@ -256,7 +252,6 @@
     ssize_t ret;
     struct desc_v1 v1_descriptor;
     struct desc_v2 v2_descriptor;
-    size_t retries = 0;
 
     v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
     v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
@@ -326,30 +321,6 @@
 
     h->read_aiob.fd = h->bulk_out;
     h->write_aiob.fd = h->bulk_in;
-
-    h->max_rw = MAX_PAYLOAD;
-    while (h->max_rw >= USB_FFS_BULK_SIZE && retries < ENDPOINT_ALLOC_RETRIES) {
-        int ret_in = ioctl(h->bulk_in, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(h->max_rw));
-        int errno_in = errno;
-        int ret_out = ioctl(h->bulk_out, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(h->max_rw));
-        int errno_out = errno;
-
-        if (ret_in || ret_out) {
-            if (errno_in == ENODEV || errno_out == ENODEV) {
-                std::this_thread::sleep_for(100ms);
-                retries += 1;
-                continue;
-            }
-            h->max_rw /= 2;
-        } else {
-            return true;
-        }
-    }
-
-    D("[ adb: cannot call endpoint alloc: errno=%d ]", errno);
-    // Kernel pre-allocation could have failed for recoverable reasons.
-    // Continue running with a safe max rw size.
-    h->max_rw = USB_FFS_BULK_SIZE;
     return true;
 
 err:
@@ -403,7 +374,7 @@
 
     const char* buf = static_cast<const char*>(data);
     while (len > 0) {
-        int write_len = std::min(h->max_rw, len);
+        int write_len = std::min(USB_FFS_BULK_SIZE, len);
         int n = adb_write(h->bulk_in, buf, write_len);
         if (n < 0) {
             D("ERROR: fd = %d, n = %d: %s", h->bulk_in, n, strerror(errno));
@@ -422,7 +393,7 @@
 
     char* buf = static_cast<char*>(data);
     while (len > 0) {
-        int read_len = std::min(h->max_rw, len);
+        int read_len = std::min(USB_FFS_BULK_SIZE, len);
         int n = adb_read(h->bulk_out, buf, read_len);
         if (n < 0) {
             D("ERROR: fd = %d, n = %d: %s", h->bulk_out, n, strerror(errno));
diff --git a/adb/daemon/usb.h b/adb/daemon/usb.h
index db1a6d6..15a7f65 100644
--- a/adb/daemon/usb.h
+++ b/adb/daemon/usb.h
@@ -54,7 +54,5 @@
     // read and write threads.
     struct aio_block read_aiob;
     struct aio_block write_aiob;
-
-    int max_rw;
 };
 
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index 04ad6f3..c818fca 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -213,6 +213,46 @@
     TerminateThread(thread);
 }
 
+#if 0
+// Ensure that if we fail to write output to an fd, we will still flush data coming from it.
+TEST_F(LocalSocketTest, flush_after_shutdown) {
+    int head_fd[2];
+    int tail_fd[2];
+    ASSERT_EQ(0, adb_socketpair(head_fd));
+    ASSERT_EQ(0, adb_socketpair(tail_fd));
+
+    asocket* head = create_local_socket(head_fd[1]);
+    asocket* tail = create_local_socket(tail_fd[1]);
+
+    head->peer = tail;
+    head->ready(head);
+
+    tail->peer = head;
+    tail->ready(tail);
+
+    PrepareThread();
+    std::thread thread(fdevent_loop);
+
+    ASSERT_TRUE(WriteFdExactly(head_fd[0], "foo", 3));
+    ASSERT_EQ(0, adb_shutdown(head_fd[0], SHUT_RD));
+    const char* str = "write succeeds, but local_socket will fail to write";
+    ASSERT_TRUE(WriteFdExactly(tail_fd[0], str, strlen(str)));
+    ASSERT_TRUE(WriteFdExactly(head_fd[0], "bar", 3));
+    char buf[6];
+    ASSERT_TRUE(ReadFdExactly(tail_fd[0], buf, 6));
+
+    ASSERT_EQ(0, memcmp(buf, "foobar", 6));
+
+    adb_close(head_fd[0]);
+    adb_close(tail_fd[0]);
+
+    // Wait until the local sockets are closed.
+    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
+    TerminateThread(thread);
+}
+#endif
+
 #if defined(__linux__)
 
 static void ClientThreadFunc() {
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 79702a6..86e8789 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -287,6 +287,7 @@
 bootstat: Service started: /system/bin/bootstat --record_boot_reason
 bootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset
 bootstat: Service started: /system/bin/bootstat -l
+bootstat: Service started: /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
 bootstat: Battery level at shutdown 100%
 bootstat: Battery level at startup 100%
 init    : Parsing file /system/etc/init/bootstat.rc...
@@ -407,29 +408,31 @@
        tr ' \f\t\r\n' '_____'`
   case ${var} in
     watchdog | watchdog,?* ) ;;
-    kernel_panic | kernel_panic,?*) ;;
-    recovery | recovery,?*) ;;
-    bootloader | bootloader,?*) ;;
-    cold | cold,?*) ;;
-    hard | hard,?*) ;;
-    warm | warm,?*) ;;
-    shutdown | shutdown,?*) ;;
+    kernel_panic | kernel_panic,?* ) ;;
+    recovery | recovery,?* ) ;;
+    bootloader | bootloader,?* ) ;;
+    cold | cold,?* ) ;;
+    hard | hard,?* ) ;;
+    warm | warm,?* ) ;;
+    shutdown | shutdown,?* ) ;;
     reboot,reboot | reboot,reboot,* )     var=${var#reboot,} ; var=${var%,} ;;
     reboot,cold | reboot,cold,* )         var=${var#reboot,} ; var=${var%,} ;;
     reboot,hard | reboot,hard,* )         var=${var#reboot,} ; var=${var%,} ;;
     reboot,warm | reboot,warm,* )         var=${var#reboot,} ; var=${var%,} ;;
     reboot,recovery | reboot,recovery,* ) var=${var#reboot,} ; var=${var%,} ;;
     reboot,bootloader | reboot,bootloader,* ) var=${var#reboot,} ; var=${var%,} ;;
-    reboot | reboot,?*) ;;
+    reboot | reboot,?* ) ;;
     # Aliases and Heuristics
-    *wdog* | *watchdog* )     var="watchdog" ;;
-    *powerkey* )              var="cold,powerkey" ;;
-    *panic* | *kernel_panic*) var="kernel_panic" ;;
-    *thermal*)                var="shutdown,thermal" ;;
-    *s3_wakeup*)              var="warm,s3_wakeup" ;;
-    *hw_reset*)               var="hard,hw_reset" ;;
-    *bootloader*)             var="bootloader" ;;
-    *)                        var="reboot" ;;
+    *wdog* | *watchdog* )                   var="watchdog" ;;
+    *powerkey* | *power_key* | *PowerKey* ) var="cold,powerkey" ;;
+    *panic* | *kernel_panic* )              var="kernel_panic" ;;
+    *thermal* )                             var="shutdown,thermal" ;;
+    *s3_wakeup* )                           var="warm,s3_wakeup" ;;
+    *hw_reset* )                            var="hard,hw_reset" ;;
+    *usb* )                                 var="cold,charger" ;;
+    *rtc* )                                 var="cold,rtc" ;;
+    *bootloader* )                          var="bootloader" ;;
+    * )                                     var="reboot" ;;
   esac
   echo ${var}
 }
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index a1fe6ed..cfea6ee 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -29,7 +29,9 @@
 #include <ctime>
 #include <map>
 #include <memory>
+#include <regex>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include <android-base/chrono_utils.h>
@@ -138,7 +140,7 @@
 // values.
 const std::map<std::string, int32_t> kBootReasonMap = {
     {"empty", kEmptyBootReason},
-    {"unknown", kUnknownBootReason},
+    {"__BOOTSTAT_UNKNOWN__", kUnknownBootReason},
     {"normal", 2},
     {"recovery", 3},
     {"reboot", 4},
@@ -191,12 +193,14 @@
     {"s3_wakeup", 51},
     {"kernel_panic,sysrq", 52},
     {"kernel_panic,NULL", 53},
+    {"kernel_panic,null", 53},
     {"kernel_panic,BUG", 54},
+    {"kernel_panic,bug", 54},
     {"bootloader", 55},
     {"cold", 56},
     {"hard", 57},
     {"warm", 58},
-    {"recovery", 59},
+    // {"recovery", 59},  // Duplicate of enum 3 above. Immediate reuse possible.
     {"thermal-shutdown", 60},
     {"shutdown,thermal", 61},
     {"shutdown,battery", 62},
@@ -227,7 +231,7 @@
     {"shutdown,thermal,battery", 87},
     {"reboot,its_just_so_hard", 88},  // produced by boot_reason_test
     {"reboot,Its Just So Hard", 89},  // produced by boot_reason_test
-    {"usb", 90},
+    // {"usb", 90},  // Duplicate of enum 80 above. Immediate reuse possible.
     {"charge", 91},
     {"oem_tz_crash", 92},
     {"uvlo", 93},
@@ -285,6 +289,8 @@
     {"oem_sdi_err_fatal", 145},
     {"pmic_watchdog", 146},
     {"software_master", 147},
+    {"cold,charger", 148},
+    {"cold,rtc", 149},
 };
 
 // Converts a string value representing the reason the system booted to an
@@ -465,7 +471,7 @@
 
 // If bit error match to needle, correct it.
 // Return true if any corrections were discovered and applied.
-bool correctForBer(std::string& reason, const std::string& needle) {
+bool correctForBitError(std::string& reason, const std::string& needle) {
   bool corrected = false;
   if (reason.length() < needle.length()) return corrected;
   const pstoreConsole console(reason);
@@ -483,20 +489,35 @@
   return corrected;
 }
 
+// If bit error match to needle, correct it.
+// Return true if any corrections were discovered and applied.
+// Try again if we can replace underline with spaces.
+bool correctForBitErrorOrUnderline(std::string& reason, const std::string& needle) {
+  bool corrected = correctForBitError(reason, needle);
+  std::string _needle(needle);
+  std::transform(_needle.begin(), _needle.end(), _needle.begin(),
+                 [](char c) { return (c == '_') ? ' ' : c; });
+  if (needle != _needle) {
+    corrected |= correctForBitError(reason, _needle);
+  }
+  return corrected;
+}
+
 bool addKernelPanicSubReason(const pstoreConsole& console, std::string& ret) {
   // Check for kernel panic types to refine information
-  if (console.rfind("SysRq : Trigger a crash") != std::string::npos) {
+  if ((console.rfind("SysRq : Trigger a crash") != std::string::npos) ||
+      (console.rfind("PC is at sysrq_handle_crash+") != std::string::npos)) {
     // Can not happen, except on userdebug, during testing/debugging.
     ret = "kernel_panic,sysrq";
     return true;
   }
   if (console.rfind("Unable to handle kernel NULL pointer dereference at virtual address") !=
       std::string::npos) {
-    ret = "kernel_panic,NULL";
+    ret = "kernel_panic,null";
     return true;
   }
   if (console.rfind("Kernel BUG at ") != std::string::npos) {
-    ret = "kernel_panic,BUG";
+    ret = "kernel_panic,bug";
     return true;
   }
   return false;
@@ -506,22 +527,14 @@
   return addKernelPanicSubReason(pstoreConsole(content), ret);
 }
 
-// std::transform Helper callback functions:
 // Converts a string value representing the reason the system booted to a
 // string complying with Android system standard reason.
-char tounderline(char c) {
-  return ::isblank(c) ? '_' : c;
-}
-
-char toprintable(char c) {
-  return ::isprint(c) ? c : '?';
-}
-
-// Cleanup boot_reason regarding acceptable character set
 void transformReason(std::string& reason) {
   std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower);
-  std::transform(reason.begin(), reason.end(), reason.begin(), tounderline);
-  std::transform(reason.begin(), reason.end(), reason.begin(), toprintable);
+  std::transform(reason.begin(), reason.end(), reason.begin(),
+                 [](char c) { return ::isblank(c) ? '_' : c; });
+  std::transform(reason.begin(), reason.end(), reason.begin(),
+                 [](char c) { return ::isprint(c) ? c : '?'; });
 }
 
 const char system_reboot_reason_property[] = "sys.boot.reason";
@@ -566,26 +579,36 @@
     // A series of checks to take some officially unsupported reasons
     // reported by the bootloader and find some logical and canonical
     // sense.  In an ideal world, we would require those bootloaders
-    // to behave and follow our standards.
+    // to behave and follow our CTS standards.
+    //
+    // first member is the output
+    // second member is an unanchored regex for an alias
+    //
+    // If output has a prefix of <bang> '!', we do not use it as a
+    // match needle (and drop the <bang> prefix when landing in output),
+    // otherwise look for it as well. This helps keep the scale of the
+    // following table smaller.
     static const std::vector<std::pair<const std::string, const std::string>> aliasReasons = {
         {"watchdog", "wdog"},
-        {"cold,powerkey", "powerkey"},
+        {"cold,powerkey", "powerkey|power_key|PowerKey"},
         {"kernel_panic", "panic"},
         {"shutdown,thermal", "thermal"},
         {"warm,s3_wakeup", "s3_wakeup"},
         {"hard,hw_reset", "hw_reset"},
+        {"cold,charger", "usb"},
+        {"cold,rtc", "rtc"},
         {"reboot,2sec", "2sec_reboot"},
         {"bootloader", ""},
     };
 
-    // Either the primary or alias is found _somewhere_ in the reason string.
     for (auto& s : aliasReasons) {
-      if (reason.find(s.first) != std::string::npos) {
+      size_t firstHasNot = s.first[0] == '!';
+      if (!firstHasNot && (reason.find(s.first) != std::string::npos)) {
         ret = s.first;
         break;
       }
-      if (s.second.size() && (reason.find(s.second) != std::string::npos)) {
-        ret = s.first;
+      if (s.second.size() && std::regex_search(reason, std::regex(s.second))) {
+        ret = s.first.substr(firstHasNot);
         break;
       }
     }
@@ -628,14 +651,14 @@
         std::string subReason(content.substr(pos, max_reason_length));
         // Correct against any known strings that Bit Error Match
         for (const auto& s : knownReasons) {
-          correctForBer(subReason, s);
+          correctForBitErrorOrUnderline(subReason, s);
         }
         for (const auto& m : kBootReasonMap) {
           if (m.first.length() <= strlen("cold")) continue;  // too short?
-          if (correctForBer(subReason, m.first + "'")) continue;
+          if (correctForBitErrorOrUnderline(subReason, m.first + "'")) continue;
           if (m.first.length() <= strlen("reboot,cold")) continue;  // short?
           if (!android::base::StartsWith(m.first, "reboot,")) continue;
-          correctForBer(subReason, m.first.substr(strlen("reboot,")) + "'");
+          correctForBitErrorOrUnderline(subReason, m.first.substr(strlen("reboot,")) + "'");
         }
         for (pos = 0; pos < subReason.length(); ++pos) {
           char c = subReason[pos];
@@ -683,7 +706,7 @@
       if (pos != std::string::npos) {
         digits = content.substr(pos + strlen(battery), strlen("100 "));
         // correct common errors
-        correctForBer(digits, "100 ");
+        correctForBitError(digits, "100 ");
         if (digits[0] == '!') digits[0] = '1';
         if (digits[1] == '!') digits[1] = '1';
       }
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 6b14289..6c8fecf 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -98,9 +98,6 @@
 ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
 LOCAL_CHARGER_NO_UI := true
 endif
-ifdef BRILLO
-LOCAL_CHARGER_NO_UI := true
-endif
 
 LOCAL_SRC_FILES := \
     healthd_common.cpp \
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 03ed55a..bb87f12 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -429,10 +429,20 @@
         if (kill_after_apps.count(s->name())) s->Stop();
     }
     // 4. sync, try umount, and optionally run fsck for user shutdown
-    sync();
+    {
+        Timer sync_timer;
+        LOG(INFO) << "sync() before umount...";
+        sync();
+        LOG(INFO) << "sync() before umount took" << sync_timer;
+    }
     UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
     // Follow what linux shutdown is doing: one more sync with little bit delay
-    sync();
+    {
+        Timer sync_timer;
+        LOG(INFO) << "sync() after umount...";
+        sync();
+        LOG(INFO) << "sync() after umount took" << sync_timer;
+    }
     if (cmd != ANDROID_RB_THERMOFF) std::this_thread::sleep_for(100ms);
     LogShutdownTime(stat, &t);
 
diff --git a/init/stable_properties.h b/init/stable_properties.h
index be35457..bd568f0 100644
--- a/init/stable_properties.h
+++ b/init/stable_properties.h
@@ -29,6 +29,7 @@
 };
 
 static const std::set<std::string> kExportedActionableProperties = {
+    "init.svc.console",
     "init.svc.mediadrm",
     "init.svc.zygote",
     "persist.bluetooth.btsnoopenable",
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index ac1d7c7..24b14c4 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -83,8 +83,8 @@
 }
 
 UeventListener::UeventListener() {
-    // is 256K enough? udev uses 16MB!
-    device_fd_.reset(uevent_open_socket(256 * 1024, true));
+    // is 2MB enough? udev uses 128MB!
+    device_fd_.reset(uevent_open_socket(2 * 1024 * 1024, true));
     if (device_fd_ == -1) {
         LOG(FATAL) << "Could not open uevent socket";
     }
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index caca377..f872d0f 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -88,7 +88,6 @@
 cc_test {
     name: "memunreachable_binder_test",
     defaults: ["libmemunreachable_defaults"],
-    test_suites: ["vts"],
     srcs: [
         "tests/Binder_test.cpp",
     ],
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 124c70e..8717283 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -47,6 +47,8 @@
 
     srcs: [
         "ArmExidx.cpp",
+        "DexFile.cpp",
+        "DexFiles.cpp",
         "DwarfCfa.cpp",
         "DwarfEhFrameWithHdr.cpp",
         "DwarfMemory.cpp",
@@ -85,11 +87,13 @@
         },
         vendor: {
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
-            exclude_static_libs: ["libunwindstack_dex"],
+            exclude_srcs: [
+                "DexFile.cpp",
+                "DexFiles.cpp",
+            ],
             exclude_shared_libs: ["libdexfile"],
         },
     },
-    whole_static_libs: ["libunwindstack_dex"],
 
     arch: {
         x86: {
@@ -114,79 +118,9 @@
     ],
 }
 
-// Isolate the dex file processing into a separate library. Currently,
-// it is necessary to add art include directories directly, which also
-// adds the art elf.h file in the include path, overriding the system one.
-// Work to isolate libdexfile is b/72216369.
-cc_library_static {
-    name: "libunwindstack_dex",
-    vendor_available: false,
-    defaults: ["libunwindstack_flags"],
-
-    cflags: [
-        "-Wexit-time-destructors",
-    ],
-
-    srcs: [
-        "DexFile.cpp",
-        "DexFiles.cpp",
-    ],
-    target: {
-        // Always disable optimizations for host to make it easier to debug.
-        host: {
-            cflags: [
-                "-O0",
-                "-g",
-            ],
-        },
-    },
-
-    shared_libs: [
-        "libbase",
-        "libdexfile",
-    ],
-    local_include_dirs: ["include"],
-    allow_undefined_symbols: true,
-
-    // libdexfile will eventually properly export headers, for now include
-    // these directly.
-    include_dirs: [
-        "art/runtime",
-    ],
-}
-
 //-------------------------------------------------------------------------
 // Unit Tests
 //-------------------------------------------------------------------------
-cc_test_library {
-    name: "libunwindstack_dex_test",
-    vendor_available: false,
-    defaults: ["libunwindstack_flags"],
-
-    shared: {
-        enabled: false,
-    },
-
-    srcs: [
-        "tests/DexFileTest.cpp",
-        "tests/DexFilesTest.cpp",
-    ],
-    local_include_dirs: ["include"],
-    allow_undefined_symbols: true,
-
-    shared_libs: [
-        "libbase",
-        "libunwindstack",
-        "libdexfile",
-    ],
-
-    // libdexfile will eventually properly export headers, for now include
-    // these directly.
-    include_dirs: [
-        "art/runtime",
-    ],
-}
-
 cc_test {
     name: "libunwindstack_test",
     defaults: ["libunwindstack_flags"],
@@ -194,6 +128,8 @@
     srcs: [
         "tests/ArmExidxDecodeTest.cpp",
         "tests/ArmExidxExtractTest.cpp",
+        "tests/DexFileTest.cpp",
+        "tests/DexFilesTest.cpp",
         "tests/DwarfCfaLogTest.cpp",
         "tests/DwarfCfaTest.cpp",
         "tests/DwarfDebugFrameTest.cpp",
@@ -242,14 +178,13 @@
         "liblog",
         "liblzma",
         "libunwindstack",
+        "libdexfile",
     ],
 
     static_libs: [
         "libgmock",
     ],
 
-    whole_static_libs: ["libunwindstack_dex_test"],
-
     data: [
         "tests/files/elf32.xz",
         "tests/files/elf64.xz",
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index dfb8e8f..a5afc7e 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -171,4 +171,17 @@
   return return_value;
 }
 
+bool ElfInterfaceArm::GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
+                                      uint64_t* offset) {
+  // For ARM, thumb function symbols have bit 0 set, but the address passed
+  // in here might not have this bit set and result in a failure to find
+  // the thumb function names. Adjust the address and offset to account
+  // for this possible case.
+  if (ElfInterface32::GetFunctionName(addr | 1, load_bias, name, offset)) {
+    *offset &= ~1;
+    return true;
+  }
+  return false;
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index 9c067ba..c1597ce 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -76,6 +76,9 @@
   bool StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
                  bool* finished);
 
+  bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
+                       uint64_t* offset) override;
+
   uint64_t start_offset() { return start_offset_; }
 
   size_t total_entries() { return total_entries_; }
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index af4a5b5..515bc8c 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -188,7 +188,7 @@
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
   EXPECT_EQ(
-      "  #00 pc 0001a9f8  libc.so (abort+63)\n"
+      "  #00 pc 0001a9f8  libc.so (abort+64)\n"
       "  #01 pc 00006a1b  libbase.so (_ZN7android4base14DefaultAborterEPKc+6)\n"
       "  #02 pc 00007441  libbase.so (_ZN7android4base10LogMessageD2Ev+748)\n"
       "  #03 pc 00015147  /does/not/exist/libhidlbase.so\n",
@@ -575,7 +575,7 @@
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
   EXPECT_EQ(
-      "  #00 pc 00018a5e  libarttestd.so (Java_Main_unwindInProcess+865)\n"
+      "  #00 pc 00018a5e  libarttestd.so (Java_Main_unwindInProcess+866)\n"
       "  #01 pc 0000212d (offset 0x2000)  137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
       "boolean)+92)\n"
       "  #02 pc 00011cb1  anonymous:e2796000 (boolean Main.bar(boolean)+72)\n"
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index a0abcca..5a8edfd 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -53,8 +53,7 @@
       printf("  PC 0x%" PRIx64, addr + load_bias);
       uint64_t func_offset;
       uint64_t pc = addr + load_bias;
-      // This might be a thumb function, so set the low bit.
-      if (interface->GetFunctionName(pc | 1, load_bias, &name, &func_offset) && !name.empty()) {
+      if (interface->GetFunctionName(pc, load_bias, &name, &func_offset) && !name.empty()) {
         printf(" <%s>", name.c_str());
       }
       printf("\n");
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 32caa69..0d7925a 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -159,7 +159,6 @@
 cc_library {
     name: "libutilscallstack",
     defaults: ["libutils_defaults"],
-    vendor_available: false,
 
     srcs: [
         "CallStack.cpp",
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 76d308a..d172755 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -6,6 +6,7 @@
         "liblog",
         "libcutils",
     ],
+    local_include_dirs: ["include"],
     cflags: ["-Werror"],
 
     init_rc: ["lmkd.rc"],
@@ -18,3 +19,17 @@
         },
     },
 }
+
+cc_library_static {
+    name: "liblmkd_utils",
+    srcs: ["liblmkd_utils.c"],
+    shared_libs: [
+        "libcutils",
+    ],
+    export_include_dirs: ["include"],
+    cppflags: [
+        "-g",
+        "-Wall",
+        "-Werror",
+    ]
+}
diff --git a/lmkd/include/liblmkd_utils.h b/lmkd/include/liblmkd_utils.h
new file mode 100644
index 0000000..72e3f4a
--- /dev/null
+++ b/lmkd/include/liblmkd_utils.h
@@ -0,0 +1,54 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef _LIBLMKD_UTILS_H_
+#define _LIBLMKD_UTILS_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <lmkd.h>
+
+__BEGIN_DECLS
+
+/*
+ * Connects to lmkd process and returns socket handle.
+ * On success returns socket handle.
+ * On error, -1 is returned, and errno is set appropriately.
+ */
+int lmkd_connect();
+
+/*
+ * Registers a process with lmkd and sets its oomadj score.
+ * On success returns 0.
+ * On error, -1 is returned.
+ * In the case of error errno is set appropriately.
+ */
+int lmkd_register_proc(int sock, struct lmk_procprio *params);
+
+/*
+ * Creates memcg directory for given process.
+ * On success returns 0.
+ * -1 is returned if path creation failed.
+ * -2 is returned if tasks file open operation failed.
+ * -3 is returned if tasks file write operation failed.
+ * In the case of error errno is set appropriately.
+ */
+int create_memcg(uid_t uid, pid_t pid);
+
+__END_DECLS
+
+#endif /* _LIBLMKD_UTILS_H_ */
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
new file mode 100644
index 0000000..fe6364d
--- /dev/null
+++ b/lmkd/include/lmkd.h
@@ -0,0 +1,147 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef _LMKD_H_
+#define _LMKD_H_
+
+#include <arpa/inet.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/*
+ * Supported LMKD commands
+ */
+enum lmk_cmd {
+    LMK_TARGET = 0,  /* Associate minfree with oom_adj_score */
+    LMK_PROCPRIO,    /* Register a process and set its oom_adj_score */
+    LMK_PROCREMOVE,  /* Unregister a process */
+};
+
+/*
+ * Max number of targets in LMK_TARGET command.
+ */
+#define MAX_TARGETS 6
+
+/*
+ * Max packet length in bytes.
+ * Longest packet is LMK_TARGET followed by MAX_TARGETS
+ * of minfree and oom_adj_score values
+ */
+#define CTRL_PACKET_MAX_SIZE (sizeof(int) * (MAX_TARGETS * 2 + 1))
+
+/* LMKD packet - first int is lmk_cmd followed by payload */
+typedef int LMKD_CTRL_PACKET[CTRL_PACKET_MAX_SIZE / sizeof(int)];
+
+/* Get LMKD packet command */
+inline enum lmk_cmd lmkd_pack_get_cmd(LMKD_CTRL_PACKET pack) {
+    return (enum lmk_cmd)ntohl(pack[0]);
+}
+
+/* LMK_TARGET packet payload */
+struct lmk_target {
+    int minfree;
+    int oom_adj_score;
+};
+
+/*
+ * For LMK_TARGET packet get target_idx-th payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_target(LMKD_CTRL_PACKET packet,
+                                 int target_idx, struct lmk_target *target) {
+    target->minfree = ntohl(packet[target_idx * 2 + 1]);
+    target->oom_adj_score = ntohl(packet[target_idx * 2 + 2]);
+}
+
+/*
+ * Prepare LMK_TARGET packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_target(LMKD_CTRL_PACKET packet,
+                                   struct lmk_target *targets,
+                                   size_t target_cnt) {
+    int idx = 0;
+    packet[idx++] = htonl(LMK_TARGET);
+    while (target_cnt) {
+        packet[idx++] = htonl(targets->minfree);
+        packet[idx++] = htonl(targets->oom_adj_score);
+        targets++;
+        target_cnt--;
+    }
+    return idx * sizeof(int);
+}
+
+/* LMK_PROCPRIO packet payload */
+struct lmk_procprio {
+    pid_t pid;
+    uid_t uid;
+    int oomadj;
+};
+
+/*
+ * For LMK_PROCPRIO packet get its payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet,
+                                   struct lmk_procprio *params) {
+    params->pid = (pid_t)ntohl(packet[1]);
+    params->uid = (uid_t)ntohl(packet[2]);
+    params->oomadj = ntohl(packet[3]);
+}
+
+/*
+ * Prepare LMK_PROCPRIO packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_procprio(LMKD_CTRL_PACKET packet,
+                                   struct lmk_procprio *params) {
+    packet[0] = htonl(LMK_PROCPRIO);
+    packet[1] = htonl(params->pid);
+    packet[2] = htonl(params->uid);
+    packet[3] = htonl(params->oomadj);
+    return 4 * sizeof(int);
+}
+
+/* LMK_PROCREMOVE packet payload */
+struct lmk_procremove {
+    pid_t pid;
+};
+
+/*
+ * For LMK_PROCREMOVE packet get its payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_procremove(LMKD_CTRL_PACKET packet,
+                                   struct lmk_procremove *params) {
+    params->pid = (pid_t)ntohl(packet[1]);
+}
+
+/*
+ * Prepare LMK_PROCREMOVE packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_procremove(LMKD_CTRL_PACKET packet,
+                                   struct lmk_procprio *params) {
+    packet[0] = htonl(LMK_PROCREMOVE);
+    packet[1] = htonl(params->pid);
+    return 2 * sizeof(int);
+}
+
+__END_DECLS
+
+#endif /* _LMKD_H_ */
diff --git a/lmkd/liblmkd_utils.c b/lmkd/liblmkd_utils.c
new file mode 100644
index 0000000..fa3b7a9
--- /dev/null
+++ b/lmkd/liblmkd_utils.c
@@ -0,0 +1,76 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  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 <errno.h>
+#include <fcntl.h>
+#include <sys/cdefs.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <liblmkd_utils.h>
+#include <cutils/sockets.h>
+
+int lmkd_connect() {
+    return socket_local_client("lmkd",
+                               ANDROID_SOCKET_NAMESPACE_RESERVED,
+                               SOCK_SEQPACKET);
+}
+
+int lmkd_register_proc(int sock, struct lmk_procprio *params) {
+    LMKD_CTRL_PACKET packet;
+    size_t size;
+    int ret;
+
+    size = lmkd_pack_set_procprio(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;
+    int written;
+
+    snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u", uid);
+    if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
+        errno != EEXIST) {
+        return -1;
+    }
+
+    snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u", uid, pid);
+    if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
+        errno != EEXIST) {
+        return -1;
+    }
+
+    snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u/tasks", uid, pid);
+    tasks_file = open(buf, O_WRONLY);
+    if (tasks_file < 0) {
+        return -2;
+    }
+    written = snprintf(buf, sizeof(buf), "%u", pid);
+    if (__predict_false(written >= (int)sizeof(buf))) {
+        written = sizeof(buf) - 1;
+    }
+    written = TEMP_FAILURE_RETRY(write(tasks_file, buf, written));
+    close(tasks_file);
+
+    return (written < 0) ? -3 : 0;
+}
+
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 338e5fa..45fa863 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -16,7 +16,6 @@
 
 #define LOG_TAG "lowmemorykiller"
 
-#include <arpa/inet.h>
 #include <errno.h>
 #include <inttypes.h>
 #include <sched.h>
@@ -34,6 +33,7 @@
 
 #include <cutils/properties.h>
 #include <cutils/sockets.h>
+#include <lmkd.h>
 #include <log/log.h>
 
 /*
@@ -71,19 +71,6 @@
 #define ARRAY_SIZE(x)   (sizeof(x) / sizeof(*(x)))
 #define EIGHT_MEGA (1 << 23)
 
-enum lmk_cmd {
-    LMK_TARGET,
-    LMK_PROCPRIO,
-    LMK_PROCREMOVE,
-};
-
-#define MAX_TARGETS 6
-/*
- * longest is LMK_TARGET followed by MAX_TARGETS each minfree and minkillprio
- * values
- */
-#define CTRL_PACKET_MAX (sizeof(int) * (MAX_TARGETS * 2 + 1))
-
 /* default to old in-kernel interface if no memory pressure events */
 static int use_inkernel_interface = 1;
 static bool has_inkernel_module;
@@ -122,13 +109,30 @@
 static bool kill_heaviest_task;
 static unsigned long kill_timeout_ms;
 
-/* control socket listen and data */
-static int ctrl_lfd;
-static int ctrl_dfd = -1;
-static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */
+/* data required to handle events */
+struct event_handler_info {
+    int data;
+    void (*handler)(int data, uint32_t events);
+};
 
-/* 3 memory pressure levels, 1 ctrl listen socket, 1 ctrl data socket */
-#define MAX_EPOLL_EVENTS 5
+/* data required to handle socket events */
+struct sock_event_handler_info {
+    int sock;
+    struct event_handler_info handler_info;
+};
+
+/* max supported number of data connections */
+#define MAX_DATA_CONN 2
+
+/* socket event handler data */
+static struct sock_event_handler_info ctrl_sock;
+static struct sock_event_handler_info data_sock[MAX_DATA_CONN];
+
+/* vmpressure event handler data */
+static struct event_handler_info vmpressure_hinfo[VMPRESS_LEVEL_COUNT];
+
+/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket */
+#define MAX_EPOLL_EVENTS (1 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
 static int epollfd;
 static int maxevents;
 
@@ -283,45 +287,49 @@
     close(fd);
 }
 
-static void cmd_procprio(int pid, int uid, int oomadj) {
+static void cmd_procprio(LMKD_CTRL_PACKET packet) {
     struct proc *procp;
     char path[80];
     char val[20];
     int soft_limit_mult;
+    struct lmk_procprio params;
 
-    if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) {
-        ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
+    lmkd_pack_get_procprio(packet, &params);
+
+    if (params.oomadj < OOM_SCORE_ADJ_MIN ||
+        params.oomadj > OOM_SCORE_ADJ_MAX) {
+        ALOGE("Invalid PROCPRIO oomadj argument %d", params.oomadj);
         return;
     }
 
-    snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid);
-    snprintf(val, sizeof(val), "%d", oomadj);
+    snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", params.pid);
+    snprintf(val, sizeof(val), "%d", params.oomadj);
     writefilestring(path, val);
 
     if (use_inkernel_interface)
         return;
 
-    if (oomadj >= 900) {
+    if (params.oomadj >= 900) {
         soft_limit_mult = 0;
-    } else if (oomadj >= 800) {
+    } else if (params.oomadj >= 800) {
         soft_limit_mult = 0;
-    } else if (oomadj >= 700) {
+    } else if (params.oomadj >= 700) {
         soft_limit_mult = 0;
-    } else if (oomadj >= 600) {
+    } else if (params.oomadj >= 600) {
         // Launcher should be perceptible, don't kill it.
-        oomadj = 200;
+        params.oomadj = 200;
         soft_limit_mult = 1;
-    } else if (oomadj >= 500) {
+    } else if (params.oomadj >= 500) {
         soft_limit_mult = 0;
-    } else if (oomadj >= 400) {
+    } else if (params.oomadj >= 400) {
         soft_limit_mult = 0;
-    } else if (oomadj >= 300) {
+    } else if (params.oomadj >= 300) {
         soft_limit_mult = 1;
-    } else if (oomadj >= 200) {
+    } else if (params.oomadj >= 200) {
         soft_limit_mult = 2;
-    } else if (oomadj >= 100) {
+    } else if (params.oomadj >= 100) {
         soft_limit_mult = 10;
-    } else if (oomadj >=   0) {
+    } else if (params.oomadj >=   0) {
         soft_limit_mult = 20;
     } else {
         // Persistent processes will have a large
@@ -329,11 +337,13 @@
         soft_limit_mult = 64;
     }
 
-    snprintf(path, sizeof(path), "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes", uid, pid);
+    snprintf(path, sizeof(path),
+             "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes",
+             params.uid, params.pid);
     snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);
     writefilestring(path, val);
 
-    procp = pid_lookup(pid);
+    procp = pid_lookup(params.pid);
     if (!procp) {
             procp = malloc(sizeof(struct proc));
             if (!procp) {
@@ -341,33 +351,38 @@
                 return;
             }
 
-            procp->pid = pid;
-            procp->uid = uid;
-            procp->oomadj = oomadj;
+            procp->pid = params.pid;
+            procp->uid = params.uid;
+            procp->oomadj = params.oomadj;
             proc_insert(procp);
     } else {
         proc_unslot(procp);
-        procp->oomadj = oomadj;
+        procp->oomadj = params.oomadj;
         proc_slot(procp);
     }
 }
 
-static void cmd_procremove(int pid) {
+static void cmd_procremove(LMKD_CTRL_PACKET packet) {
+    struct lmk_procremove params;
+
     if (use_inkernel_interface)
         return;
 
-    pid_remove(pid);
+    lmkd_pack_get_procremove(packet, &params);
+    pid_remove(params.pid);
 }
 
-static void cmd_target(int ntargets, int *params) {
+static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
     int i;
+    struct lmk_target target;
 
     if (ntargets > (int)ARRAY_SIZE(lowmem_adj))
         return;
 
     for (i = 0; i < ntargets; i++) {
-        lowmem_minfree[i] = ntohl(*params++);
-        lowmem_adj[i] = ntohl(*params++);
+        lmkd_pack_get_target(packet, i, &target);
+        lowmem_minfree[i] = target.minfree;
+        lowmem_adj[i] = target.oom_adj_score;
     }
 
     lowmem_targets_size = ntargets;
@@ -398,17 +413,24 @@
     }
 }
 
-static void ctrl_data_close(void) {
-    ALOGI("Closing Activity Manager data connection");
-    close(ctrl_dfd);
-    ctrl_dfd = -1;
+static void ctrl_data_close(int dsock_idx) {
+    struct epoll_event epev;
+
+    ALOGI("closing lmkd data connection");
+    if (epoll_ctl(epollfd, EPOLL_CTL_DEL, data_sock[dsock_idx].sock, &epev) == -1) {
+        // Log a warning and keep going
+        ALOGW("epoll_ctl for data connection socket failed; errno=%d", errno);
+    }
     maxevents--;
+
+    close(data_sock[dsock_idx].sock);
+    data_sock[dsock_idx].sock = -1;
 }
 
-static int ctrl_data_read(char *buf, size_t bufsz) {
+static int ctrl_data_read(int dsock_idx, char *buf, size_t bufsz) {
     int ret = 0;
 
-    ret = read(ctrl_dfd, buf, bufsz);
+    ret = read(data_sock[dsock_idx].sock, buf, bufsz);
 
     if (ret == -1) {
         ALOGE("control data socket read failed; errno=%d", errno);
@@ -420,39 +442,43 @@
     return ret;
 }
 
-static void ctrl_command_handler(void) {
-    int ibuf[CTRL_PACKET_MAX / sizeof(int)];
+static void ctrl_command_handler(int dsock_idx) {
+    LMKD_CTRL_PACKET packet;
     int len;
-    int cmd = -1;
+    enum lmk_cmd cmd;
     int nargs;
     int targets;
 
-    len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX);
+    len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE);
     if (len <= 0)
         return;
 
+    if (len < (int)sizeof(int)) {
+        ALOGE("Wrong control socket read length len=%d", len);
+        return;
+    }
+
+    cmd = lmkd_pack_get_cmd(packet);
     nargs = len / sizeof(int) - 1;
     if (nargs < 0)
         goto wronglen;
 
-    cmd = ntohl(ibuf[0]);
-
     switch(cmd) {
     case LMK_TARGET:
         targets = nargs / 2;
         if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
             goto wronglen;
-        cmd_target(targets, &ibuf[1]);
+        cmd_target(targets, packet);
         break;
     case LMK_PROCPRIO:
         if (nargs != 3)
             goto wronglen;
-        cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3]));
+        cmd_procprio(packet);
         break;
     case LMK_PROCREMOVE:
         if (nargs != 1)
             goto wronglen;
-        cmd_procremove(ntohl(ibuf[1]));
+        cmd_procremove(packet);
         break;
     default:
         ALOGE("Received unknown command code %d", cmd);
@@ -465,40 +491,57 @@
     ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len);
 }
 
-static void ctrl_data_handler(uint32_t events) {
-    if (events & EPOLLHUP) {
-        ALOGI("ActivityManager disconnected");
-        if (!ctrl_dfd_reopened)
-            ctrl_data_close();
-    } else if (events & EPOLLIN) {
-        ctrl_command_handler();
+static void ctrl_data_handler(int data, uint32_t events) {
+    if (events & EPOLLIN) {
+        ctrl_command_handler(data);
     }
 }
 
-static void ctrl_connect_handler(uint32_t events __unused) {
-    struct epoll_event epev;
+static int get_free_dsock() {
+    for (int i = 0; i < MAX_DATA_CONN; i++) {
+        if (data_sock[i].sock < 0) {
+            return i;
+        }
+    }
+    return -1;
+}
 
-    if (ctrl_dfd >= 0) {
-        ctrl_data_close();
-        ctrl_dfd_reopened = 1;
+static void ctrl_connect_handler(int data __unused, uint32_t events __unused) {
+    struct epoll_event epev;
+    int free_dscock_idx = get_free_dsock();
+
+    if (free_dscock_idx < 0) {
+        /*
+         * Number of data connections exceeded max supported. This should not
+         * happen but if it does we drop all existing connections and accept
+         * the new one. This prevents inactive connections from monopolizing
+         * data socket and if we drop ActivityManager connection it will
+         * immediately reconnect.
+         */
+        for (int i = 0; i < MAX_DATA_CONN; i++) {
+            ctrl_data_close(i);
+        }
+        free_dscock_idx = 0;
     }
 
-    ctrl_dfd = accept(ctrl_lfd, NULL, NULL);
-
-    if (ctrl_dfd < 0) {
+    data_sock[free_dscock_idx].sock = accept(ctrl_sock.sock, NULL, NULL);
+    if (data_sock[free_dscock_idx].sock < 0) {
         ALOGE("lmkd control socket accept failed; errno=%d", errno);
         return;
     }
 
-    ALOGI("ActivityManager connected");
-    maxevents++;
+    ALOGI("lmkd data connection established");
+    /* use data to store data connection idx */
+    data_sock[free_dscock_idx].handler_info.data = free_dscock_idx;
+    data_sock[free_dscock_idx].handler_info.handler = ctrl_data_handler;
     epev.events = EPOLLIN;
-    epev.data.ptr = (void *)ctrl_data_handler;
-    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_dfd, &epev) == -1) {
+    epev.data.ptr = (void *)&(data_sock[free_dscock_idx].handler_info);
+    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, data_sock[free_dscock_idx].sock, &epev) == -1) {
         ALOGE("epoll_ctl for data connection socket failed; errno=%d", errno);
-        ctrl_data_close();
+        ctrl_data_close(free_dscock_idx);
         return;
     }
+    maxevents++;
 }
 
 static int zoneinfo_parse_protection(char *cp) {
@@ -802,7 +845,7 @@
            (to->tv_usec - from->tv_usec) / 1000;
 }
 
-static void mp_event_common(enum vmpressure_level level) {
+static void mp_event_common(int data, uint32_t events __unused) {
     int ret;
     unsigned long long evcount;
     int64_t mem_usage, memsw_usage;
@@ -811,6 +854,7 @@
     struct mem_size free_mem;
     static struct timeval last_report_tm;
     static unsigned long skip_count = 0;
+    enum vmpressure_level level = (enum vmpressure_level)data;
 
     /*
      * Check all event counters from low to critical
@@ -927,26 +971,15 @@
     }
 }
 
-static void mp_event_low(uint32_t events __unused) {
-    mp_event_common(VMPRESS_LEVEL_LOW);
-}
-
-static void mp_event_medium(uint32_t events __unused) {
-    mp_event_common(VMPRESS_LEVEL_MEDIUM);
-}
-
-static void mp_event_critical(uint32_t events __unused) {
-    mp_event_common(VMPRESS_LEVEL_CRITICAL);
-}
-
-static bool init_mp_common(void *event_handler, enum vmpressure_level level) {
+static bool init_mp_common(enum vmpressure_level level) {
     int mpfd;
     int evfd;
     int evctlfd;
     char buf[256];
     struct epoll_event epev;
     int ret;
-    const char *levelstr = level_name[level];
+    int level_idx = (int)level;
+    const char *levelstr = level_name[level_idx];
 
     mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
     if (mpfd < 0) {
@@ -972,7 +1005,7 @@
         goto err;
     }
 
-    ret = write(evctlfd, buf, strlen(buf) + 1);
+    ret = TEMP_FAILURE_RETRY(write(evctlfd, buf, strlen(buf) + 1));
     if (ret == -1) {
         ALOGE("cgroup.event_control write failed for level %s; errno=%d",
               levelstr, errno);
@@ -980,7 +1013,10 @@
     }
 
     epev.events = EPOLLIN;
-    epev.data.ptr = event_handler;
+    /* use data to store event level */
+    vmpressure_hinfo[level_idx].data = level_idx;
+    vmpressure_hinfo[level_idx].handler = mp_event_common;
+    epev.data.ptr = (void *)&vmpressure_hinfo[level_idx];
     ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evfd, &epev);
     if (ret == -1) {
         ALOGE("epoll_ctl for level %s failed; errno=%d", levelstr, errno);
@@ -1017,21 +1053,27 @@
         return -1;
     }
 
-    ctrl_lfd = android_get_control_socket("lmkd");
-    if (ctrl_lfd < 0) {
+    // mark data connections as not connected
+    for (int i = 0; i < MAX_DATA_CONN; i++) {
+        data_sock[i].sock = -1;
+    }
+
+    ctrl_sock.sock = android_get_control_socket("lmkd");
+    if (ctrl_sock.sock < 0) {
         ALOGE("get lmkd control socket failed");
         return -1;
     }
 
-    ret = listen(ctrl_lfd, 1);
+    ret = listen(ctrl_sock.sock, MAX_DATA_CONN);
     if (ret < 0) {
         ALOGE("lmkd control socket listen failed (errno=%d)", errno);
         return -1;
     }
 
     epev.events = EPOLLIN;
-    epev.data.ptr = (void *)ctrl_connect_handler;
-    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_lfd, &epev) == -1) {
+    ctrl_sock.handler_info.handler = ctrl_connect_handler;
+    epev.data.ptr = (void *)&(ctrl_sock.handler_info);
+    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_sock.sock, &epev) == -1) {
         ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno);
         return -1;
     }
@@ -1043,10 +1085,9 @@
     if (use_inkernel_interface) {
         ALOGI("Using in-kernel low memory killer interface");
     } else {
-        if (!init_mp_common((void *)&mp_event_low, VMPRESS_LEVEL_LOW) ||
-            !init_mp_common((void *)&mp_event_medium, VMPRESS_LEVEL_MEDIUM) ||
-            !init_mp_common((void *)&mp_event_critical,
-                            VMPRESS_LEVEL_CRITICAL)) {
+        if (!init_mp_common(VMPRESS_LEVEL_LOW) ||
+            !init_mp_common(VMPRESS_LEVEL_MEDIUM) ||
+            !init_mp_common(VMPRESS_LEVEL_CRITICAL)) {
             ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
             return -1;
         }
@@ -1061,12 +1102,14 @@
 }
 
 static void mainloop(void) {
+    struct event_handler_info* handler_info;
+    struct epoll_event *evt;
+
     while (1) {
         struct epoll_event events[maxevents];
         int nevents;
         int i;
 
-        ctrl_dfd_reopened = 0;
         nevents = epoll_wait(epollfd, events, maxevents, -1);
 
         if (nevents == -1) {
@@ -1076,11 +1119,33 @@
             continue;
         }
 
-        for (i = 0; i < nevents; ++i) {
-            if (events[i].events & EPOLLERR)
+        /*
+         * First pass to see if any data socket connections were dropped.
+         * Dropped connection should be handled before any other events
+         * to deallocate data connection and correctly handle cases when
+         * connection gets dropped and reestablished in the same epoll cycle.
+         * In such cases it's essential to handle connection closures first.
+         */
+        for (i = 0, evt = &events[0]; i < nevents; ++i, evt++) {
+            if ((evt->events & EPOLLHUP) && evt->data.ptr) {
+                ALOGI("lmkd data connection dropped");
+                handler_info = (struct event_handler_info*)evt->data.ptr;
+                ctrl_data_close(handler_info->data);
+            }
+        }
+
+        /* Second pass to handle all other events */
+        for (i = 0, evt = &events[0]; i < nevents; ++i, evt++) {
+            if (evt->events & EPOLLERR)
                 ALOGD("EPOLLERR on event #%d", i);
-            if (events[i].data.ptr)
-                (*(void (*)(uint32_t))events[i].data.ptr)(events[i].events);
+            if (evt->events & EPOLLHUP) {
+                /* This case was handled in the first pass */
+                continue;
+            }
+            if (evt->data.ptr) {
+                handler_info = (struct event_handler_info*)evt->data.ptr;
+                handler_info->handler(handler_info->data, evt->events);
+            }
         }
     }
 }
diff --git a/lmkd/tests/Android.bp b/lmkd/tests/Android.bp
new file mode 100644
index 0000000..cbf44e9
--- /dev/null
+++ b/lmkd/tests/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+    name: "lmkd_unit_test",
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+
+    static_libs: [
+        "liblmkd_utils",
+    ],
+
+    target: {
+        android: {
+            srcs: ["lmkd_test.cpp"],
+        },
+    },
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+
+    compile_multilib: "first",
+}
diff --git a/lmkd/tests/lmkd_test.cpp b/lmkd/tests/lmkd_test.cpp
new file mode 100644
index 0000000..f17512d
--- /dev/null
+++ b/lmkd/tests/lmkd_test.cpp
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sstream>
+#include <stdio.h>
+#include <string.h>
+#include <string>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+#include <lmkd.h>
+#include <liblmkd_utils.h>
+#include <log/log_properties.h>
+#include <private/android_filesystem_config.h>
+
+using namespace android::base;
+
+#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
+#define LMKDTEST_RESPAWN_FLAG "LMKDTEST_RESPAWN"
+
+#define LMKD_LOGCAT_MARKER "lowmemorykiller"
+#define LMKD_KILL_MARKER_TEMPLATE LMKD_LOGCAT_MARKER ": Killing '%s'"
+#define OOM_MARKER "Out of memory"
+#define OOM_KILL_MARKER "Killed process"
+#define MIN_LOG_SIZE 100
+
+#define ONE_MB (1 << 20)
+
+/* Test constant parameters */
+#define OOM_ADJ_MAX 1000
+#define OOM_ADJ_MIN 0
+#define OOM_ADJ_STEP 100
+#define STEP_COUNT ((OOM_ADJ_MAX - OOM_ADJ_MIN) / OOM_ADJ_STEP + 1)
+
+#define ALLOC_STEP (ONE_MB)
+#define ALLOC_DELAY 1000
+
+/* Utility functions */
+std::string readCommand(const std::string& command) {
+    FILE* fp = popen(command.c_str(), "r");
+    std::string content;
+    ReadFdToString(fileno(fp), &content);
+    pclose(fp);
+    return content;
+}
+
+std::string readLogcat(const std::string& marker) {
+    std::string content = readCommand("logcat -d -b all");
+    size_t pos = content.find(marker);
+    if (pos == std::string::npos) return "";
+    content.erase(0, pos);
+    return content;
+}
+
+bool writeFile(const std::string& file, const std::string& string) {
+    if (getuid() == static_cast<unsigned>(AID_ROOT)) {
+        return WriteStringToFile(string, file);
+    }
+    return string == readCommand(
+        "echo -n '" + string + "' | su root tee " + file + " 2>&1");
+}
+
+bool writeKmsg(const std::string& marker) {
+    return writeFile("/dev/kmsg", marker);
+}
+
+std::string getTextAround(const std::string& text, size_t pos,
+                          size_t lines_before, size_t lines_after) {
+    size_t start_pos = pos;
+
+    // find start position
+    // move up lines_before number of lines
+    while (lines_before > 0 &&
+           (start_pos = text.rfind('\n', start_pos)) != std::string::npos) {
+        lines_before--;
+    }
+    // move to the beginning of the line
+    start_pos = text.rfind('\n', start_pos);
+    start_pos = (start_pos == std::string::npos) ? 0 : start_pos + 1;
+
+    // find end position
+    // move down lines_after number of lines
+    while (lines_after > 0 &&
+           (pos = text.find('\n', pos)) != std::string::npos) {
+        pos++;
+        lines_after--;
+    }
+    return text.substr(start_pos, (pos == std::string::npos) ?
+                       std::string::npos : pos - start_pos);
+}
+
+bool getExecPath(std::string &path) {
+    // exec path as utf8z c_str().
+    // std::string contains _all_ nul terminated argv[] strings.
+    return android::base::ReadFileToString("/proc/self/cmdline", &path);
+}
+
+/* Child synchronization primitives */
+#define STATE_INIT 0
+#define STATE_CHILD_READY 1
+#define STATE_PARENT_READY 2
+
+struct state_sync {
+    pthread_mutex_t mutex;
+    pthread_cond_t condition;
+    int state;
+};
+
+struct state_sync * init_state_sync_obj() {
+    struct state_sync *ssync;
+
+    ssync = (struct state_sync*)mmap(NULL, sizeof(struct state_sync),
+                PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+    if (ssync == MAP_FAILED) {
+        return NULL;
+    }
+
+    pthread_mutexattr_t mattr;
+    pthread_mutexattr_init(&mattr);
+    pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
+    pthread_mutex_init(&ssync->mutex, &mattr);
+
+    pthread_condattr_t cattr;
+    pthread_condattr_init(&cattr);
+    pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
+    pthread_cond_init(&ssync->condition, &cattr);
+
+    ssync->state = STATE_INIT;
+    return ssync;
+}
+
+void destroy_state_sync_obj(struct state_sync *ssync) {
+    pthread_cond_destroy(&ssync->condition);
+    pthread_mutex_destroy(&ssync->mutex);
+    munmap(ssync, sizeof(struct state_sync));
+}
+
+void signal_state(struct state_sync *ssync, int state) {
+    pthread_mutex_lock(&ssync->mutex);
+    ssync->state = state;
+    pthread_cond_signal(&ssync->condition);
+    pthread_mutex_unlock(&ssync->mutex);
+}
+
+void wait_for_state(struct state_sync *ssync, int state) {
+    pthread_mutex_lock(&ssync->mutex);
+    while (ssync->state != state) {
+        pthread_cond_wait(&ssync->condition, &ssync->mutex);
+    }
+    pthread_mutex_unlock(&ssync->mutex);
+}
+
+/* Memory allocation and data sharing */
+struct shared_data {
+    size_t allocated;
+    bool finished;
+    size_t total_size;
+    size_t step_size;
+    size_t step_delay;
+    int oomadj;
+};
+
+volatile void *gptr;
+void add_pressure(struct shared_data *data) {
+    volatile void *ptr;
+    size_t allocated_size = 0;
+
+    data->finished = false;
+    while (allocated_size < data->total_size) {
+        ptr = mmap(NULL, data->step_size, PROT_READ | PROT_WRITE,
+                MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
+        if (ptr != MAP_FAILED) {
+            /* create ptr aliasing to prevent compiler optimizing the access */
+            gptr = ptr;
+            /* make data non-zero */
+            memset((void*)ptr, (int)(allocated_size + 1), data->step_size);
+            allocated_size += data->step_size;
+            data->allocated = allocated_size;
+        }
+        usleep(data->step_delay);
+    }
+    data->finished = (allocated_size >= data->total_size);
+}
+
+/* Memory stress test main body */
+void runMemStressTest() {
+    struct shared_data *data;
+    struct state_sync *ssync;
+    int sock;
+    pid_t pid;
+    uid_t uid = getuid();
+
+    ASSERT_FALSE((sock = lmkd_connect()) < 0)
+        << "Failed to connect to lmkd process, err=" << strerror(errno);
+
+    /* allocate shared memory to communicate params with a child */
+    data = (struct shared_data*)mmap(NULL, sizeof(struct shared_data),
+                PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+    ASSERT_FALSE(data == MAP_FAILED) << "Memory allocation failure";
+    data->total_size = (size_t)-1; /* allocate until killed */
+    data->step_size = ALLOC_STEP;
+    data->step_delay = ALLOC_DELAY;
+
+    /* allocate state sync object */
+    ASSERT_FALSE((ssync = init_state_sync_obj()) == NULL)
+        << "Memory allocation failure";
+
+    /* run the test gradually decreasing oomadj */
+    data->oomadj = OOM_ADJ_MAX;
+    while (data->oomadj >= OOM_ADJ_MIN) {
+        ASSERT_FALSE((pid = fork()) < 0)
+            << "Failed to spawn a child process, err=" << strerror(errno);
+        if (pid != 0) {
+            /* Parent */
+            struct lmk_procprio params;
+            /* wait for child to start and get ready */
+            wait_for_state(ssync, STATE_CHILD_READY);
+            params.pid = pid;
+            params.uid = uid;
+            params.oomadj = data->oomadj;
+            ASSERT_FALSE(lmkd_register_proc(sock, &params) < 0)
+                << "Failed to communicate with lmkd, err=" << strerror(errno);
+            // signal the child it can proceed
+            signal_state(ssync, STATE_PARENT_READY);
+            waitpid(pid, NULL, 0);
+            if (data->finished) {
+                GTEST_LOG_(INFO) << "Child [pid=" << pid << "] allocated "
+                                 << data->allocated / ONE_MB << "MB";
+            } else {
+                GTEST_LOG_(INFO) << "Child [pid=" << pid << "] allocated "
+                                 << data->allocated / ONE_MB
+                                 << "MB before being killed";
+            }
+            data->oomadj -= OOM_ADJ_STEP;
+        } else {
+            /* Child */
+            pid = getpid();
+            GTEST_LOG_(INFO) << "Child [pid=" << pid
+                             << "] is running at oomadj="
+                             << data->oomadj;
+            data->allocated = 0;
+            data->finished = false;
+            ASSERT_FALSE(create_memcg(uid, pid) != 0)
+                << "Child [pid=" << pid << "] failed to create a cgroup";
+            signal_state(ssync, STATE_CHILD_READY);
+            wait_for_state(ssync, STATE_PARENT_READY);
+            add_pressure(data);
+            /* should not reach here, child should be killed by OOM/LMK */
+            FAIL() << "Child [pid=" << pid << "] was not killed";
+            break;
+        }
+    }
+    destroy_state_sync_obj(ssync);
+    munmap(data, sizeof(struct shared_data));
+    close(sock);
+}
+
+TEST(lmkd, check_for_oom) {
+    // test requirements
+    //   userdebug build
+    if (!__android_log_is_debuggable()) {
+        GTEST_LOG_(INFO) << "Must be userdebug build, terminating test";
+        return;
+    }
+    // check if in-kernel LMK driver is present
+    if (!access(INKERNEL_MINFREE_PATH, W_OK)) {
+        GTEST_LOG_(INFO) << "Must not have kernel lowmemorykiller driver,"
+                         << " terminating test";
+        return;
+    }
+
+    // if respawned test process then run the test and exit (no analysis)
+    if (getenv(LMKDTEST_RESPAWN_FLAG) != NULL) {
+        runMemStressTest();
+        return;
+    }
+
+    // Main test process
+    // mark the beginning of the test
+    std::string marker = StringPrintf(
+        "LMKD test start %lu\n", static_cast<unsigned long>(time(nullptr)));
+    ASSERT_TRUE(writeKmsg(marker));
+
+    // get executable complete path
+    std::string test_path;
+    ASSERT_TRUE(getExecPath(test_path));
+
+    std::string test_output;
+    if (getuid() != static_cast<unsigned>(AID_ROOT)) {
+        // if not root respawn itself as root and capture output
+        std::string command = StringPrintf(
+            "%s=true su root %s --gtest_filter=lmkd.check_for_oom 2>&1",
+            LMKDTEST_RESPAWN_FLAG, test_path.c_str());
+        std::string test_output = readCommand(command);
+        GTEST_LOG_(INFO) << test_output;
+    } else {
+        // main test process is root, run the test
+        runMemStressTest();
+    }
+
+    // Analyze results
+    // capture logcat containind kernel logs
+    std::string logcat_out = readLogcat(marker);
+
+    // 1. extract LMKD kills from logcat output, count kills
+    std::stringstream kill_logs;
+    int hit_count = 0;
+    size_t pos = 0;
+    marker = StringPrintf(LMKD_KILL_MARKER_TEMPLATE, test_path.c_str());
+
+    while (true) {
+        if ((pos = logcat_out.find(marker, pos)) != std::string::npos) {
+            kill_logs << getTextAround(logcat_out, pos, 0, 1);
+            pos += marker.length();
+            hit_count++;
+        } else {
+            break;
+        }
+    }
+    GTEST_LOG_(INFO) << "====Logged kills====" << std::endl
+                     << kill_logs.str();
+    EXPECT_TRUE(hit_count == STEP_COUNT) << "Number of kills " << hit_count
+                                         << " is less than expected "
+                                         << STEP_COUNT;
+
+    // 2. check kernel logs for OOM kills
+    pos = logcat_out.find(OOM_MARKER);
+    bool oom_detected = (pos != std::string::npos);
+    bool oom_kill_detected = (oom_detected &&
+        logcat_out.find(OOM_KILL_MARKER, pos) != std::string::npos);
+
+    EXPECT_FALSE(oom_kill_detected) << "OOM kill is detected!";
+    if (oom_detected || oom_kill_detected) {
+        // capture logcat with logs around all OOMs
+        pos = 0;
+        while ((pos = logcat_out.find(OOM_MARKER, pos)) != std::string::npos) {
+            GTEST_LOG_(INFO) << "====Logs around OOM====" << std::endl
+                             << getTextAround(logcat_out, pos,
+                                    MIN_LOG_SIZE / 2, MIN_LOG_SIZE / 2);
+            pos += strlen(OOM_MARKER);
+        }
+    }
+
+    // output complete logcat with kernel (might get truncated)
+    GTEST_LOG_(INFO) << "====Complete logcat output====" << std::endl
+                     << logcat_out;
+}
+
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index b8af2f0..a78319f 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -171,7 +171,9 @@
     }
 
     // audit message (except sequence number) identical?
-    if (last->isBinary()) {
+    if (last->isBinary() &&
+        (lenl > static_cast<ssize_t>(sizeof(android_log_event_string_t))) &&
+        (lenr > static_cast<ssize_t>(sizeof(android_log_event_string_t)))) {
         if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) -
                                             sizeof(int32_t))) {
             return DIFFERENT;
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index feb100e..f488ed5 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -93,6 +93,23 @@
 else
   LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product $(TARGET_ROOT_OUT)/product
 endif
+
+# For /odm partition.
+LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm
+# For Treble Generic System Image (GSI), system-as-root GSI needs to work on
+# both devices with and without /odm partition. Those symlinks are for devices
+# without /odm partition. For devices with /odm partition, mount odm.img under
+# /odm will hide those symlinks.
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/app $(TARGET_ROOT_OUT)/odm/app
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/bin $(TARGET_ROOT_OUT)/odm/bin
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/etc $(TARGET_ROOT_OUT)/odm/etc
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/firmware $(TARGET_ROOT_OUT)/odm/firmware
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/framework $(TARGET_ROOT_OUT)/odm/framework
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/lib $(TARGET_ROOT_OUT)/odm/lib
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/lib64 $(TARGET_ROOT_OUT)/odm/lib64
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/overlay $(TARGET_ROOT_OUT)/odm/overlay
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/priv-app $(TARGET_ROOT_OUT)/odm/priv-app
+
 ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
 else
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index c8d87c8..6e46295 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -54,6 +54,9 @@
 namespace.default.permitted.paths += /vendor/framework
 namespace.default.permitted.paths += /vendor/app
 namespace.default.permitted.paths += /vendor/priv-app
+namespace.default.permitted.paths += /odm/framework
+namespace.default.permitted.paths += /odm/app
+namespace.default.permitted.paths += /odm/priv-app
 namespace.default.permitted.paths += /oem/app
 namespace.default.permitted.paths += /product/framework
 namespace.default.permitted.paths += /product/app
@@ -74,6 +77,9 @@
 namespace.default.asan.permitted.paths += /vendor/framework
 namespace.default.asan.permitted.paths += /vendor/app
 namespace.default.asan.permitted.paths += /vendor/priv-app
+namespace.default.asan.permitted.paths += /odm/framework
+namespace.default.asan.permitted.paths += /odm/app
+namespace.default.asan.permitted.paths += /odm/priv-app
 namespace.default.asan.permitted.paths += /oem/app
 namespace.default.asan.permitted.paths += /product/framework
 namespace.default.asan.permitted.paths += /product/app
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a213ffb..1462570 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -573,6 +573,9 @@
     hostname localhost
     domainname localdomain
 
+    # IPsec SA default expiration length
+    write /proc/sys/net/core/xfrm_acq_expires 3600
+
     # Memory management.  Basic kernel parameters, and allow the high
     # level system server to be able to adjust the kernel OOM driver
     # parameters to match how it is managing things.