logd: liblog: logcat: Add LogWhiteBlackList

- liblog android_logger_get_log_size and android_logger_get_readable_size
  adjusted to return long instead of int because of -G flag extending range

NB: ifdef'd only for userdebug and eng builds

- liblog Add android_logger_[sg]et_prune_list and android_logger_set_log_size
- logcat Add -P, -p and -G flags
- logd Add LogWhiteBlackList and configurable log size

(cherry picked from commit 18a5432158ad43b8faefe4950b30e760200ce0b4)

Change-Id: I1572338c1b34bd968ad7867857ef708156ec3b6a
diff --git a/logd/Android.mk b/logd/Android.mk
index 3dd8e0f..b0bc746 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -4,6 +4,10 @@
 
 LOCAL_MODULE:= logd
 
+ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),)
+LOCAL_CFLAGS += -DUSERDEBUG_BUILD=1
+endif
+
 LOCAL_SRC_FILES := \
     main.cpp \
     LogCommand.cpp \
@@ -14,7 +18,8 @@
     LogBuffer.cpp \
     LogBufferElement.cpp \
     LogTimes.cpp \
-    LogStatistics.cpp
+    LogStatistics.cpp \
+    LogWhiteBlackList.cpp
 
 LOCAL_SHARED_LIBRARIES := \
     libsysutils \
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 43a283c..12b10ca 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -37,8 +37,15 @@
     // registerCmd(new ShutdownCmd(buf, writer, swl));
     registerCmd(new ClearCmd(buf));
     registerCmd(new GetBufSizeCmd(buf));
+#ifdef USERDEBUG_BUILD
+    registerCmd(new SetBufSizeCmd(buf));
+#endif
     registerCmd(new GetBufSizeUsedCmd(buf));
     registerCmd(new GetStatisticsCmd(buf));
+#ifdef USERDEBUG_BUILD
+    registerCmd(new SetPruneListCmd(buf));
+    registerCmd(new GetPruneListCmd(buf));
+#endif
 }
 
 CommandListener::ShutdownCmd::ShutdownCmd(LogBuffer *buf, LogReader *reader,
@@ -110,6 +117,43 @@
     return 0;
 }
 
+#ifdef USERDEBUG_BUILD
+
+CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer *buf)
+        : LogCommand("setLogSize")
+        , mBuf(*buf)
+{ }
+
+int CommandListener::SetBufSizeCmd::runCommand(SocketClient *cli,
+                                         int argc, char **argv) {
+    if (!clientHasLogCredentials(cli)) {
+        cli->sendMsg("Permission Denied");
+        return 0;
+    }
+
+    if (argc < 3) {
+        cli->sendMsg("Missing Argument");
+        return 0;
+    }
+
+    int id = atoi(argv[1]);
+    if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+        cli->sendMsg("Range Error");
+        return 0;
+    }
+
+    unsigned long size = atol(argv[2]);
+    if (mBuf.setSize((log_id_t) id, size)) {
+        cli->sendMsg("Range Error");
+        return 0;
+    }
+
+    cli->sendMsg("success");
+    return 0;
+}
+
+#endif // USERDEBUG_BUILD
+
 CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer *buf)
         : LogCommand("getLogSizeUsed")
         , mBuf(*buf)
@@ -140,6 +184,24 @@
         , mBuf(*buf)
 { }
 
+static void package_string(char **strp) {
+    const char *a = *strp;
+    if (!a) {
+        a = "";
+    }
+
+    // Calculate total buffer size prefix, count is the string length w/o nul
+    char fmt[32];
+    for(size_t l = strlen(a), y = 0, x = 6; y != x; y = x, x = strlen(fmt) - 2) {
+        snprintf(fmt, sizeof(fmt), "%zu\n%%s\n\f", l + x);
+    }
+
+    char *b = *strp;
+    *strp = NULL;
+    asprintf(strp, fmt, a);
+    free(b);
+}
+
 int CommandListener::GetStatisticsCmd::runCommand(SocketClient *cli,
                                          int argc, char **argv) {
     uid_t uid = cli->getUid();
@@ -167,8 +229,69 @@
     if (!buf) {
         cli->sendMsg("Failed");
     } else {
+        package_string(&buf);
         cli->sendMsg(buf);
         free(buf);
     }
     return 0;
 }
