Merge "liblog: Add private interfaces for buffer size properties"
diff --git a/include/private/android_logger.h b/include/private/android_logger.h
index 94095d1..02764d3 100644
--- a/include/private/android_logger.h
+++ b/include/private/android_logger.h
@@ -21,6 +21,7 @@
 
 /* Android private interfaces */
 
+#include <stdbool.h>
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -132,6 +133,20 @@
 
 int __android_log_is_debuggable();
 
+#define BOOL_DEFAULT_FLAG_TRUE_FALSE 0x1
+#define BOOL_DEFAULT_FALSE       0x0     /* false if property not present   */
+#define BOOL_DEFAULT_TRUE        0x1     /* true if property not present    */
+#define BOOL_DEFAULT_FLAG_PERSIST    0x2 /* <key>, persist.<key>, ro.<key>  */
+#define BOOL_DEFAULT_FLAG_ENG        0x4 /* off for user                    */
+#define BOOL_DEFAULT_FLAG_SVELTE     0x8 /* off for low_ram                 */
+bool __android_logger_property_get_bool(const char *key, int flag);
+
+#define LOG_BUFFER_SIZE (256 * 1024) /* Tuned with ro.logd.size per-platform */
+#define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
+#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL)
+unsigned long __android_logger_get_buffer_size(log_id_t logId);
+bool __android_logger_valid_buffer_size(unsigned long value);
+
 #if defined(__cplusplus)
 }
 #endif
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c
index 3dfc6a6..132d96f 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -16,12 +16,16 @@
 
 #include <ctype.h>
 #include <pthread.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
+#include <unistd.h>
 
 #include <android/log.h>
+#include <log/logger.h>
+#include <private/android_logger.h>
 
 #include "log_portability.h"
 
@@ -47,12 +51,16 @@
 }
 
 struct cache {
-    const prop_info *pinfo;
+    const prop_info* pinfo;
     uint32_t serial;
+};
+
+struct cache_char {
+    struct cache cache;
     unsigned char c;
 };
 
