logging: Use more inclusive language.

Also generic syntax clean up and removing some unused aspects (sorting
the list and the TODO increasing performance based on this sorting).

Test: logging unit tests
Change-Id: I56bb3866c13cb4c28bd48665bf32ec620cf0278e
diff --git a/logd/Android.bp b/logd/Android.bp
index e0a1168..036cb7e 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -58,8 +58,8 @@
         "LogReaderThread.cpp",
         "LogBufferElement.cpp",
         "LogStatistics.cpp",
-        "LogWhiteBlackList.cpp",
         "LogTags.cpp",
+        "PruneList.cpp",
         "SerializedFlushToState.cpp",
         "SerializedLogBuffer.cpp",
         "SerializedLogChunk.cpp",
diff --git a/logd/ChattyLogBuffer.cpp b/logd/ChattyLogBuffer.cpp
index c213448..fd183e4 100644
--- a/logd/ChattyLogBuffer.cpp
+++ b/logd/ChattyLogBuffer.cpp
@@ -298,33 +298,37 @@
 // invariably move the logs value down faster as less chatty sources would be
 // expired in the noise.
 //
-// The first loop performs blacklisting and worst offender pruning. Falling
-// through when there are no notable worst offenders and have not hit the
-// region lock preventing further worst offender pruning. This loop also looks
-// after managing the chatty log entries and merging to help provide
-// statistical basis for blame. The chatty entries are not a notification of
-// how much logs you may have, but instead represent how much logs you would
-// have had in a virtual log buffer that is extended to cover all the in-memory
-// logs without loss. They last much longer than the represented pruned logs
-// since they get multiplied by the gains in the non-chatty log sources.
+// The first pass prunes elements that match 3 possible rules:
+// 1) A high priority prune rule, for example ~100/20, which indicates elements from UID 100 and PID
+//    20 should be pruned in this first pass.
+// 2) The default chatty pruning rule, ~!.  This rule sums the total size spent on log messages for
+//    each UID this log buffer.  If the highest sum consumes more than 12.5% of the log buffer, then
+//    these elements from that UID are pruned.
+// 3) The default AID_SYSTEM pruning rule, ~1000/!.  This rule is a special case to 2), if
+//    AID_SYSTEM is the top consumer of the log buffer, then this rule sums the total size spent on
+//    log messages for each PID in AID_SYSTEM in this log buffer and prunes elements from the PID
+//    with the highest sum.
+// This pass reevaluates the sums for rules 2) and 3) for every log message pruned. It creates
+// 'chatty' entries for the elements that it prunes and merges related chatty entries together. It
+// completes when one of three conditions have been met:
+// 1) The requested element count has been pruned.
+// 2) There are no elements that match any of these rules.
+// 3) A reader is referencing the oldest element that would match these rules.
 //
-// The second loop get complicated because an algorithm of watermarks and
-// history is maintained to reduce the order and keep processing time
-// down to a minimum at scale. These algorithms can be costly in the face
-// of larger log buffers, or severly limited processing time granted to a
-// background task at lowest priority.
+// The second pass prunes elements starting from the beginning of the log.  It skips elements that
+// match any low priority prune rules.  It completes when one of three conditions have been met:
+// 1) The requested element count has been pruned.
+// 2) All elements except those mwatching low priority prune rules have been pruned.
+// 3) A reader is referencing the oldest element that would match these rules.
 //
-// This second loop does straight-up expiration from the end of the logs
-// (again, remember for the specified log buffer id) but does some whitelist
-// preservation. Thus whitelist is a Hail Mary low priority, blacklists and
-// spam filtration all take priority. This second loop also checks if a region
-// lock is causing us to buffer too much in the logs to help the reader(s),
-// and will tell the slowest reader thread to skip log entries, and if
-// persistent and hits a further threshold, kill the reader thread.
+// The final pass only happens if there are any low priority prune rules and if the first two passes
+// were unable to prune the requested number of elements.  It prunes elements all starting from the
+// beginning of the log, regardless of if they match any low priority prune rules.
 //