+
+#ifdef USERDEBUG_BUILD
+
+CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer *buf)
+        : LogCommand("getPruneList")
+        , mBuf(*buf)
+{ }
+
+int CommandListener::GetPruneListCmd::runCommand(SocketClient *cli,
+                                         int /*argc*/, char ** /*argv*/) {
+    char *buf = NULL;
+    mBuf.formatPrune(&buf);
+    if (!buf) {
+        cli->sendMsg("Failed");
+    } else {
+        package_string(&buf);
+        cli->sendMsg(buf);
+        free(buf);
+    }
+    return 0;
+}
+
+CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer *buf)
+        : LogCommand("setPruneList")
+        , mBuf(*buf)
+{ }
+
+int CommandListener::SetPruneListCmd::runCommand(SocketClient *cli,
+                                         int argc, char **argv) {
+    if (!clientHasLogCredentials(cli)) {
+        cli->sendMsg("Permission Denied");
+        return 0;
+    }
+
+    char *cp = NULL;
+    for (int i = 1; i < argc; ++i) {
+        char *p = cp;
+        if (p) {
+            cp = NULL;
+            asprintf(&cp, "%s %s", p, argv[i]);
+            free(p);
+        } else {
+            asprintf(&cp, "%s", argv[i]);
+        }
+    }
+
+    int ret = mBuf.initPrune(cp);
+    free(cp);
+
+    if (ret) {
+        cli->sendMsg("Invalid");
+        return 0;
+    }
+
+    cli->sendMsg("success");
+
+    return 0;
+}
+
+#endif // USERDEBUG_BUILD
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index a841610..de1dcb9 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -53,8 +53,15 @@
 
     LogBufferCmd(Clear)
     LogBufferCmd(GetBufSize)
+#ifdef USERDEBUG_BUILD
+    LogBufferCmd(SetBufSize)
+#endif
     LogBufferCmd(GetBufSizeUsed)
     LogBufferCmd(GetStatistics)
+#ifdef USERDEBUG_BUILD
+    LogBufferCmd(GetPruneList)
+    LogBufferCmd(SetPruneList)
+#endif
 };
 
 #endif
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index cf172d1..197b7e8 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -23,13 +23,25 @@
 
 #include "LogBuffer.h"
 #include "LogStatistics.h"
+#include "LogWhiteBlackList.h"
 #include "LogReader.h"
 
+// Default
 #define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here?
