Merge "Enable libprocessgroup on host bionic"
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index f10b9ce..8a6877a 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -135,7 +135,7 @@
     return android::base::StringPrintf("%s/%s", dir, img_name);
 }
 
-std::string find_item(const std::string& item) {
+static std::string find_item(const std::string& item) {
     for (size_t i = 0; i < arraysize(images); ++i) {
         if (images[i].nickname && item == images[i].nickname) {
             return find_item_given_name(images[i].img_name);
@@ -1262,12 +1262,10 @@
 static void do_oem_command(std::vector<std::string>* args) {
     if (args->empty()) syntax_error("empty oem command");
 
-    std::string command;
+    std::string command("oem");
     while (!args->empty()) {
-        if (!command.empty()) command += ' ';
-        command += next_arg(args);
+        command += " " + next_arg(args);
     }
-
     fb_queue_command(command.c_str(), "");
 }
 
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index c21139e..2b32201 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -486,8 +486,11 @@
         return nullptr;
     }
 
-    AvbSlotVerifyResult verify_result = avb_ops->AvbSlotVerify(
-        fs_mgr_get_slot_suffix(), avb_verifier->IsDeviceUnlocked(), &avb_handle->avb_slot_data_);
+    AvbSlotVerifyFlags flags = avb_verifier->IsDeviceUnlocked()
+                                   ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
+                                   : AVB_SLOT_VERIFY_FLAGS_NONE;
+    AvbSlotVerifyResult verify_result =
+        avb_ops->AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->avb_slot_data_);
 
     // Only allow two verify results:
     //   - AVB_SLOT_VERIFY_RESULT_OK.
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index 8feeb53..512839b 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -177,13 +177,15 @@
 }
 
 AvbSlotVerifyResult FsManagerAvbOps::AvbSlotVerify(const std::string& ab_suffix,
-                                                   bool allow_verification_error,
+                                                   AvbSlotVerifyFlags flags,
                                                    AvbSlotVerifyData** out_data) {
     // Invokes avb_slot_verify() to load and verify all vbmeta images.
     // Sets requested_partitions to nullptr as it's to copy the contents
     // of HASH partitions into handle>avb_slot_data_, which is not required as
     // fs_mgr only deals with HASHTREE partitions.
     const char* requested_partitions[] = {nullptr};
-    return avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(),
-                           allow_verification_error, out_data);
+    // The |hashtree_error_mode| field doesn't matter as it only
+    // influences the generated kernel cmdline parameters.
+    return avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(), flags,
+                           AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, out_data);
 }
diff --git a/fs_mgr/fs_mgr_priv_avb_ops.h b/fs_mgr/fs_mgr_priv_avb_ops.h
index a6b52e4..d1ef2e9 100644
--- a/fs_mgr/fs_mgr_priv_avb_ops.h
+++ b/fs_mgr/fs_mgr_priv_avb_ops.h
@@ -56,7 +56,7 @@
     AvbIOResult ReadFromPartition(const char* partition, int64_t offset, size_t num_bytes,
                                   void* buffer, size_t* out_num_read);
 
