Merge 'origin/upstream/main' to 'origin/main'

Change-Id: Ic33ce959b0334318b247c5f9dc4372c22389f6d4
diff --git a/liblog/.clang-format b/liblog/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/liblog/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/liblog/Android.bp b/liblog/Android.bp
new file mode 100644
index 0000000..1c088d5
--- /dev/null
+++ b/liblog/Android.bp
@@ -0,0 +1,239 @@
+//
+// Copyright (C) 2008-2014 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.
+//
+
+package {
+    default_applicable_licenses: ["system_logging_liblog_license"],
+}
+
+// Added automatically by a large-scale-change
+license {
+    name: "system_logging_liblog_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
+liblog_sources = [
+    "log_event_list.cpp",
+    "log_event_write.cpp",
+    "logger_name.cpp",
+    "logger_read.cpp",
+    "logger_write.cpp",
+    "logprint.cpp",
+    "properties.cpp",
+]
+liblog_target_sources = [
+    "event_tag_map.cpp",
+    "log_time.cpp",
+    "pmsg_reader.cpp",
+    "pmsg_writer.cpp",
+    "logd_reader.cpp",
+    "logd_writer.cpp",
+]
+
+cc_library_headers {
+    name: "liblog_headers",
+    host_supported: true,
+    vendor_available: true,
+    product_available: true,
+    ramdisk_available: true,
+    vendor_ramdisk_available: true,
+    recovery_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    min_sdk_version: "29",
+    sdk_version: "minimum",
+    native_bridge_supported: true,
+    export_include_dirs: ["include"],
+    system_shared_libs: [],
+    stl: "none",
+    target: {
+        windows: {
+            enabled: true,
+        },
+        linux_bionic: {
+            enabled: true,
+        },
+        vendor: {
+            override_export_include_dirs: ["include_vndk"],
+        },
+        product: {
+            override_export_include_dirs: ["include_vndk"],
+        },
+    },
+}
+
+// Shared and static library for host and device
+// ========================================================
+cc_defaults {
+    name: "liblog.defaults",
+    host_supported: true,
+    ramdisk_available: true,
+    vendor_ramdisk_available: true,
+    recovery_available: true,
+    native_bridge_supported: true,
+    srcs: liblog_sources,
+
+    target: {
+        android: {
+            version_script: "liblog.map.txt",
+            srcs: liblog_target_sources,
+            // AddressSanitizer runtime library depends on liblog.
+            sanitize: {
+                address: false,
+            },
+        },
+        android_arm: {
+            // TODO: This is to work around b/24465209. Remove after root cause is fixed
+            pack_relocations: false,
+            ldflags: ["-Wl,--hash-style=both"],
+        },
+        windows: {
+            enabled: true,
+        },
+        not_windows: {
+            srcs: ["event_tag_map.cpp"],
+        },
+        linux_bionic: {
+            enabled: true,
+        },
+    },
+
+    header_libs: [
+        "libbase_headers",
+        "libcutils_headers",
+        "liblog_headers",
+    ],
+    export_header_lib_headers: ["liblog_headers"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+        "-Wexit-time-destructors",
+        // This is what we want to do:
+        //  liblog_cflags := $(shell \
+        //   sed -n \
+        //       's/^\([0-9]*\)[ \t]*liblog[ \t].*/-DLIBLOG_LOG_TAG=\1/p' \
+        //       $(LOCAL_PATH)/event.logtags)
+        // so make sure we do not regret hard-coding it as follows:
+        "-DLIBLOG_LOG_TAG=1006",
+        "-DSNET_EVENT_LOG_TAG=1397638484",
+        "-DANDROID_DEBUGGABLE=0",
+    ],
+    logtags: ["event.logtags"],
+    compile_multilib: "both",
+    afdo: true,
+    product_variables: {
+        debuggable: {
+            cflags: [
+                "-UANDROID_DEBUGGABLE",
+                "-DANDROID_DEBUGGABLE=1",
+            ],
+        },
+    },
+}
+
+cc_library {
+    name: "liblog",
+    defaults: ["liblog.defaults"],
+
+    llndk: {
+        symbol_file: "liblog.map.txt",
+        override_export_include_dirs: ["include_vndk"],
+    },
+
+    stubs: {
+        symbol_file: "liblog.map.txt",
+        versions: [
+            "29",
+            "30",
+        ],
+    },
+
+}
+
+// liblog_for_runtime_apex is a static liblog which is exclusively for
+// the runtime APEX. See b/151051671
+cc_library_static {
+    name: "liblog_for_runtime_apex",
+    defaults: ["liblog.defaults"],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.runtime",
+        // DO NOT add more apex names here
+    ],
+    visibility: [
+        "//bionic/linker",
+        "//frameworks/libs/native_bridge_support/linker",
+        "//system/apex/libs/libapexutil",
+        "//system/core/debuggerd",
+        "//system/linkerconfig",
+    ],
+}
+
+ndk_headers {
+    name: "liblog_ndk_headers",
+    from: "include/android",
+    to: "android",
+    srcs: ["include/android/log.h"],
+    license: "NOTICE",
+}
+
+ndk_library {
+    name: "liblog",
+    symbol_file: "liblog.map.txt",
+    first_version: "9",
+    unversioned_until: "current",
+    export_header_libs: [
+        "liblog_ndk_headers",
+    ],
+}
+
+rust_bindgen {
+    name: "liblog_event_list_bindgen",
+    wrapper_src: "rust/liblog_wrapper.h",
+    crate_name: "log_event_list_bindgen",
+    visibility: ["//system/logging/rust"],
+    source_stem: "bindings",
+
+    bindgen_flags: [
+        "--size_t-is-usize",
+        "--allowlist-function=create_android_logger",
+        "--allowlist-function=android_log_destroy",
+        "--allowlist-function=android_log_write_int32",
+        "--allowlist-function=android_log_write_string8_len",
+        "--allowlist-function=android_log_write_list",
+        "--allowlist-function=__android_log_security",
+        "--allowlist-type=log_id",
+    ],
+}
+
+rust_test {
+    name: "liblog_event_list_bindgen_test",
+    srcs: [":liblog_event_list_bindgen"],
+    crate_name: "log_event_list_bindgen_test",
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    clippy_lints: "none",
+    lints: "none",
+}
diff --git a/liblog/NOTICE b/liblog/NOTICE
new file mode 100644
index 0000000..06a9081
--- /dev/null
+++ b/liblog/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2014, 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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/liblog/README.md b/liblog/README.md
new file mode 100644
index 0000000..5431f12
--- /dev/null
+++ b/liblog/README.md
@@ -0,0 +1,161 @@
+Android liblog
+--------------
+
+Public Functions and Macros
+---------------------------
+
+    /*
+     * Please limit to 24 characters for runtime is loggable,
+     * 16 characters for persist is loggable, and logcat pretty
+     * alignment with limit of 7 characters.
+    */
+    #define LOG_TAG "yourtag"
+    #include <log/log.h>
+
+    ALOG(android_priority, tag, format, ...)
+    IF_ALOG(android_priority, tag)
+    LOG_PRI(priority, tag, format, ...)
+    LOG_PRI_VA(priority, tag, format, args)
+    #define LOG_TAG NULL
+    ALOGV(format, ...)
+    SLOGV(format, ...)
+    RLOGV(format, ...)
+    ALOGV_IF(cond, format, ...)
+    SLOGV_IF(cond, format, ...)
+    RLOGV_IF(cond, format, ...)
+    IF_ALOGC()
+    ALOGD(format, ...)
+    SLOGD(format, ...)
+    RLOGD(format, ...)
+    ALOGD_IF(cond, format, ...)
+    SLOGD_IF(cond, format, ...)
+    RLOGD_IF(cond, format, ...)
+    IF_ALOGD()
+    ALOGI(format, ...)
+    SLOGI(format, ...)
+    RLOGI(format, ...)
+    ALOGI_IF(cond, format, ...)
+    SLOGI_IF(cond, format, ...)
+    RLOGI_IF(cond, format, ...)
+    IF_ALOGI()
+    ALOGW(format, ...)
+    SLOGW(format, ...)
+    RLOGW(format, ...)
+    ALOGW_IF(cond, format, ...)
+    SLOGW_IF(cond, format, ...)
+    RLOGW_IF(cond, format, ...)
+    IF_ALOGW()
+    ALOGE(format, ...)
+    SLOGE(format, ...)
+    RLOGE(format, ...)
+    ALOGE_IF(cond, format, ...)
+    SLOGE_IF(cond, format, ...)
+    RLOGE_IF(cond, format, ...)
+    IF_ALOGE()
+    LOG_FATAL(format, ...)
+    LOG_ALWAYS_FATAL(format, ...)
+    LOG_FATAL_IF(cond, format, ...)
+    LOG_ALWAYS_FATAL_IF(cond, format, ...)
+    ALOG_ASSERT(cond, format, ...)
+    LOG_EVENT_INT(tag, value)
+    LOG_EVENT_LONG(tag, value)
+
+    log_id_t android_logger_get_id(struct logger *logger)
+    int android_logger_clear(struct logger *logger)
+    int android_logger_get_log_size(struct logger *logger)
+    int android_logger_get_log_readable_size(struct logger *logger)
+    int android_logger_get_log_version(struct logger *logger)
+
+    struct logger_list *android_logger_list_alloc(int mode, unsigned int tail, pid_t pid)
+    struct logger *android_logger_open(struct logger_list *logger_list, log_id_t id)
+    struct logger_list *android_logger_list_open(log_id_t id, int mode, unsigned int tail, pid_t pid)
+    int android_logger_list_read(struct logger_list *logger_list, struct log_msg *log_msg)
+    void android_logger_list_free(struct logger_list *logger_list)
+
+    log_id_t android_name_to_log_id(const char *logName)
+    const char *android_log_id_to_name(log_id_t log_id)
+
+    android_log_context create_android_logger(uint32_t tag)
+
+    int android_log_write_list_begin(android_log_context ctx)
+    int android_log_write_list_end(android_log_context ctx)
+
+    int android_log_write_int32(android_log_context ctx, int32_t value)
+    int android_log_write_int64(android_log_context ctx, int64_t value)
+    int android_log_write_string8(android_log_context ctx, const char *value)
+    int android_log_write_string8_len(android_log_context ctx, const char *value, size_t maxlen)
+    int android_log_write_float32(android_log_context ctx, float value)
+
+    int android_log_write_list(android_log_context ctx, log_id_t id = LOG_ID_EVENTS)
+
+    android_log_context create_android_log_parser(const char *msg, size_t len)
+    android_log_list_element android_log_read_next(android_log_context ctx)
+    android_log_list_element android_log_peek_next(android_log_context ctx)
+
+    int android_log_destroy(android_log_context *ctx)
+
+Description
+-----------
+
+liblog represents an interface to the volatile Android Logging system for NDK (Native) applications
+and libraries.  Interfaces for either writing or reading logs.  The log buffers are divided up in
+Main, System, Radio and Events sub-logs.
+
+The logging interfaces are a series of macros, all of which can be overridden individually in order
+to control the verbosity of the application or library.  `[ASR]LOG[VDIWE]` calls are used to log to
+Basic, System or Radio sub-logs in either the Verbose, Debug, Info, Warning or Error priorities.
+`[ASR]LOG[VDIWE]_IF` calls are used to perform thus based on a condition being true.
+`IF_ALOG[VDIWE]` calls are true if the current `LOG_TAG` is enabled at the specified priority.
+`LOG_ALWAYS_FATAL` is used to `ALOG` a message, then kill the process.  `LOG_FATAL` call is a
+variant of `LOG_ALWAYS_FATAL`, only enabled in engineering, and not release builds.  `ALOG_ASSERT`
+is used to `ALOG` a message if the condition is false; the condition is part of the logged message.
+`LOG_EVENT_(INT|LONG)` is used to drop binary content into the Events sub-log.
+
+The log reading interfaces permit opening the logs either singly or multiply, retrieving a log entry
+at a time in time sorted order, optionally limited to a specific pid and tail of the log(s) and
+finally a call closing the logs.  A single log can be opened with `android_logger_list_open()`; or
+multiple logs can be opened with `android_logger_list_alloc()`, calling in turn the
+`android_logger_open()` for each log id.  Each entry can be retrieved with
+`android_logger_list_read()`.  The log(s) can be closed with `android_logger_list_free()`.
+`ANDROID_LOG_NONBLOCK` mode will report when the log reading is done with an `EAGAIN` error return
+code, otherwise the `android_logger_list_read()` call will block for new entries.
+
+The `ANDROID_LOG_WRAP` mode flag to the `android_logger_list_alloc_time()` signals logd to quiesce
+the reader until the buffer is about to prune at the start time then proceed to dumping content.
+
+The `ANDROID_LOG_PSTORE` mode flag to the `android_logger_open()` is used to switch from the active
+logs to the persistent logs from before the last reboot.
+
+The value returned by `android_logger_open()` can be used as a parameter to the
+`android_logger_clear()` function to empty the sub-log.
+
+The value returned by `android_logger_open()` can be used as a parameter to the
+`android_logger_get_log_(size|readable_size|version)` to retrieve the sub-log maximum size, readable
+size and log buffer format protocol version respectively.  `android_logger_get_id()` returns the id
+that was used when opening the sub-log.
+
+Errors
+------
+
+If messages fail, a negative error code will be returned to the caller.
+
+The `-ENOTCONN` return code indicates that the logger daemon is stopped.
+
+The `-EBADF` return code indicates that the log access point can not be opened, or the log buffer id
+is out of range.
+
+For the `-EAGAIN` return code, this means that the logging message was temporarily backed-up either
+because of Denial Of Service (DOS) logging pressure from some spammy application or service in the
+Android system, or if too small of a value is set in /proc/sys/net/unix/max_dgram_qlen.  To aid in
+diagnosing the occurence of this, a binary event from liblog will be sent to the log daemon once a
+new message can get through indicating how many messages were dropped as a result.  Please take
+action to resolve the structural problems at the source.
+
+It is generally not advised for the caller to retry the `-EAGAIN` return code as this will only make
+the problem(s) worse and cause your application to temporarily drop to the logger daemon priority,
+BATCH scheduling policy and background task cgroup. If you require a group of messages to be passed
+atomically, merge them into one message with embedded newlines to the maximum length
+`LOGGER_ENTRY_MAX_PAYLOAD`.
+
+Other return codes from writing operation can be returned.  Since the library retries on `EINTR`,
+`-EINTR` should never be returned.
diff --git a/liblog/README.protocol.md b/liblog/README.protocol.md
new file mode 100644
index 0000000..f247b28
--- /dev/null
+++ b/liblog/README.protocol.md
@@ -0,0 +1,92 @@
+# liblog -> logd
+
+The data that liblog sends to logd is represented below.
+
+    struct {
+        android_log_header_t header;
+        union {
+           struct {
+                char     prio;
+                char     tag[...];
+                char     message[...];
+            } string;
+            struct {
+                android_event_header_t event_header;
+                android_event_*_t      payload[...];
+            } binary;
+        };
+    };
+
+where the embedded structs are defined as:
+
+    struct android_log_header_t {
+        uint8_t id;
+        uint16_t tid;
+        log_time realtime;
+    };
+
+    struct log_time {
+        uint32_t tv_sec = 0;
+        uint32_t tv_nsec = 0;
+    }
+
+    struct android_event_header_t {
+        int32_t tag;
+    };
+
+    struct android_event_list_t {
+        int8_t type;  // EVENT_TYPE_LIST
+        int8_t element_count;
+    };
+
+    struct android_event_float_t {
+        int8_t type;  // EVENT_TYPE_FLOAT
+        float data;
+    };
+
+    struct android_event_int_t {
+        int8_t type;   // EVENT_TYPE_INT
+        int32_t data;
+    } android_event_int_t;
+
+    struct android_event_long_t {
+        int8_t type;   // EVENT_TYPE_LONG
+        int64_t data;
+    };
+
+    struct android_event_string_t {
+        int8_t type;     // EVENT_TYPE_STRING;
+        int32_t length;
+        char data[];
+    };
+
+The payload, excluding the header, has a max size of LOGGER_ENTRY_MAX_PAYLOAD.
+
+## header
+
+The header is added immediately before sending the log message to logd.
+
+## `string` payload
+
+The `string` part of the union is for normal buffers (main, system, radio, etc) and consists of a
+single character priority, followed by a variable length null terminated string for the tag, and
+finally a variable length null terminated string for the message.
+
+This payload is used for the `__android_log_buf_write()` family of functions.
+
+## `binary` payload
+
+The `binary` part of the union is for binary buffers (events, security, etc) and consists of an
+android_event_header_t struct followed by a variable number of android_event_*_t
+(android_event_list_t, android_event_int_t, etc) structs.
+
+If multiple android_event_*_t elements are present, then they must be in a list and the first
+element in payload must be an android_event_list_t.
+
+This payload is used for the `__android_log_bwrite()` family of functions. It is additionally used
+for `android_log_write_list()` and the related functions that manipulate event lists.
+
+# logd -> liblog
+
+logd sends a `logger_entry` struct to liblog followed by the payload. The payload is identical to
+the payloads defined above. The max size of the entire message from logd is LOGGER_ENTRY_MAX_LEN.
diff --git a/liblog/event.logtags b/liblog/event.logtags
new file mode 100644
index 0000000..0a3b650
--- /dev/null
+++ b/liblog/event.logtags
@@ -0,0 +1,37 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31.  (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_".  Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace.  Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+#    (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+#
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# s: Number of seconds (monotonic time)
+# Default value for data of type int/long is 2 (bytes).
+#
+# TODO: generate ".java" and ".h" files with integer constants from this file.
+
+1006  liblog (dropped|1)
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
new file mode 100644
index 0000000..85556e8
--- /dev/null
+++ b/liblog/event_tag_map.cpp
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2007-2016 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 <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include <functional>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+
+#include <log/event_tag_map.h>
+#include <private/android_logger.h>
+#include <utils/FastStrcmp.h>
+#include <utils/RWLock.h>
+
+#define OUT_TAG "EventTagMap"
+
+typedef std::pair<std::string_view, std::string_view> TagFmt;
+
+// Map
+struct EventTagMap {
+#define NUM_MAPS 2
+  // memory-mapped source file; we get strings from here
+  void* mapAddr[NUM_MAPS];
+  size_t mapLen[NUM_MAPS];
+
+ private:
+  std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;
+  std::unordered_map<std::string_view, uint32_t> Tag2Idx;
+  // protect unordered sets
+  android::RWLock rwlock;
+
+ public:
+  EventTagMap() {
+    memset(mapAddr, 0, sizeof(mapAddr));
+    memset(mapLen, 0, sizeof(mapLen));
+  }
+
+  ~EventTagMap() {
+    Idx2TagFmt.clear();
+    Tag2Idx.clear();
+    for (size_t which = 0; which < NUM_MAPS; ++which) {
+      if (mapAddr[which]) {
+        munmap(mapAddr[which], mapLen[which]);
+        mapAddr[which] = 0;
+      }
+    }
+  }
+
+  bool emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose = false);
+  const TagFmt* find(uint32_t tag) const;
+  int find(std::string_view tag) const;
+};
+
+bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt,
+                                bool verbose) {
+  bool ret = true;
+  static const char errorFormat[] =
+      OUT_TAG ": duplicate tag entries %" PRIu32 ":%.*s:%.*s and %" PRIu32
+              ":%.*s:%.*s)\n";
+  android::RWLock::AutoWLock writeLock(rwlock);
+  {
+    auto it = Idx2TagFmt.find(tag);
+    if (it != Idx2TagFmt.end()) {
+      if (verbose) {
+        fprintf(stderr, errorFormat, it->first, (int)it->second.first.length(),
+                it->second.first.data(), (int)it->second.second.length(),
+                it->second.second.data(), tag, (int)tagfmt.first.length(),
+                tagfmt.first.data(), (int)tagfmt.second.length(),
+                tagfmt.second.data());
+      }
+      ret = false;
+    } else {
+      Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
+    }
+  }
+
+  {
+    auto it = Tag2Idx.find(tagfmt.first);
+    if (!tagfmt.second.length() && (it != Tag2Idx.end())) {
+      Tag2Idx.erase(it);
+      it = Tag2Idx.end();
+    }
+    if (it == Tag2Idx.end()) {
+      Tag2Idx.emplace(std::make_pair(tagfmt.first, tag));
+    }
+  }
+
+  return ret;
+}
+
+const TagFmt* EventTagMap::find(uint32_t tag) const {
+  android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+  auto it = Idx2TagFmt.find(tag);
+  if (it == Idx2TagFmt.end()) return NULL;
+  return &(it->second);
+}
+
+int EventTagMap::find(std::string_view tag) const {
+  android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+  auto it = Tag2Idx.find(std::move(tag));
+  if (it == Tag2Idx.end()) return -1;
+  return it->second;
+}
+
+// The position after the end of a valid section of the tag string,
+// caller makes sure delimited appropriately.
+static const char* endOfTag(const char* cp) {
+  while (*cp && (isalnum(*cp) || strchr("_.-@,", *cp))) ++cp;
+  return cp;
+}
+
+// Scan one tag line.
+//
+// "pData" should be pointing to the first digit in the tag number.  On
+// successful return, it will be pointing to the last character in the
+// tag line (i.e. the character before the start of the next line).
+//
+// Returns 0 on success, nonzero on failure.
+static int scanTagLine(EventTagMap* map, const char*& pData, int line_num) {
+  char* ep;
+  unsigned long val = strtoul(pData, &ep, 10);
+  const char* cp = ep;
+  if (cp == pData) {
+    fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", line_num);
+    errno = EINVAL;
+    return -1;
+  }
+
+  uint32_t tagIndex = val;
+  if (tagIndex != val) {
+    fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", line_num);
+    errno = ERANGE;
+    return -1;
+  }
+
+  while ((*++cp != '\n') && isspace(*cp)) {
+  }
+
+  if (*cp == '\n') {
+    fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", line_num);
+    errno = EINVAL;
+    return -1;
+  }
+
+  const char* tag = cp;
+  cp = endOfTag(cp);
+  size_t tagLen = cp - tag;
+
+  if (!isspace(*cp)) {
+    fprintf(stderr, OUT_TAG ": invalid tag char %c on line %d\n", *cp, line_num);
+    errno = EINVAL;
+    return -1;
+  }
+
+  while (isspace(*cp) && (*cp != '\n')) ++cp;
+  const char* fmt = NULL;
+  size_t fmtLen = 0;
+  if (*cp && (*cp != '#')) {
+    fmt = cp;
+    while (*cp && (*cp != '\n') && (*cp != '#')) ++cp;
+    while ((cp > fmt) && isspace(*(cp - 1))) --cp;
+    fmtLen = cp - fmt;
+  }
+
+  // KISS Only report identicals if they are global
+  // Ideally we want to check if there are identicals
+  // recorded for the same uid, but recording that
+  // unused detail in our database is too burdensome.
+  bool verbose = true;
+  while (*cp && (*cp != '#') && (*cp != '\n')) ++cp;
+  if (*cp == '#') {
+    do {
+      ++cp;
+    } while (isspace(*cp) && (*cp != '\n'));
+    verbose = !!fastcmp<strncmp>(cp, "uid=", strlen("uid="));
+  }
+
+  while (*cp && (*cp != '\n')) ++cp;
+#ifdef DEBUG
+  fprintf(stderr, "%d: %p: %.*s\n", line_num, tag, (int)(cp - pData), pData);
+#endif
+  pData = cp;
+
+  if (map->emplaceUnique(
+          tagIndex,
+          TagFmt(std::make_pair(std::string_view(tag, tagLen), std::string_view(fmt, fmtLen))),
+          verbose)) {
+    return 0;
+  }
+  errno = EMLINK;
+  return -1;
+}
+
+static const char* eventTagFiles[NUM_MAPS] = {
+  EVENT_TAG_MAP_FILE, "/dev/event-log-tags",
+};
+
+// Parse the tags out of the file.
+static int parseMapLines(EventTagMap* map, size_t which) {
+  const char* cp = static_cast<char*>(map->mapAddr[which]);
+  size_t len = map->mapLen[which];
+  const char* endp = cp + len;
+
+  // insist on EOL at EOF; simplifies parsing and null-termination
+  if (!len || (*(endp - 1) != '\n')) {
+#ifdef DEBUG
+    fprintf(stderr, OUT_TAG ": map file %zu[%zu] missing EOL on last line\n",
+            which, len);
+#endif
+    if (which) {  // do not propagate errors for other files
+      return 0;
+    }
+    errno = EINVAL;
+    return -1;
+  }
+
+  bool lineStart = true;
+  int lineNum = 1;
+  while (cp < endp) {
+    if (*cp == '\n') {
+      lineStart = true;
+      lineNum++;
+    } else if (lineStart) {
+      if (*cp == '#') {
+        // comment; just scan to end
+        lineStart = false;
+      } else if (isdigit(*cp)) {
+        // looks like a tag; scan it out
+        if (scanTagLine(map, cp, lineNum) != 0) {
+          if (!which || (errno != EMLINK)) {
+            return -1;
+          }
+        }
+        lineNum++;  // we eat the '\n'
+                    // leave lineStart==true
+      } else if (isspace(*cp)) {
+        // looks like leading whitespace; keep scanning
+      } else {
+        fprintf(stderr,
+                OUT_TAG
+                ": unexpected chars (0x%02x) in tag number on line %d\n",
+                *cp, lineNum);
+        errno = EINVAL;
+        return -1;
+      }
+    } else {
+      // this is a blank or comment line
+    }
+    cp++;
+  }
+
+  return 0;
+}
+
+// Open the map file and allocate a structure to manage it.
+//
+// We create a private mapping because we want to terminate the log tag
+// strings with '\0'.
+EventTagMap* android_openEventTagMap(const char* fileName) {
+  EventTagMap* newTagMap;
+  off_t end[NUM_MAPS];
+  int save_errno, fd[NUM_MAPS];
+  size_t which;
+
+  memset(fd, -1, sizeof(fd));
+  memset(end, 0, sizeof(end));
+
+  for (which = 0; which < NUM_MAPS; ++which) {
+    const char* tagfile = fileName ? fileName : eventTagFiles[which];
+
+    fd[which] = open(tagfile, O_RDONLY | O_CLOEXEC);
+    if (fd[which] < 0) {
+      if (!which) {
+        save_errno = errno;
+        fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n", tagfile,
+                strerror(save_errno));
+        goto fail_errno;
+      }
+      continue;
+    }
+    end[which] = lseek(fd[which], 0L, SEEK_END);
+    save_errno = errno;
+    (void)lseek(fd[which], 0L, SEEK_SET);
+    if (!which && (end[0] < 0)) {
+      fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n", tagfile,
+              strerror(save_errno));
+      goto fail_close;
+    }
+    if (fileName) break;  // Only allow one as specified
+  }
+
+  newTagMap = new EventTagMap;
+  if (newTagMap == NULL) {
+    save_errno = errno;
+    goto fail_close;
+  }
+
+  for (which = 0; which < NUM_MAPS; ++which) {
+    if (fd[which] >= 0) {
+      newTagMap->mapAddr[which] =
+          mmap(NULL, end[which], which ? PROT_READ : PROT_READ | PROT_WRITE,
+               which ? MAP_SHARED : MAP_PRIVATE, fd[which], 0);
+      save_errno = errno;
+      close(fd[which]); /* fd DONE */
+      fd[which] = -1;
+      if ((newTagMap->mapAddr[which] != MAP_FAILED) &&
+          (newTagMap->mapAddr[which] != NULL)) {
+        newTagMap->mapLen[which] = end[which];
+      } else if (!which) {
+        const char* tagfile = fileName ? fileName : eventTagFiles[which];
+
+        fprintf(stderr, OUT_TAG ": mmap(%s) failed: %s\n", tagfile,
+                strerror(save_errno));
+        goto fail_unmap;
+      }
+    }
+  }
+
+  for (which = 0; which < NUM_MAPS; ++which) {
+    if (parseMapLines(newTagMap, which) != 0) {
+      delete newTagMap;
+      return NULL;
+    }
+    /* See 'fd DONE' comments above and below, no need to clean up here */
+  }
+
+  return newTagMap;
+
+fail_unmap:
+  save_errno = EINVAL;
+  delete newTagMap;
+fail_close:
+  for (which = 0; which < NUM_MAPS; ++which) close(fd[which]); /* fd DONE */
+fail_errno:
+  errno = save_errno;
+  return NULL;
+}
+
+// Close the map.
+void android_closeEventTagMap(EventTagMap* map) {
+  if (map) delete map;
+}
+
+// Look up an entry in the map.
+const char* android_lookupEventTag_len(const EventTagMap* map, size_t* len, unsigned int tag) {
+  if (len) *len = 0;
+  const TagFmt* str = map->find(tag);
+  if (!str) return NULL;
+  if (len) *len = str->first.length();
+  return str->first.data();
+}
+
+// Look up an entry in the map.
+const char* android_lookupEventFormat_len(const EventTagMap* map, size_t* len, unsigned int tag) {
+  if (len) *len = 0;
+  const TagFmt* str = map->find(tag);
+  if (!str) return NULL;
+  if (len) *len = str->second.length();
+  return str->second.data();
+}
+
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
new file mode 100644
index 0000000..5dc365a
--- /dev/null
+++ b/liblog/include/android/log.h
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2009 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
+
+/**
+ * @addtogroup Logging
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * Support routines to send messages to the Android log buffer,
+ * which can later be accessed through the `logcat` utility.
+ *
+ * Each log message must have
+ *   - a priority
+ *   - a log tag
+ *   - some text
+ *
+ * The tag normally corresponds to the component that emits the log message,
+ * and should be reasonably small.
+ *
+ * Log message text may be truncated to less than an implementation-specific
+ * limit (1023 bytes).
+ *
+ * Note that a newline character ("\n") will be appended automatically to your
+ * log message, if not already there. It is not possible to send several
+ * messages and have them appear on a single line in logcat.
+ *
+ * Please use logging in moderation:
+ *
+ *  - Sending log messages eats CPU and slow down your application and the
+ *    system.
+ *
+ *  - The circular log buffer is pretty small, so sending many messages
+ *    will hide other important log messages.
+ *
+ *  - In release builds, only send log messages to account for exceptional
+ *    conditions.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+#if !defined(__BIONIC__) && !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(x)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Android log priority values, in increasing order of priority.
+ */
+typedef enum android_LogPriority {
+  /** For internal use only.  */
+  ANDROID_LOG_UNKNOWN = 0,
+  /** The default priority, for internal use only.  */
+  ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
+  /** Verbose logging. Should typically be disabled for a release apk. */
+  ANDROID_LOG_VERBOSE,
+  /** Debug logging. Should typically be disabled for a release apk. */
+  ANDROID_LOG_DEBUG,
+  /** Informational logging. Should typically be disabled for a release apk. */
+  ANDROID_LOG_INFO,
+  /** Warning logging. For use with recoverable failures. */
+  ANDROID_LOG_WARN,
+  /** Error logging. For use with unrecoverable failures. */
+  ANDROID_LOG_ERROR,
+  /** Fatal logging. For use when aborting. */
+  ANDROID_LOG_FATAL,
+  /** For internal use only.  */
+  ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
+} android_LogPriority;
+
+/**
+ * Writes the constant string `text` to the log, with priority `prio` and tag
+ * `tag`.
+ */
+int __android_log_write(int prio, const char* tag, const char* text);
+
+/**
+ * Writes a formatted string to the log, with priority `prio` and tag `tag`.
+ * The details of formatting are the same as for
+ * [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
+ */
+int __android_log_print(int prio, const char* tag, const char* fmt, ...)
+    __attribute__((__format__(printf, 3, 4)));
+
+/**
+ * Equivalent to `__android_log_print`, but taking a `va_list`.
+ * (If `__android_log_print` is like `printf`, this is like `vprintf`.)
+ */
+int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap)
+    __attribute__((__format__(printf, 3, 0)));
+
+/**
+ * Writes an assertion failure to the log (as `ANDROID_LOG_FATAL`) and to
+ * stderr, before calling
+ * [abort(3)](http://man7.org/linux/man-pages/man3/abort.3.html).
+ *
+ * If `fmt` is non-null, `cond` is unused. If `fmt` is null, the string
+ * `Assertion failed: %s` is used with `cond` as the string argument.
+ * If both `fmt` and `cond` are null, a default string is provided.
+ *
+ * Most callers should use
+ * [assert(3)](http://man7.org/linux/man-pages/man3/assert.3.html) from
+ * `&lt;assert.h&gt;` instead, or the `__assert` and `__assert2` functions
+ * provided by bionic if more control is needed. They support automatically
+ * including the source filename and line number more conveniently than this
+ * function.
+ */
+void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...)
+    __attribute__((__noreturn__)) __attribute__((__format__(printf, 3, 4)));
+
+/**
+ * Identifies a specific log buffer for __android_log_buf_write()
+ * and __android_log_buf_print().
+ */
+typedef enum log_id {
+  LOG_ID_MIN = 0,
+
+  /** The main log buffer. This is the only log buffer available to apps. */
+  LOG_ID_MAIN = 0,
+  /** The radio log buffer. */
+  LOG_ID_RADIO = 1,
+  /** The event log buffer. */
+  LOG_ID_EVENTS = 2,
+  /** The system log buffer. */
+  LOG_ID_SYSTEM = 3,
+  /** The crash log buffer. */
+  LOG_ID_CRASH = 4,
+  /** The statistics log buffer. */
+  LOG_ID_STATS = 5,
+  /** The security log buffer. */
+  LOG_ID_SECURITY = 6,
+  /** The kernel log buffer. */
+  LOG_ID_KERNEL = 7,
+
+  LOG_ID_MAX,
+
+  /** Let the logging function choose the best log target. */
+  LOG_ID_DEFAULT = 0x7FFFFFFF
+} log_id_t;
+
+/**
+ * Writes the constant string `text` to the log buffer `id`,
+ * with priority `prio` and tag `tag`.
+ *
+ * Apps should use __android_log_write() instead.
+ */
+int __android_log_buf_write(int bufID, int prio, const char* tag, const char* text);
+
+/**
+ * Writes a formatted string to log buffer `id`,
+ * with priority `prio` and tag `tag`.
+ * The details of formatting are the same as for
+ * [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
+ *
+ * Apps should use __android_log_print() instead.
+ */
+int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
+    __attribute__((__format__(printf, 4, 5)));
+
+/**
+ * Logger data struct used for writing log messages to liblog via __android_log_write_logger_data()
+ * and sending log messages to user defined loggers specified in __android_log_set_logger().
+ */
+struct __android_log_message {
+  /** Must be set to sizeof(__android_log_message) and is used for versioning. */
+  size_t struct_size;
+
+  /** {@link log_id_t} values. */
+  int32_t buffer_id;
+
+  /** {@link android_LogPriority} values. */
+  int32_t priority;
+
+  /** The tag for the log message. */
+  const char* tag;
+
+  /** Optional file name, may be set to nullptr. */
+  const char* file;
+
+  /** Optional line number, ignore if file is nullptr. */
+  uint32_t line;
+
+  /** The log message itself. */
+  const char* message;
+};
+
+/**
+ * Prototype for the 'logger' function that is called for every log message.
+ */
+typedef void (*__android_logger_function)(const struct __android_log_message* log_message);
+/**
+ * Prototype for the 'abort' function that is called when liblog will abort due to
+ * __android_log_assert() failures.
+ */
+typedef void (*__android_aborter_function)(const char* abort_message);
+
+/**
+ * Writes the log message specified by log_message.  log_message includes additional file name and
+ * line number information that a logger may use.  log_message is versioned for backwards
+ * compatibility.
+ * This assumes that loggability has already been checked through __android_log_is_loggable().
+ * Higher level logging libraries, such as libbase, first check loggability, then format their
+ * buffers, then pass the message to liblog via this function, and therefore we do not want to
+ * duplicate the loggability check here.
+ *
+ * @param log_message the log message itself, see __android_log_message.
+ *
+ * Available since API level 30.
+ */
+void __android_log_write_log_message(struct __android_log_message* log_message) __INTRODUCED_IN(30);
+
+/**
+ * Sets a user defined logger function.  All log messages sent to liblog will be set to the
+ * function pointer specified by logger for processing.  It is not expected that log messages are
+ * already terminated with a new line.  This function should add new lines if required for line
+ * separation.
+ *
+ * @param logger the new function that will handle log messages.
+ *
+ * Available since API level 30.
+ */
+void __android_log_set_logger(__android_logger_function logger) __INTRODUCED_IN(30);
+
+/**
+ * Writes the log message to logd.  This is an __android_logger_function and can be provided to
+ * __android_log_set_logger().  It is the default logger when running liblog on a device.
+ *
+ * @param log_message the log message to write, see __android_log_message.
+ *
+ * Available since API level 30.
+ */
+void __android_log_logd_logger(const struct __android_log_message* log_message) __INTRODUCED_IN(30);
+
+/**
+ * Writes the log message to stderr.  This is an __android_logger_function and can be provided to
+ * __android_log_set_logger().  It is the default logger when running liblog on host.
+ *
+ * @param log_message the log message to write, see __android_log_message.
+ *
+ * Available since API level 30.
+ */
+void __android_log_stderr_logger(const struct __android_log_message* log_message)
+    __INTRODUCED_IN(30);
+
+/**
+ * Sets a user defined aborter function that is called for __android_log_assert() failures.  This
+ * user defined aborter function is highly recommended to abort and be noreturn, but is not strictly
+ * required to.
+ *
+ * @param aborter the new aborter function, see __android_aborter_function.
+ *
+ * Available since API level 30.
+ */
+void __android_log_set_aborter(__android_aborter_function aborter) __INTRODUCED_IN(30);
+
+/**
+ * Calls the stored aborter function.  This allows for other logging libraries to use the same
+ * aborter function by calling this function in liblog.
+ *
+ * @param abort_message an additional message supplied when aborting, for example this is used to
+ *                      call android_set_abort_message() in __android_log_default_aborter().
+ *
+ * Available since API level 30.
+ */
+void __android_log_call_aborter(const char* abort_message) __INTRODUCED_IN(30);
+
+/**
+ * Sets android_set_abort_message() on device then aborts().  This is the default aborter.
+ *
+ * @param abort_message an additional message supplied when aborting.  This functions calls
+ *                      android_set_abort_message() with its contents.
+ *
+ * Available since API level 30.
+ */
+void __android_log_default_aborter(const char* abort_message) __attribute__((noreturn))
+__INTRODUCED_IN(30);
+
+/**
+ * Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
+ * __android_log_set_minimum_priority() to determine if a log message with a given prio and tag will
+ * be printed.  A non-zero result indicates yes, zero indicates false.
+ *
+ * If both a priority for a tag and a minimum priority are set by
+ * __android_log_set_minimum_priority(), then the lowest of the two values are to determine the
+ * minimum priority needed to log.  If only one is set, then that value is used to determine the
+ * minimum priority needed.  If none are set, then default_priority is used.
+ *
+ * @param prio         the priority to test, takes android_LogPriority values.
+ * @param tag          the tag to test.
+ * @param default_prio the default priority to use if no properties or minimum priority are set.
+ * @return an integer where 1 indicates that the message is loggable and 0 indicates that it is not.
+ *
+ * Available since API level 30.
+ */
+int __android_log_is_loggable(int prio, const char* tag, int default_prio) __INTRODUCED_IN(30);
+
+/**
+ * Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
+ * __android_log_set_minimum_priority() to determine if a log message with a given prio and tag will
+ * be printed.  A non-zero result indicates yes, zero indicates false.
+ *
+ * If both a priority for a tag and a minimum priority are set by
+ * __android_log_set_minimum_priority(), then the lowest of the two values are to determine the
+ * minimum priority needed to log.  If only one is set, then that value is used to determine the
+ * minimum priority needed.  If none are set, then default_priority is used.
+ *
+ * @param prio         the priority to test, takes android_LogPriority values.
+ * @param tag          the tag to test.
+ * @param len          the length of the tag.
+ * @param default_prio the default priority to use if no properties or minimum priority are set.
+ * @return an integer where 1 indicates that the message is loggable and 0 indicates that it is not.
+ *
+ * Available since API level 30.
+ */
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio)
+    __INTRODUCED_IN(30);
+
+/**
+ * Sets the minimum priority that will be logged for this process.
+ *
+ * @param priority the new minimum priority to set, takes android_LogPriority values.
+ * @return the previous set minimum priority as android_LogPriority values, or
+ *         ANDROID_LOG_DEFAULT if none was set.
+ *
+ * Available since API level 30.
+ */
+int32_t __android_log_set_minimum_priority(int32_t priority) __INTRODUCED_IN(30);
+
+/**
+ * Gets the minimum priority that will be logged for this process.  If none has been set by a
+ * previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
+ *
+ * @return the current minimum priority as android_LogPriority values, or
+ *         ANDROID_LOG_DEFAULT if none is set.
+ *
+ * Available since API level 30.
+ */
+int32_t __android_log_get_minimum_priority(void) __INTRODUCED_IN(30);
+
+/**
+ * Sets the default tag if no tag is provided when writing a log message.  Defaults to
+ * getprogname().  This truncates tag to the maximum log message size, though appropriate tags
+ * should be much smaller.
+ *
+ * @param tag the new log tag.
+ *
+ * Available since API level 30.
+ */
+void __android_log_set_default_tag(const char* tag) __INTRODUCED_IN(30);
+
+#ifdef __cplusplus
+}
+#endif
+
+/** @} */
diff --git a/liblog/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
new file mode 100644
index 0000000..de49fbf
--- /dev/null
+++ b/liblog/include/log/event_tag_map.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2007 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 <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EVENT_TAG_MAP_FILE "/system/etc/event-log-tags"
+
+struct EventTagMap;
+typedef struct EventTagMap EventTagMap;
+
+/*
+ * Open the specified file as an event log tag map.
+ *
+ * Returns NULL on failure.
+ */
+EventTagMap* android_openEventTagMap(const char* fileName);
+
+/*
+ * Close the map.
+ */
+void android_closeEventTagMap(EventTagMap* map);
+
+/*
+ * Look up a tag by index.  Returns the tag string & string length, or NULL if
+ * not found.  Returned string is not guaranteed to be nul terminated.
+ */
+const char* android_lookupEventTag_len(const EventTagMap* map, size_t* len,
+                                       unsigned int tag);
+
+/*
+ * Look up a format by index. Returns the format string & string length,
+ * or NULL if not found. Returned string is not guaranteed to be nul terminated.
+ */
+const char* android_lookupEventFormat_len(const EventTagMap* map, size_t* len,
+                                          unsigned int tag);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
new file mode 100644
index 0000000..d7e9b7d
--- /dev/null
+++ b/liblog/include/log/log.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2005-2014 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
+
+/* Too many in the ecosystem assume these are included */
+#if !defined(_WIN32)
+#include <pthread.h>
+#endif
+#include <stdint.h> /* uint16_t, int32_t */
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <android/log.h>
+#include <log/log_id.h>
+#include <log/log_main.h>
+#include <log/log_radio.h>
+#include <log/log_safetynet.h>
+#include <log/log_system.h>
+#include <log/log_time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * LOG_TAG is the local tag used for the following simplified
+ * logging macros.  You can change this preprocessor definition
+ * before using the other macros to change the tag.
+ */
+
+#ifndef LOG_TAG
+#define LOG_TAG NULL
+#endif
+
+/*
+ * Normally we strip the effects of ALOGV (VERBOSE messages),
+ * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
+ * release builds be defining NDEBUG.  You can modify this (for
+ * example with "#define LOG_NDEBUG 0" at the top of your source
+ * file) to change that behavior.
+ */
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/*
+ * The maximum size of the log entry payload that can be
+ * written to the logger. An attempt to write more than
+ * this amount will result in a truncated log entry.
+ */
+#define LOGGER_ENTRY_MAX_PAYLOAD 4068
+
+/*
+ * Event logging.
+ */
+
+/*
+ * The following should not be used directly.
+ */
+
+int __android_log_bwrite(int32_t tag, const void* payload, size_t len);
+int __android_log_btwrite(int32_t tag, char type, const void* payload,
+                          size_t len);
+int __android_log_bswrite(int32_t tag, const char* payload);
+
+int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len);
+
+#define android_bWriteLog(tag, payload, len) \
+  __android_log_bwrite(tag, payload, len)
+#define android_btWriteLog(tag, type, payload, len) \
+  __android_log_btwrite(tag, type, payload, len)
+
+/*
+ * Event log entry types.
+ */
+typedef enum {
+  /* Special markers for android_log_list_element type */
+  EVENT_TYPE_LIST_STOP = '\n', /* declare end of list  */
+  EVENT_TYPE_UNKNOWN = '?',    /* protocol error       */
+
+  /* must match with declaration in java/android/android/util/EventLog.java */
+  EVENT_TYPE_INT = 0,  /* int32_t */
+  EVENT_TYPE_LONG = 1, /* int64_t */
+  EVENT_TYPE_STRING = 2,
+  EVENT_TYPE_LIST = 3,
+  EVENT_TYPE_FLOAT = 4,
+} AndroidEventLogType;
+
+#ifndef LOG_EVENT_INT
+#define LOG_EVENT_INT(_tag, _value)                                          \
+  {                                                                          \
+    int intBuf = _value;                                                     \
+    (void)android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, sizeof(intBuf)); \
+  }
+#endif
+#ifndef LOG_EVENT_LONG
+#define LOG_EVENT_LONG(_tag, _value)                                            \
+  {                                                                             \
+    long long longBuf = _value;                                                 \
+    (void)android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, sizeof(longBuf)); \
+  }
+#endif
+#ifndef LOG_EVENT_FLOAT
+#define LOG_EVENT_FLOAT(_tag, _value)                           \
+  {                                                             \
+    float floatBuf = _value;                                    \
+    (void)android_btWriteLog(_tag, EVENT_TYPE_FLOAT, &floatBuf, \
+                             sizeof(floatBuf));                 \
+  }
+#endif
+#ifndef LOG_EVENT_STRING
+#define LOG_EVENT_STRING(_tag, _value) \
+  (void)__android_log_bswrite(_tag, _value);
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Release any logger resources (a new log write will immediately re-acquire)
+ *
+ * This is specifically meant to be used by Zygote to close open file descriptors after fork()
+ * and before specialization.  O_CLOEXEC is used on file descriptors, so they will be closed upon
+ * exec() in normal use cases.
+ *
+ * Note that this is not safe to call from a multi-threaded program.
+ */
+void __android_log_close(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
new file mode 100644
index 0000000..deadf20
--- /dev/null
+++ b/liblog/include/log/log_event_list.h
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2005-2016 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 <errno.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+#include <string>
+#endif
+
+#include <log/log.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For manipulating lists of events. */
+
+#define ANDROID_MAX_LIST_NEST_DEPTH 8
+
+/*
+ * The opaque context used to manipulate lists of events.
+ */
+typedef struct android_log_context_internal* android_log_context;
+
+/*
+ * Elements returned when reading a list of events.
+ */
+typedef struct {
+  AndroidEventLogType type;
+  uint16_t complete;
+  uint16_t len;
+  union {
+    int32_t int32;
+    int64_t int64;
+    char* string;
+    float float32;
+  } data;
+} android_log_list_element;
+
+/*
+ * Creates a context associated with an event tag to write elements to
+ * the list of events.
+ */
+android_log_context create_android_logger(uint32_t tag);
+
+/* All lists must be braced by a begin and end call */
+/*
+ * NB: If the first level braces are missing when specifying multiple
+ *     elements, we will manufacturer a list to embrace it for your API
+ *     convenience. For a single element, it will remain solitary.
+ */
+int android_log_write_list_begin(android_log_context ctx);
+int android_log_write_list_end(android_log_context ctx);
+
+int android_log_write_int32(android_log_context ctx, int32_t value);
+int android_log_write_int64(android_log_context ctx, int64_t value);
+int android_log_write_string8(android_log_context ctx, const char* value);
+int android_log_write_string8_len(android_log_context ctx, const char* value,
+                                  size_t maxlen);
+int android_log_write_float32(android_log_context ctx, float value);
+
+/* Submit the composed list context to the specified logger id */
+/* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */
+int android_log_write_list(android_log_context ctx, log_id_t id);
+
+/*
+ * Creates a context from a raw buffer representing a list of events to be read.
+ */
+android_log_context create_android_log_parser(const char* msg, size_t len);
+
+android_log_list_element android_log_read_next(android_log_context ctx);
+android_log_list_element android_log_peek_next(android_log_context ctx);
+
+/* Reset writer context */
+int android_log_reset(android_log_context ctx);
+
+/* Reset reader context */
+int android_log_parser_reset(android_log_context ctx,
+                             const char* msg, size_t len);
+
+/* Finished with reader or writer context */
+int android_log_destroy(android_log_context* ctx);
+
+#ifdef __cplusplus
+/* android_log_list C++ helpers */
+extern "C++" {
+class android_log_event_list {
+ private:
+  android_log_context ctx;
+  int ret;
+
+  android_log_event_list(const android_log_event_list&) = delete;
+  void operator=(const android_log_event_list&) = delete;
+
+ public:
+  explicit android_log_event_list(int tag) : ret(0) {
+    ctx = create_android_logger(static_cast<uint32_t>(tag));
+  }
+  ~android_log_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;
+  }
+
+  android_log_event_list& operator<<(int32_t value) {
+    int retval = android_log_write_int32(ctx, value);
+    if (retval < 0) ret = retval;
+    return *this;
+  }
+
+  android_log_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;
+  }
+
+  android_log_event_list& operator<<(bool value) {
+    int retval = android_log_write_int32(ctx, value ? 1 : 0);
+    if (retval < 0) ret = retval;
+    return *this;
+  }
+
+  android_log_event_list& operator<<(int64_t value) {
+    int retval = android_log_write_int64(ctx, value);
+    if (retval < 0) ret = retval;
+    return *this;
+  }
+
+  android_log_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;
+  }
+
+  android_log_event_list& operator<<(const char* value) {
+    int retval = android_log_write_string8(ctx, value);
+    if (retval < 0) ret = retval;
+    return *this;
+  }
+
+  android_log_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;
+  }
+
+  android_log_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 = android_log_write_list(ctx, id);
+    /* existing errors trump transmission errors */
+    if (!ret) ret = retval;
+    return ret;
+  }
+
+  int operator<<(log_id_t id) {
+    write(id);
+    android_log_destroy(&ctx);
+    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;
+  }
+};
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
new file mode 100644
index 0000000..8e4faeb
--- /dev/null
+++ b/liblog/include/log/log_id.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/log.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * log_id_t helpers
+ */
+log_id_t android_name_to_log_id(const char* logName);
+const char* android_log_id_to_name(log_id_t log_id);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
new file mode 100644
index 0000000..799a8e2
--- /dev/null
+++ b/liblog/include/log/log_main.h
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2005-2017 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 <stdbool.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <android/log.h>
+
+__BEGIN_DECLS
+
+/*
+ * Normally we strip the effects of ALOGV (VERBOSE messages),
+ * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
+ * release builds be defining NDEBUG.  You can modify this (for
+ * example with "#define LOG_NDEBUG 0" at the top of your source
+ * file) to change that behavior.
+ */
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * This file uses ", ## __VA_ARGS__" zero-argument token pasting to
+ * work around issues with debug-only syntax errors in assertions
+ * that are missing format strings.  See commit
+ * 19299904343daf191267564fe32e6cd5c165cd42
+ */
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+#endif
+
+/*
+ * Use __VA_ARGS__ if running a static analyzer,
+ * to avoid warnings of unused variables in __VA_ARGS__.
+ * Use constexpr function in C++ mode, so these macros can be used
+ * in other constexpr functions without warning.
+ */
+#ifdef __clang_analyzer__
+#ifdef __cplusplus
+extern "C++" {
+template <typename... Ts>
+constexpr int __fake_use_va_args(Ts...) {
+  return 0;
+}
+}
+#else
+extern int __fake_use_va_args(int, ...);
+#endif /* __cplusplus */
+#define __FAKE_USE_VA_ARGS(...) ((void)__fake_use_va_args(0, ##__VA_ARGS__))
+#else
+#define __FAKE_USE_VA_ARGS(...) ((void)(0))
+#endif /* __clang_analyzer__ */
+
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
+
+#define android_writeLog(prio, tag, text) __android_log_write(prio, tag, text)
+
+#define android_printLog(prio, tag, ...) \
+  __android_log_print(prio, tag, __VA_ARGS__)
+
+#define android_vprintLog(prio, cond, tag, ...) \
+  __android_log_vprint(prio, tag, __VA_ARGS__)
+
+/*
+ * Log macro that allows you to specify a number for the priority.
+ */
+#ifndef LOG_PRI
+#define LOG_PRI(priority, tag, ...) android_printLog(priority, tag, __VA_ARGS__)
+#endif
+
+/*
+ * Log macro that allows you to pass in a varargs ("args" is a va_list).
+ */
+#ifndef LOG_PRI_VA
+#define LOG_PRI_VA(priority, tag, fmt, args) \
+  android_vprintLog(priority, NULL, tag, fmt, args)
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/* XXX Macros to work around syntax errors in places where format string
+ * arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF
+ * (happens only in debug builds).
+ */
+
+/* Returns 2nd arg.  Used to substitute default value if caller's vararg list
+ * is empty.
+ */
+#define __android_second(dummy, second, ...) second
+
+/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise
+ * returns nothing.
+ */
+#define __android_rest(first, ...) , ##__VA_ARGS__
+
+#define android_printAssert(cond, tag, ...)                     \
+  __android_log_assert(cond, tag,                               \
+                       __android_second(0, ##__VA_ARGS__, NULL) \
+                           __android_rest(__VA_ARGS__))
+
+/*
+ * Log a fatal error.  If the given condition fails, this stops program
+ * execution like a normal assertion, but also generating the given message.
+ * It is NOT stripped from release builds.  Note that the condition test
+ * is -inverted- from the normal assert() semantics.
+ */
+#ifndef LOG_ALWAYS_FATAL_IF
+#define LOG_ALWAYS_FATAL_IF(cond, ...)                                                    \
+  ((__predict_false(cond)) ? (__FAKE_USE_VA_ARGS(__VA_ARGS__),                            \
+                              ((void)android_printAssert(#cond, LOG_TAG, ##__VA_ARGS__))) \
+                           : ((void)0))
+#endif
+
+#ifndef LOG_ALWAYS_FATAL
+#define LOG_ALWAYS_FATAL(...) \
+  (((void)android_printAssert(NULL, LOG_TAG, ##__VA_ARGS__)))
+#endif
+
+/*
+ * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
+ * are stripped out of release builds.
+ */
+
+#if LOG_NDEBUG
+
+#ifndef LOG_FATAL_IF
+#define LOG_FATAL_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
+#endif
+#ifndef LOG_FATAL
+#define LOG_FATAL(...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
+#endif
+
+#else
+
+#ifndef LOG_FATAL_IF
+#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ##__VA_ARGS__)
+#endif
+#ifndef LOG_FATAL
+#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
+#endif
+
+#endif
+
+/*
+ * Assertion that generates a log message when the assertion fails.
+ * Stripped out of release builds.  Uses the current LOG_TAG.
+ */
+#ifndef ALOG_ASSERT
+#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__)
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * C/C++ logging functions.  See the logging documentation for API details.
+ *
+ * We'd like these to be available from C code (in case we import some from
+ * somewhere), so this has a C interface.
+ *
+ * The output will be correct when the log file is shared between multiple
+ * threads and/or multiple processes so long as the operating system
+ * supports O_APPEND.  These calls have mutex-protected data structures
+ * and so are NOT reentrant.  Do not use LOG in a signal handler.
+ */
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Simplified macro to send a verbose log message using the current LOG_TAG.
+ */
+#ifndef ALOGV
+#define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#if LOG_NDEBUG
+#define ALOGV(...)                   \
+  do {                               \
+    __FAKE_USE_VA_ARGS(__VA_ARGS__); \
+    if (false) {                     \
+      __ALOGV(__VA_ARGS__);          \
+    }                                \
+  } while (false)
+#else
+#define ALOGV(...) __ALOGV(__VA_ARGS__)
+#endif
+#endif
+
+#ifndef ALOGV_IF
+#if LOG_NDEBUG
+#define ALOGV_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
+#else
+#define ALOGV_IF(cond, ...)                                                               \
+  ((__predict_false(cond))                                                                \
+       ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+       : ((void)0))
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug log message using the current LOG_TAG.
+ */
+#ifndef ALOGD
+#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGD_IF
+#define ALOGD_IF(cond, ...)                                                             \
+  ((__predict_false(cond))                                                              \
+       ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+       : ((void)0))
+#endif
+
+/*
+ * Simplified macro to send an info log message using the current LOG_TAG.
+ */
+#ifndef ALOGI
+#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGI_IF
+#define ALOGI_IF(cond, ...)                                                            \
+  ((__predict_false(cond))                                                             \
+       ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+       : ((void)0))
+#endif
+
+/*
+ * Simplified macro to send a warning log message using the current LOG_TAG.
+ */
+#ifndef ALOGW
+#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGW_IF
+#define ALOGW_IF(cond, ...)                                                            \
+  ((__predict_false(cond))                                                             \
+       ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+       : ((void)0))
+#endif
+
+/*
+ * Simplified macro to send an error log message using the current LOG_TAG.
+ */
+#ifndef ALOGE
+#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGE_IF
+#define ALOGE_IF(cond, ...)                                                             \
+  ((__predict_false(cond))                                                              \
+       ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+       : ((void)0))
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * verbose priority.
+ */
+#ifndef IF_ALOGV
+#if LOG_NDEBUG
+#define IF_ALOGV() if (false)
+#else
+#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG)
+#endif
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * debug priority.
+ */
+#ifndef IF_ALOGD
+#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * info priority.
+ */
+#ifndef IF_ALOGI
+#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * warn priority.
+ */
+#ifndef IF_ALOGW
+#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * error priority.
+ */
+#ifndef IF_ALOGE
+#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG)
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Basic log message macro.
+ *
+ * Example:
+ *  ALOG(LOG_WARN, NULL, "Failed with error %d", errno);
+ *
+ * The second argument may be NULL or "" to indicate the "global" tag.
+ */
+#ifndef ALOG
+#define ALOG(priority, tag, ...) LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
+#endif
+
+/*
+ * Conditional given a desired logging priority and tag.
+ */
+#ifndef IF_ALOG
+#define IF_ALOG(priority, tag) if (android_testLog(ANDROID_##priority, tag))
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ *    IF_ALOG uses android_testLog, but IF_ALOG can be overridden.
+ *    android_testLog will remain constant in its purpose as a wrapper
+ *        for Android logging filter policy, and can be subject to
+ *        change. It can be reused by the developers that override
+ *        IF_ALOG as a convenient means to reimplement their policy
+ *        over Android.
+ */
+
+/*
+ * Use the per-tag properties "log.tag.<tagname>" to generate a runtime
+ * result of non-zero to expose a log. prio is ANDROID_LOG_VERBOSE to
+ * ANDROID_LOG_FATAL. default_prio if no property. Undefined behavior if
+ * any other value.
+ */
+int __android_log_is_loggable(int prio, const char* tag, int default_prio);
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio);
+
+#if LOG_NDEBUG /* Production */
+#define android_testLog(prio, tag) \
+  (__android_log_is_loggable_len(prio, tag, (tag) ? strlen(tag) : 0, ANDROID_LOG_DEBUG) != 0)
+#else
+#define android_testLog(prio, tag) \
+  (__android_log_is_loggable_len(prio, tag, (tag) ? strlen(tag) : 0, ANDROID_LOG_VERBOSE) != 0)
+#endif
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+__END_DECLS
diff --git a/liblog/include/log/log_properties.h b/liblog/include/log/log_properties.h
new file mode 100644
index 0000000..2a0230f
--- /dev/null
+++ b/liblog/include/log/log_properties.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Returns `1` if the device is debuggable or `0` if not. */
+int __android_log_is_debuggable();
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/log/log_radio.h b/liblog/include/log/log_radio.h
new file mode 100644
index 0000000..f5525c1
--- /dev/null
+++ b/liblog/include/log/log_radio.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/log.h>
+
+/*
+ * Normally we strip the effects of ALOGV (VERBOSE messages),
+ * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
+ * release builds be defining NDEBUG.  You can modify this (for
+ * example with "#define LOG_NDEBUG 0" at the top of your source
+ * file) to change that behavior.
+ */
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
+
+/*
+ * Simplified macro to send a verbose radio log message using current LOG_TAG.
+ */
+#ifndef RLOGV
+#define __RLOGV(...)                                                         \
+  ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, \
+                                 __VA_ARGS__))
+#if LOG_NDEBUG
+#define RLOGV(...)          \
+  do {                      \
+    if (0) {                \
+      __RLOGV(__VA_ARGS__); \
+    }                       \
+  } while (0)
+#else
+#define RLOGV(...) __RLOGV(__VA_ARGS__)
+#endif
+#endif
+
+#ifndef RLOGV_IF
+#if LOG_NDEBUG
+#define RLOGV_IF(cond, ...) ((void)0)
+#else
+#define RLOGV_IF(cond, ...)                                                \
+  ((__predict_false(cond))                                                 \
+       ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, \
+                                        LOG_TAG, __VA_ARGS__))             \
+       : (void)0)
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug radio log message using  current LOG_TAG.
+ */
+#ifndef RLOGD
+#define RLOGD(...)                                                         \
+  ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, \
+                                 __VA_ARGS__))
+#endif
+
+#ifndef RLOGD_IF
+#define RLOGD_IF(cond, ...)                                              \
+  ((__predict_false(cond))                                               \
+       ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, \
+                                        LOG_TAG, __VA_ARGS__))           \
+       : (void)0)
+#endif
+
+/*
+ * Simplified macro to send an info radio log message using  current LOG_TAG.
+ */
+#ifndef RLOGI
+#define RLOGI(...)                                                        \
+  ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, \
+                                 __VA_ARGS__))
+#endif
+
+#ifndef RLOGI_IF
+#define RLOGI_IF(cond, ...)                                             \
+  ((__predict_false(cond))                                              \
+       ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, \
+                                        LOG_TAG, __VA_ARGS__))          \
+       : (void)0)
+#endif
+
+/*
+ * Simplified macro to send a warning radio log message using current LOG_TAG.
+ */
+#ifndef RLOGW
+#define RLOGW(...)                                                        \
+  ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, \
+                                 __VA_ARGS__))
+#endif
+
+#ifndef RLOGW_IF
+#define RLOGW_IF(cond, ...)                                             \
+  ((__predict_false(cond))                                              \
+       ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, \
+                                        LOG_TAG, __VA_ARGS__))          \
+       : (void)0)
+#endif
+
+/*
+ * Simplified macro to send an error radio log message using current LOG_TAG.
+ */
+#ifndef RLOGE
+#define RLOGE(...)                                                         \
+  ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, \
+                                 __VA_ARGS__))
+#endif
+
+#ifndef RLOGE_IF
+#define RLOGE_IF(cond, ...)                                              \
+  ((__predict_false(cond))                                               \
+       ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, \
+                                        LOG_TAG, __VA_ARGS__))           \
+       : (void)0)
+#endif
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
new file mode 100644
index 0000000..1736934
--- /dev/null
+++ b/liblog/include/log/log_read.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2005-2017 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 <stdint.h>
+#include <sys/types.h>
+
+#include <android/log.h>
+#include <log/log_time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
+
+/*
+ * Native log reading interface section. See logcat for sample code.
+ *
+ * The preferred API is an exec of logcat. Likely uses of this interface
+ * are if native code suffers from exec or filtration being too costly,
+ * access to raw information, or parsing is an issue.
+ */
+
+struct logger_entry {
+  uint16_t len;      /* length of the payload */
+  uint16_t hdr_size; /* sizeof(struct logger_entry) */
+  int32_t pid;       /* generating process's pid */
+  uint32_t tid;      /* generating process's tid */
+  uint32_t sec;      /* seconds since Epoch */
+  uint32_t nsec;     /* nanoseconds */
+  uint32_t lid;      /* log id of the payload, bottom 4 bits currently */
+  uint32_t uid;      /* generating process's uid */
+};
+
+/*
+ * The maximum size of a log entry which can be read.
+ * An attempt to read less than this amount may result
+ * in read() returning EINVAL.
+ */
+#define LOGGER_ENTRY_MAX_LEN (5 * 1024)
+
+struct log_msg {
+  union {
+    unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
+    struct logger_entry entry;
+  } __attribute__((aligned(4)));
+#ifdef __cplusplus
+  uint64_t nsec() const {
+    return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec;
+  }
+  log_id_t id() {
+    return static_cast<log_id_t>(entry.lid);
+  }
+  char* msg() {
+    unsigned short hdr_size = entry.hdr_size;
+    if (hdr_size >= sizeof(struct log_msg) - sizeof(entry)) {
+      return nullptr;
+    }
+    return reinterpret_cast<char*>(buf) + hdr_size;
+  }
+  unsigned int len() { return entry.hdr_size + entry.len; }
+#endif
+};
+
+struct logger;
+
+log_id_t android_logger_get_id(struct logger* logger);
+
+/* Clears the given log buffer. */
+int android_logger_clear(struct logger* logger);
+/* Return the allotted size for the given log buffer. */
+long android_logger_get_log_size(struct logger* logger);
+/* Set the allotted size for the given log buffer. */
+int android_logger_set_log_size(struct logger* logger, unsigned long size);
+/* Return the actual, uncompressed size that can be read from the given log buffer. */
+long android_logger_get_log_readable_size(struct logger* logger);
+/* Return the actual, compressed size that the given log buffer is consuming. */
+long android_logger_get_log_consumed_size(struct logger* logger);
+/* Deprecated.  Always returns '4' regardless of input. */
+int android_logger_get_log_version(struct logger* logger);
+
+struct logger_list;
+
+ssize_t android_logger_get_statistics(struct logger_list* logger_list,
+                                      char* buf, size_t len);
+ssize_t android_logger_get_prune_list(struct logger_list* logger_list,
+                                      char* buf, size_t len);
+int android_logger_set_prune_list(struct logger_list* logger_list, const char* buf, size_t len);
+
+/* The below values are used for the `mode` argument of the below functions. */
+/* Note that 0x00000003 were previously used and should be considered reserved. */
+#define ANDROID_LOG_NONBLOCK 0x00000800
+#define ANDROID_LOG_WRAP 0x40000000 /* Block until buffer about to wrap */
+#define ANDROID_LOG_PSTORE 0x80000000
+
+struct logger_list* android_logger_list_alloc(int mode, unsigned int tail,
+                                              pid_t pid);
+struct logger_list* android_logger_list_alloc_time(int mode, log_time start,
+                                                   pid_t pid);
+void android_logger_list_free(struct logger_list* logger_list);
+/* In the purest sense, the following two are orthogonal interfaces */
+int android_logger_list_read(struct logger_list* logger_list,
+                             struct log_msg* log_msg);
+
+/* Multiple log_id_t opens */
+struct logger* android_logger_open(struct logger_list* logger_list, log_id_t id);
+/* Single log_id_t open */
+struct logger_list* android_logger_list_open(log_id_t id, int mode,
+                                             unsigned int tail, pid_t pid);
+#define android_logger_list_close android_logger_list_free
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
new file mode 100644
index 0000000..b2604b5
--- /dev/null
+++ b/liblog/include/log/log_safetynet.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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)
+
+int __android_log_error_write(int tag, const char* subTag, int32_t uid,
+                              const char* data, uint32_t dataLen);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/log/log_system.h b/liblog/include/log/log_system.h
new file mode 100644
index 0000000..6f40515
--- /dev/null
+++ b/liblog/include/log/log_system.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/log.h>
+
+/*
+ * Normally we strip the effects of ALOGV (VERBOSE messages),
+ * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
+ * release builds be defining NDEBUG.  You can modify this (for
+ * example with "#define LOG_NDEBUG 0" at the top of your source
+ * file) to change that behavior.
+ */
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
+
+/*
+ * Simplified macro to send a verbose system log message using current LOG_TAG.
+ */
+#ifndef SLOGV
+#define __SLOGV(...)                                                          \
+  ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, \
+                                 __VA_ARGS__))
+#if LOG_NDEBUG
+#define SLOGV(...)          \
+  do {                      \
+    if (0) {                \
+      __SLOGV(__VA_ARGS__); \
+    }                       \
+  } while (0)
+#else
+#define SLOGV(...) __SLOGV(__VA_ARGS__)
+#endif
+#endif
+
+#ifndef SLOGV_IF
+#if LOG_NDEBUG
+#define SLOGV_IF(cond, ...) ((void)0)
+#else
+#define SLOGV_IF(cond, ...)                                                 \
+  ((__predict_false(cond))                                                  \
+       ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, \
+                                        LOG_TAG, __VA_ARGS__))              \
+       : (void)0)
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug system log message using current LOG_TAG.
+ */
+#ifndef SLOGD
+#define SLOGD(...)                                                          \
+  ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, \
+                                 __VA_ARGS__))
+#endif
+
+#ifndef SLOGD_IF
+#define SLOGD_IF(cond, ...)                                               \
+  ((__predict_false(cond))                                                \
+       ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, \
+                                        LOG_TAG, __VA_ARGS__))            \
+       : (void)0)
+#endif
+
+/*
+ * Simplified macro to send an info system log message using current LOG_TAG.
+ */
+#ifndef SLOGI
+#define SLOGI(...)                                                         \
+  ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, \
+                                 __VA_ARGS__))
+#endif
+
+#ifndef SLOGI_IF
+#define SLOGI_IF(cond, ...)                                              \
+  ((__predict_false(cond))                                               \
+       ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, \
+                                        LOG_TAG, __VA_ARGS__))           \
+       : (void)0)
+#endif
+
+/*
+ * Simplified macro to send a warning system log message using current LOG_TAG.
+ */
+#ifndef SLOGW
+#define SLOGW(...)                                                         \
+  ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, \
+                                 __VA_ARGS__))
+#endif
+
+#ifndef SLOGW_IF
+#define SLOGW_IF(cond, ...)                                              \
+  ((__predict_false(cond))                                               \
+       ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, \
+                                        LOG_TAG, __VA_ARGS__))           \
+       : (void)0)
+#endif
+
+/*
+ * Simplified macro to send an error system log message using current LOG_TAG.
+ */
+#ifndef SLOGE
+#define SLOGE(...)                                                          \
+  ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, \
+                                 __VA_ARGS__))
+#endif
+
+#ifndef SLOGE_IF
+#define SLOGE_IF(cond, ...)                                               \
+  ((__predict_false(cond))                                                \
+       ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, \
+                                        LOG_TAG, __VA_ARGS__))            \
+       : (void)0)
+#endif
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
new file mode 100644
index 0000000..f50764d
--- /dev/null
+++ b/liblog/include/log/log_time.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2005-2017 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 <stdint.h>
+#include <time.h>
+
+/* struct log_time is a wire-format variant of struct timespec */
+#define NS_PER_SEC 1000000000ULL
+#define US_PER_SEC 1000000ULL
+#define MS_PER_SEC 1000ULL
+
+#define LOG_TIME_SEC(t) ((t)->tv_sec)
+/* next power of two after NS_PER_SEC */
+#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
+
+#ifdef __cplusplus
+
+extern "C" {
+
+struct log_time {
+ public:
+  uint32_t tv_sec = 0; /* good to Feb 5 2106 */
+  uint32_t tv_nsec = 0;
+
+  static constexpr timespec EPOCH = {0, 0};
+
+  log_time() {}
+  explicit log_time(const timespec& T)
+      : tv_sec(static_cast<uint32_t>(T.tv_sec)), tv_nsec(static_cast<uint32_t>(T.tv_nsec)) {}
+  explicit log_time(uint32_t sec, uint32_t nsec = 0)
+      : tv_sec(sec), tv_nsec(nsec) {
+  }
+#ifdef __linux__
+  explicit log_time(clockid_t id) {
+    timespec T;
+    clock_gettime(id, &T);
+    tv_sec = static_cast<uint32_t>(T.tv_sec);
+    tv_nsec = static_cast<uint32_t>(T.tv_nsec);
+  }
+#endif
+  /* timespec */
+  bool operator==(const timespec& T) const {
+    return (tv_sec == static_cast<uint32_t>(T.tv_sec)) &&
+           (tv_nsec == static_cast<uint32_t>(T.tv_nsec));
+  }
+  bool operator!=(const timespec& T) const {
+    return !(*this == T);
+  }
+  bool operator<(const timespec& T) const {
+    return (tv_sec < static_cast<uint32_t>(T.tv_sec)) ||
+           ((tv_sec == static_cast<uint32_t>(T.tv_sec)) &&
+            (tv_nsec < static_cast<uint32_t>(T.tv_nsec)));
+  }
+  bool operator>=(const timespec& T) const {
+    return !(*this < T);
+  }
+  bool operator>(const timespec& T) const {
+    return (tv_sec > static_cast<uint32_t>(T.tv_sec)) ||
+           ((tv_sec == static_cast<uint32_t>(T.tv_sec)) &&
+            (tv_nsec > static_cast<uint32_t>(T.tv_nsec)));
+  }
+  bool operator<=(const timespec& T) const {
+    return !(*this > T);
+  }
+
+  /* log_time */
+  bool operator==(const log_time& T) const {
+    return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec);
+  }
+  bool operator!=(const log_time& T) const {
+    return !(*this == T);
+  }
+  bool operator<(const log_time& T) const {
+    return (tv_sec < T.tv_sec) ||
+           ((tv_sec == T.tv_sec) && (tv_nsec < T.tv_nsec));
+  }
+  bool operator>=(const log_time& T) const {
+    return !(*this < T);
+  }
+  bool operator>(const log_time& T) const {
+    return (tv_sec > T.tv_sec) ||
+           ((tv_sec == T.tv_sec) && (tv_nsec > T.tv_nsec));
+  }
+  bool operator<=(const log_time& T) const {
+    return !(*this > T);
+  }
+
+  log_time operator-=(const log_time& T) {
+    // No concept of negative time, clamp to EPOCH
+    if (*this <= T) {
+      return *this = log_time(EPOCH);
+    }
+
+    if (this->tv_nsec < T.tv_nsec) {
+      --this->tv_sec;
+      this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
+    } else {
+      this->tv_nsec -= T.tv_nsec;
+    }
+    this->tv_sec -= T.tv_sec;
+
+    return *this;
+  }
+  log_time operator-(const log_time& T) const {
+    log_time local(*this);
+    return local -= T;
+  }
+  log_time operator+=(const log_time& T) {
+    this->tv_nsec += T.tv_nsec;
+    if (this->tv_nsec >= NS_PER_SEC) {
+      this->tv_nsec -= NS_PER_SEC;
+      ++this->tv_sec;
+    }
+    this->tv_sec += T.tv_sec;
+
+    return *this;
+  }
+  log_time operator+(const log_time& T) const {
+    log_time local(*this);
+    return local += T;
+  }
+
+  uint64_t nsec() const {
+    return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
+  }
+  uint64_t usec() const {
+    return static_cast<uint64_t>(tv_sec) * US_PER_SEC +
+           tv_nsec / (NS_PER_SEC / US_PER_SEC);
+  }
+  uint64_t msec() const {
+    return static_cast<uint64_t>(tv_sec) * MS_PER_SEC +
+           tv_nsec / (NS_PER_SEC / MS_PER_SEC);
+  }
+
+  /* Add %#q for the fraction of a second to the standard library functions */
+  char* strptime(const char* s, const char* format);
+} __attribute__((__packed__));
+}
+
+#else /* __cplusplus */
+
+typedef struct log_time {
+  uint32_t tv_sec;
+  uint32_t tv_nsec;
+} __attribute__((__packed__)) log_time;
+
+#endif /* __cplusplus */
diff --git a/liblog/include/log/logprint.h b/liblog/include/log/logprint.h
new file mode 100644
index 0000000..0cff640
--- /dev/null
+++ b/liblog/include/log/logprint.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2006 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 <stdint.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <android/log.h>
+#include <log/event_tag_map.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+  /* Verbs */
+  FORMAT_OFF = 0,
+  FORMAT_BRIEF,
+  FORMAT_PROCESS,
+  FORMAT_TAG,
+  FORMAT_THREAD,
+  FORMAT_RAW,
+  FORMAT_TIME,
+  FORMAT_THREADTIME,
+  FORMAT_LONG,
+  /* Adverbs. The following are modifiers to above format verbs */
+  FORMAT_MODIFIER_COLOR,     /* converts priority to color */
+  FORMAT_MODIFIER_TIME_USEC, /* switches from msec to usec time precision */
+  FORMAT_MODIFIER_PRINTABLE, /* converts non-printable to printable escapes */
+  FORMAT_MODIFIER_YEAR,      /* Adds year to date */
+  FORMAT_MODIFIER_ZONE,      /* Adds zone to date, + UTC */
+  FORMAT_MODIFIER_EPOCH,     /* Print time as seconds since Jan 1 1970 */
+  FORMAT_MODIFIER_MONOTONIC, /* Print cpu time as seconds since start */
+  FORMAT_MODIFIER_UID,       /* Adds uid */
+  FORMAT_MODIFIER_DESCRIPT,  /* Adds descriptive */
+  /* private, undocumented */
+  FORMAT_MODIFIER_TIME_NSEC, /* switches from msec to nsec time precision */
+} AndroidLogPrintFormat;
+
+typedef struct AndroidLogFormat_t AndroidLogFormat;
+
+typedef struct AndroidLogEntry_t {
+  time_t tv_sec;
+  long tv_nsec;
+  android_LogPriority priority;
+  int32_t uid;
+  int32_t pid;
+  int32_t tid;
+  const char* tag;
+  size_t tagLen;
+  size_t messageLen;
+  const char* message;
+} AndroidLogEntry;
+
+AndroidLogFormat* android_log_format_new();
+
+void android_log_format_free(AndroidLogFormat* p_format);
+
+/* currently returns 0 if format is a modifier, 1 if not */
+int android_log_setPrintFormat(AndroidLogFormat* p_format,
+                               AndroidLogPrintFormat format);
+
+/**
+ * Returns FORMAT_OFF on invalid string
+ */
+AndroidLogPrintFormat android_log_formatFromString(const char* s);
+
+/**
+ * filterExpression: a single filter expression
+ * eg "AT:d"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterRule(AndroidLogFormat* p_format,
+                              const char* filterExpression);
+
+/**
+ * filterString: a whitespace-separated set of filter expressions
+ * eg "AT:d *:i"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterString(AndroidLogFormat* p_format,
+                                const char* filterString);
+
+/**
+ * returns 1 if this log line should be printed based on its priority
+ * and tag, and 0 if it should not
+ */
+int android_log_shouldPrintLine(AndroidLogFormat* p_format, const char* tag,
+                                android_LogPriority pri);
+
+/**
+ * Splits a wire-format buffer into an AndroidLogEntry
+ * entry allocated by caller. Pointers will point directly into buf
+ *
+ * Returns 0 on success and -1 on invalid wire format (entry will be
+ * in unspecified state)
+ */
+int android_log_processLogBuffer(struct logger_entry* buf,
+                                 AndroidLogEntry* entry);
+
+/**
+ * Like android_log_processLogBuffer, but for binary logs.
+ *
+ * If "map" is non-NULL, it will be used to convert the log tag number
+ * into a string.
+ */
+int android_log_processBinaryLogBuffer(struct logger_entry* buf,
+                                       AndroidLogEntry* entry,
+                                       const EventTagMap* map, char* messageBuf,
+                                       int messageBufLen);
+
+/**
+ * Formats a log message into a buffer
+ *
+ * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
+ * If return value != defaultBuffer, caller must call free()
+ * Returns NULL on malloc error
+ */
+
+char* android_log_formatLogLine(AndroidLogFormat* p_format, char* defaultBuffer,
+                                size_t defaultBufferSize,
+                                const AndroidLogEntry* p_line,
+                                size_t* p_outLength);
+
+/**
+ * Formats a log message into a FILE*.
+ */
+size_t android_log_printLogLine(AndroidLogFormat* p_format, FILE* fp, const AndroidLogEntry* entry);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
new file mode 100644
index 0000000..d414b07
--- /dev/null
+++ b/liblog/include/private/android_logger.h
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+/* This file is used to define the internal protocol for the Android Logger */
+
+#pragma once
+
+/* Android private interfaces */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+#include <string>
+#endif
+
+#include <log/log.h>
+#include <log/log_event_list.h>
+
+#define LOGGER_MAGIC 'l'
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Header Structure to pstore */
+typedef struct __attribute__((__packed__)) {
+  uint8_t magic;
+  uint16_t len;
+  uint16_t uid;
+  uint16_t pid;
+} android_pmsg_log_header_t;
+
+/* Header Structure to logd, and second header for pstore */
+typedef struct __attribute__((__packed__)) {
+  uint8_t id;
+  uint16_t tid;
+  log_time realtime;
+} android_log_header_t;
+
+/* Event Header Structure to logd */
+typedef struct __attribute__((__packed__)) {
+  int32_t tag;  // Little Endian Order
+} android_event_header_t;
+
+// Event payload EVENT_TYPE_LIST
+typedef struct __attribute__((__packed__)) {
+  int8_t type;  // EVENT_TYPE_LIST
+  int8_t element_count;
+} android_event_list_t;
+
+// Event payload EVENT_TYPE_FLOAT
+typedef struct __attribute__((__packed__)) {
+  int8_t type;  // EVENT_TYPE_FLOAT
+  float data;
+} android_event_float_t;
+
+/* Event payload EVENT_TYPE_INT */
+typedef struct __attribute__((__packed__)) {
+  int8_t type;   // EVENT_TYPE_INT
+  int32_t data;  // Little Endian Order
+} android_event_int_t;
+
+/* Event with single EVENT_TYPE_INT */
+typedef struct __attribute__((__packed__)) {
+  android_event_header_t header;
+  android_event_int_t payload;
+} android_log_event_int_t;
+
+/* Event payload EVENT_TYPE_LONG */
+typedef struct __attribute__((__packed__)) {
+  int8_t type;   // EVENT_TYPE_LONG
+  int64_t data;  // Little Endian Order
+} android_event_long_t;
+
+/* Event with single EVENT_TYPE_LONG */
+typedef struct __attribute__((__packed__)) {
+  android_event_header_t header;
+  android_event_long_t payload;
+} android_log_event_long_t;
+
+/*
+ * Event payload EVENT_TYPE_STRING
+ *
+ * Danger: do not embed this structure into another structure.
+ * This structure uses a flexible array member, and when
+ * compiled using g++, __builtin_object_size(data, 1) returns
+ * a bad value. This is possibly a g++ bug, or a bug due to
+ * the fact that flexible array members are not supported
+ * in C++.
+ * http://stackoverflow.com/questions/4412749/are-flexible-array-members-valid-in-c
+ */
+
+typedef struct __attribute__((__packed__)) {
+  int8_t type;     // EVENT_TYPE_STRING;
+  int32_t length;  // Little Endian Order
+  char data[];
+} android_event_string_t;
+
+/* Event with single EVENT_TYPE_STRING */
+typedef struct __attribute__((__packed__)) {
+  android_event_header_t header;
+  int8_t type;     // EVENT_TYPE_STRING;
+  int32_t length;  // Little Endian Order
+  char data[];
+} android_log_event_string_t;
+
+#define ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE 256 /* 1MB file */
+#define ANDROID_LOG_PMSG_FILE_SEQUENCE 1000
+
+ssize_t __android_log_pmsg_file_write(log_id_t logId, char prio,
+                                      const char* filename, const char* buf,
+                                      size_t len);
+
+#define ANDROID_LOG_ANY ANDROID_LOG_UNKNOWN
+
+/* first 5 arguments match __android_log_msg_file_write, a cast is safe */
+typedef ssize_t (*__android_log_pmsg_file_read_fn)(log_id_t logId, char prio,
+                                                   const char* filename,
+                                                   const char* buf, size_t len,
+                                                   void* arg);
+
+ssize_t __android_log_pmsg_file_read(log_id_t logId, char prio,
+                                     const char* prefix,
+                                     __android_log_pmsg_file_read_fn fn,
+                                     void* arg);
+
+int __android_log_security_bwrite(int32_t tag, const void* payload, size_t len);
+int __android_log_security_bswrite(int32_t tag, const char* payload);
+int __android_log_security(); /* Device Owner is present */
+
+/* Retrieve the composed event buffer */
+int android_log_write_list_buffer(android_log_context ctx, const char** msg);
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/liblog/include_vndk/android b/liblog/include_vndk/android
new file mode 120000
index 0000000..a3c0320
--- /dev/null
+++ b/liblog/include_vndk/android
@@ -0,0 +1 @@
+../include/android
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log.h b/liblog/include_vndk/log/log.h
new file mode 100644
index 0000000..fee18c6
--- /dev/null
+++ b/liblog/include_vndk/log/log.h
@@ -0,0 +1,27 @@
+/*Special log.h file for VNDK linking modules*/
+
+#pragma once
+
+/* Historically vendors have depended on these headers being included. */
+#include <fcntl.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include <android/log.h>
+#include <log/log_id.h>
+#include <log/log_main.h>
+#include <log/log_radio.h>
+#include <log/log_read.h>
+#include <log/log_safetynet.h>
+#include <log/log_system.h>
+#include <log/log_time.h>
+
+/*
+ * LOG_TAG is the local tag used for the following simplified
+ * logging macros.  You can change this preprocessor definition
+ * before using the other macros to change the tag.
+ */
+
+#ifndef LOG_TAG
+#define LOG_TAG NULL
+#endif
diff --git a/liblog/include_vndk/log/log_event_list.h b/liblog/include_vndk/log/log_event_list.h
new file mode 100644
index 0000000..1f3dd37
--- /dev/null
+++ b/liblog/include_vndk/log/log_event_list.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2005-2016 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.
+ */
+
+/* Special log_event_list.h file for VNDK linking modules */
+
+#ifndef _LIBS_LOG_EVENT_LIST_H
+#define _LIBS_LOG_EVENT_LIST_H
+
+#include <stdint.h>
+
+#include <log/log_id.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The opaque context used to manipulate lists of events.
+ */
+#ifndef __android_log_context_defined
+#define __android_log_context_defined
+typedef struct android_log_context_internal* android_log_context;
+#endif
+
+/*
+ * Creates a context associated with an event tag to write elements to
+ * the list of events.
+ */
+android_log_context create_android_logger(uint32_t tag);
+
+/* All lists must be braced by a begin and end call */
+/*
+ * NB: If the first level braces are missing when specifying multiple
+ *     elements, we will manufacturer a list to embrace it for your API
+ *     convenience. For a single element, it will remain solitary.
+ */
+int android_log_write_list_begin(android_log_context ctx);
+int android_log_write_list_end(android_log_context ctx);
+
+int android_log_write_int32(android_log_context ctx, int32_t value);
+int android_log_write_int64(android_log_context ctx, int64_t value);
+int android_log_write_string8(android_log_context ctx, const char* value);
+int android_log_write_string8_len(android_log_context ctx, const char* value,
+                                  size_t maxlen);
+int android_log_write_float32(android_log_context ctx, float value);
+
+/* Submit the composed list context to the specified logger id */
+/* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */
+int android_log_write_list(android_log_context ctx, log_id_t id);
+
+/* Reset writer context */
+int android_log_reset(android_log_context ctx);
+
+/* Reset reader context */
+int android_log_parser_reset(android_log_context ctx,
+                             const char* msg, size_t len);
+
+/* Finished with reader or writer context */
+int android_log_destroy(android_log_context* ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_EVENT_LIST_H */
diff --git a/liblog/include_vndk/log/log_id.h b/liblog/include_vndk/log/log_id.h
new file mode 120000
index 0000000..dce92b9
--- /dev/null
+++ b/liblog/include_vndk/log/log_id.h
@@ -0,0 +1 @@
+../../include/log/log_id.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_main.h b/liblog/include_vndk/log/log_main.h
new file mode 120000
index 0000000..f2ec018
--- /dev/null
+++ b/liblog/include_vndk/log/log_main.h
@@ -0,0 +1 @@
+../../include/log/log_main.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_properties.h b/liblog/include_vndk/log/log_properties.h
new file mode 120000
index 0000000..bbec426
--- /dev/null
+++ b/liblog/include_vndk/log/log_properties.h
@@ -0,0 +1 @@
+../../include/log/log_properties.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_radio.h b/liblog/include_vndk/log/log_radio.h
new file mode 120000
index 0000000..1e12b32
--- /dev/null
+++ b/liblog/include_vndk/log/log_radio.h
@@ -0,0 +1 @@
+../../include/log/log_radio.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_read.h b/liblog/include_vndk/log/log_read.h
new file mode 120000
index 0000000..01de8b9
--- /dev/null
+++ b/liblog/include_vndk/log/log_read.h
@@ -0,0 +1 @@
+../../include/log/log_read.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_safetynet.h b/liblog/include_vndk/log/log_safetynet.h
new file mode 120000
index 0000000..a4614e7
--- /dev/null
+++ b/liblog/include_vndk/log/log_safetynet.h
@@ -0,0 +1 @@
+../../include/log/log_safetynet.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_system.h b/liblog/include_vndk/log/log_system.h
new file mode 120000
index 0000000..d0d3904
--- /dev/null
+++ b/liblog/include_vndk/log/log_system.h
@@ -0,0 +1 @@
+../../include/log/log_system.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_time.h b/liblog/include_vndk/log/log_time.h
new file mode 100644
index 0000000..5a09959
--- /dev/null
+++ b/liblog/include_vndk/log/log_time.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2005-2017 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 _LIBS_LOG_LOG_TIME_H
+#define _LIBS_LOG_LOG_TIME_H
+
+#include <stdint.h>
+
+/* struct log_time is a wire-format variant of struct timespec */
+#ifndef NS_PER_SEC
+#define NS_PER_SEC 1000000000ULL
+#endif
+#ifndef US_PER_SEC
+#define US_PER_SEC 1000000ULL
+#endif
+#ifndef MS_PER_SEC
+#define MS_PER_SEC 1000ULL
+#endif
+
+#ifndef __struct_log_time_defined
+#define __struct_log_time_defined
+
+#define LOG_TIME_SEC(t) ((t)->tv_sec)
+/* next power of two after NS_PER_SEC */
+#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
+
+typedef struct log_time {
+  uint32_t tv_sec;
+  uint32_t tv_nsec;
+} __attribute__((__packed__)) log_time;
+
+#endif
+
+#endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
new file mode 100644
index 0000000..440e7df
--- /dev/null
+++ b/liblog/liblog.map.txt
@@ -0,0 +1,93 @@
+LIBLOG {
+  global:
+    android_name_to_log_id; # systemapi llndk
+    android_log_id_to_name; # llndk
+    __android_log_assert;
+    __android_log_buf_print;
+    __android_log_buf_write;
+    __android_log_print;
+    __android_log_vprint;
+    __android_log_write;
+  local:
+    *;
+};
+
+LIBLOG_L {
+  global:
+    android_logger_clear; # llndk
+    android_logger_get_id; # llndk
+    android_logger_get_log_readable_size; # llndk
+    android_logger_get_log_version; # llndk
+    android_logger_get_log_size; # llndk
+    android_logger_list_alloc; # systemapi llndk
+    android_logger_list_alloc_time; # systemapi llndk
+    android_logger_list_free; # systemapi llndk
+    android_logger_list_open; # systemapi llndk
+    android_logger_list_read; # systemapi llndk
+    android_logger_open; # systemapi llndk
+    android_logger_set_log_size; # llndk
+};
+
+LIBLOG_M {
+  global:
+    android_logger_get_prune_list; # llndk
+    android_logger_set_prune_list; # llndk
+    android_logger_get_statistics; # llndk
+    __android_log_error_write; # systemapi llndk
+    __android_log_is_loggable;
+    create_android_logger; # systemapi llndk
+    android_log_destroy; # systemapi llndk
+    android_log_write_list_begin; # systemapi llndk
+    android_log_write_list_end; # systemapi llndk
+    android_log_write_int32; # systemapi llndk
+    android_log_write_int64; # systemapi llndk
+    android_log_write_string8; # systemapi llndk
+    android_log_write_string8_len; # systemapi llndk
+    android_log_write_float32; # systemapi llndk
+    android_log_write_list; # systemapi llndk
+
+};
+
+LIBLOG_O {
+  global:
+    __android_log_is_loggable_len;
+    __android_log_is_debuggable; # systemapi llndk
+};
+
+LIBLOG_Q { # introduced=29
+  global:
+    __android_log_bswrite; # systemapi
+    __android_log_btwrite; # systemapi
+    __android_log_bwrite; # systemapi
+    __android_log_close; # systemapi
+    __android_log_security; # systemapi
+    android_log_reset; # llndk
+    android_log_parser_reset; # llndk
+};
+
+LIBLOG_R { # introduced=30
+  global:
+    __android_log_call_aborter;
+    __android_log_default_aborter;
+    __android_log_get_minimum_priority;
+    __android_log_logd_logger;
+    __android_log_security_bswrite; # systemapi
+    __android_log_set_aborter;
+    __android_log_set_default_tag;
+    __android_log_set_logger;
+    __android_log_set_minimum_priority;
+    __android_log_stderr_logger;
+    __android_log_write_log_message;
+};
+
+LIBLOG_PRIVATE {
+  global:
+    __android_log_pmsg_file_read;
+    __android_log_pmsg_file_write;
+    android_openEventTagMap;
+    android_log_processBinaryLogBuffer;
+    android_log_processLogBuffer;
+    android_log_read_next;
+    android_log_write_list_buffer;
+    create_android_log_parser;
+};
diff --git a/liblog/log_event_list.cpp b/liblog/log_event_list.cpp
new file mode 100644
index 0000000..cb70d48
--- /dev/null
+++ b/liblog/log_event_list.cpp
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2016 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 <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <log/log_event_list.h>
+#include <private/android_logger.h>
+
+#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
+
+enum ReadWriteFlag {
+  kAndroidLoggerRead = 1,
+  kAndroidLoggerWrite = 2,
+};
+
+struct android_log_context_internal {
+  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 */
+  ReadWriteFlag read_write_flag;
+  uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
+};
+
+static void init_context(android_log_context_internal* context, uint32_t tag) {
+  context->tag = tag;
+  context->read_write_flag = kAndroidLoggerWrite;
+  size_t needed = sizeof(android_event_list_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;
+}
+
+static void init_parser_context(android_log_context_internal* context, const char* msg,
+                                size_t len) {
+  len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD;
+  context->len = len;
+  memcpy(context->storage, msg, len);
+  context->read_write_flag = kAndroidLoggerRead;
+}
+
+android_log_context create_android_logger(uint32_t tag) {
+  android_log_context_internal* context;
+
+  context =
+      static_cast<android_log_context_internal*>(calloc(1, sizeof(android_log_context_internal)));
+  if (!context) {
+    return NULL;
+  }
+  init_context(context, tag);
+
+  return (android_log_context)context;
+}
+
+android_log_context create_android_log_parser(const char* msg, size_t len) {
+  android_log_context_internal* context;
+
+  context =
+      static_cast<android_log_context_internal*>(calloc(1, sizeof(android_log_context_internal)));
+  if (!context) {
+    return NULL;
+  }
+  init_parser_context(context, msg, len);
+
+  return (android_log_context)context;
+}
+
+int android_log_destroy(android_log_context* ctx) {
+  android_log_context_internal* context;
+
+  context = (android_log_context_internal*)*ctx;
+  if (!context) {
+    return -EBADF;
+  }
+  memset(context, 0, sizeof(*context));
+  free(context);
+  *ctx = NULL;
+  return 0;
+}
+
+int android_log_reset(android_log_context context) {
+  uint32_t tag;
+
+  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+    return -EBADF;
+  }
+
+  tag = context->tag;
+  memset(context, 0, sizeof(*context));
+  init_context(context, tag);
+
+  return 0;
+}
+
+int android_log_parser_reset(android_log_context context, const char* msg, size_t len) {
+  if (!context || (kAndroidLoggerRead != context->read_write_flag)) {
+    return -EBADF;
+  }
+
+  memset(context, 0, sizeof(*context));
+  init_parser_context(context, msg, len);
+
+  return 0;
+}
+
+int android_log_write_list_begin(android_log_context context) {
+  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+    return -EBADF;
+  }
+  if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+    context->overflow = true;
+    return -EOVERFLOW;
+  }
+  size_t needed = sizeof(android_event_list_t);
+  if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+    context->overflow = true;
+    return -EIO;
+  }
+  context->count[context->list_nest_depth]++;
+  context->list_nest_depth++;
+  if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+    context->overflow = true;
+    return -EOVERFLOW;
+  }
+  if (context->overflow) {
+    return -EIO;
+  }
+  auto* event_list = reinterpret_cast<android_event_list_t*>(&context->storage[context->pos]);
+  event_list->type = EVENT_TYPE_LIST;
+  event_list->element_count = 0;
+  context->list[context->list_nest_depth] = context->pos + 1;
+  context->count[context->list_nest_depth] = 0;
+  context->pos += needed;
+  return 0;
+}
+
+int android_log_write_int32(android_log_context context, int32_t value) {
+  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+    return -EBADF;
+  }
+  if (context->overflow) {
+    return -EIO;
+  }
+  size_t needed = sizeof(android_event_int_t);
+  if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+    context->overflow = true;
+    return -EIO;
+  }
+  context->count[context->list_nest_depth]++;
+  auto* event_int = reinterpret_cast<android_event_int_t*>(&context->storage[context->pos]);
+  event_int->type = EVENT_TYPE_INT;
+  event_int->data = value;
+  context->pos += needed;
+  return 0;
+}
+
+int android_log_write_int64(android_log_context context, int64_t value) {
+  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+    return -EBADF;
+  }
+  if (context->overflow) {
+    return -EIO;
+  }
+  size_t needed = sizeof(android_event_long_t);
+  if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+    context->overflow = true;
+    return -EIO;
+  }
+  context->count[context->list_nest_depth]++;
+  auto* event_long = reinterpret_cast<android_event_long_t*>(&context->storage[context->pos]);
+  event_long->type = EVENT_TYPE_LONG;
+  event_long->data = value;
+  context->pos += needed;
+  return 0;
+}
+
+int android_log_write_string8_len(android_log_context context, const char* value, size_t maxlen) {
+  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+    return -EBADF;
+  }
+  if (context->overflow) {
+    return -EIO;
+  }
+  if (!value) {
+    value = "";
+  }
+  int32_t len = strnlen(value, maxlen);
+  size_t needed = sizeof(android_event_string_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]++;
+  auto* event_string = reinterpret_cast<android_event_string_t*>(&context->storage[context->pos]);
+  event_string->type = EVENT_TYPE_STRING;
+  event_string->length = len;
+  if (len) {
+    memcpy(&event_string->data, value, len);
+  }
+  context->pos += needed;
+  return len;
+}
+
+int android_log_write_string8(android_log_context ctx, const char* value) {
+  return android_log_write_string8_len(ctx, value, MAX_EVENT_PAYLOAD);
+}
+
+int android_log_write_float32(android_log_context context, float value) {
+  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+    return -EBADF;
+  }
+  if (context->overflow) {
+    return -EIO;
+  }
+  size_t needed = sizeof(android_event_float_t);
+  if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+    context->overflow = true;
+    return -EIO;
+  }
+  context->count[context->list_nest_depth]++;
+  auto* event_float = reinterpret_cast<android_event_float_t*>(&context->storage[context->pos]);
+  event_float->type = EVENT_TYPE_FLOAT;
+  event_float->data = value;
+  context->pos += needed;
+  return 0;
+}
+
+int android_log_write_list_end(android_log_context context) {
+  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+    return -EBADF;
+  }
+  if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+    context->overflow = true;
+    context->list_nest_depth--;
+    return -EOVERFLOW;
+  }
+  if (!context->list_nest_depth) {
+    context->overflow = true;
+    return -EOVERFLOW;
+  }
+  if (context->list[context->list_nest_depth] <= 0) {
+    context->list_nest_depth--;
+    context->overflow = true;
+    return -EOVERFLOW;
+  }
+  context->storage[context->list[context->list_nest_depth]] =
+      context->count[context->list_nest_depth];
+  context->list_nest_depth--;
+  return 0;
+}
+
+/*
+ * Logs the list of elements to the event log.
+ */
+int android_log_write_list(android_log_context context, log_id_t id) {
+  const char* msg;
+  ssize_t len;
+
+  if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY) && (id != LOG_ID_STATS)) {
+    return -EINVAL;
+  }
+
+  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);
+  }
+  return (id == LOG_ID_EVENTS)
+             ? __android_log_bwrite(context->tag, msg, len)
+             : ((id == LOG_ID_STATS) ? __android_log_stats_bwrite(context->tag, msg, len)
+                                     : __android_log_security_bwrite(context->tag, msg, len));
+}
+
+int android_log_write_list_buffer(android_log_context context, const char** buffer) {
+  const char* msg;
+  ssize_t len;
+
+  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+    return -EBADF;
+  }
+  if (context->list_nest_depth) {
+    return -EIO;
+  }
+  if (buffer == NULL) {
+    return -EFAULT;
+  }
+  /* 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);
+  }
+  *buffer = msg;
+  return len;
+}
+
+/*
+ * Gets the next element. Parsing errors result in an EVENT_TYPE_UNKNOWN type.
+ * If there is nothing to process, the complete field is set to non-zero. If
+ * an EVENT_TYPE_UNKNOWN type is returned once, and the caller does not check
+ * this and continues to call this function, the behavior is undefined
+ * (although it won't crash).
+ */
+static android_log_list_element android_log_read_next_internal(android_log_context context,
+                                                               int peek) {
+  android_log_list_element elem;
+  unsigned pos;
+
+  memset(&elem, 0, sizeof(elem));
+
+  /* Nothing to parse from this context, so return complete. */
+  if (!context || (kAndroidLoggerRead != context->read_write_flag) ||
+      (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) ||
+      (context->count[context->list_nest_depth] >=
+       (MAX_EVENT_PAYLOAD / (sizeof(uint8_t) + sizeof(uint8_t))))) {
+    elem.type = EVENT_TYPE_UNKNOWN;
+    if (context &&
+        (context->list_stop || ((context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) &&
+                                !context->count[context->list_nest_depth]))) {
+      elem.type = EVENT_TYPE_LIST_STOP;
+    }
+    elem.complete = true;
+    return elem;
+  }
+
+  /*
+   * Use a different variable to update the position in case this
+   * operation is a "peek".
+   */
+  pos = context->pos;
+  if (context->list_stop) {
+    elem.type = EVENT_TYPE_LIST_STOP;
+    elem.complete = !context->count[0] && (!context->list_nest_depth ||
+                                           ((context->list_nest_depth == 1) && !context->count[1]));
+    if (!peek) {
+      /* Suck in superfluous stop */
+      if (context->storage[pos] == EVENT_TYPE_LIST_STOP) {
+        context->pos = pos + 1;
+      }
+      if (context->list_nest_depth) {
+        --context->list_nest_depth;
+        if (context->count[context->list_nest_depth]) {
+          context->list_stop = false;
+        }
+      } else {
+        context->list_stop = false;
+      }
+    }
+    return elem;
+  }
+  if ((pos + 1) > context->len) {
+    elem.type = EVENT_TYPE_UNKNOWN;
+    elem.complete = true;
+    return elem;
+  }
+
+  elem.type = static_cast<AndroidEventLogType>(context->storage[pos]);
+  switch ((int)elem.type) {
+    case EVENT_TYPE_FLOAT:
+    /* Rely on union to translate elem.data.int32 into elem.data.float32 */
+    /* FALLTHRU */
+    case EVENT_TYPE_INT: {
+      elem.len = sizeof(int32_t);
+      if ((pos + sizeof(android_event_int_t)) > context->len) {
+        elem.type = EVENT_TYPE_UNKNOWN;
+        return elem;
+      }
+
+      auto* event_int = reinterpret_cast<android_event_int_t*>(&context->storage[pos]);
+      pos += sizeof(android_event_int_t);
+      elem.data.int32 = event_int->data;
+      /* common tangeable object suffix */
+      elem.complete = !context->list_nest_depth && !context->count[0];
+      if (!peek) {
+        if (!context->count[context->list_nest_depth] ||
+            !--(context->count[context->list_nest_depth])) {
+          context->list_stop = true;
+        }
+        context->pos = pos;
+      }
+      return elem;
+    }
+
+    case EVENT_TYPE_LONG: {
+      elem.len = sizeof(int64_t);
+      if ((pos + sizeof(android_event_long_t)) > context->len) {
+        elem.type = EVENT_TYPE_UNKNOWN;
+        return elem;
+      }
+
+      auto* event_long = reinterpret_cast<android_event_long_t*>(&context->storage[pos]);
+      pos += sizeof(android_event_long_t);
+      elem.data.int64 = event_long->data;
+      /* common tangeable object suffix */
+      elem.complete = !context->list_nest_depth && !context->count[0];
+      if (!peek) {
+        if (!context->count[context->list_nest_depth] ||
+            !--(context->count[context->list_nest_depth])) {
+          context->list_stop = true;
+        }
+        context->pos = pos;
+      }
+      return elem;
+    }
+
+    case EVENT_TYPE_STRING: {
+      if ((pos + sizeof(android_event_string_t)) > context->len) {
+        elem.type = EVENT_TYPE_UNKNOWN;
+        elem.complete = true;
+        return elem;
+      }
+      auto* event_string = reinterpret_cast<android_event_string_t*>(&context->storage[pos]);
+      pos += sizeof(android_event_string_t);
+      // Wire format is int32_t, but elem.len is uint16_t...
+      if (event_string->length >= UINT16_MAX) {
+        elem.type = EVENT_TYPE_UNKNOWN;
+        return elem;
+      }
+      elem.len = event_string->length;
+      if ((pos + elem.len) > context->len) {
+        elem.len = context->len - pos; /* truncate string */
+        elem.complete = true;
+        if (!elem.len) {
+          elem.type = EVENT_TYPE_UNKNOWN;
+          return elem;
+        }
+      }
+      elem.data.string = event_string->data;
+      /* common tangeable object suffix */
+      pos += elem.len;
+      elem.complete = !context->list_nest_depth && !context->count[0];
+      if (!peek) {
+        if (!context->count[context->list_nest_depth] ||
+            !--(context->count[context->list_nest_depth])) {
+          context->list_stop = true;
+        }
+        context->pos = pos;
+      }
+      return elem;
+    }
+
+    case EVENT_TYPE_LIST: {
+      if ((pos + sizeof(android_event_list_t)) > context->len) {
+        elem.type = EVENT_TYPE_UNKNOWN;
+        elem.complete = true;
+        return elem;
+      }
+      auto* event_list = reinterpret_cast<android_event_list_t*>(&context->storage[pos]);
+      pos += sizeof(android_event_list_t);
+      elem.complete = context->list_nest_depth >= ANDROID_MAX_LIST_NEST_DEPTH;
+      if (peek) {
+        return elem;
+      }
+      if (context->count[context->list_nest_depth]) {
+        context->count[context->list_nest_depth]--;
+      }
+      context->list_stop = event_list->element_count == 0;
+      context->list_nest_depth++;
+      if (context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) {
+        context->count[context->list_nest_depth] = event_list->element_count;
+      }
+      context->pos = pos;
+      return elem;
+    }
+
+    case EVENT_TYPE_LIST_STOP: /* Suprise Newline terminates lists. */
+      pos++;
+      if (!peek) {
+        context->pos = pos;
+      }
+      elem.type = EVENT_TYPE_UNKNOWN;
+      elem.complete = !context->list_nest_depth;
+      if (context->list_nest_depth > 0) {
+        elem.type = EVENT_TYPE_LIST_STOP;
+        if (!peek) {
+          context->list_nest_depth--;
+        }
+      }
+      return elem;
+
+    default:
+      elem.type = EVENT_TYPE_UNKNOWN;
+      return elem;
+  }
+}
+
+android_log_list_element android_log_read_next(android_log_context ctx) {
+  return android_log_read_next_internal(ctx, 0);
+}
+
+android_log_list_element android_log_peek_next(android_log_context ctx) {
+  return android_log_read_next_internal(ctx, 1);
+}
diff --git a/liblog/log_event_write.cpp b/liblog/log_event_write.cpp
new file mode 100644
index 0000000..39afd0c
--- /dev/null
+++ b/liblog/log_event_write.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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 <stdint.h>
+
+#include <log/log.h>
+#include <log/log_event_list.h>
+
+#define MAX_SUBTAG_LEN 32
+
+int __android_log_error_write(int tag, const char* subTag, int32_t uid, const char* data,
+                              uint32_t dataLen) {
+  int ret = -EINVAL;
+
+  if (subTag && (data || !dataLen)) {
+    android_log_context ctx = create_android_logger(tag);
+
+    ret = -ENOMEM;
+    if (ctx) {
+      ret = android_log_write_string8_len(ctx, subTag, MAX_SUBTAG_LEN);
+      if (ret >= 0) {
+        ret = android_log_write_int32(ctx, uid);
+        if (ret >= 0) {
+          ret = android_log_write_string8_len(ctx, data, dataLen);
+          if (ret >= 0) {
+            ret = android_log_write_list(ctx, LOG_ID_EVENTS);
+          }
+        }
+      }
+      android_log_destroy(&ctx);
+    }
+  }
+  return ret;
+}
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
new file mode 100644
index 0000000..14c408c
--- /dev/null
+++ b/liblog/log_time.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 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 <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <private/android_logger.h>
+
+// Add %#q for fractional seconds to standard strptime function
+char* log_time::strptime(const char* s, const char* format) {
+  time_t now;
+#ifdef __linux__
+  *this = log_time(CLOCK_REALTIME);
+  now = tv_sec;
+#else
+  time(&now);
+  tv_sec = now;
+  tv_nsec = 0;
+#endif
+
+  struct tm* ptm;
+#if !defined(_WIN32)
+  struct tm tmBuf;
+  ptm = localtime_r(&now, &tmBuf);
+#else
+  ptm = localtime(&now);
+#endif
+
+  char fmt[strlen(format) + 1];
+  strcpy(fmt, format);
+
+  char* ret = const_cast<char*>(s);
+  char* cp;
+  for (char* f = cp = fmt;; ++cp) {
+    if (!*cp) {
+      if (f != cp) {
+        ret = ::strptime(ret, f, ptm);
+      }
+      break;
+    }
+    if (*cp != '%') {
+      continue;
+    }
+    char* e = cp;
+    ++e;
+#if (defined(__BIONIC__))
+    if (*e == 's') {
+      *cp = '\0';
+      if (*f) {
+        ret = ::strptime(ret, f, ptm);
+        if (!ret) {
+          break;
+        }
+      }
+      tv_sec = 0;
+      while (isdigit(*ret)) {
+        tv_sec = tv_sec * 10 + *ret - '0';
+        ++ret;
+      }
+      now = tv_sec;
+#if !defined(_WIN32)
+      ptm = localtime_r(&now, &tmBuf);
+#else
+      ptm = localtime(&now);
+#endif
+    } else
+#endif
+    {
+      unsigned num = 0;
+      while (isdigit(*e)) {
+        num = num * 10 + *e - '0';
+        ++e;
+      }
+      if (*e != 'q') {
+        continue;
+      }
+      *cp = '\0';
+      if (*f) {
+        ret = ::strptime(ret, f, ptm);
+        if (!ret) {
+          break;
+        }
+      }
+      unsigned long mul = NS_PER_SEC;
+      if (num == 0) {
+        num = INT_MAX;
+      }
+      tv_nsec = 0;
+      while (isdigit(*ret) && num && (mul > 1)) {
+        --num;
+        mul /= 10;
+        tv_nsec = tv_nsec + (*ret - '0') * mul;
+        ++ret;
+      }
+    }
+    f = cp = e;
+    ++f;
+  }
+
+  if (ret) {
+    tv_sec = mktime(ptm);
+    return ret;
+  }
+
+// Upon error, place a known value into the class, the current time.
+#ifdef __linux__
+  *this = log_time(CLOCK_REALTIME);
+#else
+  time(&now);
+  tv_sec = now;
+  tv_nsec = 0;
+#endif
+  return ret;
+}
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
new file mode 100644
index 0000000..6bff078
--- /dev/null
+++ b/liblog/logd_reader.cpp
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2007-2016 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 "logd_reader.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/parseint.h>
+#include <private/android_logger.h>
+
+#include "logger.h"
+
+// Connects to /dev/socket/<name> and returns the associated fd or returns -1 on error.
+// O_CLOEXEC is always set.
+static int socket_local_client(const std::string& name, int type, bool timeout) {
+  sockaddr_un addr = {.sun_family = AF_LOCAL};
+
+  std::string path = "/dev/socket/" + name;
+  if (path.size() + 1 > sizeof(addr.sun_path)) {
+    return -1;
+  }
+  strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
+
+  int fd = socket(AF_LOCAL, type | SOCK_CLOEXEC, 0);
+  if (fd == -1) {
+    return -1;
+  }
+
+  if (timeout) {
+    // Sending and receiving messages should be instantaneous, but we don't want to wait forever if
+    // logd is hung, so we set a gracious 2s timeout.
+    struct timeval t = {2, 0};
+    if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t)) == -1) {
+      return -1;
+    }
+    if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t)) == -1) {
+      return -1;
+    }
+  }
+
+  if (connect(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
+    close(fd);
+    return -1;
+  }
+
+  return fd;
+}
+
+/* worker for sending the command to the logger */
+ssize_t SendLogdControlMessage(char* buf, size_t buf_size) {
+  ssize_t ret;
+  size_t len;
+  char* cp;
+  int errno_save = 0;
+  int sock = socket_local_client("logd", SOCK_STREAM, true);
+  if (sock < 0) {
+    return sock;
+  }
+
+  len = strlen(buf) + 1;
+  ret = TEMP_FAILURE_RETRY(write(sock, buf, len));
+  if (ret <= 0) {
+    goto done;
+  }
+
+  len = buf_size;
+  cp = buf;
+  while ((ret = TEMP_FAILURE_RETRY(read(sock, cp, len))) > 0) {
+    if (((size_t)ret == len) || (buf_size < PAGE_SIZE)) {
+      break;
+    }
+
+    len -= ret;
+    cp += ret;
+
+    // Give other side 20ms to refill pipe.
+    struct pollfd p = {.fd = sock, .events = POLLIN};
+    ret = TEMP_FAILURE_RETRY(poll(&p, 1, 20));
+
+    if (ret <= 0) {
+      break;
+    }
+
+    if (!(p.revents & POLLIN)) {
+      ret = 0;
+      break;
+    }
+  }
+
+  if (ret >= 0) {
+    ret += buf_size - len;
+  }
+
+done:
+  if ((ret == -1) && errno) {
+    errno_save = errno;
+  }
+  close(sock);
+  if (errno_save) {
+    errno = errno_save;
+  }
+  return ret;
+}
+
+static int check_log_success(char* buf, ssize_t ret) {
+  if (ret < 0) {
+    return ret;
+  }
+
+  if (strncmp(buf, "success", 7)) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  return 0;
+}
+
+int android_logger_clear(struct logger* logger) {
+  if (!android_logger_is_logd(logger)) {
+    return -EINVAL;
+  }
+  uint32_t log_id = android_logger_get_id(logger);
+  char buf[512];
+  snprintf(buf, sizeof(buf), "clear %" PRIu32, log_id);
+
+  return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
+}
+
+enum class LogSizeType : uint32_t {
+  kAllotted = 0,
+  kReadable,
+  kConsumed,
+};
+
+static long GetLogSize(struct logger* logger, LogSizeType type) {
+  if (!android_logger_is_logd(logger)) {
+    return -EINVAL;
+  }
+
+  uint32_t log_id = android_logger_get_id(logger);
+  char buf[512];
+  switch (type) {
+    case LogSizeType::kAllotted:
+      snprintf(buf, sizeof(buf), "getLogSize %" PRIu32, log_id);
+      break;
+    case LogSizeType::kReadable:
+      snprintf(buf, sizeof(buf), "getLogSizeReadable %" PRIu32, log_id);
+      break;
+    case LogSizeType::kConsumed:
+      snprintf(buf, sizeof(buf), "getLogSizeUsed %" PRIu32, log_id);
+      break;
+    default:
+      abort();
+  }
+
+  ssize_t ret = SendLogdControlMessage(buf, sizeof(buf));
+  if (ret < 0) {
+    return ret;
+  }
+
+  long size;
+  if (!android::base::ParseInt(buf, &size)) {
+    return -1;
+  }
+
+  return size;
+}
+
+long android_logger_get_log_size(struct logger* logger) {
+  return GetLogSize(logger, LogSizeType::kAllotted);
+}
+
+long android_logger_get_log_readable_size(struct logger* logger) {
+  return GetLogSize(logger, LogSizeType::kReadable);
+}
+
+long android_logger_get_log_consumed_size(struct logger* logger) {
+  return GetLogSize(logger, LogSizeType::kConsumed);
+}
+
+int android_logger_set_log_size(struct logger* logger, unsigned long size) {
+  if (!android_logger_is_logd(logger)) {
+    return -EINVAL;
+  }
+
+  uint32_t log_id = android_logger_get_id(logger);
+  char buf[512];
+  snprintf(buf, sizeof(buf), "setLogSize %" PRIu32 " %lu", log_id, size);
+
+  return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
+}
+
+int android_logger_get_log_version(struct logger*) {
+  return 4;
+}
+
+ssize_t android_logger_get_statistics(struct logger_list* logger_list, char* buf, size_t len) {
+  if (logger_list->mode & ANDROID_LOG_PSTORE) {
+    return -EINVAL;
+  }
+
+  char* cp = buf;
+  size_t remaining = len;
+  size_t n;
+
+  n = snprintf(cp, remaining, "getStatistics");
+  n = MIN(n, remaining);
+  remaining -= n;
+  cp += n;
+
+  for (size_t log_id = 0; log_id < LOG_ID_MAX; ++log_id) {
+    if ((1 << log_id) & logger_list->log_mask) {
+      n = snprintf(cp, remaining, " %zu", log_id);
+      n = MIN(n, remaining);
+      remaining -= n;
+      cp += n;
+    }
+  }
+
+  if (logger_list->pid) {
+    snprintf(cp, remaining, " pid=%u", logger_list->pid);
+  }
+
+  return SendLogdControlMessage(buf, len);
+}
+ssize_t android_logger_get_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
+  if (logger_list->mode & ANDROID_LOG_PSTORE) {
+    return -EINVAL;
+  }
+
+  snprintf(buf, len, "getPruneList");
+  return SendLogdControlMessage(buf, len);
+}
+
+int android_logger_set_prune_list(struct logger_list* logger_list, const char* buf, size_t len) {
+  if (logger_list->mode & ANDROID_LOG_PSTORE) {
+    return -EINVAL;
+  }
+
+  std::string cmd = "setPruneList " + std::string{buf, len};
+
+  return check_log_success(cmd.data(), SendLogdControlMessage(cmd.data(), cmd.size()));
+}
+
+static int logdOpen(struct logger_list* logger_list) {
+  char buffer[256], *cp, c;
+  int ret, remaining, sock;
+
+  sock = atomic_load(&logger_list->fd);
+  if (sock > 0) {
+    return sock;
+  }
+
+  sock = socket_local_client("logdr", SOCK_SEQPACKET, false);
+  if (sock <= 0) {
+    if ((sock == -1) && errno) {
+      return -errno;
+    }
+    return sock;
+  }
+
+  strcpy(buffer, (logger_list->mode & ANDROID_LOG_NONBLOCK) ? "dumpAndClose" : "stream");
+  cp = buffer + strlen(buffer);
+
+  strcpy(cp, " lids");
+  cp += 5;
+  c = '=';
+  remaining = sizeof(buffer) - (cp - buffer);
+
+  for (size_t log_id = 0; log_id < LOG_ID_MAX; ++log_id) {
+    if ((1 << log_id) & logger_list->log_mask) {
+      ret = snprintf(cp, remaining, "%c%zu", c, log_id);
+      ret = MIN(ret, remaining);
+      remaining -= ret;
+      cp += ret;
+      c = ',';
+    }
+  }
+
+  if (logger_list->tail) {
+    ret = snprintf(cp, remaining, " tail=%u", logger_list->tail);
+    ret = MIN(ret, remaining);
+    remaining -= ret;
+    cp += ret;
+  }
+
+  if (logger_list->start.tv_sec || logger_list->start.tv_nsec) {
+    if (logger_list->mode & ANDROID_LOG_WRAP) {
+      // ToDo: alternate API to allow timeout to be adjusted.
+      ret = snprintf(cp, remaining, " timeout=%u", ANDROID_LOG_WRAP_DEFAULT_TIMEOUT);
+      ret = MIN(ret, remaining);
+      remaining -= ret;
+      cp += ret;
+    }
+    ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32, logger_list->start.tv_sec,
+                   logger_list->start.tv_nsec);
+    ret = MIN(ret, remaining);
+    remaining -= ret;
+    cp += ret;
+  }
+
+  if (logger_list->pid) {
+    ret = snprintf(cp, remaining, " pid=%u", logger_list->pid);
+    ret = MIN(ret, remaining);
+    cp += ret;
+  }
+
+  ret = TEMP_FAILURE_RETRY(write(sock, buffer, cp - buffer));
+  int write_errno = errno;
+
+  if (ret <= 0) {
+    close(sock);
+    if (ret == -1) {
+      return -write_errno;
+    }
+    if (ret == 0) {
+      return -EIO;
+    }
+    return ret;
+  }
+
+  ret = atomic_exchange(&logger_list->fd, sock);
+  if ((ret > 0) && (ret != sock)) {
+    close(ret);
+  }
+  return sock;
+}
+
+/* Read from the selected logs */
+int LogdRead(struct logger_list* logger_list, struct log_msg* log_msg) {
+  int ret = logdOpen(logger_list);
+  if (ret < 0) {
+    return ret;
+  }
+
+  /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
+  ret = TEMP_FAILURE_RETRY(recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0));
+  if ((logger_list->mode & ANDROID_LOG_NONBLOCK) && ret == 0) {
+    return -EAGAIN;
+  }
+
+  if (ret == -1) {
+    return -errno;
+  }
+  return ret;
+}
+
+/* Close all the logs */
+void LogdClose(struct logger_list* logger_list) {
+  int sock = atomic_exchange(&logger_list->fd, -1);
+  if (sock > 0) {
+    close(sock);
+  }
+}
diff --git a/liblog/logd_reader.h b/liblog/logd_reader.h
new file mode 100644
index 0000000..68eef02
--- /dev/null
+++ b/liblog/logd_reader.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+#include <unistd.h>
+
+#include "log/log_read.h"
+
+__BEGIN_DECLS
+
+int LogdRead(struct logger_list* logger_list, struct log_msg* log_msg);
+void LogdClose(struct logger_list* logger_list);
+
+ssize_t SendLogdControlMessage(char* buf, size_t buf_size);
+
+__END_DECLS
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
new file mode 100644
index 0000000..3d5cee6
--- /dev/null
+++ b/liblog/logd_writer.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2007-2016 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 "logd_writer.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "logger.h"
+#include "uio.h"
+
+class LogdSocket {
+ public:
+  static LogdSocket& BlockingSocket() {
+    static LogdSocket logd_socket(true);
+    return logd_socket;
+  }
+  static LogdSocket& NonBlockingSocket() {
+    static LogdSocket logd_socket(false);
+    return logd_socket;
+  }
+
+  void Reconnect() { LogdConnect(sock_); }
+
+  // Zygote uses this to clean up open FD's after fork() and before specialization.  It is single
+  // threaded at this point and therefore this function is explicitly not thread safe.  It sets
+  // sock_ to kUninitialized, so future logs will be safely initialized whenever they happen.
+  void Close() {
+    if (sock_ != kUninitialized) {
+      close(sock_);
+    }
+    sock_ = kUninitialized;
+  }
+
+  int sock() {
+    GetSocket();
+    return sock_;
+  }
+
+ private:
+  LogdSocket(bool blocking) : blocking_(blocking) {}
+
+  // Note that it is safe to call connect() multiple times on DGRAM Unix domain sockets, so this
+  // function is used to reconnect to logd without requiring a new socket.
+  static void LogdConnect(int sock) {
+    sockaddr_un un = {};
+    un.sun_family = AF_UNIX;
+    strcpy(un.sun_path, "/dev/socket/logdw");
+    TEMP_FAILURE_RETRY(connect(sock, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un)));
+  }
+
+  // sock_ should only be opened once.  If we see that sock_ is uninitialized, we
+  // create a new socket and attempt to exchange it into the atomic sock_.  If the
+  // compare/exchange was successful, then that will be the socket used for the duration of the
+  // program, otherwise a different thread has already opened and written the socket to the atomic,
+  // so close the new socket and return.
+  void GetSocket() {
+    if (sock_ != kUninitialized) {
+      return;
+    }
+
+    int flags = SOCK_DGRAM | SOCK_CLOEXEC;
+    if (!blocking_) {
+      flags |= SOCK_NONBLOCK;
+    }
+    int new_socket = TEMP_FAILURE_RETRY(socket(PF_UNIX, flags, 0));
+    if (new_socket < 0) {
+      return;
+    }
+
+    LogdConnect(new_socket);
+
+    int uninitialized_value = kUninitialized;
+    if (!sock_.compare_exchange_strong(uninitialized_value, new_socket)) {
+      close(new_socket);
+      return;
+    }
+  }
+
+  static const int kUninitialized = -1;
+  atomic_int sock_ = kUninitialized;
+  bool blocking_;
+};
+
+void LogdClose() {
+  LogdSocket::BlockingSocket().Close();
+  LogdSocket::NonBlockingSocket().Close();
+}
+
+int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
+  ssize_t ret;
+  static const unsigned headerLength = 1;
+  struct iovec newVec[nr + headerLength];
+  android_log_header_t header;
+  size_t i, payloadSize;
+  static atomic_int dropped;
+
+  LogdSocket& logd_socket =
+      logId == LOG_ID_SECURITY ? LogdSocket::BlockingSocket() : LogdSocket::NonBlockingSocket();
+
+  if (logd_socket.sock() < 0) {
+    return -EBADF;
+  }
+
+  /* logd, after initialization and priv drop */
+  if (getuid() == AID_LOGD) {
+    /*
+     * ignore log messages we send to ourself (logd).
+     * Such log messages are often generated by libraries we depend on
+     * which use standard Android logging.
+     */
+    return 0;
+  }
+
+  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);
+
+  int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+  if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog", strlen("liblog"),
+                                                ANDROID_LOG_VERBOSE)) {
+    android_log_event_int_t buffer;
+
+    header.id = LOG_ID_EVENTS;
+    buffer.header.tag = LIBLOG_LOG_TAG;
+    buffer.payload.type = EVENT_TYPE_INT;
+    buffer.payload.data = snapshot;
+
+    newVec[headerLength].iov_base = &buffer;
+    newVec[headerLength].iov_len = sizeof(buffer);
+
+    ret = TEMP_FAILURE_RETRY(writev(logd_socket.sock(), newVec, 2));
+    if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+      atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
+    }
+  }
+
+  header.id = logId;
+
+  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;
+    }
+  }
+
+  // EAGAIN occurs if logd is overloaded, other errors indicate that something went wrong with
+  // the connection, so we reset it and try again.
+  ret = TEMP_FAILURE_RETRY(writev(logd_socket.sock(), newVec, i));
+  if (ret < 0 && errno != EAGAIN) {
+    logd_socket.Reconnect();
+
+    ret = TEMP_FAILURE_RETRY(writev(logd_socket.sock(), newVec, i));
+  }
+
+  if (ret < 0) {
+    ret = -errno;
+  }
+
+  if (ret > (ssize_t)sizeof(header)) {
+    ret -= sizeof(header);
+  } else if (ret < 0) {
+    atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+  }
+
+  return ret;
+}
diff --git a/liblog/logd_writer.h b/liblog/logd_writer.h
new file mode 100644
index 0000000..41197b5
--- /dev/null
+++ b/liblog/logd_writer.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 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 <stddef.h>
+
+#include <android/log.h>
+
+int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+void LogdClose();
diff --git a/liblog/logger.h b/liblog/logger.h
new file mode 100644
index 0000000..ddff19d
--- /dev/null
+++ b/liblog/logger.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 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 <stdatomic.h>
+#include <sys/cdefs.h>
+
+#include <log/log.h>
+
+#include "uio.h"
+
+__BEGIN_DECLS
+
+struct logger_list {
+  atomic_int fd;
+  int mode;
+  unsigned int tail;
+  log_time start;
+  pid_t pid;
+  uint32_t log_mask;
+};
+
+// Format for a 'logger' entry: uintptr_t where only the bottom 32 bits are used.
+// bit 31: Set if this 'logger' is for logd.
+// bit 30: Set if this 'logger' is for pmsg
+// bits 0-2: the decimal value of the log buffer.
+// Other bits are unused.
+
+#define LOGGER_LOGD (1U << 31)
+#define LOGGER_PMSG (1U << 30)
+#define LOGGER_LOG_ID_MASK ((1U << 3) - 1)
+
+inline bool android_logger_is_logd(struct logger* logger) {
+  return reinterpret_cast<uintptr_t>(logger) & LOGGER_LOGD;
+}
+
+__END_DECLS
diff --git a/liblog/logger_name.cpp b/liblog/logger_name.cpp
new file mode 100644
index 0000000..e72290e
--- /dev/null
+++ b/liblog/logger_name.cpp
@@ -0,0 +1,72 @@
+/*
+** Copyright 2013-2014, 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 <string.h>
+#include <type_traits>
+
+#include <log/log.h>
+
+/* In the future, we would like to make this list extensible */
+static const char* LOG_NAME[LOG_ID_MAX] = {
+    /* clang-format off */
+  [LOG_ID_MAIN] = "main",
+  [LOG_ID_RADIO] = "radio",
+  [LOG_ID_EVENTS] = "events",
+  [LOG_ID_SYSTEM] = "system",
+  [LOG_ID_CRASH] = "crash",
+  [LOG_ID_STATS] = "stats",
+  [LOG_ID_SECURITY] = "security",
+  [LOG_ID_KERNEL] = "kernel",
+    /* clang-format on */
+};
+
+const char* android_log_id_to_name(log_id_t log_id) {
+  if (log_id >= LOG_ID_MAX) {
+    log_id = LOG_ID_MAIN;
+  }
+  return LOG_NAME[log_id];
+}
+
+static_assert(std::is_same<std::underlying_type<log_id_t>::type, uint32_t>::value,
+              "log_id_t must be an uint32_t");
+
+static_assert(std::is_same<std::underlying_type<android_LogPriority>::type, uint32_t>::value,
+              "log_id_t must be an uint32_t");
+
+log_id_t android_name_to_log_id(const char* logName) {
+  const char* b;
+  unsigned int ret;
+
+  if (!logName) {
+    return static_cast<log_id_t>(LOG_ID_MAX);
+  }
+
+  b = strrchr(logName, '/');
+  if (!b) {
+    b = logName;
+  } else {
+    ++b;
+  }
+
+  for (ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
+    const char* l = LOG_NAME[ret];
+    if (l && !strcmp(b, l)) {
+      return static_cast<log_id_t>(ret);
+    }
+  }
+
+  return static_cast<log_id_t>(LOG_ID_MAX);
+}
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
new file mode 100644
index 0000000..4937042
--- /dev/null
+++ b/liblog/logger_read.cpp
@@ -0,0 +1,149 @@
+/*
+** Copyright 2013-2014, 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 "log/log_read.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <android/log.h>
+
+#include "logd_reader.h"
+#include "logger.h"
+#include "pmsg_reader.h"
+
+/* method for getting the associated sublog id */
+log_id_t android_logger_get_id(struct logger* logger) {
+  return static_cast<log_id_t>(reinterpret_cast<uintptr_t>(logger) & LOGGER_LOG_ID_MASK);
+}
+
+static struct logger_list* android_logger_list_alloc_internal(int mode, unsigned int tail,
+                                                              log_time start, pid_t pid) {
+  auto* logger_list = static_cast<struct logger_list*>(calloc(1, sizeof(struct logger_list)));
+  if (!logger_list) {
+    return nullptr;
+  }
+
+  logger_list->mode = mode;
+  logger_list->start = start;
+  logger_list->tail = tail;
+  logger_list->pid = pid;
+
+  return logger_list;
+}
+
+struct logger_list* android_logger_list_alloc(int mode, unsigned int tail, pid_t pid) {
+  return android_logger_list_alloc_internal(mode, tail, log_time(0, 0), pid);
+}
+
+struct logger_list* android_logger_list_alloc_time(int mode, log_time start, pid_t pid) {
+  return android_logger_list_alloc_internal(mode, 0, start, pid);
+}
+
+/* Open the named log and add it to the logger list */
+struct logger* android_logger_open(struct logger_list* logger_list, log_id_t logId) {
+  if (!logger_list || (logId >= LOG_ID_MAX)) {
+    return nullptr;
+  }
+
+  logger_list->log_mask |= 1 << logId;
+
+  uintptr_t logger = logId;
+  logger |= (logger_list->mode & ANDROID_LOG_PSTORE) ? LOGGER_PMSG : LOGGER_LOGD;
+  return reinterpret_cast<struct logger*>(logger);
+}
+
+/* Open the single named log and make it part of a new logger list */
+struct logger_list* android_logger_list_open(log_id_t logId, int mode, unsigned int tail,
+                                             pid_t pid) {
+  struct logger_list* logger_list = android_logger_list_alloc(mode, tail, pid);
+
+  if (!logger_list) {
+    return NULL;
+  }
+
+  if (!android_logger_open(logger_list, logId)) {
+    android_logger_list_free(logger_list);
+    return NULL;
+  }
+
+  return logger_list;
+}
+
+int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
+  if (logger_list == nullptr || logger_list->log_mask == 0) {
+    return -EINVAL;
+  }
+
+  int ret = 0;
+
+#ifdef __ANDROID__
+  if (logger_list->mode & ANDROID_LOG_PSTORE) {
+    ret = PmsgRead(logger_list, log_msg);
+  } else {
+    ret = LogdRead(logger_list, log_msg);
+  }
+#endif
+
+  if (ret <= 0) {
+    return ret;
+  }
+
+  if (ret > LOGGER_ENTRY_MAX_LEN) {
+    ret = LOGGER_ENTRY_MAX_LEN;
+  }
+
+  if (ret < static_cast<int>(sizeof(log_msg->entry))) {
+    return -EINVAL;
+  }
+
+  if (log_msg->entry.hdr_size < sizeof(log_msg->entry) ||
+      log_msg->entry.hdr_size >= LOGGER_ENTRY_MAX_LEN - sizeof(log_msg->entry)) {
+    return -EINVAL;
+  }
+
+  if (log_msg->entry.len > ret - log_msg->entry.hdr_size) {
+    return -EINVAL;
+  }
+
+  log_msg->buf[log_msg->entry.len + log_msg->entry.hdr_size] = '\0';
+
+  return ret;
+}
+
+/* Close all the logs */
+void android_logger_list_free(struct logger_list* logger_list) {
+  if (logger_list == NULL) {
+    return;
+  }
+
+#ifdef __ANDROID__
+  if (logger_list->mode & ANDROID_LOG_PSTORE) {
+    PmsgClose(logger_list);
+  } else {
+    LogdClose(logger_list);
+  }
+#endif
+
+  free(logger_list);
+}
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
new file mode 100644
index 0000000..596b1af
--- /dev/null
+++ b/liblog/logger_write.cpp
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2007-2016 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 "logger_write.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#ifdef __BIONIC__
+#include <android/set_abort_message.h>
+#endif
+
+#include <atomic>
+
+#include <android-base/errno_restorer.h>
+#include <android-base/macros.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "android/log.h"
+#include "log/log_read.h"
+#include "logger.h"
+#include "uio.h"
+
+#ifdef __ANDROID__
+#include "logd_writer.h"
+#include "pmsg_writer.h"
+#endif
+
+#if defined(__APPLE__)
+#include <pthread.h>
+#elif defined(__linux__) && !defined(__ANDROID__)
+#include <syscall.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#endif
+
+// The preferred way to access system properties is using android::base::GetProperty in libbase.
+// However, adding dependency to libbase requires that if liblog was statically linked to a client,
+// that client now has additional dependency to libbase as well because static dependencies of
+// static library is not exported. (users of liblog.so however is fine).
+#ifdef __ANDROID__
+#include <sys/system_properties.h>
+#endif
+
+using android::base::ErrnoRestorer;
+
+#define LOG_BUF_SIZE 1024
+
+#if defined(__ANDROID__)
+static int check_log_uid_permissions() {
+  uid_t uid = getuid();
+
+  /* Matches clientCanWriteSecurityLog() in logd */
+  if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
+    uid = geteuid();
+    if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
+      gid_t gid = getgid();
+      if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
+        gid = getegid();
+        if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
+          int num_groups;
+          gid_t* groups;
+
+          num_groups = getgroups(0, NULL);
+          if (num_groups <= 0) {
+            return -EPERM;
+          }
+          groups = static_cast<gid_t*>(calloc(num_groups, sizeof(gid_t)));
+          if (!groups) {
+            return -ENOMEM;
+          }
+          num_groups = getgroups(num_groups, groups);
+          while (num_groups > 0) {
+            if (groups[num_groups - 1] == AID_LOG ||
+                groups[num_groups - 1] == AID_SECURITY_LOG_WRITER) {
+              break;
+            }
+            --num_groups;
+          }
+          free(groups);
+          if (num_groups <= 0) {
+            return -EPERM;
+          }
+        }
+      }
+    }
+  }
+  return 0;
+}
+#endif
+
+/*
+ * Release any logger resources. A new log write will immediately re-acquire.
+ */
+void __android_log_close() {
+#ifdef __ANDROID__
+  LogdClose();
+  PmsgClose();
+#endif
+}
+
+// BSD-based systems like Android/macOS have getprogname(). Others need us to provide one.
+#if !defined(__APPLE__) && !defined(__BIONIC__)
+static const char* getprogname() {
+#ifdef _WIN32
+  static bool first = true;
+  static char progname[MAX_PATH] = {};
+
+  if (first) {
+    char path[PATH_MAX + 1];
+    DWORD result = GetModuleFileName(nullptr, path, sizeof(path) - 1);
+    if (result == 0 || result == sizeof(path) - 1) return "";
+    path[PATH_MAX - 1] = 0;
+
+    char* path_basename = basename(path);
+
+    snprintf(progname, sizeof(progname), "%s", path_basename);
+    first = false;
+  }
+
+  return progname;
+#else
+  return program_invocation_short_name;
+#endif
+}
+#endif
+
+// It's possible for logging to happen during static initialization before our globals are
+// initialized, so we place this std::string in a function such that it is initialized on the first
+// call. We use a pointer to avoid exit time destructors.
+std::string& GetDefaultTag() {
+  static std::string* default_tag = new std::string(getprogname());
+  return *default_tag;
+}
+
+void __android_log_set_default_tag(const char* tag) {
+  GetDefaultTag().assign(tag, 0, LOGGER_ENTRY_MAX_PAYLOAD);
+}
+
+static std::atomic_int32_t minimum_log_priority = ANDROID_LOG_DEFAULT;
+int32_t __android_log_set_minimum_priority(int32_t priority) {
+  return minimum_log_priority.exchange(priority, std::memory_order_relaxed);
+}
+
+int32_t __android_log_get_minimum_priority() {
+  return minimum_log_priority;
+}
+
+#ifdef __ANDROID__
+static const char* get_file_logger_path() {
+  static const char* file_logger_path = []() {
+    static char path[PROP_VALUE_MAX] = {};
+    if (__system_property_get("ro.log.file_logger.path", path) > 0) {
+      return path;
+    }
+    return (char*)nullptr;  // means file_logger should not be used
+  }();
+  return file_logger_path;
+}
+#endif
+
+static void file_logger(const struct __android_log_message* log_message);
+
+static __android_logger_function user_set_logger_function = nullptr;
+
+static __android_logger_function get_logger_function() {
+  if (user_set_logger_function != nullptr) {
+    return user_set_logger_function;
+  }
+  static __android_logger_function default_logger_function = []() {
+#if __ANDROID__
+    if (get_file_logger_path() != nullptr) {
+      return file_logger;
+    } else {
+      return __android_log_logd_logger;
+    }
+#else
+    return file_logger;
+#endif
+  }();
+  return default_logger_function;
+}
+
+void __android_log_set_logger(__android_logger_function logger) {
+  user_set_logger_function = logger;
+}
+
+void __android_log_default_aborter(const char* abort_message) {
+#ifdef __ANDROID__
+  android_set_abort_message(abort_message);
+#else
+  UNUSED(abort_message);
+#endif
+  abort();
+}
+
+static __android_aborter_function aborter_function = __android_log_default_aborter;
+
+void __android_log_set_aborter(__android_aborter_function aborter) {
+  aborter_function = aborter;
+}
+
+void __android_log_call_aborter(const char* abort_message) {
+  aborter_function(abort_message);
+}
+
+#ifdef __ANDROID__
+static int write_to_log(log_id_t log_id, struct iovec* vec, size_t nr) {
+  int ret;
+  struct timespec ts;
+
+  if (log_id == LOG_ID_KERNEL) {
+    return -EINVAL;
+  }
+
+  clock_gettime(CLOCK_REALTIME, &ts);
+
+  if (log_id == LOG_ID_SECURITY) {
+    if (vec[0].iov_len < 4) {
+      return -EINVAL;
+    }
+
+    ret = check_log_uid_permissions();
+    if (ret < 0) {
+      return ret;
+    }
+    if (!__android_log_security()) {
+      /* If only we could reset downstream logd counter */
+      return -EPERM;
+    }
+  } else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
+    if (vec[0].iov_len < 4) {
+      return -EINVAL;
+    }
+  }
+
+  ret = LogdWrite(log_id, &ts, vec, nr);
+  PmsgWrite(log_id, &ts, vec, nr);
+
+  return ret;
+}
+#else
+static int write_to_log(log_id_t, struct iovec*, size_t) {
+  // Non-Android text logs should go to __android_log_stderr_logger, not here.
+  // Non-Android binary logs are always dropped.
+  return 1;
+}
+#endif
+
+// Copied from base/threads.cpp
+static uint64_t GetThreadId() {
+#if defined(__BIONIC__)
+  return gettid();
+#elif defined(__APPLE__)
+  uint64_t tid;
+  pthread_threadid_np(NULL, &tid);
+  return tid;
+#elif defined(__linux__)
+  return syscall(__NR_gettid);
+#elif defined(_WIN32)
+  return GetCurrentThreadId();
+#endif
+}
+
+static void filestream_logger(const struct __android_log_message* log_message, FILE* stream) {
+  struct tm now;
+  time_t t = time(nullptr);
+
+#if defined(_WIN32)
+  localtime_s(&now, &t);
+#else
+  localtime_r(&t, &now);
+#endif
+
+  char timestamp[32];
+  strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
+
+  static const char log_characters[] = "XXVDIWEF";
+  static_assert(arraysize(log_characters) - 1 == ANDROID_LOG_SILENT,
+                "Mismatch in size of log_characters and values in android_LogPriority");
+  int32_t priority =
+      log_message->priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : log_message->priority;
+  char priority_char = log_characters[priority];
+  uint64_t tid = GetThreadId();
+
+  if (log_message->file != nullptr) {
+    fprintf(stream, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n",
+            log_message->tag ? log_message->tag : "nullptr", priority_char, timestamp, getpid(),
+            tid, log_message->file, log_message->line, log_message->message);
+  } else {
+    fprintf(stream, "%s %c %s %5d %5" PRIu64 "] %s\n",
+            log_message->tag ? log_message->tag : "nullptr", priority_char, timestamp, getpid(),
+            tid, log_message->message);
+  }
+}
+
+static void file_logger(const struct __android_log_message* log_message) {
+  static FILE* stream = []() {
+#ifdef __ANDROID__
+    const char* file_logger_path = get_file_logger_path();
+    if (file_logger_path != nullptr) {
+      FILE* f = fopen(file_logger_path, "ae");
+      if (f != nullptr) return f;
+      using namespace std::string_literals;
+      std::string err_msg = "Cannot open "s + file_logger_path + " for logging: (" + strerror(errno) +
+                            "). Falling back to stderr";
+      __android_log_message m = {sizeof(__android_log_message),
+                                 LOG_ID_DEFAULT,
+                                 ANDROID_LOG_WARN,
+                                 "liblog",
+                                 __FILE__,
+                                 __LINE__,
+                                 err_msg.c_str()};
+      filestream_logger(&m, stderr);
+    }
+#endif
+    // defaults to stderr if the sysprop is not set or the file is not available
+    return stderr;
+  }();
+  filestream_logger(log_message, stream);
+}
+
+void __android_log_stderr_logger(const struct __android_log_message* log_message) {
+  filestream_logger(log_message, stderr);
+}
+
+void __android_log_logd_logger(const struct __android_log_message* log_message) {
+  int buffer_id = log_message->buffer_id == LOG_ID_DEFAULT ? LOG_ID_MAIN : log_message->buffer_id;
+
+  struct iovec vec[3];
+  vec[0].iov_base =
+      const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(&log_message->priority));
+  vec[0].iov_len = 1;
+  vec[1].iov_base = const_cast<void*>(static_cast<const void*>(log_message->tag));
+  vec[1].iov_len = strlen(log_message->tag) + 1;
+  vec[2].iov_base = const_cast<void*>(static_cast<const void*>(log_message->message));
+  vec[2].iov_len = strlen(log_message->message) + 1;
+
+  write_to_log(static_cast<log_id_t>(buffer_id), vec, 3);
+}
+
+int __android_log_write(int prio, const char* tag, const char* msg) {
+  return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
+}
+
+void __android_log_write_log_message(__android_log_message* log_message) {
+  ErrnoRestorer errno_restorer;
+
+  if (log_message->buffer_id != LOG_ID_DEFAULT && log_message->buffer_id != LOG_ID_MAIN &&
+      log_message->buffer_id != LOG_ID_SYSTEM && log_message->buffer_id != LOG_ID_RADIO &&
+      log_message->buffer_id != LOG_ID_CRASH) {
+    return;
+  }
+
+  if (log_message->tag == nullptr) {
+    log_message->tag = GetDefaultTag().c_str();
+  }
+
+#if __BIONIC__
+  if (log_message->priority == ANDROID_LOG_FATAL) {
+    android_set_abort_message(log_message->message);
+  }
+#endif
+
+  get_logger_function()(log_message);
+}
+
+int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
+  ErrnoRestorer errno_restorer;
+
+  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+    return -EPERM;
+  }
+
+  __android_log_message log_message = {
+      sizeof(__android_log_message), bufID, prio, tag, nullptr, 0, msg};
+  __android_log_write_log_message(&log_message);
+  return 1;
+}
+
+int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) {
+  ErrnoRestorer errno_restorer;
+
+  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+    return -EPERM;
+  }
+
+  __attribute__((uninitialized)) char buf[LOG_BUF_SIZE];
+
+  vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+
+  __android_log_message log_message = {
+      sizeof(__android_log_message), LOG_ID_MAIN, prio, tag, nullptr, 0, buf};
+  __android_log_write_log_message(&log_message);
+  return 1;
+}
+
+int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+  ErrnoRestorer errno_restorer;
+
+  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+    return -EPERM;
+  }
+
+  va_list ap;
+  __attribute__((uninitialized)) char buf[LOG_BUF_SIZE];
+
+  va_start(ap, fmt);
+  vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+  va_end(ap);
+
+  __android_log_message log_message = {
+      sizeof(__android_log_message), LOG_ID_MAIN, prio, tag, nullptr, 0, buf};
+  __android_log_write_log_message(&log_message);
+  return 1;
+}
+
+int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...) {
+  ErrnoRestorer errno_restorer;
+
+  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+    return -EPERM;
+  }
+
+  va_list ap;
+  __attribute__((uninitialized)) char buf[LOG_BUF_SIZE];
+
+  va_start(ap, fmt);
+  vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+  va_end(ap);
+
+  __android_log_message log_message = {
+      sizeof(__android_log_message), bufID, prio, tag, nullptr, 0, buf};
+  __android_log_write_log_message(&log_message);
+  return 1;
+}
+
+void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...) {
+  __attribute__((uninitialized)) char buf[LOG_BUF_SIZE];
+
+  if (fmt) {
+    va_list ap;
+    va_start(ap, fmt);
+    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+    va_end(ap);
+  } else {
+    /* Msg not provided, log condition.  N.B. Do not use cond directly as
+     * format string as it could contain spurious '%' syntax (e.g.
+     * "%d" in "blocks%devs == 0").
+     */
+    if (cond)
+      snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
+    else
+      strcpy(buf, "Unspecified assertion failed");
+  }
+
+  // Log assertion failures to stderr for the benefit of "adb shell" users
+  // and gtests (http://b/23675822).
+  TEMP_FAILURE_RETRY(write(2, buf, strlen(buf)));
+  TEMP_FAILURE_RETRY(write(2, "\n", 1));
+
+  __android_log_write(ANDROID_LOG_FATAL, tag, buf);
+  __android_log_call_aborter(buf);
+  abort();
+}
+
+int __android_log_bwrite(int32_t tag, const void* payload, size_t len) {
+  ErrnoRestorer errno_restorer;
+
+  struct iovec vec[2];
+
+  vec[0].iov_base = &tag;
+  vec[0].iov_len = sizeof(tag);
+  vec[1].iov_base = (void*)payload;
+  vec[1].iov_len = len;
+
+  return write_to_log(LOG_ID_EVENTS, vec, 2);
+}
+
+int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len) {
+  ErrnoRestorer errno_restorer;
+
+  struct iovec vec[2];
+
+  vec[0].iov_base = &tag;
+  vec[0].iov_len = sizeof(tag);
+  vec[1].iov_base = (void*)payload;
+  vec[1].iov_len = len;
+
+  return write_to_log(LOG_ID_STATS, vec, 2);
+}
+
+int __android_log_security_bwrite(int32_t tag, const void* payload, size_t len) {
+  ErrnoRestorer errno_restorer;
+
+  struct iovec vec[2];
+
+  vec[0].iov_base = &tag;
+  vec[0].iov_len = sizeof(tag);
+  vec[1].iov_base = (void*)payload;
+  vec[1].iov_len = len;
+
+  return write_to_log(LOG_ID_SECURITY, vec, 2);
+}
+
+/*
+ * Like __android_log_bwrite, but takes the type as well.  Doesn't work
+ * for the general case where we're generating lists of stuff, but very
+ * handy if we just want to dump an integer into the log.
+ */
+int __android_log_btwrite(int32_t tag, char type, const void* payload, size_t len) {
+  ErrnoRestorer errno_restorer;
+
+  struct iovec vec[3];
+
+  vec[0].iov_base = &tag;
+  vec[0].iov_len = sizeof(tag);
+  vec[1].iov_base = &type;
+  vec[1].iov_len = sizeof(type);
+  vec[2].iov_base = (void*)payload;
+  vec[2].iov_len = len;
+
+  return write_to_log(LOG_ID_EVENTS, vec, 3);
+}
+
+/*
+ * Like __android_log_bwrite, but used for writing strings to the
+ * event log.
+ */
+int __android_log_bswrite(int32_t tag, const char* payload) {
+  ErrnoRestorer errno_restorer;
+
+  struct iovec vec[4];
+  char type = EVENT_TYPE_STRING;
+  uint32_t len = strlen(payload);
+
+  vec[0].iov_base = &tag;
+  vec[0].iov_len = sizeof(tag);
+  vec[1].iov_base = &type;
+  vec[1].iov_len = sizeof(type);
+  vec[2].iov_base = &len;
+  vec[2].iov_len = sizeof(len);
+  vec[3].iov_base = (void*)payload;
+  vec[3].iov_len = len;
+
+  return write_to_log(LOG_ID_EVENTS, vec, 4);
+}
+
+/*
+ * Like __android_log_security_bwrite, but used for writing strings to the
+ * security log.
+ */
+int __android_log_security_bswrite(int32_t tag, const char* payload) {
+  ErrnoRestorer errno_restorer;
+
+  struct iovec vec[4];
+  char type = EVENT_TYPE_STRING;
+  uint32_t len = strlen(payload);
+
+  vec[0].iov_base = &tag;
+  vec[0].iov_len = sizeof(tag);
+  vec[1].iov_base = &type;
+  vec[1].iov_len = sizeof(type);
+  vec[2].iov_base = &len;
+  vec[2].iov_len = sizeof(len);
+  vec[3].iov_base = (void*)payload;
+  vec[3].iov_len = len;
+
+  return write_to_log(LOG_ID_SECURITY, vec, 4);
+}
diff --git a/liblog/logger_write.h b/liblog/logger_write.h
new file mode 100644
index 0000000..eee2778
--- /dev/null
+++ b/liblog/logger_write.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+std::string& GetDefaultTag();
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
new file mode 100644
index 0000000..f83e58f
--- /dev/null
+++ b/liblog/logprint.cpp
@@ -0,0 +1,1712 @@
+/*
+ * Copyright 2006, 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 <log/logprint.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#ifndef __MINGW32__
+#include <pwd.h>
+#endif
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <wchar.h>
+
+#include <cutils/list.h>
+
+#include <algorithm>
+
+#include <log/log.h>
+#include <log/log_read.h>
+#include <private/android_logger.h>
+
+#define MS_PER_NSEC 1000000
+#define US_PER_NSEC 1000
+
+typedef struct FilterInfo_t {
+  char* mTag;
+  android_LogPriority mPri;
+  struct FilterInfo_t* p_next;
+} FilterInfo;
+
+struct AndroidLogFormat_t {
+  android_LogPriority global_pri;
+  FilterInfo* filters;
+  AndroidLogPrintFormat format;
+  bool colored_output;
+  bool usec_time_output;
+  bool nsec_time_output;
+  bool printable_output;
+  bool year_output;
+  bool zone_output;
+  bool epoch_output;
+  bool monotonic_output;
+  bool uid_output;
+  bool descriptive_output;
+};
+
+/*
+ * API issues prevent us from exposing "descriptive" in AndroidLogFormat_t
+ * during android_log_processBinaryLogBuffer(), so we break layering.
+ */
+static bool descriptive_output = false;
+
+/*
+ * 8-bit color tags. See ECMA-48 Set Graphics Rendition in
+ * [console_codes(4)](https://man7.org/linux/man-pages/man4/console_codes.4.html).
+ *
+ * The text manipulation character stream is defined as:
+ *   ESC [ <parameter #> m
+ *
+ * We use "set <color> foreground" escape sequences instead of
+ * "256/24-bit foreground color". This allows colors to render
+ * according to user preferences in terminal emulator settings
+ */
+#define ANDROID_COLOR_BLUE 34
+#define ANDROID_COLOR_DEFAULT 39
+#define ANDROID_COLOR_GREEN 32
+#define ANDROID_COLOR_RED 31
+#define ANDROID_COLOR_YELLOW 33
+
+static FilterInfo* filterinfo_new(const char* tag, android_LogPriority pri) {
+  FilterInfo* p_ret;
+
+  p_ret = (FilterInfo*)calloc(1, sizeof(FilterInfo));
+  p_ret->mTag = strdup(tag);
+  p_ret->mPri = pri;
+
+  return p_ret;
+}
+
+/* balance to above, filterinfo_free left unimplemented */
+
+/*
+ * Note: also accepts 0-9 priorities
+ * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
+ */
+static android_LogPriority filterCharToPri(char c) {
+  android_LogPriority pri;
+
+  c = tolower(c);
+
+  if (c >= '0' && c <= '9') {
+    if (c >= ('0' + ANDROID_LOG_SILENT)) {
+      pri = ANDROID_LOG_VERBOSE;
+    } else {
+      pri = (android_LogPriority)(c - '0');
+    }
+  } else if (c == 'v') {
+    pri = ANDROID_LOG_VERBOSE;
+  } else if (c == 'd') {
+    pri = ANDROID_LOG_DEBUG;
+  } else if (c == 'i') {
+    pri = ANDROID_LOG_INFO;
+  } else if (c == 'w') {
+    pri = ANDROID_LOG_WARN;
+  } else if (c == 'e') {
+    pri = ANDROID_LOG_ERROR;
+  } else if (c == 'f') {
+    pri = ANDROID_LOG_FATAL;
+  } else if (c == 's') {
+    pri = ANDROID_LOG_SILENT;
+  } else if (c == '*') {
+    pri = ANDROID_LOG_DEFAULT;
+  } else {
+    pri = ANDROID_LOG_UNKNOWN;
+  }
+
+  return pri;
+}
+
+static char filterPriToChar(android_LogPriority pri) {
+  switch (pri) {
+    /* clang-format off */
+    case ANDROID_LOG_VERBOSE: return 'V';
+    case ANDROID_LOG_DEBUG:   return 'D';
+    case ANDROID_LOG_INFO:    return 'I';
+    case ANDROID_LOG_WARN:    return 'W';
+    case ANDROID_LOG_ERROR:   return 'E';
+    case ANDROID_LOG_FATAL:   return 'F';
+    case ANDROID_LOG_SILENT:  return 'S';
+
+    case ANDROID_LOG_DEFAULT:
+    case ANDROID_LOG_UNKNOWN:
+    default:                  return '?';
+      /* clang-format on */
+  }
+}
+
+static int colorFromPri(android_LogPriority pri) {
+  switch (pri) {
+    /* clang-format off */
+    case ANDROID_LOG_VERBOSE: return ANDROID_COLOR_DEFAULT;
+    case ANDROID_LOG_DEBUG:   return ANDROID_COLOR_BLUE;
+    case ANDROID_LOG_INFO:    return ANDROID_COLOR_GREEN;
+    case ANDROID_LOG_WARN:    return ANDROID_COLOR_YELLOW;
+    case ANDROID_LOG_ERROR:   return ANDROID_COLOR_RED;
+    case ANDROID_LOG_FATAL:   return ANDROID_COLOR_RED;
+    case ANDROID_LOG_SILENT:  return ANDROID_COLOR_DEFAULT;
+
+    case ANDROID_LOG_DEFAULT:
+    case ANDROID_LOG_UNKNOWN:
+    default:                  return ANDROID_COLOR_DEFAULT;
+      /* clang-format on */
+  }
+}
+
+static android_LogPriority filterPriForTag(AndroidLogFormat* p_format, const char* tag) {
+  FilterInfo* p_curFilter;
+
+  for (p_curFilter = p_format->filters; p_curFilter != NULL; p_curFilter = p_curFilter->p_next) {
+    if (0 == strcmp(tag, p_curFilter->mTag)) {
+      if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) {
+        return p_format->global_pri;
+      } else {
+        return p_curFilter->mPri;
+      }
+    }
+  }
+
+  return p_format->global_pri;
+}
+
+/**
+ * returns 1 if this log line should be printed based on its priority
+ * and tag, and 0 if it should not
+ */
+int android_log_shouldPrintLine(AndroidLogFormat* p_format, const char* tag,
+                                android_LogPriority pri) {
+  return pri >= filterPriForTag(p_format, tag);
+}
+
+AndroidLogFormat* android_log_format_new() {
+  AndroidLogFormat* p_ret;
+
+  p_ret = static_cast<AndroidLogFormat*>(calloc(1, sizeof(AndroidLogFormat)));
+
+  p_ret->global_pri = ANDROID_LOG_VERBOSE;
+  p_ret->format = FORMAT_BRIEF;
+  p_ret->colored_output = false;
+  p_ret->usec_time_output = false;
+  p_ret->nsec_time_output = false;
+  p_ret->printable_output = false;
+  p_ret->year_output = false;
+  p_ret->zone_output = false;
+  p_ret->epoch_output = false;
+  p_ret->monotonic_output = false;
+  p_ret->uid_output = false;
+  p_ret->descriptive_output = false;
+  descriptive_output = false;
+
+  return p_ret;
+}
+
+static list_declare(convertHead);
+
+void android_log_format_free(AndroidLogFormat* p_format) {
+  FilterInfo *p_info, *p_info_old;
+
+  p_info = p_format->filters;
+
+  while (p_info != NULL) {
+    p_info_old = p_info;
+    p_info = p_info->p_next;
+
+    free(p_info_old);
+  }
+
+  free(p_format);
+
+  /* Free conversion resource, can always be reconstructed */
+  while (!list_empty(&convertHead)) {
+    struct listnode* node = list_head(&convertHead);
+    list_remove(node);
+    LOG_ALWAYS_FATAL_IF(node == list_head(&convertHead), "corrupted list");
+    free(node);
+  }
+}
+
+int android_log_setPrintFormat(AndroidLogFormat* p_format, AndroidLogPrintFormat format) {
+  switch (format) {
+    case FORMAT_MODIFIER_COLOR:
+      p_format->colored_output = true;
+      return 0;
+    case FORMAT_MODIFIER_TIME_USEC:
+      p_format->usec_time_output = true;
+      return 0;
+    case FORMAT_MODIFIER_TIME_NSEC:
+      p_format->nsec_time_output = true;
+      return 0;
+    case FORMAT_MODIFIER_PRINTABLE:
+      p_format->printable_output = true;
+      return 0;
+    case FORMAT_MODIFIER_YEAR:
+      p_format->year_output = true;
+      return 0;
+    case FORMAT_MODIFIER_ZONE:
+      p_format->zone_output = !p_format->zone_output;
+      return 0;
+    case FORMAT_MODIFIER_EPOCH:
+      p_format->epoch_output = true;
+      return 0;
+    case FORMAT_MODIFIER_MONOTONIC:
+      p_format->monotonic_output = true;
+      return 0;
+    case FORMAT_MODIFIER_UID:
+      p_format->uid_output = true;
+      return 0;
+    case FORMAT_MODIFIER_DESCRIPT:
+      p_format->descriptive_output = true;
+      descriptive_output = true;
+      return 0;
+    default:
+      break;
+  }
+  p_format->format = format;
+  return 1;
+}
+
+#if !defined(__MINGW32__)
+// Sets $TZ, but allows it to be optionally reset.
+class TzSetter {
+ public:
+  TzSetter(const char* new_tz) {
+    old_tz_ = getenv("TZ");
+    if (old_tz_) old_tz_ = strdup(old_tz_);
+
+    setenv("TZ", new_tz, 1);
+    tzset();
+  }
+
+  ~TzSetter() { free(old_tz_); }
+
+  void Reset() {
+    if (old_tz_) {
+      setenv("TZ", old_tz_, 1);
+    } else {
+      unsetenv("TZ");
+    }
+    tzset();
+  }
+
+ private:
+  char* old_tz_;
+};
+#endif
+
+AndroidLogPrintFormat android_log_formatFromString(const char* formatString) {
+  /* clang-format off */
+  if (!strcmp(formatString, "brief")) return FORMAT_BRIEF;
+  if (!strcmp(formatString, "process")) return FORMAT_PROCESS;
+  if (!strcmp(formatString, "tag")) return FORMAT_TAG;
+  if (!strcmp(formatString, "thread")) return FORMAT_THREAD;
+  if (!strcmp(formatString, "raw")) return FORMAT_RAW;
+  if (!strcmp(formatString, "time")) return FORMAT_TIME;
+  if (!strcmp(formatString, "threadtime")) return FORMAT_THREADTIME;
+  if (!strcmp(formatString, "long")) return FORMAT_LONG;
+  if (!strcmp(formatString, "color")) return FORMAT_MODIFIER_COLOR;
+  if (!strcmp(formatString, "colour")) return FORMAT_MODIFIER_COLOR;
+  if (!strcmp(formatString, "usec")) return FORMAT_MODIFIER_TIME_USEC;
+  if (!strcmp(formatString, "nsec")) return FORMAT_MODIFIER_TIME_NSEC;
+  if (!strcmp(formatString, "printable")) return FORMAT_MODIFIER_PRINTABLE;
+  if (!strcmp(formatString, "year")) return FORMAT_MODIFIER_YEAR;
+  if (!strcmp(formatString, "zone")) return FORMAT_MODIFIER_ZONE;
+  if (!strcmp(formatString, "epoch")) return FORMAT_MODIFIER_EPOCH;
+  if (!strcmp(formatString, "monotonic")) return FORMAT_MODIFIER_MONOTONIC;
+  if (!strcmp(formatString, "uid")) return FORMAT_MODIFIER_UID;
+  if (!strcmp(formatString, "descriptive")) return FORMAT_MODIFIER_DESCRIPT;
+    /* clang-format on */
+
+#if !defined(__MINGW32__)
+  // Check whether the format string is actually a time zone. If tzname[0]
+  // is the empty string, that's tzset() signalling that it doesn't know
+  // the requested timezone.
+  TzSetter tz(formatString);
+  if (!*tzname[0]) {
+    tz.Reset();
+  } else {
+    // We keep the new time zone as a side effect!
+    return FORMAT_MODIFIER_ZONE;
+  }
+#endif
+
+  return FORMAT_OFF;
+}
+
+/**
+ * filterExpression: a single filter expression
+ * eg "AT:d"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ */
+
+int android_log_addFilterRule(AndroidLogFormat* p_format, const char* filterExpression) {
+  size_t tagNameLength;
+  android_LogPriority pri = ANDROID_LOG_DEFAULT;
+
+  tagNameLength = strcspn(filterExpression, ":");
+
+  if (tagNameLength == 0) {
+    goto error;
+  }
+
+  if (filterExpression[tagNameLength] == ':') {
+    pri = filterCharToPri(filterExpression[tagNameLength + 1]);
+
+    if (pri == ANDROID_LOG_UNKNOWN) {
+      goto error;
+    }
+  }
+
+  if (0 == strncmp("*", filterExpression, tagNameLength)) {
+    /*
+     * This filter expression refers to the global filter
+     * The default level for this is DEBUG if the priority
+     * is unspecified
+     */
+    if (pri == ANDROID_LOG_DEFAULT) {
+      pri = ANDROID_LOG_DEBUG;
+    }
+
+    p_format->global_pri = pri;
+  } else {
+    /*
+     * for filter expressions that don't refer to the global
+     * filter, the default is verbose if the priority is unspecified
+     */
+    if (pri == ANDROID_LOG_DEFAULT) {
+      pri = ANDROID_LOG_VERBOSE;
+    }
+
+    char* tagName;
+
+/*
+ * Presently HAVE_STRNDUP is never defined, so the second case is always taken
+ * Darwin doesn't have strndup, everything else does
+ */
+#ifdef HAVE_STRNDUP
+    tagName = strndup(filterExpression, tagNameLength);
+#else
+    /* a few extra bytes copied... */
+    tagName = strdup(filterExpression);
+    tagName[tagNameLength] = '\0';
+#endif /*HAVE_STRNDUP*/
+
+    FilterInfo* p_fi = filterinfo_new(tagName, pri);
+    free(tagName);
+
+    p_fi->p_next = p_format->filters;
+    p_format->filters = p_fi;
+  }
+
+  return 0;
+error:
+  return -1;
+}
+
+#if defined(__MINGW32__)  // Windows doesn't have strsep(3).
+static char* strsep(char** stringp, const char* delim) {
+  char* token;
+  char* ret = *stringp;
+
+  if (!ret || !*ret) {
+    return NULL;
+  }
+  token = strpbrk(ret, delim);
+  if (token) {
+    *token = '\0';
+    ++token;
+  } else {
+    token = ret + strlen(ret);
+  }
+  *stringp = token;
+  return ret;
+}
+#endif
+
+/**
+ * filterString: a comma/whitespace-separated set of filter expressions
+ *
+ * eg "AT:d *:i"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+int android_log_addFilterString(AndroidLogFormat* p_format, const char* filterString) {
+  char* filterStringCopy = strdup(filterString);
+  char* p_cur = filterStringCopy;
+  char* p_ret;
+  int err;
+
+  /* Yes, I'm using strsep */
+  while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {
+    /* ignore whitespace-only entries */
+    if (p_ret[0] != '\0') {
+      err = android_log_addFilterRule(p_format, p_ret);
+
+      if (err < 0) {
+        goto error;
+      }
+    }
+  }
+
+  free(filterStringCopy);
+  return 0;
+error:
+  free(filterStringCopy);
+  return -1;
+}
+
+/**
+ * Splits a wire-format buffer into an AndroidLogEntry
+ * entry allocated by caller. Pointers will point directly into buf
+ *
+ * Returns 0 on success and -1 on invalid wire format (entry will be
+ * in unspecified state)
+ */
+int android_log_processLogBuffer(struct logger_entry* buf, AndroidLogEntry* entry) {
+  entry->message = NULL;
+  entry->messageLen = 0;
+
+  entry->tv_sec = buf->sec;
+  entry->tv_nsec = buf->nsec;
+  entry->uid = -1;
+  entry->pid = buf->pid;
+  entry->tid = buf->tid;
+
+  /*
+   * format: <priority:1><tag:N>\0<message:N>\0
+   *
+   * tag str
+   *   starts at buf + buf->hdr_size + 1
+   * msg
+   *   starts at buf + buf->hdr_size + 1 + len(tag) + 1
+   *
+   * The message may have been truncated.  When that happens, we must null-terminate the message
+   * ourselves.
+   */
+  if (buf->len < 3) {
+    /*
+     * An well-formed entry must consist of at least a priority
+     * and two null characters
+     */
+    fprintf(stderr, "+++ LOG: entry too small\n");
+    return -1;
+  }
+
+  int msgStart = -1;
+  int msgEnd = -1;
+
+  int i;
+  if (buf->hdr_size < sizeof(logger_entry)) {
+    fprintf(stderr, "+++ LOG: hdr_size must be at least as big as struct logger_entry\n");
+    return -1;
+  }
+  char* msg = reinterpret_cast<char*>(buf) + buf->hdr_size;
+  entry->uid = buf->uid;
+
+  for (i = 1; i < buf->len; i++) {
+    if (msg[i] == '\0') {
+      if (msgStart == -1) {
+        msgStart = i + 1;
+      } else {
+        msgEnd = i;
+        break;
+      }
+    }
+  }
+
+  if (msgStart == -1) {
+    /* +++ LOG: malformed log message, DYB */
+    for (i = 1; i < buf->len; i++) {
+      /* odd characters in tag? */
+      if ((msg[i] <= ' ') || (msg[i] == ':') || (msg[i] >= 0x7f)) {
+        msg[i] = '\0';
+        msgStart = i + 1;
+        break;
+      }
+    }
+    if (msgStart == -1) {
+      msgStart = buf->len - 1; /* All tag, no message, print truncates */
+    }
+  }
+  if (msgEnd == -1) {
+    /* incoming message not null-terminated; force it */
+    msgEnd = buf->len - 1; /* may result in msgEnd < msgStart */
+    msg[msgEnd] = '\0';
+  }
+
+  entry->priority = static_cast<android_LogPriority>(msg[0]);
+  entry->tag = msg + 1;
+  entry->tagLen = msgStart - 1;
+  entry->message = msg + msgStart;
+  entry->messageLen = (msgEnd < msgStart) ? 0 : (msgEnd - msgStart);
+
+  return 0;
+}
+
+static bool findChar(const char** cp, size_t* len, int c) {
+  while ((*len) && isspace(*(*cp))) {
+    ++(*cp);
+    --(*len);
+  }
+  if (c == INT_MAX) return *len;
+  if ((*len) && (*(*cp) == c)) {
+    ++(*cp);
+    --(*len);
+    return true;
+  }
+  return false;
+}
+
+/*
+ * Recursively convert binary log data to printable form.
+ *
+ * This needs to be recursive because you can have lists of lists.
+ *
+ * If we run out of room, we stop processing immediately.  It's important
+ * for us to check for space on every output element to avoid producing
+ * garbled output.
+ *
+ * Returns 0 on success, 1 on buffer full, -1 on failure.
+ */
+enum objectType {
+  TYPE_OBJECTS = '1',
+  TYPE_BYTES = '2',
+  TYPE_MILLISECONDS = '3',
+  TYPE_ALLOCATIONS = '4',
+  TYPE_ID = '5',
+  TYPE_PERCENT = '6',
+  TYPE_MONOTONIC = 's'
+};
+
+static int android_log_printBinaryEvent(const unsigned char** pEventData, size_t* pEventDataLen,
+                                        char** pOutBuf, size_t* pOutBufLen, const char** fmtStr,
+                                        size_t* fmtLen) {
+  const unsigned char* eventData = *pEventData;
+  size_t eventDataLen = *pEventDataLen;
+  char* outBuf = *pOutBuf;
+  char* outBufSave = outBuf;
+  size_t outBufLen = *pOutBufLen;
+  size_t outBufLenSave = outBufLen;
+  unsigned char type;
+  size_t outCount = 0;
+  int result = 0;
+  const char* cp;
+  size_t len;
+  int64_t lval;
+
+  if (eventDataLen < 1) return -1;
+
+  type = *eventData;
+
+  cp = NULL;
+  len = 0;
+  if (fmtStr && *fmtStr && fmtLen && *fmtLen && **fmtStr) {
+    cp = *fmtStr;
+    len = *fmtLen;
+  }
+  /*
+   * event.logtag format specification:
+   *
+   * Optionally, after the tag names can be put a description for the value(s)
+   * of the tag. Description are in the format
+   *    (<name>|data type[|data unit])
+   * Multiple values are separated by commas.
+   *
+   * The data type is a number from the following values:
+   * 1: int
+   * 2: long
+   * 3: string
+   * 4: list
+   * 5: float
+   *
+   * The data unit is a number taken from the following list:
+   * 1: Number of objects
+   * 2: Number of bytes
+   * 3: Number of milliseconds
+   * 4: Number of allocations
+   * 5: Id
+   * 6: Percent
+   * s: Number of seconds (monotonic time)
+   * Default value for data of type int/long is 2 (bytes).
+   */
+  if (!cp || !findChar(&cp, &len, '(')) {
+    len = 0;
+  } else {
+    char* outBufLastSpace = NULL;
+
+    findChar(&cp, &len, INT_MAX);
+    while (len && *cp && (*cp != '|') && (*cp != ')')) {
+      if (outBufLen <= 0) {
+        /* halt output */
+        goto no_room;
+      }
+      outBufLastSpace = isspace(*cp) ? outBuf : NULL;
+      *outBuf = *cp;
+      ++outBuf;
+      ++cp;
+      --outBufLen;
+      --len;
+    }
+    if (outBufLastSpace) {
+      outBufLen += outBuf - outBufLastSpace;
+      outBuf = outBufLastSpace;
+    }
+    if (outBufLen <= 0) {
+      /* halt output */
+      goto no_room;
+    }
+    if (outBufSave != outBuf) {
+      *outBuf = '=';
+      ++outBuf;
+      --outBufLen;
+    }
+
+    if (findChar(&cp, &len, '|') && findChar(&cp, &len, INT_MAX)) {
+      static const unsigned char typeTable[] = {EVENT_TYPE_INT, EVENT_TYPE_LONG, EVENT_TYPE_STRING,
+                                                EVENT_TYPE_LIST, EVENT_TYPE_FLOAT};
+
+      if ((*cp >= '1') && (*cp < (char)('1' + (sizeof(typeTable) / sizeof(typeTable[0])))) &&
+          (type != typeTable[(size_t)(*cp - '1')]))
+        len = 0;
+
+      if (len) {
+        ++cp;
+        --len;
+      } else {
+        /* reset the format */
+        outBuf = outBufSave;
+        outBufLen = outBufLenSave;
+      }
+    }
+  }
+  outCount = 0;
+  lval = 0;
+  switch (type) {
+    case EVENT_TYPE_INT:
+      /* 32-bit signed int */
+      {
+        if (eventDataLen < sizeof(android_event_int_t)) return -1;
+        auto* event_int = reinterpret_cast<const android_event_int_t*>(eventData);
+        lval = event_int->data;
+        eventData += sizeof(android_event_int_t);
+        eventDataLen -= sizeof(android_event_int_t);
+      }
+      goto pr_lval;
+    case EVENT_TYPE_LONG:
+      /* 64-bit signed long */
+      if (eventDataLen < sizeof(android_event_long_t)) {
+        return -1;
+      }
+      {
+        auto* event_long = reinterpret_cast<const android_event_long_t*>(eventData);
+        lval = event_long->data;
+      }
+      eventData += sizeof(android_event_long_t);
+      eventDataLen -= sizeof(android_event_long_t);
+    pr_lval:
+      outCount = snprintf(outBuf, outBufLen, "%" PRId64, lval);
+      if (outCount < outBufLen) {
+        outBuf += outCount;
+        outBufLen -= outCount;
+      } else {
+        /* halt output */
+        goto no_room;
+      }
+      break;
+    case EVENT_TYPE_FLOAT:
+      /* float */
+      {
+        if (eventDataLen < sizeof(android_event_float_t)) return -1;
+        auto* event_float = reinterpret_cast<const android_event_float_t*>(eventData);
+        float fval = event_float->data;
+        eventData += sizeof(android_event_int_t);
+        eventDataLen -= sizeof(android_event_int_t);
+
+        outCount = snprintf(outBuf, outBufLen, "%f", fval);
+        if (outCount < outBufLen) {
+          outBuf += outCount;
+          outBufLen -= outCount;
+        } else {
+          /* halt output */
+          goto no_room;
+        }
+      }
+      break;
+    case EVENT_TYPE_STRING:
+      /* UTF-8 chars, not NULL-terminated */
+      {
+        if (eventDataLen < sizeof(android_event_string_t)) return -1;
+        auto* event_string = reinterpret_cast<const android_event_string_t*>(eventData);
+        unsigned int strLen = event_string->length;
+        eventData += sizeof(android_event_string_t);
+        eventDataLen -= sizeof(android_event_string_t);
+
+        if (eventDataLen < strLen) {
+          result = -1; /* mark truncated */
+          strLen = eventDataLen;
+        }
+
+        if (cp && (strLen == 0)) {
+          /* reset the format if no content */
+          outBuf = outBufSave;
+          outBufLen = outBufLenSave;
+        }
+        if (strLen < outBufLen) {
+          memcpy(outBuf, eventData, strLen);
+          outBuf += strLen;
+          outBufLen -= strLen;
+        } else {
+          if (outBufLen > 0) {
+            /* copy what we can */
+            memcpy(outBuf, eventData, outBufLen);
+            outBuf += outBufLen;
+            outBufLen -= outBufLen;
+          }
+          if (!result) result = 1; /* if not truncated, return no room */
+        }
+        eventData += strLen;
+        eventDataLen -= strLen;
+        if (result != 0) goto bail;
+        break;
+      }
+    case EVENT_TYPE_LIST:
+      /* N items, all different types */
+      {
+        if (eventDataLen < sizeof(android_event_list_t)) return -1;
+        auto* event_list = reinterpret_cast<const android_event_list_t*>(eventData);
+
+        int8_t count = event_list->element_count;
+        eventData += sizeof(android_event_list_t);
+        eventDataLen -= sizeof(android_event_list_t);
+
+        if (outBufLen <= 0) goto no_room;
+
+        *outBuf++ = '[';
+        outBufLen--;
+
+        for (int i = 0; i < count; i++) {
+          result = android_log_printBinaryEvent(&eventData, &eventDataLen, &outBuf, &outBufLen,
+                                                fmtStr, fmtLen);
+          if (result != 0) goto bail;
+
+          if (i < (count - 1)) {
+            if (outBufLen <= 0) goto no_room;
+            *outBuf++ = ',';
+            outBufLen--;
+          }
+        }
+
+        if (outBufLen <= 0) goto no_room;
+
+        *outBuf++ = ']';
+        outBufLen--;
+      }
+      break;
+    default:
+      fprintf(stderr, "Unknown binary event type %d\n", type);
+      return -1;
+  }
+  if (cp && len) {
+    if (findChar(&cp, &len, '|') && findChar(&cp, &len, INT_MAX)) {
+      switch (*cp) {
+        case TYPE_OBJECTS:
+          outCount = 0;
+          /* outCount = snprintf(outBuf, outBufLen, " objects"); */
+          break;
+        case TYPE_BYTES:
+          if ((lval != 0) && ((lval % 1024) == 0)) {
+            /* repaint with multiplier */
+            static const char suffixTable[] = {'K', 'M', 'G', 'T'};
+            size_t idx = 0;
+            outBuf -= outCount;
+            outBufLen += outCount;
+            do {
+              lval /= 1024;
+              if ((lval % 1024) != 0) break;
+            } while (++idx < ((sizeof(suffixTable) / sizeof(suffixTable[0])) - 1));
+            outCount = snprintf(outBuf, outBufLen, "%" PRId64 "%cB", lval, suffixTable[idx]);
+          } else {
+            outCount = snprintf(outBuf, outBufLen, "B");
+          }
+          break;
+        case TYPE_MILLISECONDS:
+          if (((lval <= -1000) || (1000 <= lval)) && (outBufLen || (outBuf[-1] == '0'))) {
+            /* repaint as (fractional) seconds, possibly saving space */
+            if (outBufLen) outBuf[0] = outBuf[-1];
+            outBuf[-1] = outBuf[-2];
+            outBuf[-2] = outBuf[-3];
+            outBuf[-3] = '.';
+            while ((outBufLen == 0) || (*outBuf == '0')) {
+              --outBuf;
+              ++outBufLen;
+            }
+            if (*outBuf != '.') {
+              ++outBuf;
+              --outBufLen;
+            }
+            outCount = snprintf(outBuf, outBufLen, "s");
+          } else {
+            outCount = snprintf(outBuf, outBufLen, "ms");
+          }
+          break;
+        case TYPE_MONOTONIC: {
+          static const uint64_t minute = 60;
+          static const uint64_t hour = 60 * minute;
+          static const uint64_t day = 24 * hour;
+
+          /* Repaint as unsigned seconds, minutes, hours ... */
+          outBuf -= outCount;
+          outBufLen += outCount;
+          uint64_t val = lval;
+          if (val >= day) {
+            outCount = snprintf(outBuf, outBufLen, "%" PRIu64 "d ", val / day);
+            if (outCount >= outBufLen) break;
+            outBuf += outCount;
+            outBufLen -= outCount;
+            val = (val % day) + day;
+          }
+          if (val >= minute) {
+            if (val >= hour) {
+              outCount = snprintf(outBuf, outBufLen, "%" PRIu64 ":", (val / hour) % (day / hour));
+              if (outCount >= outBufLen) break;
+              outBuf += outCount;
+              outBufLen -= outCount;
+            }
+            outCount =
+                snprintf(outBuf, outBufLen, (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
+                         (val / minute) % (hour / minute));
+            if (outCount >= outBufLen) break;
+            outBuf += outCount;
+            outBufLen -= outCount;
+          }
+          outCount = snprintf(outBuf, outBufLen, (val >= minute) ? "%02" PRIu64 : "%" PRIu64 "s",
+                              val % minute);
+        } break;
+        case TYPE_ALLOCATIONS:
+          outCount = 0;
+          /* outCount = snprintf(outBuf, outBufLen, " allocations"); */
+          break;
+        case TYPE_ID:
+          outCount = 0;
+          break;
+        case TYPE_PERCENT:
+          outCount = snprintf(outBuf, outBufLen, "%%");
+          break;
+        default: /* ? */
+          outCount = 0;
+          break;
+      }
+      ++cp;
+      --len;
+      if (outCount < outBufLen) {
+        outBuf += outCount;
+        outBufLen -= outCount;
+      } else if (outCount) {
+        /* halt output */
+        goto no_room;
+      }
+    }
+    if (!findChar(&cp, &len, ')')) len = 0;
+    if (!findChar(&cp, &len, ',')) len = 0;
+  }
+
+bail:
+  *pEventData = eventData;
+  *pEventDataLen = eventDataLen;
+  *pOutBuf = outBuf;
+  *pOutBufLen = outBufLen;
+  if (cp) {
+    *fmtStr = cp;
+    *fmtLen = len;
+  }
+  return result;
+
+no_room:
+  result = 1;
+  goto bail;
+}
+
+/**
+ * Convert a binary log entry to ASCII form.
+ *
+ * For convenience we mimic the processLogBuffer API.  There is no
+ * pre-defined output length for the binary data, since we're free to format
+ * it however we choose, which means we can't really use a fixed-size buffer
+ * here.
+ */
+int android_log_processBinaryLogBuffer(
+    struct logger_entry* buf, AndroidLogEntry* entry,
+    [[maybe_unused]] const EventTagMap* map, /* only on !__ANDROID__ */
+    char* messageBuf, int messageBufLen) {
+  size_t inCount;
+  uint32_t tagIndex;
+  const unsigned char* eventData;
+
+  entry->message = NULL;
+  entry->messageLen = 0;
+
+  entry->tv_sec = buf->sec;
+  entry->tv_nsec = buf->nsec;
+  entry->priority = ANDROID_LOG_INFO;
+  entry->uid = -1;
+  entry->pid = buf->pid;
+  entry->tid = buf->tid;
+
+  if (buf->hdr_size < sizeof(logger_entry)) {
+    fprintf(stderr, "+++ LOG: hdr_size must be at least as big as struct logger_entry\n");
+    return -1;
+  }
+  eventData = reinterpret_cast<unsigned char*>(buf) + buf->hdr_size;
+  if (buf->lid == LOG_ID_SECURITY) {
+    entry->priority = ANDROID_LOG_WARN;
+  }
+  entry->uid = buf->uid;
+  inCount = buf->len;
+  if (inCount < sizeof(android_event_header_t)) return -1;
+  auto* event_header = reinterpret_cast<const android_event_header_t*>(eventData);
+  tagIndex = event_header->tag;
+  eventData += sizeof(android_event_header_t);
+  inCount -= sizeof(android_event_header_t);
+
+  entry->tagLen = 0;
+  entry->tag = NULL;
+#ifdef __ANDROID__
+  if (map != NULL) {
+    entry->tag = android_lookupEventTag_len(map, &entry->tagLen, tagIndex);
+  }
+#endif
+
+  /*
+   * If we don't have a map, or didn't find the tag number in the map,
+   * stuff a generated tag value into the start of the output buffer and
+   * shift the buffer pointers down.
+   */
+  if (entry->tag == NULL) {
+    size_t tagLen;
+
+    tagLen = snprintf(messageBuf, messageBufLen, "[%" PRIu32 "]", tagIndex);
+    if (tagLen >= (size_t)messageBufLen) {
+      tagLen = messageBufLen - 1;
+    }
+    entry->tag = messageBuf;
+    entry->tagLen = tagLen;
+    messageBuf += tagLen + 1;
+    messageBufLen -= tagLen + 1;
+  }
+
+  /*
+   * Format the event log data into the buffer.
+   */
+  const char* fmtStr = NULL;
+  size_t fmtLen = 0;
+#ifdef __ANDROID__
+  if (descriptive_output && map) {
+    fmtStr = android_lookupEventFormat_len(map, &fmtLen, tagIndex);
+  }
+#endif
+
+  char* outBuf = messageBuf;
+  size_t outRemaining = messageBufLen - 1; /* leave one for nul byte */
+  int result = 0;
+
+  if ((inCount > 0) || fmtLen) {
+    result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, &outRemaining, &fmtStr,
+                                          &fmtLen);
+  }
+  if ((result == 1) && fmtStr) {
+    /* We overflowed :-(, let's repaint the line w/o format dressings */
+    eventData = reinterpret_cast<unsigned char*>(buf) + buf->hdr_size;
+    eventData += 4;
+    outBuf = messageBuf;
+    outRemaining = messageBufLen - 1;
+    result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, &outRemaining, NULL, NULL);
+  }
+  if (result < 0) {
+    fprintf(stderr, "Binary log entry conversion failed\n");
+  }
+  if (result) {
+    if (!outRemaining) {
+      /* make space to leave an indicator */
+      --outBuf;
+      ++outRemaining;
+    }
+    *outBuf++ = (result < 0) ? '!' : '^'; /* Error or Truncation? */
+    outRemaining--;
+    /* pretend we ate all the data to prevent log stutter */
+    inCount = 0;
+    if (result > 0) result = 0;
+  }
+
+  /* eat the silly terminating '\n' */
+  if (inCount == 1 && *eventData == '\n') {
+    eventData++;
+    inCount--;
+  }
+
+  if (inCount != 0) {
+    fprintf(stderr, "Warning: leftover binary log data (%zu bytes)\n", inCount);
+  }
+
+  /*
+   * Terminate the buffer.  The NUL byte does not count as part of
+   * entry->messageLen.
+   */
+  *outBuf = '\0';
+  entry->messageLen = outBuf - messageBuf;
+  assert(entry->messageLen == (messageBufLen - 1) - outRemaining);
+
+  entry->message = messageBuf;
+
+  return result;
+}
+
+/*
+ * Convert to printable from message to p buffer, return string length. If p is
+ * NULL, do not copy, but still return the expected string length.
+ */
+size_t convertPrintable(char* p, const char* message, size_t messageLen) {
+  char* begin = p;
+  bool print = p != NULL;
+  mbstate_t mb_state = {};
+
+  while (messageLen) {
+    char buf[6];
+    ssize_t len = sizeof(buf) - 1;
+    if ((size_t)len > messageLen) {
+      len = messageLen;
+    }
+    len = mbrtowc(nullptr, message, len, &mb_state);
+
+    if (len <= 0) {
+      snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
+      len = 1;
+    } else {
+      buf[0] = '\0';
+      if (len == 1) {
+        if (*message == '\a') {
+          strcpy(buf, "\\a");
+        } else if (*message == '\b') {
+          strcpy(buf, "\\b");
+        } else if (*message == '\t') {
+          strcpy(buf, "\t"); /* Do not escape tabs */
+        } else if (*message == '\v') {
+          strcpy(buf, "\\v");
+        } else if (*message == '\f') {
+          strcpy(buf, "\\f");
+        } else if (*message == '\r') {
+          strcpy(buf, "\\r");
+        } else if (*message == '\\') {
+          strcpy(buf, "\\\\");
+        } else if ((*message < ' ') || (*message & 0x80)) {
+          snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
+        }
+      }
+      if (!buf[0]) {
+        strncpy(buf, message, len);
+        buf[len] = '\0';
+      }
+    }
+    if (print) {
+      strcpy(p, buf);
+    }
+    p += strlen(buf);
+    message += len;
+    messageLen -= len;
+  }
+  return p - begin;
+}
+
+#ifdef __ANDROID__
+static char* readSeconds(char* e, struct timespec* t) {
+  unsigned long multiplier;
+  char* p;
+  t->tv_sec = strtoul(e, &p, 10);
+  if (*p != '.') {
+    return NULL;
+  }
+  t->tv_nsec = 0;
+  multiplier = NS_PER_SEC;
+  while (isdigit(*++p) && (multiplier /= 10)) {
+    t->tv_nsec += (*p - '0') * multiplier;
+  }
+  return p;
+}
+
+static struct timespec* sumTimespec(struct timespec* left, struct timespec* right) {
+  left->tv_nsec += right->tv_nsec;
+  left->tv_sec += right->tv_sec;
+  if (left->tv_nsec >= (long)NS_PER_SEC) {
+    left->tv_nsec -= NS_PER_SEC;
+    left->tv_sec += 1;
+  }
+  return left;
+}
+
+static struct timespec* subTimespec(struct timespec* result, struct timespec* left,
+                                    struct timespec* right) {
+  result->tv_nsec = left->tv_nsec - right->tv_nsec;
+  result->tv_sec = left->tv_sec - right->tv_sec;
+  if (result->tv_nsec < 0) {
+    result->tv_nsec += NS_PER_SEC;
+    result->tv_sec -= 1;
+  }
+  return result;
+}
+
+static long long nsecTimespec(struct timespec* now) {
+  return (long long)now->tv_sec * NS_PER_SEC + now->tv_nsec;
+}
+
+static void convertMonotonic(struct timespec* result, const AndroidLogEntry* entry) {
+  struct listnode* node;
+  struct conversionList {
+    struct listnode node; /* first */
+    struct timespec time;
+    struct timespec convert;
+  } * list, *next;
+  struct timespec time, convert;
+
+  /* If we do not have a conversion list, build one up */
+  if (list_empty(&convertHead)) {
+    bool suspended_pending = false;
+    struct timespec suspended_monotonic = {0, 0};
+    struct timespec suspended_diff = {0, 0};
+
+    /*
+     * Read dmesg for _some_ synchronization markers and insert
+     * Anything in the Android Logger before the dmesg logging span will
+     * be highly suspect regarding the monotonic time calculations.
+     */
+    FILE* p = popen("/system/bin/dmesg", "re");
+    if (p) {
+      char* line = NULL;
+      size_t len = 0;
+      while (getline(&line, &len, p) > 0) {
+        static const char suspend[] = "PM: suspend entry ";
+        static const char resume[] = "PM: suspend exit ";
+        static const char healthd[] = "healthd";
+        static const char battery[] = ": battery ";
+        static const char suspended[] = "Suspended for ";
+        struct timespec monotonic;
+        struct tm tm;
+        char *cp, *e = line;
+        bool add_entry = true;
+
+        if (*e == '<') {
+          while (*e && (*e != '>')) {
+            ++e;
+          }
+          if (*e != '>') {
+            continue;
+          }
+        }
+        if (*e != '[') {
+          continue;
+        }
+        while (*++e == ' ') {
+          ;
+        }
+        e = readSeconds(e, &monotonic);
+        if (!e || (*e != ']')) {
+          continue;
+        }
+
+        if ((e = strstr(e, suspend))) {
+          e += sizeof(suspend) - 1;
+        } else if ((e = strstr(line, resume))) {
+          e += sizeof(resume) - 1;
+        } else if (((e = strstr(line, healthd))) &&
+                   ((e = strstr(e + sizeof(healthd) - 1, battery)))) {
+          /* NB: healthd is roughly 150us late, worth the price to
+           * deal with ntp-induced or hardware clock drift. */
+          e += sizeof(battery) - 1;
+        } else if ((e = strstr(line, suspended))) {
+          e += sizeof(suspended) - 1;
+          e = readSeconds(e, &time);
+          if (!e) {
+            continue;
+          }
+          add_entry = false;
+          suspended_pending = true;
+          suspended_monotonic = monotonic;
+          suspended_diff = time;
+        } else {
+          continue;
+        }
+        if (add_entry) {
+          /* look for "????-??-?? ??:??:??.????????? UTC" */
+          cp = strstr(e, " UTC");
+          if (!cp || ((cp - e) < 29) || (cp[-10] != '.')) {
+            continue;
+          }
+          e = cp - 29;
+          cp = readSeconds(cp - 10, &time);
+          if (!cp) {
+            continue;
+          }
+          cp = strptime(e, "%Y-%m-%d %H:%M:%S.", &tm);
+          if (!cp) {
+            continue;
+          }
+          {
+            TzSetter tz(cp);
+            time.tv_sec = mktime(&tm);
+          }
+          list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+          list_init(&list->node);
+          list->time = time;
+          subTimespec(&list->convert, &time, &monotonic);
+          list_add_tail(&convertHead, &list->node);
+        }
+        if (suspended_pending && !list_empty(&convertHead)) {
+          list = node_to_item(list_tail(&convertHead), struct conversionList, node);
+          if (subTimespec(&time, subTimespec(&time, &list->time, &list->convert),
+                          &suspended_monotonic)
+                  ->tv_sec > 0) {
+            /* resume, what is convert factor before? */
+            subTimespec(&convert, &list->convert, &suspended_diff);
+          } else {
+            /* suspend */
+            convert = list->convert;
+          }
+          time = suspended_monotonic;
+          sumTimespec(&time, &convert);
+          /* breakpoint just before sleep */
+          list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+          list_init(&list->node);
+          list->time = time;
+          list->convert = convert;
+          list_add_tail(&convertHead, &list->node);
+          /* breakpoint just after sleep */
+          list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+          list_init(&list->node);
+          list->time = time;
+          sumTimespec(&list->time, &suspended_diff);
+          list->convert = convert;
+          sumTimespec(&list->convert, &suspended_diff);
+          list_add_tail(&convertHead, &list->node);
+          suspended_pending = false;
+        }
+      }
+      free(line);
+      pclose(p);
+    }
+    /* last entry is our current time conversion */
+    list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+    list_init(&list->node);
+    clock_gettime(CLOCK_REALTIME, &list->time);
+    clock_gettime(CLOCK_MONOTONIC, &convert);
+    clock_gettime(CLOCK_MONOTONIC, &time);
+    /* Correct for instant clock_gettime latency (syscall or ~30ns) */
+    subTimespec(&time, &convert, subTimespec(&time, &time, &convert));
+    /* Calculate conversion factor */
+    subTimespec(&list->convert, &list->time, &time);
+    list_add_tail(&convertHead, &list->node);
+    if (suspended_pending) {
+      /* manufacture a suspend @ point before */
+      subTimespec(&convert, &list->convert, &suspended_diff);
+      time = suspended_monotonic;
+      sumTimespec(&time, &convert);
+      /* breakpoint just after sleep */
+      list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+      list_init(&list->node);
+      list->time = time;
+      sumTimespec(&list->time, &suspended_diff);
+      list->convert = convert;
+      sumTimespec(&list->convert, &suspended_diff);
+      list_add_head(&convertHead, &list->node);
+      /* breakpoint just before sleep */
+      list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+      list_init(&list->node);
+      list->time = time;
+      list->convert = convert;
+      list_add_head(&convertHead, &list->node);
+    }
+  }
+
+  /* Find the breakpoint in the conversion list */
+  list = node_to_item(list_head(&convertHead), struct conversionList, node);
+  next = NULL;
+  list_for_each(node, &convertHead) {
+    next = node_to_item(node, struct conversionList, node);
+    if (entry->tv_sec < next->time.tv_sec) {
+      break;
+    } else if (entry->tv_sec == next->time.tv_sec) {
+      if (entry->tv_nsec < next->time.tv_nsec) {
+        break;
+      }
+    }
+    list = next;
+  }
+
+  /* blend time from one breakpoint to the next */
+  convert = list->convert;
+  if (next) {
+    unsigned long long total, run;
+
+    total = nsecTimespec(subTimespec(&time, &next->time, &list->time));
+    time.tv_sec = entry->tv_sec;
+    time.tv_nsec = entry->tv_nsec;
+    run = nsecTimespec(subTimespec(&time, &time, &list->time));
+    if (run < total) {
+      long long crun;
+
+      float f = nsecTimespec(subTimespec(&time, &next->convert, &convert));
+      f *= run;
+      f /= total;
+      crun = f;
+      convert.tv_sec += crun / (long long)NS_PER_SEC;
+      if (crun < 0) {
+        convert.tv_nsec -= (-crun) % NS_PER_SEC;
+        if (convert.tv_nsec < 0) {
+          convert.tv_nsec += NS_PER_SEC;
+          convert.tv_sec -= 1;
+        }
+      } else {
+        convert.tv_nsec += crun % NS_PER_SEC;
+        if (convert.tv_nsec >= (long)NS_PER_SEC) {
+          convert.tv_nsec -= NS_PER_SEC;
+          convert.tv_sec += 1;
+        }
+      }
+    }
+  }
+
+  /* Apply the correction factor */
+  result->tv_sec = entry->tv_sec;
+  result->tv_nsec = entry->tv_nsec;
+  subTimespec(result, result, &convert);
+}
+#endif
+
+/**
+ * Formats a log message into a buffer
+ *
+ * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
+ * If return value != defaultBuffer, caller must call free()
+ * Returns NULL on malloc error
+ */
+
+char* android_log_formatLogLine(AndroidLogFormat* p_format, char* defaultBuffer,
+                                size_t defaultBufferSize, const AndroidLogEntry* entry,
+                                size_t* p_outLength) {
+#if !defined(_WIN32)
+  struct tm tmBuf;
+#endif
+  struct tm* ptm;
+  /* good margin, 23+nul for msec, 26+nul for usec, 29+nul to nsec */
+  char timeBuf[64];
+  char prefixBuf[128], suffixBuf[128];
+  char priChar;
+  int prefixSuffixIsHeaderFooter = 0;
+  char* ret;
+  time_t now;
+  unsigned long nsec;
+
+  priChar = filterPriToChar(entry->priority);
+  size_t prefixLen = 0, suffixLen = 0;
+  size_t len;
+
+  /*
+   * Get the current date/time in pretty form
+   *
+   * It's often useful when examining a log with "less" to jump to
+   * a specific point in the file by searching for the date/time stamp.
+   * For this reason it's very annoying to have regexp meta characters
+   * in the time stamp.  Don't use forward slashes, parenthesis,
+   * brackets, asterisks, or other special chars here.
+   *
+   * The caller may have affected the timezone environment, this is
+   * expected to be sensitive to that.
+   */
+  now = entry->tv_sec;
+  nsec = entry->tv_nsec;
+#if __ANDROID__
+  if (p_format->monotonic_output) {
+    struct timespec time;
+    convertMonotonic(&time, entry);
+    now = time.tv_sec;
+    nsec = time.tv_nsec;
+  }
+#endif
+  if (now < 0) {
+    nsec = NS_PER_SEC - nsec;
+  }
+  if (p_format->epoch_output || p_format->monotonic_output) {
+    ptm = NULL;
+    snprintf(timeBuf, sizeof(timeBuf), p_format->monotonic_output ? "%6lld" : "%19lld",
+             (long long)now);
+  } else {
+#if !defined(_WIN32)
+    ptm = localtime_r(&now, &tmBuf);
+#else
+    ptm = localtime(&now);
+#endif
+    strftime(timeBuf, sizeof(timeBuf), &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3], ptm);
+  }
+  len = strlen(timeBuf);
+  if (p_format->nsec_time_output) {
+    len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%09ld", nsec);
+  } else if (p_format->usec_time_output) {
+    len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%06ld", nsec / US_PER_NSEC);
+  } else {
+    len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%03ld", nsec / MS_PER_NSEC);
+  }
+  if (p_format->zone_output && ptm) {
+    strftime(timeBuf + len, sizeof(timeBuf) - len, " %z", ptm);
+  }
+
+  /*
+   * Construct a buffer containing the log header and log message.
+   */
+  if (p_format->colored_output) {
+    prefixLen =
+        snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[%dm", colorFromPri(entry->priority));
+    prefixLen = std::min(prefixLen, sizeof(prefixBuf));
+
+    const char suffixContents[] = "\x1B[0m";
+    strcpy(suffixBuf, suffixContents);
+    suffixLen = strlen(suffixContents);
+  }
+
+  char uid[16];
+  uid[0] = '\0';
+  if (p_format->uid_output) {
+    if (entry->uid >= 0) {
+/*
+ * This code is Android specific, bionic guarantees that
+ * calls to non-reentrant getpwuid() are thread safe.
+ */
+#ifdef __ANDROID__
+      struct passwd* pwd = getpwuid(entry->uid);
+      if (pwd && (strlen(pwd->pw_name) <= 5)) {
+        snprintf(uid, sizeof(uid), "%5s:", pwd->pw_name);
+      } else
+#endif
+      {
+        /* Not worth parsing package list, names all longer than 5 */
+        snprintf(uid, sizeof(uid), "%5d:", entry->uid);
+      }
+    } else {
+      snprintf(uid, sizeof(uid), "      ");
+    }
+  }
+
+  switch (p_format->format) {
+    case FORMAT_TAG:
+      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c/%-8.*s: ", priChar,
+                     (int)entry->tagLen, entry->tag);
+      strcpy(suffixBuf + suffixLen, "\n");
+      ++suffixLen;
+      break;
+    case FORMAT_PROCESS:
+      len = snprintf(suffixBuf + suffixLen, sizeof(suffixBuf) - suffixLen, "  (%.*s)\n",
+                     (int)entry->tagLen, entry->tag);
+      suffixLen += std::min(len, sizeof(suffixBuf) - suffixLen);
+      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c(%s%5d) ", priChar,
+                     uid, entry->pid);
+      break;
+    case FORMAT_THREAD:
+      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c(%s%5d:%5d) ",
+                     priChar, uid, entry->pid, entry->tid);
+      strcpy(suffixBuf + suffixLen, "\n");
+      ++suffixLen;
+      break;
+    case FORMAT_RAW:
+      prefixBuf[prefixLen] = 0;
+      len = 0;
+      strcpy(suffixBuf + suffixLen, "\n");
+      ++suffixLen;
+      break;
+    case FORMAT_TIME:
+      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+                     "%s %c/%-8.*s(%s%5d): ", timeBuf, priChar, (int)entry->tagLen, entry->tag, uid,
+                     entry->pid);
+      strcpy(suffixBuf + suffixLen, "\n");
+      ++suffixLen;
+      break;
+    case FORMAT_THREADTIME:
+      ret = strchr(uid, ':');
+      if (ret) {
+        *ret = ' ';
+      }
+      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+                     "%s %s%5d %5d %c %-8.*s: ", timeBuf, uid, entry->pid, entry->tid, priChar,
+                     (int)entry->tagLen, entry->tag);
+      strcpy(suffixBuf + suffixLen, "\n");
+      ++suffixLen;
+      break;
+    case FORMAT_LONG:
+      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+                     "[ %s %s%5d:%5d %c/%-8.*s ]\n", timeBuf, uid, entry->pid, entry->tid, priChar,
+                     (int)entry->tagLen, entry->tag);
+      strcpy(suffixBuf + suffixLen, "\n\n");
+      suffixLen += 2;
+      prefixSuffixIsHeaderFooter = 1;
+      break;
+    case FORMAT_BRIEF:
+    default:
+      len =
+          snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+                   "%c/%-8.*s(%s%5d): ", priChar, (int)entry->tagLen, entry->tag, uid, entry->pid);
+      strcpy(suffixBuf + suffixLen, "\n");
+      ++suffixLen;
+      break;
+  }
+
+  /* snprintf has a weird return value.   It returns what would have been
+   * written given a large enough buffer.  In the case that the prefix is
+   * longer then our buffer(128), it messes up the calculations below
+   * possibly causing heap corruption.  To avoid this we double check and
+   * set the length at the maximum (size minus null byte)
+   */
+  prefixLen += len;
+  if (prefixLen >= sizeof(prefixBuf)) {
+    prefixLen = sizeof(prefixBuf) - 1;
+    prefixBuf[sizeof(prefixBuf) - 1] = '\0';
+  }
+  if (suffixLen >= sizeof(suffixBuf)) {
+    suffixLen = sizeof(suffixBuf) - 1;
+    suffixBuf[sizeof(suffixBuf) - 2] = '\n';
+    suffixBuf[sizeof(suffixBuf) - 1] = '\0';
+  }
+
+  /* the following code is tragically unreadable */
+
+  size_t numLines;
+  char* p;
+  size_t bufferSize;
+  const char* pm;
+
+  if (prefixSuffixIsHeaderFooter) {
+    /* we're just wrapping message with a header/footer */
+    numLines = 1;
+  } else {
+    pm = entry->message;
+    numLines = 0;
+
+    /*
+     * The line-end finding here must match the line-end finding
+     * in for ( ... numLines...) loop below
+     */
+    while (pm < (entry->message + entry->messageLen)) {
+      if (*pm++ == '\n') numLines++;
+    }
+    /* plus one line for anything not newline-terminated at the end */
+    if (pm > entry->message && *(pm - 1) != '\n') numLines++;
+  }
+
+  /*
+   * this is an upper bound--newlines in message may be counted
+   * extraneously
+   */
+  bufferSize = (numLines * (prefixLen + suffixLen)) + 1;
+  if (p_format->printable_output) {
+    /* Calculate extra length to convert non-printable to printable */
+    bufferSize += convertPrintable(NULL, entry->message, entry->messageLen);
+  } else {
+    bufferSize += entry->messageLen;
+  }
+
+  if (defaultBufferSize >= bufferSize) {
+    ret = defaultBuffer;
+  } else {
+    ret = (char*)malloc(bufferSize);
+
+    if (ret == NULL) {
+      return ret;
+    }
+  }
+
+  ret[0] = '\0'; /* to start strcat off */
+
+  p = ret;
+  pm = entry->message;
+
+  if (prefixSuffixIsHeaderFooter) {
+    strcat(p, prefixBuf);
+    p += prefixLen;
+    if (p_format->printable_output) {
+      p += convertPrintable(p, entry->message, entry->messageLen);
+    } else {
+      strncat(p, entry->message, entry->messageLen);
+      p += entry->messageLen;
+    }
+    strcat(p, suffixBuf);
+    p += suffixLen;
+  } else {
+    do {
+      const char* lineStart;
+      size_t lineLen;
+      lineStart = pm;
+
+      /* Find the next end-of-line in message */
+      while (pm < (entry->message + entry->messageLen) && *pm != '\n') pm++;
+      lineLen = pm - lineStart;
+
+      strcat(p, prefixBuf);
+      p += prefixLen;
+      if (p_format->printable_output) {
+        p += convertPrintable(p, lineStart, lineLen);
+      } else {
+        strncat(p, lineStart, lineLen);
+        p += lineLen;
+      }
+      strcat(p, suffixBuf);
+      p += suffixLen;
+
+      if (*pm == '\n') pm++;
+    } while (pm < (entry->message + entry->messageLen));
+  }
+
+  if (p_outLength != NULL) {
+    *p_outLength = p - ret;
+  }
+
+  return ret;
+}
+
+size_t android_log_printLogLine(AndroidLogFormat* p_format, FILE* fp,
+                                const AndroidLogEntry* entry) {
+  char buf[4096] __attribute__((__uninitialized__));
+  size_t line_length;
+  char* line = android_log_formatLogLine(p_format, buf, sizeof(buf), entry, &line_length);
+  if (!line) {
+    fprintf(stderr, "android_log_formatLogLine failed\n");
+    exit(1);
+  }
+
+  size_t bytesWritten = fwrite(line, 1, line_length, fp);
+  if (bytesWritten != line_length) {
+    perror("fwrite failed");
+    exit(1);
+  }
+
+  if (line != buf) free(line);
+  return bytesWritten;
+}
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
new file mode 100644
index 0000000..4d603db
--- /dev/null
+++ b/liblog/pmsg_reader.cpp
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2007-2016 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 "pmsg_reader.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <cutils/list.h>
+#include <private/android_logger.h>
+
+#include "logger.h"
+
+int PmsgRead(struct logger_list* logger_list, struct log_msg* log_msg) {
+  ssize_t ret;
+  off_t current, next;
+  struct __attribute__((__packed__)) {
+    android_pmsg_log_header_t p;
+    android_log_header_t l;
+    uint8_t prio;
+  } buf;
+  static uint8_t preread_count;
+
+  memset(log_msg, 0, sizeof(*log_msg));
+
+  if (atomic_load(&logger_list->fd) <= 0) {
+    int i, fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC);
+
+    if (fd < 0) {
+      return -errno;
+    }
+    if (fd == 0) { /* Argggg */
+      fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC);
+      close(0);
+      if (fd < 0) {
+        return -errno;
+      }
+    }
+    i = atomic_exchange(&logger_list->fd, fd);
+    if ((i > 0) && (i != fd)) {
+      close(i);
+    }
+    preread_count = 0;
+  }
+
+  while (1) {
+    int fd;
+
+    if (preread_count < sizeof(buf)) {
+      fd = atomic_load(&logger_list->fd);
+      if (fd <= 0) {
+        return -EBADF;
+      }
+      ret = TEMP_FAILURE_RETRY(read(fd, &buf.p.magic + preread_count, sizeof(buf) - preread_count));
+      if (ret < 0) {
+        return -errno;
+      }
+      preread_count += ret;
+    }
+    if (preread_count != sizeof(buf)) {
+      return preread_count ? -EIO : -EAGAIN;
+    }
+    if ((buf.p.magic != LOGGER_MAGIC) || (buf.p.len <= sizeof(buf)) ||
+        (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD)) || (buf.l.id >= LOG_ID_MAX) ||
+        (buf.l.realtime.tv_nsec >= NS_PER_SEC) ||
+        ((buf.l.id != LOG_ID_EVENTS) && (buf.l.id != LOG_ID_SECURITY) &&
+         ((buf.prio == ANDROID_LOG_UNKNOWN) || (buf.prio == ANDROID_LOG_DEFAULT) ||
+          (buf.prio >= ANDROID_LOG_SILENT)))) {
+      do {
+        memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count);
+      } while (preread_count && (buf.p.magic != LOGGER_MAGIC));
+      continue;
+    }
+    preread_count = 0;
+
+    if ((logger_list->log_mask & (1 << buf.l.id)) &&
+        ((!logger_list->start.tv_sec && !logger_list->start.tv_nsec) ||
+         ((logger_list->start.tv_sec <= buf.l.realtime.tv_sec) &&
+          ((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
+           (logger_list->start.tv_nsec <= buf.l.realtime.tv_nsec)))) &&
+        (!logger_list->pid || (logger_list->pid == buf.p.pid))) {
+      char* msg = reinterpret_cast<char*>(&log_msg->entry) + sizeof(log_msg->entry);
+      *msg = buf.prio;
+      fd = atomic_load(&logger_list->fd);
+      if (fd <= 0) {
+        return -EBADF;
+      }
+      ret = TEMP_FAILURE_RETRY(read(fd, msg + sizeof(buf.prio), buf.p.len - sizeof(buf)));
+      if (ret < 0) {
+        return -errno;
+      }
+      if (ret != (ssize_t)(buf.p.len - sizeof(buf))) {
+        return -EIO;
+      }
+
+      log_msg->entry.len = buf.p.len - sizeof(buf) + sizeof(buf.prio);
+      log_msg->entry.hdr_size = sizeof(log_msg->entry);
+      log_msg->entry.pid = buf.p.pid;
+      log_msg->entry.tid = buf.l.tid;
+      log_msg->entry.sec = buf.l.realtime.tv_sec;
+      log_msg->entry.nsec = buf.l.realtime.tv_nsec;
+      log_msg->entry.lid = buf.l.id;
+      log_msg->entry.uid = buf.p.uid;
+
+      return ret + sizeof(buf.prio) + log_msg->entry.hdr_size;
+    }
+
+    fd = atomic_load(&logger_list->fd);
+    if (fd <= 0) {
+      return -EBADF;
+    }
+    current = TEMP_FAILURE_RETRY(lseek(fd, (off_t)0, SEEK_CUR));
+    if (current < 0) {
+      return -errno;
+    }
+    fd = atomic_load(&logger_list->fd);
+    if (fd <= 0) {
+      return -EBADF;
+    }
+    next = TEMP_FAILURE_RETRY(lseek(fd, (off_t)(buf.p.len - sizeof(buf)), SEEK_CUR));
+    if (next < 0) {
+      return -errno;
+    }
+    if ((next - current) != (ssize_t)(buf.p.len - sizeof(buf))) {
+      return -EIO;
+    }
+  }
+}
+
+void PmsgClose(struct logger_list* logger_list) {
+  int fd = atomic_exchange(&logger_list->fd, 0);
+  if (fd > 0) {
+    close(fd);
+  }
+}
+
+static void* realloc_or_free(void* ptr, size_t new_size) {
+  void* result = realloc(ptr, new_size);
+  if (!result) {
+    free(ptr);
+  }
+  return result;
+}
+
+ssize_t __android_log_pmsg_file_read(log_id_t logId, char prio, const char* prefix,
+                                     __android_log_pmsg_file_read_fn fn, void* arg) {
+  ssize_t ret;
+  struct content {
+    struct listnode node;
+    struct logger_entry entry;
+  } * content;
+  struct names {
+    struct listnode node;
+    struct listnode content;
+    log_id_t id;
+    char prio;
+    char name[];
+  } * names;
+  struct listnode name_list;
+  struct listnode *node, *n;
+  size_t len, prefix_len;
+
+  if (!fn) {
+    return -EINVAL;
+  }
+
+  /* Add just enough clues in logger_list and transp to make API function */
+  struct logger_list logger_list = {
+      .mode = static_cast<int>(ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK),
+      .log_mask = (unsigned)-1};
+  logger_list.log_mask = (1 << logId);
+  logger_list.log_mask &= ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
+  if (!logger_list.log_mask) {
+    return -EINVAL;
+  }
+
+  /* Initialize name list */
+  list_init(&name_list);
+
+  ret = SSIZE_MAX;
+
+  /* Validate incoming prefix, shift until it contains only 0 or 1 : or / */
+  prefix_len = 0;
+  if (prefix) {
+    const char *prev = NULL, *last = NULL, *cp = prefix;
+    while ((cp = strpbrk(cp, "/:"))) {
+      prev = last;
+      last = cp;
+      cp = cp + 1;
+    }
+    if (prev) {
+      prefix = prev + 1;
+    }
+    prefix_len = strlen(prefix);
+  }
+
+  /* Read the file content */
+  log_msg log_msg;
+  while (PmsgRead(&logger_list, &log_msg) > 0) {
+    const char* cp;
+    size_t hdr_size = log_msg.entry.hdr_size;
+
+    char* msg = (char*)&log_msg + hdr_size;
+    const char* split = NULL;
+
+    if (hdr_size != sizeof(log_msg.entry)) {
+      continue;
+    }
+    /* Check for invalid sequence number */
+    if (log_msg.entry.nsec % ANDROID_LOG_PMSG_FILE_SEQUENCE ||
+        (log_msg.entry.nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
+            ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
+      continue;
+    }
+
+    /* Determine if it has <dirbase>:<filebase> format for tag */
+    len = log_msg.entry.len - sizeof(prio);
+    for (cp = msg + sizeof(prio); *cp && isprint(*cp) && !isspace(*cp) && --len; ++cp) {
+      if (*cp == ':') {
+        if (split) {
+          break;
+        }
+        split = cp;
+      }
+    }
+    if (*cp || !split) {
+      continue;
+    }
+
+    /* Filters */
+    if (prefix_len && strncmp(msg + sizeof(prio), prefix, prefix_len)) {
+      size_t offset;
+      /*
+       *   Allow : to be a synonym for /
+       * Things we do dealing with const char * and do not alloc
+       */
+      split = strchr(prefix, ':');
+      if (split) {
+        continue;
+      }
+      split = strchr(prefix, '/');
+      if (!split) {
+        continue;
+      }
+      offset = split - prefix;
+      if ((msg[offset + sizeof(prio)] != ':') || strncmp(msg + sizeof(prio), prefix, offset)) {
+        continue;
+      }
+      ++offset;
+      if ((prefix_len > offset) &&
+          strncmp(&msg[offset + sizeof(prio)], split + 1, prefix_len - offset)) {
+        continue;
+      }
+    }
+
+    if ((prio != ANDROID_LOG_ANY) && (*msg < prio)) {
+      continue;
+    }
+
+    /* check if there is an existing entry */
+    list_for_each(node, &name_list) {
+      names = node_to_item(node, struct names, node);
+      if (!strcmp(names->name, msg + sizeof(prio)) && names->id == log_msg.entry.lid &&
+          names->prio == *msg) {
+        break;
+      }
+    }
+
+    /* We do not have an existing entry, create and add one */
+    if (node == &name_list) {
+      static const char numbers[] = "0123456789";
+      unsigned long long nl;
+
+      len = strlen(msg + sizeof(prio)) + 1;
+      names = static_cast<struct names*>(calloc(1, sizeof(*names) + len));
+      if (!names) {
+        ret = -ENOMEM;
+        break;
+      }
+      strcpy(names->name, msg + sizeof(prio));
+      names->id = static_cast<log_id_t>(log_msg.entry.lid);
+      names->prio = *msg;
+      list_init(&names->content);
+      /*
+       * Insert in reverse numeric _then_ alpha sorted order as
+       * representative of log rotation:
+       *
+       *   log.10
+       *   klog.10
+       *   . . .
+       *   log.2
+       *   klog.2
+       *   log.1
+       *   klog.1
+       *   log
+       *   klog
+       *
+       * thus when we present the content, we are provided the oldest
+       * first, which when 'refreshed' could spill off the end of the
+       * pmsg FIFO but retaining the newest data for last with best
+       * chances to survive.
+       */
+      nl = 0;
+      cp = strpbrk(names->name, numbers);
+      if (cp) {
+        nl = strtoull(cp, NULL, 10);
+      }
+      list_for_each_reverse(node, &name_list) {
+        struct names* a_name = node_to_item(node, struct names, node);
+        const char* r = a_name->name;
+        int compare = 0;
+
+        unsigned long long nr = 0;
+        cp = strpbrk(r, numbers);
+        if (cp) {
+          nr = strtoull(cp, NULL, 10);
+        }
+        if (nr != nl) {
+          compare = (nl > nr) ? 1 : -1;
+        }
+        if (compare == 0) {
+          compare = strcmp(names->name, r);
+        }
+        if (compare <= 0) {
+          break;
+        }
+      }
+      list_add_head(node, &names->node);
+    }
+
+    /* Remove any file fragments that match our sequence number */
+    list_for_each_safe(node, n, &names->content) {
+      content = node_to_item(node, struct content, node);
+      if (log_msg.entry.nsec == content->entry.nsec) {
+        list_remove(&content->node);
+        free(content);
+      }
+    }
+
+    /* Add content */
+    content = static_cast<struct content*>(
+        calloc(1, sizeof(content->node) + hdr_size + log_msg.entry.len));
+    if (!content) {
+      ret = -ENOMEM;
+      break;
+    }
+    memcpy(&content->entry, &log_msg.entry, hdr_size + log_msg.entry.len);
+
+    /* Insert in sequence number sorted order, to ease reconstruction */
+    list_for_each_reverse(node, &names->content) {
+      if ((node_to_item(node, struct content, node))->entry.nsec < log_msg.entry.nsec) {
+        break;
+      }
+    }
+    list_add_head(node, &content->node);
+  }
+  PmsgClose(&logger_list);
+
+  /* Progress through all the collected files */
+  list_for_each_safe(node, n, &name_list) {
+    struct listnode *content_node, *m;
+    char* buf;
+    size_t sequence, tag_len;
+
+    names = node_to_item(node, struct names, node);
+
+    /* Construct content into a linear buffer */
+    buf = NULL;
+    len = 0;
+    sequence = 0;
+    tag_len = strlen(names->name) + sizeof(char); /* tag + nul */
+    list_for_each_safe(content_node, m, &names->content) {
+      ssize_t add_len;
+
+      content = node_to_item(content_node, struct content, node);
+      add_len = content->entry.len - tag_len - sizeof(prio);
+      if (add_len <= 0) {
+        list_remove(content_node);
+        free(content);
+        continue;
+      }
+
+      if (!buf) {
+        buf = static_cast<char*>(malloc(sizeof(char)));
+        if (!buf) {
+          ret = -ENOMEM;
+          list_remove(content_node);
+          free(content);
+          continue;
+        }
+        *buf = '\0';
+      }
+
+      /* Missing sequence numbers */
+      while (sequence < content->entry.nsec) {
+        /* plus space for enforced nul */
+        buf = static_cast<char*>(realloc_or_free(buf, len + sizeof(char) + sizeof(char)));
+        if (!buf) {
+          break;
+        }
+        buf[len] = '\f'; /* Mark missing content with a form feed */
+        buf[++len] = '\0';
+        sequence += ANDROID_LOG_PMSG_FILE_SEQUENCE;
+      }
+      if (!buf) {
+        ret = -ENOMEM;
+        list_remove(content_node);
+        free(content);
+        continue;
+      }
+      /* plus space for enforced nul */
+      buf = static_cast<char*>(realloc_or_free(buf, len + add_len + sizeof(char)));
+      if (!buf) {
+        ret = -ENOMEM;
+        list_remove(content_node);
+        free(content);
+        continue;
+      }
+      memcpy(buf + len, (char*)&content->entry + content->entry.hdr_size + tag_len + sizeof(prio),
+             add_len);
+      len += add_len;
+      buf[len] = '\0'; /* enforce trailing hidden nul */
+      sequence = content->entry.nsec + ANDROID_LOG_PMSG_FILE_SEQUENCE;
+
+      list_remove(content_node);
+      free(content);
+    }
+    if (buf) {
+      if (len) {
+        /* Buffer contains enforced trailing nul just beyond length */
+        ssize_t r;
+        *strchr(names->name, ':') = '/'; /* Convert back to filename */
+        r = (*fn)(names->id, names->prio, names->name, buf, len, arg);
+        if ((ret >= 0) && (r > 0)) {
+          if (ret == SSIZE_MAX) {
+            ret = r;
+          } else {
+            ret += r;
+          }
+        } else if (r < ret) {
+          ret = r;
+        }
+      }
+      free(buf);
+    }
+    list_remove(node);
+    free(names);
+  }
+  return (ret == SSIZE_MAX) ? -ENOENT : ret;
+}
diff --git a/liblog/pmsg_reader.h b/liblog/pmsg_reader.h
new file mode 100644
index 0000000..b784f9f
--- /dev/null
+++ b/liblog/pmsg_reader.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+#include <unistd.h>
+
+#include "log/log_read.h"
+
+__BEGIN_DECLS
+
+int PmsgRead(struct logger_list* logger_list, struct log_msg* log_msg);
+void PmsgClose(struct logger_list* logger_list);
+
+__END_DECLS
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
new file mode 100644
index 0000000..1cf8f96
--- /dev/null
+++ b/liblog/pmsg_writer.cpp
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2007-2016 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 "pmsg_writer.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <log/log_properties.h>
+#include <private/android_logger.h>
+
+#include "logger.h"
+#include "uio.h"
+
+static atomic_int pmsg_fd;
+
+static void GetPmsgFd() {
+  // Note if open() fails and returns -1, that value is stored into pmsg_fd as an indication that
+  // pmsg is not available and open() should not be retried.
+  if (pmsg_fd != 0) {
+    return;
+  }
+
+  int new_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+
+  // Unlikely that new_fd is 0, but that is synonymous with our uninitialized value, and we'd prefer
+  // STDIN_FILENO != stdin, so we call open() to get a new fd value in this case.
+  if (new_fd == 0) {
+    new_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+    close(0);
+  }
+
+  // pmsg_fd should only be opened once.  If we see that pmsg_fd is uninitialized, we open
+  // "/dev/pmsg0" then attempt to compare/exchange it into pmsg_fd.  If the compare/exchange was
+  // successful, then that will be the fd used for the duration of the program, otherwise a
+  // different thread has already opened and written the fd to the atomic, so close the new fd and
+  // return.
+  int uninitialized_value = 0;
+  if (!pmsg_fd.compare_exchange_strong(uninitialized_value, new_fd)) {
+    if (new_fd != -1) {
+      close(new_fd);
+    }
+  }
+}
+
+void PmsgClose() {
+  if (pmsg_fd > 0) {
+    close(pmsg_fd);
+  }
+  pmsg_fd = 0;
+}
+
+int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
+  static const unsigned headerLength = 2;
+  struct iovec newVec[nr + headerLength];
+  android_log_header_t header;
+  android_pmsg_log_header_t pmsgHeader;
+  size_t i, payloadSize;
+  ssize_t ret;
+
+  if (!ANDROID_DEBUGGABLE) {
+    if (logId != LOG_ID_EVENTS && logId != LOG_ID_SECURITY) {
+      return -1;
+    }
+
+    if (logId == LOG_ID_EVENTS) {
+      if (vec[0].iov_len < 4) {
+        return -EINVAL;
+      }
+
+      if (SNET_EVENT_LOG_TAG != *static_cast<uint32_t*>(vec[0].iov_base)) {
+        return -EPERM;
+      }
+    }
+  }
+
+  GetPmsgFd();
+
+  if (pmsg_fd <= 0) {
+    return -EBADF;
+  }
+
+  /*
+   *  struct {
+   *      // what we provide to pstore
+   *      android_pmsg_log_header_t pmsgHeader;
+   *      // what we provide to file
+   *      android_log_header_t header;
+   *      // caller provides
+   *      union {
+   *          struct {
+   *              char     prio;
+   *              char     payload[];
+   *          } string;
+   *          struct {
+   *              uint32_t tag
+   *              char     payload[];
+   *          } binary;
+   *      };
+   *  };
+   */
+
+  pmsgHeader.magic = LOGGER_MAGIC;
+  pmsgHeader.len = sizeof(pmsgHeader) + sizeof(header);
+  pmsgHeader.uid = getuid();
+  pmsgHeader.pid = getpid();
+
+  header.id = logId;
+  header.tid = gettid();
+  header.realtime.tv_sec = ts->tv_sec;
+  header.realtime.tv_nsec = ts->tv_nsec;
+
+  newVec[0].iov_base = (unsigned char*)&pmsgHeader;
+  newVec[0].iov_len = sizeof(pmsgHeader);
+  newVec[1].iov_base = (unsigned char*)&header;
+  newVec[1].iov_len = sizeof(header);
+
+  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;
+      }
+      payloadSize = LOGGER_ENTRY_MAX_PAYLOAD;
+      break;
+    }
+  }
+  pmsgHeader.len += payloadSize;
+
+  ret = TEMP_FAILURE_RETRY(writev(pmsg_fd, newVec, i));
+  if (ret < 0) {
+    ret = errno ? -errno : -ENOTCONN;
+  }
+
+  if (ret > (ssize_t)(sizeof(header) + sizeof(pmsgHeader))) {
+    ret -= sizeof(header) - sizeof(pmsgHeader);
+  }
+
+  return ret;
+}
+
+/*
+ * Virtual pmsg filesystem
+ *
+ * Payload will comprise the string "<basedir>:<basefile>\0<content>" to a
+ * maximum of LOGGER_ENTRY_MAX_PAYLOAD, but scaled to the last newline in the
+ * file.
+ *
+ * Will hijack the header.realtime.tv_nsec field for a sequence number in usec.
+ */
+
+static inline const char* strnrchr(const char* buf, size_t len, char c) {
+  const char* cp = buf + len;
+  while ((--cp > buf) && (*cp != c))
+    ;
+  if (cp <= buf) {
+    return buf + len;
+  }
+  return cp;
+}
+
+/* Write a buffer as filename references (tag = <basedir>:<basename>) */
+ssize_t __android_log_pmsg_file_write(log_id_t logId, char prio, const char* filename,
+                                      const char* buf, size_t len) {
+  size_t length, packet_len;
+  const char* tag;
+  char *cp, *slash;
+  struct timespec ts;
+  struct iovec vec[3];
+
+  /* Make sure the logId value is not a bad idea */
+  if ((logId == LOG_ID_KERNEL) ||   /* Verbotten */
+      (logId == LOG_ID_EVENTS) ||   /* Do not support binary content */
+      (logId == LOG_ID_SECURITY) || /* Bad idea to allow */
+      ((unsigned)logId >= 32)) {    /* fit within logMask on arch32 */
+    return -EINVAL;
+  }
+
+  clock_gettime(CLOCK_REALTIME, &ts);
+
+  cp = strdup(filename);
+  if (!cp) {
+    return -ENOMEM;
+  }
+
+  tag = cp;
+  slash = strrchr(cp, '/');
+  if (slash) {
+    *slash = ':';
+    slash = strrchr(cp, '/');
+    if (slash) {
+      tag = slash + 1;
+    }
+  }
+
+  length = strlen(tag) + 1;
+  packet_len = LOGGER_ENTRY_MAX_PAYLOAD - sizeof(char) - length;
+
+  vec[0].iov_base = &prio;
+  vec[0].iov_len = sizeof(char);
+  vec[1].iov_base = (unsigned char*)tag;
+  vec[1].iov_len = length;
+
+  for (ts.tv_nsec = 0, length = len; length; ts.tv_nsec += ANDROID_LOG_PMSG_FILE_SEQUENCE) {
+    ssize_t ret;
+    size_t transfer;
+
+    if ((ts.tv_nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >= ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
+      len -= length;
+      break;
+    }
+
+    transfer = length;
+    if (transfer > packet_len) {
+      transfer = strnrchr(buf, packet_len - 1, '\n') - buf;
+      if ((transfer < length) && (buf[transfer] == '\n')) {
+        ++transfer;
+      }
+    }
+
+    vec[2].iov_base = (unsigned char*)buf;
+    vec[2].iov_len = transfer;
+
+    ret = PmsgWrite(logId, &ts, vec, sizeof(vec) / sizeof(vec[0]));
+
+    if (ret <= 0) {
+      free(cp);
+      return ret ? ret : (len - length);
+    }
+    length -= transfer;
+    buf += transfer;
+  }
+  free(cp);
+  return len;
+}
diff --git a/liblog/pmsg_writer.h b/liblog/pmsg_writer.h
new file mode 100644
index 0000000..d5e1a1c
--- /dev/null
+++ b/liblog/pmsg_writer.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 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 <stddef.h>
+
+#include <android/log.h>
+
+int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+void PmsgClose();
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
new file mode 100644
index 0000000..bd5f5e7
--- /dev/null
+++ b/liblog/properties.cpp
@@ -0,0 +1,353 @@
+/*
+** Copyright 2014, 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 <log/log_properties.h>
+
+#include <ctype.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include <android-base/macros.h>
+
+#include <private/android_logger.h>
+
+#include "logger_write.h"
+
+#ifdef __ANDROID__
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
+
+static bool trylock() {
+  /*
+   * 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.
+   */
+  /*
+   *  Any contention, and we can turn around and use the non-cached method
+   * in less time than the system call associated with a mutex to deal with
+   * the contention.
+   */
+  return pthread_mutex_trylock(&lock_loggable) == 0;
+}
+
+static void unlock() {
+  pthread_mutex_unlock(&lock_loggable);
+}
+
+struct cache {
+  const prop_info* pinfo;
+  uint32_t serial;
+};
+
+struct cache_char {
+  struct cache cache;
+  unsigned char c;
+};
+
+static int check_cache(struct cache* cache) {
+  return cache->pinfo && __system_property_serial(cache->pinfo) != cache->serial;
+}
+
+#define BOOLEAN_TRUE 0xFF
+#define BOOLEAN_FALSE 0xFE
+
+static void refresh_cache(struct cache_char* cache, const char* key) {
+  char buf[PROP_VALUE_MAX];
+
+  if (!cache->cache.pinfo) {
+    cache->cache.pinfo = __system_property_find(key);
+    if (!cache->cache.pinfo) {
+      return;
+    }
+  }
+  cache->cache.serial = __system_property_serial(cache->cache.pinfo);
+  __system_property_read(cache->cache.pinfo, 0, buf);
+  switch (buf[0]) {
+    case 't':
+    case 'T':
+      cache->c = strcasecmp(buf + 1, "rue") ? buf[0] : BOOLEAN_TRUE;
+      break;
+    case 'f':
+    case 'F':
+      cache->c = strcasecmp(buf + 1, "alse") ? buf[0] : BOOLEAN_FALSE;
+      break;
+    default:
+      cache->c = buf[0];
+  }
+}
+
+static int __android_log_level(const char* tag, size_t tag_len) {
+  if (tag == nullptr || tag_len == 0) {
+    auto& tag_string = GetDefaultTag();
+    tag = tag_string.c_str();
+    tag_len = tag_string.size();
+  }
+
+  /*
+   * Single layer cache of four properties. Priorities are:
+   *    log.tag.<tag>
+   *    persist.log.tag.<tag>
+   *    log.tag
+   *    persist.log.tag
+   * Where the missing tag matches all tags and becomes the
+   * system global default. We do not support ro.log.tag* .
+   */
+  static std::string* last_tag = new std::string;
+  static uint32_t global_serial;
+  uint32_t current_global_serial;
+  static cache_char tag_cache[2];
+  static cache_char global_cache[2];
+
+  static const char* log_namespace = "persist.log.tag.";
+  char key[strlen(log_namespace) + tag_len + 1];
+  strcpy(key, log_namespace);
+
+  bool locked = trylock();
+  bool change_detected, global_change_detected;
+  global_change_detected = change_detected = !locked;
+
+  char c = 0;
+  if (locked) {
+    // Check all known serial numbers for changes.
+    for (size_t i = 0; i < arraysize(tag_cache); ++i) {
+      if (check_cache(&tag_cache[i].cache)) {
+        change_detected = true;
+      }
+    }
+    for (size_t i = 0; i < arraysize(global_cache); ++i) {
+      if (check_cache(&global_cache[i].cache)) {
+        global_change_detected = true;
+      }
+    }
+
+    current_global_serial = __system_property_area_serial();
+    if (current_global_serial != global_serial) {
+      global_change_detected = change_detected = true;
+    }
+  }
+
+  if (tag_len != 0) {
+    bool local_change_detected = change_detected;
+    if (locked) {
+      // compare() rather than == because tag isn't guaranteed 0-terminated.
+      if (last_tag->compare(0, last_tag->size(), tag, tag_len) != 0) {
+        // Invalidate log.tag.<tag> cache.
+        for (size_t i = 0; i < arraysize(tag_cache); ++i) {
+          tag_cache[i].cache.pinfo = NULL;
+          tag_cache[i].c = '\0';
+        }
+        last_tag->assign(tag, tag_len);
+        local_change_detected = true;
+      }
+    }
+    *stpncpy(key + strlen(log_namespace), tag, tag_len) = '\0';
+
+    for (size_t i = 0; i < arraysize(tag_cache); ++i) {
+      cache_char* cache = &tag_cache[i];
+      cache_char temp_cache;
+
+      if (!locked) {
+        temp_cache.cache.pinfo = NULL;
+        temp_cache.c = '\0';
+        cache = &temp_cache;
+      }
+      if (local_change_detected) {
+        refresh_cache(cache, i == 0 ? key : key + strlen("persist."));
+      }
+
+      if (cache->c) {
+        c = cache->c;
+        break;
+      }
+    }
+  }
+
+  switch (toupper(c)) { /* if invalid, resort to global */
+    case 'V':
+    case 'D':
+    case 'I':
+    case 'W':
+    case 'E':
+    case 'F': /* Not officially supported */
+    case 'A':
+    case 'S':
+    case BOOLEAN_FALSE: /* Not officially supported */
+      break;
+    default:
+      /* clear '.' after log.tag */
+      key[strlen(log_namespace) - 1] = '\0';
+
+      for (size_t i = 0; i < arraysize(global_cache); ++i) {
+        cache_char* cache = &global_cache[i];
+        cache_char temp_cache;
+
+        if (!locked) {
+          temp_cache = *cache;
+          if (temp_cache.cache.pinfo != cache->cache.pinfo) {  // check atomic
+            temp_cache.cache.pinfo = NULL;
+            temp_cache.c = '\0';
+          }
+          cache = &temp_cache;
+        }
+        if (global_change_detected) {
+          refresh_cache(cache, i == 0 ? key : key + strlen("persist."));
+        }
+
+        if (cache->c) {
+          c = cache->c;
+          break;
+        }
+      }
+      break;
+  }
+
+  if (locked) {
+    global_serial = current_global_serial;
+    unlock();
+  }
+
+  switch (toupper(c)) {
+    /* clang-format off */
+    case 'V': return ANDROID_LOG_VERBOSE;
+    case 'D': return ANDROID_LOG_DEBUG;
+    case 'I': return ANDROID_LOG_INFO;
+    case 'W': return ANDROID_LOG_WARN;
+    case 'E': return ANDROID_LOG_ERROR;
+    case 'F': /* FALLTHRU */ /* Not officially supported */
+    case 'A': return ANDROID_LOG_FATAL;
+    case BOOLEAN_FALSE: /* FALLTHRU */ /* Not Officially supported */
+    case 'S': return ANDROID_LOG_SILENT;
+      /* clang-format on */
+  }
+  return -1;
+}
+
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio) {
+  int minimum_log_priority = __android_log_get_minimum_priority();
+  int property_log_level = __android_log_level(tag, len);
+
+  if (property_log_level >= 0 && minimum_log_priority != ANDROID_LOG_DEFAULT) {
+    return prio >= std::min(property_log_level, minimum_log_priority);
+  } else if (property_log_level >= 0) {
+    return prio >= property_log_level;
+  } else if (minimum_log_priority != ANDROID_LOG_DEFAULT) {
+    return prio >= minimum_log_priority;
+  } else {
+    return prio >= default_prio;
+  }
+}
+
+int __android_log_is_loggable(int prio, const char* tag, int default_prio) {
+  auto len = tag ? strlen(tag) : 0;
+  return __android_log_is_loggable_len(prio, tag, len, default_prio);
+}
+
+int __android_log_is_debuggable() {
+  static int is_debuggable = [] {
+    char value[PROP_VALUE_MAX] = {};
+    return __system_property_get("ro.debuggable", value) > 0 && !strcmp(value, "1");
+  }();
+
+  return is_debuggable;
+}
+
+/*
+ * For properties that are read often, but generally remain constant.
+ * Since a change is rare, we will accept a trylock failure gracefully.
+ * Use a separate lock from is_loggable to keep contention down b/25563384.
+ */
+struct cache2_char {
+  pthread_mutex_t lock;
+  uint32_t serial;
+  const char* key_persist;
+  struct cache_char cache_persist;
+  const char* key_ro;
+  struct cache_char cache_ro;
+  unsigned char (*const evaluate)(const struct cache2_char* self);
+};
+
+static inline unsigned char do_cache2_char(struct cache2_char* self) {
+  uint32_t current_serial;
+  int change_detected;
+  unsigned char c;
+
+  if (pthread_mutex_trylock(&self->lock)) {
+    /* We are willing to accept some race in this context */
+    return self->evaluate(self);
+  }
+
+  change_detected = check_cache(&self->cache_persist.cache) || check_cache(&self->cache_ro.cache);
+  current_serial = __system_property_area_serial();
+  if (current_serial != self->serial) {
+    change_detected = 1;
+  }
+  if (change_detected) {
+    refresh_cache(&self->cache_persist, self->key_persist);
+    refresh_cache(&self->cache_ro, self->key_ro);
+    self->serial = current_serial;
+  }
+  c = self->evaluate(self);
+
+  pthread_mutex_unlock(&self->lock);
+
+  return c;
+}
+
+/*
+ * Security state generally remains constant, but the DO must be able
+ * to turn off logging should it become spammy after an attack is detected.
+ */
+static unsigned char evaluate_security(const struct cache2_char* self) {
+  unsigned char c = self->cache_ro.c;
+
+  return (c != BOOLEAN_FALSE) && c && (self->cache_persist.c == BOOLEAN_TRUE);
+}
+
+int __android_log_security() {
+  static struct cache2_char security = {
+      PTHREAD_MUTEX_INITIALIZER, 0,
+      "persist.logd.security",   {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
+      "ro.organization_owned",   {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
+      evaluate_security};
+
+  return do_cache2_char(&security);
+}
+
+#else
+
+int __android_log_is_loggable(int prio, const char*, int) {
+  int minimum_priority = __android_log_get_minimum_priority();
+  if (minimum_priority == ANDROID_LOG_DEFAULT) {
+    minimum_priority = ANDROID_LOG_INFO;
+  }
+  return prio >= minimum_priority;
+}
+
+int __android_log_is_loggable_len(int prio, const char*, size_t, int def) {
+  return __android_log_is_loggable(prio, nullptr, def);
+}
+
+int __android_log_is_debuggable() {
+  return 1;
+}
+
+#endif
diff --git a/liblog/rust/liblog_wrapper.h b/liblog/rust/liblog_wrapper.h
new file mode 100644
index 0000000..e6680fe
--- /dev/null
+++ b/liblog/rust/liblog_wrapper.h
@@ -0,0 +1,4 @@
+#pragma once
+
+#include <log/log_event_list.h>
+#include <private/android_logger.h>
diff --git a/liblog/uio.h b/liblog/uio.h
new file mode 100644
index 0000000..c85893c
--- /dev/null
+++ b/liblog/uio.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#if defined(_WIN32)
+#include <stddef.h>
+struct iovec {
+  void* iov_base;
+  size_t iov_len;
+};
+#else
+#include <sys/uio.h>
+#endif