Merge "Support symbol versioning"
diff --git a/libc/Android.mk b/libc/Android.mk
index 78b8475..9d3b62a 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -63,6 +63,7 @@
     stdio/sprintf.c \
     stdio/stdio.c \
     stdio/stdio_ext.cpp \
+    stdlib/exit.c \
 
 # Fortify implementations of libc functions.
 libc_common_src_files += \
@@ -70,6 +71,8 @@
     bionic/__fgets_chk.cpp \
     bionic/__memmove_chk.cpp \
     bionic/__poll_chk.cpp \
+    bionic/__pread64_chk.cpp \
+    bionic/__pread_chk.cpp \
     bionic/__read_chk.cpp \
     bionic/__recvfrom_chk.cpp \
     bionic/__stpcpy_chk.cpp \
@@ -480,7 +483,6 @@
     upstream-openbsd/lib/libc/stdlib/atoi.c \
     upstream-openbsd/lib/libc/stdlib/atol.c \
     upstream-openbsd/lib/libc/stdlib/atoll.c \
-    upstream-openbsd/lib/libc/stdlib/exit.c \
     upstream-openbsd/lib/libc/stdlib/getenv.c \
     upstream-openbsd/lib/libc/stdlib/insque.c \
     upstream-openbsd/lib/libc/stdlib/lsearch.c \
diff --git a/libc/bionic/__pread64_chk.cpp b/libc/bionic/__pread64_chk.cpp
new file mode 100644
index 0000000..5d6ad2d
--- /dev/null
+++ b/libc/bionic/__pread64_chk.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#undef _FORTIFY_SOURCE
+#include <unistd.h>
+#include "private/libc_logging.h"
+
+extern "C" ssize_t __pread64_chk(int fd, void* buf, size_t count, off64_t offset, size_t buf_size) {
+  if (__predict_false(count > buf_size)) {
+    __fortify_chk_fail("pread64: prevented write past end of buffer", 0);
+  }
+
+  if (__predict_false(count > SSIZE_MAX)) {
+    __fortify_chk_fail("pread64: count > SSIZE_MAX", 0);
+  }
+
+  return pread64(fd, buf, count, offset);
+}
diff --git a/libc/bionic/__pread_chk.cpp b/libc/bionic/__pread_chk.cpp
new file mode 100644
index 0000000..7109ce6
--- /dev/null
+++ b/libc/bionic/__pread_chk.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#undef _FORTIFY_SOURCE
+#include <unistd.h>
+#include "private/libc_logging.h"
+
+extern "C" ssize_t __pread_chk(int fd, void* buf, size_t count, off_t offset, size_t buf_size) {
+  if (__predict_false(count > buf_size)) {
+    __fortify_chk_fail("pread: prevented write past end of buffer", 0);
+  }
+
+  if (__predict_false(count > SSIZE_MAX)) {
+    __fortify_chk_fail("pread: count > SSIZE_MAX", 0);
+  }
+
+  return pread(fd, buf, count, offset);
+}
diff --git a/libc/bionic/legacy_32_bit_support.cpp b/libc/bionic/legacy_32_bit_support.cpp
index 50e4643..a107664 100644
--- a/libc/bionic/legacy_32_bit_support.cpp
+++ b/libc/bionic/legacy_32_bit_support.cpp
@@ -26,6 +26,8 @@
  * SUCH DAMAGE.
  */
 
+#undef _FORTIFY_SOURCE
+
 #include <errno.h>
 #include <fcntl.h>
 #include <stdarg.h>
diff --git a/libc/bionic/posix_timers.cpp b/libc/bionic/posix_timers.cpp
index 9991573..bc3aeb2 100644
--- a/libc/bionic/posix_timers.cpp
+++ b/libc/bionic/posix_timers.cpp
@@ -26,14 +26,15 @@
  * SUCH DAMAGE.
  */
 
-#include "pthread_internal.h"
-#include "private/bionic_futex.h"
 #include "private/kernel_sigset_t.h"
 
 #include <errno.h>
 #include <malloc.h>
+#include <pthread.h>
+#include <stdatomic.h>
 #include <stdio.h>
 #include <string.h>
+#include <time.h>
 
 // System calls.
 extern "C" int __rt_sigtimedwait(const sigset_t*, siginfo_t*, const struct timespec*, size_t);
@@ -59,11 +60,11 @@
 
   int sigev_notify;
 
-  // These fields are only needed for a SIGEV_THREAD timer.
+  // The fields below are only needed for a SIGEV_THREAD timer.
   pthread_t callback_thread;
   void (*callback)(sigval_t);
   sigval_t callback_argument;
