Merge "adb unittest: get test_unicode_paths passing on win32"
diff --git a/adb/adb.cpp b/adb/adb.cpp
index a0501a6..d6e6d91 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -972,8 +972,7 @@
     if (!strncmp(service, "disconnect:", 11)) {
         const std::string address(service + 11);
         if (address.empty()) {
-            // disconnect from all TCP devices
-            unregister_all_tcp_transports();
+            kick_all_tcp_devices();
             return SendOkay(reply_fd, "disconnected everything");
         }
 
@@ -990,7 +989,7 @@
             return SendFail(reply_fd, android::base::StringPrintf("no such device '%s'",
                                                                   serial.c_str()));
         }
-        unregister_transport(t);
+        kick_transport(t);
         return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
     }
 
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index e70d550..94876ee 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -179,39 +179,41 @@
     return sync_start_stat(sc, path) && sync_finish_stat(sc, timestamp, mode, size);
 }
 
-static int write_data_file(SyncConnection& sc, const char* path, syncsendbuf* sbuf, bool show_progress) {
-    int err = 0;
+static bool write_data_file(SyncConnection& sc, const char* path, syncsendbuf* sbuf, bool show_progress) {
     unsigned long long size = 0;
+    if (show_progress) {
+        // Determine local file size.
+        struct stat st;
+        if (stat(path, &st) == -1) {
+            fprintf(stderr, "cannot stat '%s': %s\n", path, strerror(errno));
+            return false;
+        }
+
+        size = st.st_size;
+    }
 
     int lfd = adb_open(path, O_RDONLY);
     if (lfd < 0) {
         fprintf(stderr, "cannot open '%s': %s\n", path, strerror(errno));
-        return -1;
-    }
-
-    if (show_progress) {
-        // Determine local file size.
-        struct stat st;
-        if (stat(path, &st)) {
-            fprintf(stderr, "cannot stat '%s': %s\n", path, strerror(errno));
-            return -1;
-        }
-
-        size = st.st_size;
+        return false;
     }
 
     sbuf->id = ID_DATA;
     while (true) {
         int ret = adb_read(lfd, sbuf->data, sc.max);
         if (ret <= 0) {
-            if (ret < 0) fprintf(stderr, "cannot read '%s': %s\n", path, strerror(errno));
+            if (ret < 0) {
+                fprintf(stderr, "cannot read '%s': %s\n", path, strerror(errno));
+                adb_close(lfd);
+                return false;
+            }
             break;
         }
 
         sbuf->size = ret;
         if (!WriteFdExactly(sc.fd, sbuf, sizeof(unsigned) * 2 + ret)) {
-            err = -1;
-            break;
+            adb_close(lfd);
+            return false;
         }
         sc.total_bytes += ret;
 
@@ -221,17 +223,17 @@
     }
 
     adb_close(lfd);
-    return err;
+    return true;
 }
 
 #if defined(_WIN32)
-extern int write_data_link(SyncConnection& sc, const char* path, syncsendbuf* sbuf) __attribute__((error("no symlinks on Windows")));
+extern bool write_data_link(SyncConnection& sc, const char* path, syncsendbuf* sbuf) __attribute__((error("no symlinks on Windows")));
 #else
-static int write_data_link(SyncConnection& sc, const char* path, syncsendbuf* sbuf) {
+static bool write_data_link(SyncConnection& sc, const char* path, syncsendbuf* sbuf) {
     ssize_t len = readlink(path, sbuf->data, sc.max - 1);
     if (len < 0) {
         fprintf(stderr, "error reading link '%s': %s\n", path, strerror(errno));
-        return -1;
+        return false;
     }
     sbuf->data[len] = '\0';
 
@@ -239,12 +241,12 @@
     sbuf->id = ID_DATA;
 
     if (!WriteFdExactly(sc.fd, sbuf, sizeof(unsigned) * 2 + len + 1)) {
-        return -1;
+        return false;
     }
 
     sc.total_bytes += len + 1;
 
-    return 0;
+    return true;
 }
 #endif
 
@@ -257,9 +259,9 @@
     if (!SendRequest(sc.fd, ID_SEND, path_and_mode.c_str())) goto fail;
 
     if (S_ISREG(mode)) {
-        write_data_file(sc, lpath, sbuf, show_progress);
+        if (!write_data_file(sc, lpath, sbuf, show_progress)) return false;
     } else if (S_ISLNK(mode)) {
-        write_data_link(sc, lpath, sbuf);
+        if (!write_data_link(sc, lpath, sbuf)) return false;
     } else {
         goto fail;
     }
@@ -325,6 +327,7 @@
 
     while (true) {
         if(!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
+            adb_close(lfd);
             return -1;
         }
         id = msg.data.id;
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index a85d5ad..9c13936 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -471,14 +471,6 @@
 }
 #endif /* ADB_HOST */
 
-/* a Remote socket is used to send/receive data to/from a given transport object
-** it needs to be closed when the transport is forcibly destroyed by the user
-*/
-struct aremotesocket {
-    asocket      socket;
-    adisconnect  disconnect;
-};
-
 static int remote_socket_enqueue(asocket *s, apacket *p)
 {
     D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d\n",
@@ -526,33 +518,17 @@
     D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n",
       s->id, s->fd, s->peer?s->peer->fd:-1);
     D("RS(%d): closed\n", s->id);
-    remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
     free(s);
 }
 
-static void remote_socket_disconnect(void*  _s, atransport*  t)
-{
-    asocket* s = reinterpret_cast<asocket*>(_s);
-    asocket* peer = s->peer;
-
-    D("remote_socket_disconnect RS(%d)\n", s->id);
-    if (peer) {
-        peer->peer = NULL;
-        peer->close(peer);
-    }
-    remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
-    free(s);
-}
-
-/* Create an asocket to exchange packets with a remote service through transport
-  |t|. Where |id| is the socket id of the corresponding service on the other
-   side of the transport (it is allocated by the remote side and _cannot_ be 0).
-   Returns a new non-NULL asocket handle. */
+// Create a remote socket to exchange packets with a remote service through transport
+// |t|. Where |id| is the socket id of the corresponding service on the other
+//  side of the transport (it is allocated by the remote side and _cannot_ be 0).
+// Returns a new non-NULL asocket handle.
 asocket *create_remote_socket(unsigned id, atransport *t)
 {
     if (id == 0) fatal("invalid remote socket id (0)");
-    asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(aremotesocket)));
-    adisconnect* dis = &reinterpret_cast<aremotesocket*>(s)->disconnect;
+    asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
 
     if (s == NULL) fatal("cannot allocate socket");
     s->id = id;
