Merge changes from topic 'enable_persist_kernel_log' into nyc-mr1-dev

* changes:
  logcatd: trampoline persist.logd.logpersistd to logd.logpersistd
  logcatd: add stop and clear actions
  logcatd: Do not su for setprop
  logcat: allow comma-separate list of buffers
  logcat: clear when specifying file output
  logcat: Adjust help to make it more meaningful
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 37f4f4f..33f22dd 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -278,61 +278,59 @@
     fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd);
 
     fprintf(stderr, "options include:\n"
-                    "  -s              Set default filter to silent.\n"
-                    "                  Like specifying filterspec '*:S'\n"
-                    "  -f <filename>   Log to file. Default is stdout\n"
-                    "  --file=<filename>\n"
-                    "  -r <kbytes>     Rotate log every kbytes. Requires -f\n"
-                    "  --rotate-kbytes=<kbytes>\n"
-                    "  -n <count>      Sets max number of rotated logs to <count>, default 4\n"
-                    "  --rotate-count=<count>\n"
-                    "  -v <format>     Sets the log print format, where <format> is:\n"
-                    "  --format=<format>\n"
-                    "                      brief color epoch long monotonic printable process raw\n"
-                    "                      tag thread threadtime time uid usec UTC year zone\n\n"
-                    "  -D              print dividers between each log buffer\n"
-                    "  --dividers\n"
-                    "  -c              clear (flush) the entire log and exit\n"
-                    "  --clear\n"
-                    "  -d              dump the log and then exit (don't block)\n"
-                    "  -e <expr>       only print lines where the log message matches <expr>\n"
-                    "  --regex <expr>  where <expr> is a regular expression\n"
-                    "  -m <count>      quit after printing <count> lines. This is meant to be\n"
-                    "  --max-count=<count> paired with --regex, but will work on its own.\n"
-                    "  --print         paired with --regex and --max-count to let content bypass\n"
+                    "  -s              Set default filter to silent. Equivalent to filterspec '*:S'\n"
+                    "  -f <file>, --file=<file>               Log to file. Default is stdout\n"
+                    "  -r <kbytes>, --rotate-kbytes=<kbytes>\n"
+                    "                  Rotate log every kbytes. Requires -f option\n"
+                    "  -n <count>, --rotate-count=<count>\n"
+                    "                  Sets max number of rotated logs to <count>, default 4\n"
+                    "  -v <format>, --format=<format>\n"
+                    "                  Sets the log print format, where <format> is:\n"
+                    "                    brief color epoch long monotonic printable process raw\n"
+                    "                    tag thread threadtime time uid usec UTC year zone\n"
+                    "  -D, --dividers  Print dividers between each log buffer\n"
+                    "  -c, --clear     Clear (flush) the entire log and exit\n"
+                    "                  if Log to File specified, clear fileset instead\n"
+                    "  -d              Dump the log and then exit (don't block)\n"
+                    "  -e <expr>, --regex=<expr>\n"
+                    "                  Only print lines where the log message matches <expr>\n"
+                    "                  where <expr> is a regular expression\n"
+                    // Leave --head undocumented as alias for -m
+                    "  -m <count>, --max-count=<count>\n"
+                    "                  Quit after printing <count> lines. This is meant to be\n"
+                    "                  paired with --regex, but will work on its own.\n"
+                    "  --print         Paired with --regex and --max-count to let content bypass\n"
                     "                  regex filter but still stop at number of matches.\n"
-                    "  -t <count>      print only the most recent <count> lines (implies -d)\n"
-                    "  -t '<time>'     print most recent lines since specified time (implies -d)\n"
-                    "  -T <count>      print only the most recent <count> lines (does not imply -d)\n"
-                    "  -T '<time>'     print most recent lines since specified time (not imply -d)\n"
+                    // Leave --tail undocumented as alias for -t
+                    "  -t <count>      Print only the most recent <count> lines (implies -d)\n"
+                    "  -t '<time>'     Print most recent lines since specified time (implies -d)\n"
+                    "  -T <count>      Print only the most recent <count> lines (does not imply -d)\n"
+                    "  -T '<time>'     Print most recent lines since specified time (not imply -d)\n"
                     "                  count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'\n"
                     "                  'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format\n"
-                    "  -g              get the size of the log's ring buffer and exit\n"
-                    "  --buffer-size\n"
-                    "  -G <size>       set size of log ring buffer, may suffix with K or M.\n"
-                    "  --buffer-size=<size>\n"
-                    "  -L              dump logs from prior to last reboot\n"
-                    "  --last\n"
+                    "  -g, --buffer-size                      Get the size of the ring buffer.\n"
+                    "  -G <size>, --buffer-size=<size>\n"
+                    "                  Set size of log ring buffer, may suffix with K or M.\n"
+                    "  -L, -last       Dump logs from prior to last reboot\n"
                     // Leave security (Device Owner only installations) and
                     // kernel (userdebug and eng) buffers undocumented.
-                    "  -b <buffer>     Request alternate ring buffer, 'main', 'system', 'radio',\n"
-                    "  --buffer=<buffer> 'events', 'crash', 'default' or 'all'. Multiple -b\n"
-                    "                  parameters are allowed and results are interleaved. The\n"
-                    "                  default is -b main -b system -b crash.\n"
-                    "  -B              output the log in binary.\n"
-                    "  --binary\n"
-                    "  -S              output statistics.\n"
-                    "  --statistics\n"
-                    "  -p              print prune white and ~black list. Service is specified as\n"
-                    "  --prune         UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n"
+                    "  -b <buffer>, --buffer=<buffer>         Request alternate ring buffer, 'main',\n"
+                    "                  'system', 'radio', 'events', 'crash', 'default' or 'all'.\n"
+                    "                  Multiple -b parameters or comma separated list of buffers are\n"
+                    "                  allowed. Buffers interleaved. Default -b main,system,crash.\n"
+                    "  -B, --binary    Output the log in binary.\n"
+                    "  -S, --statistics                       Output statistics.\n"
+                    "  -p, --prune     Print prune white and ~black list. Service is specified as\n"
+                    "                  UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n"
                     "                  with ~, otherwise weighed for longevity if unadorned. All\n"
                     "                  other pruning activity is oldest first. Special case ~!\n"
                     "                  represents an automatic quicker pruning for the noisiest\n"
                     "                  UID as determined by the current statistics.\n"
-                    "  -P '<list> ...' set prune white and ~black list, using same format as\n"
-                    "  --prune='<list> ...'  printed above. Must be quoted.\n"
+                    "  -P '<list> ...', --prune='<list> ...'\n"
+                    "                  Set prune white and ~black list, using same format as\n"
+                    "                  listed above. Must be quoted.\n"
                     "  --pid=<pid>     Only prints logs from the given pid.\n"
-                    // Check ANDROID_LOG_WRAP_DEFAULT_TIMEOUT value
+                    // Check ANDROID_LOG_WRAP_DEFAULT_TIMEOUT value for match to 2 hours
                     "  --wrap          Sleep for 2 hours or when buffer about to wrap whichever\n"
                     "                  comes first. Improves efficiency of polling by providing\n"
                     "                  an about-to-wrap wakeup.\n");
