Bluetooth: Add a timeout in async_fd_watcher

Add a timeout to the select call and a callback.
Add unit tests for async_fd_watcher.

Test: unit tests pass
Change-Id: I0076fd52e79aac0d2a9fcceb90aae318d5f0757b
diff --git a/bluetooth/1.0/default/async_fd_watcher.cc b/bluetooth/1.0/default/async_fd_watcher.cc
index 636b4b6..9cd86f1 100644
--- a/bluetooth/1.0/default/async_fd_watcher.cc
+++ b/bluetooth/1.0/default/async_fd_watcher.cc
@@ -50,6 +50,20 @@
   return 0;
 }
 
+int AsyncFdWatcher::ConfigureTimeout(
+    const std::chrono::milliseconds timeout,
+    const TimeoutCallback& on_timeout_callback) {
+  // Add timeout and callback
+  {
+    std::unique_lock<std::mutex> guard(timeout_mutex_);
+    timeout_cb_ = on_timeout_callback;
+    timeout_ms_ = timeout;
+  }
+
+  notifyThread();
+  return 0;
+}
+
 void AsyncFdWatcher::StopWatchingFileDescriptor() { stopThread(); }
 
 AsyncFdWatcher::~AsyncFdWatcher() {}
@@ -86,6 +100,11 @@
     read_fd_ = -1;
   }
 
+  {
+    std::unique_lock<std::mutex> guard(timeout_mutex_);
+    timeout_cb_ = nullptr;
+  }
+
   return 0;
 }
 
@@ -104,21 +123,37 @@
     FD_SET(notification_listen_fd_, &read_fds);
     FD_SET(read_fd_, &read_fds);
 
-    // Wait until there is data available to read on some FD
-    int nfds = std::max(notification_listen_fd_, read_fd_);
-    int retval = select(nfds + 1, &read_fds, NULL, NULL, NULL);
-    if (retval <= 0) continue;  // there was some error or a timeout
+    struct timeval timeout;
+    struct timeval* timeout_ptr = NULL;
+    if (timeout_ms_ > std::chrono::milliseconds(0)) {
+      timeout.tv_sec = timeout_ms_.count() / 1000;
+      timeout.tv_usec = (timeout_ms_.count() % 1000) * 1000;
+      timeout_ptr = &timeout;
+    }
 
-    // Read data from the notification FD
+    // Wait until there is data available to read on some FD.
+    int nfds = std::max(notification_listen_fd_, read_fd_);
+    int retval = select(nfds + 1, &read_fds, NULL, NULL, timeout_ptr);
+
+    // There was some error.
+    if (retval < 0) continue;
+
+    // Timeout.
+    if (retval == 0) {
+      std::unique_lock<std::mutex> guard(timeout_mutex_);
+      if (timeout_ms_ > std::chrono::milliseconds(0) && timeout_cb_)
+        timeout_cb_();
+      continue;
+    }
+
+    // Read data from the notification FD.
     if (FD_ISSET(notification_listen_fd_, &read_fds)) {
       char buffer[] = {0};
       TEMP_FAILURE_RETRY(read(notification_listen_fd_, buffer, 1));
+      continue;
     }
 
-    // Make sure we're still running
-    if (!running_) break;
-
-    // Invoke the data ready callback if appropriate
+    // Invoke the data ready callback if appropriate.
     if (FD_ISSET(read_fd_, &read_fds)) {
       std::unique_lock<std::mutex> guard(internal_mutex_);
       if (cb_) cb_(read_fd_);