@@ -562,9 +538,6 @@
     s->close = remote_socket_close;
     s->transport = t;
 
-    dis->func   = remote_socket_disconnect;
-    dis->opaque = s;
-    add_transport_disconnect( t, dis );
     D("RS(%d): created\n", s->id);
     return s;
 }
diff --git a/adb/test_device.py b/adb/test_device.py
index 024d163..2006937 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -215,12 +215,18 @@
     def test_install_argument_escaping(self):
         """Make sure that install argument escaping works."""
         # http://b/20323053
-        tf = tempfile.NamedTemporaryFile('wb', suffix='-text;ls;1.apk')
+        tf = tempfile.NamedTemporaryFile('wb', suffix='-text;ls;1.apk',
+                                         delete=False)
+        tf.close()
         self.assertIn("-text;ls;1.apk", self.device.install(tf.name))
+        os.remove(tf.name)
 
         # http://b/3090932
-        tf = tempfile.NamedTemporaryFile('wb', suffix="-Live Hold'em.apk")
+        tf = tempfile.NamedTemporaryFile('wb', suffix="-Live Hold'em.apk",
+                                         delete=False)
+        tf.close()
         self.assertIn("-Live Hold'em.apk", self.device.install(tf.name))
+        os.remove(tf.name)
 
 
 class RootUnrootTest(DeviceTest):
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 5e4ffbb..46c709f 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -28,6 +28,7 @@
 
 #include <list>
 
+#include <base/logging.h>
 #include <base/stringprintf.h>
 #include <base/strings.h>
 
@@ -41,23 +42,6 @@
 
 ADB_MUTEX_DEFINE( transport_lock );
 
-void kick_transport(atransport* t)
-{
-    if (t && !t->kicked)
-    {
-        int  kicked;
-
-        adb_mutex_lock(&transport_lock);
-        kicked = t->kicked;
-        if (!kicked)
-            t->kicked = 1;
-        adb_mutex_unlock(&transport_lock);
-
-        if (!kicked)
-            t->kick(t);
-    }
-}
-
 // Each atransport contains a list of adisconnects (t->disconnects).
 // An adisconnect contains a link to the next/prev adisconnect, a function
 // pointer to a disconnect callback which takes a void* piece of user data and
@@ -336,6 +320,19 @@
     return 0;
 }
 
+static void kick_transport_locked(atransport* t) {
+    CHECK(t != nullptr);
+    if (!t->kicked) {
+        t->kicked = true;
+        t->kick(t);
+    }
+}
+
+void kick_transport(atransport* t) {
+    adb_mutex_lock(&transport_lock);
+    kick_transport_locked(t);
+    adb_mutex_unlock(&transport_lock);
+}
 
 static int transport_registration_send = -1;
 static int transport_registration_recv = -1;