@@ -765,111 +763,63 @@
             break;
 
             case 'b': {
-                if (strcmp(optarg, "default") == 0) {
-                    for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
-                        switch (i) {
-                        case LOG_ID_SECURITY:
-                        case LOG_ID_EVENTS:
-                            continue;
-                        case LOG_ID_MAIN:
-                        case LOG_ID_SYSTEM:
-                        case LOG_ID_CRASH:
-                            break;
-                        default:
-                            continue;
-                        }
+                unsigned idMask = 0;
+                while ((optarg = strtok(optarg, ",:; \t\n\r\f")) != NULL) {
+                    if (strcmp(optarg, "default") == 0) {
+                        idMask |= (1 << LOG_ID_MAIN) |
+                                  (1 << LOG_ID_SYSTEM) |
+                                  (1 << LOG_ID_CRASH);
+                    } else if (strcmp(optarg, "all") == 0) {
+                        idMask = (unsigned)-1;
+                    } else {
+                        log_id_t log_id = android_name_to_log_id(optarg);
+                        const char *name = android_log_id_to_name(log_id);
 
-                        const char *name = android_log_id_to_name((log_id_t)i);
-                        log_id_t log_id = android_name_to_log_id(name);
-
-                        if (log_id != (log_id_t)i) {
-                            continue;
+                        if (strcmp(name, optarg) != 0) {
+                            logcat_panic(true, "unknown buffer %s\n", optarg);
                         }
-
-                        bool found = false;
-                        for (dev = devices; dev; dev = dev->next) {
-                            if (!strcmp(optarg, dev->device)) {
-                                found = true;
-                                break;
-                            }
-                            if (!dev->next) {
-                                break;
-                            }
-                        }
-                        if (found) {
-                            break;
-                        }
-
-                        log_device_t* d = new log_device_t(name, false);
-
-                        if (dev) {
-                            dev->next = d;
-                            dev = d;
-                        } else {
-                            devices = dev = d;
-                        }
-                        g_devCount++;
+                        idMask |= (1 << log_id);
                     }
-                    break;
+                    optarg = NULL;
                 }
 
-                if (strcmp(optarg, "all") == 0) {
-                    for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
-                        const char *name = android_log_id_to_name((log_id_t)i);
-                        log_id_t log_id = android_name_to_log_id(name);
+                for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+                    const char *name = android_log_id_to_name((log_id_t)i);
+                    log_id_t log_id = android_name_to_log_id(name);
 
-                        if (log_id != (log_id_t)i) {
-                            continue;
-                        }
+                    if (log_id != (log_id_t)i) {
+                        continue;
+                    }
+                    if ((idMask & (1 << i)) == 0) {
+                        continue;
+                    }
 
-                        bool found = false;
-                        for (dev = devices; dev; dev = dev->next) {
-                            if (!strcmp(optarg, dev->device)) {
-                                found = true;
-                                break;
-                            }
-                            if (!dev->next) {
-                                break;
-                            }
-                        }
-                        if (found) {
+                    bool found = false;
+                    for (dev = devices; dev; dev = dev->next) {
+                        if (!strcmp(name, dev->device)) {
+                            found = true;
                             break;
                         }
-
-                        bool binary = !strcmp(name, "events") ||
-                                      !strcmp(name, "security");
-                        log_device_t* d = new log_device_t(name, binary);
-
-                        if (dev) {
-                            dev->next = d;
-                            dev = d;
-                        } else {
-                            devices = dev = d;
-                        }
-                        g_devCount++;
-                    }
-                    break;
-                }
-
-                bool binary = !(strcmp(optarg, "events") &&
-                                strcmp(optarg, "security"));
-
-                if (devices) {
-                    dev = devices;
-                    while (dev->next) {
-                        if (!strcmp(optarg, dev->device)) {
-                            dev = NULL;
+                        if (!dev->next) {
                             break;
                         }
-                        dev = dev->next;
                     }
+                    if (found) {
+                        continue;
+                    }
+
+                    bool binary = !strcmp(name, "events") ||
+                                  !strcmp(name, "security");
+                    log_device_t* d = new log_device_t(name, binary);
+
                     if (dev) {
-                        dev->next = new log_device_t(optarg, binary);
+                        dev->next = d;
+                        dev = d;
+                    } else {
+                        devices = dev = d;
                     }
-                } else {
-                    devices = new log_device_t(optarg, binary);
+                    g_devCount++;
                 }
-                g_devCount++;
             }
             break;
 
@@ -1084,7 +1034,35 @@
         }
 
         if (clearLog) {
-            if (android_logger_clear(dev->logger)) {
+            if (g_outputFileName) {
+                int maxRotationCountDigits =
+                    (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
+
+                for (int i = g_maxRotatedLogs ; i >= 0 ; --i) {
+                    char *file;
+
+                    if (i == 0) {
+                        asprintf(&file, "%s", g_outputFileName);
+                    } else {
+                        asprintf(&file, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i);
+                    }
+
+                    if (!file) {
+                        perror("while clearing log files");
+                        clearFail = clearFail ?: dev->device;
+                        break;
+                    }
+
+                    err = unlink(file);
+
+                    if (err < 0 && errno != ENOENT && clearFail == NULL) {
+                        perror("while clearing log files");
+                        clearFail = dev->device;
+                    }
+
+                    free(file);
+                }
+            } else if (android_logger_clear(dev->logger)) {
                 clearFail = clearFail ?: dev->device;
             }
         }
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index 70d1dd4..ce1a451 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -1,11 +1,62 @@
+#
+# init scriptures for logcatd persistent logging.
+#
+# Make sure any property changes are only performed with /data mounted, after
+# post-fs-data state because otherwise behavior is undefined. The exceptions
+# are device adjustments for logcatd service properties (persist.* overrides
+# notwithstanding) for logd.logpersistd.size and logd.logpersistd.buffer.
+
+# persist to non-persistent trampolines to permit device properties can be
+# overridden when /data mounts, or during runtime.
+on property:persist.logd.logpersistd.size=256
+    setprop persist.logd.logpersistd.size ""
+    setprop logd.logpersistd.size ""
+
+on property:persist.logd.logpersistd.size=*
+    # expect /init to report failure if property empty (default)
+    setprop logd.logpersistd.size ${persist.logd.logpersistd.size}
+
+on property:persist.logd.logpersistd.buffer=all
+    setprop persist.logd.logpersistd.buffer ""
+    setprop logd.logpersistd.buffer ""
+
+on property:persist.logd.logpersistd.buffer=*
+    # expect /init to report failure if property empty (default)
+    setprop logd.logpersistd.buffer ${persist.logd.logpersistd.buffer}
+
 on property:persist.logd.logpersistd=logcatd
+    setprop logd.logpersistd logcatd
+
+# enable, prep and start logcatd service
+on load_persist_props_action
+    setprop logd.logpersistd.enable true
+
+on property:logd.logpersistd.enable=true && property:logd.logpersistd=logcatd
     # all exec/services are called with umask(077), so no gain beyond 0700
     mkdir /data/misc/logd 0700 logd log
     # logd for write to /data/misc/logd, log group for read from pstore (-L)
-    exec - logd log -- /system/bin/logcat -L -b ${persist.logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${persist.logd.logpersistd.size:-256}
+    exec - logd log -- /system/bin/logcat -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256}
     start logcatd
 
-service logcatd /system/bin/logcat -b ${persist.logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${persist.logd.logpersistd.size:-256}
+# stop logcatd service and clear data
+on property:logd.logpersistd.enable=true && property:logd.logpersistd=clear
+    setprop persist.logd.logpersistd ""
+    stop logcatd
+    # logd for clear of only our files in /data/misc/logd
+    exec - logd log -- /system/bin/logcat -c -f /data/misc/logd/logcat -n ${logd.logpersistd.size:-256}
+    setprop logd.logpersistd ""
+
+# stop logcatd service
+on property:logd.logpersistd=stop
+    setprop persist.logd.logpersistd ""
+    stop logcatd
+    setprop logd.logpersistd ""
+
+on property:logd.logpersistd.enable=false
+    stop logcatd
+
+# logcatd service
+service logcatd /system/bin/logcat -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256}
     class late_start
     disabled
     # logd for write to /data/misc/logd, log group for read from log daemon
diff --git a/logcat/logpersist b/logcat/logpersist
index 923c5fb..a0e27ce 100755
--- a/logcat/logpersist
+++ b/logcat/logpersist
@@ -8,8 +8,16 @@
    ;;
 esac
 
-data=/data/misc/logd
 property=persist.logd.logpersistd
+
+case `getprop ${property#persist.}.enable` in
+true) ;;
+*) echo "${progname} - Disabled"
+   exit 1
+   ;;
+esac
+
+data=/data/misc/logd
 service=logcatd
 size_default=256
 buffer_default=all
@@ -69,14 +77,11 @@
   su logd xargs cat
   ;;
 *.start)
-  current_buffer="`getprop ${property}.buffer`"
-  current_size="`getprop ${property}.size`"
-  if [ "${service}" = "`getprop ${property}`" ]; then
+  current_buffer="`getprop ${property#persist.}.buffer`"
+  current_size="`getprop ${property#persist.}.size`"
+  if [ "${service}" = "`getprop ${property#persist.}`" ]; then
     if [ "true" = "${clear}" ]; then
-      su root stop ${service}
-      su root setprop ${property} ""
-      # 20ms done, guarantees content stop before rm
-      sleep 1
+      setprop ${property#persist.} "clear"
     elif [ "${buffer}|${size}" != "${current_buffer}|${current_size}" ]; then
       echo   "ERROR: Changing existing collection parameters from" >&2
       if [ "${buffer}" != "${current_buffer}" ]; then
@@ -98,21 +103,31 @@
       echo   "       To blindly override and retain data, ${progname%.*}.stop first." >&2
       exit 1
     fi
-  fi
-  if [ "true" = "${clear}" ]; then
-    su logd,misc rm -rf "${data}"
+  elif [ "true" = "${clear}" ]; then
+    setprop ${property#persist.} "clear"
   fi
   if [ -n "${buffer}${current_buffer}" ]; then
-    su root setprop ${property}.buffer "${buffer}"
+    setprop ${property}.buffer "${buffer}"
+    if [ -z "${buffer}" ]; then
+      # deal with trampoline for empty properties
+      setprop ${property#persist.}.buffer ""
+    fi
   fi
   if [ -n "${size}${current_size}" ]; then
-    su root setprop ${property}.size "${size}"
+    setprop ${property}.size "${size}"
+    if [ -z "${size}" ]; then
+      # deal with trampoline for empty properties
+      setprop ${property#persist.}.size ""
+    fi
   fi
+  while [ "clear" = "`getprop ${property#persist.}`" ]; do
+    continue
+  done
   # ${service}.rc does the heavy lifting with the following trigger
-  su root setprop ${property} ${service}
-  getprop ${property}
+  setprop ${property} ${service}
   # 20ms done, to permit process feedback check
   sleep 1
+  getprop ${property#persist.}
   # also generate an error return code if not found running, bonus
   ps -t | grep "${data##*/}.*${service%d}"
   ;;
@@ -120,19 +135,24 @@
   if [ -n "${size}${buffer}" ]; then
     echo "WARNING: Can not use --size or --buffer with ${progname%.*}.stop" >&2
   fi
-  su root stop ${service}
-  su root setprop ${property} ""
-  if [ -n "`getprop ${property}.buffer`" ]; then
-    su root setprop ${property}.buffer ""
-  fi
-  if [ -n "`getprop ${property}.size`" ]; then
-    su root setprop ${property}.size ""
-  fi
   if [ "true" = "${clear}" ]; then
-    # 20ms done, guarantees content stop before rm
-    sleep 1
-    su logd,misc rm -rf "${data}"
+    setprop ${property} "clear"
+  else
+    setprop ${property} "stop"
   fi
+  if [ -n "`getprop ${property#persist.}.buffer`" ]; then
+    setprop ${property}.buffer ""
+    # deal with trampoline for empty properties
+    setprop ${property#persist.}.buffer ""
+  fi
+  if [ -n "`getprop ${property#persist.}.size`" ]; then
+    setprop ${property}.size ""
+    # deal with trampoline for empty properties
+    setprop ${property#persist.}.size ""
+  fi
+  while [ "clear" = "`getprop ${property#persist.}`" ]; do
+    continue
+  done
   ;;
 *)
   echo "ERROR: Unexpected command ${0##*/} ${args}" >&2
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index dfcca12..d79fc08 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -28,6 +28,8 @@
 #include <log/logger.h>
 #include <log/log_read.h>
 
+#define BIG_BUFFER (5 * 1024)
+
 // enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
 // non-syscall libs. Since we are only using this in the emergency of
 // a signal to stuff a terminating code into the logs, we will spin rather
@@ -52,7 +54,7 @@
       "logcat -b radio -b events -b system -b main -d 2>/dev/null",
       "r")));
 
-    char buffer[5120];
+    char buffer[BIG_BUFFER];
 
     int ids = 0;
     int count = 0;
@@ -100,7 +102,7 @@
       "logcat -v long -v year -b all -t 3 2>/dev/null",
       "r")));
 
-    char buffer[5120];
+    char buffer[BIG_BUFFER];
 
     int count = 0;
 
@@ -163,7 +165,7 @@
           "logcat -v long -v America/Los_Angeles -b all -t 3 2>/dev/null",
           "r")));
 
