Merge "Ensure all init commands are documented."
diff --git a/adb/adb.cpp b/adb/adb.cpp
index d37ca36..ad85184f 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -79,18 +79,19 @@
 
 #if !ADB_HOST
 void start_device_log(void) {
-    adb_mkdir("/data/adb", 0775);
-
     struct tm now;
     time_t t;
     tzset();
     time(&t);
     localtime_r(&t, &now);
 
-    char path[PATH_MAX];
-    strftime(path, sizeof(path), "/data/adb/adb-%Y-%m-%d-%H-%M-%S.txt", &now);
+    char timestamp[PATH_MAX];
+    strftime(timestamp, sizeof(timestamp), "%Y-%m-%d-%H-%M-%S", &now);
 
-    int fd = unix_open(path, O_WRONLY | O_CREAT | O_TRUNC, 0640);
+    char path[PATH_MAX];
+    snprintf(path, sizeof(path), "/data/adb/adb-%s-%d", timestamp, getpid());
+
+    int fd = unix_open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
     if (fd == -1) {
         return;
     }
@@ -100,10 +101,6 @@
     dup2(fd, STDERR_FILENO);
     fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
     adb_close(fd);
-
-    fd = unix_open("/dev/null", O_RDONLY);
-    dup2(fd, 0);
-    adb_close(fd);
 }
 #endif
 
diff --git a/adb/adb_main.cpp b/adb/adb_main.cpp
index c1e4b13..a03fcf1 100644
--- a/adb/adb_main.cpp
+++ b/adb/adb_main.cpp
@@ -349,9 +349,23 @@
     return 0;
 }
 
+#if !ADB_HOST
+void close_stdin() {
+    int fd = unix_open("/dev/null", O_RDONLY);
+    if (fd == -1) {
+        perror("failed to open /dev/null, stdin will remain open");
+        return;
+    }
+    dup2(fd, 0);
+    adb_close(fd);
+}
+#endif
+
 int main(int argc, char **argv) {
 #if ADB_HOST
     adb_sysdeps_init();
+#else
+    close_stdin();
 #endif
     adb_trace_init();
 
diff --git a/base/Android.mk b/base/Android.mk
index 17d6ece..162c6cb 100644
--- a/base/Android.mk
+++ b/base/Android.mk
@@ -18,13 +18,17 @@
 
 libbase_src_files := \
     file.cpp \
+    logging.cpp \
     stringprintf.cpp \
     strings.cpp \
 
 libbase_test_src_files := \
     file_test.cpp \
+    logging_test.cpp \
     stringprintf_test.cpp \
     strings_test.cpp \
+    test_main.cpp \
+    test_utils.cpp \
 
 libbase_cppflags := \
     -Wall \
@@ -77,6 +81,7 @@
 LOCAL_MODULE := libbase_test
 LOCAL_CLANG := true
 LOCAL_SRC_FILES := $(libbase_test_src_files)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)
 LOCAL_CPPFLAGS := $(libbase_cppflags)
 LOCAL_SHARED_LIBRARIES := libbase
 LOCAL_MULTILIB := both
@@ -87,6 +92,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := libbase_test
 LOCAL_SRC_FILES := $(libbase_test_src_files)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)
 LOCAL_CPPFLAGS := $(libbase_cppflags)
 LOCAL_SHARED_LIBRARIES := libbase
 LOCAL_MULTILIB := both
diff --git a/base/CPPLINT.cfg b/base/CPPLINT.cfg
index 5ee068e..a61c08d 100644
--- a/base/CPPLINT.cfg
+++ b/base/CPPLINT.cfg
@@ -1,2 +1,2 @@
 set noparent
-filter=-build/header_guard
+filter=-build/header_guard,-build/include,-build/c++11
diff --git a/base/file_test.cpp b/base/file_test.cpp
index 34b8755..fc48b32 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -24,29 +24,7 @@
 
 #include <string>
 