+#ifdef USERDEBUG_BUILD
+#define log_buffer_size(id) mMaxSize[id]
+#else
+#define log_buffer_size(id) LOG_BUFFER_SIZE
+#endif
 
 LogBuffer::LogBuffer(LastLogTimes *times)
         : mTimes(*times) {
     pthread_mutex_init(&mLogElementsLock, NULL);
+#ifdef USERDEBUG_BUILD
+    log_id_for_each(i) {
+        mMaxSize[i] = LOG_BUFFER_SIZE;
+    }
+#endif
 }
 
 void LogBuffer::log(log_id_t log_id, log_time realtime,
@@ -100,8 +112,8 @@
 // mLogElementsLock must be held when this function is called.
 void LogBuffer::maybePrune(log_id_t id) {
     size_t sizes = stats.sizes(id);
-    if (sizes > LOG_BUFFER_SIZE) {
-        size_t sizeOver90Percent = sizes - ((LOG_BUFFER_SIZE * 9) / 10);
+    if (sizes > log_buffer_size(id)) {
+        size_t sizeOver90Percent = sizes - ((log_buffer_size(id) * 9) / 10);
         size_t elements = stats.elements(id);
         unsigned long pruneRows = elements * sizeOver90Percent / sizes;
         elements /= 10;
@@ -140,18 +152,23 @@
         size_t worst_sizes = 0;
         size_t second_worst_sizes = 0;
 
-        LidStatistics &l = stats.id(id);
-        UidStatisticsCollection::iterator iu;
-        for (iu = l.begin(); iu != l.end(); ++iu) {
-            UidStatistics *u = (*iu);
-            size_t sizes = u->sizes();
-            if (worst_sizes < sizes) {
-                second_worst_sizes = worst_sizes;
-                worst_sizes = sizes;
-                worst = u->getUid();
-            }
-            if ((second_worst_sizes < sizes) && (sizes < worst_sizes)) {
-                second_worst_sizes = sizes;
+#ifdef USERDEBUG_BUILD
+        if (mPrune.worstUidEnabled())
+#endif
+        {
+            LidStatistics &l = stats.id(id);
+            UidStatisticsCollection::iterator iu;
+            for (iu = l.begin(); iu != l.end(); ++iu) {
+                UidStatistics *u = (*iu);
+                size_t sizes = u->sizes();
+                if (worst_sizes < sizes) {
+                    second_worst_sizes = worst_sizes;
+                    worst_sizes = sizes;
+                    worst = u->getUid();
+                }
+                if ((second_worst_sizes < sizes) && (sizes < worst_sizes)) {
+                    second_worst_sizes = sizes;
+                }
             }
         }
 
@@ -163,7 +180,14 @@
                 break;
             }
 
-            if ((e->getLogId() == id) && (e->getUid() == worst)) {
+            if (e->getLogId() != id) {
+                ++it;
+                continue;
+            }
+
+            uid_t uid = e->getUid();
+
+            if (uid == worst) {
                 it = mLogElements.erase(it);
                 unsigned short len = e->getMsgLen();
                 stats.subtract(len, id, worst, e->getPid());
@@ -174,29 +198,60 @@
                     break;
                 }
                 worst_sizes -= len;
-            } else {
+            }
+#ifdef USERDEBUG_BUILD
+            else if (mPrune.naughty(e)) { // BlackListed
+                it = mLogElements.erase(it);
+                stats.subtract(e->getMsgLen(), id, uid, e->getPid());
+                delete e;
+                pruneRows--;
+                if (pruneRows == 0) {
+                    break;
+                }
+            }
+#endif
+            else {
                 ++it;
             }
         }
 
-        if (!kick) {
+        if (!kick
+#ifdef USERDEBUG_BUILD
+                || !mPrune.worstUidEnabled()
+#endif
+        ) {
             break; // the following loop will ask bad clients to skip/drop
         }
     }
 
+#ifdef USERDEBUG_BUILD
+    bool whitelist = false;
+#endif
     it = mLogElements.begin();
     while((pruneRows > 0) && (it != mLogElements.end())) {
         LogBufferElement *e = *it;
         if (e->getLogId() == id) {
             if (oldest && (oldest->mStart <= e->getMonotonicTime())) {
-                if (stats.sizes(id) > (2 * LOG_BUFFER_SIZE)) {
-                    // kick a misbehaving log reader client off the island
-                    oldest->release_Locked();
-                } else {
-                    oldest->triggerSkip_Locked(pruneRows);
+#ifdef USERDEBUG_BUILD
+                if (!whitelist)
+#endif
+                {
+                    if (stats.sizes(id) > (2 * log_buffer_size(id))) {
+                        // kick a misbehaving log reader client off the island
+                        oldest->release_Locked();
+                    } else {
+                        oldest->triggerSkip_Locked(pruneRows);
+                    }
                 }
                 break;
             }
+#ifdef USERDEBUG_BUILD
+            if (mPrune.nice(e)) { // WhiteListed
+                whitelist = true;
+                it++;
+                continue;
+            }
+#endif
             it = mLogElements.erase(it);
             stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid());
             delete e;
@@ -206,6 +261,32 @@
         }
     }
 
+#ifdef USERDEBUG_BUILD
+    if (whitelist && (pruneRows > 0)) {
+        it = mLogElements.begin();
+        while((it != mLogElements.end()) && (pruneRows > 0)) {
+            LogBufferElement *e = *it;
+            if (e->getLogId() == id) {
+                if (oldest && (oldest->mStart <= e->getMonotonicTime())) {
+                    if (stats.sizes(id) > (2 * log_buffer_size(id))) {
+                        // kick a misbehaving log reader client off the island
+                        oldest->release_Locked();
+                    } else {
+                        oldest->triggerSkip_Locked(pruneRows);
+                    }
+                    break;
+                }
+                it = mLogElements.erase(it);
+                stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid());
+                delete e;
+                pruneRows--;
+            } else {
+                it++;
+            }
+        }
+    }
+#endif
+
     LogTimeEntry::unlock();
 }
 
@@ -224,11 +305,37 @@
     return retval;
 }
 
+#ifdef USERDEBUG_BUILD
+
+// set the total space allocated to "id"
+int LogBuffer::setSize(log_id_t id, unsigned long size) {
+    // Reasonable limits ...
+    if ((size < (64 * 1024)) || ((256 * 1024 * 1024) < size)) {
+        return -1;
+    }
+    pthread_mutex_lock(&mLogElementsLock);
+    log_buffer_size(id) = size;
+    pthread_mutex_unlock(&mLogElementsLock);
+    return 0;
+}
+
+// get the total space allocated to "id"
+unsigned long LogBuffer::getSize(log_id_t id) {
+    pthread_mutex_lock(&mLogElementsLock);
+    size_t retval = log_buffer_size(id);
+    pthread_mutex_unlock(&mLogElementsLock);
+    return retval;
+}
+
+#else // ! USERDEBUG_BUILD
+
 // get the total space allocated to "id"
 unsigned long LogBuffer::getSize(log_id_t /*id*/) {
-    return LOG_BUFFER_SIZE;
+    return log_buffer_size(id);
 }
 
+#endif
+
 log_time LogBuffer::flushTo(
         SocketClient *reader, const log_time start, bool privileged,
         bool (*filter)(const LogBufferElement *element, void *arg), void *arg) {
@@ -269,7 +376,7 @@
     return max;
 }
 
-size_t LogBuffer::formatStatistics(char **strp, uid_t uid, unsigned int logMask) {
+void LogBuffer::formatStatistics(char **strp, uid_t uid, unsigned int logMask) {
     log_time oldest(CLOCK_MONOTONIC);
 
     pthread_mutex_lock(&mLogElementsLock);
@@ -285,9 +392,7 @@
         }
     }
 
-    size_t ret = stats.format(strp, uid, logMask, oldest);
+    stats.format(strp, uid, logMask, oldest);
 
     pthread_mutex_unlock(&mLogElementsLock);
-
-    return ret;
 }
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 92dd107..0745e56 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -26,6 +26,7 @@
 #include "LogBufferElement.h"
 #include "LogTimes.h"
 #include "LogStatistics.h"