-        char buffer[5120];
+        char buffer[BIG_BUFFER];
 
         count = 0;
 
@@ -187,7 +189,7 @@
       "logcat -v long -v America/Los_Angeles -v zone -b all -t 3 2>/dev/null",
       "r")));
 
-    char buffer[5120];
+    char buffer[BIG_BUFFER];
 
     int count = 0;
 
@@ -207,7 +209,7 @@
     int count;
 
     do {
-        char buffer[5120];
+        char buffer[BIG_BUFFER];
 
         snprintf(buffer, sizeof(buffer),
           "logcat -v long -b radio -b events -b system -b main -t %d 2>/dev/null",
@@ -250,7 +252,7 @@
 
     ASSERT_TRUE(NULL != (fp = popen("logcat -v long -b all -t 10 2>&1", "r")));
 
-    char buffer[5120];
+    char buffer[BIG_BUFFER];
     char *last_timestamp = NULL;
     char *first_timestamp = NULL;
     int count = 0;
@@ -313,7 +315,7 @@
       "logcat -v brief -b events -t 100 2>/dev/null",
       "r")));
 
-    char buffer[5120];
+    char buffer[BIG_BUFFER];
 
     int count = 0;
 
@@ -337,15 +339,17 @@
     ASSERT_EQ(1, count);
 }
 