-class TemporaryFile {
- public:
-  TemporaryFile() {
-    init("/data/local/tmp");
-    if (fd == -1) {
-      init("/tmp");
-    }
-  }
-
-  ~TemporaryFile() {
-    close(fd);
-    unlink(filename);
-  }
-
-  int fd;
-  char filename[1024];
-
- private:
-  void init(const char* tmp_dir) {
-    snprintf(filename, sizeof(filename), "%s/TemporaryFile-XXXXXX", tmp_dir);
-    fd = mkstemp(filename);
-  }
-};
+#include "test_utils.h"
 
 TEST(file, ReadFileToString_ENOENT) {
   std::string s("hello");
diff --git a/base/include/base/logging.h b/base/include/base/logging.h
new file mode 100644
index 0000000..5e115fe
--- /dev/null
+++ b/base/include/base/logging.h
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BASE_LOGGING_H
+#define BASE_LOGGING_H
+
+#include <memory>
+#include <ostream>
+
+#include "base/macros.h"
+
+namespace android {
+namespace base {
+
+enum LogSeverity {
+  VERBOSE,
+  DEBUG,
+  INFO,
+  WARNING,
+  ERROR,
+  FATAL,
+};
+
+// Configure logging based on ANDROID_LOG_TAGS environment variable.
+// We need to parse a string that looks like
+//
+//      *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
+//
+// The tag (or '*' for the global level) comes first, followed by a colon and a
+// letter indicating the minimum priority level we're expected to log.  This can
+// be used to reveal or conceal logs with specific tags.
+extern void InitLogging(char* argv[]);
+
+// Returns the command line used to invoke the current tool or nullptr if
+// InitLogging hasn't been performed.
+extern const char* GetCmdLine();
+
+// The command used to start the program, such as "/system/bin/dalvikvm". If
+// InitLogging hasn't been performed then just returns "unknown"
+extern const char* ProgramInvocationName();
+
+// A short version of the command used to start the program, such as "dalvikvm".
+// If InitLogging hasn't been performed then just returns "unknown"
+extern const char* ProgramInvocationShortName();
+
+// Logs a message to logcat on Android otherwise to stderr. If the severity is
+// FATAL it also causes an abort. For example:
+//
+//     LOG(FATAL) << "We didn't expect to reach here";
+#define LOG(severity)                                                        \
+  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::severity, \
+                              -1).stream()
+
+// A variant of LOG that also logs the current errno value. To be used when
+// library calls fail.
+#define PLOG(severity)                                                       \
+  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::severity, \
+                              errno).stream()
+
+// Marker that code is yet to be implemented.
+#define UNIMPLEMENTED(level) \
+  LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
+
+// Check whether condition x holds and LOG(FATAL) if not. The value of the
+// expression x is only evaluated once. Extra logging can be appended using <<
+// after. For example:
+//
+//     CHECK(false == true) results in a log message of
+//       "Check failed: false == true".
+#define CHECK(x)                                                            \
+  if (UNLIKELY(!(x)))                                                       \
+    ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, \
+                                -1).stream()                                \
+        << "Check failed: " #x << " "
+
+// Helper for CHECK_xx(x,y) macros.
+#define CHECK_OP(LHS, RHS, OP)                                                \
+  for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS);          \
+       UNLIKELY(!(_values.lhs OP _values.rhs));                               \
+       /* empty */)                                                           \
+  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, -1) \
+          .stream()                                                           \
+      << "Check failed: " << #LHS << " " << #OP << " " << #RHS                \
+      << " (" #LHS "=" << _values.lhs << ", " #RHS "=" << _values.rhs << ") "
+
+// Check whether a condition holds between x and y, LOG(FATAL) if not. The value
+// of the expressions x and y is evaluated once. Extra logging can be appended
+// using << after. For example:
+//
+//     CHECK_NE(0 == 1, false) results in
+//       "Check failed: false != false (0==1=false, false=false) ".
+#define CHECK_EQ(x, y) CHECK_OP(x, y, == )
+#define CHECK_NE(x, y) CHECK_OP(x, y, != )
+#define CHECK_LE(x, y) CHECK_OP(x, y, <= )
+#define CHECK_LT(x, y) CHECK_OP(x, y, < )
+#define CHECK_GE(x, y) CHECK_OP(x, y, >= )
+#define CHECK_GT(x, y) CHECK_OP(x, y, > )
+
+// Helper for CHECK_STRxx(s1,s2) macros.
+#define CHECK_STROP(s1, s2, sense)                                         \
+  if (UNLIKELY((strcmp(s1, s2) == 0) != sense))                            \
+    LOG(FATAL) << "Check failed: "                                         \
+               << "\"" << s1 << "\""                                       \
+               << (sense ? " == " : " != ") << "\"" << s2 << "\""
+
+// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
+#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
+#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
+
+// Perform the pthread function call(args), LOG(FATAL) on error.
+#define CHECK_PTHREAD_CALL(call, args, what)                           \
+  do {                                                                 \
+    int rc = call args;                                                \
+    if (rc != 0) {                                                     \
+      errno = rc;                                                      \
+      PLOG(FATAL) << #call << " failed for " << what; \
+    }                                                                  \
+  } while (false)
+
+// CHECK that can be used in a constexpr function. For example:
+//
+//    constexpr int half(int n) {
+//      return
+//          DCHECK_CONSTEXPR(n >= 0, , 0)
+//          CHECK_CONSTEXPR((n & 1) == 0),
+//              << "Extra debugging output: n = " << n, 0)
+//          n / 2;
+//    }
+#define CHECK_CONSTEXPR(x, out, dummy)                                     \
+  (UNLIKELY(!(x)))                                                         \
+      ? (LOG(FATAL) << "Check failed: " << #x out, dummy) \
+      :
+
+// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
+// CHECK should be used unless profiling identifies a CHECK as being in
+// performance critical code.
+#if defined(NDEBUG)
+static constexpr bool kEnableDChecks = false;
+#else
+static constexpr bool kEnableDChecks = true;
+#endif
+
+#define DCHECK(x) \
+  if (::android::base::kEnableDChecks) CHECK(x)
+#define DCHECK_EQ(x, y) \
+  if (::android::base::kEnableDChecks) CHECK_EQ(x, y)
+#define DCHECK_NE(x, y) \
+  if (::android::base::kEnableDChecks) CHECK_NE(x, y)
+#define DCHECK_LE(x, y) \
+  if (::android::base::kEnableDChecks) CHECK_LE(x, y)
+#define DCHECK_LT(x, y) \
+  if (::android::base::kEnableDChecks) CHECK_LT(x, y)
+#define DCHECK_GE(x, y) \
+  if (::android::base::kEnableDChecks) CHECK_GE(x, y)
+#define DCHECK_GT(x, y) \
+  if (::android::base::kEnableDChecks) CHECK_GT(x, y)
+#define DCHECK_STREQ(s1, s2) \
+  if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
+#define DCHECK_STRNE(s1, s2) \
+  if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
+#if defined(NDEBUG)
+#define DCHECK_CONSTEXPR(x, out, dummy)
+#else
+#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
+#endif
+
+// Temporary class created to evaluate the LHS and RHS, used with
+// MakeEagerEvaluator to infer the types of LHS and RHS.
+template <typename LHS, typename RHS>
+struct EagerEvaluator {
+  EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) {
+  }
+  LHS lhs;
+  RHS rhs;
+};
+
+// Helper function for CHECK_xx.
+template <typename LHS, typename RHS>
+static inline EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
+  return EagerEvaluator<LHS, RHS>(lhs, rhs);
+}
+
+// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated
+// as strings. To compare strings use CHECK_STREQ and CHECK_STRNE. We rely on
+// signed/unsigned warnings to protect you against combinations not explicitly
+// listed below.
+#define EAGER_PTR_EVALUATOR(T1, T2)               \
+  template <>                                     \
+  struct EagerEvaluator<T1, T2> {                 \
+    EagerEvaluator(T1 l, T2 r)                    \
+        : lhs(reinterpret_cast<const void*>(l)),  \
+          rhs(reinterpret_cast<const void*>(r)) { \
+    }                                             \
+    const void* lhs;                              \
+    const void* rhs;                              \
+  }
+EAGER_PTR_EVALUATOR(const char*, const char*);
+EAGER_PTR_EVALUATOR(const char*, char*);
+EAGER_PTR_EVALUATOR(char*, const char*);
+EAGER_PTR_EVALUATOR(char*, char*);
+EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*);
+EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*);
+EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*);
+EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*);
+EAGER_PTR_EVALUATOR(const signed char*, const signed char*);
+EAGER_PTR_EVALUATOR(const signed char*, signed char*);
+EAGER_PTR_EVALUATOR(signed char*, const signed char*);
+EAGER_PTR_EVALUATOR(signed char*, signed char*);
+
+// Data for the log message, not stored in LogMessage to avoid increasing the
+// stack size.
+class LogMessageData;
+
+// A LogMessage is a temporarily scoped object used by LOG and the unlikely part
+// of a CHECK. The destructor will abort if the severity is FATAL.
+class LogMessage {
+ public:
+  LogMessage(const char* file, unsigned int line, LogSeverity severity,
+             int error);
+
+  ~LogMessage();
+
+  // Returns the stream associated with the message, the LogMessage performs
+  // output when it goes out of scope.
+  std::ostream& stream();
+
+  // The routine that performs the actual logging.
+  static void LogLine(const char* file, unsigned int line, LogSeverity severity,
+                      const char* msg);
+
+  // A variant of the above for use with little stack.
+  static void LogLineLowStack(const char* file, unsigned int line,
+                              LogSeverity severity, const char* msg);
+
+ private:
+  const std::unique_ptr<LogMessageData> data_;
+
+  DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+// Allows to temporarily change the minimum severity level for logging.
+class ScopedLogSeverity {
+ public:
+  explicit ScopedLogSeverity(LogSeverity level);
+  ~ScopedLogSeverity();
+
+ private:
+  LogSeverity old_;
+};
+
+}  // namespace base
+}  // namespace android
+
+#endif  // BASE_LOGGING_H
diff --git a/base/logging.cpp b/base/logging.cpp
new file mode 100644
index 0000000..3d6c0c2
--- /dev/null
+++ b/base/logging.cpp
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2015 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 "base/logging.h"
+
+#include <iostream>
+#include <limits>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "base/strings.h"
+
+// Headers for LogMessage::LogLine.
+#ifdef __ANDROID__
+#include <android/set_abort_message.h>
+#include "cutils/log.h"
+#else
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+// For GetTid.
+#if defined(__APPLE__)
+#include "AvailabilityMacros.h"  // For MAC_OS_X_VERSION_MAX_ALLOWED
+#include <sys/syscall.h>
+#include <sys/time.h>
+#elif !defined(__BIONIC__)
+#include <syscall.h>
+#endif
+
+namespace android {
+namespace base {
+
+static std::mutex logging_lock;
+
+static LogSeverity gMinimumLogSeverity = INFO;
+static std::unique_ptr<std::string> gCmdLine;
+static std::unique_ptr<std::string> gProgramInvocationName;
+static std::unique_ptr<std::string> gProgramInvocationShortName;
+
+#ifndef __ANDROID__
+static pid_t GetTid() {
+#if defined(__APPLE__)
+  uint64_t owner;
+  // Requires Mac OS 10.6
+  CHECK_PTHREAD_CALL(pthread_threadid_np, (NULL, &owner), __FUNCTION__);
+  return owner;
+#else
+  return syscall(__NR_gettid);
+#endif
+}
+#endif  // __ANDROID__
+
+const char* GetCmdLine() {
+  return (gCmdLine.get() != nullptr) ? gCmdLine->c_str() : nullptr;
+}
+
+const char* ProgramInvocationName() {
+  return (gProgramInvocationName.get() != nullptr)
+             ? gProgramInvocationName->c_str()
+             : "unknown";
+}
+
+const char* ProgramInvocationShortName() {
+  return (gProgramInvocationShortName.get() != nullptr)
+             ? gProgramInvocationShortName->c_str()
+             : "unknown";
+}
+
+void InitLogging(char* argv[]) {
+  if (gCmdLine.get() != nullptr) {
+    return;
+  }
+
+  // Stash the command line for later use. We can use /proc/self/cmdline on
+  // Linux to recover this, but we don't have that luxury on the Mac, and there
+  // are a couple of argv[0] variants that are commonly used.
+  if (argv != nullptr) {
+    gCmdLine.reset(new std::string(argv[0]));
+    for (size_t i = 1; argv[i] != nullptr; ++i) {
+      gCmdLine->append(" ");
+      gCmdLine->append(argv[i]);
+    }
+    gProgramInvocationName.reset(new std::string(argv[0]));
+    const char* last_slash = strrchr(argv[0], '/');
+    gProgramInvocationShortName.reset(
+        new std::string((last_slash != nullptr) ? last_slash + 1 : argv[0]));
+  } else {
+    // TODO: fall back to /proc/self/cmdline when argv is NULL on Linux.
+    gCmdLine.reset(new std::string("<unset>"));
+  }
+  const char* tags = getenv("ANDROID_LOG_TAGS");
+  if (tags == nullptr) {
+    return;
+  }
+
+  std::vector<std::string> specs;
+  Split(tags, ' ', &specs);
+  for (size_t i = 0; i < specs.size(); ++i) {
+    // "tag-pattern:[vdiwefs]"
+    std::string spec(specs[i]);
+    if (spec.size() == 3 && StartsWith(spec, "*:")) {
+      switch (spec[2]) {
+        case 'v':
+          gMinimumLogSeverity = VERBOSE;
+          continue;
+        case 'd':
+          gMinimumLogSeverity = DEBUG;
+          continue;
+        case 'i':
+          gMinimumLogSeverity = INFO;
+          continue;
+        case 'w':
+          gMinimumLogSeverity = WARNING;
+          continue;
+        case 'e':
+          gMinimumLogSeverity = ERROR;
+          continue;
+        case 'f':
+          gMinimumLogSeverity = FATAL;
+          continue;
+        // liblog will even suppress FATAL if you say 's' for silent, but that's
+        // crazy!
+        case 's':
+          gMinimumLogSeverity = FATAL;
+          continue;
+      }
+    }
+    LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags
+               << ")";
+  }
+}
+
+// This indirection greatly reduces the stack impact of having lots of
+// checks/logging in a function.
+class LogMessageData {
+ public:
+  LogMessageData(const char* file, unsigned int line, LogSeverity severity,
+                 int error)
+      : file_(file), line_number_(line), severity_(severity), error_(error) {
+    const char* last_slash = strrchr(file, '/');
+    file = (last_slash == nullptr) ? file : last_slash + 1;
+  }
+
+  const char* GetFile() const {
+    return file_;
+  }
+
+  unsigned int GetLineNumber() const {
+    return line_number_;
+  }
+
+  LogSeverity GetSeverity() const {
+    return severity_;
+  }
+
+  int GetError() const {
+    return error_;
+  }
+
+  std::ostream& GetBuffer() {
+    return buffer_;
+  }
+
+  std::string ToString() const {
+    return buffer_.str();
+  }
+
+ private:
+  std::ostringstream buffer_;
+  const char* const file_;
+  const unsigned int line_number_;
+  const LogSeverity severity_;
+  const int error_;
+
+  DISALLOW_COPY_AND_ASSIGN(LogMessageData);
+};
+
+LogMessage::LogMessage(const char* file, unsigned int line,
+                       LogSeverity severity, int error)
+    : data_(new LogMessageData(file, line, severity, error)) {
+}
+
+LogMessage::~LogMessage() {
+  if (data_->GetSeverity() < gMinimumLogSeverity) {
+    return;  // No need to format something we're not going to output.
+  }
+
+  // Finish constructing the message.
+  if (data_->GetError() != -1) {
+    data_->GetBuffer() << ": " << strerror(data_->GetError());
+  }
+  std::string msg(data_->ToString());
+
+  // Do the actual logging with the lock held.
+  {
+    std::lock_guard<std::mutex> lock(logging_lock);
+    if (msg.find('\n') == std::string::npos) {
+      LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(),
+              msg.c_str());
+    } else {
+      msg += '\n';
+      size_t i = 0;
+      while (i < msg.size()) {
+        size_t nl = msg.find('\n', i);
+        msg[nl] = '\0';
+        LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(),
+                &msg[i]);
+        i = nl + 1;
+      }
+    }
+  }
+
+  // Abort if necessary.
+  if (data_->GetSeverity() == FATAL) {
+#ifdef __ANDROID__
+    android_set_abort_message(msg.c_str());
+#endif
+    abort();
+  }
+}
+
+std::ostream& LogMessage::stream() {
+  return data_->GetBuffer();
+}
+
+#ifdef __ANDROID__
+static const android_LogPriority kLogSeverityToAndroidLogPriority[] = {
+    ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO,
+    ANDROID_LOG_WARN,    ANDROID_LOG_ERROR, ANDROID_LOG_FATAL};
+static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1,
+              "Mismatch in size of kLogSeverityToAndroidLogPriority and values "
+              "in LogSeverity");
+#endif
+
+void LogMessage::LogLine(const char* file, unsigned int line,
+                         LogSeverity log_severity, const char* message) {
+#ifdef __ANDROID__
+  const char* tag = ProgramInvocationShortName();
+  int priority = kLogSeverityToAndroidLogPriority[log_severity];
+  if (priority == ANDROID_LOG_FATAL) {
+    LOG_PRI(priority, tag, "%s:%u] %s", file, line, message);
+  } else {
+    LOG_PRI(priority, tag, "%s", message);
+  }
+#else
+  static const char* log_characters = "VDIWEF";
+  CHECK_EQ(strlen(log_characters), FATAL + 1U);
+  char severity = log_characters[log_severity];
+  fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", ProgramInvocationShortName(),
+          severity, getpid(), GetTid(), file, line, message);
+#endif
+}
+
+void LogMessage::LogLineLowStack(const char* file, unsigned int line,
+                                 LogSeverity log_severity, const char* message) {
+#ifdef __ANDROID__
+  // Use android_writeLog() to avoid stack-based buffers used by
+  // android_printLog().
+  const char* tag = ProgramInvocationShortName();
+  int priority = kLogSeverityToAndroidLogPriority[log_severity];
+  char* buf = nullptr;
+  size_t buf_size = 0u;
+  if (priority == ANDROID_LOG_FATAL) {
+    // Allocate buffer for snprintf(buf, buf_size, "%s:%u] %s", file, line,
+    // message) below.  If allocation fails, fall back to printing only the
+    // message.
+    buf_size = strlen(file) + 1 /* ':' */ +
+               std::numeric_limits<typeof(line)>::max_digits10 + 2 /* "] " */ +
+               strlen(message) + 1 /* terminating 0 */;
+    buf = reinterpret_cast<char*>(malloc(buf_size));
+  }
+  if (buf != nullptr) {
+    snprintf(buf, buf_size, "%s:%u] %s", file, line, message);
+    android_writeLog(priority, tag, buf);
+    free(buf);
+  } else {
+    android_writeLog(priority, tag, message);
+  }
+#else
+  static const char* log_characters = "VDIWEF";
+  CHECK_EQ(strlen(log_characters), FATAL + 1U);
+
+  const char* program_name = ProgramInvocationShortName();
+  write(STDERR_FILENO, program_name, strlen(program_name));
+  write(STDERR_FILENO, " ", 1);
+  write(STDERR_FILENO, &log_characters[log_severity], 1);
+  write(STDERR_FILENO, " ", 1);
+  // TODO: pid and tid.
+  write(STDERR_FILENO, file, strlen(file));
+  // TODO: line.
+  UNUSED(line);
+  write(STDERR_FILENO, "] ", 2);
+  write(STDERR_FILENO, message, strlen(message));
+  write(STDERR_FILENO, "\n", 1);
+#endif
+}
+
+ScopedLogSeverity::ScopedLogSeverity(LogSeverity level) {
+  old_ = gMinimumLogSeverity;
+  gMinimumLogSeverity = level;
+}
+
+ScopedLogSeverity::~ScopedLogSeverity() {
+  gMinimumLogSeverity = old_;
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
new file mode 100644
index 0000000..0a03e38
--- /dev/null
+++ b/base/logging_test.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2015 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 "base/logging.h"
+
+#include <regex>
+#include <string>
+
+#include "base/file.h"
+#include "base/stringprintf.h"
+#include "test_utils.h"
+
+#include <gtest/gtest.h>
+
+#ifdef __ANDROID__
+#define HOST_TEST(suite, name) TEST(suite, DISABLED_ ## name)
+#else
+#define HOST_TEST(suite, name) TEST(suite, name)
+#endif
+
+class CapturedStderr {
+ public:
+  CapturedStderr() : old_stderr_(-1) {
+    init();
+  }
+
+  ~CapturedStderr() {
+    reset();
+  }
+
+  int fd() const {
+    return temp_file_.fd;
+  }
+
+ private:
+  void init() {
+    old_stderr_ = dup(STDERR_FILENO);
+    ASSERT_NE(-1, old_stderr_);
+    ASSERT_NE(-1, dup2(fd(), STDERR_FILENO));
+  }
+
+  void reset() {
+    ASSERT_NE(-1, dup2(old_stderr_, STDERR_FILENO));
+    ASSERT_EQ(0, close(old_stderr_));
+  }
+
+  TemporaryFile temp_file_;
+  int old_stderr_;
+};
+
+HOST_TEST(logging, CHECK) {
+  ASSERT_DEATH(CHECK(false), "Check failed: false ");
+  CHECK(true);
+
+  ASSERT_DEATH(CHECK_EQ(0, 1), "Check failed: 0 == 1 ");
+  CHECK_EQ(0, 0);
+
+  ASSERT_DEATH(CHECK_STREQ("foo", "bar"), R"(Check failed: "foo" == "bar")");
+  CHECK_STREQ("foo", "foo");
+}
+
+std::string make_log_pattern(android::base::LogSeverity severity,
+                             const char* message) {
+  static const char* log_characters = "VDIWEF";
+  char log_char = log_characters[severity];
+  return android::base::StringPrintf(
+      "%c[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+ " __FILE__
+      ":[[:digit:]]+] %s",
+      log_char, message);
+}
+
+HOST_TEST(logging, LOG) {
+  ASSERT_DEATH(LOG(FATAL) << "foobar", "foobar");
+
+  {
+    CapturedStderr cap;
+    LOG(WARNING) << "foobar";
+    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+    std::string output;
+    android::base::ReadFdToString(cap.fd(), &output);
+
+    std::regex message_regex(
+        make_log_pattern(android::base::WARNING, "foobar"));
+    ASSERT_TRUE(std::regex_search(output, message_regex));
+  }
+
+  {
+    CapturedStderr cap;
+    LOG(INFO) << "foobar";
+    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+    std::string output;
+    android::base::ReadFdToString(cap.fd(), &output);
+
+    std::regex message_regex(
+        make_log_pattern(android::base::INFO, "foobar"));
+    ASSERT_TRUE(std::regex_search(output, message_regex));
+  }
+
+  {
+    CapturedStderr cap;
+    LOG(DEBUG) << "foobar";
+    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+    std::string output;
+    android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_TRUE(output.empty());
+  }
+
+  {
+    android::base::ScopedLogSeverity severity(android::base::DEBUG);
+    CapturedStderr cap;
+    LOG(DEBUG) << "foobar";
+    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+    std::string output;
+    android::base::ReadFdToString(cap.fd(), &output);
+
+    std::regex message_regex(
+        make_log_pattern(android::base::DEBUG, "foobar"));
+    ASSERT_TRUE(std::regex_search(output, message_regex));
+  }
+}
+
+HOST_TEST(logging, PLOG) {
+  {
+    CapturedStderr cap;
+    errno = ENOENT;
+    PLOG(INFO) << "foobar";
+    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+    std::string output;
+    android::base::ReadFdToString(cap.fd(), &output);
+
+    std::regex message_regex(make_log_pattern(
+        android::base::INFO, "foobar: No such file or directory"));
+    ASSERT_TRUE(std::regex_search(output, message_regex));
+  }
+}
+
+HOST_TEST(logging, UNIMPLEMENTED) {
+  {
+    CapturedStderr cap;
+    errno = ENOENT;
+    UNIMPLEMENTED(ERROR);
+    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+    std::string output;
+    android::base::ReadFdToString(cap.fd(), &output);
+
+    std::string expected_message =
+        android::base::StringPrintf("%s unimplemented ", __PRETTY_FUNCTION__);
+    std::regex message_regex(
+        make_log_pattern(android::base::ERROR, expected_message.c_str()));
+    ASSERT_TRUE(std::regex_search(output, message_regex));
+  }
+}
diff --git a/base/test_main.cpp b/base/test_main.cpp
new file mode 100644
index 0000000..c49ca4b
--- /dev/null
+++ b/base/test_main.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 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 <gtest/gtest.h>
+
+#include "base/logging.h"
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  android::base::InitLogging(argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
new file mode 100644
index 0000000..1f6d3cf
--- /dev/null
+++ b/base/test_utils.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 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 "test_utils.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+TemporaryFile::TemporaryFile() {
+  init("/data/local/tmp");
+  if (fd == -1) {
+    init("/tmp");
+  }
+}
+
+TemporaryFile::~TemporaryFile() {
+  close(fd);
+  unlink(filename);
+}
+
+void TemporaryFile::init(const char* tmp_dir) {
+  snprintf(filename, sizeof(filename), "%s/TemporaryFile-XXXXXX", tmp_dir);
+  fd = mkstemp(filename);
+}
diff --git a/base/test_utils.h b/base/test_utils.h
new file mode 100644
index 0000000..132d3a7
--- /dev/null
+++ b/base/test_utils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TEST_UTILS_H
+#define TEST_UTILS_H
+
+class TemporaryFile {
+ public:
+  TemporaryFile();
+  ~TemporaryFile();
+
+  int fd;
+  char filename[1024];
+
+ private:
+  void init(const char* tmp_dir);
+};
+
+#endif // TEST_UTILS_H
diff --git a/include/system/graphics.h b/include/system/graphics.h
index c3fca97..efd48cb 100644
--- a/include/system/graphics.h
+++ b/include/system/graphics.h
@@ -45,9 +45,12 @@
     /*
      * "linear" color pixel formats:
      *
-     * The pixel formats below contain sRGB data but are otherwise treated
-     * as linear formats, i.e.: no special operation is performed when
-     * reading or writing into a buffer in one of these formats
+     * When used with ANativeWindow, the dataSpace field describes the color
+     * space of the buffer.
+     *
+     * The color space determines, for example, if the formats are linear or
+     * gamma-corrected; or whether any special operations are performed when
+     * reading or writing into a buffer in one of these formats.
      */
     HAL_PIXEL_FORMAT_RGBA_8888          = 1,
     HAL_PIXEL_FORMAT_RGBX_8888          = 2,
@@ -55,25 +58,8 @@
     HAL_PIXEL_FORMAT_RGB_565            = 4,
     HAL_PIXEL_FORMAT_BGRA_8888          = 5,
 
-    /*
-     * sRGB color pixel formats:
-     *
-     * The red, green and blue components are stored in sRGB space, and converted
-     * to linear space when read, using the standard sRGB to linear equation:
-     *
-     * Clinear = Csrgb / 12.92                  for Csrgb <= 0.04045
-     *         = (Csrgb + 0.055 / 1.055)^2.4    for Csrgb >  0.04045
-     *
-     * When written the inverse transformation is performed:
-     *
-     * Csrgb = 12.92 * Clinear                  for Clinear <= 0.0031308
-     *       = 1.055 * Clinear^(1/2.4) - 0.055  for Clinear >  0.0031308
-     *
-     *
-     *  The alpha component, if present, is always stored in linear space and
-     *  is left unmodified when read or written.
-     *
-     */
+    // Deprecated sRGB formats for source code compatibility
+    // Not for use in new code
     HAL_PIXEL_FORMAT_sRGB_A_8888        = 0xC,
     HAL_PIXEL_FORMAT_sRGB_X_8888        = 0xD,
 
@@ -111,6 +97,8 @@
      *   cr_offset = y_size
      *   cb_offset = y_size + c_size
      *
+     * When used with ANativeWindow, the dataSpace field describes the color
+     * space of the buffer.
      */
     HAL_PIXEL_FORMAT_YV12   = 0x32315659, // YCrCb 4:2:0 Planar
 
@@ -135,6 +123,8 @@
      *
      *   size = stride * height
      *
+     * When used with ANativeWindow, the dataSpace field describes the color
+     * space of the buffer.
      */
     HAL_PIXEL_FORMAT_Y8     = 0x20203859,
 
@@ -159,6 +149,10 @@
      *
      *   size = stride * height * 2
      *
+     * When used with ANativeWindow, the dataSpace field describes the color
+     * space of the buffer, except that dataSpace field
+     * HAL_DATASPACE_DEPTH indicates that this buffer contains a depth
+     * image where each sample is a distance value measured by a depth camera.
      */
     HAL_PIXEL_FORMAT_Y16    = 0x20363159,
 
@@ -167,7 +161,7 @@
      *
      * This format is exposed outside of the camera HAL to applications.
      *
-     * RAW_SENSOR is a single-channel, 16-bit, little endian  format, typically
+     * RAW16 is a single-channel, 16-bit, little endian format, typically
      * representing raw Bayer-pattern images from an image sensor, with minimal
      * processing.
      *
@@ -193,9 +187,15 @@
      *    - GRALLOC_USAGE_HW_CAMERA_*
      *    - GRALLOC_USAGE_SW_*
      *    - GRALLOC_USAGE_RENDERSCRIPT
+     *
+     * When used with ANativeWindow, the dataSpace should be
+     * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial
+     * extra metadata to define.
      */
     HAL_PIXEL_FORMAT_RAW16 = 0x20,
-    HAL_PIXEL_FORMAT_RAW_SENSOR = 0x20, // TODO(rubenbrunk): Remove RAW_SENSOR.
+
+    // Temporary alias for source code compatibility; do not use in new code
+    HAL_PIXEL_FORMAT_RAW_SENSOR = HAL_PIXEL_FORMAT_RAW16,
 
     /*
      * Android RAW10 format:
@@ -244,6 +244,10 @@
      *    - GRALLOC_USAGE_HW_CAMERA_*
      *    - GRALLOC_USAGE_SW_*
      *    - GRALLOC_USAGE_RENDERSCRIPT
+     *
+     * When used with ANativeWindow, the dataSpace field should be
+     * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial
+     * extra metadata to define.
      */
     HAL_PIXEL_FORMAT_RAW10 = 0x25,
 
@@ -261,6 +265,10 @@
      *    - GRALLOC_USAGE_HW_CAMERA_*
      *    - GRALLOC_USAGE_SW_*
      *    - GRALLOC_USAGE_RENDERSCRIPT
+     *
+     * When used with ANativeWindow, the dataSpace field should be
+     * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial
+     * extra metadata to define.
      */
     HAL_PIXEL_FORMAT_RAW_OPAQUE = 0x24,
 
@@ -276,6 +284,16 @@
      *
      * Buffers of this format must have a height of 1, and width equal to their
      * size in bytes.
+     *
+     * When used with ANativeWindow, the mapping of the dataSpace field to
+     * buffer contents for BLOB is as follows:
+     *
+     *  dataSpace value               | Buffer contents
+     * -------------------------------+-----------------------------------------
+     *  HAL_DATASPACE_JFIF            | An encoded JPEG image
+     *  HAL_DATASPACE_DEPTH           | An android_depth_points buffer
+     *  Other                         | Unsupported
+     *
      */
     HAL_PIXEL_FORMAT_BLOB = 0x21,
 
@@ -292,6 +310,8 @@
      * framework will assume that sampling the texture will always return an
      * alpha value of 1.0 (i.e. the buffer contains only opaque pixel values).
      *
+     * When used with ANativeWindow, the dataSpace field describes the color
+     * space of the buffer.
      */
     HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22,
 
@@ -311,6 +331,9 @@
      *
      * This format is locked for use by gralloc's (*lock_ycbcr) method, and
      * locking with the (*lock) method will return an error.
+     *
+     * When used with ANativeWindow, the dataSpace field describes the color
+     * space of the buffer.
      */
     HAL_PIXEL_FORMAT_YCbCr_420_888 = 0x23,
 
@@ -355,6 +378,42 @@
 };
 
 /**
+ * Structure used to define depth point clouds for format HAL_PIXEL_FORMAT_BLOB
+ * with dataSpace value of HAL_DATASPACE_DEPTH.
+ * When locking a native buffer of the above format and dataSpace value,
+ * the vaddr pointer can be cast to this structure.
+ *
+ * A variable-length list of (x,y,z) 3D points, as floats.
+ *
+ * @num_points is the number of points in the list
+ *
+ * @xyz_points is the flexible array of floating-point values.
+ *   It contains (num_points) * 3 floats.
+ *
+ *   For example:
+ *     android_depth_points d = get_depth_buffer();
+ *     struct {
+ *       float x; float y; float z;
+ *     } firstPoint, lastPoint;
+ *
+ *     firstPoint.x = d.xyz_points[0];
+ *     firstPoint.y = d.xyz_points[1];
+ *     firstPoint.z = d.xyz_points[2];
+ *     lastPoint.x = d.xyz_points[(d.num_points - 1) * 3 + 0];
+ *     lastPoint.y = d.xyz_points[(d.num_points - 1) * 3 + 1];
+ *     lastPoint.z = d.xyz_points[(d.num_points - 1) * 3 + 2];
+ */
+
+struct android_depth_points {
+    uint32_t num_points;
+
+    /** reserved for future use, set to 0 by gralloc's (*lock)() */
+    uint32_t reserved[8];
+
+    float xyz_points[];
+};
+
+/**
  * Transformation definitions
  *
  * IMPORTANT NOTE:
@@ -378,19 +437,33 @@
 };
 
 /**
- * Colorspace Definitions
+ * Dataspace Definitions
  * ======================
  *
- * Colorspace is the definition of how pixel values should be interpreted.
- * It includes primaries (including white point) and the transfer
- * characteristic function, which describes both gamma curve and numeric
- * range (within the bit depth).
+ * Dataspace is the definition of how pixel values should be interpreted.
+ *
+ * For many formats, this is the colorspace of the image data, which includes
+ * primaries (including white point) and the transfer characteristic function,
+ * which describes both gamma curve and numeric range (within the bit depth).
+ *
+ * Other dataspaces include depth measurement data from a depth camera.
  */
 