-  volatile bool armed;
+  atomic_bool deleted;  // Set when the timer is deleted, to prevent further calling of callback.
 };
 
 static __kernel_timer_t to_kernel_timer_id(timer_t timer) {
@@ -85,8 +86,13 @@
       continue;
     }
 
-    if (si.si_code == SI_TIMER && timer->armed) {
+    if (si.si_code == SI_TIMER) {
       // This signal was sent because a timer fired, so call the callback.
+
+      // All events to the callback will be ignored when the timer is deleted.
+      if (atomic_load(&timer->deleted) == true) {
+        continue;
+      }
       timer->callback(timer->callback_argument);
     } else if (si.si_code == SI_TKILL) {
       // This signal was sent because someone wants us to exit.
@@ -97,9 +103,7 @@
 }
 
 static void __timer_thread_stop(PosixTimer* timer) {
-  // Immediately mark the timer as disarmed so even if some events
-  // continue to happen, the callback won't be called.
-  timer->armed = false;
+  atomic_store(&timer->deleted, true);
   pthread_kill(timer->callback_thread, TIMER_SIGNAL);
 }
 
@@ -126,7 +130,7 @@
   // Otherwise, this must be SIGEV_THREAD timer...
   timer->callback = evp->sigev_notify_function;
   timer->callback_argument = evp->sigev_value;
-  timer->armed = false;
+  atomic_init(&timer->deleted, false);
 
   // Check arguments that the kernel doesn't care about but we do.
   if (timer->callback == NULL) {
@@ -199,25 +203,19 @@
   return 0;
 }
 
-// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html
+// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_gettime.html
 int timer_gettime(timer_t id, itimerspec* ts) {
   return __timer_gettime(to_kernel_timer_id(id), ts);
 }
 
-// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html
+// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_settime.html
+// When using timer_settime to disarm a repeatable SIGEV_THREAD timer with a very small
+// period (like below 1ms), the kernel may continue to send events to the callback thread
+// for a few extra times. This behavior is fine because in POSIX standard: The effect of
+// disarming or resetting a timer with pending expiration notifications is unspecified.
 int timer_settime(timer_t id, int flags, const itimerspec* ts, itimerspec* ots) {
   PosixTimer* timer= reinterpret_cast<PosixTimer*>(id);
-  int rc = __timer_settime(timer->kernel_timer_id, flags, ts, ots);
-  if (rc == 0) {
-    // Mark the timer as either being armed or disarmed. This avoids the
-    // callback being called after the disarm for SIGEV_THREAD timers only.
-    if (ts->it_value.tv_sec != 0 || ts->it_value.tv_nsec != 0) {
-      timer->armed = true;
-    } else {
-      timer->armed = false;
-    }
-  }
-  return rc;
+  return __timer_settime(timer->kernel_timer_id, flags, ts, ots);
 }
 
 // http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html
diff --git a/libc/include/sys/_system_properties.h b/libc/include/sys/_system_properties.h
index 44fe991..7ff3ded 100644
--- a/libc/include/sys/_system_properties.h
+++ b/libc/include/sys/_system_properties.h
@@ -80,7 +80,6 @@
 
 #define PROP_PATH_RAMDISK_DEFAULT  "/default.prop"
 #define PROP_PATH_SYSTEM_BUILD     "/system/build.prop"
-#define PROP_PATH_SYSTEM_DEFAULT   "/system/default.prop"
 #define PROP_PATH_VENDOR_BUILD     "/vendor/build.prop"
 #define PROP_PATH_BOOTIMAGE_BUILD  "/build.prop"
 #define PROP_PATH_LOCAL_OVERRIDE   "/data/local.prop"
diff --git a/libc/include/unistd.h b/libc/include/unistd.h
index 92d3abe..a601cb7 100644
--- a/libc/include/unistd.h
+++ b/libc/include/unistd.h
@@ -224,6 +224,16 @@
     } while (_rc == -1 && errno == EINTR); \
     _rc; })
 