-    AvbSlotVerifyResult AvbSlotVerify(const std::string& ab_suffix, bool allow_verification_error,
+    AvbSlotVerifyResult AvbSlotVerify(const std::string& ab_suffix, AvbSlotVerifyFlags flags,
                                       AvbSlotVerifyData** out_data);
 
   private:
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 9c07472..786fb14 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -31,6 +31,7 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/stringprintf.h>
 #include <gtest/gtest.h>
 #include <log/event_tag_map.h>
 #include <log/log.h>
@@ -80,8 +81,13 @@
     logcat_define(ctx);
 
 #undef LOG_TAG
-#define LOG_TAG "inject"
-    RLOGE(logcat_executable ".buckets");
+#define LOG_TAG "inject.buckets"
+    // inject messages into radio, system, main and events buffers to
+    // ensure that we see all the begin[] bucket messages.
+    RLOGE(logcat_executable);
+    SLOGE(logcat_executable);
+    ALOGE(logcat_executable);
+    __android_log_bswrite(0, logcat_executable ".inject.buckets");
     rest();
 
     ASSERT_TRUE(NULL !=
@@ -107,32 +113,45 @@
 
     logcat_pclose(ctx, fp);
 
-    EXPECT_EQ(15, ids);
+    EXPECT_EQ(ids, 15);
 
-    EXPECT_EQ(4, count);
+    EXPECT_EQ(count, 4);
 }
 
 TEST(logcat, event_tag_filter) {
     FILE* fp;
     logcat_define(ctx);
 
-    ASSERT_TRUE(NULL !=
-                (fp = logcat_popen(ctx, logcat_executable
-                                   " -b events -d -s auditd "
-                                   "am_proc_start am_pss am_proc_bound "
-                                   "dvm_lock_sample am_wtf 2>/dev/null")));
+#undef LOG_TAG
+#define LOG_TAG "inject.filter"
+    // inject messages into radio, system and main buffers
+    // with our unique log tag to test logcat filter.
+    RLOGE(logcat_executable);
+    SLOGE(logcat_executable);
+    ALOGE(logcat_executable);
+    rest();
+
+    std::string command = android::base::StringPrintf(
+        logcat_executable
+        " -b radio -b system -b main --pid=%d -d -s inject.filter 2>/dev/null",
+        getpid());
+    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, command.c_str())));
 
     char buffer[BIG_BUFFER];
 
     int count = 0;
 
     while (fgets(buffer, sizeof(buffer), fp)) {
-        ++count;
+        if (strncmp(begin, buffer, sizeof(begin) - 1)) ++count;
     }
 
     logcat_pclose(ctx, fp);
 
-    EXPECT_LT(4, count);
+    // logcat, liblogcat and logcatd test instances result in the progression
+    // of 3, 6 and 9 for our counts as each round is performed.
+    EXPECT_GE(count, 3);
+    EXPECT_LE(count, 9);
+    EXPECT_EQ(count % 3, 0);
 }
 
 // If there is not enough background noise in the logs, then spam the logs to
@@ -376,7 +395,7 @@
 
     } while ((count < 10) && --tries && inject(10 - count));
 
-    EXPECT_EQ(10, count);  // We want _some_ history, too small, falses below
+    EXPECT_EQ(count, 10);  // We want _some_ history, too small, falses below
     EXPECT_TRUE(last_timestamp != NULL);
     EXPECT_TRUE(first_timestamp != NULL);
     EXPECT_TRUE(second_timestamp != NULL);
@@ -701,9 +720,9 @@
 
     pclose(fp);
 
-    EXPECT_LE(2, count);
+    EXPECT_GE(count, 2);
 
-    EXPECT_EQ(1, signals);
+    EXPECT_EQ(signals, 1);
 }
 
 static void caught_blocking_tail(int signum) {
@@ -771,9 +790,9 @@
 
     pclose(fp);
 
-    EXPECT_LE(2, count);
+    EXPECT_GE(count, 2);
 
-    EXPECT_EQ(1, signals);
+    EXPECT_EQ(signals, 1);
 }
 #endif
 
@@ -881,7 +900,7 @@
             ADD_FAILURE();
         }
         pclose(fp);
-        EXPECT_EQ(11, log_file_count);
+        EXPECT_EQ(log_file_count, 11);
     }
     snprintf(command, sizeof(command), "rm -rf %s", tmp_out_dir);
     EXPECT_FALSE(IsFalse(system(command), command));
@@ -1134,17 +1153,17 @@
     char tmp_out_dir[strlen(tmp_out_dir_form) + 1];
     ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
 
-    EXPECT_EQ(34, logrotate_count_id(logcat_cmd, tmp_out_dir));
-    EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+    EXPECT_EQ(logrotate_count_id(logcat_cmd, tmp_out_dir), 34);
+    EXPECT_EQ(logrotate_count_id(logcat_short_cmd, tmp_out_dir), 34);
 
     char id_file[strlen(tmp_out_dir_form) + strlen(log_filename) + 5];
     snprintf(id_file, sizeof(id_file), "%s/%s.id", tmp_out_dir, log_filename);
     if (getuid() != 0) {
         chmod(id_file, 0);
-        EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+        EXPECT_EQ(logrotate_count_id(logcat_short_cmd, tmp_out_dir), 34);
     }
     unlink(id_file);
-    EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+    EXPECT_EQ(logrotate_count_id(logcat_short_cmd, tmp_out_dir), 34);
 
     FILE* fp = fopen(id_file, "w");
     if (fp) {
@@ -1159,9 +1178,9 @@
     }
 
     int new_signature;