+#include "LogWhiteBlackList.h"
 
 typedef android::List<LogBufferElement *> LogBufferElementCollection;
 
@@ -35,6 +36,12 @@
 
     LogStatistics stats;
 
+#ifdef USERDEBUG_BUILD
+    PruneList mPrune;
+
+    unsigned long mMaxSize[LOG_ID_MAX];
+#endif
+
 public:
     LastLogTimes &mTimes;
 
@@ -49,9 +56,18 @@
 
     void clear(log_id_t id);
     unsigned long getSize(log_id_t id);
+#ifdef USERDEBUG_BUILD
+    int setSize(log_id_t id, unsigned long size);
+#endif
     unsigned long getSizeUsed(log_id_t id);
     // *strp uses malloc, use free to release.
-    size_t formatStatistics(char **strp, uid_t uid, unsigned int logMask);
+    void formatStatistics(char **strp, uid_t uid, unsigned int logMask);
+
+#ifdef USERDEBUG_BUILD
+    int initPrune(char *cp) { return mPrune.init(cp); }
+    // *strp uses malloc, use free to release.
+    void formatPrune(char **strp) { mPrune.format(strp); }
+#endif
 
 private:
     void maybePrune(log_id_t id);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 50ce442..49ee50d 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -335,8 +335,8 @@
     return elements;
 }
 
-size_t LogStatistics::format(char **buf,
-                             uid_t uid, unsigned int logMask, log_time oldest) {
+void LogStatistics::format(char **buf,
+                           uid_t uid, unsigned int logMask, log_time oldest) {
     const unsigned short spaces_current = 13;
     const unsigned short spaces_total = 19;
 
@@ -551,20 +551,5 @@
         }
     }
 