-TEST(logcat, get_size) {
+int get_groups(const char *cmd) {
     FILE *fp;
 
     // NB: crash log only available in user space
-    ASSERT_TRUE(NULL != (fp = popen(
-      "logcat -v brief -b radio -b events -b system -b main -g 2>/dev/null",
-      "r")));
+    EXPECT_TRUE(NULL != (fp = popen(cmd, "r")));
 
-    char buffer[5120];
+    if (fp == NULL) {
+        return 0;
+    }
+
+    char buffer[BIG_BUFFER];
 
     int count = 0;
 
@@ -405,7 +409,23 @@
 
     pclose(fp);
 
-    ASSERT_EQ(4, count);
+    return count;
+}
+
+TEST(logcat, get_size) {
+    ASSERT_EQ(4, get_groups(
+      "logcat -v brief -b radio -b events -b system -b main -g 2>/dev/null"));
+}
+
+// duplicate test for get_size, but use comma-separated list of buffers
+TEST(logcat, multiple_buffer) {
+    ASSERT_EQ(4, get_groups(
+      "logcat -v brief -b radio,events,system,main -g 2>/dev/null"));
+}
+
+TEST(logcat, bad_buffer) {
+    ASSERT_EQ(0, get_groups(
+      "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
 }
 
 static void caught_blocking(int /*signum*/)
@@ -434,7 +454,7 @@
       " logcat -v brief -b events 2>&1",
       "r")));
 
