Merge "bootstat: Log bootloader timing metrics to Tron."
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 032ae31..9069baa 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -302,13 +302,6 @@
 
     // So, we have a device, finally. Grab its vitals.
 
-
-    kr = (*dev)->USBDeviceOpen(dev);
-    if (kr != 0) {
-        WARN("USBDeviceOpen");
-        goto out;
-    }
-
     kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor);
     if (kr != 0) {
         ERR("GetDeviceVendor");
@@ -381,16 +374,12 @@
         goto error;
     }
 
-    out:
-
-    (*dev)->USBDeviceClose(dev);
     (*dev)->Release(dev);
     return 0;
 
     error:
 
     if (dev != NULL) {
-        (*dev)->USBDeviceClose(dev);
         (*dev)->Release(dev);
     }
 
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index c53ea2c..1f08eb4 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -19,6 +19,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -26,15 +27,63 @@
 
 #include <experimental/string_view>
 #include <functional>
+#include <string>
 #include <unordered_map>
 
 #include <log/event_tag_map.h>
+#include <utils/FastStrcmp.h>
+#include <utils/RWLock.h>
+#include <private/android_logger.h>
 
 #include "log_portability.h"
+#include "logd_reader.h"
 
 #define OUT_TAG "EventTagMap"
 
-typedef std::experimental::string_view MapString;
+class MapString {
+private:
+    const std::string* alloc; // HAS-AN
+    const std::experimental::string_view str; // HAS-A
+
+public:
+    operator const std::experimental::string_view() const { return str; }
+
+    const char* data() const { return str.data(); }
+    size_t length() const { return str.length(); }
+
+    bool operator== (const MapString& rval) const {
+        if (length() != rval.length()) return false;
+        if (length() == 0) return true;
+        return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
+    }
+    bool operator!= (const MapString& rval) const {
+        return !(*this == rval);
+    }
+
+    MapString(const char* str, size_t len) : alloc(NULL), str(str, len) { }
+    explicit MapString(const std::string& str) :
+            alloc(new std::string(str)),
+            str(alloc->data(), alloc->length()) { }
+    MapString(MapString &&rval) :
+            alloc(rval.alloc),
+            str(rval.data(), rval.length()) {
+        rval.alloc = NULL;
+    }
+    explicit MapString(const MapString &rval) :
+            alloc(rval.alloc ? new std::string(*rval.alloc) : NULL),
+            str(alloc ? alloc->data() : rval.data(), rval.length()) { }
+
+    ~MapString() { if (alloc) delete alloc; }
+};
+
+// Hash for MapString
+template <> struct std::hash<MapString>
+        : public std::unary_function<const MapString&, size_t> {
+    size_t operator()(const MapString& __t) const noexcept {
+        if (!__t.length()) return 0;
+        return std::hash<std::experimental::string_view>()(std::experimental::string_view(__t));
+    }
+};
 
 typedef std::pair<MapString, MapString> TagFmt;
 
@@ -53,57 +102,125 @@
 
 // Map
 struct EventTagMap {
+#   define NUM_MAPS 2
     // memory-mapped source file; we get strings from here
-    void*  mapAddr;
-    size_t mapLen;
+    void*  mapAddr[NUM_MAPS];
+    size_t mapLen[NUM_MAPS];
 
 private:
     std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;
+    std::unordered_map<TagFmt, uint32_t> TagFmt2Idx;
+    std::unordered_map<MapString, uint32_t> Tag2Idx;
+    // protect unordered sets
+    android::RWLock rwlock;
 
 public:
-    EventTagMap() : mapAddr(NULL), mapLen(0) { }
+    EventTagMap() {
+        memset(mapAddr, 0, sizeof(mapAddr));
+        memset(mapLen, 0, sizeof(mapLen));
+    }
 
     ~EventTagMap() {
         Idx2TagFmt.clear();
-        if (mapAddr) {
-            munmap(mapAddr, mapLen);
-            mapAddr = 0;
+        TagFmt2Idx.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(TagFmt&& tagfmt) const;
+    int find(MapString&& tag) const;
 };
 
 bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose) {
-    std::unordered_map<uint32_t, TagFmt>::const_iterator it;
-    it = Idx2TagFmt.find(tag);
-    if (it != Idx2TagFmt.end()) {
-        if (verbose) {
-            fprintf(stderr,
-                    OUT_TAG ": duplicate tag entries %" PRIu32
-                        ":%.*s:%.*s and %" PRIu32 ":%.*s:%.*s)\n",
-                    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());
+    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);
+    {
+        std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+        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));
         }
-        return false;
     }
 
-    Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
-    return true;
+    {
+        std::unordered_map<TagFmt, uint32_t>::const_iterator it;
+        it = TagFmt2Idx.find(tagfmt);
+        if (it != TagFmt2Idx.end()) {
+            if (verbose) {
+                fprintf(stderr, errorFormat,
+                        it->second,
+                        (int)it->first.first.length(), it->first.first.data(),
+                        (int)it->first.second.length(), it->first.second.data(),
+                        tag,
+                        (int)tagfmt.first.length(), tagfmt.first.data(),
+                        (int)tagfmt.second.length(), tagfmt.second.data());
+            }
+            ret = false;
+        } else {
+            TagFmt2Idx.emplace(std::make_pair(tagfmt, tag));
+        }
+    }
+
+    {
+        std::unordered_map<MapString, uint32_t>::const_iterator it;
+        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 {
     std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+    android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
     it = Idx2TagFmt.find(tag);
     if (it == Idx2TagFmt.end()) return NULL;
     return &(it->second);
 }
 
+int EventTagMap::find(TagFmt&& tagfmt) const {
+    std::unordered_map<TagFmt, uint32_t>::const_iterator it;
+    android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+    it = TagFmt2Idx.find(std::move(tagfmt));
+    if (it == TagFmt2Idx.end()) return -1;
+    return it->second;
+}
+
+int EventTagMap::find(MapString&& tag) const {
+    std::unordered_map<MapString, uint32_t>::const_iterator it;
+    android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+    it = Tag2Idx.find(std::move(tag));
+    if (it == Tag2Idx.end()) return -1;
+    return it->second;
+}
+
 // Scan one tag line.
 //
 // "*pData" should be pointing to the first digit in the tag number.  On
@@ -157,6 +274,19 @@
         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 != '\n')) ++cp;
+    if (*cp == '#') {
+        do {
+            ++cp;
+        } while (isspace(*cp) && (*cp != '\n'));
+        verbose = !!fastcmp<strncmp>(cp, "uid=", strlen("uid="));
+    }
+
     while (*cp != '\n') ++cp;
 #ifdef DEBUG
     fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - *pData), *pData);
@@ -164,24 +294,33 @@
     *pData = cp;
 
     if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
-            MapString(tag, tagLen), MapString(fmt, fmtLen))), true)) {
+            MapString(tag, tagLen), MapString(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) {
-    char* cp = static_cast<char*>(map->mapAddr);
-    size_t len = map->mapLen;
+static int parseMapLines(EventTagMap* map, size_t which) {
+    char* cp = static_cast<char*>(map->mapAddr[which]);
+    size_t len = map->mapLen[which];
     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 missing EOL on last line\n");
+        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;
     }
@@ -199,7 +338,9 @@
             } else if (isdigit(*cp)) {
                 // looks like a tag; scan it out
                 if (scanTagLine(map, &cp, lineNum) != 0) {
-                    return -1;
+                    if (!which || (errno != EMLINK)) {
+                        return -1;
+                    }
                 }
                 lineNum++;      // we eat the '\n'
                 // leave lineStart==true
@@ -226,57 +367,87 @@
 // We create a private mapping because we want to terminate the log tag
 // strings with '\0'.
 LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName) {
-    int save_errno;
+    EventTagMap* newTagMap;
+    off_t end[NUM_MAPS];
+    int save_errno, fd[NUM_MAPS];
+    size_t which;
 
-    const char* tagfile = fileName ? fileName : EVENT_TAG_MAP_FILE;
-    int fd = open(tagfile, O_RDONLY | O_CLOEXEC);
-    if (fd < 0) {
+    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;
-        fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n",
-                tagfile, strerror(save_errno));
-        errno = save_errno;
-        return NULL;
-    }
-    off_t end = lseek(fd, 0L, SEEK_END);
-    save_errno = errno;
-    (void)lseek(fd, 0L, SEEK_SET);
-    if (end < 0) {
-        fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n",
-                tagfile, strerror(save_errno));
-        close(fd);
-        errno = save_errno;
-        return NULL;
+        (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
     }
 
-    EventTagMap* newTagMap = new EventTagMap;
+    newTagMap = new EventTagMap;
     if (newTagMap == NULL) {
         save_errno = errno;
-        close(fd);
-        errno = save_errno;
-        return NULL;
+        goto fail_close;
     }
 
-    newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE,
-                              MAP_PRIVATE, fd, 0);
-    save_errno = errno;
-    close(fd);
-    fd = -1;
-    if ((newTagMap->mapAddr == MAP_FAILED) || (newTagMap->mapAddr == NULL)) {
-        fprintf(stderr, OUT_TAG ": mmap(%s) failed: %s\n",
-                tagfile, strerror(save_errno));
-        delete newTagMap;
-        errno = save_errno;
-        return NULL;
+    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[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;
+            }
+        }
     }
 