+extern ssize_t __pread_chk(int, void*, size_t, off_t, size_t);
+__errordecl(__pread_dest_size_error, "pread called with size bigger than destination");
+__errordecl(__pread_count_toobig_error, "pread called with count > SSIZE_MAX");
+extern ssize_t __pread_real(int, void*, size_t, off_t) __RENAME(pread);
+
+extern ssize_t __pread64_chk(int, void*, size_t, off64_t, size_t);
+__errordecl(__pread64_dest_size_error, "pread64 called with size bigger than destination");
+__errordecl(__pread64_count_toobig_error, "pread64 called with count > SSIZE_MAX");
+extern ssize_t __pread64_real(int, void*, size_t, off64_t) __RENAME(pread64);
+
 extern ssize_t __read_chk(int, void*, size_t, size_t);
 __errordecl(__read_dest_size_error, "read called with size bigger than destination");
 __errordecl(__read_count_toobig_error, "read called with count > SSIZE_MAX");
@@ -231,6 +241,62 @@
 
 #if defined(__BIONIC_FORTIFY)
 
+#if defined(__USE_FILE_OFFSET64)
+#define __PREAD_PREFIX(x) __pread64_ ## x
+#else
+#define __PREAD_PREFIX(x) __pread_ ## x
+#endif
+
+__BIONIC_FORTIFY_INLINE
+ssize_t pread(int fd, void* buf, size_t count, off_t offset) {
+    size_t bos = __bos0(buf);
+
+#if !defined(__clang__)
+    if (__builtin_constant_p(count) && (count > SSIZE_MAX)) {
+        __PREAD_PREFIX(count_toobig_error)();
+    }
+
+    if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
+        return __PREAD_PREFIX(real)(fd, buf, count, offset);
+    }
+
+    if (__builtin_constant_p(count) && (count > bos)) {
+        __PREAD_PREFIX(dest_size_error)();
+    }
+
+    if (__builtin_constant_p(count) && (count <= bos)) {
+        return __PREAD_PREFIX(real)(fd, buf, count, offset);
+    }
+#endif
+
+    return __PREAD_PREFIX(chk)(fd, buf, count, offset, bos);
+}
+
+__BIONIC_FORTIFY_INLINE
+ssize_t pread64(int fd, void* buf, size_t count, off64_t offset) {
+    size_t bos = __bos0(buf);
+
+#if !defined(__clang__)
+    if (__builtin_constant_p(count) && (count > SSIZE_MAX)) {
+        __pread64_count_toobig_error();
+    }
+
+    if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
+        return __pread64_real(fd, buf, count, offset);
+    }
+
+    if (__builtin_constant_p(count) && (count > bos)) {
+        __pread64_dest_size_error();
+    }
+
+    if (__builtin_constant_p(count) && (count <= bos)) {
+        return __pread64_real(fd, buf, count, offset);
+    }
+#endif
+
+    return __pread64_chk(fd, buf, count, offset, bos);
+}
+
 __BIONIC_FORTIFY_INLINE
 ssize_t read(int fd, void* buf, size_t count) {
     size_t bos = __bos0(buf);
@@ -255,6 +321,7 @@
 
     return __read_chk(fd, buf, count, bos);
 }
+
 #endif /* defined(__BIONIC_FORTIFY) */
 
 __END_DECLS
diff --git a/libc/private/bionic_tls.h b/libc/private/bionic_tls.h
index 414d171..30dc0eb 100644
--- a/libc/private/bionic_tls.h
+++ b/libc/private/bionic_tls.h
@@ -97,14 +97,8 @@
 #define LIBC_PTHREAD_KEY_RESERVED_COUNT 12
 
 #if defined(USE_JEMALLOC)
-/* Following are current pthread keys used internally by jemalloc:
- * je_thread_allocated_tsd jemalloc
- * je_arenas_tsd           jemalloc
- * je_tcache_tsd           jemalloc
- * je_tcache_enabled_tsd   jemalloc
- * je_quarantine_tsd       jemalloc
- */
-#define JEMALLOC_PTHREAD_KEY_RESERVED_COUNT 5
+/* Internally, jemalloc uses a single key for per thread data. */
+#define JEMALLOC_PTHREAD_KEY_RESERVED_COUNT 1
 #define BIONIC_PTHREAD_KEY_RESERVED_COUNT (LIBC_PTHREAD_KEY_RESERVED_COUNT + JEMALLOC_PTHREAD_KEY_RESERVED_COUNT)
 #else
 #define BIONIC_PTHREAD_KEY_RESERVED_COUNT LIBC_PTHREAD_KEY_RESERVED_COUNT
diff --git a/libc/upstream-openbsd/lib/libc/stdlib/exit.c b/libc/stdlib/exit.c
similarity index 88%
rename from libc/upstream-openbsd/lib/libc/stdlib/exit.c
rename to libc/stdlib/exit.c
index 83fe3d2..10ce674 100644
--- a/libc/upstream-openbsd/lib/libc/stdlib/exit.c
+++ b/libc/stdlib/exit.c
@@ -32,8 +32,6 @@
 #include <sys/mman.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include "atexit.h"