-enum {
+typedef enum android_dataspace {
     /*
-     * Arbitrary colorspace with manually defined characteristics.
-     * Colorspace definition must be communicated separately.
+     * Default-assumption data space, when not explicitly specified.
+     *
+     * It is safest to assume the buffer is an image with sRGB primaries and
+     * encoding ranges, but the consumer and/or the producer of the data may
+     * simply be using defaults. No automatic gamma transform should be
+     * expected, except for a possible display gamma transform when drawn to a
+     * screen.
+     */
+    HAL_DATASPACE_UNKNOWN = 0x0,
+
+    /*
+     * Arbitrary dataspace with manually defined characteristics.  Definition
+     * for colorspaces or other meaning must be communicated separately.
      *
      * This is used when specifying primaries, transfer characteristics,
      * etc. separately.
@@ -399,7 +472,57 @@
      * where a colorspace can have separately defined primaries, transfer
      * characteristics, etc.
      */
-    HAL_COLORSPACE_ARBITRARY = 0x1,
+    HAL_DATASPACE_ARBITRARY = 0x1,
+
+    /*
+     * RGB Colorspaces
+     * -----------------
+     *
+     * Primaries are given using (x,y) coordinates in the CIE 1931 definition
+     * of x and y specified by ISO 11664-1.
+     *
+     * Transfer characteristics are the opto-electronic transfer characteristic
+     * at the source as a function of linear optical intensity (luminance).
+     */
+
+    /*
+     * sRGB linear encoding:
+     *
+     * The red, green, and blue components are stored in sRGB space, but
+     * are linear, not gamma-encoded.
+     * The RGB primaries and the white point are the same as BT.709.
+     *
+     * The values are encoded using the full range ([0,255] for 8-bit) for all
+     * components.
+     */
+    HAL_DATASPACE_SRGB_LINEAR = 0x200,
+
+    /*
+     * sRGB gamma encoding:
+     *
+     * The red, green and blue components are stored in sRGB space, and
+     * converted to linear space when read, using the standard sRGB to linear
+     * equation:
+     *
+     * Clinear = Csrgb / 12.92                  for Csrgb <= 0.04045
+     *         = (Csrgb + 0.055 / 1.055)^2.4    for Csrgb >  0.04045
+     *
+     * When written the inverse transformation is performed:
+     *
+     * Csrgb = 12.92 * Clinear                  for Clinear <= 0.0031308
+     *       = 1.055 * Clinear^(1/2.4) - 0.055  for Clinear >  0.0031308
+     *
+     *
+     * The alpha component, if present, is always stored in linear space and
+     * is left unmodified when read or written.
+     *
+     * The RGB primaries and the white point are the same as BT.709.
+     *
+     * The values are encoded using the full range ([0,255] for 8-bit) for all
+     * components.
+     *
+     */
+    HAL_DATASPACE_SRGB = 0x201,
 
     /*
      * YCbCr Colorspaces
@@ -429,7 +552,7 @@
      *  red             0.640   0.330
      *  white (D65)     0.3127  0.3290
      */
-    HAL_COLORSPACE_JFIF = 0x101,
+    HAL_DATASPACE_JFIF = 0x101,
 
     /*
      * ITU-R Recommendation 601 (BT.601) - 625-line
@@ -456,7 +579,7 @@
      *  red             0.640   0.330
      *  white (D65)     0.3127  0.3290
      */
-    HAL_COLORSPACE_BT601_625 = 0x102,
+    HAL_DATASPACE_BT601_625 = 0x102,
 
     /*
      * ITU-R Recommendation 601 (BT.601) - 525-line
@@ -483,7 +606,7 @@
      *  red             0.630   0.340
      *  white (D65)     0.3127  0.3290
      */
-    HAL_COLORSPACE_BT601_525 = 0x103,
+    HAL_DATASPACE_BT601_525 = 0x103,
 
     /*
      * ITU-R Recommendation 709 (BT.709)
@@ -504,8 +627,20 @@
      *  red             0.640   0.330
      *  white (D65)     0.3127  0.3290
      */
-    HAL_COLORSPACE_BT709 = 0x104,
-};
+    HAL_DATASPACE_BT709 = 0x104,
+
+    /*
+     * The buffer contains depth ranging measurements from a depth camera.
+     * This value is valid with formats:
+     *    HAL_PIXEL_FORMAT_Y16: 16-bit single channel depth image.
+     *    HAL_PIXEL_FORMAT_BLOB: A depth point cloud, as
+     *       a variable-length float (x,y,z) coordinate point list.
+     *       The point cloud will be represented with the android_depth_points
+     *       structure.
+     */
+    HAL_DATASPACE_DEPTH = 0x1000
+
+} android_dataspace_t;
 
 #ifdef __cplusplus
 }