-    newTagMap->mapLen = end;
-
-    if (parseMapLines(newTagMap) != 0) {
-        delete newTagMap;
-        return NULL;
+    for (which = 0; which < NUM_MAPS; ++which) {
+        if (parseMapLines(newTagMap, which) != 0) {
+            delete newTagMap;
+            return NULL;
+        }
     }
 
     return newTagMap;
+
+fail_unmap:
+    save_errno = EINVAL;
+    delete newTagMap;
+fail_close:
+    for (which = 0; which < NUM_MAPS; ++which) close(fd[which]);
+fail_errno:
+    errno = save_errno;
+    return NULL;
 }
 
 // Close the map.
@@ -320,3 +491,75 @@
     if (*cp) *cp = '\0'; // Trigger copy on write :-( and why deprecated.
     return tagStr;
 }
+
+// Look up tagname, generate one if necessary, and return a tag
+LIBLOG_ABI_PUBLIC int android_lookupEventTagNum(EventTagMap* map,
+                                                const char* tagname,
+                                                const char* format,
+                                                int prio) {
+    size_t len = strlen(tagname);
+    if (!len) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    if ((prio != ANDROID_LOG_UNKNOWN) && (prio < ANDROID_LOG_SILENT) &&
+            !__android_log_is_loggable_len(prio, tagname, len,
+                                           __android_log_is_debuggable() ?
+                                             ANDROID_LOG_VERBOSE :
+                                             ANDROID_LOG_DEBUG)) {
+        errno = EPERM;
+        return -1;
+    }
+
+    if (!format) format="";
+    ssize_t fmtLen = strlen(format);
+    int ret = map->find(TagFmt(std::make_pair(MapString(tagname, len),
+                                              MapString(format, fmtLen))));
+    if (ret != -1) return ret;
+
+    // call event tag service to arrange for a new tag
+    char *buf = NULL;
+    // Can not use android::base::StringPrintf, asprintf + free instead.
+    static const char command_template[] = "getEventTag name=%s format=\"%s\"";
+    ret = asprintf(&buf, command_template, tagname, format);
+    if (ret > 0) {
+        // Add some buffer margin for an estimate of the full return content.
+        char *cp;
+        size_t size = ret - strlen(command_template) +
+            strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
+        if (size > (size_t)ret) {
+            cp = static_cast<char*>(realloc(buf, size));
+            if (cp) {
+                buf = cp;
+            } else {
+                size = ret;
+            }
+        } else {
+            size = ret;
+        }
+        // Ask event log tag service for an allocation
+        if (__send_log_msg(buf, size) >= 0) {
+            buf[size - 1] = '\0';
+            unsigned long val = strtoul(buf, &cp, 10); // return size
+            if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
+                val = strtoul(cp + 1, &cp, 10); // allocated tag number
+                if ((val > 0) && (val < UINT32_MAX) && (*cp == '\t')) {
+                    free(buf);
+                    ret = val;
+                    // cache
+                    map->emplaceUnique(ret, TagFmt(std::make_pair(
+                            MapString(std::string(tagname, len)),
+                            MapString(std::string(format, fmtLen)))));
+                    return ret;
+                }
+            }
+        }
+        free(buf);
+    }
+
+    // Hail Mary
+    ret = map->find(MapString(tagname, len));
+    if (ret == -1) errno = ESRCH;
+    return ret;
+}
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index 4939221..957129e 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -715,6 +715,12 @@
     return redirectWritev(fd, vector, count);
 }
 
+LIBLOG_HIDDEN ssize_t __send_log_msg(char *buf __unused,
+                                     size_t buf_size __unused)
+{
+    return -ENODEV;
+}
+
 LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio,
                                                 const char *tag __unused,
                                                 int def)
diff --git a/liblog/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
index 22e62ec..e57e47b 100644
--- a/liblog/include/log/event_tag_map.h
+++ b/liblog/include/log/event_tag_map.h
@@ -58,6 +58,12 @@
 const char* android_lookupEventFormat_len(const EventTagMap* map,
                                           size_t* len, unsigned int tag);
 
+/*
+ * Look up tagname, generate one if necessary, and return a tag
+ */
+int android_lookupEventTagNum(EventTagMap* map, const char* tagname,
+                              const char* format, int prio);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.c
index 99d7fea..ccc7da8 100644
--- a/liblog/logd_reader.c
+++ b/liblog/logd_reader.c
@@ -37,6 +37,7 @@
 
 #include "config_read.h"
 #include "log_portability.h"
+#include "logd_reader.h"
 #include "logger.h"
 
 /* branchless on many architectures. */
@@ -324,6 +325,11 @@
     return ret;
 }
 