-    char buffer[5120];
+    char buffer[BIG_BUFFER];
 
     int count = 0;
 
@@ -503,7 +523,7 @@
       " logcat -v brief -b events -T 5 2>&1",
       "r")));
 
-    char buffer[5120];
+    char buffer[BIG_BUFFER];
 
     int count = 0;
 
@@ -566,7 +586,7 @@
         FILE *fp;
         EXPECT_TRUE(NULL != (fp = popen(command, "r")));
         if (fp) {
-            char buffer[5120];
+            char buffer[BIG_BUFFER];
             int count = 0;
 
             while (fgets(buffer, sizeof(buffer), fp)) {
@@ -609,7 +629,7 @@
 
         FILE *fp;
         EXPECT_TRUE(NULL != (fp = popen(command, "r")));
-        char buffer[5120];
+        char buffer[BIG_BUFFER];
         int log_file_count = 0;
 
         while (fgets(buffer, sizeof(buffer), fp)) {
@@ -751,6 +771,82 @@
     EXPECT_FALSE(system(command));
 }
 
+TEST(logcat, logrotate_clear) {
+    static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+    char tmp_out_dir[sizeof(tmp_out_dir_form)];
+    ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
+
+    static const char log_filename[] = "log.txt";
+    static const unsigned num_val = 32;
+    static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n %d -r 1";
+    static const char clear_cmd[] = " -c";
+    static const char cleanup_cmd[] = "rm -rf %s";
+    char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename) + sizeof(clear_cmd) + 32];
+
+    // Run command with all data
+    {
+        snprintf(command, sizeof(command) - sizeof(clear_cmd),
+                 logcat_cmd, tmp_out_dir, log_filename, num_val);
+
+        int ret;
+        EXPECT_FALSE((ret = system(command)));
+        if (ret) {
+            snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+            EXPECT_FALSE(system(command));
+            return;
+        }
+        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+        EXPECT_NE(nullptr, dir);
+        if (!dir) {
+            snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+            EXPECT_FALSE(system(command));
+            return;
+        }
+        struct dirent *entry;
+        unsigned count = 0;
+        while ((entry = readdir(dir.get()))) {
+            if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
+                continue;
+            }
+            ++count;
+        }
+        EXPECT_EQ(count, num_val + 1);
+    }
+
+    {
+        // Now with -c option tacked onto the end
+        strcat(command, clear_cmd);
+
+        int ret;
+        EXPECT_FALSE((ret = system(command)));
+        if (ret) {
+            snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+            EXPECT_FALSE(system(command));
+            return;
+        }
+        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+        EXPECT_NE(nullptr, dir);
+        if (!dir) {
+            snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+            EXPECT_FALSE(system(command));
+            return;
+        }
+        struct dirent *entry;
+        unsigned count = 0;
+        while ((entry = readdir(dir.get()))) {
+            if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
+                continue;
+            }
+            fprintf(stderr, "Found %s/%s!!!\n", tmp_out_dir, entry->d_name);
+            ++count;
+        }
+        EXPECT_EQ(count, 0U);
+    }
+
+    snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+    EXPECT_FALSE(system(command));
+}
+
 TEST(logcat, logrotate_nodir) {
     // expect logcat to error out on writing content and exit(1) for nodir
     EXPECT_EQ(W_EXITCODE(1, 0),
@@ -783,7 +879,7 @@
       " logcat -v brief -b events 2>&1",
       "r")));
 