diff --git a/include/system/window.h b/include/system/window.h
index bf93b79..af0418b 100644
--- a/include/system/window.h
+++ b/include/system/window.h
@@ -262,6 +262,12 @@
      * the aspect ratio of the buffers produced.
      */
     NATIVE_WINDOW_STICKY_TRANSFORM = 11,
+
+    /**
+     * The default data space for the buffers as set by the consumer.
+     * The values are defined in graphics.h.
+     */
+    NATIVE_WINDOW_DEFAULT_DATASPACE = 12
 };
 
 /* Valid operations for the (*perform)() hook.
@@ -294,6 +300,7 @@
     NATIVE_WINDOW_SET_POST_TRANSFORM_CROP   = 16,   /* private */
     NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17,/* private */
     NATIVE_WINDOW_SET_SIDEBAND_STREAM       = 18,
+    NATIVE_WINDOW_SET_BUFFERS_DATASPACE     = 19
 };
 
 /* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
@@ -498,6 +505,7 @@
      *     NATIVE_WINDOW_SET_BUFFERS_GEOMETRY  (deprecated)
      *     NATIVE_WINDOW_SET_BUFFERS_TRANSFORM
      *     NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP
+     *     NATIVE_WINDOW_SET_BUFFERS_DATASPACE
      *     NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS
      *     NATIVE_WINDOW_SET_BUFFERS_FORMAT
      *     NATIVE_WINDOW_SET_SCALING_MODE       (private)
@@ -799,6 +807,26 @@
 }
 
 /*
+ * native_window_set_buffers_data_space(..., int dataSpace)
+ * All buffers queued after this call will be associated with the dataSpace
+ * parameter specified.
+ *
+ * dataSpace specifies additional information about the buffer that's dependent
+ * on the buffer format and the endpoints. For example, it can be used to convey
+ * the color space of the image data in the buffer, or it can be used to
+ * indicate that the buffers contain depth measurement data instead of color
+ * images.  The default dataSpace is 0, HAL_DATASPACE_UNKNOWN, unless it has been
+ * overridden by the consumer.
+ */
+static inline int native_window_set_buffers_data_space(
+        struct ANativeWindow* window,
+        android_dataspace_t dataSpace)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DATASPACE,
+            dataSpace);
+}
+
+/*
  * native_window_set_buffers_transform(..., int transform)
  * All buffers queued after this call will be displayed transformed according
  * to the transform parameter specified.
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 530eba8..03c8b30 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -30,6 +30,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <memory>
 #include <string>
 
 #include <base/file.h>
@@ -114,9 +115,9 @@
 static void do_log_procs(FILE* log) {
     do_log_uptime(log);
 
-    DIR* dir = opendir("/proc");
+    std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir("/proc"), closedir);
     struct dirent* entry;
-    while ((entry = readdir(dir)) != NULL) {
+    while ((entry = readdir(dir.get())) != NULL) {
         // Only match numeric values.
         char* end;
         int pid = strtol(entry->d_name, &end, 10);
@@ -146,7 +147,6 @@
             }
         }
     }
-    closedir(dir);
 
     fputc('\n', log);
 }
diff --git a/init/devices.cpp b/init/devices.cpp
index 3a9b753..aa86e5f 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -49,9 +49,9 @@
 #include "log.h"
 
 #define SYSFS_PREFIX    "/sys"
-#define FIRMWARE_DIR1   "/etc/firmware"
-#define FIRMWARE_DIR2   "/vendor/firmware"
-#define FIRMWARE_DIR3   "/firmware/image"
+static const char *firmware_dirs[] = { "/etc/firmware",
+                                       "/vendor/firmware",
+                                       "/firmware/image" };
 
 extern struct selabel_handle *sehandle;
 
@@ -818,8 +818,9 @@
 
 static void process_firmware_event(struct uevent *uevent)
 {
-    char *root, *loading, *data, *file1 = NULL, *file2 = NULL, *file3 = NULL;
+    char *root, *loading, *data;
     int l, loading_fd, data_fd, fw_fd;
+    size_t i;
     int booting = is_booting();
 
     INFO("firmware: loading '%s' for '%s'\n",
@@ -837,62 +838,49 @@
     if (l == -1)
         goto loading_free_out;
 
-    l = asprintf(&file1, FIRMWARE_DIR1"/%s", uevent->firmware);
-    if (l == -1)
-        goto data_free_out;
-
-    l = asprintf(&file2, FIRMWARE_DIR2"/%s", uevent->firmware);
-    if (l == -1)
-        goto data_free_out;
-
-    l = asprintf(&file3, FIRMWARE_DIR3"/%s", uevent->firmware);
-    if (l == -1)
-        goto data_free_out;
-
     loading_fd = open(loading, O_WRONLY|O_CLOEXEC);
     if(loading_fd < 0)
-        goto file_free_out;
+        goto data_free_out;
 
     data_fd = open(data, O_WRONLY|O_CLOEXEC);
     if(data_fd < 0)
         goto loading_close_out;
 
 try_loading_again:
-    fw_fd = open(file1, O_RDONLY|O_CLOEXEC);
-    if(fw_fd < 0) {
-        fw_fd = open(file2, O_RDONLY|O_CLOEXEC);
-        if (fw_fd < 0) {
-            fw_fd = open(file3, O_RDONLY|O_CLOEXEC);
-            if (fw_fd < 0) {
-                if (booting) {
-                        /* If we're not fully booted, we may be missing
-                         * filesystems needed for firmware, wait and retry.
-                         */
-                    usleep(100000);
-                    booting = is_booting();
-                    goto try_loading_again;
-                }
-                INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno);
-                write(loading_fd, "-1", 2);
-                goto data_close_out;
-            }
+    for (i = 0; i < ARRAY_SIZE(firmware_dirs); i++) {
+        char *file = NULL;
+        l = asprintf(&file, "%s/%s", firmware_dirs[i], uevent->firmware);
+        if (l == -1)
+            goto data_free_out;
+        fw_fd = open(file, O_RDONLY|O_CLOEXEC);
+        free(file);
+        if (fw_fd >= 0) {
+            if(!load_firmware(fw_fd, loading_fd, data_fd))
+                INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware);
+            else
+                INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware);
+            break;
         }
     }
-
-    if(!load_firmware(fw_fd, loading_fd, data_fd))
-        INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware);
-    else
-        INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware);
+    if (fw_fd < 0) {
+        if (booting) {
+            /* If we're not fully booted, we may be missing
+             * filesystems needed for firmware, wait and retry.
+             */
+            usleep(100000);
+            booting = is_booting();
+            goto try_loading_again;
+        }
+        INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno);
+        write(loading_fd, "-1", 2);
+        goto data_close_out;
+    }
 
     close(fw_fd);
 data_close_out:
     close(data_fd);
 loading_close_out:
     close(loading_fd);
-file_free_out:
-    free(file1);
-    free(file2);
-    free(file3);
 data_free_out:
     free(data);
 loading_free_out:
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 13d671f..2031aae 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -26,6 +26,8 @@
 #include <errno.h>
 #include <sys/poll.h>
 
+#include <memory>
+
 #include <cutils/misc.h>
 #include <cutils/sockets.h>
 #include <cutils/multiuser.h>
@@ -425,64 +427,62 @@
     }
 }
 
-static void load_persistent_properties()
-{
-    DIR* dir = opendir(PERSISTENT_PROPERTY_DIR);
-    int dir_fd;
-    struct dirent*  entry;
-    char value[PROP_VALUE_MAX];
-    int fd, length;
-    struct stat sb;
+static void load_persistent_properties() {
+    persistent_properties_loaded = 1;
 
-    if (dir) {
-        dir_fd = dirfd(dir);
-        while ((entry = readdir(dir)) != NULL) {
-            if (strncmp("persist.", entry->d_name, strlen("persist.")))
-                continue;
-            if (entry->d_type != DT_REG)
-                continue;
-            /* open the file and read the property value */
-            fd = openat(dir_fd, entry->d_name, O_RDONLY | O_NOFOLLOW);
-            if (fd < 0) {
-                ERROR("Unable to open persistent property file \"%s\" errno: %d\n",
-                      entry->d_name, errno);
-                continue;
-            }
-            if (fstat(fd, &sb) < 0) {
-                ERROR("fstat on property file \"%s\" failed errno: %d\n", entry->d_name, errno);
-                close(fd);
-                continue;
-            }
-
-            // File must not be accessible to others, be owned by root/root, and
-            // not be a hard link to any other file.
-            if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0)
-                    || (sb.st_uid != 0)
-                    || (sb.st_gid != 0)
-                    || (sb.st_nlink != 1)) {
-                ERROR("skipping insecure property file %s (uid=%u gid=%u nlink=%u mode=%o)\n",
-                      entry->d_name, (unsigned int)sb.st_uid, (unsigned int)sb.st_gid,
-                      (unsigned int)sb.st_nlink, sb.st_mode);
-                close(fd);
-                continue;
-            }
-
-            length = read(fd, value, sizeof(value) - 1);
-            if (length >= 0) {
-                value[length] = 0;
-                property_set(entry->d_name, value);
-            } else {
-                ERROR("Unable to read persistent property file %s errno: %d\n",
-                      entry->d_name, errno);
-            }
-            close(fd);
-        }
-        closedir(dir);
-    } else {
-        ERROR("Unable to open persistent property directory %s errno: %d\n", PERSISTENT_PROPERTY_DIR, errno);
+    std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(PERSISTENT_PROPERTY_DIR), closedir);
+    if (!dir) {
+        ERROR("Unable to open persistent property directory \"%s\": %s\n",
+              PERSISTENT_PROPERTY_DIR, strerror(errno));
+        return;
     }
 