+LIBLOG_HIDDEN ssize_t __send_log_msg(char *buf, size_t buf_size)
+{
+    return send_log_msg(NULL, NULL, buf, buf_size);
+}
+
 static int check_log_success(char *buf, ssize_t ret)
 {
     if (ret < 0) {
diff --git a/liblog/logd_reader.h b/liblog/logd_reader.h
new file mode 100644
index 0000000..04c2cf2
--- /dev/null
+++ b/liblog/logd_reader.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBLOG_LOGD_READER_H__
+#define _LIBLOG_LOGD_READER_H__
+
+#include <unistd.h>
+
+#include "log_portability.h"
+
+__BEGIN_DECLS
+
+LIBLOG_HIDDEN ssize_t __send_log_msg(char *buf, size_t buf_size);
+
+__END_DECLS
+
+#endif /* _LIBLOG_LOGD_READER_H__ */
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 5420f68..dc411c3 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -15,6 +15,8 @@
  */
 
 #include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
 #include <sys/endian.h>
 #include <sys/socket.h>
 #include <sys/types.h>
@@ -22,6 +24,7 @@
 
 #include <unordered_set>
 
+#include <android-base/file.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
 #include <private/android_logger.h>
@@ -695,7 +698,7 @@
 
 // Keep maps around for multiple iterations
 static std::unordered_set<uint32_t> set;
-static const EventTagMap* map;
+static EventTagMap* map;
 
 static bool prechargeEventMap() {
     if (map) return true;
@@ -785,3 +788,142 @@
     StopBenchmarkTiming();
 }
 BENCHMARK(BM_lookupEventFormat);
+
+/*
+ *	Measure the time it takes for android_lookupEventTagNum plus above
+ */
+static void BM_lookupEventTagNum(int iters) {
+
+    prechargeEventMap();
+
+    std::unordered_set<uint32_t>::const_iterator it = set.begin();
+
+    for (int i = 0; i < iters; ++i) {
+        size_t len;
+        const char* name = android_lookupEventTag_len(map, &len, (*it));
+        std::string Name(name, len);
+        const char* format = android_lookupEventFormat_len(map, &len, (*it));
+        std::string Format(format, len);
+        StartBenchmarkTiming();
+        android_lookupEventTagNum(map, Name.c_str(), Format.c_str(),
+                                  ANDROID_LOG_UNKNOWN);
+        StopBenchmarkTiming();
+        ++it;
+        if (it == set.end()) it = set.begin();
+    }
+
+}
+BENCHMARK(BM_lookupEventTagNum);
+
+// Must be functionally identical to liblog internal __send_log_msg.
+static void send_to_control(char *buf, size_t len)
+{
+    int sock = socket_local_client("logd",
+                                   ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                   SOCK_STREAM);
+    if (sock < 0) return;
+    size_t writeLen = strlen(buf) + 1;
+
+    ssize_t ret = TEMP_FAILURE_RETRY(write(sock, buf, writeLen));
+    if (ret <= 0) {
+        close(sock);
+        return;
+    }
+    while ((ret = read(sock, buf, len)) > 0) {
+        if (((size_t)ret == len) || (len < PAGE_SIZE)) {
+            break;
+        }
+        len -= ret;
+        buf += ret;
+
+        struct pollfd p = {
+            .fd = sock,
+            .events = POLLIN,
+            .revents = 0
+        };
+
+        ret = poll(&p, 1, 20);
+        if ((ret <= 0) || !(p.revents & POLLIN)) {
+            break;
+        }
+    }
+    close(sock);
+}
+
+static void BM_lookupEventTagNum_logd_new(int iters) {
+    fprintf(stderr, "WARNING: "
+            "This test can cause logd to grow in size and hit DOS limiter\n");
+    // Make copies
+    static const char empty_event_log_tags[] = "# content owned by logd\n";
+    static const char dev_event_log_tags_path[] = "/dev/event-log-tags";
+    std::string dev_event_log_tags;
+    if (android::base::ReadFileToString(dev_event_log_tags_path,
+                                        &dev_event_log_tags) &&
+            (dev_event_log_tags.length() == 0)) {
+        dev_event_log_tags = empty_event_log_tags;
+    }
+    static const char data_event_log_tags_path[] = "/data/misc/logd/event-log-tags";
+    std::string data_event_log_tags;
+    if (android::base::ReadFileToString(data_event_log_tags_path,
+                                        &data_event_log_tags) &&
+            (data_event_log_tags.length() == 0)) {
+        data_event_log_tags = empty_event_log_tags;
+    }
+
+    for (int i = 0; i < iters; ++i) {
+        char buffer[256];
+        memset(buffer, 0, sizeof(buffer));
+        log_time now(CLOCK_MONOTONIC);
+        char name[64];
+        snprintf(name, sizeof(name), "a%" PRIu64, now.nsec());
+        snprintf(buffer, sizeof(buffer),
+                 "getEventTag name=%s format=\"(new|1)\"", name);
+        StartBenchmarkTiming();
+        send_to_control(buffer, sizeof(buffer));
+        StopBenchmarkTiming();
+    }
+
+    // Restore copies (logd still know about them, until crash or reboot)
+    if (dev_event_log_tags.length() &&
+            !android::base::WriteStringToFile(dev_event_log_tags,
+                                              dev_event_log_tags_path)) {
+        fprintf(stderr, "WARNING: "
+                "failed to restore %s\n", dev_event_log_tags_path);
+    }
+    if (data_event_log_tags.length() &&
+            !android::base::WriteStringToFile(data_event_log_tags,
+                                              data_event_log_tags_path)) {
+        fprintf(stderr, "WARNING: "
+                "failed to restore %s\n", data_event_log_tags_path);
+    }
+    fprintf(stderr, "WARNING: "
+            "Restarting logd to make it forget what we just did\n");
+    system("stop logd ; start logd");
+}
+BENCHMARK(BM_lookupEventTagNum_logd_new);
+
+static void BM_lookupEventTagNum_logd_existing(int iters) {
+    prechargeEventMap();
+
+    std::unordered_set<uint32_t>::const_iterator it = set.begin();
+
+    for (int i = 0; i < iters; ++i) {
+        size_t len;
+        const char* name = android_lookupEventTag_len(map, &len, (*it));
+        std::string Name(name, len);
+        const char* format = android_lookupEventFormat_len(map, &len, (*it));
+        std::string Format(format, len);
+
+        char buffer[256];
+        snprintf(buffer, sizeof(buffer),
+                 "getEventTag name=%s format=\"%s\"",
+                 Name.c_str(), Format.c_str());
+
+        StartBenchmarkTiming();
+        send_to_control(buffer, sizeof(buffer));
+        StopBenchmarkTiming();
+        ++it;
+        if (it == set.end()) it = set.begin();
+    }
+}
+BENCHMARK(BM_lookupEventTagNum_logd_existing);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 8b9f0f0..5faf8e1 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -2929,3 +2929,19 @@
     }
     // Do not test default seconds, to allow liblog to tune freely
 }
+
+TEST(liblog, android_lookupEventTagNum) {
+#ifdef __ANDROID__
+    EventTagMap* map = android_openEventTagMap(NULL);
+    EXPECT_TRUE(NULL != map);
+    std::string Name = android::base::StringPrintf("a%d", getpid());
+    int tag = android_lookupEventTagNum(map, Name.c_str(), "(new|1)", ANDROID_LOG_UNKNOWN);
+    android_closeEventTagMap(map);
+    if (tag == -1) system("tail -3 /dev/event-log-tags >&2");
+    EXPECT_NE(-1, tag);
+    EXPECT_NE(0, tag);
+    EXPECT_GT(UINT32_MAX, (unsigned)tag);
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/logd/Android.mk b/logd/Android.mk
index 2da9782..9211037 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -21,6 +21,7 @@
     libaudit.c \
     LogAudit.cpp \
     LogKlog.cpp \
+    LogTags.cpp \
     event.logtags
 
 LOCAL_SHARED_LIBRARIES := \
@@ -38,12 +39,23 @@
 #        $(LOCAL_PATH)/$2/event.logtags)
 #  event_flag := $(call event_logtags,auditd)
 #  event_flag += $(call event_logtags,logd)
+#  event_flag += $(call event_logtags,tag_def)
 # so make sure we do not regret hard-coding it as follows:
-event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004
+event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004 -DTAG_DEF_LOG_TAG=1005
 event_flag += -DLIBLOG_LOG_TAG=1006
 
 LOCAL_CFLAGS := -Werror $(event_flag)
 
 include $(BUILD_EXECUTABLE)
 
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := logtagd.rc
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
+
+include $(BUILD_PREBUILT)
+
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 52c6742..74e0ea5 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <arpa/inet.h>
+#include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -47,6 +48,7 @@
     registerCmd(new GetStatisticsCmd(buf));
     registerCmd(new SetPruneListCmd(buf));
     registerCmd(new GetPruneListCmd(buf));
+    registerCmd(new GetEventTagCmd(buf));
     registerCmd(new ReinitCmd());
     registerCmd(new ExitCmd(this));
 }
@@ -284,6 +286,41 @@
     return 0;
 }
 
+CommandListener::GetEventTagCmd::GetEventTagCmd(LogBuffer *buf) :
+        LogCommand("getEventTag"),
+        mBuf(*buf) {
+}
+
+int CommandListener::GetEventTagCmd::runCommand(SocketClient *cli,
+                                         int argc, char ** argv) {
+    setname();
+    uid_t uid = cli->getUid();
+    if (clientHasLogCredentials(cli)) {
+        uid = AID_ROOT;
+    }
+
+    const char *name = NULL;
+    const char *format = NULL;
+    for (int i = 1; i < argc; ++i) {
+        static const char _name[] = "name=";
+        if (!strncmp(argv[i], _name, strlen(_name))) {
+            name = argv[i] + strlen(_name);
+            continue;
+        }
+
+        static const char _format[] = "format=";
+        if (!strncmp(argv[i], _format, strlen(_format))) {
+            format = argv[i] + strlen(_format);
+            continue;
+        }
+    }
+
+    cli->sendMsg(package_string(mBuf.formatGetEventTag(uid,
+                                                       name, format)).c_str());
+
+    return 0;
+}
+
 CommandListener::ReinitCmd::ReinitCmd() : LogCommand("reinit") {
 }
 
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index 5d50177..39de03b 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -61,6 +61,7 @@
     LogBufferCmd(GetStatistics);
     LogBufferCmd(GetPruneList);
     LogBufferCmd(SetPruneList);