-    // Calculate total buffer size prefix
-    char re_fmt[32];
-    size_t ret;
-    for(size_t l = string.length(), y = 0, x = 6;
-           y != x;
-           y = x, x = strlen(re_fmt) - 2) {
-       snprintf(re_fmt, sizeof(re_fmt), "%zu\n%%s\n\f", l + x);
-       ret = l + x;
-    }
-
-    android::String8 intermediate = string.format(re_fmt, string.string());
-    string.clear();
-
-    *buf = strdup(intermediate.string());
-
-    return ret;
+    *buf = strdup(string.string());
 }
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index c8eeb45..d44afa2 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -146,7 +146,7 @@
                          pid_t pid = pid_all);
 
     // *strp = malloc, balance with free
-    size_t format(char **strp, uid_t uid, unsigned int logMask, log_time oldest);
+    void format(char **strp, uid_t uid, unsigned int logMask, log_time oldest);
 };
 
 #endif // _LOGD_LOG_STATISTICS_H__
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
new file mode 100644
index 0000000..d0ceb9f
--- /dev/null
+++ b/logd/LogWhiteBlackList.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef USERDEBUG_BUILD
+
+#include <ctype.h>
+
+#include <utils/String8.h>
+
+#include "LogWhiteBlackList.h"
+
+// White and Black list
+
+Prune::Prune(uid_t uid, pid_t pid)
+        : mUid(uid)
+        , mPid(pid)
+{ }
+
+int Prune::cmp(uid_t uid, pid_t pid) const {
+    if ((mUid == uid_all) || (mUid == uid)) {
+        if (mPid == pid_all) {
+            return 0;
+        }
+        return pid - mPid;
+    }
+    return uid - mUid;
+}
+
+void Prune::format(char **strp) {
+    if (mUid != uid_all) {
+        asprintf(strp, (mPid != pid_all) ? "%u/%u" : "%u", mUid, mPid);
+    } else {
+        // NB: mPid == pid_all can not happen if mUid == uid_all
+        asprintf(strp, (mPid != pid_all) ? "/%u" : "/", mPid);
+    }
+}
+
+PruneList::PruneList()
+        : mWorstUidEnabled(true) {
+    mNaughty.clear();
+    mNice.clear();
+}
+
+PruneList::~PruneList() {
+    PruneCollection::iterator it;
+    for (it = mNice.begin(); it != mNice.end();) {
+        delete (*it);
+        it = mNice.erase(it);
+    }
+    for (it = mNaughty.begin(); it != mNaughty.end();) {
+        delete (*it);
+        it = mNaughty.erase(it);
+    }
+}
+
+int PruneList::init(char *str) {
+    mWorstUidEnabled = true;
+    PruneCollection::iterator it;
+    for (it = mNice.begin(); it != mNice.end();) {
+        delete (*it);
+        it = mNice.erase(it);
+    }
+    for (it = mNaughty.begin(); it != mNaughty.end();) {
+        delete (*it);
+        it = mNaughty.erase(it);
+    }
+
+    if (!str) {
+        return 0;
+    }
+
+    mWorstUidEnabled = false;
+
+    for(; *str; ++str) {
+        if (isspace(*str)) {
+            continue;
+        }
+
+        PruneCollection *list;
+        if ((*str == '~') || (*str == '!')) { // ~ supported, ! undocumented
+            ++str;
+            // special case, translates to worst UID at priority in blacklist
+            if (*str == '!') {
+                mWorstUidEnabled = true;
+                ++str;
+                if (!*str) {
+                    break;
+                }
+                if (!isspace(*str)) {
+                    return 1;
+                }
+                continue;
+            }
+            if (!*str) {
+                return 1;
+            }
+            list = &mNaughty;
+        } else {
+            list = &mNice;
+        }
+
+        uid_t uid = Prune::uid_all;
+        if (isdigit(*str)) {
+            uid = 0;
+            do {
+                uid = uid * 10 + *str++ - '0';
+            } while (isdigit(*str));
+        }
+
+        pid_t pid = Prune::pid_all;
+        if (*str == '/') {
+            ++str;
+            if (isdigit(*str)) {
+                pid = 0;
+                do {
+                    pid = pid * 10 + *str++ - '0';
+                } while (isdigit(*str));
+            }
+        }
+
+        if ((uid == Prune::uid_all) && (pid == Prune::pid_all)) {
+            return 1;
+        }
+
+        if (*str && !isspace(*str)) {
+            return 1;
+        }
+
+        // insert sequentially into list
+        PruneCollection::iterator it = list->begin();
+        while (it != list->end()) {
+            Prune *p = *it;
+            int m = uid - p->mUid;
+            if (m == 0) {
+                if (p->mPid == p->pid_all) {
+                    break;
+                }
+                if ((pid == p->pid_all) && (p->mPid != p->pid_all)) {
+                    it = list->erase(it);
+                    continue;
+                }
+                m = pid - p->mPid;
+            }
+            if (m >= 0) {
+                if (m > 0) {
+                    list->insert(it, new Prune(uid,pid));
+                }
+                break;
+            }
+            ++it;
+        }
+        if (it == list->end()) {
+            list->push_back(new Prune(uid,pid));
+        }
+        if (!*str) {
+            break;
+        }
+    }
+
+    return 0;
+}
+
+void PruneList::format(char **strp) {
+    if (*strp) {
+        free(*strp);
+        *strp = NULL;
+    }
+
+    static const char nice_format[] = " %s";
+    const char *fmt = nice_format + 1;
+
+    android::String8 string;
+
+    if (mWorstUidEnabled) {
+        string.setTo("~!");
+        fmt = nice_format;
+    }
+
+    PruneCollection::iterator it;
+
+    for (it = mNice.begin(); it != mNice.end(); ++it) {
+        char *a = NULL;
+        (*it)->format(&a);
+
+        string.appendFormat(fmt, a);
+        fmt = nice_format;
+
+        free(a);
+    }
+
+    static const char naughty_format[] = " ~%s";
+    fmt = naughty_format + (*fmt != ' ');
+    for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
+        char *a = NULL;
+        (*it)->format(&a);
+
+        string.appendFormat(fmt, a);
+        fmt = naughty_format;
+
+        free(a);
+    }
+
+    *strp = strdup(string.string());
+}
+
+// ToDo: Lists are in sorted order, Prune->cmp() returns + or -
+// If there is scaling issues, resort to a better algorithm than linear
+// based on these assumptions.
+
+bool PruneList::naughty(LogBufferElement *element) {
+    PruneCollection::iterator it;
+    for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
+        if (!(*it)->cmp(element)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool PruneList::nice(LogBufferElement *element) {
+    PruneCollection::iterator it;
+    for (it = mNice.begin(); it != mNice.end(); ++it) {
+        if (!(*it)->cmp(element)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+#endif // USERDEBUG_BUILD
diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h
new file mode 100644
index 0000000..769d651
--- /dev/null
+++ b/logd/LogWhiteBlackList.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_WHITE_BLACK_LIST_H__
+#define _LOGD_LOG_WHITE_BLACK_LIST_H__
+
+#include <sys/types.h>
+
+#include <utils/List.h>
+
+#include <LogBufferElement.h>
+
+// White and Blacklist
+
+class Prune {
+    friend class PruneList;
+
+    const uid_t mUid;
+    const pid_t mPid;
+    int cmp(uid_t uid, pid_t pid) const;
+
+public:
+    static const uid_t uid_all = (uid_t) -1;
+    static const pid_t pid_all = (pid_t) -1;
+
+    Prune(uid_t uid, pid_t pid);
+
+    uid_t getUid() const { return mUid; }
+    pid_t getPid() const { return mPid; }
+
+    int cmp(LogBufferElement *e) const { return cmp(e->getUid(), e->getPid()); }
+
+    // *strp is malloc'd, use free to release
+    void format(char **strp);
+};
+
+typedef android::List<Prune *> PruneCollection;
+
+class PruneList {
+    PruneCollection mNaughty;
+    PruneCollection mNice;
+    bool mWorstUidEnabled;
+
+public:
+    PruneList();
+    ~PruneList();
+
+    int init(char *str);
+
+    bool naughty(LogBufferElement *element);
+    bool nice(LogBufferElement *element);
+    bool worstUidEnabled() const { return mWorstUidEnabled; }
+
+    // *strp is malloc'd, use free to release
+    void format(char **strp);
+};
+
+#endif // _LOGD_LOG_WHITE_BLACK_LIST_H__