Start with async and HCI

Bug: 205758693
Test: unit tests
Change-Id: I16a8be44bce5f2d233582ab6db17c30d068fa9c0
diff --git a/bluetooth/hci/Android.bp b/bluetooth/hci/Android.bp
new file mode 100644
index 0000000..f0f6e8f
--- /dev/null
+++ b/bluetooth/hci/Android.bp
@@ -0,0 +1,46 @@
+package {
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_static {
+    name: "android.hardware.bluetooth.hci",
+    vendor_available: true,
+    host_supported: true,
+    defaults: ["hidl_defaults"],
+    srcs: [
+        "hci_packetizer.cc",
+        "h4_protocol.cc",
+    ],
+    export_include_dirs: ["."],
+    shared_libs: [
+        "libbase",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+    ],
+}
+
+cc_test {
+    name: "bluetooth-vendor-interface-hci-test",
+    host_supported: true,
+    defaults: ["hidl_defaults"],
+    srcs: [
+        "test/h4_protocol_unittest.cc",
+    ],
+    shared_libs: [
+        "libbase",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+    ],
+    static_libs: [
+        "android.hardware.bluetooth.async",
+        "android.hardware.bluetooth.hci",
+        "libgmock",
+    ],
+    sanitize: {
+        address: true,
+        cfi: true,
+    },
+    test_suites: ["general-tests"],
+}
diff --git a/bluetooth/hci/h4_protocol.cc b/bluetooth/hci/h4_protocol.cc
new file mode 100644
index 0000000..97ba7aa
--- /dev/null
+++ b/bluetooth/hci/h4_protocol.cc
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2022 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 "h4_protocol.h"
+
+#define LOG_TAG "android.hardware.bluetooth.hci-h4"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/uio.h>
+
+#include "log/log.h"
+
+namespace android::hardware::bluetooth::hci {
+
+H4Protocol::H4Protocol(int fd, PacketReadCallback cmd_cb,
+                       PacketReadCallback acl_cb, PacketReadCallback sco_cb,
+                       PacketReadCallback event_cb, PacketReadCallback iso_cb,
+                       DisconnectCallback disconnect_cb)
+    : uart_fd_(fd),
+      cmd_cb_(std::move(cmd_cb)),
+      acl_cb_(std::move(acl_cb)),
+      sco_cb_(std::move(sco_cb)),
+      event_cb_(std::move(event_cb)),
+      iso_cb_(std::move(iso_cb)),
+      disconnect_cb_(std::move(disconnect_cb)) {}
+
+size_t H4Protocol::Send(PacketType type, const std::vector<uint8_t>& vector) {
+  return Send(type, vector.data(), vector.size());
+}
+
+size_t H4Protocol::Send(PacketType type, const uint8_t* data, size_t length) {
+  /* For HCI communication over USB dongle, multiple write results in
+   * response timeout as driver expect type + data at once to process
+   * the command, so using "writev"(for atomicity) here.
+   */
+  struct iovec iov[2];
+  ssize_t ret = 0;
+  iov[0].iov_base = &type;
+  iov[0].iov_len = sizeof(type);
+  iov[1].iov_base = (void*)data;
+  iov[1].iov_len = length;
+  while (1) {
+    ret = TEMP_FAILURE_RETRY(writev(uart_fd_, iov, 2));
+    if (ret == -1) {
+      if (errno == EAGAIN) {
+        ALOGE("%s error writing to UART (%s)", __func__, strerror(errno));
+        continue;
+      }
+    } else if (ret == 0) {
+      // Nothing written :(
+      ALOGE("%s zero bytes written - something went wrong...", __func__);
+      break;
+    }
+    break;
+  }
+  return ret;
+}
+
+size_t H4Protocol::OnPacketReady(const std::vector<uint8_t>& packet) {
+  switch (hci_packet_type_) {
+    case PacketType::COMMAND:
+      cmd_cb_(packet);
+      break;
+    case PacketType::ACL_DATA:
+      acl_cb_(packet);
+      break;
+    case PacketType::SCO_DATA:
+      sco_cb_(packet);
+      break;
+    case PacketType::EVENT:
+      event_cb_(packet);
+      break;
+    case PacketType::ISO_DATA:
+      iso_cb_(packet);
+      break;
+    default: {
+      LOG_ALWAYS_FATAL("Bad packet type 0x%x",
+                       static_cast<int>(hci_packet_type_));
+    }
+  }
+  return packet.size();
+}
+
+void H4Protocol::SendDataToPacketizer(uint8_t* buffer, size_t length) {
+  std::vector<uint8_t> input_buffer{buffer, buffer + length};
+  size_t buffer_offset = 0;
+  while (buffer_offset < input_buffer.size()) {
+    if (hci_packet_type_ == PacketType::UNKNOWN) {
+      hci_packet_type_ =
+          static_cast<PacketType>(input_buffer.data()[buffer_offset]);
+      buffer_offset += 1;
+    } else {
+      bool packet_ready = hci_packetizer_.OnDataReady(
+          hci_packet_type_, input_buffer, buffer_offset);
+      if (packet_ready) {
+        // Call packet callback and move offset.
+        buffer_offset += OnPacketReady(hci_packetizer_.GetPacket());
+        // Get ready for the next type byte.
+        hci_packet_type_ = PacketType::UNKNOWN;
+      } else {
+        // The data was consumed, but there wasn't a packet.
+        buffer_offset = input_buffer.size();
+      }
+    }
+  }
+}
+
+void H4Protocol::OnDataReady() {
+  if (disconnected_) {
+    return;
+  }
+  uint8_t buffer[kMaxPacketLength];
+  ssize_t bytes_read =
+      TEMP_FAILURE_RETRY(read(uart_fd_, buffer, kMaxPacketLength));
+  if (bytes_read == 0) {
+    ALOGI("No bytes read, calling the disconnect callback");
+    disconnected_ = true;
+    disconnect_cb_();
+    return;
+  }
+  if (bytes_read < 0) {
+    ALOGW("error reading from UART (%s)", strerror(errno));
+    return;
+  }
+  SendDataToPacketizer(buffer, bytes_read);
+}
+
+}  // namespace android::hardware::bluetooth::hci
diff --git a/bluetooth/hci/h4_protocol.h b/bluetooth/hci/h4_protocol.h
new file mode 100644
index 0000000..5daee83
--- /dev/null
+++ b/bluetooth/hci/h4_protocol.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "hci_internals.h"
+#include "hci_packetizer.h"
+
+namespace android::hardware::bluetooth::hci {
+
+using PacketReadCallback = std::function<void(const std::vector<uint8_t>&)>;
+using DisconnectCallback = std::function<void(void)>;
+
+class H4Protocol {
+ public:
+  H4Protocol(int fd, PacketReadCallback cmd_cb, PacketReadCallback acl_cb,
+             PacketReadCallback sco_cb, PacketReadCallback event_cb,
+             PacketReadCallback iso_cb, DisconnectCallback disconnect_cb);
+
+  size_t Send(PacketType type, const uint8_t* data, size_t length);
+  size_t Send(PacketType type, const std::vector<uint8_t>& data);
+
+  void OnDataReady();
+
+ protected:
+  size_t OnPacketReady(const std::vector<uint8_t>& packet);
+  void SendDataToPacketizer(uint8_t* buffer, size_t length);
+
+ private:
+  int uart_fd_;
+  bool disconnected_{false};
+
+  PacketReadCallback cmd_cb_;
+  PacketReadCallback acl_cb_;
+  PacketReadCallback sco_cb_;
+  PacketReadCallback event_cb_;
+  PacketReadCallback iso_cb_;
+  DisconnectCallback disconnect_cb_;
+
+  PacketType hci_packet_type_{PacketType::UNKNOWN};
+  HciPacketizer hci_packetizer_;
+
+  /**
+   * Question : Why read in single chunk rather than multiple reads?
+   * Answer: Using multiple reads does not work with some BT USB dongles.
+   * Reading in single shot gives expected response.
+   * ACL max length is 2 bytes, so using 64K as the buffer length.
+   */
+  static constexpr size_t kMaxPacketLength = 64 * 1024;
+};
+
+}  // namespace android::hardware::bluetooth::hci
diff --git a/bluetooth/hci/hci_internals.h b/bluetooth/hci/hci_internals.h
new file mode 100644
index 0000000..71b6191
--- /dev/null
+++ b/bluetooth/hci/hci_internals.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#pragma once
+
+#include <cstdlib>
+
+namespace android::hardware::bluetooth::hci {
+
+// HCI UART transport packet types (Volume 4, Part A, 2)
+enum class PacketType : uint8_t {
+  UNKNOWN = 0,
+  COMMAND = 1,
+  ACL_DATA = 2,
+  SCO_DATA = 3,
+  EVENT = 4,
+  ISO_DATA = 5,
+};
+
+// 2 bytes for opcode, 1 byte for parameter length (Volume 4, Part E, 5.4.1)
+static constexpr size_t kCommandHeaderSize = 3;
+static constexpr size_t kCommandLengthOffset = 2;
+
+// 2 bytes for handle, 2 bytes for data length (Volume 4, Part E, 5.4.2)
+static constexpr size_t kAclHeaderSize = 4;
+static constexpr size_t kAclLengthOffset = 2;
+
+// 2 bytes for handle, 1 byte for data length (Volume 4, Part E, 5.4.3)
+static constexpr size_t kScoHeaderSize = 3;
+static constexpr size_t kScoLengthOffset = 2;
+
+// 1 byte for event code, 1 byte for parameter length (Volume 4, Part E, 5.4.4)
+static constexpr size_t kEventHeaderSize = 2;
+static constexpr size_t kEventLengthOffset = 1;
+
+// 2 bytes for handle, 2 bytes for data length (Volume 4, Part E, 5.4.5)
+static constexpr size_t kIsoHeaderSize = 4;
+static constexpr size_t kIsoLengthOffset = 2;
+
+static constexpr size_t kMaxHeaderSize = kAclHeaderSize;
+
+}  // namespace android::hardware::bluetooth::hci
diff --git a/bluetooth/hci/hci_packetizer.cc b/bluetooth/hci/hci_packetizer.cc
new file mode 100644
index 0000000..5b6c443
--- /dev/null
+++ b/bluetooth/hci/hci_packetizer.cc
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2022 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 "hci_packetizer.h"
+
+#define LOG_TAG "android.hardware.bluetooth.hci-packetizer"
+#include "log/log.h"
+
+namespace android::hardware::bluetooth::hci {
+
+namespace {
+
+const size_t header_size_for_type[] = {0,
+                                       kCommandHeaderSize,
+                                       kAclHeaderSize,
+                                       kScoHeaderSize,
+                                       kEventHeaderSize,
+                                       kIsoHeaderSize};
+const size_t packet_length_offset_for_type[] = {0,
+                                                kCommandLengthOffset,
+                                                kAclLengthOffset,
+                                                kScoLengthOffset,
+                                                kEventLengthOffset,
+                                                kIsoLengthOffset};
+
+size_t HciGetPacketLengthForType(PacketType type,
+                                 const std::vector<uint8_t>& header) {
+  size_t offset = packet_length_offset_for_type[static_cast<uint8_t>(type)];
+  if (type != PacketType::ACL_DATA && type != PacketType::ISO_DATA) {
+    return header[offset];
+  }
+  return (((header[offset + 1]) << 8) | header[offset]);
+}
+
+}  // namespace
+
+const std::vector<uint8_t>& HciPacketizer::GetPacket() const { return packet_; }
+
+bool HciPacketizer::OnDataReady(PacketType packet_type,
+                                const std::vector<uint8_t>& buffer,
+                                size_t offset) {
+  bool packet_completed = false;
+  size_t bytes_available = buffer.size() - offset;
+  switch (state_) {
+    case HCI_HEADER: {
+      size_t header_size =
+          header_size_for_type[static_cast<size_t>(packet_type)];
+      if (bytes_remaining_ == 0) {
+        bytes_remaining_ = header_size;
+        packet_.clear();
+      }
+      size_t bytes_to_copy = std::min(bytes_remaining_, bytes_available);
+      packet_.insert(packet_.end(), buffer.begin() + offset,
+                     buffer.begin() + offset + bytes_to_copy);
+      bytes_remaining_ -= bytes_to_copy;
+      bytes_available -= bytes_to_copy;
+      if (bytes_remaining_ == 0) {
+        bytes_remaining_ = HciGetPacketLengthForType(packet_type, packet_);
+        if (bytes_remaining_ > 0) {
+          state_ = HCI_PAYLOAD;
+          if (bytes_available > 0) {
+            packet_completed =
+                OnDataReady(packet_type, buffer, offset + bytes_to_copy);
+          }
+        } else {
+          packet_completed = true;
+        }
+      }
+      break;
+    }
+
+    case HCI_PAYLOAD: {
+      size_t bytes_to_copy = std::min(bytes_remaining_, bytes_available);
+      packet_.insert(packet_.end(), buffer.begin() + offset,
+                     buffer.begin() + offset + bytes_to_copy);
+      bytes_remaining_ -= bytes_to_copy;
+      if (bytes_remaining_ == 0) {
+        state_ = HCI_HEADER;
+        packet_completed = true;
+      }
+      break;
+    }
+  }
+  return packet_completed;
+}
+
+}  // namespace android::hardware::bluetooth::hci
diff --git a/bluetooth/hci/hci_packetizer.h b/bluetooth/hci/hci_packetizer.h
new file mode 100644
index 0000000..ba3e841
--- /dev/null
+++ b/bluetooth/hci/hci_packetizer.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include "hci_internals.h"
+
+namespace android::hardware::bluetooth::hci {
+
+class HciPacketizer {
+ public:
+  HciPacketizer() = default;
+  bool OnDataReady(PacketType packet_type, const std::vector<uint8_t>& data,
+                   size_t offset);
+  const std::vector<uint8_t>& GetPacket() const;
+
+ protected:
+  enum State { HCI_HEADER, HCI_PAYLOAD };
+  State state_{HCI_HEADER};
+  std::vector<uint8_t> packet_;
+  size_t bytes_remaining_{0};
+};
+
+}  // namespace android::hardware::bluetooth::hci
diff --git a/bluetooth/hci/test/h4_protocol_unittest.cc b/bluetooth/hci/test/h4_protocol_unittest.cc
new file mode 100644
index 0000000..d6f74fc
--- /dev/null
+++ b/bluetooth/hci/test/h4_protocol_unittest.cc
@@ -0,0 +1,441 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#define LOG_TAG "bt_h4_unittest"
+
+#include "h4_protocol.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cstdint>
+#include <cstring>
+#include <future>
+#include <vector>
+
+#include "async_fd_watcher.h"
+#include "log/log.h"
+
+using android::hardware::bluetooth::async::AsyncFdWatcher;
+using namespace android::hardware::bluetooth::hci;
+using ::testing::Eq;
+
+static char sample_data1[100] = "A point is that which has no part.";
+static char sample_data2[100] = "A line is breadthless length.";
+static char sample_data3[100] = "The ends of a line are points.";
+static char sample_data4[100] =
+    "A plane surface is a surface which lies evenly with the straight ...";
+static char acl_data[100] =
+    "A straight line is a line which lies evenly with the points on itself.";
+static char sco_data[100] =
+    "A surface is that which has length and breadth only.";
+static char event_data[100] = "The edges of a surface are lines.";
+static char iso_data[100] =
+    "A plane angle is the inclination to one another of two lines in a ...";
+
+MATCHER_P3(PacketMatches, header_, header_length, payload,
+           "Match header_length bytes of header and then the payload") {
+  size_t payload_length = strlen(payload);
+  if (header_length + payload_length != arg.size()) {
+    return false;
+  }
+
+  if (memcmp(header_, arg.data(), header_length) != 0) {
+    return false;
+  }
+
+  return memcmp(payload, arg.data() + header_length, payload_length) == 0;
+};
+
+ACTION_P(Notify, barrier) {
+  ALOGD("%s", __func__);
+  barrier->set_value();
+}
+
+class H4ProtocolTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ALOGD("%s", __func__);
+
+    int sockfd[2];
+    socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
+    chip_uart_fd_ = sockfd[1];
+    stack_uart_fd_ = sockfd[0];
+    h4_hci_ = std::make_shared<H4Protocol>(
+        stack_uart_fd_, cmd_cb_.AsStdFunction(), acl_cb_.AsStdFunction(),
+        sco_cb_.AsStdFunction(), event_cb_.AsStdFunction(),
+        iso_cb_.AsStdFunction(), disconnect_cb_.AsStdFunction());
+  }
+
+  void TearDown() override {
+    close(stack_uart_fd_);
+    close(chip_uart_fd_);
+  }
+
+  virtual void CallDataReady() { h4_hci_->OnDataReady(); }
+
+  void SendAndReadUartOutbound(PacketType type, char* data) {
+    ALOGD("%s sending", __func__);
+    int data_length = strlen(data);
+    h4_hci_->Send(type, (uint8_t*)data, data_length);
+
+    int uart_length = data_length + 1;  // + 1 for data type code
+    int i;
+
+    ALOGD("%s reading", __func__);
+    for (i = 0; i < uart_length; i++) {
+      fd_set read_fds;
+      FD_ZERO(&read_fds);
+      FD_SET(chip_uart_fd_, &read_fds);
+      TEMP_FAILURE_RETRY(
+          select(chip_uart_fd_ + 1, &read_fds, nullptr, nullptr, nullptr));
+
+      char byte;
+      TEMP_FAILURE_RETRY(read(chip_uart_fd_, &byte, 1));
+
+      EXPECT_EQ(i == 0 ? static_cast<uint8_t>(type) : data[i - 1], byte);
+    }
+
+    EXPECT_EQ(i, uart_length);
+  }
+
+  void ExpectInboundAclData(char* payload, std::promise<void>* promise) {
+    // h4 type[1] + handle[2] + size[2]
+    header_[0] = static_cast<uint8_t>(PacketType::ACL_DATA);
+    header_[1] = 19;
+    header_[2] = 92;
+    int length = strlen(payload);
+    header_[3] = length & 0xFF;
+    header_[4] = (length >> 8) & 0xFF;
+    ALOGD("(%d bytes) %s", length, payload);
+
+    EXPECT_CALL(acl_cb_,
+                Call(PacketMatches(header_ + 1, kAclHeaderSize, payload)))
+        .WillOnce(Notify(promise));
+  }
+
+  void WaitForTimeout(size_t timeout_ms, std::promise<void>* promise) {
+    auto future = promise->get_future();
+    auto status = future.wait_for(std::chrono::milliseconds(timeout_ms));
+    EXPECT_EQ(status, std::future_status::ready);
+  }
+
+  void WriteInboundAclData(char* payload) {
+    // Use the header_ computed in ExpectInboundAclData
+    TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, kAclHeaderSize + 1));
+    TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
+  }
+
+  void ExpectInboundScoData(char* payload, std::promise<void>* promise) {
+    // h4 type[1] + handle[2] + size[1]
+    header_[0] = static_cast<uint8_t>(PacketType::SCO_DATA);
+    header_[1] = 20;
+    header_[2] = 17;
+    header_[3] = strlen(payload) & 0xFF;
+    EXPECT_CALL(sco_cb_,
+                Call(PacketMatches(header_ + 1, kScoHeaderSize, payload)))
+        .WillOnce(Notify(promise));
+  }
+
+  void WriteInboundScoData(char* payload) {
+    // Use the header_ computed in ExpectInboundScoData
+    ALOGD("%s writing", __func__);
+    TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, kScoHeaderSize + 1));
+    TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
+  }
+
+  void ExpectInboundEvent(char* payload, std::promise<void>* promise) {
+    // h4 type[1] + event_code[1] + size[1]
+    header_[0] = static_cast<uint8_t>(PacketType::EVENT);
+    header_[1] = 9;
+    header_[2] = strlen(payload) & 0xFF;
+    EXPECT_CALL(event_cb_,
+                Call(PacketMatches(header_ + 1, kEventHeaderSize, payload)))
+        .WillOnce(Notify(promise));
+  }
+
+  void WriteInboundEvent(char* payload) {
+    // Use the header_ computed in ExpectInboundEvent
+    char preamble[3] = {static_cast<uint8_t>(PacketType::EVENT), 9, 0};
+    preamble[2] = strlen(payload) & 0xFF;
+    ALOGD("%s writing", __func__);
+    TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, kEventHeaderSize + 1));
+    TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
+  }
+
+  void ExpectInboundIsoData(char* payload, std::promise<void>* promise) {
+    // h4 type[1] + handle[2] + size[1]
+    header_[0] = static_cast<uint8_t>(PacketType::ISO_DATA);
+    header_[1] = 19;
+    header_[2] = 92;
+    int length = strlen(payload);
+    header_[3] = length & 0xFF;
+    header_[4] = (length >> 8) & 0x3F;
+
+    EXPECT_CALL(iso_cb_,
+                Call(PacketMatches(header_ + 1, kIsoHeaderSize, payload)))
+        .WillOnce(Notify(promise));
+  }
+
+  void WriteInboundIsoData(char* payload) {
+    // Use the header_ computed in ExpectInboundIsoData
+    ALOGD("%s writing", __func__);
+    TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, kIsoHeaderSize + 1));
+    TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
+  }
+
+  void WriteAndExpectManyInboundAclDataPackets(char* payload) {
+    size_t kNumPackets = 20;
+    // h4 type[1] + handle[2] + size[2]
+    char preamble[5] = {static_cast<uint8_t>(PacketType::ACL_DATA), 19, 92, 0,
+                        0};
+    int length = strlen(payload);
+    preamble[3] = length & 0xFF;
+    preamble[4] = (length >> 8) & 0xFF;
+
+    EXPECT_CALL(acl_cb_, Call(PacketMatches(preamble + 1, sizeof(preamble) - 1,
+                                            payload)))
+        .Times(kNumPackets);
+
+    for (size_t i = 0; i < kNumPackets; i++) {
+      TEMP_FAILURE_RETRY(write(chip_uart_fd_, preamble, sizeof(preamble)));
+      TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
+    }
+
+    CallDataReady();
+  }
+
+  testing::MockFunction<void(const std::vector<uint8_t>&)> cmd_cb_;
+  testing::MockFunction<void(const std::vector<uint8_t>&)> event_cb_;
+  testing::MockFunction<void(const std::vector<uint8_t>&)> acl_cb_;
+  testing::MockFunction<void(const std::vector<uint8_t>&)> sco_cb_;
+  testing::MockFunction<void(const std::vector<uint8_t>&)> iso_cb_;
+  testing::MockFunction<void(void)> disconnect_cb_;
+  std::shared_ptr<H4Protocol> h4_hci_;
+  int chip_uart_fd_;
+  int stack_uart_fd_;
+
+  char header_[5];
+};
+
+// Test sending data sends correct data onto the UART
+TEST_F(H4ProtocolTest, TestSends) {
+  SendAndReadUartOutbound(PacketType::COMMAND, sample_data1);
+  SendAndReadUartOutbound(PacketType::ACL_DATA, sample_data2);
+  SendAndReadUartOutbound(PacketType::SCO_DATA, sample_data3);
+  SendAndReadUartOutbound(PacketType::ISO_DATA, sample_data4);
+}
+
+// Ensure we properly parse data coming from the UART
+TEST_F(H4ProtocolTest, TestReads) {
+  std::promise<void> acl_promise;
+  std::promise<void> sco_promise;
+  std::promise<void> event_promise;
+  std::promise<void> iso_promise;
+
+  ExpectInboundAclData(acl_data, &acl_promise);
+  WriteInboundAclData(acl_data);
+  CallDataReady();
+  ExpectInboundScoData(sco_data, &sco_promise);
+  WriteInboundScoData(sco_data);
+  CallDataReady();
+  ExpectInboundEvent(event_data, &event_promise);
+  WriteInboundEvent(event_data);
+  CallDataReady();
+  ExpectInboundIsoData(iso_data, &iso_promise);
+  WriteInboundIsoData(iso_data);
+  CallDataReady();
+
+  WaitForTimeout(100, &acl_promise);
+  WaitForTimeout(100, &sco_promise);
+  WaitForTimeout(100, &event_promise);
+  WaitForTimeout(100, &iso_promise);
+}
+
+TEST_F(H4ProtocolTest, TestMultiplePackets) {
+  WriteAndExpectManyInboundAclDataPackets(sco_data);
+}
+
+TEST_F(H4ProtocolTest, TestDisconnect) {
+  EXPECT_CALL(disconnect_cb_, Call());
+  close(chip_uart_fd_);
+  CallDataReady();
+}
+
+TEST_F(H4ProtocolTest, TestPartialWrites) {
+  size_t payload_len = strlen(acl_data);
+  const size_t kNumIntervals = payload_len + 1;
+  // h4 type[1] + handle[2] + size[2]
+  header_[0] = static_cast<uint8_t>(PacketType::ACL_DATA);
+  header_[1] = 19;
+  header_[2] = 92;
+  header_[3] = payload_len & 0xFF;
+  header_[4] = (payload_len >> 8) & 0xFF;
+
+  EXPECT_CALL(acl_cb_,
+              Call(PacketMatches(header_ + 1, sizeof(header_) - 1, acl_data)))
+      .Times(kNumIntervals);
+
+  for (size_t interval = 1; interval < kNumIntervals + 1; interval++) {
+    // Use the header_ data that expect already set up.
+    if (interval < kAclHeaderSize) {
+      TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, interval));
+      CallDataReady();
+      TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_ + interval,
+                               kAclHeaderSize + 1 - interval));
+      CallDataReady();
+    } else {
+      TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, kAclHeaderSize + 1));
+      CallDataReady();
+    }
+
+    for (size_t bytes = 0; bytes + interval <= payload_len; bytes += interval) {
+      TEMP_FAILURE_RETRY(write(chip_uart_fd_, acl_data + bytes, interval));
+      CallDataReady();
+    }
+    size_t extra_bytes = payload_len % interval;
+    if (extra_bytes) {
+      TEMP_FAILURE_RETRY(write(
+          chip_uart_fd_, acl_data + payload_len - extra_bytes, extra_bytes));
+      CallDataReady();
+    }
+  }
+}
+
+class H4ProtocolAsyncTest : public H4ProtocolTest {
+ protected:
+  void SetUp() override {
+    H4ProtocolTest::SetUp();
+    fd_watcher_.WatchFdForNonBlockingReads(
+        stack_uart_fd_, [this](int) { h4_hci_->OnDataReady(); });
+  }
+
+  void TearDown() override { fd_watcher_.StopWatchingFileDescriptors(); }
+
+  void CallDataReady() override {
+    // The Async test can't call data ready.
+    FAIL();
+  }
+
+  void SendAndReadUartOutbound(PacketType type, char* data) {
+    ALOGD("%s sending", __func__);
+    int data_length = strlen(data);
+    h4_hci_->Send(type, (uint8_t*)data, data_length);
+
+    int uart_length = data_length + 1;  // + 1 for data type code
+    int i;
+
+    ALOGD("%s reading", __func__);
+    for (i = 0; i < uart_length; i++) {
+      fd_set read_fds;
+      FD_ZERO(&read_fds);
+      FD_SET(chip_uart_fd_, &read_fds);
+      TEMP_FAILURE_RETRY(
+          select(chip_uart_fd_ + 1, &read_fds, nullptr, nullptr, nullptr));
+
+      char byte;
+      TEMP_FAILURE_RETRY(read(chip_uart_fd_, &byte, 1));
+
+      EXPECT_EQ(i == 0 ? static_cast<uint8_t>(type) : data[i - 1], byte);
+    }
+
+    EXPECT_EQ(i, uart_length);
+  }
+
+  void WriteAndExpectInboundAclData(char* payload) {
+    std::promise<void> promise;
+    ExpectInboundAclData(payload, &promise);
+    WriteInboundAclData(payload);
+    WaitForTimeout(100, &promise);
+  }
+
+  void WriteAndExpectInboundScoData(char* payload) {
+    std::promise<void> promise;
+    ExpectInboundScoData(payload, &promise);
+    WriteInboundScoData(payload);
+    WaitForTimeout(100, &promise);
+  }
+
+  void WriteAndExpectInboundEvent(char* payload) {
+    std::promise<void> promise;
+    ExpectInboundEvent(payload, &promise);
+    WriteInboundEvent(payload);
+    WaitForTimeout(100, &promise);
+  }
+
+  void WriteAndExpectInboundIsoData(char* payload) {
+    std::promise<void> promise;
+    ExpectInboundIsoData(payload, &promise);
+    WriteInboundIsoData(payload);
+    WaitForTimeout(100, &promise);
+  }
+
+  void WriteAndExpectManyInboundAclDataPackets(char* payload) {
+    const size_t kNumPackets = 20;
+    // h4 type[1] + handle[2] + size[2]
+    char preamble[5] = {static_cast<uint8_t>(PacketType::ACL_DATA), 19, 92, 0,
+                        0};
+    int length = strlen(payload);
+    preamble[3] = length & 0xFF;
+    preamble[4] = (length >> 8) & 0xFF;
+
+    EXPECT_CALL(acl_cb_, Call(PacketMatches(preamble + 1, sizeof(preamble) - 1,
+                                            payload)))
+        .Times(kNumPackets);
+
+    for (size_t i = 0; i < kNumPackets; i++) {
+      TEMP_FAILURE_RETRY(write(chip_uart_fd_, preamble, sizeof(preamble)));
+      TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
+    }
+
+    WriteAndExpectInboundEvent(event_data);
+  }
+
+  AsyncFdWatcher fd_watcher_;
+};
+
+// Test sending data sends correct data onto the UART
+TEST_F(H4ProtocolAsyncTest, TestSends) {
+  SendAndReadUartOutbound(PacketType::COMMAND, sample_data1);
+  SendAndReadUartOutbound(PacketType::ACL_DATA, sample_data2);
+  SendAndReadUartOutbound(PacketType::SCO_DATA, sample_data3);
+  SendAndReadUartOutbound(PacketType::ISO_DATA, sample_data4);
+}
+
+// Ensure we properly parse data coming from the UART
+TEST_F(H4ProtocolAsyncTest, TestReads) {
+  WriteAndExpectInboundAclData(acl_data);
+  WriteAndExpectInboundScoData(sco_data);
+  WriteAndExpectInboundEvent(event_data);
+  WriteAndExpectInboundIsoData(iso_data);
+}
+
+TEST_F(H4ProtocolAsyncTest, TestMultiplePackets) {
+  WriteAndExpectManyInboundAclDataPackets(sco_data);
+}
+
+TEST_F(H4ProtocolAsyncTest, TestDisconnect) {
+  std::promise<void> promise;
+  EXPECT_CALL(disconnect_cb_, Call()).WillOnce(Notify(&promise));
+  close(chip_uart_fd_);
+
+  // Fail if it takes longer than 100 ms.
+  WaitForTimeout(100, &promise);
+}