-static int check_cache(struct cache *cache)
+static int check_cache(struct cache* cache)
 {
     return cache->pinfo
         && __system_property_serial(cache->pinfo) != cache->serial;
@@ -61,18 +69,18 @@
 #define BOOLEAN_TRUE 0xFF
 #define BOOLEAN_FALSE 0xFE
 
-static void refresh_cache(struct cache *cache, const char *key)
+static void refresh_cache(struct cache_char* cache, const char* key)
 {
     char buf[PROP_VALUE_MAX];
 
-    if (!cache->pinfo) {
-        cache->pinfo = __system_property_find(key);
-        if (!cache->pinfo) {
+    if (!cache->cache.pinfo) {
+        cache->cache.pinfo = __system_property_find(key);
+        if (!cache->cache.pinfo) {
             return;
         }
     }
-    cache->serial = __system_property_serial(cache->pinfo);
-    __system_property_read(cache->pinfo, 0, buf);
+    cache->cache.serial = __system_property_serial(cache->cache.pinfo);
+    __system_property_read(cache->cache.pinfo, 0, buf);
     switch(buf[0]) {
     case 't': case 'T':
         cache->c = strcasecmp(buf + 1, "rue") ? buf[0] : BOOLEAN_TRUE;
@@ -85,7 +93,7 @@
     }
 }
 
-static int __android_log_level(const char *tag, size_t len, int default_prio)
+static int __android_log_level(const char* tag, size_t len, int default_prio)
 {
     /* sizeof() is used on this array below */
     static const char log_namespace[] = "persist.log.tag.";
@@ -93,8 +101,8 @@
     /* calculate the size of our key temporary buffer */
     const size_t taglen = tag ? len : 0;
     /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
-    char key[sizeof(log_namespace) + taglen]; /* may be > PROPERTY_KEY_MAX */
-    char *kp;
+    char key[sizeof(log_namespace) + taglen]; /* may be > PROP_NAME_MAX */
+    char* kp;
     size_t i;
     char c = 0;
     /*
@@ -110,8 +118,8 @@
     static uint32_t global_serial;
     /* some compilers erroneously see uninitialized use. !not_locked */
     uint32_t current_global_serial = 0;
-    static struct cache tag_cache[2];
-    static struct cache global_cache[2];
+    static struct cache_char tag_cache[2];
+    static struct cache_char global_cache[2];
     int change_detected;
     int global_change_detected;
     int not_locked;
@@ -125,12 +133,12 @@
          *  check all known serial numbers to changes.
          */
         for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
-            if (check_cache(&tag_cache[i])) {
+            if (check_cache(&tag_cache[i].cache)) {
                 change_detected = 1;
             }
         }
         for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
-            if (check_cache(&global_cache[i])) {
+            if (check_cache(&global_cache[i].cache)) {
                 global_change_detected = 1;
             }
         }
@@ -154,7 +162,7 @@
                     || ((len < sizeof(last_tag)) && last_tag[len])) {
                 /* invalidate log.tag.<tag> cache */
                 for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
-                    tag_cache[i].pinfo = NULL;
+                    tag_cache[i].cache.pinfo = NULL;
                     tag_cache[i].c = '\0';
                 }
                 last_tag[0] = '\0';
@@ -174,11 +182,11 @@
 
         kp = key;
         for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
-            struct cache *cache = &tag_cache[i];
-            struct cache temp_cache;
+            struct cache_char* cache = &tag_cache[i];
+            struct cache_char temp_cache;
 
             if (not_locked) {
-                temp_cache.pinfo = NULL;
+                temp_cache.cache.pinfo = NULL;
                 temp_cache.c = '\0';
                 cache = &temp_cache;
             }
@@ -212,13 +220,13 @@
 
         kp = key;
         for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
-            struct cache *cache = &global_cache[i];
-            struct cache temp_cache;
+            struct cache_char* cache = &global_cache[i];
+            struct cache_char temp_cache;
 
             if (not_locked) {
                 temp_cache = *cache;
-                if (temp_cache.pinfo != cache->pinfo) { /* check atomic */
-                    temp_cache.pinfo = NULL;
+                if (temp_cache.cache.pinfo != cache->cache.pinfo) { /* check atomic */
+                    temp_cache.cache.pinfo = NULL;
                     temp_cache.c = '\0';
                 }
                 cache = &temp_cache;
@@ -257,7 +265,7 @@
 }
 
 LIBLOG_ABI_PUBLIC int __android_log_is_loggable_len(int prio,
-                                                    const char *tag, size_t len,
+                                                    const char* tag, size_t len,
                                                     int default_prio)
 {
     int logLevel = __android_log_level(tag, len, default_prio);
@@ -265,7 +273,7 @@
 }
 
 LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio,
-                                                const char *tag,
+                                                const char* tag,
                                                 int default_prio)
 {
     int logLevel = __android_log_level(tag,
@@ -277,18 +285,18 @@
 LIBLOG_ABI_PRIVATE int __android_log_is_debuggable()
 {
     static uint32_t serial;
-    static struct cache tag_cache;
+    static struct cache_char tag_cache;
     static const char key[] = "ro.debuggable";
     int ret;
 
     if (tag_cache.c) { /* ro property does not change after set */
         ret = tag_cache.c == '1';
     } else if (lock()) {
-        struct cache temp_cache = { NULL, -1, '\0' };
+        struct cache_char temp_cache = { { NULL, -1 }, '\0' };
         refresh_cache(&temp_cache, key);
         ret = temp_cache.c == '1';
     } else {
-        int change_detected = check_cache(&tag_cache);
+        int change_detected = check_cache(&tag_cache.cache);
         uint32_t current_serial = __system_property_area_serial();
         if (current_serial != serial) {
             change_detected = 1;
@@ -310,17 +318,17 @@
  * Since a change is rare, we will accept a trylock failure gracefully.
  * Use a separate lock from is_loggable to keep contention down b/25563384.
  */
-struct cache2 {
+struct cache2_char {
     pthread_mutex_t lock;
     uint32_t serial;
-    const char *key_persist;
-    struct cache cache_persist;
-    const char *key_ro;
-    struct cache cache_ro;
-    unsigned char (*const evaluate)(const struct cache2 *self);
+    const char* key_persist;
+    struct cache_char cache_persist;
+    const char* key_ro;
+    struct cache_char cache_ro;
+    unsigned char (*const evaluate)(const struct cache2_char *self);
 };
 
-static inline unsigned char do_cache2(struct cache2 *self)
+static inline unsigned char do_cache2_char(struct cache2_char *self)
 {
     uint32_t current_serial;
     int change_detected;
@@ -331,8 +339,8 @@
         return self->evaluate(self);
     }
 
-    change_detected = check_cache(&self->cache_persist)
-                   || check_cache(&self->cache_ro);
+    change_detected = check_cache(&self->cache_persist.cache)
+                   || check_cache(&self->cache_ro.cache);
     current_serial = __system_property_area_serial();
     if (current_serial != self->serial) {
         change_detected = 1;
@@ -349,7 +357,7 @@
     return c;
 }
 
-static unsigned char evaluate_persist_ro(const struct cache2 *self)
+static unsigned char evaluate_persist_ro(const struct cache2_char *self)
 {
     unsigned char c = self->cache_persist.c;
 
@@ -366,17 +374,17 @@
  */
 LIBLOG_ABI_PUBLIC clockid_t android_log_clockid()
 {
-    static struct cache2 clockid = {
+    static struct cache2_char clockid = {
         PTHREAD_MUTEX_INITIALIZER,
         0,
         "persist.logd.timestamp",
-        { NULL, -1, '\0' },
+        { { NULL, -1 }, '\0' },
         "ro.logd.timestamp",
-        { NULL, -1, '\0' },
+        { { NULL, -1 }, '\0' },
         evaluate_persist_ro
     };
 
-    return (tolower(do_cache2(&clockid)) == 'm')
+    return (tolower(do_cache2_char(&clockid)) == 'm')
         ? CLOCK_MONOTONIC
         : CLOCK_REALTIME;
 }
@@ -385,7 +393,7 @@
  * Security state generally remains constant, but the DO must be able
  * to turn off logging should it become spammy after an attack is detected.
  */
-static unsigned char evaluate_security(const struct cache2 *self)
+static unsigned char evaluate_security(const struct cache2_char *self)
 {
     unsigned char c = self->cache_ro.c;
 
@@ -394,15 +402,265 @@
 
 LIBLOG_ABI_PUBLIC int __android_log_security()
 {
-    static struct cache2 security = {
+    static struct cache2_char security = {
         PTHREAD_MUTEX_INITIALIZER,
         0,
         "persist.logd.security",
-        { NULL, -1, BOOLEAN_FALSE },
+        { { NULL, -1 }, BOOLEAN_FALSE },
         "ro.device_owner",
-        { NULL, -1, BOOLEAN_FALSE },
+        { { NULL, -1 }, BOOLEAN_FALSE },
         evaluate_security
     };
 
-    return do_cache2(&security);
+    return do_cache2_char(&security);
+}
+
+/*
+ * Interface that represents the logd buffer size determination so that others
+ * need not guess our intentions.
+ */
+
+/* Property helper */
+static bool check_flag(const char* prop, const char* flag) {
+    const char* cp = strcasestr(prop, flag);
+    if (!cp) {
+        return false;
+    }
+    /* We only will document comma (,) */
+    static const char sep[] = ",:;|+ \t\f";
+    if ((cp != prop) && !strchr(sep, cp[-1])) {
+        return false;
+    }
+    cp += strlen(flag);
+    return !*cp || !!strchr(sep, *cp);
+}
+
+/* cache structure */
+struct cache_property {
+    struct cache cache;
+    char property[PROP_VALUE_MAX];
+};
+
+static void refresh_cache_property(struct cache_property* cache, const char* key)
+{
+    if (!cache->cache.pinfo) {
+        cache->cache.pinfo = __system_property_find(key);
+        if (!cache->cache.pinfo) {
+            return;
+        }
+    }
+    cache->cache.serial = __system_property_serial(cache->cache.pinfo);
+    __system_property_read(cache->cache.pinfo, 0, cache->property);
+}
+
+/* get boolean with the logger twist that supports eng adjustments */
+LIBLOG_ABI_PRIVATE bool __android_logger_property_get_bool(const char* key,
+                                                           int flag)
+{
+    struct cache_property property = { { NULL, -1 }, { 0 } };
+    if (flag & BOOL_DEFAULT_FLAG_PERSIST) {
+        char newkey[PROP_NAME_MAX];
+        snprintf(newkey, sizeof(newkey), "ro.%s", key);
+        refresh_cache_property(&property, newkey);
+        property.cache.pinfo = NULL;
+        property.cache.serial = -1;
+        snprintf(newkey, sizeof(newkey), "persist.%s", key);
+        refresh_cache_property(&property, newkey);
+        property.cache.pinfo = NULL;
+        property.cache.serial = -1;
+    }
+
+    refresh_cache_property(&property, key);
+
+    if (check_flag(property.property, "true")) {
+        return true;
+    }
+    if (check_flag(property.property, "false")) {
+        return false;
+    }
+    if (check_flag(property.property, "eng")) {
+       flag |= BOOL_DEFAULT_FLAG_ENG;
+    }
+    /* this is really a "not" flag */
+    if (check_flag(property.property, "svelte")) {
+       flag |= BOOL_DEFAULT_FLAG_SVELTE;
+    }
+
+    /* Sanity Check */
+    if (flag & (BOOL_DEFAULT_FLAG_SVELTE | BOOL_DEFAULT_FLAG_ENG)) {
+        flag &= ~BOOL_DEFAULT_FLAG_TRUE_FALSE;
+        flag |= BOOL_DEFAULT_TRUE;
+    }
+
+    if ((flag & BOOL_DEFAULT_FLAG_SVELTE)
+            && __android_logger_property_get_bool("ro.config.low_ram",
+                                 BOOL_DEFAULT_FALSE)) {
+        return false;
+    }
+    if ((flag & BOOL_DEFAULT_FLAG_ENG) && !__android_log_is_debuggable()) {
+        return false;
+    }
+
+    return (flag & BOOL_DEFAULT_FLAG_TRUE_FALSE) != BOOL_DEFAULT_FALSE;
+}
+
+LIBLOG_ABI_PRIVATE bool __android_logger_valid_buffer_size(unsigned long value)
+{
+    static long pages, pagesize;
+    unsigned long maximum;
+
+    if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) {
+        return false;
+    }
+
+    if (!pages) {
+        pages = sysconf(_SC_PHYS_PAGES);
+    }
+    if (pages < 1) {
+        return true;
+    }
+
+    if (!pagesize) {
+        pagesize = sysconf(_SC_PAGESIZE);
+        if (pagesize <= 1) {
+            pagesize = PAGE_SIZE;
+        }
+    }
+
+    /* maximum memory impact a somewhat arbitrary ~3% */
+    pages = (pages + 31) / 32;
+    maximum = pages * pagesize;
+
+    if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) {
+        return true;
+    }
+
+    return value <= maximum;
+}
+
+struct cache2_property_size {
+    pthread_mutex_t lock;
+    uint32_t serial;
+    const char* key_persist;
+    struct cache_property cache_persist;
+    const char* key_ro;
+    struct cache_property cache_ro;
+    unsigned long (*const evaluate)(const struct cache2_property_size* self);
+};
+
+static inline unsigned long do_cache2_property_size(struct cache2_property_size* self)
+{
+    uint32_t current_serial;
+    int change_detected;
+    unsigned long v;
+
+    if (pthread_mutex_trylock(&self->lock)) {
+        /* We are willing to accept some race in this context */
+        return self->evaluate(self);
+    }
+
+    change_detected = check_cache(&self->cache_persist.cache)
+                   || check_cache(&self->cache_ro.cache);
+    current_serial = __system_property_area_serial();
+    if (current_serial != self->serial) {
+        change_detected = 1;
+    }
+    if (change_detected) {
+        refresh_cache_property(&self->cache_persist, self->key_persist);
+        refresh_cache_property(&self->cache_ro, self->key_ro);
+        self->serial = current_serial;
+    }
+    v = self->evaluate(self);
+
+    pthread_mutex_unlock(&self->lock);
+
+    return v;
+}
+
+static unsigned long property_get_size_from_cache(const struct cache_property* cache)
+{
+    char* cp;
+    unsigned long value = strtoul(cache->property, &cp, 10);
+
+    switch(*cp) {
+    case 'm':
+    case 'M':
+        value *= 1024;
+    /* FALLTHRU */
+    case 'k':
+    case 'K':
+        value *= 1024;
+    /* FALLTHRU */
+    case '\0':
+        break;
+
+    default:
+        value = 0;
+    }
+
+    if (!__android_logger_valid_buffer_size(value)) {
+        value = 0;
+    }
+
+    return value;
+}
+
+static unsigned long evaluate_property_get_size(const struct cache2_property_size* self)
+{
+    unsigned long size = property_get_size_from_cache(&self->cache_persist);
+    if (size) {
+        return size;
+    }
+    return property_get_size_from_cache(&self->cache_ro);
+}
+
+LIBLOG_ABI_PRIVATE unsigned long __android_logger_get_buffer_size(log_id_t logId)
+{
+    static const char global_tunable[] = "persist.logd.size"; /* Settings App */
+    static const char global_default[] = "ro.logd.size"; /* BoardConfig.mk */
+    static struct cache2_property_size global = {
+        PTHREAD_MUTEX_INITIALIZER,
+        0,
+        global_tunable,
+        { { NULL, -1 }, {} },
+        global_default,
+        { { NULL, -1 }, {} },
+        evaluate_property_get_size
+    };
+    char key_persist[PROP_NAME_MAX];
+    char key_ro[PROP_NAME_MAX];
+    struct cache2_property_size local = {
+        PTHREAD_MUTEX_INITIALIZER,
+        0,
+        key_persist,
+        { { NULL, -1 }, {} },
+        key_ro,
+        { { NULL, -1 }, {} },
+        evaluate_property_get_size
+    };
+    unsigned long property_size, default_size;
+
+    default_size =  do_cache2_property_size(&global);
+    if (!default_size) {
+        default_size = __android_logger_property_get_bool("ro.config.low_ram",
+                                                          BOOL_DEFAULT_FALSE)
+            ? LOG_BUFFER_MIN_SIZE /* 64K  */
+            : LOG_BUFFER_SIZE;    /* 256K */
+    }
+
+    snprintf(key_persist, sizeof(key_persist), "%s.%s",
+             global_tunable, android_log_id_to_name(logId));
+    snprintf(key_ro, sizeof(key_ro), "%s.%s",
+             global_default, android_log_id_to_name(logId));
+    property_size = do_cache2_property_size(&local);
+
+    if (!property_size) {
+        property_size = default_size;
+    }
+
+    if (!property_size) {
+        property_size = LOG_BUFFER_SIZE;
+    }
+
+    return property_size;
 }