+    LogBufferCmd(GetEventTag);
 
 #define LogCmd(name)                                             \
     class name##Cmd : public LogCommand {                        \
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 820ff64..7613c1e 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -199,15 +199,13 @@
     if (log_id != LOG_ID_SECURITY) {
         int prio = ANDROID_LOG_INFO;
         const char *tag = NULL;
-        size_t len = 0;
         if (log_id == LOG_ID_EVENTS) {
-            tag = android::tagToName(&len, elem->getTag());
+            tag = tagToName(elem->getTag());
         } else {
             prio = *msg;
             tag = msg + 1;
-            len = strlen(tag);
         }
-        if (!__android_log_is_loggable_len(prio, tag, len, ANDROID_LOG_VERBOSE)) {
+        if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
             // Log traffic received to total
             pthread_mutex_lock(&mLogElementsLock);
             stats.add(elem);
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 932d55f..da63e12 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -27,6 +27,7 @@
 #include <sysutils/SocketClient.h>
 
 #include "LogBufferElement.h"
+#include "LogTags.h"
 #include "LogTimes.h"
 #include "LogStatistics.h"
 #include "LogWhiteBlackList.h"
@@ -99,6 +100,8 @@
 
     bool monotonic;
 
+    LogTags tags;
+
     LogBufferElement* lastLoggedElements[LOG_ID_MAX];
     LogBufferElement* droppedElements[LOG_ID_MAX];
     void log(LogBufferElement* elem);
@@ -133,6 +136,12 @@
     int initPrune(const char *cp) { return mPrune.init(cp); }
     std::string formatPrune() { return mPrune.format(); }
 
+    std::string formatGetEventTag(uid_t uid,
+                                  const char *name, const char *format) {
+        return tags.formatGetEventTag(uid, name, format);
+    }
+    const char *tagToName(uint32_t tag) { return tags.tagToName(tag); }
+
     // helper must be protected directly or implicitly by lock()/unlock()
     const char *pidToName(pid_t pid) { return stats.pidToName(pid); }
     uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 273150e..7e0a6b7 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -452,12 +452,11 @@
         name = android::base::StringPrintf("%7u/%u",
                                            getKey(), uid);
     }
-    size_t len = 0;
-    const char *nameTmp = getName(len);
+    const char *nameTmp = getName();
     if (nameTmp) {
         name += android::base::StringPrintf(
-            "%*s%.*s", (int)std::max(14 - name.length(), (size_t)1),
-            "", (int)len, nameTmp);
+            "%*s%s", (int)std::max(14 - name.length(), (size_t)1),
+            "", nameTmp);
     }
 
     std::string size = android::base::StringPrintf("%zu",
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 7acef6d..777dc33 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -413,7 +413,7 @@
     const uint32_t&getKey() const { return tag; }
     const pid_t&getPid() const { return pid; }
     const uid_t&getUid() const { return uid; }
-    const char*getName(size_t &len) const { return android::tagToName(&len, tag); }
+    const char*getName() const { return android::tagToName(tag); }
 
     inline void add(LogBufferElement *element) {
         if (uid != element->getUid()) {
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
new file mode 100644
index 0000000..a109592
--- /dev/null
+++ b/logd/LogTags.cpp
@@ -0,0 +1,884 @@
+/*
+ * 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.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <log/log_event_list.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "LogTags.h"
+#include "LogUtils.h"
+
+static LogTags* logtags;
+
+const char LogTags::system_event_log_tags[] = "/system/etc/event-log-tags";
+const char LogTags::dynamic_event_log_tags[] = "/dev/event-log-tags";
+// Only for debug
+const char LogTags::debug_event_log_tags[] = "/data/misc/logd/event-log-tags";
+
+// Sniff for first uid=%d in utf8z comment string
+static uid_t sniffUid(const char* comment, const char* endp) {
+    if (!comment) return AID_ROOT;
+
+    if (*comment == '#') ++comment;
+    while ((comment < endp) && (*comment != '\n') && isspace(*comment)) ++comment;
+    static const char uid_str[] = "uid=";
+    if (((comment + strlen(uid_str)) >= endp) ||
+            fastcmp<strncmp>(comment, uid_str, strlen(uid_str)) ||
+            !isdigit(comment[strlen(uid_str)])) return AID_ROOT;
+    char* cp;
+    unsigned long Uid = strtoul(comment + 4, &cp, 10);
+    if ((cp > endp) || (Uid >= INT_MAX)) return AID_ROOT;
+
+    return Uid;
+}
+
+// Checks for file corruption, and report false if there was no need
+// to rebuild the referenced file.  Failure to rebuild is only logged,
+// does not cause a return value of false.
+bool LogTags::RebuildFileEventLogTags(const char* filename, bool warn) {
+    int fd;
+
+    {
+        android::RWLock::AutoRLock readLock(rwlock);
+
+        if (tag2total.begin() == tag2total.end()) {
+            return false;
+        }
+
+        file2watermark_const_iterator iwater = file2watermark.find(filename);
+        if (iwater == file2watermark.end()) {
+            return false;
+        }
+
+        struct stat sb;
+        if (!stat(filename, &sb) && ((size_t)sb.st_size >= iwater->second)) {
+            return false;
+        }
+
+        // dump what we already know back into the file?
+        fd = TEMP_FAILURE_RETRY(open(filename,
+                                     O_WRONLY | O_TRUNC | O_CLOEXEC |
+                                     O_NOFOLLOW | O_BINARY));
+        if (fd >= 0) {
+            time_t now = time(NULL);
+            struct tm tm;
+            localtime_r(&now, &tm);
+            char timebuf[20];
+            size_t len = strftime(timebuf, sizeof(timebuf),
+                                  "%Y-%m-%d %H:%M:%S", &tm);
+            android::base::WriteStringToFd(
+                android::base::StringPrintf(
+                    "# Rebuilt %.20s, content owned by logd\n", timebuf),
+                fd);
+            for (const auto& it : tag2total) {
+                android::base::WriteStringToFd(formatEntry_locked(it.first,
+                                                                  AID_ROOT),
+                                               fd);
+            }
+            TEMP_FAILURE_RETRY(close(fd));
+        }
+    }
+
+    if (warn) {
+        android::prdebug(((fd < 0) ?
+                              "%s failed to rebuild" :
+                              "%s missing, damaged or truncated; rebuilt"),
+                         filename);
+    }
+
+    if (fd >= 0) {
+        android::RWLock::AutoWLock writeLock(rwlock);
+
+        struct stat sb;
+        if (!stat(filename, &sb)) file2watermark[filename] = sb.st_size;
+    }
+
+    return true;
+}
+
+void LogTags::AddEventLogTags(uint32_t tag, uid_t uid,
+                              const std::string& Name,
+                              const std::string& Format,
+                              const char* source, bool warn) {
+    std::string Key = Name;
+    if (Format.length()) Key += "+" + Format;
+
+    bool update = !source || !!strcmp(source, system_event_log_tags);
+    bool newOne;
+
+    {
+        android::RWLock::AutoWLock writeLock(rwlock);
+
+        tag2total_const_iterator itot = tag2total.find(tag);
+
+        // unlikely except for dupes, or updates to uid list (more later)
+        if (itot != tag2total.end()) update = false;
+
+        newOne = tag2name.find(tag) == tag2name.end();
+        key2tag[Key] = tag;
+
+        if (Format.length()) {
+            if (key2tag.find(Name) == key2tag.end()) {
+                key2tag[Name] = tag;
+            }
+            tag2format[tag] = Format;
+        }
+        tag2name[tag] = Name;
+
+        tag2uid_const_iterator ut = tag2uid.find(tag);
+        if (ut != tag2uid.end()) {
+            if (uid == AID_ROOT) {
+                tag2uid.erase(ut);
+                update = true;
+            } else if (ut->second.find(uid) == ut->second.end()) {
+                const_cast<uid_list&>(ut->second).emplace(uid);
+                update = true;
+            }
+        } else if (newOne && (uid != AID_ROOT)) {
+            tag2uid[tag].emplace(uid);
+            update = true;
+        }
+
+        // updatePersist -> trigger output on modified
+        // content, reset tag2total if available
+        if (update && (itot != tag2total.end())) tag2total[tag] = 0;
+    }
+
+    if (update) {
+        WritePersistEventLogTags(tag, uid, source);
+    } else if (warn && !newOne && source) {
+        // For the files, we want to report dupes.
+        android::prdebug("Multiple tag %" PRIu32 " %s %s %s", tag,
+            Name.c_str(), Format.c_str(), source);
+    }
+}
+
+// Read the event log tags file, and build up our internal database
+void LogTags::ReadFileEventLogTags(const char* filename, bool warn) {
+    bool etc = !strcmp(filename, system_event_log_tags);
+    bool debug = !etc && !strcmp(filename, debug_event_log_tags);
+
+    if (!etc) {
+        RebuildFileEventLogTags(filename, warn);
+    }
+    std::string content;
+    if (android::base::ReadFileToString(filename, &content)) {
+        char* cp = (char*) content.c_str();
+        char* endp = cp + content.length();
+
+        {
+            android::RWLock::AutoRLock writeLock(rwlock);
+
+            file2watermark[filename] = content.length();
+        }
+
+        char* lineStart = cp;
+        while (cp < endp) {
+            if (*cp == '\n') {
+                lineStart = cp;
+            } else if (lineStart) {
+                if (*cp == '#') {
+                    /* comment; just scan to end */
+                    lineStart = NULL;
+                } else if (isdigit(*cp)) {
+                    unsigned long Tag = strtoul(cp, &cp, 10);
+                    if (warn && (Tag > emptyTag)) {
+                        android::prdebug("tag too large %lu", Tag);
+                    }
+                    while ((cp < endp) && (*cp != '\n') && isspace(*cp)) ++cp;
+                    if (cp >= endp) break;
+                    if (*cp == '\n') continue;
+                    const char* name = cp;
+                    /* Determine whether it is a valid tag name [a-zA-Z0-9_] */
+                    bool hasAlpha = false;
+                    while ((cp < endp) && (isalnum(*cp) || (*cp == '_'))) {
+                        if (!isdigit(*cp)) hasAlpha = true;
+                        ++cp;
+                    }
+                    std::string Name(name, cp - name);
+#ifdef ALLOW_NOISY_LOGGING_OF_PROBLEM_WITH_LOTS_OF_TECHNICAL_DEBT
+                    static const size_t maximum_official_tag_name_size = 24;
+                    if (warn && (Name.length() > maximum_official_tag_name_size)) {
+                       android::prdebug("tag name too long %s", Name.c_str());
+                    }
+#endif
+                    if (hasAlpha && ((cp >= endp) || (*cp == '#') || isspace(*cp))) {
+                        if (Tag > emptyTag) {
+                            if (*cp != '\n') lineStart = NULL;
+                            continue;
+                        }
+                        while ((cp < endp) && (*cp != '\n') && isspace(*cp)) ++cp;
+                        const char* format = cp;
+                        uid_t uid = AID_ROOT;
+                        while ((cp < endp) && (*cp != '\n')) {
+                            if (*cp == '#') {
+                                uid = sniffUid(cp, endp);
+                                lineStart = NULL;
+                                break;
+                            }
+                            ++cp;
+                        }
+                        while ((cp > format) && isspace(cp[-1])) {
+                            --cp;
+                            lineStart = NULL;
+                        }
+                        std::string Format(format, cp - format);
+
+                        AddEventLogTags((uint32_t)Tag, uid, Name, Format,
+                                        filename, warn);
+                    } else {
+                        if (warn) {
+                            android::prdebug("tag name invalid %.*s",
+                                             (int)(cp - name + 1), name);
+                        }
+                        lineStart = NULL;
+                    }
+                } else if (!isspace(*cp)) break;
+            }
+            cp++;
+        }
+    } else if (warn) {
+        android::prdebug("Cannot read %s", filename);
+    }
+}
+
+// Extract a 4-byte value from a byte stream.
+static inline uint32_t get4LE(const char* msg)
+{
+    const uint8_t* src = reinterpret_cast<const uint8_t*>(msg);
+    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+// Additional persistent sources for invented log tags.  Read the
+// special pmsg event for log tags, and build up our internal
+// database with any found.
+void LogTags::ReadPersistEventLogTags() {
+    struct logger_list* logger_list = android_logger_list_alloc(
+        ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK,
+        0, (pid_t)0);
+    if (!logger_list) return;
+
+    struct logger* e = android_logger_open(logger_list, LOG_ID_EVENTS);
+    struct logger* s = android_logger_open(logger_list, LOG_ID_SECURITY);
+    if (!e && !s) {
+        android_logger_list_free(logger_list);
+        return;
+    }
+
+    for (;;) {
+        struct log_msg log_msg;
+        int ret = android_logger_list_read(logger_list, &log_msg);
+        if (ret <= 0) break;
+
+        const char* msg = log_msg.msg();
+        if (!msg) continue;
+        if (log_msg.entry.len <= sizeof(uint32_t)) continue;
+        uint32_t Tag = get4LE(msg);
+        if (Tag != TAG_DEF_LOG_TAG) continue;
+        uid_t uid = (log_msg.entry.hdr_size >= sizeof(logger_entry_v4)) ?
+            log_msg.entry.uid : AID_ROOT;
+
+        std::string Name;
+        std::string Format;
+        android_log_list_element elem;
+        {
+            android_log_event_list ctx(log_msg);
+            elem = ctx.read();
+            if (elem.type != EVENT_TYPE_LIST) {
+                continue;
+            }
+            elem = ctx.read();
+            if (elem.type != EVENT_TYPE_INT) {
+                continue;
+            }
+            Tag = elem.data.int32;
+            elem = ctx.read();
+            if (elem.type != EVENT_TYPE_STRING) {
+                continue;
+            }
+            Name = std::string(elem.data.string, elem.len);
+            elem = ctx.read();
+            if (elem.type != EVENT_TYPE_STRING) {
+                continue;
+            }
+            Format = std::string(elem.data.string, elem.len);
+            elem = ctx.read();
+        }
+        if ((elem.type != EVENT_TYPE_LIST_STOP) || !elem.complete) continue;
+
+        AddEventLogTags(Tag, uid, Name, Format);
+    }
+    android_logger_list_free(logger_list);
+}
+
+LogTags::LogTags() {
+    ReadFileEventLogTags(system_event_log_tags);
+    // Following will likely fail on boot, but is required if logd restarts
+    ReadFileEventLogTags(dynamic_event_log_tags, false);
+    if (__android_log_is_debuggable()) {
+        ReadFileEventLogTags(debug_event_log_tags, false);
+    }
+    ReadPersistEventLogTags();
+
+    logtags = this;
+}
+
+// Converts an event tag into a name
+const char* LogTags::tagToName(uint32_t tag) const {
+    tag2name_const_iterator it;
+
+    android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+    it = tag2name.find(tag);
+    if ((it == tag2name.end()) || (it->second.length() == 0)) return NULL;
+
+    return it->second.c_str();
+}
+
+// Prototype in LogUtils.h allowing external access to our database.
+//
+// This must be a pure reader to our database, as everything else is
+// guaranteed single-threaded except this access point which is
+// asynchonous and can be multithreaded and thus rentrant.  The
+// object's rwlock is only used to guarantee atomic access to the
+// unordered_map to prevent corruption, with a requirement to be a
+// low chance of contention for this call.  If we end up changing
+// this algorithm resulting in write, then we should use a different
+// lock than the object's rwlock to protect groups of associated
+// actions.
+const char* android::tagToName(uint32_t tag) {
+    LogTags* me = logtags;
+
+    if (!me) return NULL;
+    me->WritePmsgEventLogTags(tag);
+    return me->tagToName(tag);
+}
+
+// Prototype in LogUtils.h allowing external access to our database.
+//
+// This only works on userdebug and eng devices to re-read the
+// /data/misc/logd/event-log-tags file right after /data is mounted.
+// The operation is near to boot and should only happen once.  There
+// are races associated with its use since it can trigger a Rebuild
+// of the file, but that is a can-not-happen since the file was not
+// read yet.  More dangerous if called later, but if all is well it
+// should just skip over everything and not write any new entries.
+void android::ReReadEventLogTags() {
+    LogTags* me = logtags;
+
+    if (me && __android_log_is_debuggable()) {
+        me->ReadFileEventLogTags(me->debug_event_log_tags);
+    }
+}
+
+// converts an event tag into a format
+const char* LogTags::tagToFormat(uint32_t tag) const {
+    tag2format_const_iterator iform;
+
+    android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+    iform = tag2format.find(tag);
+    if (iform == tag2format.end()) return NULL;
+
+    return iform->second.c_str();
+}
+
+// converts a name into an event tag
+uint32_t LogTags::nameToTag(const char* name) const {
+    uint32_t ret = emptyTag;
+
+    // Bug: Only works for a single entry, we can have multiple entries,
+    // one for each format, so we find first entry recorded, or entry with
+    // no format associated with it.
+
+    android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+    key2tag_const_iterator ik = key2tag.find(std::string(name));
+    if (ik != key2tag.end()) ret = ik->second;
+
+    return ret;
+}
+
+// Caller must perform locks, can be under reader (for pre-check) or
+// writer lock.  We use this call to invent a new deterministically
+// random tag, unique is cleared if no conflicts.  If format is NULL,
+// we are in readonly mode.
+uint32_t LogTags::nameToTag_locked(const std::string& name,
+                                   const char* format,
+                                   bool& unique) {
+    key2tag_const_iterator ik;
+
+    bool write = format != NULL;
+    unique = write;
+
+    if (!write) {
+        // Bug: Only works for a single entry, we can have multiple entries,
+        // one for each format, so we find first entry recorded, or entry with
+        // no format associated with it.
+        ik = key2tag.find(name);
+        if (ik == key2tag.end()) return emptyTag;
+        return ik->second;
+    }
+
+    std::string Key(name);
+    if (*format) Key += std::string("+") + format;
+
+    ik = key2tag.find(Key);
+    if (ik != key2tag.end()) {
+        unique = false;
+        return ik->second;
+    }
+
+    size_t Hash = key2tag.hash_function()(Key);
+    uint32_t Tag = Hash;
+    // This sets an upper limit on the conflics we are allowed to deal with.
+    for (unsigned i = 0; i < 256; ) {
+        tag2name_const_iterator it = tag2name.find(Tag);
+        if (it == tag2name.end()) return Tag;
+        std::string localKey(it->second);
+        tag2format_const_iterator iform = tag2format.find(Tag);
+        if ((iform == tag2format.end()) && iform->second.length()) {
+            localKey += "+" + iform->second;
+        }
+        unique = !!it->second.compare(localKey);
+        if (!unique) return Tag; // unlikely except in a race
+
+        ++i;
+        // Algorithm to convert hash to next tag
+        if (i < 32) {
+            Tag = (Hash >> i);
+            // size_t is 32 bits, or upper word zero, rotate
+            if ((sizeof(Hash) <= 4) ||
+                    ((Hash & (uint64_t(-1LL) << 32)) == 0)) {
+                Tag |= Hash << (32 - i);
+            }
+        } else {
+            Tag = Hash + i - 31;
+        }
+    }
+    return emptyTag;
+}
+
+static int openFile(const char* name, int mode, bool warning) {
+    int fd = TEMP_FAILURE_RETRY(open(name, mode));
+    if ((fd < 0) && warning) {
+        android::prdebug("Failed open %s (%d)", name, errno);
+    }
+    return fd;
+}
+
+void LogTags::WritePmsgEventLogTags(uint32_t tag, uid_t uid) {
+    android::RWLock::AutoRLock readLock(rwlock);
+
+    tag2total_const_iterator itot = tag2total.find(tag);
+    if (itot == tag2total.end()) return; // source is a static entry
+
+    size_t lastTotal = itot->second;
+
+    // Every 16K (half the smallest configurable pmsg buffer size) record
+    static const size_t rate_to_pmsg = 16 * 1024;
+    if (lastTotal && ((android::sizesTotal() - lastTotal) < rate_to_pmsg)) {
+        return;
+    }
+
+    static int pmsg_fd = -1;
+    if (pmsg_fd < 0) {
+        pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+        // unlikely, but deal with partners with borken pmsg
+        if (pmsg_fd < 0) return;
+    }
+
+    std::string Name = tag2name[tag];
+    tag2format_const_iterator iform = tag2format.find(tag);
+    std::string Format = (iform != tag2format.end()) ? iform->second : "";
+
+    __android_log_event_list ctx(TAG_DEF_LOG_TAG);
+    ctx << tag << Name << Format;
+    std::string buffer(ctx);
+    if (buffer.length() <= 0) return; // unlikely
+
+    /*
+     *  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;
+     *      };
+     *  };
+     */
+
+    struct timespec ts;
+    clock_gettime(android_log_clockid(), &ts);
+
+    android_log_header_t header = {
+        .id = LOG_ID_EVENTS,
+        .tid = (uint16_t)gettid(),
+        .realtime.tv_sec = (uint32_t)ts.tv_sec,
+        .realtime.tv_nsec = (uint32_t)ts.tv_nsec,
+    };
+
+    uint32_t outTag = TAG_DEF_LOG_TAG;
+    outTag = get4LE((const char*)&outTag);
+
+    android_pmsg_log_header_t pmsgHeader = {
+        .magic = LOGGER_MAGIC,
+        .len = (uint16_t)(sizeof(pmsgHeader) + sizeof(header) +
+                          sizeof(outTag) + buffer.length()),
+        .uid = (uint16_t)AID_ROOT,
+        .pid = (uint16_t)getpid(),
+    };
+
+    struct iovec Vec[] = {
+        { (unsigned char*)&pmsgHeader, sizeof(pmsgHeader) },
+        { (unsigned char*)&header, sizeof(header) },
+        { (unsigned char*)&outTag, sizeof(outTag) },
+        { (unsigned char*)const_cast<char*>(buffer.data()), buffer.length() }
+    };
+
+    tag2uid_const_iterator ut = tag2uid.find(tag);
+    if (ut == tag2uid.end()) {
+        TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+    } else if (uid != AID_ROOT) {
+        pmsgHeader.uid = (uint16_t)uid;
+        TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+    } else {
+        for (auto &it : ut->second) {
+            pmsgHeader.uid = (uint16_t)it;
+            TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+        }
+    }
+}
+
+void LogTags::WriteDynamicEventLogTags(uint32_t tag, uid_t uid) {
+    static const int mode = O_WRONLY | O_APPEND |
+                            O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+
+    int fd = openFile(dynamic_event_log_tags, mode, true);
+    if (fd < 0) return;
+
+    android::RWLock::AutoWLock writeLock(rwlock);
+
+    std::string ret = formatEntry_locked(tag, uid, false);
+    android::base::WriteStringToFd(ret, fd);
+    TEMP_FAILURE_RETRY(close(fd));
+
+    size_t size = 0;
+    file2watermark_const_iterator iwater;
+
+    iwater = file2watermark.find(dynamic_event_log_tags);
+    if (iwater != file2watermark.end()) size = iwater->second;
+
+    file2watermark[dynamic_event_log_tags] = size + ret.length();
+}
+
+void LogTags::WriteDebugEventLogTags(uint32_t tag, uid_t uid) {
+    static const int mode = O_WRONLY | O_APPEND |
+                            O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+
+    static bool one = true;
+    int fd = openFile(debug_event_log_tags, mode, one);
+    one = fd >= 0;
+    if (!one) return;
+
+    android::RWLock::AutoWLock writeLock(rwlock);
+
+    std::string ret = formatEntry_locked(tag, uid, false);
+    android::base::WriteStringToFd(ret, fd);
+    TEMP_FAILURE_RETRY(close(fd));
+
+    size_t size = 0;
+    file2watermark_const_iterator iwater;
+
+    iwater = file2watermark.find(debug_event_log_tags);
+    if (iwater != file2watermark.end()) size = iwater->second;
+
+    file2watermark[debug_event_log_tags] = size + ret.length();
+}
+
+// How we maintain some runtime or reboot stickiness
+void LogTags::WritePersistEventLogTags(uint32_t tag,
+                                       uid_t uid, const char* source) {
+    // very unlikely
+    bool etc = source && !strcmp(source, system_event_log_tags);
+    if (etc) return;
+
+    bool dynamic = source && !strcmp(source, dynamic_event_log_tags);
+    bool debug = (!dynamic &&
+                  source &&
+                  !strcmp(source, debug_event_log_tags)) ||
+                 !__android_log_is_debuggable();
+
+    WritePmsgEventLogTags(tag, uid);
+
+    size_t lastTotal = 0;
+    {
+        android::RWLock::AutoRLock readLock(rwlock);
+
+        tag2total_const_iterator itot = tag2total.find(tag);
+        if (itot != tag2total.end()) lastTotal = itot->second;
+    }
+
+    if (lastTotal == 0) { // denotes first time for this one
+        if (!dynamic || !RebuildFileEventLogTags(dynamic_event_log_tags)) {
+            WriteDynamicEventLogTags(tag, uid);
+        }
+
+        if (!debug && !RebuildFileEventLogTags(debug_event_log_tags)) {
+            WriteDebugEventLogTags(tag, uid);
+        }
+    }
+
+    lastTotal = android::sizesTotal();
+    if (!lastTotal) ++lastTotal;
+
+    // record totals for next watermark.
+    android::RWLock::AutoWLock writeLock(rwlock);
+    tag2total[tag] = lastTotal;
+}
+
+// nameToTag converts a name into an event tag. If format is NULL, then we
+// are in readonly mode.
+uint32_t LogTags::nameToTag(uid_t uid, const char* name, const char* format) {
+    std::string Name = std::string(name);
+    bool write = format != NULL;
+    bool updateUid = uid != AID_ROOT;
+    bool updateFormat = format && *format;
+    bool unique;
+    uint32_t Tag;
+
+    {
+        android::RWLock::AutoRLock readLock(rwlock);
+
+        Tag = nameToTag_locked(Name, format, unique);
+        if (updateUid && (Tag != emptyTag) && !unique) {
+            tag2uid_const_iterator ut = tag2uid.find(Tag);
+            if (updateUid) {
+                if ((ut != tag2uid.end()) &&
+                        (ut->second.find(uid) == ut->second.end())) {
+                    unique = write; // write passthrough to update uid counts
+                    if (!write) Tag = emptyTag; // deny read access
+                }
+            } else {
+                unique = write && (ut != tag2uid.end());
+            }
+        }
+    }
+
+    if (Tag == emptyTag) return Tag;
+    WritePmsgEventLogTags(Tag, uid); // record references periodically
+    if (!unique) return Tag;
+
+    bool updateWrite = false;
+    bool updateTag;
+
+    // Special case of AddEventLogTags, checks per-uid counter which makes
+    // no sense there, and is also optimized somewhat to reduce write times.
+    {
+        android::RWLock::AutoWLock writeLock(rwlock);
+
+        // double check after switch from read lock to write lock for Tag
+        updateTag = tag2name.find(Tag) == tag2name.end();
+        // unlikely, either update, race inviting conflict or multiple uids
+        if (!updateTag) {
+            Tag = nameToTag_locked(Name, format, unique);
+            if (Tag == emptyTag) return Tag;
+            // is it multiple uid's setting this value
+            if (!unique) {
+                tag2uid_const_iterator ut = tag2uid.find(Tag);
+                if (updateUid) {
+                    // Add it to the uid list
+                    if ((ut == tag2uid.end()) ||
+                        (ut->second.find(uid) != ut->second.end())) return Tag;
+                    const_cast<uid_list&>(ut->second).emplace(uid);
+                    updateWrite = true;
+                } else {
+                    if (ut == tag2uid.end()) return Tag;
+                    // (system) adding a global one, erase the uid list
+                    tag2uid.erase(ut);
+                    updateWrite = true;
+                }
+            }
+        }
+
+        // Update section
+        size_t count;
+        if (updateUid) {
+            count = 0;
+            uid2count_const_iterator ci = uid2count.find(uid);
+            if (ci != uid2count.end()) {
+                count = ci->second;
+                if (count >= max_per_uid) {
+                    if (!updateWrite) return emptyTag;
+                    // If we are added to the per-Uid perms, leak the Tag
+                    // if it already exists.
+                    updateUid = false;
+                    updateTag = false;
+                    updateFormat = false;
+                }
+            }
+        }
+
+        // updateWrite -> trigger output on modified content, reset tag2total
+        //    also sets static to dynamic entries if they are alterred,
+        //    only occurs if they have a uid, and runtime adds another uid.
+        if (updateWrite) tag2total[Tag] = 0;
+
+        if (updateTag) {
+            // mark as a dynamic entry, but do not upset current total counter
+            tag2total_const_iterator itot = tag2total.find(Tag);
+            if (itot == tag2total.end()) tag2total[Tag] = 0;
+
+            if (*format) {
+                key2tag[Name + "+" + format] = Tag;
+                if (key2tag.find(Name) == key2tag.end()) key2tag[Name] = Tag;
+            } else {
+                key2tag[Name] = Tag;
+            }
+            tag2name[Tag] = Name;
+        }
+        if (updateFormat) tag2format[Tag] = format;
+
+        if (updateUid) {
+            tag2uid[Tag].emplace(uid);
+            uid2count[uid] = count + 1;
+        }
+    }
+
+    if (updateTag || updateFormat || updateWrite) {
+        WritePersistEventLogTags(Tag, uid);
+    }
+
+    return Tag;
+}
+
+std::string LogTags::formatEntry(uint32_t tag, uid_t uid,
+                                 const char* name,
+                                 const char* format) {
+    if (!format || !format[0]) {
+        return android::base::StringPrintf("%" PRIu32 "\t%s\n", tag, name);
+    }
+    size_t len = (strlen(name) + 7) / 8;
+    static const char tabs[] = "\t\t\t";
+    if (len > strlen(tabs)) len = strlen(tabs);
+    std::string Uid;
+    if (uid != AID_ROOT) Uid = android::base::StringPrintf(" # uid=%u", uid);
+    return android::base::StringPrintf("%" PRIu32 "\t%s%s\t%s%s\n",
+                                       tag, name, &tabs[len], format,
+                                       Uid.c_str());
+}
+
+std::string LogTags::formatEntry_locked(uint32_t tag, uid_t uid,
+                                        bool authenticate) {
+    const char* name = tag2name[tag].c_str();
+
+    const char* format = "";
+    tag2format_const_iterator iform = tag2format.find(tag);
+    if (iform != tag2format.end()) format = iform->second.c_str();
+
+    // Access permission test, do not report dynamic entries
+    // that do not belong to us.
+    tag2uid_const_iterator ut = tag2uid.find(tag);
+    if (ut == tag2uid.end()) {
+        return formatEntry(tag, AID_ROOT, name, format);
+    }
+    if (uid != AID_ROOT) {
+        if (authenticate && (ut->second.find(uid) == ut->second.end())) {
+            return std::string("");
+        }
+        return formatEntry(tag, uid, name, format);
+    }
+
+    // Show all, one for each registered uid (we are group root)
+    std::string ret;
+    for (auto &it : ut->second) {
+        ret += formatEntry(tag, it, name, format);
+    }
+    return ret;
+}
+
+std::string LogTags::formatGetEventTag(uid_t uid,
+                                       const char* name, const char* format) {
+    bool all = name && (name[0] == '*') && !name[1];
+    bool list = !name || all;
+    std::string ret;
+
+    if (!list) {
+        // switch to read entry only if format == "*"
+        if (format && (format[0] == '*') && !format[1]) format = NULL;
+
+        // WAI: for null format, only works for a single entry, we can have
+        // multiple entries, one for each format, so we find first entry
+        // recorded, or entry with no format associated with it.
+        // We may desire to print all that match the name, but we did not
+        // add a mapping table for that and the cost is too high.
+        uint32_t tag = nameToTag(uid, name, format);
+        if (tag == emptyTag) return std::string("-1 ESRCH");
+        if (uid == AID_ROOT) {
+            android::RWLock::AutoRLock readLock(rwlock);
+
+            // first uid in list so as to manufacture an accurate reference
+            tag2uid_const_iterator ut = tag2uid.find(tag);
+            if ((ut != tag2uid.end()) &&
+                 (ut->second.begin() != ut->second.end())) {
+                uid = *(ut->second.begin());
+            }
+        }
+        ret = formatEntry(tag, uid, name, format ?: tagToFormat(tag));
+        if (!ret.length()) return std::string("-1 ESRCH");
+        return ret;
+    }
+
+    android::RWLock::AutoRLock readLock(rwlock);
+    if (all) {
+        // everything under the sun
+        for (const auto& it : tag2name) {
+            ret += formatEntry_locked(it.first, uid);
+        }
+    } else {
+        // set entries are dynamic
+        for (const auto& it : tag2total) {
+            ret += formatEntry_locked(it.first, uid);
+        }
+    }
+    return ret;
+}
diff --git a/logd/LogTags.h b/logd/LogTags.h
new file mode 100644
index 0000000..37a6d96
--- /dev/null
+++ b/logd/LogTags.h
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+#ifndef _LOGD_LOG_TAGS_H__
+#define _LOGD_LOG_TAGS_H__
+
+#include <unordered_map>
+#include <unordered_set>
+#include <string>
+
+#include <utils/RWLock.h>
+
+class LogTags {
+    // This lock protects all the unordered_map accesses below.  It
+    // is a reader/writer lock so that contentions are kept to a
+    // minimum since writes are rare, even administratably when
+    // reads are extended.  Resist the temptation to use the writer
+    // lock to protect anything outside the following unordered_maps
+    // as that would increase the reader contentions.  Use a separate
+    // mutex to protect the other entities.
+    android::RWLock rwlock;
+
+    // key is Name + "+" + Format
+    std::unordered_map<std::string, uint32_t> key2tag;
+    typedef std::unordered_map<std::string, uint32_t>::const_iterator key2tag_const_iterator;
+
+    // Allows us to manage access permissions based on uid registrants
+    // Global entries are specifically erased.
+    typedef std::unordered_set<uid_t> uid_list;
+    std::unordered_map<uint32_t, uid_list> tag2uid;
+    typedef std::unordered_map<uint32_t, uid_list>::const_iterator tag2uid_const_iterator;
+
+    std::unordered_map<uint32_t, std::string> tag2name;
+    typedef std::unordered_map<uint32_t, std::string>::const_iterator tag2name_const_iterator;
+
+    std::unordered_map<uint32_t, std::string> tag2format;
+    typedef std::unordered_map<uint32_t, std::string>::const_iterator tag2format_const_iterator;
+
+    static const size_t max_per_uid = 256; // Put a cap on the tags per uid
+    std::unordered_map<uid_t, size_t> uid2count;
+    typedef std::unordered_map<uid_t, size_t>::const_iterator uid2count_const_iterator;
+
+    // Dynamic entries are assigned
+    std::unordered_map<uint32_t, size_t> tag2total;
+    typedef std::unordered_map<uint32_t, size_t>::const_iterator tag2total_const_iterator;
+
+    // emplace unique tag
+    uint32_t nameToTag(uid_t uid, const char* name, const char* format);
+    // find unique or associated tag
+    uint32_t nameToTag_locked(const std::string& name, const char* format, bool &unique);
+
+    // Record expected file watermarks to detect corruption.
+    std::unordered_map<std::string, size_t> file2watermark;
+    typedef std::unordered_map<std::string, size_t>::const_iterator file2watermark_const_iterator;
+
+    void ReadPersistEventLogTags();
+
+    // format helpers
+    // format a single entry, does not need object data
+    static std::string formatEntry(uint32_t tag, uid_t uid,
+                                   const char* name, const char* format);
+    // caller locks, database lookup, authenticate against uid
+    std::string formatEntry_locked(uint32_t tag, uid_t uid,
+                                   bool authenticate = true);
+
+    bool RebuildFileEventLogTags(const char* filename, bool warn = true);
+
+    void AddEventLogTags(uint32_t tag, uid_t uid,
+                         const std::string& Name, const std::string& Format,
+                         const char* source = NULL, bool warn = false);
+
+    void WriteDynamicEventLogTags(uint32_t tag, uid_t uid);
+    void WriteDebugEventLogTags(uint32_t tag, uid_t uid);
+    // push tag details to persistent storage
+    void WritePersistEventLogTags(uint32_t tag,
+                                  uid_t uid = AID_ROOT,
+                                  const char* source = NULL);
+
+    static const uint32_t emptyTag = uint32_t(-1);
+
+public:
+
+    static const char system_event_log_tags[];
+    static const char dynamic_event_log_tags[];
+    // Only for userdebug and eng
+    static const char debug_event_log_tags[];
+
+    LogTags();
+
+    void WritePmsgEventLogTags(uint32_t tag, uid_t uid = AID_ROOT);
+    void ReadFileEventLogTags(const char* filename, bool warn = true);
+
+    // reverse lookup from tag
+    const char* tagToName(uint32_t tag) const;
+    const char* tagToFormat(uint32_t tag) const;
+    // find associated tag
+    uint32_t nameToTag(const char* name) const;
+
+    // emplace tag if necessary, provide event-log-tag formated output in string
+    std::string formatGetEventTag(uid_t uid,
+                                  const char* name,
+                                  const char* format);
+};
+
+#endif // _LOGD_LOG_TAGS_H__
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index 70f24e4..f044b27 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -39,8 +39,9 @@
 char *pidToName(pid_t pid);
 char *tidToName(pid_t tid);
 
-// Furnished in main.cpp. Thread safe.
-const char *tagToName(size_t *len, uint32_t tag);
+// Furnished in LogTags.cpp. Thread safe.
+const char *tagToName(uint32_t tag);
+void ReReadEventLogTags();
 
 // Furnished by LogKlog.cpp.
 const char* strnstr(const char* s, size_t len, const char* needle);
diff --git a/logd/event.logtags b/logd/event.logtags
index 0d24df0..39063a9 100644
--- a/logd/event.logtags
+++ b/logd/event.logtags
@@ -35,3 +35,4 @@
 
 1003  auditd (avc|3)
 1004  chatty (dropped|3)
+1005  tag_def (tag|1),(name|3),(format|3)
diff --git a/logd/logd.rc b/logd/logd.rc
index 54349dd..ee89b83 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -14,3 +14,10 @@
     user logd
     group logd
     writepid /dev/cpuset/system-background/tasks
+
+on fs
+    write /dev/event-log-tags "# content owned by logd
+"
+    chown logd logd /dev/event-log-tags
+    chmod 0644 /dev/event-log-tags
+    restorecon /dev/event-log-tags
diff --git a/logd/logtagd.rc b/logd/logtagd.rc
new file mode 100644
index 0000000..46aa8c1
--- /dev/null
+++ b/logd/logtagd.rc
@@ -0,0 +1,9 @@
+#
+# logtagd event log tag service (debug only)
+#
+on post-fs-data
+    mkdir /data/misc/logd 0700 logd log
+    write /data/misc/logd/event-log-tags ""
+    chown logd log /data/misc/logd/event-log-tags
+    chmod 0600 /data/misc/logd/event-log-tags
+    restorecon /data/misc/logd/event-log-tags
diff --git a/logd/main.cpp b/logd/main.cpp
index 5878f15..2551f2e 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -244,7 +244,7 @@
     // anything else, we have even lesser privileges and accept our fate. Not
     // worth checking for error returns setting this thread's privileges.
     (void)setgid(AID_SYSTEM); // readonly access to /data/system/packages.list
-    (void)setuid(AID_LOGD);   // access to everything logd.
+    (void)setuid(AID_LOGD);   // access to everything logd, eg /data/misc/logd
 
     while (reinit_running && !sem_wait(&reinit) && reinit_running) {
 
@@ -271,6 +271,7 @@
             logBuf->init();
             logBuf->initPrune(NULL);
         }
+        android::ReReadEventLogTags();
     }
 
     return NULL;