-    EXPECT_LE(
-        2, (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir)));
-    EXPECT_GT(34, new_signature);
+    EXPECT_GE(
+        (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir)), 2);
+    EXPECT_LT(new_signature, 34);
 
     static const char cleanup_cmd[] = "rm -rf %s";
     char command[strlen(cleanup_cmd) + strlen(tmp_out_dir_form)];
@@ -1302,10 +1321,10 @@
 
     pclose(fp);
 
-    EXPECT_LE(1, count);
-    EXPECT_EQ(1, minus_g);
+    EXPECT_GE(count, 1);
+    EXPECT_EQ(minus_g, 1);
 
-    EXPECT_EQ(1, signals);
+    EXPECT_EQ(signals, 1);
 }
 #endif
 
@@ -1560,7 +1579,7 @@
         ctx << theAnswer;
         // crafted to rest at least once after, and rest between retries.
         for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-        EXPECT_LE(0, ret);
+        EXPECT_GE(ret, 0);
         EXPECT_TRUE(
             End_to_End(hhgtg.tagStr, "to life the universe etc=%s", theAnswer));
     }
@@ -1572,7 +1591,7 @@
             android_log_event_list ctx(sync.tagNo);
             ctx << id << (int32_t)42 << (int32_t)-1 << (int32_t)0;
             for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-            EXPECT_LE(0, ret);
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr,
                                    "[id=%s,event=42,source=-1,account=0]", id));
         }
@@ -1582,7 +1601,7 @@
             android_log_event_list ctx(sync.tagNo);
             ctx << id << (int32_t)43 << (int64_t)-1 << (int32_t)0;
             for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-            EXPECT_LE(0, ret);
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "[id=%s,event=43,-1,0]", id));
         }
 
@@ -1591,7 +1610,7 @@
             android_log_event_list ctx(sync.tagNo);
             ctx << id << (int32_t)44 << (int32_t)-1 << (int64_t)0;
             for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-            EXPECT_LE(0, ret);
+            EXPECT_GE(ret, 0);
             fprintf(stderr, "Expect a \"Closest match\" message\n");
             EXPECT_FALSE(End_to_End(
                 sync.tagStr, "[id=%s,event=44,source=-1,account=0]", id));
@@ -1604,7 +1623,7 @@
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint64_t)30 << (int32_t)2;
             for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-            EXPECT_LE(0, ret);
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(
                 End_to_End(sync.tagStr, "[aggregation time=30ms,count=2]"));
         }
@@ -1613,7 +1632,7 @@
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint64_t)31570 << (int32_t)911;
             for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-            EXPECT_LE(0, ret);
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(
                 End_to_End(sync.tagStr, "[aggregation time=31.57s,count=911]"));
         }
@@ -1625,7 +1644,7 @@
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)512;
             for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-            EXPECT_LE(0, ret);
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=512B"));
         }
 
@@ -1633,7 +1652,7 @@
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)3072;
             for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-            EXPECT_LE(0, ret);
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=3KB"));
         }
 
@@ -1641,7 +1660,7 @@
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)2097152;
             for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-            EXPECT_LE(0, ret);
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=2MB"));
         }
 
@@ -1649,7 +1668,7 @@
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)2097153;
             for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-            EXPECT_LE(0, ret);
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=2097153B"));
         }
 
@@ -1657,7 +1676,7 @@
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)1073741824;
             for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-            EXPECT_LE(0, ret);
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=1GB"));
         }
 
@@ -1665,7 +1684,7 @@
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)3221225472;  // 3MB, but on purpose overflowed
             for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-            EXPECT_LE(0, ret);
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=-1GB"));
         }
     }
@@ -1674,7 +1693,7 @@
         static const struct tag sync = { 27501, "notification_panel_hidden" };
         android_log_event_list ctx(sync.tagNo);
         for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-        EXPECT_LE(0, ret);
+        EXPECT_GE(ret, 0);
         EXPECT_TRUE(End_to_End(sync.tagStr, ""));
     }
 
@@ -1694,28 +1713,28 @@
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)7;
             for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-            EXPECT_LE(0, ret);
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "new=7s"));
         }
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)62;
             for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-            EXPECT_LE(0, ret);
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:02"));
         }
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)3673;
             for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-            EXPECT_LE(0, ret);
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:01:13"));
         }
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)(86400 + 7200 + 180 + 58);
             for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