-    persistent_properties_loaded = 1;
+    struct dirent* entry;
+    while ((entry = readdir(dir.get())) != NULL) {
+        if (strncmp("persist.", entry->d_name, strlen("persist."))) {
+            continue;
+        }
+        if (entry->d_type != DT_REG) {
+            continue;
+        }
+
+        // Open the file and read the property value.
+        int fd = openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW);
+        if (fd == -1) {
+            ERROR("Unable to open persistent property file \"%s\": %s\n",
+                  entry->d_name, strerror(errno));
+            continue;
+        }
+
+        struct stat sb;
+        if (fstat(fd, &sb) == -1) {
+            ERROR("fstat on property file \"%s\" failed: %s\n", entry->d_name, strerror(errno));
+            close(fd);
+            continue;
+        }
+
+        // File must not be accessible to others, be owned by root/root, and
+        // not be a hard link to any other file.
+        if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || (sb.st_uid != 0) || (sb.st_gid != 0) ||
+                (sb.st_nlink != 1)) {
+            ERROR("skipping insecure property file %s (uid=%u gid=%u nlink=%u mode=%o)\n",
+                  entry->d_name, (unsigned int)sb.st_uid, (unsigned int)sb.st_gid,
+                  (unsigned int)sb.st_nlink, sb.st_mode);
+            close(fd);
+            continue;
+        }
+
+        char value[PROP_VALUE_MAX];
+        int length = read(fd, value, sizeof(value) - 1);
+        if (length >= 0) {
+            value[length] = 0;
+            property_set(entry->d_name, value);
+        } else {
+            ERROR("Unable to read persistent property file %s: %s\n",
+                  entry->d_name, strerror(errno));
+        }
+        close(fd);
+    }
 }
 
 void property_init(void)
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 2693583..d11b129 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -27,8 +27,6 @@
 
 #include "LogBuffer.h"
 #include "LogReader.h"
-#include "LogStatistics.h"
-#include "LogWhiteBlackList.h"
 
 // Default
 #define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here?
@@ -193,7 +191,7 @@
         LogTimeEntry::unlock();
     }
 
-    stats.add(len, log_id, uid, pid);
+    stats.add(elem);
     maybePrune(log_id);
     pthread_mutex_unlock(&mLogElementsLock);
 }
@@ -216,6 +214,16 @@
     }
 }
 
+LogBufferElementCollection::iterator LogBuffer::erase(LogBufferElementCollection::iterator it) {
+    LogBufferElement *e = *it;
+
+    it = mLogElements.erase(it);
+    stats.subtract(e);
+    delete e;
+
+    return it;
+}
+
 // prune "pruneRows" of type "id" from the buffer.
 //
 // mLogElementsLock must be held when this function is called.
@@ -250,12 +258,8 @@
                 continue;
             }
 