@@ -642,29 +639,20 @@
 }
 
 
-static void transport_unref_locked(atransport *t)
-{
+static void transport_unref(atransport* t) {
+    CHECK(t != nullptr);
+    adb_mutex_lock(&transport_lock);
+    CHECK_GT(t->ref_count, 0u);
     t->ref_count--;
     if (t->ref_count == 0) {
         D("transport: %s unref (kicking and closing)\n", t->serial);
-        if (!t->kicked) {
-            t->kicked = 1;
-            t->kick(t);
-        }
+        kick_transport_locked(t);
         t->close(t);
         remove_transport(t);
     } else {
-        D("transport: %s unref (count=%d)\n", t->serial, t->ref_count);
+        D("transport: %s unref (count=%zu)\n", t->serial, t->ref_count);
     }
-}
-
-static void transport_unref(atransport *t)
-{
-    if (t) {
-        adb_mutex_lock(&transport_lock);
-        transport_unref_locked(t);
-        adb_mutex_unlock(&transport_lock);
-    }
+    adb_mutex_unlock(&transport_lock);
 }
 
 void add_transport_disconnect(atransport*  t, adisconnect*  dis)
@@ -967,7 +955,7 @@
     atransport* result = nullptr;
 
     adb_mutex_lock(&transport_lock);
-    for (auto t : transport_list) {
+    for (auto& t : transport_list) {
         if (t->serial && strcmp(serial, t->serial) == 0) {
             result = t;
             break;
@@ -978,35 +966,18 @@
     return result;
 }
 
-void unregister_transport(atransport *t)
-{
+void kick_all_tcp_devices() {
     adb_mutex_lock(&transport_lock);
-    transport_list.remove(t);
-    adb_mutex_unlock(&transport_lock);
-
-    kick_transport(t);
-    transport_unref(t);
-}
-
-// Unregisters all non-emulator TCP transports.
-void unregister_all_tcp_transports() {
-    adb_mutex_lock(&transport_lock);
-    for (auto it = transport_list.begin(); it != transport_list.end(); ) {
-        atransport* t = *it;
+    for (auto& t : transport_list) {
+        // TCP/IP devices have adb_port == 0.
         if (t->type == kTransportLocal && t->adb_port == 0) {
-            // We cannot call kick_transport when holding transport_lock.
-            if (!t->kicked) {
-                t->kicked = 1;
-                t->kick(t);
-            }
-            transport_unref_locked(t);
-
-            it = transport_list.erase(it);
-        } else {
-            ++it;
+            // Kicking breaks the output thread of this transport out of any read, then
+            // the output thread will notify the main thread to make this transport
+            // offline. Then the main thread will notify the input thread to exit.
+            // Finally, this transport will be closed and freed in the main thread.
+            kick_transport_locked(t);
         }
     }
-
     adb_mutex_unlock(&transport_lock);
 }
 
diff --git a/adb/transport.h b/adb/transport.h
index e809407..abb26a7 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -52,7 +52,7 @@
     int fd = -1;
     int transport_socket = -1;
     fdevent transport_fde;
-    int ref_count = 0;
+    size_t ref_count = 0;
     uint32_t sync_token = 0;
     ConnectionState connection_state = kCsOffline;
     bool online = false;
@@ -120,12 +120,10 @@
 void run_transport_disconnects(atransport* t);
 void update_transports(void);
 
-/* transports are ref-counted
-** get_device_transport does an acquire on your behalf before returning
-*/
 void init_transport_registration(void);
 std::string list_transports(bool long_listing);
 atransport* find_transport(const char* serial);
+void kick_all_tcp_devices();
 
 void register_usb_transport(usb_handle* h, const char* serial,
                             const char* devpath, unsigned writeable);
@@ -136,10 +134,6 @@
 // This should only be used for transports with connection_state == kCsNoPerm.
 void unregister_usb_transport(usb_handle* usb);
 
-/* these should only be used for the "adb disconnect" command */
-void unregister_transport(atransport* t);
-void unregister_all_tcp_transports();
-
 int check_header(apacket* p, atransport* t);
 int check_data(apacket* p);
 
diff --git a/include/log/log.h b/include/log/log.h
index 0b17574..1cdf7bc 100644
--- a/include/log/log.h
+++ b/include/log/log.h
@@ -563,6 +563,12 @@
 #define android_btWriteLog(tag, type, payload, len) \
     __android_log_btwrite(tag, type, payload, len)
 
+#define android_errorWriteLog(tag, subTag) \
+    __android_log_error_write(tag, subTag, -1, NULL, 0)
+
+#define android_errorWriteWithInfoLog(tag, subTag, uid, data, dataLen) \
+    __android_log_error_write(tag, subTag, uid, data, dataLen)
+
 /*
  *    IF_ALOG uses android_testLog, but IF_ALOG can be overridden.
  *    android_testLog will remain constant in its purpose as a wrapper
@@ -612,6 +618,9 @@
  */
 int __android_log_is_loggable(int prio, const char *tag, int def);
 
+int __android_log_error_write(int tag, const char *subTag, int32_t uid, const char *data,
+                              uint32_t dataLen);
+
 /*
  * Send a simple string to the log.
  */
diff --git a/libcutils/arch-mips/android_memset.c b/libcutils/arch-mips/android_memset.c
index a6b7496..c0fe3d1 100644
--- a/libcutils/arch-mips/android_memset.c
+++ b/libcutils/arch-mips/android_memset.c
@@ -30,6 +30,9 @@
 
 #include <cutils/memory.h>
 
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
 void android_memset16(uint16_t* dst, uint16_t value, size_t size)
 {
    /* optimized version of
@@ -46,7 +49,7 @@
    }
    /* dst is now 32-bit-aligned */
    /* fill body with 32-bit pairs */
-   uint32_t value32 = (value << 16) | value;
+   uint32_t value32 = (((uint32_t)value) << 16) | ((uint32_t)value);
    android_memset32((uint32_t*) dst, value32, size<<1);
    if (size & 1) {
       dst[size-1] = value;  /* fill unpaired last elem */
@@ -54,6 +57,9 @@
 }
 
 
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
 void android_memset32(uint32_t* dst, uint32_t value, size_t size)
 {
    /* optimized version of
@@ -70,7 +76,7 @@
    }
    /* dst is now 64-bit aligned */
    /* fill body with 64-bit pairs */
-   uint64_t value64 = (((uint64_t)value)<<32) | value;
+   uint64_t value64 = (((uint64_t)value) << 32) | ((uint64_t)value);
    uint64_t* dst64 = (uint64_t*)dst;
 
    while (size >= 12) {
@@ -86,7 +92,8 @@
 
    /* fill remainder with original 32-bit single-elem loop */
    dst = (uint32_t*) dst64;
-   while (size--) {
+   while (size != 0) {
+       size--;
       *dst++ = value;
    }
 
diff --git a/libcutils/strdup16to8.c b/libcutils/strdup16to8.c
index 1a8ba86..4dc987e 100644
--- a/libcutils/strdup16to8.c
+++ b/libcutils/strdup16to8.c
@@ -55,7 +55,8 @@
     /* Fast path for the usual case where 3*len is < SIZE_MAX-1.
      */
     if (len < (SIZE_MAX-1)/3) {
-        while (len--) {
+        while (len != 0) {
+            len--;
             unsigned int uic = *utf16Str++;
 
             if (uic > 0x07ff)
@@ -69,7 +70,8 @@
     }
 
     /* The slower but paranoid version */
-    while (len--) {
+    while (len != 0) {
+        len--;
         unsigned int  uic     = *utf16Str++;
         size_t        utf8Cur = utf8Len;
 
@@ -112,7 +114,8 @@
      * strnlen16to8() properly or at a minimum checked the result of
      * its malloc(SIZE_MAX) in case of overflow.
      */
-    while (len--) {
+    while (len != 0) {
+        len--;
         unsigned int uic = *utf16Str++;
 
         if (uic > 0x07ff) {
diff --git a/liblog/Android.mk b/liblog/Android.mk
index 930dcf7..5eed634 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -24,7 +24,7 @@
 # so make sure we do not regret hard-coding it as follows:
 liblog_cflags := -DLIBLOG_LOG_TAG=1005
 
-liblog_sources := logd_write.c
+liblog_sources := logd_write.c log_event_write.c
 
 # some files must not be compiled when building against Mingw
 # they correspond to features not used by our host development tools
diff --git a/liblog/log_event_write.c b/liblog/log_event_write.c
new file mode 100644
index 0000000..0bc42d5
--- /dev/null
+++ b/liblog/log_event_write.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 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 <errno.h>
+#include <string.h>
+
+#include <log/log.h>
+#include <log/logger.h>
+
+#define MAX_EVENT_PAYLOAD 512
+#define MAX_SUBTAG_LEN 32
+
+static inline void copy4LE(uint8_t *buf, size_t pos, int val)
+{
+    buf[pos] = val & 0xFF;
+    buf[pos+1] = (val >> 8) & 0xFF;
+    buf[pos+2] = (val >> 16) & 0xFF;
+    buf[pos+3] = (val >> 24) & 0xFF;
+}
+
+int __android_log_error_write(int tag, const char *subTag, int32_t uid, const char *data,
+                              uint32_t dataLen)
+{
+    uint8_t buf[MAX_EVENT_PAYLOAD];
+    size_t pos = 0;
+    uint32_t subTagLen = 0;
+    uint32_t roomLeftForData = 0;
+
+    if ((subTag == NULL) || ((data == NULL) && (dataLen != 0))) return -EINVAL;
+
+    subTagLen = strlen(subTag);
+
+    // Truncate subtags that are too long.
+    subTagLen = subTagLen > MAX_SUBTAG_LEN ? MAX_SUBTAG_LEN : subTagLen;
+
+    // Truncate dataLen if it is too long.
+    roomLeftForData = MAX_EVENT_PAYLOAD -
+            (1 + // EVENT_TYPE_LIST
+             1 + // Number of elements in list
+             1 + // EVENT_TYPE_STRING
+             sizeof(subTagLen) +
+             subTagLen +
+             1 + // EVENT_TYPE_INT
+             sizeof(uid) +
+             1 + // EVENT_TYPE_STRING
+             sizeof(dataLen));
+    dataLen = dataLen > roomLeftForData ? roomLeftForData : dataLen;
+
+    buf[pos++] = EVENT_TYPE_LIST;
+    buf[pos++] = 3; // Number of elements in the list (subTag, uid, data)
+
+    // Write sub tag.
+    buf[pos++] = EVENT_TYPE_STRING;
+    copy4LE(buf, pos, subTagLen);
+    pos += 4;
+    memcpy(&buf[pos], subTag, subTagLen);
+    pos += subTagLen;
+
+    // Write UID.
+    buf[pos++] = EVENT_TYPE_INT;
+    copy4LE(buf, pos, uid);
+    pos += 4;
+
+    // Write data.
+    buf[pos++] = EVENT_TYPE_STRING;
+    copy4LE(buf, pos, dataLen);
+    pos += 4;
+    if (dataLen != 0)
+    {
+        memcpy(&buf[pos], data, dataLen);
+        pos += dataLen;
+    }
+
+    return __android_log_bwrite(tag, buf, pos);
+}
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index abe0239..c987041 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -17,6 +17,7 @@
 #include <fcntl.h>
 #include <inttypes.h>
 #include <signal.h>
+#include <string.h>
 
 #include <cutils/properties.h>
 #include <gtest/gtest.h>
@@ -876,3 +877,398 @@
     property_set(key, hold[2]);
     property_set(key + base_offset, hold[3]);
 }
+
+static inline int32_t get4LE(const char* src)
+{
+    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__typical) {
+    const int TAG = 123456781;
+    const char SUBTAG[] = "test-subtag";
+    const int UID = -1;
+    const int DATA_LEN = 200;
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    ASSERT_LT(0, android_errorWriteWithInfoLog(
+            TAG, SUBTAG, UID, max_payload_buf, DATA_LEN));
+
+    sleep(2);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        char *eventData = log_msg.msg();
+
+        // Tag
+        int tag = get4LE(eventData);
+        eventData += 4;
+
+        if (tag != TAG) {
+            continue;
+        }
+
+        // List type
+        ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
+        eventData++;
+
+        // Number of elements in list
+        ASSERT_EQ(3, eventData[0]);
+        eventData++;
+
+        // Element #1: string type for subtag
+        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+        eventData++;
+
+        ASSERT_EQ((int) strlen(SUBTAG), get4LE(eventData));
+        eventData +=4;
+
+        if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) {
+            continue;
+        }
+        eventData += strlen(SUBTAG);
+
+        // Element #2: int type for uid
+        ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
+        eventData++;
+
+        ASSERT_EQ(UID, get4LE(eventData));
+        eventData += 4;
+
+        // Element #3: string type for data
+        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+        eventData++;
+
+        ASSERT_EQ(DATA_LEN, get4LE(eventData));
+        eventData += 4;
+
+        if (memcmp(max_payload_buf, eventData, DATA_LEN)) {
+            continue;
+        }
+
+        ++count;
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__data_too_large) {
+    const int TAG = 123456782;
+    const char SUBTAG[] = "test-subtag";
+    const int UID = -1;
+    const int DATA_LEN = sizeof(max_payload_buf);
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    ASSERT_LT(0, android_errorWriteWithInfoLog(
+            TAG, SUBTAG, UID, max_payload_buf, DATA_LEN));
+
+    sleep(2);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        char *eventData = log_msg.msg();
+        char *original = eventData;
+
+        // Tag
+        int tag = get4LE(eventData);
+        eventData += 4;
+
+        if (tag != TAG) {
+            continue;
+        }
+
+        // List type
+        ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
+        eventData++;
+
+        // Number of elements in list
+        ASSERT_EQ(3, eventData[0]);
+        eventData++;
+
+        // Element #1: string type for subtag
+        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+        eventData++;
+
+        ASSERT_EQ((int) strlen(SUBTAG), get4LE(eventData));
+        eventData +=4;
+
+        if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) {
+            continue;
+        }
+        eventData += strlen(SUBTAG);
+
+        // Element #2: int type for uid
+        ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
+        eventData++;
+
+        ASSERT_EQ(UID, get4LE(eventData));
+        eventData += 4;
+
+        // Element #3: string type for data
+        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+        eventData++;
+
+        size_t dataLen = get4LE(eventData);
+        eventData += 4;
+
+        if (memcmp(max_payload_buf, eventData, dataLen)) {
+            continue;
+        }
+        eventData += dataLen;
+
+        // 4 bytes for the tag, and 512 bytes for the log since the max_payload_buf should be
+        // truncated.
+        ASSERT_EQ(4 + 512, eventData - original);
+
+        ++count;
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__null_data) {
+    const int TAG = 123456783;
+    const char SUBTAG[] = "test-subtag";
+    const int UID = -1;
+    const int DATA_LEN = 200;
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    ASSERT_GT(0, android_errorWriteWithInfoLog(
+            TAG, SUBTAG, UID, NULL, DATA_LEN));
+
+    sleep(2);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        char *eventData = log_msg.msg();
+
+        // Tag
+        int tag = get4LE(eventData);
+        eventData += 4;
+
+        if (tag == TAG) {
+            // This tag should not have been written because the data was null
+            count++;
+            break;
+        }
+    }
+
+    EXPECT_EQ(0, count);
+
+    android_logger_list_close(logger_list);
+}
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__subtag_too_long) {
+    const int TAG = 123456784;
+    const char SUBTAG[] = "abcdefghijklmnopqrstuvwxyz now i know my abc";
+    const int UID = -1;
+    const int DATA_LEN = 200;
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    ASSERT_LT(0, android_errorWriteWithInfoLog(
+            TAG, SUBTAG, UID, max_payload_buf, DATA_LEN));
+
+    sleep(2);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        char *eventData = log_msg.msg();
+
+        // Tag
+        int tag = get4LE(eventData);
+        eventData += 4;
+
+        if (tag != TAG) {
+            continue;
+        }
+
+        // List type
+        ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
+        eventData++;
+
+        // Number of elements in list
+        ASSERT_EQ(3, eventData[0]);
+        eventData++;
+
+        // Element #1: string type for subtag
+        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+        eventData++;
+
+        // The subtag is longer than 32 and should be truncated to that.
+        ASSERT_EQ(32, get4LE(eventData));
+        eventData +=4;
+
+        if (memcmp(SUBTAG, eventData, 32)) {
+            continue;
+        }
+        eventData += 32;
+
+        // Element #2: int type for uid
+        ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
+        eventData++;
+
+        ASSERT_EQ(UID, get4LE(eventData));
+        eventData += 4;
+
+        // Element #3: string type for data
+        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+        eventData++;
+
+        ASSERT_EQ(DATA_LEN, get4LE(eventData));
+        eventData += 4;
+
+        if (memcmp(max_payload_buf, eventData, DATA_LEN)) {
+            continue;
+        }
+
+        ++count;
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
+TEST(liblog, android_errorWriteLog__android_logger_list_read__success) {
+    const int TAG = 123456785;
+    const char SUBTAG[] = "test-subtag";
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    ASSERT_LT(0, android_errorWriteLog(TAG, SUBTAG));
+
+    sleep(2);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        char *eventData = log_msg.msg();
+
+        // Tag
+        int tag = get4LE(eventData);
+        eventData += 4;
+
+        if (tag != TAG) {
+            continue;
+        }
+
+        // List type
+        ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
+        eventData++;
+
+        // Number of elements in list
+        ASSERT_EQ(3, eventData[0]);
+        eventData++;
+
+        // Element #1: string type for subtag
+        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+        eventData++;
+
+        ASSERT_EQ((int) strlen(SUBTAG), get4LE(eventData));
+        eventData +=4;
+
+        if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) {
+            continue;
+        }
+        ++count;
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
+TEST(liblog, android_errorWriteLog__android_logger_list_read__null_subtag) {
+    const int TAG = 123456786;
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    ASSERT_GT(0, android_errorWriteLog(TAG, NULL));
+
+    sleep(2);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        char *eventData = log_msg.msg();
+
+        // Tag
+        int tag = get4LE(eventData);
+        eventData += 4;
+
+        if (tag == TAG) {
+            // This tag should not have been written because the data was null
+            count++;
+            break;
+        }
+    }
+
+    EXPECT_EQ(0, count);
+
+    android_logger_list_close(logger_list);
+}
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index bdb54b1..2f770f5 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -198,7 +198,10 @@
                     _do_copy(next, curr, 1);
                     next = curr;
                     --j;
-                    curr = reinterpret_cast<char*>(array) + mItemSize*(j);                    
+                    curr = NULL;
+                    if (j >= 0) {
+                        curr = reinterpret_cast<char*>(array) + mItemSize*(j);
+                    }
                 } while (j>=0 && (cmp(curr, temp, state) > 0));
 
                 _do_destroy(next, 1);
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 1e6f55f..febf775 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -48,7 +48,7 @@
     char c;
     while (((c = *s++)) && (++len <= max_prio_len)) {
         if (!isdigit(c)) {
-            return (c == '>') ? s : NULL;
+            return ((c == '>') && (*s == '[')) ? s : NULL;
         }
     }
     return NULL;
@@ -294,6 +294,22 @@
     }
 }
 
+pid_t LogKlog::sniffPid(const char *cp) {
+    while (*cp) {
+        // Mediatek kernels with modified printk
+        if (*cp == '[') {
+            int pid = 0;
+            char dummy;
+            if (sscanf(cp, "[%d:%*[a-z_./0-9:A-Z]]%c", &pid, &dummy) == 2) {
+                return pid;
+            }
+            break; // Only the first one
+        }
+        ++cp;
+    }
+    return 0;
+}
+
 // Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
 // compensated start time.
 void LogKlog::synchronize(const char *buf) {
@@ -417,9 +433,9 @@
 
     // sniff for start marker
     const char klogd_message[] = "logd.klogd: ";
-    if (!strncmp(buf, klogd_message, sizeof(klogd_message) - 1)) {
-        char *endp;
-        uint64_t sig = strtoll(buf + sizeof(klogd_message) - 1, &endp, 10);
+    const char *start = strstr(buf, klogd_message);
+    if (start) {
+        uint64_t sig = strtoll(start + sizeof(klogd_message) - 1, NULL, 10);
         if (sig == signature.nsec()) {
             if (initialized) {
                 enableLogging = true;
@@ -435,10 +451,10 @@
         return 0;
     }
 
-    // Parse pid, tid and uid (not possible)
-    const pid_t pid = 0;
-    const pid_t tid = 0;
-    const uid_t uid = 0;
+    // Parse pid, tid and uid
+    const pid_t pid = sniffPid(buf);
+    const pid_t tid = pid;
+    const uid_t uid = pid ? logbuf->pidToUid(pid) : 0;
 
     // Parse (rules at top) to pull out a tag from the incoming kernel message.
     // Some may view the following as an ugly heuristic, the desire is to
@@ -450,7 +466,7 @@
     if (!*buf) {
         return 0;
     }
-    const char *start = buf;
+    start = buf;
     const char *tag = "";
     const char *etag = tag;
     if (!isspace(*buf)) {
@@ -461,7 +477,14 @@
             // <PRI>[<TIME>] "[INFO]"<tag> ":" message
             bt = buf + 6;
         }
-        for(et = bt; *et && (*et != ':') && !isspace(*et); ++et);
+        for(et = bt; *et && (*et != ':') && !isspace(*et); ++et) {
+           // skip ':' within [ ... ]
+           if (*et == '[') {
+               while (*et && *et != ']') {
+                   ++et;
+               }
+            }
+        }
         for(cp = et; isspace(*cp); ++cp);
         size_t size;
 
@@ -557,7 +580,17 @@
             etag = tag = "";
         }
     }
-    size_t l = etag - tag;
+    // Suppress additional stutter in tag:
+    //   eg: [143:healthd]healthd -> [143:healthd]
+    size_t taglen = etag - tag;
+    // Mediatek-special printk induced stutter
+    char *np = strrchr(tag, ']');
+    if (np && (++np < etag)) {
+        size_t s = etag - np;
+        if (((s + s) < taglen) && !strncmp(np, np - 1 - s, s)) {
+            taglen = np - tag;
+        }
+    }
     // skip leading space
     while (isspace(*buf)) {
         ++buf;
@@ -568,11 +601,11 @@
         --b;
     }
     // trick ... allow tag with empty content to be logged. log() drops empty
-    if (!b && l) {
+    if (!b && taglen) {
         buf = " ";
         b = 1;
     }
-    size_t n = 1 + l + 1 + b + 1;
+    size_t n = 1 + taglen + 1 + b + 1;
 
     // Allocate a buffer to hold the interpreted log message
     int rc = n;
@@ -581,15 +614,15 @@
         rc = -ENOMEM;
         return rc;
     }
-    char *np = newstr;
+    np = newstr;
 
     // Convert priority into single-byte Android logger priority
     *np = convertKernelPrioToAndroidPrio(pri);
     ++np;
 
     // Copy parsed tag following priority
-    strncpy(np, tag, l);
-    np += l;
+    strncpy(np, tag, taglen);
+    np += taglen;
     *np = '\0';
     ++np;
 
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index 24b2685..7e4fde0 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -47,6 +47,7 @@
 
 protected:
     void sniffTime(log_time &now, const char **buf, bool reverse);
+    pid_t sniffPid(const char *buf);
     void calculateCorrection(const log_time &monotonic, const char *real_string);
     virtual bool onDataAvailable(SocketClient *cli);
 
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index 9fd8eda..89fa222 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -110,6 +110,7 @@
   libprotobuf-cpp-lite \
   libchromeos-http \
   libchromeos-dbus \
+  libcutils \
   libdbus
 LOCAL_SRC_FILES := $(metrics_daemon_sources)
 LOCAL_STATIC_LIBRARIES := metrics_daemon_protos
diff --git a/metricsd/constants.h b/metricsd/constants.h
index 56dac0d..15c15d9 100644
--- a/metricsd/constants.h
+++ b/metricsd/constants.h
@@ -24,6 +24,11 @@
 static const char kMetricsServer[] = "https://clients4.google.com/uma/v2";
 static const char kConsentFilePath[] = "/data/misc/metrics/enabled";
 static const char kDefaultVersion[] = "0.0.0.0";
+
+// System properties used.
+static const char kBuildTargetIdProperty[] = "ro.product.build_target_id";
+static const char kChannelProperty[] = "ro.product.channel";
+static const char kProductVersionProperty[] = "ro.product.version";
 }  // namespace metrics
 
 #endif  // METRICS_CONSTANTS_H_
diff --git a/metricsd/metrics_daemon.cc b/metricsd/metrics_daemon.cc
index 069f68e..5855cee 100644
--- a/metricsd/metrics_daemon.cc
+++ b/metricsd/metrics_daemon.cc
@@ -32,7 +32,7 @@
 #include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <base/sys_info.h>
+#include <cutils/properties.h>
 #include <dbus/dbus.h>
 #include <dbus/message.h>
 
@@ -209,10 +209,13 @@
   if (version_hash_is_cached)
     return cached_version_hash;
   version_hash_is_cached = true;
-  std::string version = metrics::kDefaultVersion;
+
+  char version[PROPERTY_VALUE_MAX];
   // The version might not be set for development devices. In this case, use the
   // zero version.
-  base::SysInfo::GetLsbReleaseValue("BRILLO_VERSION", &version);
+  property_get(metrics::kProductVersionProperty, version,
+               metrics::kDefaultVersion);
+
   cached_version_hash = base::Hash(version);
   if (testing_) {
     cached_version_hash = 42;  // return any plausible value for the hash
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
index 7dd0323..21ec229 100644
--- a/metricsd/uploader/system_profile_cache.cc
+++ b/metricsd/uploader/system_profile_cache.cc
@@ -21,7 +21,7 @@
 #include <base/logging.h>
 #include <base/strings/string_number_conversions.h>
 #include <base/strings/string_util.h>
-#include <base/sys_info.h>
+#include <cutils/properties.h>
 #include <string>
 #include <vector>
 
@@ -73,21 +73,28 @@
   CHECK(!initialized_)
       << "this should be called only once in the metrics_daemon lifetime.";
 
-  if (!base::SysInfo::GetLsbReleaseValue("BRILLO_BUILD_TARGET_ID",
-                                         &profile_.build_target_id)) {
-    LOG(ERROR) << "BRILLO_BUILD_TARGET_ID is not set in /etc/lsb-release.";
+  char property_value[PROPERTY_VALUE_MAX];
+  property_get(metrics::kBuildTargetIdProperty, property_value, "");
+  profile_.build_target_id = std::string(property_value);
+
+  if (profile_.build_target_id.empty()) {
+    LOG(ERROR) << "System property " << metrics::kBuildTargetIdProperty
+               << " is not set.";
     return false;
   }
 
-  std::string channel;
-  if (!base::SysInfo::GetLsbReleaseValue("BRILLO_CHANNEL", &channel) ||
-      !base::SysInfo::GetLsbReleaseValue("BRILLO_VERSION", &profile_.version)) {
+  property_get(metrics::kChannelProperty, property_value, "");
+  std::string channel(property_value);
+
+  property_get(metrics::kProductVersionProperty, property_value, "");
+  profile_.version = std::string(property_value);
+
+  if (channel.empty() || profile_.version.empty()) {
     // If the channel or version is missing, the image is not official.
     // In this case, set the channel to unknown and the version to 0.0.0.0 to
     // avoid polluting the production data.
     channel = "";
     profile_.version = metrics::kDefaultVersion;
-
   }
   profile_.client_id =
       testing_ ? "client_id_test" :