-    char buffer[5120];
+    char buffer[BIG_BUFFER];
 
     int count = 0;
 
@@ -844,7 +940,7 @@
         return false;
     }
 
-    char buffer[5120];
+    char buffer[BIG_BUFFER];
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         char *hold = *list;
@@ -873,7 +969,7 @@
 static bool set_white_black(const char *list) {
     FILE *fp;
 
-    char buffer[5120];
+    char buffer[BIG_BUFFER];
 
     snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list ? list : "");
     fp = popen(buffer, "r");
@@ -935,7 +1031,7 @@
     FILE *fp;
     int count = 0;
 
-    char buffer[5120];
+    char buffer[BIG_BUFFER];
 
     snprintf(buffer, sizeof(buffer), "logcat --pid %d -d -e logcat_test_a+b", getpid());
 
@@ -968,7 +1064,7 @@
     FILE *fp;
     int count = 0;
 
-    char buffer[5120];
+    char buffer[BIG_BUFFER];
 
     snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3", getpid());
 
diff --git a/logd/README.property b/logd/README.property
index 22f86b9..b2708e4 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -1,4 +1,4 @@
-The properties that logd responds to are:
+The properties that logd and friends react to are:
 
 name                       type default  description
 ro.logd.auditd             bool   true   Enable selinux audit daemon
