Move minimum log priority from libbase to liblog

See the previous commit moving SetLogger and SetAborter to liblog for
motivation.

This creates more harmony between the two mechanisms in libbase and
liblog for checking loggability.
Currently:
1) libbase filters all messages based on its minimum log priority. For
   example, if minimum log priority in libbase remained at its
   default, but a tag was specifically opted into DEBUG logs via
   log.tag.<tag>, libbase would not print this log.
2) liblog ignores libbase's minimum log priority.  For example if a
   process called SetMinimumLogPriority(WARNING) but used a library
   that logged via liblog's ALOGI macro, that log would still be
   printed even though the process intends on filtering out those INFO
   messages.

With this change:
1) If both a minimum log priority and a priority through log.tag.<tag>
   are set, then the lower of the two values is used.
2) If only one or the other is set, then that value is used.  This
   fixes the two issues described above.
3) If neither of these values are set, then the default of using INFO
   is unchanged.

Bug: 116329414
Bug: 119867234
Test: libbase and liblog minimum log priority tests
Change-Id: Icb49b30b9d93bf797470e23730ae9e537931bb6c
diff --git a/liblog/fake_log_device.cpp b/liblog/fake_log_device.cpp
index 2582cea..f1147dc 100644
--- a/liblog/fake_log_device.cpp
+++ b/liblog/fake_log_device.cpp
@@ -517,13 +517,16 @@
 }
 
 int __android_log_is_loggable(int prio, const char*, int def) {
-  int logLevel = def;
-  return logLevel >= 0 && prio >= logLevel;
+  int minimum_priority = __android_log_get_minimum_priority();
+  if (minimum_priority == ANDROID_LOG_DEFAULT) {
+    return prio >= def;
+  } else {
+    return prio >= minimum_priority;
+  }
 }
 
 int __android_log_is_loggable_len(int prio, const char*, size_t, int def) {
-  int logLevel = def;
-  return logLevel >= 0 && prio >= logLevel;
+  return __android_log_is_loggable(prio, nullptr, def);
 }
 
 int __android_log_is_debuggable() {
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index 39f885c..6530704 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -244,6 +244,34 @@
  */
 void __android_log_default_aborter(const char* abort_message);
 
+/**
+ * Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
+ * __android_log_set_minimum_priority() to determine if a log message with a given prio and tag will
+ * be printed.  A non-zero result indicates yes, zero indicates false.
+ *
+ * If both a priority for a tag and a minimum priority are set by
+ * __android_log_set_minimum_priority(), then the lowest of the two values are to determine the
+ * minimum priority needed to log.  If only one is set, then that value is used to determine the
+ * minimum priority needed.  If none are set, then default_priority is used.
+ *
+ * prio is ANDROID_LOG_VERBOSE to ANDROID_LOG_FATAL.
+ */
+int __android_log_is_loggable(int prio, const char* tag, int default_prio);
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio);
+
+/**
+ * Sets the minimum priority that will be logged for this process.
+ *
+ * This returns the previous set minimum priority, or ANDROID_LOG_DEFAULT if none was set.
+ */
+int __android_log_set_minimum_priority(int priority);
+
+/**
+ * Gets the minimum priority that will be logged for this process.  If none has been set by a
+ * previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
+ */
+int __android_log_get_minimum_priority();
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index ae2a891..62b9805 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -69,10 +69,12 @@
   global:
     __android_log_call_aborter;
     __android_log_default_aborter;
+    __android_log_get_minimum_priority;
     __android_log_logd_logger;
     __android_log_security_bswrite; # apex
     __android_log_set_aborter;
     __android_log_set_logger;
+    __android_log_set_minimum_priority;
     __android_log_stderr_logger;
     __android_log_write_logger_data;
 };
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index abc24ce..61b6fce 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -106,6 +106,17 @@
 #endif
 }
 
+static int minimum_log_priority = ANDROID_LOG_DEFAULT;
+int __android_log_set_minimum_priority(int priority) {
+  int old_minimum_log_priority = minimum_log_priority;
+  minimum_log_priority = priority;
+  return old_minimum_log_priority;
+}
+
+int __android_log_get_minimum_priority() {
+  return minimum_log_priority;
+}
+
 #ifdef __ANDROID__
 static __android_logger_function logger_function = __android_log_logd_logger;
 #else
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index 2b2327c..a53c92b 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -24,6 +24,8 @@
 #include <sys/_system_properties.h>
 #include <unistd.h>
 
+#include <algorithm>
+
 #include <private/android_logger.h>
 
 static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