@@ -304,24 +305,6 @@
     sem_post(&reinit);
 }
 
-// tagToName converts an events tag into a name
-const char *android::tagToName(size_t *len, uint32_t tag) {
-    static const EventTagMap *map;
-
-    if (!map) {
-        sem_wait(&sem_name);
-        if (!map) {
-            map = android_openEventTagMap(NULL);
-        }
-        sem_post(&sem_name);
-        if (!map) {
-            if (len) len = 0;
-            return NULL;
-        }
-    }
-    return android_lookupEventTag_len(map, len, tag);
-}
-
 static void readDmesg(LogAudit *al, LogKlog *kl) {
     if (!al && !kl) {
         return;
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 2a6cdc8..adf583b 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -39,12 +39,8 @@
 #include "../libaudit.h" // pickup AUDIT_RATE_LIMIT_*
 #include "../LogReader.h" // pickup LOGD_SNDTIMEO
 
-/*
- * returns statistics
- */
-static void my_android_logger_get_statistics(char *buf, size_t len)
+static void send_to_control(char* buf, size_t len)
 {
-    snprintf(buf, len, "getStatistics 0 1 2 3 4");
     int sock = socket_local_client("logd",
                                    ANDROID_SOCKET_NAMESPACE_RESERVED,
                                    SOCK_STREAM);
@@ -52,7 +48,7 @@
         if (write(sock, buf, strlen(buf) + 1) > 0) {
             ssize_t ret;
             while ((ret = read(sock, buf, len)) > 0) {
-                if ((size_t)ret == len) {
+                if (((size_t)ret == len) || (len < PAGE_SIZE)) {
                     break;
                 }
                 len -= ret;
@@ -74,6 +70,15 @@
     }
 }
 
+/*
+ * returns statistics
+ */
+static void my_android_logger_get_statistics(char *buf, size_t len)
+{
+    snprintf(buf, len, "getStatistics 0 1 2 3 4");
+    send_to_control(buf, len);
+}
+
 static void alloc_statistics(char **buffer, size_t *length)
 {
     size_t len = 8192;
@@ -816,6 +821,44 @@
     close(fd);
 }
 
+TEST(logd, getEventTag_list) {
+#ifdef __ANDROID__
+    char buffer[256];
+    memset(buffer, 0, sizeof(buffer));
+    snprintf(buffer, sizeof(buffer), "getEventTag name=*");
+    send_to_control(buffer, sizeof(buffer));
+    buffer[sizeof(buffer) - 1] = '\0';
+    char *cp;
+    long ret = strtol(buffer, &cp, 10);
+    EXPECT_GT(ret, 4096);
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(logd, getEventTag_newentry) {
+#ifdef __ANDROID__
+    char buffer[256];
+    memset(buffer, 0, sizeof(buffer));
+    log_time now(CLOCK_MONOTONIC);
+    char name[64];
+    snprintf(name, sizeof(name), "a%" PRIu64, now.nsec());
+    snprintf(buffer, sizeof(buffer),
+             "getEventTag name=%s format=\"(new|1)\"", name);
+    send_to_control(buffer, sizeof(buffer));
+    buffer[sizeof(buffer) - 1] = '\0';
+    char *cp;
+    long ret = strtol(buffer, &cp, 10);
+    EXPECT_GT(ret, 16);
+    EXPECT_TRUE(strstr(buffer, "\t(new|1)") != NULL);
+    EXPECT_TRUE(strstr(buffer, name) != NULL);
+    // ToDo: also look for this in /data/misc/logd/event-log-tags and
+    // /dev/event-log-tags.
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
 static inline int32_t get4LE(const char* src)
 {
     return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);