-// The third thread is optional, and only gets hit if there was a whitelist
-// and more needs to be pruned against the backstop of the region lock.
-//
+// If the requested number of logs was unable to be pruned, KickReader() is called to mitigate the
+// situation before the next call to Prune() and the function returns false.  Otherwise, if the
+// requested number of logs or all logs present in the buffer are pruned, in the case of Clear(),
+// it returns true.
 bool ChattyLogBuffer::Prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
     LogReaderThread* oldest = nullptr;
     bool clearAll = pruneRows == ULONG_MAX;
@@ -370,8 +374,8 @@
         return true;
     }
 
-    // prune by worst offenders; by blacklist, UID, and by PID of system UID
-    bool hasBlacklist = (id != LOG_ID_SECURITY) && prune_->naughty();
+    // First prune pass.
+    bool check_high_priority = id != LOG_ID_SECURITY && prune_->HasHighPriorityPruneRules();
     while (!clearAll && (pruneRows > 0)) {
         // recalculate the worst offender on every batched pass
         int worst = -1;  // not valid for uid() or getKey()
@@ -379,7 +383,7 @@
         size_t second_worst_sizes = 0;
         pid_t worstPid = 0;  // POSIX guarantees PID != 0
 
-        if (worstUidEnabledForLogid(id) && prune_->worstUidEnabled()) {
+        if (worstUidEnabledForLogid(id) && prune_->worst_uid_enabled()) {
             // Calculate threshold as 12.5% of available storage
             size_t threshold = max_size(id) / 8;
 
@@ -389,14 +393,14 @@
             } else {
                 stats()->WorstTwoUids(id, threshold, &worst, &worst_sizes, &second_worst_sizes);
 
-                if (worst == AID_SYSTEM && prune_->worstPidOfSystemEnabled()) {
+                if (worst == AID_SYSTEM && prune_->worst_pid_of_system_enabled()) {
                     stats()->WorstTwoSystemPids(id, worst_sizes, &worstPid, &second_worst_sizes);
                 }
             }
         }
 
-        // skip if we have neither worst nor naughty filters
-        if ((worst == -1) && !hasBlacklist) {
+        // skip if we have neither a worst UID or high priority prune rules
+        if (worst == -1 && !check_high_priority) {
             break;
         }
 
@@ -464,7 +468,7 @@
             int key = (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element.GetTag()
                                                                      : element.uid();
 
-            if (hasBlacklist && prune_->naughty(&element)) {
+            if (check_high_priority && prune_->IsHighPriority(&element)) {
                 last.clear(&element);
                 it = Erase(it);
                 if (dropped) {
@@ -557,15 +561,17 @@
         }
         last.clear();
 
-        if (!kick || !prune_->worstUidEnabled()) {
+        if (!kick || !prune_->worst_uid_enabled()) {
             break;  // the following loop will ask bad clients to skip/drop
         }
     }
 
-    bool whitelist = false;
-    bool hasWhitelist = (id != LOG_ID_SECURITY) && prune_->nice() && !clearAll;
+    // Second prune pass.
+    bool skipped_low_priority_prune = false;
+    bool check_low_priority =
+            id != LOG_ID_SECURITY && prune_->HasLowPriorityPruneRules() && !clearAll;
     it = GetOldest(id);
-    while ((pruneRows > 0) && (it != logs().end())) {
+    while (pruneRows > 0 && it != logs().end()) {
         LogBufferElement& element = *it;
 
         if (element.log_id() != id) {
@@ -574,13 +580,12 @@
         }
 
         if (oldest && oldest->start() <= element.sequence()) {
-            if (!whitelist) KickReader(oldest, id, pruneRows);
+            if (!skipped_low_priority_prune) KickReader(oldest, id, pruneRows);
             break;
         }
 
-        if (hasWhitelist && !element.dropped_count() && prune_->nice(&element)) {
-            // WhiteListed
-            whitelist = true;
+        if (check_low_priority && !element.dropped_count() && prune_->IsLowPriority(&element)) {
+            skipped_low_priority_prune = true;
             it++;
             continue;
         }
@@ -589,10 +594,10 @@
         pruneRows--;
     }
 
-    // Do not save the whitelist if we are reader range limited
-    if (whitelist && (pruneRows > 0)) {
+    // Third prune pass.
+    if (skipped_low_priority_prune && pruneRows > 0) {
         it = GetOldest(id);
-        while ((it != logs().end()) && (pruneRows > 0)) {
+        while (it != logs().end() && pruneRows > 0) {
             LogBufferElement& element = *it;
 
             if (element.log_id() != id) {
diff --git a/logd/ChattyLogBuffer.h b/logd/ChattyLogBuffer.h
index 6f60272..ce3dc7b 100644
--- a/logd/ChattyLogBuffer.h
+++ b/logd/ChattyLogBuffer.h
@@ -33,8 +33,8 @@
 #include "LogReaderThread.h"
 #include "LogStatistics.h"
 #include "LogTags.h"
-#include "LogWhiteBlackList.h"
 #include "LogWriter.h"
+#include "PruneList.h"
 #include "SimpleLogBuffer.h"
 #include "rwlock.h"
 
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 39c5490..2eeb0d9 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -215,15 +215,13 @@
     return 0;
 }
 
-int CommandListener::GetPruneListCmd::runCommand(SocketClient* cli,
-                                                 int /*argc*/, char** /*argv*/) {
+int CommandListener::GetPruneListCmd::runCommand(SocketClient* cli, int, char**) {
     setname();
-    cli->sendMsg(PackageString(prune()->format()).c_str());
+    cli->sendMsg(PackageString(prune()->Format()).c_str());
     return 0;
 }
 
-int CommandListener::SetPruneListCmd::runCommand(SocketClient* cli, int argc,
-                                                 char** argv) {
+int CommandListener::SetPruneListCmd::runCommand(SocketClient* cli, int argc, char** argv) {
     setname();
     if (!clientHasLogCredentials(cli)) {
         cli->sendMsg("Permission Denied");
@@ -238,15 +236,12 @@
         str += argv[i];
     }
 
-    int ret = prune()->init(str.c_str());
-
-    if (ret) {
+    if (!prune()->Init(str.c_str())) {
         cli->sendMsg("Invalid");
         return 0;
     }
 
     cli->sendMsg("success");
-
     return 0;
 }
 
@@ -301,7 +296,7 @@
 
     LOG(INFO) << "logd reinit";
     buf()->Init();
-    prune()->init(nullptr);
+    prune()->Init(nullptr);
 
     // This only works on userdebug and eng devices to re-read the
     // /data/misc/logd/event-log-tags file right after /data is mounted.
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index a55a393..c3080ab 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -23,7 +23,7 @@
 #include "LogListener.h"
 #include "LogStatistics.h"
 #include "LogTags.h"
-#include "LogWhiteBlackList.h"
+#include "PruneList.h"
 
 class CommandListener : public FrameworkListener {
   public:
diff --git a/logd/LogBufferTest.h b/logd/LogBufferTest.h
index 235f5ac..5d57ad1 100644
--- a/logd/LogBufferTest.h
+++ b/logd/LogBufferTest.h
@@ -25,7 +25,7 @@
 #include "LogReaderList.h"
 #include "LogStatistics.h"
 #include "LogTags.h"
-#include "LogWhiteBlackList.h"
+#include "PruneList.h"
 #include "SerializedLogBuffer.h"
 #include "SimpleLogBuffer.h"
 
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 1ea87a9..dbdf7fd 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -665,10 +665,9 @@
         ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1]))) ||
         // register names like x18 but not driver names like en0
         ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2]))) ||
-        // blacklist
+        // ignore
         ((size == cpuLen) && !fastcmp<strncmp>(tag, cpu, cpuLen)) ||
-        ((size == warningLen) &&
-         !fastcmp<strncasecmp>(tag, warning, warningLen)) ||
+        ((size == warningLen) && !fastcmp<strncasecmp>(tag, warning, warningLen)) ||
         ((size == errorLen) && !fastcmp<strncasecmp>(tag, error, errorLen)) ||
         ((size == infoLen) && !fastcmp<strncasecmp>(tag, info, infoLen))) {
         p = start;
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
deleted file mode 100644
index 88a3bdc..0000000
--- a/logd/LogWhiteBlackList.cpp
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <ctype.h>
-
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-
-#include "LogWhiteBlackList.h"
-
-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;
-}
-
-std::string Prune::format() {
-    if (mUid != uid_all) {
-        if (mPid != pid_all) {
-            return android::base::StringPrintf("%u/%u", mUid, mPid);
-        }
-        return android::base::StringPrintf("%u", mUid);
-    }
-    if (mPid != pid_all) {
-        return android::base::StringPrintf("/%u", mPid);
-    }
-    // NB: mPid == pid_all can not happen if mUid == uid_all
-    return std::string("/");
-}
-
-PruneList::PruneList() {
-    init(nullptr);
-}
-
-PruneList::~PruneList() {
-    PruneCollection::iterator it;
-    for (it = mNice.begin(); it != mNice.end();) {
-        it = mNice.erase(it);
-    }
-    for (it = mNaughty.begin(); it != mNaughty.end();) {
-        it = mNaughty.erase(it);
-    }
-}
-
-int PruneList::init(const char* str) {
-    mWorstUidEnabled = true;
-    mWorstPidOfSystemEnabled = true;
-    PruneCollection::iterator it;
-    for (it = mNice.begin(); it != mNice.end();) {
-        it = mNice.erase(it);
-    }
-    for (it = mNaughty.begin(); it != mNaughty.end();) {
-        it = mNaughty.erase(it);
-    }
-
-    // default here means take ro.logd.filter, persist.logd.filter then
-    // internal default in that order.
-    if (str && !strcmp(str, "default")) {
-        str = nullptr;
-    }
-    if (str && !strcmp(str, "disable")) {
-        str = "";
-    }
-
-    std::string filter;
-
-    if (str) {
-        filter = str;
-    } else {
-        filter = android::base::GetProperty("ro.logd.filter", "default");
-        auto persist_filter = android::base::GetProperty("persist.logd.filter", "default");
-        // default here means take ro.logd.filter
-        if (persist_filter != "default") {
-            filter = persist_filter;
-        }
-    }
-
-    // default here means take internal default.
-    if (filter == "default") {
-        // See README.property for description of filter format
-        filter = "~! ~1000/!";
-    }
-    if (filter == "disable") {
-        filter = "";
-    }
-
-    mWorstUidEnabled = false;
-    mWorstPidOfSystemEnabled = false;
-
-    for (str = filter.c_str(); *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;
-            }
-            // special case, translated to worst PID of System at priority
-            static const char worstPid[] = "1000/!";
-            if (!strncmp(str, worstPid, sizeof(worstPid) - 1)) {
-                mWorstPidOfSystemEnabled = true;
-                str += sizeof(worstPid) - 1;
-                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, Prune(uid, pid));
-                }
-                break;
-            }
-            ++it;
-        }
-        if (it == list->end()) {
-            list->push_back(Prune(uid, pid));
-        }
-        if (!*str) {
-            break;
-        }
-    }
-
-    return 0;
-}
-
-std::string PruneList::format() {
-    static const char nice_format[] = " %s";
-    const char* fmt = nice_format + 1;
-
-    std::string string;
-
-    if (mWorstUidEnabled) {
-        string = "~!";
-        fmt = nice_format;
-        if (mWorstPidOfSystemEnabled) {
-            string += " ~1000/!";
-        }
-    }
-
-    PruneCollection::iterator it;
-
-    for (it = mNice.begin(); it != mNice.end(); ++it) {
-        string += android::base::StringPrintf(fmt, (*it).format().c_str());
-        fmt = nice_format;
-    }
-
-    static const char naughty_format[] = " ~%s";
-    fmt = naughty_format + (*fmt != ' ');
-    for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
-        string += android::base::StringPrintf(fmt, (*it).format().c_str());
-        fmt = naughty_format;
-    }
-
-    return 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;
-}
diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h
deleted file mode 100644
index 447ab87..0000000
--- a/logd/LogWhiteBlackList.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <sys/types.h>
-
-#include <string.h>
-#include <list>
-
-#include "LogBufferElement.h"
-
-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->uid(), e->pid()); }
-
-    std::string format();
-};
-
-typedef std::list<Prune> PruneCollection;
-
-class PruneList {
-    PruneCollection mNaughty;
-    PruneCollection mNice;
-    bool mWorstUidEnabled;
-    bool mWorstPidOfSystemEnabled;
-
-   public:
-    PruneList();
-    ~PruneList();
-
-    int init(const char* str);
-
-    bool naughty(LogBufferElement* element);
-    bool naughty(void) {
-        return !mNaughty.empty();
-    }
-    bool nice(LogBufferElement* element);
-    bool nice(void) {
-        return !mNice.empty();
-    }
-    bool worstUidEnabled() const {
-        return mWorstUidEnabled;
-    }
-    bool worstPidOfSystemEnabled() const {
-        return mWorstPidOfSystemEnabled;
-    }
-
-    std::string format();
-};
diff --git a/logd/PruneList.cpp b/logd/PruneList.cpp
new file mode 100644
index 0000000..c3859f3
--- /dev/null
+++ b/logd/PruneList.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PruneList.h"
+
+#include <ctype.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+bool Prune::Matches(LogBufferElement* element) const {
+    return (uid_ == UID_ALL || uid_ == element->uid()) &&
+           (pid_ == PID_ALL || pid_ == element->pid());
+}
+
+std::string Prune::Format() const {
+    if (uid_ != UID_ALL) {
+        if (pid_ != PID_ALL) {
+            return android::base::StringPrintf("%u/%u", uid_, pid_);
+        }
+        return android::base::StringPrintf("%u", uid_);
+    }
+    if (pid_ != PID_ALL) {
+        return android::base::StringPrintf("/%u", pid_);
+    }
+    // NB: pid_ == PID_ALL can not happen if uid_ == UID_ALL
+    return std::string("/");
+}
+
+PruneList::PruneList() {
+    Init(nullptr);
+}
+
+bool PruneList::Init(const char* str) {
+    high_priority_prune_.clear();
+    low_priority_prune_.clear();
+
+    // default here means take ro.logd.filter, persist.logd.filter then internal default in order.
+    if (str && !strcmp(str, "default")) {
+        str = nullptr;
+    }
+    if (str && !strcmp(str, "disable")) {
+        str = "";
+    }
+
+    std::string filter;
+
+    if (str) {
+        filter = str;
+    } else {
+        filter = android::base::GetProperty("ro.logd.filter", "default");
+        auto persist_filter = android::base::GetProperty("persist.logd.filter", "default");
+        // default here means take ro.logd.filter
+        if (persist_filter != "default") {
+            filter = persist_filter;
+        }
+    }
+
+    // default here means take internal default.
+    if (filter == "default") {
+        filter = "~! ~1000/!";
+    }
+    if (filter == "disable") {
+        filter = "";
+    }
+
+    worst_uid_enabled_ = false;
+    worst_pid_of_system_enabled_ = false;
+
+    for (str = filter.c_str(); *str; ++str) {
+        if (isspace(*str)) {
+            continue;
+        }
+
+        std::list<Prune>* list;
+        if (*str == '~' || *str == '!') {  // ~ supported, ! undocumented
+            ++str;
+            // special case, prune the worst UID of those using at least 1/8th of the buffer.
+            if (*str == '!') {
+                worst_uid_enabled_ = true;
+                ++str;
+                if (!*str) {
+                    break;
+                }
+                if (!isspace(*str)) {
+                    LOG(ERROR) << "Nothing expected after '~!', but found '" << str << "'";
+                    return false;
+                }
+                continue;
+            }
+            // special case, translated to worst PID of System at priority
+            static const char WORST_SYSTEM_PID[] = "1000/!";
+            if (!strncmp(str, WORST_SYSTEM_PID, sizeof(WORST_SYSTEM_PID) - 1)) {
+                worst_pid_of_system_enabled_ = true;
+                str += sizeof(WORST_SYSTEM_PID) - 1;
+                if (!*str) {
+                    break;
+                }
+                if (!isspace(*str)) {
+                    LOG(ERROR) << "Nothing expected after '~1000/!', but found '" << str << "'";
+                    return false;
+                }
+                continue;
+            }
+            if (!*str) {
+                LOG(ERROR) << "Expected UID or PID after '~', but found nothing";
+                return false;
+            }
+            list = &high_priority_prune_;
+        } else {
+            list = &low_priority_prune_;
+        }
+
+        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) {
+            LOG(ERROR) << "Expected UID/PID combination, but found none";
+            return false;
+        }
+
+        if (*str && !isspace(*str)) {
+            LOG(ERROR) << "Nothing expected after UID/PID combination, but found '" << str << "'";
+            return false;
+        }
+
+        list->emplace_back(uid, pid);
+        if (!*str) {
+            break;
+        }
+    }
+
+    return true;
+}
+
+std::string PruneList::Format() const {
+    std::vector<std::string> prune_rules;
+
+    if (worst_uid_enabled_) {
+        prune_rules.emplace_back("~!");
+    }
+    if (worst_pid_of_system_enabled_) {
+        prune_rules.emplace_back("~1000/!");
+    }
+    for (const auto& rule : low_priority_prune_) {
+        prune_rules.emplace_back(rule.Format());
+    }
+    for (const auto& rule : high_priority_prune_) {
+        prune_rules.emplace_back("~" + rule.Format());
+    }
+    return android::base::Join(prune_rules, " ");
+}
+
+bool PruneList::IsHighPriority(LogBufferElement* element) const {
+    for (const auto& rule : high_priority_prune_) {
+        if (rule.Matches(element)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool PruneList::IsLowPriority(LogBufferElement* element) const {
+    for (const auto& rule : low_priority_prune_) {
+        if (rule.Matches(element)) {
+            return true;
+        }
+    }
+    return false;
+}
diff --git a/logd/PruneList.h b/logd/PruneList.h
new file mode 100644
index 0000000..94de5c5
--- /dev/null
+++ b/logd/PruneList.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <string.h>
+#include <list>
+
+#include "LogBufferElement.h"
+
+class Prune {
+  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_(uid), pid_(pid) {}
+
+    bool Matches(LogBufferElement* element) const;
+    std::string Format() const;
+
+    uid_t uid() const { return uid_; }
+    pid_t pid() const { return pid_; }
+
+  private:
+    const uid_t uid_;
+    const pid_t pid_;
+};
+
+class PruneList {
+  public:
+    PruneList();
+
+    bool Init(const char* str);
+    std::string Format() const;
+
+    bool IsHighPriority(LogBufferElement* element) const;
+    bool IsLowPriority(LogBufferElement* element) const;
+
+    bool HasHighPriorityPruneRules() const { return !high_priority_prune_.empty(); }
+    bool HasLowPriorityPruneRules() const { return !low_priority_prune_.empty(); }
+
+    bool worst_uid_enabled() const { return worst_uid_enabled_; }
+    bool worst_pid_of_system_enabled() const { return worst_pid_of_system_enabled_; }
+
+  private:
+    std::list<Prune> high_priority_prune_;
+    std::list<Prune> low_priority_prune_;
+
+    bool worst_uid_enabled_;
+    bool worst_pid_of_system_enabled_;
+};
diff --git a/logd/README.property b/logd/README.property
index 6a9369a..ab9c4d4 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -65,8 +65,8 @@
 - number - support multipliers (K or M) for convenience. Range is limited
   to between 64K and 256M for log buffer sizes. Individual log buffer ids
   such as main, system, ... override global default.
-- Pruning filter is of form of a space-separated list of [~][UID][/PID]
-  references, where '~' prefix means to blacklist otherwise whitelist. For
-  blacklisting, UID or PID may be a '!' to instead reference the chattiest
-  client, with the restriction that the PID must be in the UID group 1000
-  (system or AID_SYSTEM).
+- Pruning filter rules are specified as UID, UID/PID or /PID. A '~' prefix indicates that elements
+  matching the rule should be pruned with higher priority otherwise they're pruned with lower
+  priority. All other pruning activity is oldest first. Special case ~! represents an automatic
+  pruning for the noisiest UID as determined by the current statistics.  Special case ~1000/!
+  represents pruning of the worst PID within AID_SYSTEM when AID_SYSTEM is the noisiest UID.
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index b576ddf..a7a1792 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -97,7 +97,7 @@
     size_t data_left = size;
     const uint8_t** pdata = &data;
 
-    prune_list.init(nullptr);
+    prune_list.Init(nullptr);
     // We want to get pruning code to get called.
     log_id_for_each(i) { log_buffer->SetSize(i, 10000); }