@@ -87,7 +89,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) {
   /* sizeof() is used on this array below */
   static const char log_namespace[] = "persist.log.tag.";
   static const size_t base_offset = 8; /* skip "persist." */
@@ -256,20 +258,30 @@
     case 'F': /* FALLTHRU */ /* Not officially supported */
     case 'A': return ANDROID_LOG_FATAL;
     case BOOLEAN_FALSE: /* FALLTHRU */ /* Not Officially supported */
-    case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
+    case 'S': return ANDROID_LOG_SILENT;
       /* clang-format on */
   }
-  return default_prio;
+  return -1;
 }
 
 int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio) {
-  int logLevel = __android_log_level(tag, len, default_prio);
-  return logLevel >= 0 && prio >= logLevel;
+  int minimum_log_priority = __android_log_get_minimum_priority();
+  int property_log_level = __android_log_level(tag, len);
+
+  if (property_log_level >= 0 && minimum_log_priority != ANDROID_LOG_DEFAULT) {
+    return prio >= std::min(property_log_level, minimum_log_priority);
+  } else if (property_log_level >= 0) {
+    return prio >= property_log_level;
+  } else if (minimum_log_priority != ANDROID_LOG_DEFAULT) {
+    return prio >= minimum_log_priority;
+  } else {
+    return prio >= default_prio;
+  }
 }
 
 int __android_log_is_loggable(int prio, const char* tag, int default_prio) {
-  int logLevel = __android_log_level(tag, (tag && *tag) ? strlen(tag) : 0, default_prio);
-  return logLevel >= 0 && prio >= logLevel;
+  auto len = tag ? strlen(tag) : 0;
+  return __android_log_is_loggable_len(prio, tag, len, default_prio);
 }
 
 int __android_log_is_debuggable() {
diff --git a/liblog/tests/liblog_global_state.cpp b/liblog/tests/liblog_global_state.cpp
index 23e8758..8d73bb8 100644
--- a/liblog/tests/liblog_global_state.cpp
+++ b/liblog/tests/liblog_global_state.cpp
@@ -18,6 +18,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android/log.h>
 
 #include <gtest/gtest.h>
@@ -153,3 +154,90 @@
   EXPECT_TRUE(message_seen);
   message_seen = false;
 }
+
+TEST(liblog_global_state, is_loggable_both_default) {
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+}
+
+TEST(liblog_global_state, is_loggable_minimum_log_priority_only) {
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  EXPECT_EQ(android::base::WARNING, android::base::SetMinimumLogSeverity(android::base::DEBUG));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  EXPECT_EQ(android::base::DEBUG, android::base::SetMinimumLogSeverity(android::base::WARNING));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+}
+
+TEST(liblog_global_state, is_loggable_tag_log_priority_only) {
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  auto log_tag_property = std::string("log.tag.") + LOG_TAG;
+  android::base::SetProperty(log_tag_property, "d");
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  android::base::SetProperty(log_tag_property, "w");
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  android::base::SetProperty(log_tag_property, "");
+}
+
+TEST(liblog_global_state, is_loggable_both_set) {
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  // When both a tag and a minimum priority are set, we use the lower value of the two.
+
+  // tag = warning, minimum_priority = debug, expect 'debug'
+  auto log_tag_property = std::string("log.tag.") + LOG_TAG;
+  android::base::SetProperty(log_tag_property, "w");
+  EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  // tag = warning, minimum_priority = warning, expect 'warning'
+  EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  // tag = debug, minimum_priority = warning, expect 'debug'
+  android::base::SetProperty(log_tag_property, "d");
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  // tag = debug, minimum_priority = debug, expect 'debug'
+  EXPECT_EQ(ANDROID_LOG_WARN, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  android::base::SetProperty(log_tag_property, "");
+}
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 9cae702..75a26bf 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1258,14 +1258,10 @@
     int level;
     char type;
   } levels[] = {
-    { ANDROID_LOG_VERBOSE, 'v' },
-    { ANDROID_LOG_DEBUG, 'd' },
-    { ANDROID_LOG_INFO, 'i' },
-    { ANDROID_LOG_WARN, 'w' },
-    { ANDROID_LOG_ERROR, 'e' },
-    { ANDROID_LOG_FATAL, 'a' },
-    { -1, 's' },
-    { -2, 'g' },  // Illegal value, resort to default
+      {ANDROID_LOG_VERBOSE, 'v'}, {ANDROID_LOG_DEBUG, 'd'},
+      {ANDROID_LOG_INFO, 'i'},    {ANDROID_LOG_WARN, 'w'},
+      {ANDROID_LOG_ERROR, 'e'},   {ANDROID_LOG_FATAL, 'a'},
+      {ANDROID_LOG_SILENT, 's'},  {-2, 'g'},  // Illegal value, resort to default
   };
 
   // Set up initial test condition