-#include "thread_private.h"
 
 /*
  * This variable is zero until a process has created a thread.
@@ -44,12 +42,21 @@
  */
 int     __isthreaded    = 0;
 
+/* BEGIN android-added: using __cxa_finalize and __cxa_thread_finalize */
+extern void __cxa_finalize(void* dso_handle);
+extern void __cxa_thread_finalize();
+/* END android-added */
+
 /*
  * Exit, flushing stdio buffers if necessary.
  */
 void
 exit(int status)
 {
+  /* BEGIN android-added: call thread_local d-tors */
+  __cxa_thread_finalize();
+  /* END android-added */
+
 	/*
 	 * Call functions registered by atexit() or _cxa_atexit()
 	 * (including the stdio cleanup routine) and then _exit().
diff --git a/tests/__cxa_thread_atexit_test.cpp b/tests/__cxa_thread_atexit_test.cpp
index 83aab53..e388f3b 100644
--- a/tests/__cxa_thread_atexit_test.cpp
+++ b/tests/__cxa_thread_atexit_test.cpp
@@ -50,6 +50,29 @@
   ASSERT_EQ("dtor called.", class_with_dtor_output);
 }
 
+class ClassWithDtorForMainThread {
+ public:
+  void set_message(const std::string& msg) {
+    message = msg;
+  }
+
+  ~ClassWithDtorForMainThread() {
+    fprintf(stderr, "%s", message.c_str());
+  }
+ private:
+  std::string message;
+};
+
+static void thread_atexit_main() {
+  static thread_local ClassWithDtorForMainThread class_with_dtor_for_main_thread;
+  class_with_dtor_for_main_thread.set_message("d-tor for main thread called.");
+  exit(0);
+}
+
+TEST(thread_local, dtor_for_main_thread) {
+  ASSERT_EXIT(thread_atexit_main(), testing::ExitedWithCode(0), "d-tor for main thread called.");
+}
+
 extern "C" int __cxa_thread_atexit_impl(void (*fn)(void*), void* arg, void* dso_handle);
 
 static void thread_atexit_fn1(void* arg) {
diff --git a/tests/fortify_test.cpp b/tests/fortify_test.cpp
index 5cc728f..70159de 100644
--- a/tests/fortify_test.cpp
+++ b/tests/fortify_test.cpp
@@ -623,6 +623,22 @@
   ASSERT_FORTIFY(FD_ISSET(0, set));
 }
 
+TEST_F(DEATHTEST, pread_fortified) {
+  char buf[1];
+  size_t ct = atoi("2"); // prevent optimizations
+  int fd = open("/dev/null", O_RDONLY);
+  ASSERT_FORTIFY(pread(fd, buf, ct, 0));
+  close(fd);
+}
+
+TEST_F(DEATHTEST, pread64_fortified) {
+  char buf[1];
+  size_t ct = atoi("2"); // prevent optimizations
+  int fd = open("/dev/null", O_RDONLY);
+  ASSERT_FORTIFY(pread64(fd, buf, ct, 0));
+  close(fd);
+}
+
 TEST_F(DEATHTEST, read_fortified) {
   char buf[1];
   size_t ct = atoi("2"); // prevent optimizations
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index 691d8ff..a0b0209 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -24,6 +24,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#include <atomic>
 
 #include "ScopedSignalHandler.h"
 
@@ -197,7 +198,7 @@
   ASSERT_EQ(0, timer_delete(timer_id));
 }
 
-static int timer_create_SIGEV_SIGNAL_signal_handler_invocation_count = 0;
+static int timer_create_SIGEV_SIGNAL_signal_handler_invocation_count;
 static void timer_create_SIGEV_SIGNAL_signal_handler(int signal_number) {
   ++timer_create_SIGEV_SIGNAL_signal_handler_invocation_count;
   ASSERT_EQ(SIGUSR1, signal_number);
@@ -212,6 +213,7 @@
   timer_t timer_id;
   ASSERT_EQ(0, timer_create(CLOCK_MONOTONIC, &se, &timer_id));
 
+  timer_create_SIGEV_SIGNAL_signal_handler_invocation_count = 0;
   ScopedSignalHandler ssh(SIGUSR1, timer_create_SIGEV_SIGNAL_signal_handler);
 
   ASSERT_EQ(0, timer_create_SIGEV_SIGNAL_signal_handler_invocation_count);
@@ -228,25 +230,26 @@
 }
 
 struct Counter {
-  volatile int value;
+ private:
+  std::atomic<int> value;
   timer_t timer_id;
   sigevent_t se;
   bool timer_valid;
 
-  Counter(void (*fn)(sigval_t)) : value(0), timer_valid(false) {
-    memset(&se, 0, sizeof(se));
-    se.sigev_notify = SIGEV_THREAD;
-    se.sigev_notify_function = fn;
-    se.sigev_value.sival_ptr = this;
-    Create();
-  }
-
   void Create() {
     ASSERT_FALSE(timer_valid);
     ASSERT_EQ(0, timer_create(CLOCK_REALTIME, &se, &timer_id));
     timer_valid = true;
   }
 
+ public:
+  Counter(void (*fn)(sigval_t)) : value(0), timer_valid(false) {
+    memset(&se, 0, sizeof(se));
+    se.sigev_notify = SIGEV_THREAD;
+    se.sigev_notify_function = fn;
+    se.sigev_value.sival_ptr = this;
+    Create();
+  }
   void DeleteTimer() {
     ASSERT_TRUE(timer_valid);
     ASSERT_EQ(0, timer_delete(timer_id));
@@ -259,12 +262,16 @@
     }
   }
 
+  int Value() const {
+    return value;
+  }
+
   void SetTime(time_t value_s, time_t value_ns, time_t interval_s, time_t interval_ns) {
     ::SetTime(timer_id, value_s, value_ns, interval_s, interval_ns);
   }
 
   bool ValueUpdated() {
-    volatile int current_value = value;
+    int current_value = value;
     time_t start = time(NULL);
     while (current_value == value && (time(NULL) - start) < 5) {
     }
@@ -287,30 +294,29 @@
 
 TEST(time, timer_settime_0) {
   Counter counter(Counter::CountAndDisarmNotifyFunction);
-  ASSERT_TRUE(counter.timer_valid);
-
-  ASSERT_EQ(0, counter.value);
+  ASSERT_EQ(0, counter.Value());
 
   counter.SetTime(0, 1, 1, 0);
   usleep(500000);
 
   // The count should just be 1 because we disarmed the timer the first time it fired.
-  ASSERT_EQ(1, counter.value);
+  ASSERT_EQ(1, counter.Value());
 }
 
 TEST(time, timer_settime_repeats) {
   Counter counter(Counter::CountNotifyFunction);
-  ASSERT_TRUE(counter.timer_valid);
-
-  ASSERT_EQ(0, counter.value);
+  ASSERT_EQ(0, counter.Value());
 
   counter.SetTime(0, 1, 0, 10);
   ASSERT_TRUE(counter.ValueUpdated());
   ASSERT_TRUE(counter.ValueUpdated());
   ASSERT_TRUE(counter.ValueUpdated());
+  counter.DeleteTimer();
+  // Add a sleep as other threads may be calling the callback function when the timer is deleted.
+  usleep(500000);
 }
 
-static int timer_create_NULL_signal_handler_invocation_count = 0;
+static int timer_create_NULL_signal_handler_invocation_count;
 static void timer_create_NULL_signal_handler(int signal_number) {
   ++timer_create_NULL_signal_handler_invocation_count;
   ASSERT_EQ(SIGALRM, signal_number);
@@ -321,6 +327,7 @@
   timer_t timer_id;
   ASSERT_EQ(0, timer_create(CLOCK_MONOTONIC, NULL, &timer_id));
 
+  timer_create_NULL_signal_handler_invocation_count = 0;
   ScopedSignalHandler ssh(SIGALRM, timer_create_NULL_signal_handler);
 
   ASSERT_EQ(0, timer_create_NULL_signal_handler_invocation_count);
@@ -367,22 +374,59 @@
 
 TEST(time, timer_create_multiple) {
   Counter counter1(Counter::CountNotifyFunction);
-  ASSERT_TRUE(counter1.timer_valid);
   Counter counter2(Counter::CountNotifyFunction);
-  ASSERT_TRUE(counter2.timer_valid);
   Counter counter3(Counter::CountNotifyFunction);
-  ASSERT_TRUE(counter3.timer_valid);
 
-  ASSERT_EQ(0, counter1.value);
-  ASSERT_EQ(0, counter2.value);
-  ASSERT_EQ(0, counter3.value);
+  ASSERT_EQ(0, counter1.Value());
+  ASSERT_EQ(0, counter2.Value());
+  ASSERT_EQ(0, counter3.Value());
 
   counter2.SetTime(0, 1, 0, 0);
   usleep(500000);
 
-  EXPECT_EQ(0, counter1.value);
-  EXPECT_EQ(1, counter2.value);
-  EXPECT_EQ(0, counter3.value);
+  EXPECT_EQ(0, counter1.Value());
+  EXPECT_EQ(1, counter2.Value());
+  EXPECT_EQ(0, counter3.Value());
+}
+
+// Test to verify that disarming a repeatable timer disables the callbacks.
+TEST(time, timer_disarm_terminates) {
+  Counter counter(Counter::CountNotifyFunction);
+  ASSERT_EQ(0, counter.Value());
+
+  counter.SetTime(0, 1, 0, 1);
+  ASSERT_TRUE(counter.ValueUpdated());
+  ASSERT_TRUE(counter.ValueUpdated());
+  ASSERT_TRUE(counter.ValueUpdated());
+
+  counter.SetTime(0, 0, 0, 0);
+  // Add a sleep as the kernel may have pending events when the timer is disarmed.
+  usleep(500000);
+  int value = counter.Value();
+  usleep(500000);
+
+  // Verify the counter has not been incremented.
+  ASSERT_EQ(value, counter.Value());
+}
+
+// Test to verify that deleting a repeatable timer disables the callbacks.
+TEST(time, timer_delete_terminates) {
+  Counter counter(Counter::CountNotifyFunction);
+  ASSERT_EQ(0, counter.Value());
+
+  counter.SetTime(0, 1, 0, 1);
+  ASSERT_TRUE(counter.ValueUpdated());
+  ASSERT_TRUE(counter.ValueUpdated());
+  ASSERT_TRUE(counter.ValueUpdated());
+
+  counter.DeleteTimer();
+  // Add a sleep as other threads may be calling the callback function when the timer is deleted.
+  usleep(500000);
+  int value = counter.Value();
+  usleep(500000);
+
+  // Verify the counter has not been incremented.
+  ASSERT_EQ(value, counter.Value());
 }
 
 struct TimerDeleteData {
@@ -499,45 +543,3 @@
   timespec out;
   ASSERT_EQ(EINVAL, clock_nanosleep(-1, 0, &in, &out));
 }
-
-// Test to verify that disarming a repeatable timer disables the
-// callbacks.
-TEST(time, timer_disarm_terminates) {
-  Counter counter(Counter::CountNotifyFunction);
-  ASSERT_TRUE(counter.timer_valid);
-
-  ASSERT_EQ(0, counter.value);
-
-  counter.SetTime(0, 1, 0, 1);
-  ASSERT_TRUE(counter.ValueUpdated());
-  ASSERT_TRUE(counter.ValueUpdated());
-  ASSERT_TRUE(counter.ValueUpdated());
-
-  counter.SetTime(0, 0, 1, 0);
-  volatile int value = counter.value;
-  usleep(500000);
-
-  // Verify the counter has not been incremented.
-  ASSERT_EQ(value, counter.value);
-}
-
-// Test to verify that deleting a repeatable timer disables the
-// callbacks.
-TEST(time, timer_delete_terminates) {
-  Counter counter(Counter::CountNotifyFunction);
-  ASSERT_TRUE(counter.timer_valid);
-
-  ASSERT_EQ(0, counter.value);
-
-  counter.SetTime(0, 1, 0, 1);
-  ASSERT_TRUE(counter.ValueUpdated());
-  ASSERT_TRUE(counter.ValueUpdated());
-  ASSERT_TRUE(counter.ValueUpdated());
-
-  counter.DeleteTimer();
-  volatile int value = counter.value;
-  usleep(500000);
-
-  // Verify the counter has not been incremented.
-  ASSERT_EQ(value, counter.value);
-}
diff --git a/tools/bionicbb/README.md b/tools/bionicbb/README.md
index 91f64d8..4d3291f 100644
--- a/tools/bionicbb/README.md
+++ b/tools/bionicbb/README.md
@@ -12,7 +12,6 @@
  * [Google API Client Library](https://developers.google.com/api-client-library/python/start/installation)
  * [jenkinsapi](https://pypi.python.org/pypi/jenkinsapi)
  * [Requests](http://docs.python-requests.org/en/latest/)
- * [termcolor](https://pypi.python.org/pypi/termcolor)
 
 Setup
 -----
diff --git a/tools/bionicbb/build_listener.py b/tools/bionicbb/build_listener.py
index f7f52ed..fa55d37 100644
--- a/tools/bionicbb/build_listener.py
+++ b/tools/bionicbb/build_listener.py
@@ -15,8 +15,8 @@
 # limitations under the License.
 #
 import json
+import logging
 import requests
-import termcolor
 
 import gerrit
 
@@ -43,7 +43,7 @@
     ref = params['REF']
     patch_set = ref.split('/')[-1]
 
-    print '{} #{} {}: {}'.format(name, number, status, full_url)
+    logging.debug('%s #%s %s: %s', name, number, status, full_url)
 
     # bionic-lint is always broken, so we don't want to reject changes for
     # those failures until we clean things up.
@@ -69,19 +69,19 @@
                                                                     patch_set))
 
         headers = {'Content-Type': 'application/json;charset=UTF-8'}
-        print 'POST {}: {}'.format(url, request_data)
-        print requests.post(url, headers=headers, json=request_data)
+        logging.debug('POST %s: %s', url, request_data)
+        requests.post(url, headers=headers, json=request_data)
     elif name == 'clean-bionic-presubmit':
         request_data = {'message': 'out/ directory removed'}
         url = gerrit_url('/a/changes/{}/revisions/{}/review'.format(change_id,
                                                                     patch_set))
         headers = {'Content-Type': 'application/json;charset=UTF-8'}
-        print 'POST {}: {}'.format(url, request_data)
-        print requests.post(url, headers=headers, json=request_data)
+        logging.debug('POST %s: %s', url, request_data)
+        requests.post(url, headers=headers, json=request_data)
     elif name == 'bionic-lint':
-        print 'IGNORED'
+        logging.warning('Result for bionic-lint ignored')
     else:
-        print '{}: {}'.format(termcolor.colored('red', 'UNKNOWN'), name)
+        logging.error('Unknown project: %s', name)
     return ''
 
 
@@ -100,17 +100,17 @@
         bb_review = 0
 
     if bb_review >= 0:
-        print 'No rejection to drop: {} {}'.format(change_id, patch_set)
+        logging.info('No rejection to drop: %s %s', change_id, patch_set)
         return ''
 
-    print 'Dropping rejection: {} {}'.format(change_id, patch_set)
+    logging.info('Dropping rejection: %s %s', change_id, patch_set)
 
     request_data = {'labels': {'Verified': 0}}
     url = gerrit_url('/a/changes/{}/revisions/{}/review'.format(change_id,
                                                                 patch_set))
     headers = {'Content-Type': 'application/json;charset=UTF-8'}
-    print 'POST {}: {}'.format(url, request_data)
-    print requests.post(url, headers=headers, json=request_data)
+    logging.debug('POST %s: %s', url, request_data)
+    requests.post(url, headers=headers, json=request_data)
     return ''
 
 
diff --git a/tools/bionicbb/gmail_listener.py b/tools/bionicbb/gmail_listener.py
index dd0c008..134258a 100644
--- a/tools/bionicbb/gmail_listener.py
+++ b/tools/bionicbb/gmail_listener.py
@@ -19,10 +19,10 @@
 import httplib2
 import jenkinsapi
 import json
+import logging
 import os
 import re
 import requests
-import termcolor
 import socket
 import sys
 import time
@@ -141,7 +141,7 @@
     return info
 
 
-def clean_project(gerrit_info, dry_run):
+def clean_project(dry_run):
     username = config.jenkins_credentials['username']
     password = config.jenkins_credentials['password']
     jenkins_url = config.jenkins_url
@@ -154,16 +154,9 @@
             url = job.get_build().baseurl
         else:
             url = 'DRY_RUN_URL'
-        print '{}({}): {} {}'.format(
-            termcolor.colored('CLEAN', 'green'),
-            gerrit_info['MessageType'],
-            build,
-            url)
+        logging.info('Cleaning: %s %s', build, url)
     else:
-        print '{}({}): {}'.format(
-            termcolor.colored('CLEAN', 'red'),
-            gerrit_info['MessageType'],
-            termcolor.colored(build, 'red'))
+        logging.error('Failed to clean: could not find project %s', build)
     return True
 
 
@@ -194,21 +187,9 @@
         if not project_path:
             raise RuntimeError('bogus project: {}'.format(project))
         if project_path.startswith('platform/'):
-            print '{}({}): {} => {}'.format(
-                termcolor.colored('ERROR', 'red'),
-                'project',
-                project,
-                project_path)
-            return False
-        try:
-            ref = gerrit.ref_for_change(change_id)
-        except gerrit.GerritError as ex:
-            print '{}({}): {} {}'.format(
-                termcolor.colored('GERRIT-ERROR', 'red'),
-                ex.code,
-                change_id,
-                ex.url)
-            return False
+            raise RuntimeError('Bad project mapping: {} => {}'.format(
+                project, project_path))
+        ref = gerrit.ref_for_change(change_id)
         params = {
             'REF': ref,
             'CHANGE_ID': change_id,
@@ -223,20 +204,10 @@
             url = 'URL UNAVAILABLE'
         else:
             url = 'DRY_RUN_URL'
-        print '{}({}): {} => {} {} {}'.format(
-            termcolor.colored('BUILD', 'green'),
-            gerrit_info['MessageType'],
-            project,
-            build,
-            url,
-            change_id)
+        logging.info('Building: %s => %s %s %s', project, build, url,
+                     change_id)
     else:
-        print '{}({}): {} => {} {}'.format(
-            termcolor.colored('BUILD', 'red'),
-            gerrit_info['MessageType'],
-            project,
-            termcolor.colored(build, 'red'),
-            change_id)
+        logging.error('Unknown build: %s => %s %s', project, build, change_id)
     return True
 
 
@@ -259,13 +230,9 @@
         try:
             requests.post(url, headers=headers, data=json.dumps(request_data))
         except requests.exceptions.ConnectionError as ex:
-            print '{}(drop-rejection): {}'.format(
-                termcolor.colored('ERROR', 'red'), ex)
+            logging.error('Failed to drop rejection: %s', ex)
             return False
-    print '{}({}): {}'.format(
-        termcolor.colored('CHECK', 'green'),
-        gerrit_info['MessageType'],
-        gerrit_info['Change-Id'])
+    logging.info('Dropped rejection: %s', gerrit_info['Change-Id'])
     return True
 
 
@@ -277,7 +244,7 @@
         return True
 
     command_map = {
-        'clean': lambda: clean_project(gerrit_info, dry_run),
+        'clean': lambda: clean_project(dry_run),
         'retry': lambda: build_project(gerrit_info, dry_run),
 
         'arm': lambda: build_project(gerrit_info, dry_run,
@@ -310,11 +277,11 @@
 
 
 def skip_handler(gerrit_info, _, __):
-    print '{}({}): {}'.format(
-        termcolor.colored('SKIP', 'yellow'),
-        gerrit_info['MessageType'],
-        gerrit_info['Change-Id'])
+    logging.info('Skipping %s: %s', gerrit_info['MessageType'],
+                 gerrit_info['Change-Id'])
     return True
+
+
 handle_abandon = skip_handler
 handle_merge_failed = skip_handler
 handle_merged = skip_handler
@@ -327,28 +294,22 @@
         body = get_body(msg)
         gerrit_info = get_gerrit_info(body)
         if not gerrit_info:
-            print termcolor.colored('No info found: {}'.format(msg['id']),
-                                    'red')
+            logging.fatal('No Gerrit info found: %s', msg.subject)
         msg_type = gerrit_info['MessageType']
         handler = 'handle_{}'.format(
             gerrit_info['MessageType'].replace('-', '_'))
         if handler in globals():
             return globals()[handler](gerrit_info, body, dry_run)
         else:
-            print termcolor.colored(
-                'MessageType {} unhandled.'.format(msg_type), 'red')
-        print
+            logging.warning('MessageType %s unhandled.', msg_type)
         return False
     except NotImplementedError as ex:
-        print ex
+        logging.error("%s", ex)
         return False
     except gerrit.GerritError as ex:
-        if ex.code == 404:
-            print '{}(404): {}!'.format(
-                termcolor.colored('ERROR', 'red'), ex)
-            return True
-        else:
-            return False
+        change_id = gerrit_info['Change-Id']
+        logging.error('Gerrit error (%d): %s %s', ex.code, change_id, ex.url)
+        return ex.code == 404
 
 
 def main(argc, argv):
@@ -376,10 +337,10 @@
                     msg_service.trash(userId='me', id=msg['id']).execute()
             time.sleep(60 * 5)
         except GmailError as ex:
-            print '{}: {}!'.format(termcolor.colored('ERROR', 'red'), ex)
+            logging.error('Gmail error: %s', ex)
             time.sleep(60 * 5)
         except apiclient.errors.HttpError as ex:
-            print '{}: {}!'.format(termcolor.colored('ERROR', 'red'), ex)
+            logging.error('API Client HTTP error: %s', ex)
             time.sleep(60 * 5)
         except httplib.BadStatusLine:
             pass