-            uid_t uid = e->getUid();
-
-            if (uid == caller_uid) {
-                it = mLogElements.erase(it);
-                stats.subtract(e->getMsgLen(), id, uid, e->getPid());
-                delete e;
+            if (e->getUid() == caller_uid) {
+                it = erase(it);
                 pruneRows--;
                 if (pruneRows == 0) {
                     break;
@@ -269,6 +273,7 @@
     }
 
     // prune by worst offender by uid
+    bool hasBlacklist = mPrune.naughty();
     while (pruneRows > 0) {
         // recalculate the worst offender on every batched pass
         uid_t worst = (uid_t) -1;
@@ -276,19 +281,23 @@
         size_t second_worst_sizes = 0;
 
         if ((id != LOG_ID_CRASH) && mPrune.worstUidEnabled()) {
-            LidStatistics &l = stats.id(id);
-            l.sort();
-            UidStatisticsCollection::iterator iu = l.begin();
-            if (iu != l.end()) {
-                UidStatistics *u = *iu;
-                worst = u->getUid();
-                worst_sizes = u->sizes();
-                if (++iu != l.end()) {
-                    second_worst_sizes = (*iu)->sizes();
+            const UidEntry **sorted = stats.sort(2, id);
+
+            if (sorted) {
+                if (sorted[0] && sorted[1]) {
+                    worst = sorted[0]->getKey();
+                    worst_sizes = sorted[0]->getSizes();
+                    second_worst_sizes = sorted[1]->getSizes();
                 }
+                delete [] sorted;
             }
         }
 
+        // skip if we have neither worst nor naughty filters
+        if ((worst == (uid_t) -1) && !hasBlacklist) {
+            break;
+        }
+
         bool kick = false;
         for(it = mLogElements.begin(); it != mLogElements.end();) {
             LogBufferElement *e = *it;
@@ -304,24 +313,28 @@
 
             uid_t uid = e->getUid();
 
-            if ((uid == worst) || mPrune.naughty(e)) { // Worst or BlackListed
-                it = mLogElements.erase(it);
-                unsigned short len = e->getMsgLen();
-                stats.subtract(len, id, uid, e->getPid());
-                delete e;
-                pruneRows--;
-                if (uid == worst) {
-                    kick = true;
-                    if ((pruneRows == 0) || (worst_sizes < second_worst_sizes)) {
-                        break;
-                    }
-                    worst_sizes -= len;
-                } else if (pruneRows == 0) {
-                    break;
-                }
-            } else {
+            // !Worst and !BlackListed?
+            if ((uid != worst) && (!hasBlacklist || !mPrune.naughty(e))) {
                 ++it;
+                continue;
             }
+
+            unsigned short len = e->getMsgLen();
+            it = erase(it);
+            pruneRows--;
+            if (pruneRows == 0) {
+                break;
+            }
+
+            if (uid != worst) {
+                continue;
+            }
+
+            kick = true;
+            if (worst_sizes < second_worst_sizes) {
+                break;
+            }
+            worst_sizes -= len;
         }
 
         if (!kick || !mPrune.worstUidEnabled()) {
@@ -330,58 +343,63 @@
     }
 
     bool whitelist = false;
+    bool hasWhitelist = mPrune.nice();
     it = mLogElements.begin();
     while((pruneRows > 0) && (it != mLogElements.end())) {
         LogBufferElement *e = *it;
-        if (e->getLogId() == id) {
-            if (oldest && (oldest->mStart <= e->getSequence())) {
-                if (!whitelist) {
-                    if (stats.sizes(id) > (2 * log_buffer_size(id))) {
-                        // kick a misbehaving log reader client off the island
-                        oldest->release_Locked();
-                    } else {
-                        oldest->triggerSkip_Locked(id, pruneRows);
-                    }
-                }
+
+        if (e->getLogId() != id) {
+            it++;
+            continue;
+        }
+
+        if (oldest && (oldest->mStart <= e->getSequence())) {
+            if (whitelist) {
                 break;
             }
 
-            if (mPrune.nice(e)) { // WhiteListed
-                whitelist = true;
-                it++;
-                continue;
+            if (stats.sizes(id) > (2 * log_buffer_size(id))) {
+                // kick a misbehaving log reader client off the island
+                oldest->release_Locked();
+            } else {
+                oldest->triggerSkip_Locked(id, pruneRows);
             }
-
-            it = mLogElements.erase(it);
-            stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid());
-            delete e;
-            pruneRows--;
-        } else {
-            it++;
+            break;
         }
+
+        if (hasWhitelist && mPrune.nice(e)) { // WhiteListed
+            whitelist = true;
+            it++;
+            continue;
+        }
+
+        it = erase(it);
+        pruneRows--;
     }
 
+    // Do not save the whitelist if we are reader range limited
     if (whitelist && (pruneRows > 0)) {
         it = mLogElements.begin();
         while((it != mLogElements.end()) && (pruneRows > 0)) {
             LogBufferElement *e = *it;
-            if (e->getLogId() == id) {
-                if (oldest && (oldest->mStart <= e->getSequence())) {
-                    if (stats.sizes(id) > (2 * log_buffer_size(id))) {
-                        // kick a misbehaving log reader client off the island
-                        oldest->release_Locked();
-                    } else {
-                        oldest->triggerSkip_Locked(id, pruneRows);
-                    }
-                    break;
-                }
-                it = mLogElements.erase(it);
-                stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid());
-                delete e;
-                pruneRows--;
-            } else {
-                it++;
+
+            if (e->getLogId() != id) {
+                ++it;
+                continue;
             }
+
+            if (oldest && (oldest->mStart <= e->getSequence())) {
+                if (stats.sizes(id) > (2 * log_buffer_size(id))) {
+                    // kick a misbehaving log reader client off the island
+                    oldest->release_Locked();
+                } else {
+                    oldest->triggerSkip_Locked(id, pruneRows);
+                }
+                break;
+            }
+
+            it = erase(it);
+            pruneRows--;
         }
     }
 
@@ -487,22 +505,9 @@
 }
 
 void LogBuffer::formatStatistics(char **strp, uid_t uid, unsigned int logMask) {
-    uint64_t oldest = UINT64_MAX;
-
     pthread_mutex_lock(&mLogElementsLock);
 
-    // Find oldest element in the log(s)
-    LogBufferElementCollection::iterator it;
-    for (it = mLogElements.begin(); it != mLogElements.end(); ++it) {
-        LogBufferElement *element = *it;
-
-        if ((logMask & (1 << element->getLogId()))) {
-            oldest = element->getSequence();
-            break;
-        }
-    }
-
-    stats.format(strp, uid, logMask, oldest);
+    stats.format(strp, uid, logMask);
 
     pthread_mutex_unlock(&mLogElementsLock);
 }
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 13e6aa8..a29e015 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -78,7 +78,7 @@
 private:
     void maybePrune(log_id_t id);
     void prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
-
+    LogBufferElementCollection::iterator erase(LogBufferElementCollection::iterator it);
 };
 
 #endif // _LOGD_LOG_BUFFER_H__
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 25f1450..0628d3e 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -17,8 +17,9 @@
 #ifndef _LOGD_LOG_BUFFER_ELEMENT_H__
 #define _LOGD_LOG_BUFFER_ELEMENT_H__
 
-#include <sys/types.h>
 #include <stdatomic.h>
+#include <sys/types.h>
+
 #include <sysutils/SocketClient.h>
 #include <log/log.h>
 #include <log/log_read.h>
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 5a70689..accd660 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -14,10 +14,12 @@
  * limitations under the License.
  */
 
+#include <algorithm> // std::max
 #include <fcntl.h>
-#include <malloc.h>
-#include <stdarg.h>
-#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 
 #include <log/logger.h>
 #include <private/android_filesystem_config.h>
@@ -25,80 +27,21 @@
 
 #include "LogStatistics.h"
 
-PidStatistics::PidStatistics(pid_t pid, char *name)
-        : pid(pid)
-        , mSizesTotal(0)
-        , mElementsTotal(0)
-        , mSizes(0)
-        , mElements(0)
-        , name(name)
-        , mGone(false)
-{ }
-
-#ifdef DO_NOT_ERROR_IF_PIDSTATISTICS_USES_A_COPY_CONSTRUCTOR
-PidStatistics::PidStatistics(const PidStatistics &copy)
-        : pid(copy->pid)
-        , name(copy->name ? strdup(copy->name) : NULL)
-        , mSizesTotal(copy->mSizesTotal)
-        , mElementsTotal(copy->mElementsTotal)
-        , mSizes(copy->mSizes)
-        , mElements(copy->mElements)
-        , mGone(copy->mGone)
-{ }
-#endif
-
-PidStatistics::~PidStatistics() {
-    free(name);
-}
-
-bool PidStatistics::pidGone() {
-    if (mGone || (pid == gone)) {
-        return true;
-    }
-    if (pid == 0) {
-        return false;
-    }
-    if (kill(pid, 0) && (errno != EPERM)) {
-        mGone = true;
-        return true;
-    }
-    return false;
-}
-
-void PidStatistics::setName(char *new_name) {
-    free(name);
-    name = new_name;
-}
-
-void PidStatistics::add(unsigned short size) {
-    mSizesTotal += size;
-    ++mElementsTotal;
-    mSizes += size;
-    ++mElements;
-}
-
-bool PidStatistics::subtract(unsigned short size) {
-    mSizes -= size;
-    --mElements;
-    return (mElements == 0) && pidGone();
-}
-
-void PidStatistics::addTotal(size_t size, size_t element) {
-    if (pid == gone) {
-        mSizesTotal += size;
-        mElementsTotal += element;
+LogStatistics::LogStatistics() {
+    log_id_for_each(id) {
+        mSizes[id] = 0;
+        mElements[id] = 0;
+        mSizesTotal[id] = 0;
+        mElementsTotal[id] = 0;
     }
 }
 
-// must call free to release return value
-//  If only we could sniff our own logs for:
-//   <time> <pid> <pid> E AndroidRuntime: Process: <name>, PID: <pid>
-//  which debuggerd prints as a process is crashing.
-char *PidStatistics::pidToName(pid_t pid) {
+// caller must own and free character string
+char *LogStatistics::pidToName(pid_t pid) {
     char *retval = NULL;
     if (pid == 0) { // special case from auditd for kernel
         retval = strdup("logd.auditd");
-    } else if (pid != gone) {
+    } else {
         char buffer[512];
         snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
         int fd = open(buffer, O_RDONLY);
@@ -117,358 +60,105 @@
     return retval;
 }
 
-UidStatistics::UidStatistics(uid_t uid)
-        : uid(uid)
-        , mSizes(0)
-        , mElements(0) {
-    Pids.clear();
-}
-
-UidStatistics::~UidStatistics() {
-    PidStatisticsCollection::iterator it;
-    for (it = begin(); it != end();) {
-        delete (*it);
-        it = erase(it);
-    }
-}
-
-void UidStatistics::add(unsigned short size, pid_t pid) {
-    mSizes += size;
-    ++mElements;
-
-    PidStatistics *p = NULL;
-    PidStatisticsCollection::iterator last;
-    PidStatisticsCollection::iterator it;
-    for (last = it = begin(); it != end(); last = it, ++it) {
-        p = *it;
-        if (pid == p->getPid()) {
-            p->add(size);
-            return;
-        }
-    }
-    // insert if the gone entry.
-    bool insert_before_last = (last != it) && p && (p->getPid() == p->gone);
-    p = new PidStatistics(pid, pidToName(pid));
-    if (insert_before_last) {
-        insert(last, p);
-    } else {
-        push_back(p);
-    }
-    p->add(size);
-}
-
-void UidStatistics::subtract(unsigned short size, pid_t pid) {
-    mSizes -= size;
-    --mElements;
-
-    PidStatisticsCollection::iterator it;
-    for (it = begin(); it != end(); ++it) {
-        PidStatistics *p = *it;
-        if (pid == p->getPid()) {
-            if (p->subtract(size)) {
-                size_t szsTotal = p->sizesTotal();
-                size_t elsTotal = p->elementsTotal();
-                delete p;
-                erase(it);
-                it = end();
-                --it;
-                if (it == end()) {
-                    p = new PidStatistics(p->gone);
-                    push_back(p);
-                } else {
-                    p = *it;
-                    if (p->getPid() != p->gone) {
-                        p = new PidStatistics(p->gone);
-                        push_back(p);
-                    }
-                }
-                p->addTotal(szsTotal, elsTotal);
-            }
-            return;
-        }
-    }
-}
-
-void UidStatistics::sort() {
-    for (bool pass = true; pass;) {
-        pass = false;
-        PidStatisticsCollection::iterator it = begin();
-        if (it != end()) {
-            PidStatisticsCollection::iterator lt = it;
-            PidStatistics *l = (*lt);
-            while (++it != end()) {
-                PidStatistics *n = (*it);
-                if ((n->getPid() != n->gone) && (n->sizes() > l->sizes())) {
-                    pass = true;
-                    erase(it);
-                    insert(lt, n);
-                    it = lt;
-                    n = l;
-                }
-                lt = it;
-                l = n;
-            }
-        }
-    }
-}
-
-size_t UidStatistics::sizes(pid_t pid) {
-    if (pid == pid_all) {
-        return sizes();
-    }
-
-    PidStatisticsCollection::iterator it;
-    for (it = begin(); it != end(); ++it) {
-        PidStatistics *p = *it;
-        if (pid == p->getPid()) {
-            return p->sizes();
-        }
-    }
-    return 0;
-}
-
-size_t UidStatistics::elements(pid_t pid) {
-    if (pid == pid_all) {
-        return elements();
-    }
-
-    PidStatisticsCollection::iterator it;
-    for (it = begin(); it != end(); ++it) {
-        PidStatistics *p = *it;
-        if (pid == p->getPid()) {
-            return p->elements();
-        }
-    }
-    return 0;
-}
-
-size_t UidStatistics::sizesTotal(pid_t pid) {
-    size_t sizes = 0;
-    PidStatisticsCollection::iterator it;
-    for (it = begin(); it != end(); ++it) {
-        PidStatistics *p = *it;
-        if ((pid == pid_all) || (pid == p->getPid())) {
-            sizes += p->sizesTotal();
-        }
-    }
-    return sizes;
-}
-
-size_t UidStatistics::elementsTotal(pid_t pid) {
-    size_t elements = 0;
-    PidStatisticsCollection::iterator it;
-    for (it = begin(); it != end(); ++it) {
-        PidStatistics *p = *it;
-        if ((pid == pid_all) || (pid == p->getPid())) {
-            elements += p->elementsTotal();
-        }
-    }
-    return elements;
-}
-
-LidStatistics::LidStatistics() {
-    Uids.clear();
-}
-
-LidStatistics::~LidStatistics() {
-    UidStatisticsCollection::iterator it;
-    for (it = begin(); it != end();) {
-        delete (*it);
-        it = Uids.erase(it);
-    }
-}
-
-void LidStatistics::add(unsigned short size, uid_t uid, pid_t pid) {
-    UidStatistics *u;
-    UidStatisticsCollection::iterator it;
-    UidStatisticsCollection::iterator last;
-
-    if (uid == (uid_t) -1) { // init
-        uid = (uid_t) AID_ROOT;
-    }
-
-    for (last = it = begin(); it != end(); last = it, ++it) {
-        u = *it;
-        if (uid == u->getUid()) {
-            u->add(size, pid);
-            if ((last != it) && ((*last)->sizesTotal() < u->sizesTotal())) {
-                Uids.erase(it);
-                Uids.insert(last, u);
-            }
-            return;
-        }
-    }
-    u = new UidStatistics(uid);
-    if ((last != it) && ((*last)->sizesTotal() < (size_t) size)) {
-        Uids.insert(last, u);
-    } else {
-        Uids.push_back(u);
-    }
-    u->add(size, pid);
-}
-
-void LidStatistics::subtract(unsigned short size, uid_t uid, pid_t pid) {
-    if (uid == (uid_t) -1) { // init
-        uid = (uid_t) AID_ROOT;
-    }
-
-    UidStatisticsCollection::iterator it;
-    for (it = begin(); it != end(); ++it) {
-        UidStatistics *u = *it;
-        if (uid == u->getUid()) {
-            u->subtract(size, pid);
-            return;
-        }
-    }
-}
-
-void LidStatistics::sort() {
-    for (bool pass = true; pass;) {
-        pass = false;
-        UidStatisticsCollection::iterator it = begin();
-        if (it != end()) {
-            UidStatisticsCollection::iterator lt = it;
-            UidStatistics *l = (*lt);
-            while (++it != end()) {
-                UidStatistics *n = (*it);
-                if (n->sizes() > l->sizes()) {
-                    pass = true;
-                    Uids.erase(it);
-                    Uids.insert(lt, n);
-                    it = lt;
-                    n = l;
-                }
-                lt = it;
-                l = n;
-            }
-        }
-    }
-}
-
-size_t LidStatistics::sizes(uid_t uid, pid_t pid) {
-    size_t sizes = 0;
-    UidStatisticsCollection::iterator it;
-    for (it = begin(); it != end(); ++it) {
-        UidStatistics *u = *it;
-        if ((uid == uid_all) || (uid == u->getUid())) {
-            sizes += u->sizes(pid);
-        }
-    }
-    return sizes;
-}
-
-size_t LidStatistics::elements(uid_t uid, pid_t pid) {
-    size_t elements = 0;
-    UidStatisticsCollection::iterator it;
-    for (it = begin(); it != end(); ++it) {
-        UidStatistics *u = *it;
-        if ((uid == uid_all) || (uid == u->getUid())) {
-            elements += u->elements(pid);
-        }
-    }
-    return elements;
-}
-
-size_t LidStatistics::sizesTotal(uid_t uid, pid_t pid) {
-    size_t sizes = 0;
-    UidStatisticsCollection::iterator it;
-    for (it = begin(); it != end(); ++it) {
-        UidStatistics *u = *it;
-        if ((uid == uid_all) || (uid == u->getUid())) {
-            sizes += u->sizesTotal(pid);
-        }
-    }
-    return sizes;
-}
-
-size_t LidStatistics::elementsTotal(uid_t uid, pid_t pid) {
-    size_t elements = 0;
-    UidStatisticsCollection::iterator it;
-    for (it = begin(); it != end(); ++it) {
-        UidStatistics *u = *it;
-        if ((uid == uid_all) || (uid == u->getUid())) {
-            elements += u->elementsTotal(pid);
-        }
-    }
-    return elements;
-}
-
-LogStatistics::LogStatistics()
-        : mStatistics(false)
-        , start(CLOCK_MONOTONIC) {
-    log_id_for_each(i) {
-        mSizes[i] = 0;
-        mElements[i] = 0;
-    }
-}
-
-void LogStatistics::add(unsigned short size,
-                        log_id_t log_id, uid_t uid, pid_t pid) {
+void LogStatistics::add(LogBufferElement *e) {
+    log_id_t log_id = e->getLogId();
+    unsigned short size = e->getMsgLen();
     mSizes[log_id] += size;
     ++mElements[log_id];
-    if (!mStatistics) {
-        return;
+
+    uid_t uid = e->getUid();
+    android::hash_t hash = android::hash_type(uid);
+    uidTable_t &table = uidTable[log_id];
+    ssize_t index = table.find(-1, hash, uid);
+    if (index == -1) {
+        UidEntry initEntry(uid);
+        initEntry.add(size);
+        table.add(hash, initEntry);
+    } else {
+        UidEntry &entry = table.editEntryAt(index);
+        entry.add(size);
     }
-    id(log_id).add(size, uid, pid);
+
+    mSizesTotal[log_id] += size;
+    ++mElementsTotal[log_id];
 }
 
-void LogStatistics::subtract(unsigned short size,
-                             log_id_t log_id, uid_t uid, pid_t pid) {
+void LogStatistics::subtract(LogBufferElement *e) {
+    log_id_t log_id = e->getLogId();
+    unsigned short size = e->getMsgLen();
     mSizes[log_id] -= size;
     --mElements[log_id];
-    if (!mStatistics) {
-        return;
+
+    uid_t uid = e->getUid();
+    android::hash_t hash = android::hash_type(uid);
+    uidTable_t &table = uidTable[log_id];
+    ssize_t index = table.find(-1, hash, uid);
+    if (index != -1) {
+        UidEntry &entry = table.editEntryAt(index);
+        if (entry.subtract(size)) {
+            table.removeAt(index);
+        }
     }
-    id(log_id).subtract(size, uid, pid);
 }
 
-size_t LogStatistics::sizes(log_id_t log_id, uid_t uid, pid_t pid) {
-    if (log_id != log_id_all) {
-        return id(log_id).sizes(uid, pid);
+// caller must own and delete UidEntry array
+const UidEntry **LogStatistics::sort(size_t n, log_id id) {
+    if (!n) {
+        return NULL;
     }
-    size_t sizes = 0;
-    log_id_for_each(i) {
-        sizes += id(i).sizes(uid, pid);
+
+    const UidEntry **retval = new const UidEntry* [n];
+    memset(retval, 0, sizeof(*retval) * n);
+
+    uidTable_t &table = uidTable[id];
+    ssize_t index = -1;
+    while ((index = table.next(index)) >= 0) {
+        const UidEntry &entry = table.entryAt(index);
+        size_t s = entry.getSizes();
+        ssize_t i = n - 1;
+        while ((!retval[i] || (s > retval[i]->getSizes())) && (--i >= 0));
+        if (++i < (ssize_t)n) {
+            size_t b = n - i - 1;
+            if (b) {
+                memmove(&retval[i+1], &retval[i], b * sizeof(retval[0]));
+            }
+            retval[i] = &entry;
+        }
     }
-    return sizes;
+    return retval;
 }
 
-size_t LogStatistics::elements(log_id_t log_id, uid_t uid, pid_t pid) {
-    if (log_id != log_id_all) {
-        return id(log_id).elements(uid, pid);
+// caller must own and free character string
+char *LogStatistics::uidToName(uid_t uid) {
+    // Local hard coded favourites
+    if (uid == AID_LOGD) {
+        return strdup("auditd");
     }
-    size_t elements = 0;
-    log_id_for_each(i) {
-        elements += id(i).elements(uid, pid);
+
+    // Android hard coded
+    const struct android_id_info *info = android_ids;
+
+    for (size_t i = 0; i < android_id_count; ++i) {
+        if (info->aid == uid) {
+            return strdup(info->name);
+        }
+        ++info;
     }
-    return elements;
+
+    // No one
+    return NULL;
 }
 
-size_t LogStatistics::sizesTotal(log_id_t log_id, uid_t uid, pid_t pid) {
-    if (log_id != log_id_all) {
-        return id(log_id).sizesTotal(uid, pid);
-    }
-    size_t sizes = 0;
-    log_id_for_each(i) {
-        sizes += id(i).sizesTotal(uid, pid);
-    }
-    return sizes;
+static void format_line(android::String8 &output,
+        android::String8 &name, android::String8 &size) {
+    static const size_t total_len = 70;
+
+    output.appendFormat("%s%*s\n", name.string(),
+        (int)std::max(total_len - name.length() - 1, size.length() + 1),
+        size.string());
 }
 
-size_t LogStatistics::elementsTotal(log_id_t log_id, uid_t uid, pid_t pid) {
-    if (log_id != log_id_all) {
-        return id(log_id).elementsTotal(uid, pid);
-    }
-    size_t elements = 0;
-    log_id_for_each(i) {
-        elements += id(i).elementsTotal(uid, pid);
-    }
-    return elements;
-}
-
-void LogStatistics::format(char **buf,
-                           uid_t uid, unsigned int logMask, log_time oldest) {
-    static const unsigned short spaces_current = 13;
+void LogStatistics::format(char **buf, uid_t uid, unsigned int logMask) {
     static const unsigned short spaces_total = 19;
 
     if (*buf) {
@@ -476,368 +166,142 @@
         *buf = NULL;
     }
 
-    android::String8 string("        span -> size/num");
-    size_t oldLength;
-    short spaces = 2;
+    // Report on total logging, current and for all time
 
-    log_id_for_each(i) {
-        if (!(logMask & (1 << i))) {
+    android::String8 output("size/num");
+    size_t oldLength;
+    short spaces = 1;
+
+    log_id_for_each(id) {
+        if (!(logMask & (1 << id))) {
             continue;
         }
-        oldLength = string.length();
+        oldLength = output.length();
         if (spaces < 0) {
             spaces = 0;
         }
-        string.appendFormat("%*s%s", spaces, "", android_log_id_to_name(i));
-        spaces += spaces_total + oldLength - string.length();
-
-        LidStatistics &l = id(i);
-        l.sort();
-
-        UidStatisticsCollection::iterator iu;
-        for (iu = l.begin(); iu != l.end(); ++iu) {
-            (*iu)->sort();
-        }
+        output.appendFormat("%*s%s", spaces, "", android_log_id_to_name(id));
+        spaces += spaces_total + oldLength - output.length();
     }
 
-    spaces = 1;
-    log_time t(CLOCK_MONOTONIC);
-    unsigned long long d;
-    if (mStatistics) {
-        d = t.nsec() - start.nsec();
-        string.appendFormat("\nTotal%4llu:%02llu:%02llu.%09llu",
-                  d / NS_PER_SEC / 60 / 60, (d / NS_PER_SEC / 60) % 60,
-                  (d / NS_PER_SEC) % 60, d % NS_PER_SEC);
+    spaces = 4;
+    output.appendFormat("\nTotal");
 
-        log_id_for_each(i) {
-            if (!(logMask & (1 << i))) {
-                continue;
-            }
-            oldLength = string.length();
-            if (spaces < 0) {
-                spaces = 0;
-            }
-            string.appendFormat("%*s%zu/%zu", spaces, "",
-                                sizesTotal(i), elementsTotal(i));
-            spaces += spaces_total + oldLength - string.length();
+    log_id_for_each(id) {
+        if (!(logMask & (1 << id))) {
+            continue;
         }
-        spaces = 1;
+        oldLength = output.length();
+        if (spaces < 0) {
+            spaces = 0;
+        }
+        output.appendFormat("%*s%zu/%zu", spaces, "",
+                            sizesTotal(id), elementsTotal(id));
+        spaces += spaces_total + oldLength - output.length();
     }
 
-    d = t.nsec() - oldest.nsec();
-    string.appendFormat("\nNow%6llu:%02llu:%02llu.%09llu",
-                  d / NS_PER_SEC / 60 / 60, (d / NS_PER_SEC / 60) % 60,
-                  (d / NS_PER_SEC) % 60, d % NS_PER_SEC);
+    spaces = 6;
+    output.appendFormat("\nNow");
 
-    log_id_for_each(i) {
-        if (!(logMask & (1 << i))) {
+    log_id_for_each(id) {
+        if (!(logMask & (1 << id))) {
             continue;
         }
 
-        size_t els = elements(i);
+        size_t els = elements(id);
         if (els) {
-            oldLength = string.length();
+            oldLength = output.length();
             if (spaces < 0) {
                 spaces = 0;
             }
-            string.appendFormat("%*s%zu/%zu", spaces, "", sizes(i), els);
-            spaces -= string.length() - oldLength;
+            output.appendFormat("%*s%zu/%zu", spaces, "", sizes(id), els);
+            spaces -= output.length() - oldLength;
         }
         spaces += spaces_total;
     }
 
-    // Construct list of worst spammers by Pid
-    static const unsigned char num_spammers = 10;
-    bool header = false;
+    // Report on Chattiest
 
-    log_id_for_each(i) {
-        if (!(logMask & (1 << i))) {
+    // Chattiest by application (UID)
+    log_id_for_each(id) {
+        if (!(logMask & (1 << id))) {
             continue;
         }
 
-        PidStatisticsCollection pids;
-        pids.clear();
+        static const size_t maximum_sorted_entries = 32;
+        const UidEntry **sorted = sort(maximum_sorted_entries, id);
 
-        LidStatistics &l = id(i);
-        UidStatisticsCollection::iterator iu;
-        for (iu = l.begin(); iu != l.end(); ++iu) {
-            UidStatistics &u = *(*iu);
-            PidStatisticsCollection::iterator ip;
-            for (ip = u.begin(); ip != u.end(); ++ip) {
-                PidStatistics *p = (*ip);
-                if (p->getPid() == p->gone) {
-                    break;
-                }
-
-                size_t mySizes = p->sizes();
-
-                PidStatisticsCollection::iterator q;
-                unsigned char num = 0;
-                for (q = pids.begin(); q != pids.end(); ++q) {
-                    if (mySizes > (*q)->sizes()) {
-                        pids.insert(q, p);
-                        break;
-                    }
-                    // do we need to traverse deeper in the list?
-                    if (++num > num_spammers) {
-                        break;
-                    }
-                }
-                if (q == pids.end()) {
-                   pids.push_back(p);
-                }
-            }
+        if (!sorted) {
+            continue;
         }
 
-        size_t threshold = sizes(i);
-        if (threshold < 65536) {
-            threshold = 65536;
-        }
-        threshold /= 100;
+        bool print = false;
+        for(size_t index = 0; index < maximum_sorted_entries; ++index) {
+            const UidEntry *entry = sorted[index];
 
-        PidStatisticsCollection::iterator pt = pids.begin();
-
-        for(int line = 0;
-                (pt != pids.end()) && (line < num_spammers);
-                ++line, pt = pids.erase(pt)) {
-            PidStatistics *p = *pt;
-
-            size_t sizes = p->sizes();
-            if (sizes < threshold) {
+            if (!entry) {
                 break;
             }
 
-            char *name = p->getName();
-            pid_t pid = p->getPid();
-            if (!name || !*name) {
-                name = pidToName(pid);
-                if (name) {
-                    if (*name) {
-                        p->setName(name);
-                    } else {
-                        free(name);
-                        name = NULL;
-                    }
+            size_t sizes = entry->getSizes();
+            if (sizes < (65536/100)) {
+                break;
+            }
+
+            uid_t u = entry->getKey();
+            if ((uid != AID_ROOT) && (u != uid)) {
+                continue;
+            }
+
+            if (!print) {
+                if (uid == AID_ROOT) {
+                    output.appendFormat(
+                        "\n\nChattiest UIDs in %s:\n",
+                        android_log_id_to_name(id));
+                    android::String8 name("UID");
+                    android::String8 size("Size");
+                    format_line(output, name, size);
+                } else {
+                    output.appendFormat(
+                        "\n\nLogging for your UID in %s:\n",
+                        android_log_id_to_name(id));
                 }
+                print = true;
             }
 
-            if (!header) {
-                string.appendFormat("\n\nChattiest clients:\n"
-                                    "log id %-*s PID[?] name",
-                                    spaces_total, "size/total");
-                header = true;
+            android::String8 name("");
+            name.appendFormat("%u", u);
+            char *n = uidToName(u);
+            if (n) {
+                name.appendFormat("%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", n);
+                free(n);
             }
 
-            size_t sizesTotal = p->sizesTotal();
+            android::String8 size("");
+            size.appendFormat("%zu", sizes);
 
-            android::String8 sz("");
-            if (sizes == sizesTotal) {
-                sz.appendFormat("%zu", sizes);
-            } else {
-                sz.appendFormat("%zu/%zu", sizes, sizesTotal);
-            }
-
-            android::String8 pd("");
-            pd.appendFormat("%u%c", pid, p->pidGone() ? '?' : ' ');
-
-            string.appendFormat("\n%-7s%-*s %-7s%s",
-                                line ? "" : android_log_id_to_name(i),
-                                spaces_total, sz.string(), pd.string(),
-                                name ? name : "");
+            format_line(output, name, size);
         }
 
-        pids.clear();
+        delete [] sorted;
     }
 
-    log_id_for_each(i) {
-        if (!(logMask & (1 << i))) {
-            continue;
-        }
-
-        header = false;
-        bool first = true;
-
-        UidStatisticsCollection::iterator ut;
-        for(ut = id(i).begin(); ut != id(i).end(); ++ut) {
-            UidStatistics *up = *ut;
-            if ((uid != AID_ROOT) && (uid != up->getUid())) {
-                continue;
-            }
-
-            PidStatisticsCollection::iterator pt = up->begin();
-            if (pt == up->end()) {
-                continue;
-            }
-
-            android::String8 intermediate;
-
-            if (!header) {
-                // header below tuned to match spaces_total and spaces_current
-                spaces = 0;
-                intermediate = string.format("%s: UID/PID Total size/num",
-                                             android_log_id_to_name(i));
-                string.appendFormat("\n\n%-31sNow          "
-                                         "UID/PID[?]  Total              Now",
-                                    intermediate.string());
-                intermediate.clear();
-                header = true;
-            }
-
-            bool oneline = ++pt == up->end();
-            --pt;
-
-            if (!oneline) {
-                first = true;
-            } else if (!first && (spaces > 0)) {
-                string.appendFormat("%*s", spaces, "");
-            }
-            spaces = 0;
-
-            uid_t u = up->getUid();
-            PidStatistics *pp = *pt;
-            pid_t p = pp->getPid();
-
-            if (!oneline) {
-                intermediate = string.format("%d", u);
-            } else if (p == PidStatistics::gone) {
-                intermediate = string.format("%d/?", u);
-            } else if (pp->pidGone()) {
-                intermediate = string.format("%d/%d?", u, p);
-            } else {
-                intermediate = string.format("%d/%d", u, p);
-            }
-            string.appendFormat(first ? "\n%-12s" : "%-12s",
-                                intermediate.string());
-            intermediate.clear();
-
-            size_t elsTotal = up->elementsTotal();
-            oldLength = string.length();
-            string.appendFormat("%zu/%zu", up->sizesTotal(), elsTotal);
-            spaces += spaces_total + oldLength - string.length();
-
-            size_t els = up->elements();
-            if (els == elsTotal) {
-                if (spaces < 0) {
-                    spaces = 0;
-                }
-                string.appendFormat("%*s=", spaces, "");
-                spaces = -1;
-            } else if (els) {
-                oldLength = string.length();
-                if (spaces < 0) {
-                    spaces = 0;
-                }
-                string.appendFormat("%*s%zu/%zu", spaces, "", up->sizes(), els);
-                spaces -= string.length() - oldLength;
-            }
-            spaces += spaces_current;
-
-            first = !first;
-
-            if (oneline) {
-                continue;
-            }
-
-            size_t gone_szs = 0;
-            size_t gone_els = 0;
-
-            for(; pt != up->end(); ++pt) {
-                pp = *pt;
-                p = pp->getPid();
-
-                // If a PID no longer has any current logs, and is not
-                // active anymore, skip & report totals for gone.
-                elsTotal = pp->elementsTotal();
-                size_t szsTotal = pp->sizesTotal();
-                if (p == pp->gone) {
-                    gone_szs += szsTotal;
-                    gone_els += elsTotal;
-                    continue;
-                }
-                els = pp->elements();
-                bool gone = pp->pidGone();
-                if (gone && (els == 0)) {
-                    // ToDo: garbage collection: move this statistical bucket
-                    //       from its current UID/PID to UID/? (races and
-                    //       wrap around are our achilles heel). Below is
-                    //       merely lipservice to catch PIDs that were still
-                    //       around when the stats were pruned to zero.
-                    gone_szs += szsTotal;
-                    gone_els += elsTotal;
-                    continue;
-                }
-
-                if (!first && (spaces > 0)) {
-                    string.appendFormat("%*s", spaces, "");
-                }
-                spaces = 0;
-
-                intermediate = string.format(gone ? "%d/%d?" : "%d/%d", u, p);
-                string.appendFormat(first ? "\n%-12s" : "%-12s",
-                                    intermediate.string());
-                intermediate.clear();
-
-                oldLength = string.length();
-                string.appendFormat("%zu/%zu", szsTotal, elsTotal);
-                spaces += spaces_total + oldLength - string.length();
-
-                if (els == elsTotal) {
-                    if (spaces < 0) {
-                        spaces = 0;
-                    }
-                    string.appendFormat("%*s=", spaces, "");
-                    spaces = -1;
-                } else if (els) {
-                    oldLength = string.length();
-                    if (spaces < 0) {
-                        spaces = 0;
-                    }
-                    string.appendFormat("%*s%zu/%zu", spaces, "",
-                                        pp->sizes(), els);
-                    spaces -= string.length() - oldLength;
-                }
-                spaces += spaces_current;
-
-                first = !first;
-            }
-
-            if (gone_els) {
-                if (!first && (spaces > 0)) {
-                    string.appendFormat("%*s", spaces, "");
-                }
-
-                intermediate = string.format("%d/?", u);
-                string.appendFormat(first ? "\n%-12s" : "%-12s",
-                                    intermediate.string());
-                intermediate.clear();
-
-                spaces = spaces_total + spaces_current;
-
-                oldLength = string.length();
-                string.appendFormat("%zu/%zu", gone_szs, gone_els);
-                spaces -= string.length() - oldLength;
-
-                first = !first;
-            }
-        }
-    }
-
-    *buf = strdup(string.string());
+    *buf = strdup(output.string());
 }
 
 uid_t LogStatistics::pidToUid(pid_t pid) {
-    log_id_for_each(i) {
-        LidStatistics &l = id(i);
-        UidStatisticsCollection::iterator iu;
-        for (iu = l.begin(); iu != l.end(); ++iu) {
-            UidStatistics &u = *(*iu);
-            PidStatisticsCollection::iterator ip;
-            for (ip = u.begin(); ip != u.end(); ++ip) {
-                if ((*ip)->getPid() == pid) {
-                    return u.getUid();
-                }
+    char buffer[512];
+    snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
+    FILE *fp = fopen(buffer, "r");
+    if (fp) {
+        while (fgets(buffer, sizeof(buffer), fp)) {
+            int uid;
+            if (sscanf(buffer, "Groups: %d", &uid) == 1) {
+                fclose(fp);
+                return uid;
             }
         }
+        fclose(fp);
     }
     return getuid(); // associate this with the logger
 }
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index f892cd0..d5b8762 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -20,175 +20,60 @@
 #include <sys/types.h>
 
 #include <log/log.h>
-#include <log/log_read.h>
-#include <utils/List.h>
+#include <utils/BasicHashtable.h>
+
+#include "LogBufferElement.h"
 
 #define log_id_for_each(i) \
     for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t) (i + 1))
 
-class PidStatistics {
-    const pid_t pid;
-
-    // Total
-    size_t mSizesTotal;
-    size_t mElementsTotal;
-    // Current
-    size_t mSizes;
-    size_t mElements;
-
-    char *name;
-    bool mGone;
-
-public:
-    static const pid_t gone = (pid_t) -1;
-
-    PidStatistics(pid_t pid, char *name = NULL);
-    PidStatistics(const PidStatistics &copy);
-    ~PidStatistics();
-
-    pid_t getPid() const { return pid; }
-    bool pidGone();
-    char *getName() const { return name; }
-    void setName(char *name);
-
-    void add(unsigned short size);
-    bool subtract(unsigned short size); // returns true if stats and PID gone
-    void addTotal(size_t size, size_t element);
-
-    size_t sizes() const { return mSizes; }
-    size_t elements() const { return mElements; }
-
-    size_t sizesTotal() const { return mSizesTotal; }
-    size_t elementsTotal() const { return mElementsTotal; }
-
-    // helper
-    static char *pidToName(pid_t pid);
-};
-
-typedef android::List<PidStatistics *> PidStatisticsCollection;
-
-class UidStatistics {
+struct UidEntry {
     const uid_t uid;
+    size_t size;
 
-    PidStatisticsCollection Pids;
+    UidEntry(uid_t uid):uid(uid),size(0) { }
 
-    void insert(PidStatisticsCollection::iterator i, PidStatistics *p)
-        { Pids.insert(i, p); }
-    void push_back(PidStatistics *p) { Pids.push_back(p); }
-
-    size_t mSizes;
-    size_t mElements;
-
-public:
-    UidStatistics(uid_t uid);
-    ~UidStatistics();
-
-    PidStatisticsCollection::iterator begin() { return Pids.begin(); }
-    PidStatisticsCollection::iterator end() { return Pids.end(); }
-    PidStatisticsCollection::iterator erase(PidStatisticsCollection::iterator i)
-        { return Pids.erase(i); }
-
-    uid_t getUid() { return uid; }
-
-    void add(unsigned short size, pid_t pid);
-    void subtract(unsigned short size, pid_t pid);
-    void sort();
-
-    static const pid_t pid_all = (pid_t) -1;
-
-    // fast track current value
-    size_t sizes() const { return mSizes; };
-    size_t elements() const { return mElements; };
-
-    // statistical track
-    size_t sizes(pid_t pid);
-    size_t elements(pid_t pid);
-
-    size_t sizesTotal(pid_t pid = pid_all);
-    size_t elementsTotal(pid_t pid = pid_all);
-
-    // helper
-    static char *pidToName(pid_t pid) { return PidStatistics::pidToName(pid); }
-};
-
-typedef android::List<UidStatistics *> UidStatisticsCollection;
-
-class LidStatistics {
-    UidStatisticsCollection Uids;
-
-public:
-    LidStatistics();
-    ~LidStatistics();
-
-    UidStatisticsCollection::iterator begin() { return Uids.begin(); }
-    UidStatisticsCollection::iterator end() { return Uids.end(); }
-
-    void add(unsigned short size, uid_t uid, pid_t pid);
-    void subtract(unsigned short size, uid_t uid, pid_t pid);
-    void sort();
-
-    static const pid_t pid_all = (pid_t) -1;
-    static const uid_t uid_all = (uid_t) -1;
-
-    size_t sizes(uid_t uid = uid_all, pid_t pid = pid_all);
-    size_t elements(uid_t uid = uid_all, pid_t pid = pid_all);
-
-    size_t sizesTotal(uid_t uid = uid_all, pid_t pid = pid_all);
-    size_t elementsTotal(uid_t uid = uid_all, pid_t pid = pid_all);
+    inline const uid_t&getKey() const { return uid; }
+    size_t getSizes() const { return size; }
+    inline void add(size_t s) { size += s; }
+    inline bool subtract(size_t s) { size -= s; return !size; }
 };
 
 // Log Statistics
 class LogStatistics {
-    LidStatistics LogIds[LOG_ID_MAX];
-
     size_t mSizes[LOG_ID_MAX];
     size_t mElements[LOG_ID_MAX];
+    size_t mSizesTotal[LOG_ID_MAX];
+    size_t mElementsTotal[LOG_ID_MAX];
 
-    bool mStatistics;
-
-    static const unsigned short mBuckets[14];
-    log_time mMinimum[sizeof(mBuckets) / sizeof(mBuckets[0])];
+    // uid to size list
+    typedef android::BasicHashtable<uid_t, UidEntry> uidTable_t;
+    uidTable_t uidTable[LOG_ID_MAX];
 
 public:
-    const log_time start;
-
     LogStatistics();
 
-    LidStatistics &id(log_id_t log_id) { return LogIds[log_id]; }
+    void enableStatistics() { }
 
-    void enableStatistics() { mStatistics = true; }
+    void add(LogBufferElement *entry);
+    void subtract(LogBufferElement *entry);
 
-    void add(unsigned short size, log_id_t log_id, uid_t uid, pid_t pid);
-    void subtract(unsigned short size, log_id_t log_id, uid_t uid, pid_t pid);
-    void sort();
+    // Caller must delete array
+    const UidEntry **sort(size_t n, log_id i);
 
     // fast track current value by id only
     size_t sizes(log_id_t id) const { return mSizes[id]; }
     size_t elements(log_id_t id) const { return mElements[id]; }
-
-    // statistical track
-    static const log_id_t log_id_all = (log_id_t) -1;
-    static const uid_t uid_all = (uid_t) -1;
-    static const pid_t pid_all = (pid_t) -1;
-
-    size_t sizes(log_id_t id, uid_t uid, pid_t pid = pid_all);
-    size_t elements(log_id_t id, uid_t uid, pid_t pid = pid_all);
-    size_t sizes() { return sizes(log_id_all, uid_all); }
-    size_t elements() { return elements(log_id_all, uid_all); }
-
-    size_t sizesTotal(log_id_t id = log_id_all,
-                      uid_t uid = uid_all,
-                      pid_t pid = pid_all);
-    size_t elementsTotal(log_id_t id = log_id_all,
-                         uid_t uid = uid_all,
-                         pid_t pid = pid_all);
+    size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; }
+    size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; }
 
     // *strp = malloc, balance with free
-    void format(char **strp, uid_t uid, unsigned int logMask, log_time oldest);
+    void format(char **strp, uid_t uid, unsigned int logMask);
 
     // helper
-    static char *pidToName(pid_t pid) { return PidStatistics::pidToName(pid); }
+    char *pidToName(pid_t pid);
     uid_t pidToUid(pid_t pid);
+    char *uidToName(uid_t uid);
 };
 
 #endif // _LOGD_LOG_STATISTICS_H__
diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h
index 769d651..5f60801 100644
--- a/logd/LogWhiteBlackList.h
+++ b/logd/LogWhiteBlackList.h
@@ -61,7 +61,9 @@
     int init(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; }
 
     // *strp is malloc'd, use free to release
diff --git a/toolbox/uptime.c b/toolbox/uptime.c
index 2dd8084..ebfb15e 100644
--- a/toolbox/uptime.c
+++ b/toolbox/uptime.c
@@ -29,71 +29,33 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/time.h>
-#include <linux/ioctl.h>
-#include <linux/rtc.h>
-#include <linux/android_alarm.h>
-#include <fcntl.h>
+#include <errno.h>
 #include <stdio.h>
+#include <string.h>
 #include <time.h>
-#include <unistd.h>
 
 static void format_time(int time, char* buffer) {
-    int seconds, minutes, hours, days;
-
-    seconds = time % 60;
+    int seconds = time % 60;
     time /= 60;
-    minutes = time % 60;
+    int minutes = time % 60;
     time /= 60;
-    hours = time % 24;
-    days = time / 24;
+    int hours = time % 24;
+    int days = time / 24;
 
-    if (days > 0)
-        sprintf(buffer, "%d days, %02d:%02d:%02d", days, hours, minutes, seconds);
-    else
+    if (days > 0) {
+        sprintf(buffer, "%d day%s, %02d:%02d:%02d", days, (days == 1) ? "" : "s", hours, minutes, seconds);
+    } else {
         sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds);
+    }
 }
 
-static int elapsedRealtimeAlarm(struct timespec *ts)
-{
-    int fd, result;
-
-    fd = open("/dev/alarm", O_RDONLY);
-    if (fd < 0)
-        return fd;
-
-    result = ioctl(fd, ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), ts);
-    close(fd);
-
-    return result;
-}
-
-int64_t elapsedRealtime()
-{
-    struct timespec ts;
-
-    int result = elapsedRealtimeAlarm(&ts);
-    if (result < 0)
-        result = clock_gettime(CLOCK_BOOTTIME, &ts);
-
-    if (result == 0)
-        return ts.tv_sec;
-    return -1;
-}
-
-int uptime_main(int argc __attribute__((unused)),
-        char *argv[] __attribute__((unused)))
-{
-    float up_time, idle_time;
-    char up_string[100], idle_string[100], sleep_string[100];
-    int elapsed;
-    struct timespec up_timespec;
-
+int uptime_main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) {
     FILE* file = fopen("/proc/uptime", "r");
     if (!file) {
         fprintf(stderr, "Could not open /proc/uptime\n");
         return -1;
     }
+    float idle_time;
     if (fscanf(file, "%*f %f", &idle_time) != 1) {
         fprintf(stderr, "Could not parse /proc/uptime\n");
         fclose(file);
@@ -101,18 +63,21 @@
     }
     fclose(file);
 
-    if (clock_gettime(CLOCK_MONOTONIC, &up_timespec) < 0) {
-        fprintf(stderr, "Could not get monotonic time\n");
+    struct timespec up_timespec;
+    if (clock_gettime(CLOCK_MONOTONIC, &up_timespec) == -1) {
+        fprintf(stderr, "Could not get monotonic time: %s\n", strerror(errno));
 	return -1;
     }
-    up_time = up_timespec.tv_sec + up_timespec.tv_nsec / 1e9;
+    float up_time = up_timespec.tv_sec + up_timespec.tv_nsec / 1e9;
 
-    elapsed = elapsedRealtime();
-    if (elapsed < 0) {
-        fprintf(stderr, "elapsedRealtime failed\n");
+    struct timespec elapsed_timespec;
+    if (clock_gettime(CLOCK_BOOTTIME, &elapsed_timespec) == -1) {
+        fprintf(stderr, "Could not get boot time: %s\n", strerror(errno));
         return -1;
     }
+    int elapsed = elapsed_timespec.tv_sec;
 
+    char up_string[100], idle_string[100], sleep_string[100];
     format_time(elapsed, up_string);
     format_time((int)idle_time, idle_string);
     format_time((int)(elapsed - up_time), sleep_string);