-            EXPECT_LE(0, ret);
+            EXPECT_GE(ret, 0);
             EXPECT_TRUE(End_to_End(sync.tagStr, "new=1d 2:03:58"));
         }
     }
@@ -1756,7 +1775,7 @@
 
 TEST(logcat, help) {
     size_t logcatHelpTextSize = commandOutputSize(logcat_executable " -h 2>&1");
-    EXPECT_LT(4096UL, logcatHelpTextSize);
+    EXPECT_GT(logcatHelpTextSize, 4096UL);
     size_t logcatLastHelpTextSize =
         commandOutputSize(logcat_executable " -L -h 2>&1");
 #ifdef USING_LOGCAT_EXECUTABLE_DEFAULT  // logcat and liblogcat
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index e597754..f0d6fcb 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -159,9 +159,12 @@
     const char* pidToName(pid_t pid) {
         return stats.pidToName(pid);
     }
-    uid_t pidToUid(pid_t pid) {
+    virtual uid_t pidToUid(pid_t pid) override {
         return stats.pidToUid(pid);
     }
+    virtual pid_t tidToPid(pid_t tid) override {
+        return stats.tidToPid(tid);
+    }
     const char* uidToName(uid_t uid) {
         return stats.uidToName(uid);
     }
diff --git a/logd/LogBufferInterface.cpp b/logd/LogBufferInterface.cpp
index 3cb2b89..4b6d363 100644
--- a/logd/LogBufferInterface.cpp
+++ b/logd/LogBufferInterface.cpp
@@ -15,8 +15,15 @@
  */
 
 #include "LogBufferInterface.h"
+#include "LogUtils.h"
 
 LogBufferInterface::LogBufferInterface() {
 }
 LogBufferInterface::~LogBufferInterface() {
-}
\ No newline at end of file
+}
+uid_t LogBufferInterface::pidToUid(pid_t pid) {
+    return android::pidToUid(pid);
+}
+pid_t LogBufferInterface::tidToPid(pid_t tid) {
+    return android::tidToPid(tid);
+}
diff --git a/logd/LogBufferInterface.h b/logd/LogBufferInterface.h
index 7d82b91..ff73a22 100644
--- a/logd/LogBufferInterface.h
+++ b/logd/LogBufferInterface.h
@@ -33,6 +33,9 @@
     virtual int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
                     pid_t tid, const char* msg, unsigned short len) = 0;
 
+    virtual uid_t pidToUid(pid_t pid);
+    virtual pid_t tidToPid(pid_t tid);
+
    private:
     DISALLOW_COPY_AND_ASSIGN(LogBufferInterface);
 };
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index 3c0d08d..d2df68e 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
 #include <limits.h>
+#include <stdio.h>
 #include <sys/cdefs.h>
 #include <sys/prctl.h>
 #include <sys/socket.h>
@@ -72,8 +74,11 @@
         cmsg = CMSG_NXTHDR(&hdr, cmsg);
     }
 
+    struct ucred fake_cred;
     if (cred == NULL) {
-        return false;
+        cred = &fake_cred;
+        cred->pid = 0;
+        cred->uid = DEFAULT_OVERFLOWUID;
     }
 
     if (cred->uid == AID_LOGD) {
@@ -96,6 +101,27 @@
         return false;
     }
 
+    // Check credential validity, acquire corrected details if not supplied.
+    if (cred->pid == 0) {
+        cred->pid = logbuf ? logbuf->tidToPid(header->tid)
+                           : android::tidToPid(header->tid);
+        if (cred->pid == getpid()) {
+            // We expect that /proc/<tid>/ is accessible to self even without
+            // readproc group, so that we will always drop messages that come
+            // from any of our logd threads and their library calls.
+            return false;  // ignore self
+        }
+    }
+    if (cred->uid == DEFAULT_OVERFLOWUID) {
+        uid_t uid =
+            logbuf ? logbuf->pidToUid(cred->pid) : android::pidToUid(cred->pid);
+        if (uid == AID_LOGD) {
+            uid = logbuf ? logbuf->pidToUid(header->tid)
+                         : android::pidToUid(cred->pid);
+        }
+        if (uid != AID_LOGD) cred->uid = uid;
+    }
+
     char* msg = ((char*)buffer) + sizeof(android_log_header_t);
     n -= sizeof(android_log_header_t);
 