@@ -10,8 +10,16 @@
 ro.logd.statistics         bool+ svelte+ Enable logcat -S statistics.
 ro.build.type              string        if user, logd.statistics &
                                          ro.logd.kernel default false.
+logd.logpersistd.enable    bool   auto   Safe to start logpersist daemon service
+logd.logpersistd          string persist Enable logpersist daemon, "logcatd"
+                                         turns on logcat -f in logd context.
+					 Responds to logcatd, clear and stop.
+logd.logpersistd.buffer          persist logpersistd buffers to collect
+logd.logpersistd.size            persist logpersistd size in MB
 persist.logd.logpersistd   string        Enable logpersist daemon, "logcatd"
-                                         turns on logcat -f in logd context
+                                         turns on logcat -f in logd context.
+persist.logd.logpersistd.buffer    all   logpersistd buffers to collect
+persist.logd.logpersistd.size      256   logpersistd size in MB
 persist.logd.size          number  ro    Global default size of the buffer for
                                          all log ids at initial startup, at
                                          runtime use: logcat -b all -G <value>
@@ -45,6 +53,7 @@
 persist.log.tag.<tag>      string build  default for log.tag.<tag>
 
 NB:
+- auto - managed by /init
 - bool+ - "true", "false" and comma separated list of "eng" (forced false if
   ro.build.type is "user") or "svelte" (forced false if ro.config.low_ram is
   true).