Create libstatssocket_q

Break up libstatssocket into libstatssocket_q and libstatssocket.
libstatssocket_q is for Q Mainline modules.

Bug: 145569088
Test: m -j
Test: bit statsd_test:*
Test: adb shell cmd stats print-logs && adb logcat "*:S statsd:*"
Change-Id: I9d113b37640345ebad6aa269ab710db0d2179671
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
new file mode 100644
index 0000000..b7c07b6
--- /dev/null
+++ b/libstats/socket/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.
+//
+
+// =========================================================================
+// Native library to write stats log to statsd socket on Android R and later
+// =========================================================================
+cc_library {
+    name: "libstatssocket",
+    srcs: [
+        "stats_event.c",
+        "stats_event_list.c",
+        "statsd_writer.c",
+    ],
+    host_supported: true,
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-DLIBLOG_LOG_TAG=1006",
+        "-DWRITE_TO_STATSD=1",
+        "-DWRITE_TO_LOGD=0",
+    ],
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+    ],
+}
diff --git a/libstats/socket/include/stats_event.h b/libstats/socket/include/stats_event.h
new file mode 100644
index 0000000..89cb420
--- /dev/null
+++ b/libstats/socket/include/stats_event.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STATS_LOG_STATS_EVENT_H
+#define ANDROID_STATS_LOG_STATS_EVENT_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * Functionality to build and store the buffer sent over the statsd socket.
+ * This code defines and encapsulates the socket protocol.
+ *
+ * Usage:
+ *      struct stats_event* event = stats_event_obtain();
+ *
+ *      stats_event_set_atom_id(event, atomId);
+ *      stats_event_write_int32(event, 24);
+ *      stats_event_add_bool_annotation(event, 1, true); // annotations apply to the previous field
+ *      stats_event_add_int32_annotation(event, 2, 128);
+ *      stats_event_write_float(event, 2.0);
+ *
+ *      stats_event_build(event);
+ *      stats_event_write(event);
+ *      stats_event_release(event);
+ *
+ * Notes:
+ *    (a) write_<type>() and add_<type>_annotation() should be called in the order that fields
+ *        and annotations are defined in the atom.
+ *    (b) set_atom_id() can be called anytime before stats_event_write().
+ *    (c) add_<type>_annotation() calls apply to the previous field.
+ *    (d) If errors occur, stats_event_write() will write a bitmask of the errors to the socket.
+ *    (e) All strings should be encoded using UTF8.
+ */
+
+/* ERRORS */
+#define ERROR_NO_TIMESTAMP 0x1
+#define ERROR_NO_ATOM_ID 0x2
+#define ERROR_OVERFLOW 0x4
+#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
+#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
+#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
+#define ERROR_INVALID_ANNOTATION_ID 0x40
+#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
+#define ERROR_TOO_MANY_ANNOTATIONS 0x100
+#define ERROR_TOO_MANY_FIELDS 0x200
+#define ERROR_INVALID_VALUE_TYPE 0x400
+#define ERROR_STRING_NOT_NULL_TERMINATED 0x800
+
+/* TYPE IDS */
+#define INT32_TYPE 0x00
+#define INT64_TYPE 0x01
+#define STRING_TYPE 0x02
+#define LIST_TYPE 0x03
+#define FLOAT_TYPE 0x04
+#define BOOL_TYPE 0x05
+#define BYTE_ARRAY_TYPE 0x06
+#define OBJECT_TYPE 0x07
+#define KEY_VALUE_PAIRS_TYPE 0x08
+#define ATTRIBUTION_CHAIN_TYPE 0x09
+#define ERROR_TYPE 0x0F
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct stats_event;
+
+/* SYSTEM API */
+struct stats_event* stats_event_obtain();
+// The build function can be called multiple times without error. If the event
+// has been built before, this function is a no-op.
+void stats_event_build(struct stats_event* event);
+void stats_event_write(struct stats_event* event);
+void stats_event_release(struct stats_event* event);
+
+void stats_event_set_atom_id(struct stats_event* event, uint32_t atomId);
+
+void stats_event_write_int32(struct stats_event* event, int32_t value);
+void stats_event_write_int64(struct stats_event* event, int64_t value);
+void stats_event_write_float(struct stats_event* event, float value);
+void stats_event_write_bool(struct stats_event* event, bool value);
+
+void stats_event_write_byte_array(struct stats_event* event, uint8_t* buf, size_t numBytes);
+
+// Buf must be null-terminated.
+void stats_event_write_string8(struct stats_event* event, const char* buf);
+
+// Tags must be null-terminated.
+void stats_event_write_attribution_chain(struct stats_event* event, uint32_t* uids,
+                                         const char** tags, uint8_t numNodes);
+
+/* key_value_pair struct can be constructed as follows:
+ *    struct key_value_pair pair = {.key = key, .valueType = STRING_TYPE,
+ *                                  .stringValue = buf};
+ */
+struct key_value_pair {
+    int32_t key;
+    uint8_t valueType;  // expected to be INT32_TYPE, INT64_TYPE, FLOAT_TYPE, or STRING_TYPE
+    union {
+        int32_t int32Value;
+        int64_t int64Value;
+        float floatValue;
+        const char* stringValue;  // must be null terminated
+    };
+};
+
+void stats_event_write_key_value_pairs(struct stats_event* event, struct key_value_pair* pairs,
+                                       uint8_t numPairs);
+
+void stats_event_add_bool_annotation(struct stats_event* event, uint8_t annotationId, bool value);
+void stats_event_add_int32_annotation(struct stats_event* event, uint8_t annotationId,
+                                      int32_t value);
+
+uint32_t stats_event_get_atom_id(struct stats_event* event);
+uint8_t* stats_event_get_buffer(struct stats_event* event, size_t* size);
+uint32_t stats_event_get_errors(struct stats_event* event);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // ANDROID_STATS_LOG_STATS_EVENT_H
diff --git a/libstats/socket/include/stats_event_list.h b/libstats/socket/include/stats_event_list.h
new file mode 100644
index 0000000..b7ada0c
--- /dev/null
+++ b/libstats/socket/include/stats_event_list.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <log/log_event_list.h>
+#include <sys/uio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void reset_log_context(android_log_context ctx);
+int write_to_logger(android_log_context context, log_id_t id);
+void note_log_drop(int error, int atom_tag);
+void stats_log_close();
+int android_log_write_char_array(android_log_context ctx, const char* value, size_t len);
+extern int (*write_to_statsd)(struct iovec* vec, size_t nr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef __cplusplus
+/**
+ * A copy of android_log_event_list class.
+ *
+ * android_log_event_list is going to be deprecated soon, so copy it here to
+ * avoid creating dependency on upstream code. TODO(b/78304629): Rewrite this
+ * code.
+ */
+class stats_event_list {
+  private:
+    android_log_context ctx;
+    int ret;
+
+    stats_event_list(const stats_event_list&) = delete;
+    void operator=(const stats_event_list&) = delete;
+
+  public:
+    explicit stats_event_list(int tag) : ret(0) {
+        ctx = create_android_logger(static_cast<uint32_t>(tag));
+    }
+    ~stats_event_list() { android_log_destroy(&ctx); }
+
+    int close() {
+        int retval = android_log_destroy(&ctx);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return retval;
+    }
+
+    /* To allow above C calls to use this class as parameter */
+    operator android_log_context() const { return ctx; }
+
+    /* return errors or transmit status */
+    int status() const { return ret; }
+
+    int begin() {
+        int retval = android_log_write_list_begin(ctx);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret;
+    }
+    int end() {
+        int retval = android_log_write_list_end(ctx);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret;
+    }
+
+    stats_event_list& operator<<(int32_t value) {
+        int retval = android_log_write_int32(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(uint32_t value) {
+        int retval = android_log_write_int32(ctx, static_cast<int32_t>(value));
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(bool value) {
+        int retval = android_log_write_int32(ctx, value ? 1 : 0);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(int64_t value) {
+        int retval = android_log_write_int64(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(uint64_t value) {
+        int retval = android_log_write_int64(ctx, static_cast<int64_t>(value));
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(const char* value) {
+        int retval = android_log_write_string8(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(const std::string& value) {
+        int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(float value) {
+        int retval = android_log_write_float32(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    int write(log_id_t id = LOG_ID_EVENTS) {
+        /* facilitate -EBUSY retry */
+        if ((ret == -EBUSY) || (ret > 0)) {
+            ret = 0;
+        }
+        int retval = write_to_logger(ctx, id);
+        /* existing errors trump transmission errors */
+        if (!ret) {
+            ret = retval;
+        }
+        return ret;
+    }
+
+    /*
+     * Append<Type> methods removes any integer promotion
+     * confusion, and adds access to string with length.
+     * Append methods are also added for all types for
+     * convenience.
+     */
+
+    bool AppendInt(int32_t value) {
+        int retval = android_log_write_int32(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendLong(int64_t value) {
+        int retval = android_log_write_int64(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendString(const char* value) {
+        int retval = android_log_write_string8(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendString(const char* value, size_t len) {
+        int retval = android_log_write_string8_len(ctx, value, len);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendString(const std::string& value) {
+        int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret;
+    }
+
+    bool Append(const std::string& value) {
+        int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret;
+    }
+
+    bool AppendFloat(float value) {
+        int retval = android_log_write_float32(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    template <typename Tvalue>
+    bool Append(Tvalue value) {
+        *this << value;
+        return ret >= 0;
+    }
+
+    bool Append(const char* value, size_t len) {
+        int retval = android_log_write_string8_len(ctx, value, len);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendCharArray(const char* value, size_t len) {
+        int retval = android_log_write_char_array(ctx, value, len);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+};
+
+#endif
diff --git a/libstats/socket/stats_event.c b/libstats/socket/stats_event.c
new file mode 100644
index 0000000..35081dc
--- /dev/null
+++ b/libstats/socket/stats_event.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/stats_event.h"
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "include/stats_event_list.h"
+
+#define STATS_EVENT_TAG 1937006964
+#define LOGGER_ENTRY_MAX_PAYLOAD 4068
+// Max payload size is 4 bytes less as 4 bytes are reserved for stats_eventTag.
+// See android_util_Stats_Log.cpp
+#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - 4)
+
+/* POSITIONS */
+#define POS_NUM_ELEMENTS 1
+#define POS_TIMESTAMP (POS_NUM_ELEMENTS + sizeof(uint8_t))
+#define POS_ATOM_ID (POS_TIMESTAMP + sizeof(uint8_t) + sizeof(uint64_t))
+#define POS_FIRST_FIELD (POS_ATOM_ID + sizeof(uint8_t) + sizeof(uint32_t))
+
+/* LIMITS */
+#define MAX_ANNOTATION_COUNT 15
+#define MAX_BYTE_VALUE 127  // parsing side requires that lengths fit in 7 bits
+
+// The stats_event struct holds the serialized encoding of an event
+// within a buf. Also includes other required fields.
+struct stats_event {
+    uint8_t buf[MAX_EVENT_PAYLOAD];
+    size_t lastFieldPos;  // location of last field within the buf
+    size_t size;          // number of valid bytes within buffer
+    uint32_t numElements;
+    uint32_t atomId;
+    uint32_t errors;
+    uint32_t tag;
+    bool built;
+};
+
+static int64_t get_elapsed_realtime_ns() {
+    struct timespec t;
+    t.tv_sec = t.tv_nsec = 0;
+    clock_gettime(CLOCK_BOOTTIME, &t);
+    return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
+}
+
+struct stats_event* stats_event_obtain() {
+    struct stats_event* event = malloc(sizeof(struct stats_event));
+
+    memset(event->buf, 0, MAX_EVENT_PAYLOAD);
+    event->buf[0] = OBJECT_TYPE;
+    event->atomId = 0;
+    event->errors = 0;
+    event->tag = STATS_EVENT_TAG;
+    event->built = false;
+
+    // place the timestamp
+    uint64_t timestampNs = get_elapsed_realtime_ns();
+    event->buf[POS_TIMESTAMP] = INT64_TYPE;
+    memcpy(&event->buf[POS_TIMESTAMP + sizeof(uint8_t)], &timestampNs, sizeof(timestampNs));
+
+    event->numElements = 1;
+    event->lastFieldPos = 0;  // 0 since we haven't written a field yet
+    event->size = POS_FIRST_FIELD;
+
+    return event;
+}
+
+void stats_event_release(struct stats_event* event) {
+    free(event);
+}
+
+void stats_event_set_atom_id(struct stats_event* event, uint32_t atomId) {
+    event->atomId = atomId;
+    event->buf[POS_ATOM_ID] = INT32_TYPE;
+    memcpy(&event->buf[POS_ATOM_ID + sizeof(uint8_t)], &atomId, sizeof(atomId));
+    event->numElements++;
+}
+
+// Side-effect: modifies event->errors if the buffer would overflow
+static bool overflows(struct stats_event* event, size_t size) {
+    if (event->size + size > MAX_EVENT_PAYLOAD) {
+        event->errors |= ERROR_OVERFLOW;
+        return true;
+    }
+    return false;
+}
+
+// Side-effect: all append functions increment event->size if there is
+// sufficient space within the buffer to place the value
+static void append_byte(struct stats_event* event, uint8_t value) {
+    if (!overflows(event, sizeof(value))) {
+        event->buf[event->size] = value;
+        event->size += sizeof(value);
+    }
+}
+
+static void append_bool(struct stats_event* event, bool value) {
+    append_byte(event, (uint8_t)value);
+}
+
+static void append_int32(struct stats_event* event, int32_t value) {
+    if (!overflows(event, sizeof(value))) {
+        memcpy(&event->buf[event->size], &value, sizeof(value));
+        event->size += sizeof(value);
+    }
+}
+
+static void append_int64(struct stats_event* event, int64_t value) {
+    if (!overflows(event, sizeof(value))) {
+        memcpy(&event->buf[event->size], &value, sizeof(value));
+        event->size += sizeof(value);
+    }
+}
+
+static void append_float(struct stats_event* event, float value) {
+    if (!overflows(event, sizeof(value))) {
+        memcpy(&event->buf[event->size], &value, sizeof(value));
+        event->size += sizeof(float);
+    }
+}
+
+static void append_byte_array(struct stats_event* event, uint8_t* buf, size_t size) {
+    if (!overflows(event, size)) {
+        memcpy(&event->buf[event->size], buf, size);
+        event->size += size;
+    }
+}
+
+// Side-effect: modifies event->errors if buf is not properly null-terminated
+static void append_string(struct stats_event* event, const char* buf) {
+    size_t size = strnlen(buf, MAX_EVENT_PAYLOAD);
+    if (event->errors) {
+        event->errors |= ERROR_STRING_NOT_NULL_TERMINATED;
+        return;
+    }
+
+    append_int32(event, size);
+    append_byte_array(event, (uint8_t*)buf, size);
+}
+
+static void start_field(struct stats_event* event, uint8_t typeId) {
+    event->lastFieldPos = event->size;
+    append_byte(event, typeId);
+    event->numElements++;
+}
+
+void stats_event_write_int32(struct stats_event* event, int32_t value) {
+    if (event->errors) return;
+
+    start_field(event, INT32_TYPE);
+    append_int32(event, value);
+}
+
+void stats_event_write_int64(struct stats_event* event, int64_t value) {
+    if (event->errors) return;
+
+    start_field(event, INT64_TYPE);
+    append_int64(event, value);
+}
+
+void stats_event_write_float(struct stats_event* event, float value) {
+    if (event->errors) return;
+
+    start_field(event, FLOAT_TYPE);
+    append_float(event, value);
+}
+
+void stats_event_write_bool(struct stats_event* event, bool value) {
+    if (event->errors) return;
+
+    start_field(event, BOOL_TYPE);
+    append_bool(event, value);
+}
+
+void stats_event_write_byte_array(struct stats_event* event, uint8_t* buf, size_t numBytes) {
+    if (event->errors) return;
+
+    start_field(event, BYTE_ARRAY_TYPE);
+    append_int32(event, numBytes);
+    append_byte_array(event, buf, numBytes);
+}
+
+// Buf is assumed to be encoded using UTF8
+void stats_event_write_string8(struct stats_event* event, const char* buf) {
+    if (event->errors) return;
+
+    start_field(event, STRING_TYPE);
+    append_string(event, buf);
+}
+
+// Tags are assumed to be encoded using UTF8
+void stats_event_write_attribution_chain(struct stats_event* event, uint32_t* uids,
+                                         const char** tags, uint8_t numNodes) {
+    if (numNodes > MAX_BYTE_VALUE) event->errors |= ERROR_ATTRIBUTION_CHAIN_TOO_LONG;
+    if (event->errors) return;
+
+    start_field(event, ATTRIBUTION_CHAIN_TYPE);
+    append_byte(event, numNodes);
+
+    for (uint8_t i = 0; i < numNodes; i++) {
+        append_int32(event, uids[i]);
+        append_string(event, tags[i]);
+    }
+}
+
+void stats_event_write_key_value_pairs(struct stats_event* event, struct key_value_pair* pairs,
+                                       uint8_t numPairs) {
+    if (numPairs > MAX_BYTE_VALUE) event->errors |= ERROR_TOO_MANY_KEY_VALUE_PAIRS;
+    if (event->errors) return;
+
+    start_field(event, KEY_VALUE_PAIRS_TYPE);
+    append_byte(event, numPairs);
+
+    for (uint8_t i = 0; i < numPairs; i++) {
+        append_int32(event, pairs[i].key);
+        append_byte(event, pairs[i].valueType);
+        switch (pairs[i].valueType) {
+            case INT32_TYPE:
+                append_int32(event, pairs[i].int32Value);
+                break;
+            case INT64_TYPE:
+                append_int64(event, pairs[i].int64Value);
+                break;
+            case FLOAT_TYPE:
+                append_float(event, pairs[i].floatValue);
+                break;
+            case STRING_TYPE:
+                append_string(event, pairs[i].stringValue);
+                break;
+            default:
+                event->errors |= ERROR_INVALID_VALUE_TYPE;
+                return;
+        }
+    }
+}
+
+// Side-effect: modifies event->errors if field has too many annotations
+static void increment_annotation_count(struct stats_event* event) {
+    uint8_t fieldType = event->buf[event->lastFieldPos] & 0x0F;
+    uint32_t oldAnnotationCount = (event->buf[event->lastFieldPos] & 0xF0) >> 4;
+    uint32_t newAnnotationCount = oldAnnotationCount + 1;
+
+    if (newAnnotationCount > MAX_ANNOTATION_COUNT) {
+        event->errors |= ERROR_TOO_MANY_ANNOTATIONS;
+        return;
+    }
+
+    event->buf[event->lastFieldPos] = (((uint8_t)newAnnotationCount << 4) & 0xF0) | fieldType;
+}
+
+void stats_event_add_bool_annotation(struct stats_event* event, uint8_t annotationId, bool value) {
+    if (event->lastFieldPos == 0) event->errors |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
+    if (annotationId > MAX_BYTE_VALUE) event->errors |= ERROR_ANNOTATION_ID_TOO_LARGE;
+    if (event->errors) return;
+
+    append_byte(event, annotationId);
+    append_byte(event, BOOL_TYPE);
+    append_bool(event, value);
+    increment_annotation_count(event);
+}
+
+void stats_event_add_int32_annotation(struct stats_event* event, uint8_t annotationId,
+                                      int32_t value) {
+    if (event->lastFieldPos == 0) event->errors |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
+    if (annotationId > MAX_BYTE_VALUE) event->errors |= ERROR_ANNOTATION_ID_TOO_LARGE;
+    if (event->errors) return;
+
+    append_byte(event, annotationId);
+    append_byte(event, INT32_TYPE);
+    append_int32(event, value);
+    increment_annotation_count(event);
+}
+
+uint32_t stats_event_get_atom_id(struct stats_event* event) {
+    return event->atomId;
+}
+
+uint8_t* stats_event_get_buffer(struct stats_event* event, size_t* size) {
+    if (size) *size = event->size;
+    return event->buf;
+}
+
+uint32_t stats_event_get_errors(struct stats_event* event) {
+    return event->errors;
+}
+
+void stats_event_build(struct stats_event* event) {
+    if (event->built) return;
+
+    if (event->atomId == 0) event->errors |= ERROR_NO_ATOM_ID;
+
+    if (event->numElements > MAX_BYTE_VALUE) {
+        event->errors |= ERROR_TOO_MANY_FIELDS;
+    } else {
+        event->buf[POS_NUM_ELEMENTS] = event->numElements;
+    }
+
+    // If there are errors, rewrite buffer.
+    if (event->errors) {
+        event->buf[POS_NUM_ELEMENTS] = 3;
+        event->buf[POS_FIRST_FIELD] = ERROR_TYPE;
+        memcpy(&event->buf[POS_FIRST_FIELD + sizeof(uint8_t)], &event->errors,
+               sizeof(event->errors));
+        event->size = POS_FIRST_FIELD + sizeof(uint8_t) + sizeof(uint32_t);
+    }
+
+    event->built = true;
+}
+
+void stats_event_write(struct stats_event* event) {
+    stats_event_build(event);
+
+    // Prepare iovecs for write to statsd.
+    struct iovec vecs[2];
+    vecs[0].iov_base = &event->tag;
+    vecs[0].iov_len = sizeof(event->tag);
+    vecs[1].iov_base = &event->buf;
+    vecs[1].iov_len = event->size;
+    write_to_statsd(vecs, 2);
+}
diff --git a/libstats/socket/stats_event_list.c b/libstats/socket/stats_event_list.c
new file mode 100644
index 0000000..ae12cbe
--- /dev/null
+++ b/libstats/socket/stats_event_list.c
@@ -0,0 +1,239 @@
+/*
+ * 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 "include/stats_event_list.h"
+
+#include <string.h>
+#include <sys/time.h>
+#include "statsd_writer.h"
+
+#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
+
+typedef struct {
+    uint32_t tag;
+    unsigned pos;                                    /* Read/write position into buffer */
+    unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements   */
+    unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1];  /* pos for list counter */
+    unsigned list_nest_depth;
+    unsigned len; /* Length or raw buffer. */
+    bool overflow;
+    bool list_stop; /* next call decrement list_nest_depth and issue a stop */
+    enum {
+        kAndroidLoggerRead = 1,
+        kAndroidLoggerWrite = 2,
+    } read_write_flag;
+    uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
+} android_log_context_internal;
+
+extern struct android_log_transport_write statsdLoggerWrite;
+
+static int __write_to_statsd_init(struct iovec* vec, size_t nr);
+int (*write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
+
+// Similar to create_android_logger(), but instead of allocation a new buffer,
+// this function resets the buffer for resuse.
+void reset_log_context(android_log_context ctx) {
+    if (!ctx) {
+        return;
+    }
+    android_log_context_internal* context = (android_log_context_internal*)(ctx);
+    uint32_t tag = context->tag;
+    memset(context, 0, sizeof(android_log_context_internal));
+
+    context->tag = tag;
+    context->read_write_flag = kAndroidLoggerWrite;
+    size_t needed = sizeof(uint8_t) + sizeof(uint8_t);
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        context->overflow = true;
+    }
+    /* Everything is a list */
+    context->storage[context->pos + 0] = EVENT_TYPE_LIST;
+    context->list[0] = context->pos + 1;
+    context->pos += needed;
+}
+
+int stats_write_list(android_log_context ctx) {
+    android_log_context_internal* context;
+    const char* msg;
+    ssize_t len;
+
+    context = (android_log_context_internal*)(ctx);
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+
+    if (context->list_nest_depth) {
+        return -EIO;
+    }
+
+    /* NB: if there was overflow, then log is truncated. Nothing reported */
+    context->storage[1] = context->count[0];
+    len = context->len = context->pos;
+    msg = (const char*)context->storage;
+    /* it's not a list */
+    if (context->count[0] <= 1) {
+        len -= sizeof(uint8_t) + sizeof(uint8_t);
+        if (len < 0) {
+            len = 0;
+        }
+        msg += sizeof(uint8_t) + sizeof(uint8_t);
+    }
+
+    struct iovec vec[2];
+    vec[0].iov_base = &context->tag;
+    vec[0].iov_len = sizeof(context->tag);
+    vec[1].iov_base = (void*)msg;
+    vec[1].iov_len = len;
+    return write_to_statsd(vec, 2);
+}
+
+int write_to_logger(android_log_context ctx, log_id_t id) {
+    int retValue = 0;
+
+    if (WRITE_TO_LOGD) {
+        retValue = android_log_write_list(ctx, id);
+    }
+
+    if (WRITE_TO_STATSD) {
+        // log_event_list's cast operator is overloaded.
+        int ret = stats_write_list(ctx);
+        // In debugging phase, we may write to both logd and statsd. Prefer to
+        // return statsd socket write error code here.
+        if (ret < 0) {
+            retValue = ret;
+        }
+    }
+
+    return retValue;
+}
+
+void note_log_drop(int error, int tag) {
+    statsdLoggerWrite.noteDrop(error, tag);
+}
+
+void stats_log_close() {
+    statsd_writer_init_lock();
+    write_to_statsd = __write_to_statsd_init;
+    if (statsdLoggerWrite.close) {
+        (*statsdLoggerWrite.close)();
+    }
+    statsd_writer_init_unlock();
+}
+
+/* log_init_lock assumed */
+static int __write_to_statsd_initialize_locked() {
+    if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
+        if (statsdLoggerWrite.close) {
+            (*statsdLoggerWrite.close)();
+            return -ENODEV;
+        }
+    }
+    return 1;
+}
+
+static int __write_to_stats_daemon(struct iovec* vec, size_t nr) {
+    int save_errno;
+    struct timespec ts;
+    size_t len, i;
+
+    for (len = i = 0; i < nr; ++i) {
+        len += vec[i].iov_len;
+    }
+    if (!len) {
+        return -EINVAL;
+    }
+
+    save_errno = errno;
+#if defined(__ANDROID__)
+    clock_gettime(CLOCK_REALTIME, &ts);
+#else
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    ts.tv_sec = tv.tv_sec;
+    ts.tv_nsec = tv.tv_usec * 1000;
+#endif
+
+    int ret = (int)(*statsdLoggerWrite.write)(&ts, vec, nr);
+    errno = save_errno;
+    return ret;
+}
+
+static int __write_to_statsd_init(struct iovec* vec, size_t nr) {
+    int ret, save_errno = errno;
+
+    statsd_writer_init_lock();
+
+    if (write_to_statsd == __write_to_statsd_init) {
+        ret = __write_to_statsd_initialize_locked();
+        if (ret < 0) {
+            statsd_writer_init_unlock();
+            errno = save_errno;
+            return ret;
+        }
+
+        write_to_statsd = __write_to_stats_daemon;
+    }
+
+    statsd_writer_init_unlock();
+
+    ret = write_to_statsd(vec, nr);
+    errno = save_errno;
+    return ret;
+}
+
+static inline void copy4LE(uint8_t* buf, uint32_t val) {
+    buf[0] = val & 0xFF;
+    buf[1] = (val >> 8) & 0xFF;
+    buf[2] = (val >> 16) & 0xFF;
+    buf[3] = (val >> 24) & 0xFF;
+}
+
+// Note: this function differs from android_log_write_string8_len in that the length passed in
+// should be treated as actual length and not max length.
+int android_log_write_char_array(android_log_context ctx, const char* value, size_t actual_len) {
+    size_t needed;
+    ssize_t len = actual_len;
+    android_log_context_internal* context;
+
+    context = (android_log_context_internal*)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->overflow) {
+        return -EIO;
+    }
+    if (!value) {
+        value = "";
+        len = 0;
+    }
+    needed = sizeof(uint8_t) + sizeof(int32_t) + len;
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        /* Truncate string for delivery */
+        len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
+        if (len <= 0) {
+            context->overflow = true;
+            return -EIO;
+        }
+    }
+    context->count[context->list_nest_depth]++;
+    context->storage[context->pos + 0] = EVENT_TYPE_STRING;
+    copy4LE(&context->storage[context->pos + 1], len);
+    if (len) {
+        memcpy(&context->storage[context->pos + 5], value, len);
+    }
+    context->pos += needed;
+    return len;
+}
diff --git a/libstats/socket/statsd_writer.c b/libstats/socket/statsd_writer.c
new file mode 100644
index 0000000..04d3b46
--- /dev/null
+++ b/libstats/socket/statsd_writer.c
@@ -0,0 +1,285 @@
+/*
+ * 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 "statsd_writer.h"
+
+#include <cutils/fs.h>
+#include <cutils/sockets.h>
+#include <cutils/threads.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+#include <stdarg.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+static atomic_int dropped = 0;
+static atomic_int log_error = 0;
+static atomic_int atom_tag = 0;
+
+void statsd_writer_init_lock() {
+    /*
+     * If we trigger a signal handler in the middle of locked activity and the
+     * signal handler logs a message, we could get into a deadlock state.
+     */
+    pthread_mutex_lock(&log_init_lock);
+}
+
+int statd_writer_trylock() {
+    return pthread_mutex_trylock(&log_init_lock);
+}
+
+void statsd_writer_init_unlock() {
+    pthread_mutex_unlock(&log_init_lock);
+}
+
+static int statsdAvailable();
+static int statsdOpen();
+static void statsdClose();
+static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
+static void statsdNoteDrop();
+
+struct android_log_transport_write statsdLoggerWrite = {
+        .name = "statsd",
+        .sock = -EBADF,
+        .available = statsdAvailable,
+        .open = statsdOpen,
+        .close = statsdClose,
+        .write = statsdWrite,
+        .noteDrop = statsdNoteDrop,
+};
+
+/* log_init_lock assumed */
+static int statsdOpen() {
+    int i, ret = 0;
+
+    i = atomic_load(&statsdLoggerWrite.sock);
+    if (i < 0) {
+        int flags = SOCK_DGRAM;
+#ifdef SOCK_CLOEXEC
+        flags |= SOCK_CLOEXEC;
+#endif
+#ifdef SOCK_NONBLOCK
+        flags |= SOCK_NONBLOCK;
+#endif
+        int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, flags, 0));
+        if (sock < 0) {
+            ret = -errno;
+        } else {
+            int sndbuf = 1 * 1024 * 1024;  // set max send buffer size 1MB
+            socklen_t bufLen = sizeof(sndbuf);
+            // SO_RCVBUF does not have an effect on unix domain socket, but SO_SNDBUF does.
+            // Proceed to connect even setsockopt fails.
+            setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, bufLen);
+            struct sockaddr_un un;
+            memset(&un, 0, sizeof(struct sockaddr_un));
+            un.sun_family = AF_UNIX;
+            strcpy(un.sun_path, "/dev/socket/statsdw");
+
+            if (TEMP_FAILURE_RETRY(
+                        connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {
+                ret = -errno;
+                switch (ret) {
+                    case -ENOTCONN:
+                    case -ECONNREFUSED:
+                    case -ENOENT:
+                        i = atomic_exchange(&statsdLoggerWrite.sock, ret);
+                    /* FALLTHRU */
+                    default:
+                        break;
+                }
+                close(sock);
+            } else {
+                ret = atomic_exchange(&statsdLoggerWrite.sock, sock);
+                if ((ret >= 0) && (ret != sock)) {
+                    close(ret);
+                }
+                ret = 0;
+            }
+        }
+    }
+
+    return ret;
+}
+
+static void __statsdClose(int negative_errno) {
+    int sock = atomic_exchange(&statsdLoggerWrite.sock, negative_errno);
+    if (sock >= 0) {
+        close(sock);
+    }
+}
+
+static void statsdClose() {
+    __statsdClose(-EBADF);
+}
+
+static int statsdAvailable() {
+    if (atomic_load(&statsdLoggerWrite.sock) < 0) {
+        if (access("/dev/socket/statsdw", W_OK) == 0) {
+            return 0;
+        }
+        return -EBADF;
+    }
+    return 1;
+}
+
+static void statsdNoteDrop(int error, int tag) {
+    atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+    atomic_exchange_explicit(&log_error, error, memory_order_relaxed);
+    atomic_exchange_explicit(&atom_tag, tag, memory_order_relaxed);
+}
+
+static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
+    ssize_t ret;
+    int sock;
+    static const unsigned headerLength = 1;
+    struct iovec newVec[nr + headerLength];
+    android_log_header_t header;
+    size_t i, payloadSize;
+
+    sock = atomic_load(&statsdLoggerWrite.sock);
+    if (sock < 0) switch (sock) {
+            case -ENOTCONN:
+            case -ECONNREFUSED:
+            case -ENOENT:
+                break;
+            default:
+                return -EBADF;
+        }
+    /*
+     *  struct {
+     *      // what we provide to socket
+     *      android_log_header_t header;
+     *      // caller provides
+     *      union {
+     *          struct {
+     *              char     prio;
+     *              char     payload[];
+     *          } string;
+     *          struct {
+     *              uint32_t tag
+     *              char     payload[];
+     *          } binary;
+     *      };
+     *  };
+     */
+
+    header.tid = gettid();
+    header.realtime.tv_sec = ts->tv_sec;
+    header.realtime.tv_nsec = ts->tv_nsec;
+
+    newVec[0].iov_base = (unsigned char*)&header;
+    newVec[0].iov_len = sizeof(header);
+
+    // If we dropped events before, try to tell statsd.
+    if (sock >= 0) {
+        int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+        if (snapshot) {
+            android_log_event_long_t buffer;
+            header.id = LOG_ID_STATS;
+            // store the last log error in the tag field. This tag field is not used by statsd.
+            buffer.header.tag = atomic_load(&log_error);
+            buffer.payload.type = EVENT_TYPE_LONG;
+            // format:
+            // |atom_tag|dropped_count|
+            int64_t composed_long = atomic_load(&atom_tag);
+            // Send 2 int32's via an int64.
+            composed_long = ((composed_long << 32) | ((int64_t)snapshot));
+            buffer.payload.data = composed_long;
+
+            newVec[headerLength].iov_base = &buffer;
+            newVec[headerLength].iov_len = sizeof(buffer);
+
+            ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
+            if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+                atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
+            }
+        }
+    }
+
+    header.id = LOG_ID_STATS;
+
+    for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
+        newVec[i].iov_base = vec[i - headerLength].iov_base;
+        payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
+
+        if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
+            newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
+            if (newVec[i].iov_len) {
+                ++i;
+            }
+            break;
+        }
+    }
+
+    /*
+     * The write below could be lost, but will never block.
+     *
+     * ENOTCONN occurs if statsd has died.
+     * ENOENT occurs if statsd is not running and socket is missing.
+     * ECONNREFUSED occurs if we can not reconnect to statsd.
+     * EAGAIN occurs if statsd is overloaded.
+     */
+    if (sock < 0) {
+        ret = sock;
+    } else {
+        ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i));
+        if (ret < 0) {
+            ret = -errno;
+        }
+    }
+    switch (ret) {
+        case -ENOTCONN:
+        case -ECONNREFUSED:
+        case -ENOENT:
+            if (statd_writer_trylock()) {
+                return ret; /* in a signal handler? try again when less stressed
+                             */
+            }
+            __statsdClose(ret);
+            ret = statsdOpen();
+            statsd_writer_init_unlock();
+
+            if (ret < 0) {
+                return ret;
+            }
+
+            ret = TEMP_FAILURE_RETRY(writev(atomic_load(&statsdLoggerWrite.sock), newVec, i));
+            if (ret < 0) {
+                ret = -errno;
+            }
+        /* FALLTHRU */
+        default:
+            break;
+    }
+
+    if (ret > (ssize_t)sizeof(header)) {
+        ret -= sizeof(header);
+    }
+
+    return ret;
+}
diff --git a/libstats/socket/statsd_writer.h b/libstats/socket/statsd_writer.h
new file mode 100644
index 0000000..fe2d37c
--- /dev/null
+++ b/libstats/socket/statsd_writer.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_STATS_LOG_STATS_WRITER_H
+#define ANDROID_STATS_LOG_STATS_WRITER_H
+
+#include <pthread.h>
+#include <stdatomic.h>
+#include <sys/socket.h>
+
+/**
+ * Internal lock should not be exposed. This is bad design.
+ * TODO: rewrite it in c++ code and encapsulate the functionality in a
+ * StatsdWriter class.
+ */
+void statsd_writer_init_lock();
+int statsd_writer_init_trylock();
+void statsd_writer_init_unlock();
+
+struct android_log_transport_write {
+    const char* name; /* human name to describe the transport */
+    atomic_int sock;
+    int (*available)(); /* Does not cause resources to be taken */
+    int (*open)();      /* can be called multiple times, reusing current resources */
+    void (*close)();    /* free up resources */
+    /* write log to transport, returns number of bytes propagated, or -errno */
+    int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
+    /* note one log drop */
+    void (*noteDrop)(int error, int tag);
+};
+
+#endif  // ANDROID_STATS_LOG_STATS_WRITER_H