diff --git a/logd/LogListener.h b/logd/LogListener.h
index e16c5fb..a562a54 100644
--- a/logd/LogListener.h
+++ b/logd/LogListener.h
@@ -20,6 +20,16 @@
 #include <sysutils/SocketListener.h>
 #include "LogReader.h"
 
+// DEFAULT_OVERFLOWUID is defined in linux/highuid.h, which is not part of
+// the uapi headers for userspace to use.  This value is filled in on the
+// out-of-band socket credentials if the OS fails to find one available.
+// One of the causes of this is if SO_PASSCRED is set, all the packets before
+// that point will have this value.  We also use it in a fake credential if
+// no socket credentials are supplied.
+#ifndef DEFAULT_OVERFLOWUID
+#define DEFAULT_OVERFLOWUID 65534
+#endif
+
 class LogListener : public SocketListener {
     LogBufferInterface* logbuf;
     LogReader* reader;
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index d20d90e..af59ddc 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <pwd.h>
@@ -824,8 +825,10 @@
     FILE* fp = fopen(buffer, "r");
     if (fp) {
         while (fgets(buffer, sizeof(buffer), fp)) {
-            int uid;
-            if (sscanf(buffer, "Uid: %d", &uid) == 1) {
+            int uid = AID_LOGD;
+            char space = 0;
+            if ((sscanf(buffer, "Uid: %d%c", &uid, &space) == 2) &&
+                isspace(space)) {
                 fclose(fp);
                 return uid;
             }
@@ -834,12 +837,35 @@
     }
     return AID_LOGD;  // associate this with the logger
 }
+
+pid_t tidToPid(pid_t tid) {
+    char buffer[512];
+    snprintf(buffer, sizeof(buffer), "/proc/%u/status", tid);
+    FILE* fp = fopen(buffer, "r");
+    if (fp) {
+        while (fgets(buffer, sizeof(buffer), fp)) {
+            int pid = tid;
+            char space = 0;
+            if ((sscanf(buffer, "Tgid: %d%c", &pid, &space) == 2) &&
+                isspace(space)) {
+                fclose(fp);
+                return pid;
+            }
+        }
+        fclose(fp);
+    }
+    return tid;
+}
 }
 
 uid_t LogStatistics::pidToUid(pid_t pid) {
     return pidTable.add(pid)->second.getUid();
 }
 
+pid_t LogStatistics::tidToPid(pid_t tid) {
+    return tidTable.add(tid)->second.getPid();
+}
+
 // caller must free character string
 const char* LogStatistics::pidToName(pid_t pid) const {
     // An inconvenient truth ... getName() can alter the object
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 945fc0a..8808aac 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -306,10 +306,6 @@
     std::string format(const LogStatistics& stat, log_id_t id) const;
 };
 
-namespace android {
-uid_t pidToUid(pid_t pid);
-}
-
 struct PidEntry : public EntryBaseDropped {
     const pid_t pid;
     uid_t uid;
@@ -389,6 +385,13 @@
           uid(android::pidToUid(tid)),
           name(android::tidToName(tid)) {
     }
+    TidEntry(pid_t tid)
+        : EntryBaseDropped(),
+          tid(tid),
+          pid(android::tidToPid(tid)),
+          uid(android::pidToUid(tid)),
+          name(android::tidToName(tid)) {
+    }
     explicit TidEntry(const LogBufferElement* element)
         : EntryBaseDropped(element),
           tid(element->getTid()),
@@ -785,6 +788,7 @@
     // helper (must be locked directly or implicitly by mLogElementsLock)
     const char* pidToName(pid_t pid) const;
     uid_t pidToUid(pid_t pid);
+    pid_t tidToPid(pid_t tid);
     const char* uidToName(uid_t uid) const;
 };
 
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index fa9f398..4dcd3e7 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -38,6 +38,8 @@
 // Caller must own and free returned value
 char* pidToName(pid_t pid);
 char* tidToName(pid_t tid);
+uid_t pidToUid(pid_t pid);
+pid_t tidToPid(pid_t tid);
 
 // Furnished in LogTags.cpp. Thread safe.
 const char* tagToName(uint32_t tag);