hikey: Make the Hikey Bluetooth HAL independent

Test: Bluetooth starts/stops
Change-Id: I8a8b113f3edcdb092c36fd7822aeabf900c0f0b3
diff --git a/bluetooth/Android.bp b/bluetooth/Android.bp
index aecdcb7..142ced2 100644
--- a/bluetooth/Android.bp
+++ b/bluetooth/Android.bp
@@ -18,8 +18,11 @@
     proprietary: true,
     relative_install_path: "hw",
     srcs: [
+        "async_fd_watcher.cc",
         "bluetooth_hci.cc",
-        "hci_packetizer_hikey.cc",
+        "h4_protocol.cc",
+        "hci_packetizer.cc",
+        "hci_protocol.cc",
         "service.cc",
     ],
     shared_libs: [
@@ -33,9 +36,5 @@
         "liblog",
         "libutils",
     ],
-    static_libs: [
-        "android.hardware.bluetooth-async",
-        "android.hardware.bluetooth-hci",
-    ],
     init_rc: ["android.hardware.bluetooth@1.0-service.hikey.rc"],
 }
diff --git a/bluetooth/async_fd_watcher.cc b/bluetooth/async_fd_watcher.cc
new file mode 100644
index 0000000..c4470d0
--- /dev/null
+++ b/bluetooth/async_fd_watcher.cc
@@ -0,0 +1,181 @@
+//
+// Copyright 2016 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 "async_fd_watcher.h"
+
+#include <algorithm>
+#include <atomic>
+#include <condition_variable>
+#include <map>
+#include <mutex>
+#include <thread>
+#include <vector>
+#include "fcntl.h"
+#include "sys/select.h"
+#include "unistd.h"
+
+static const int INVALID_FD = -1;
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace async {
+
+int AsyncFdWatcher::WatchFdForNonBlockingReads(
+    int file_descriptor, const ReadCallback& on_read_fd_ready_callback) {
+  // Add file descriptor and callback
+  {
+    std::unique_lock<std::mutex> guard(internal_mutex_);
+    watched_fds_[file_descriptor] = on_read_fd_ready_callback;
+  }
+
+  // Start the thread if not started yet
+  return tryStartThread();
+}
+
+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::StopWatchingFileDescriptors() { stopThread(); }
+
+AsyncFdWatcher::~AsyncFdWatcher() {}
+
+// Make sure to call this with at least one file descriptor ready to be
+// watched upon or the thread routine will return immediately
+int AsyncFdWatcher::tryStartThread() {
+  if (std::atomic_exchange(&running_, true)) return 0;
+
+  // Set up the communication channel
+  int pipe_fds[2];
+  if (pipe2(pipe_fds, O_NONBLOCK)) return -1;
+
+  notification_listen_fd_ = pipe_fds[0];
+  notification_write_fd_ = pipe_fds[1];
+
+  thread_ = std::thread([this]() { ThreadRoutine(); });
+  if (!thread_.joinable()) return -1;
+
+  return 0;
+}
+
+int AsyncFdWatcher::stopThread() {
+  if (!std::atomic_exchange(&running_, false)) return 0;
+
+  notifyThread();
+  if (std::this_thread::get_id() != thread_.get_id()) {
+    thread_.join();
+  }
+
+  {
+    std::unique_lock<std::mutex> guard(internal_mutex_);
+    watched_fds_.clear();
+  }
+
+  {
+    std::unique_lock<std::mutex> guard(timeout_mutex_);
+    timeout_cb_ = nullptr;
+  }
+
+  return 0;
+}
+
+int AsyncFdWatcher::notifyThread() {
+  uint8_t buffer[] = {0};
+  if (TEMP_FAILURE_RETRY(write(notification_write_fd_, &buffer, 1)) < 0) {
+    return -1;
+  }
+  return 0;
+}
+
+void AsyncFdWatcher::ThreadRoutine() {
+  while (running_) {
+    fd_set read_fds;
+    FD_ZERO(&read_fds);
+    FD_SET(notification_listen_fd_, &read_fds);
+    int max_read_fd = INVALID_FD;
+    for (auto& it : watched_fds_) {
+      FD_SET(it.first, &read_fds);
+      max_read_fd = std::max(max_read_fd, it.first);
+    }
+
+    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;
+    }
+
+    // Wait until there is data available to read on some FD.
+    int nfds = std::max(notification_listen_fd_, max_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) {
+      // Allow the timeout callback to modify the timeout.
+      TimeoutCallback saved_cb;
+      {
+        std::unique_lock<std::mutex> guard(timeout_mutex_);
+        if (timeout_ms_ > std::chrono::milliseconds(0)) saved_cb = timeout_cb_;
+      }
+      if (saved_cb != nullptr) saved_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;
+    }
+
+    // Invoke the data ready callbacks if appropriate.
+    std::vector<decltype(watched_fds_)::value_type> saved_callbacks;
+    {
+      std::unique_lock<std::mutex> guard(internal_mutex_);
+      for (auto& it : watched_fds_) {
+        if (FD_ISSET(it.first, &read_fds)) {
+          saved_callbacks.push_back(it);
+        }
+      }
+    }
+
+    for (auto& it : saved_callbacks) {
+      if (it.second) {
+        it.second(it.first);
+      }
+    }
+  }
+}
+
+}  // namespace async
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/async_fd_watcher.h b/bluetooth/async_fd_watcher.h
new file mode 100644
index 0000000..b51f921
--- /dev/null
+++ b/bluetooth/async_fd_watcher.h
@@ -0,0 +1,66 @@
+//
+// Copyright 2016 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 <map>
+#include <mutex>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace async {
+
+using ReadCallback = std::function<void(int)>;
+using TimeoutCallback = std::function<void(void)>;
+
+class AsyncFdWatcher {
+ public:
+  AsyncFdWatcher() = default;
+  ~AsyncFdWatcher();
+
+  int WatchFdForNonBlockingReads(int file_descriptor,
+                                 const ReadCallback& on_read_fd_ready_callback);
+  int ConfigureTimeout(const std::chrono::milliseconds timeout,
+                       const TimeoutCallback& on_timeout_callback);
+  void StopWatchingFileDescriptors();
+
+ private:
+  AsyncFdWatcher(const AsyncFdWatcher&) = delete;
+  AsyncFdWatcher& operator=(const AsyncFdWatcher&) = delete;
+
+  int tryStartThread();
+  int stopThread();
+  int notifyThread();
+  void ThreadRoutine();
+
+  std::atomic_bool running_{false};
+  std::thread thread_;
+  std::mutex internal_mutex_;
+  std::mutex timeout_mutex_;
+
+  std::map<int, ReadCallback> watched_fds_;
+  int notification_listen_fd_;
+  int notification_write_fd_;
+  TimeoutCallback timeout_cb_;
+  std::chrono::milliseconds timeout_ms_;
+};
+
+}  // namespace async
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/bluetooth_hci.cc b/bluetooth/bluetooth_hci.cc
index 6410765..fa14b53 100644
--- a/bluetooth/bluetooth_hci.cc
+++ b/bluetooth/bluetooth_hci.cc
@@ -18,44 +18,11 @@
 
 #include "bluetooth_hci.h"
 
-#include <utils/Log.h>
-
 #include <android-base/logging.h>
-
-#include "hci_internals.h"
-
-namespace {
-
-using android::hardware::bluetooth::V1_0::hikey::BluetoothHci;
-using android::hardware::hidl_vec;
-
-BluetoothHci* g_bluetooth_hci = nullptr;
-
-size_t write_safely(int fd, const uint8_t* data, size_t length) {
-  size_t transmitted_length = 0;
-  while (length > 0) {
-    ssize_t ret =
-        TEMP_FAILURE_RETRY(write(fd, data + transmitted_length, length));
-
-    if (ret == -1) {
-      if (errno == EAGAIN) continue;
-      ALOGE("%s error writing to UART (%s)", __func__, strerror(errno));
-      break;
-
-    } else if (ret == 0) {
-      // Nothing written :(
-      ALOGE("%s zero bytes written - something went wrong...", __func__);
-      break;
-    }
-
-    transmitted_length += ret;
-    length -= ret;
-  }
-
-  return transmitted_length;
-}
-
-}  // namespace
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <utils/Log.h>
 
 namespace android {
 namespace hardware {
@@ -63,88 +30,87 @@
 namespace V1_0 {
 namespace hikey {
 
+using android::hardware::hidl_vec;
+
 Return<void> BluetoothHci::initialize(
     const ::android::sp<IBluetoothHciCallbacks>& cb) {
   ALOGI("BluetoothHci::initialize()");
 
-  CHECK(cb != nullptr);
-  event_cb_ = cb;
-
   hci_tty_fd_ = open("/dev/hci_tty", O_RDWR);
   if (hci_tty_fd_ < 0) {
-    ALOGE("%s: Can't open hci_tty", __func__);
-    event_cb_->initializationComplete(Status::INITIALIZATION_ERROR);
+    ALOGE("%s: Can't open hci_tty (%s)", __func__, strerror(errno));
+    cb->initializationComplete(Status::INITIALIZATION_ERROR);
+    return Void();
   }
 
-  CHECK(g_bluetooth_hci == nullptr) << __func__ << " is not reentrant";
-  g_bluetooth_hci = this;
+  event_cb_ = cb;
+
+  hci_ = new hci::H4Protocol(
+      hci_tty_fd_,
+      [cb](const hidl_vec<uint8_t>& packet) { cb->hciEventReceived(packet); },
+      [cb](const hidl_vec<uint8_t>& packet) { cb->aclDataReceived(packet); },
+      [cb](const hidl_vec<uint8_t>& packet) { cb->scoDataReceived(packet); });
+
+  // Use a socket pair to enforce the TI FIONREAD requirement.
+  int sockfd[2];
+  socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
+  int shim_fd = sockfd[0];
+  int for_hci = sockfd[1];
+
+  fd_watcher_.WatchFdForNonBlockingReads(hci_tty_fd_, [this, shim_fd](int fd) {
+    int tty_bytes = 0;
+    if (TEMP_FAILURE_RETRY(ioctl(fd, FIONREAD, &tty_bytes)))
+      ALOGE("%s:FIONREAD %s", __func__, strerror(errno));
+    ALOGV("%s:tty_bytes = %d", __func__, tty_bytes);
+
+    uint8_t* tmp_buffer = new uint8_t[tty_bytes];
+    size_t bytes_read = TEMP_FAILURE_RETRY(read(fd, tmp_buffer, tty_bytes));
+    CHECK(static_cast<int>(bytes_read) == tty_bytes);
+    size_t bytes_written =
+        TEMP_FAILURE_RETRY(write(shim_fd, tmp_buffer, tty_bytes));
+    CHECK(static_cast<int>(bytes_written) == tty_bytes);
+    delete[] tmp_buffer;
+  });
 
   fd_watcher_.WatchFdForNonBlockingReads(
-      hci_tty_fd_, [this](int fd) { hci_packetizer_.OnDataReadyHikey(fd); });
+      for_hci, [this](int fd) { hci_->OnDataReady(fd); });
 
-  event_cb_->initializationComplete(Status::SUCCESS);
+  cb->initializationComplete(Status::SUCCESS);
   return Void();
 }
 
 Return<void> BluetoothHci::close() {
-  ALOGW("BluetoothHci::close()");
-  ::close(hci_tty_fd_);
-  hci_tty_fd_ = -1;
-  g_bluetooth_hci = nullptr;
+  ALOGI("BluetoothHci::close()");
+
+  if (hci_tty_fd_ >= 0) {
+    fd_watcher_.StopWatchingFileDescriptors();
+    ::close(hci_tty_fd_);
+    hci_tty_fd_ = -1;
+  }
+
+  if (hci_ != nullptr) {
+    delete hci_;
+    hci_ = nullptr;
+  }
+
   return Void();
 }
 
 Return<void> BluetoothHci::sendHciCommand(const hidl_vec<uint8_t>& packet) {
-  uint8_t type = HCI_PACKET_TYPE_COMMAND;
-  int rv = write_safely(hci_tty_fd_, &type, sizeof(type));
-  if (rv == sizeof(type))
-    rv = write_safely(hci_tty_fd_, packet.data(), packet.size());
+  hci_->Send(HCI_PACKET_TYPE_COMMAND, packet.data(), packet.size());
   return Void();
 }
 
 Return<void> BluetoothHci::sendAclData(const hidl_vec<uint8_t>& packet) {
-  uint8_t type = HCI_PACKET_TYPE_ACL_DATA;
-  int rv = write_safely(hci_tty_fd_, &type, sizeof(type));
-  if (rv == sizeof(type))
-    rv = write_safely(hci_tty_fd_, packet.data(), packet.size());
+  hci_->Send(HCI_PACKET_TYPE_ACL_DATA, packet.data(), packet.size());
   return Void();
 }
 
 Return<void> BluetoothHci::sendScoData(const hidl_vec<uint8_t>& packet) {
-  uint8_t type = HCI_PACKET_TYPE_SCO_DATA;
-  int rv = write_safely(hci_tty_fd_, &type, sizeof(type));
-  if (rv == sizeof(type))
-    rv = write_safely(hci_tty_fd_, packet.data(), packet.size());
+  hci_->Send(HCI_PACKET_TYPE_SCO_DATA, packet.data(), packet.size());
   return Void();
 }
 
-BluetoothHci* BluetoothHci::get() { return g_bluetooth_hci; }
-
-void BluetoothHci::OnPacketReady() {
-  BluetoothHci::get()->HandleIncomingPacket();
-}
-
-void BluetoothHci::HandleIncomingPacket() {
-  HciPacketType hci_packet_type = hci_packetizer_.GetPacketType();
-  hidl_vec<uint8_t> hci_packet = hci_packetizer_.GetPacket();
-
-  switch (hci_packet_type) {
-    case HCI_PACKET_TYPE_EVENT:
-      event_cb_->hciEventReceived(hci_packet);
-      break;
-    case HCI_PACKET_TYPE_ACL_DATA:
-      event_cb_->aclDataReceived(hci_packet);
-      break;
-    case HCI_PACKET_TYPE_SCO_DATA:
-      event_cb_->scoDataReceived(hci_packet);
-      break;
-    default: {
-      bool hci_packet_type_corrupted = true;
-      CHECK(hci_packet_type_corrupted == false);
-    }
-  }
-}
-
 }  // namespace hikey
 }  // namespace V1_0
 }  // namespace bluetooth
diff --git a/bluetooth/bluetooth_hci.h b/bluetooth/bluetooth_hci.h
index 91fad27..484a6ab 100644
--- a/bluetooth/bluetooth_hci.h
+++ b/bluetooth/bluetooth_hci.h
@@ -21,7 +21,7 @@
 #include <hidl/MQDescriptor.h>
 
 #include "async_fd_watcher.h"
-#include "hci_packetizer_hikey.h"
+#include "h4_protocol.h"
 
 namespace android {
 namespace hardware {
@@ -43,17 +43,13 @@
 
   static void OnPacketReady();
 
-  static BluetoothHci* get();
-
  private:
   ::android::sp<IBluetoothHciCallbacks> event_cb_;
   int hci_tty_fd_;
 
-  void HandleIncomingPacket();
-
   async::AsyncFdWatcher fd_watcher_;
 
-  HciPacketizerHikey hci_packetizer_{BluetoothHci::OnPacketReady};
+  hci::H4Protocol* hci_;
 };
 
 }  // namespace hikey
diff --git a/bluetooth/h4_protocol.cc b/bluetooth/h4_protocol.cc
new file mode 100644
index 0000000..8f24b5e
--- /dev/null
+++ b/bluetooth/h4_protocol.cc
@@ -0,0 +1,71 @@
+//
+// Copyright 2017 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 <android-base/logging.h>
+#include <assert.h>
+#include <fcntl.h>
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace hci {
+
+size_t H4Protocol::Send(uint8_t type, const uint8_t* data, size_t length) {
+  int rv = WriteSafely(uart_fd_, &type, sizeof(type));
+  if (rv == sizeof(type)) {
+    rv = WriteSafely(uart_fd_, data, length);
+  }
+  return rv;
+}
+
+void H4Protocol::OnPacketReady() {
+  switch (hci_packet_type_) {
+    case HCI_PACKET_TYPE_EVENT:
+      event_cb_(hci_packetizer_.GetPacket());
+      break;
+    case HCI_PACKET_TYPE_ACL_DATA:
+      acl_cb_(hci_packetizer_.GetPacket());
+      break;
+    case HCI_PACKET_TYPE_SCO_DATA:
+      sco_cb_(hci_packetizer_.GetPacket());
+      break;
+    default: {
+      bool bad_packet_type = true;
+      CHECK(!bad_packet_type);
+    }
+  }
+  // Get ready for the next type byte.
+  hci_packet_type_ = HCI_PACKET_TYPE_UNKNOWN;
+}
+
+void H4Protocol::OnDataReady(int fd) {
+  if (hci_packet_type_ == HCI_PACKET_TYPE_UNKNOWN) {
+    uint8_t buffer[1] = {0};
+    size_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, 1));
+    CHECK(bytes_read == 1);
+    hci_packet_type_ = static_cast<HciPacketType>(buffer[0]);
+  } else {
+    hci_packetizer_.OnDataReady(fd, hci_packet_type_);
+  }
+}
+
+}  // namespace hci
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/h4_protocol.h b/bluetooth/h4_protocol.h
new file mode 100644
index 0000000..67e2b03
--- /dev/null
+++ b/bluetooth/h4_protocol.h
@@ -0,0 +1,60 @@
+//
+// Copyright 2017 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 <hidl/HidlSupport.h>
+
+#include "async_fd_watcher.h"
+#include "hci_internals.h"
+#include "hci_protocol.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace hci {
+
+class H4Protocol : public HciProtocol {
+ public:
+  H4Protocol(int fd, PacketReadCallback event_cb, PacketReadCallback acl_cb,
+             PacketReadCallback sco_cb)
+      : uart_fd_(fd),
+        event_cb_(event_cb),
+        acl_cb_(acl_cb),
+        sco_cb_(sco_cb),
+        hci_packetizer_([this]() { OnPacketReady(); }) {}
+
+  size_t Send(uint8_t type, const uint8_t* data, size_t length);
+
+  void OnPacketReady();
+
+  void OnDataReady(int fd);
+
+ private:
+  int uart_fd_;
+
+  PacketReadCallback event_cb_;
+  PacketReadCallback acl_cb_;
+  PacketReadCallback sco_cb_;
+
+  HciPacketType hci_packet_type_{HCI_PACKET_TYPE_UNKNOWN};
+  hci::HciPacketizer hci_packetizer_;
+};
+
+}  // namespace hci
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/hci_internals.h b/bluetooth/hci_internals.h
new file mode 100644
index 0000000..1e1f300
--- /dev/null
+++ b/bluetooth/hci_internals.h
@@ -0,0 +1,49 @@
+//
+// Copyright 2016 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 <stdlib.h>
+
+// HCI UART transport packet types (Volume 4, Part A, 2)
+enum HciPacketType {
+  HCI_PACKET_TYPE_UNKNOWN = 0,
+  HCI_PACKET_TYPE_COMMAND = 1,
+  HCI_PACKET_TYPE_ACL_DATA = 2,
+  HCI_PACKET_TYPE_SCO_DATA = 3,
+  HCI_PACKET_TYPE_EVENT = 4
+};
+
+// 2 bytes for opcode, 1 byte for parameter length (Volume 2, Part E, 5.4.1)
+const size_t HCI_COMMAND_PREAMBLE_SIZE = 3;
+const size_t HCI_LENGTH_OFFSET_CMD = 2;
+
+// 2 bytes for handle, 2 bytes for data length (Volume 2, Part E, 5.4.2)
+const size_t HCI_ACL_PREAMBLE_SIZE = 4;
+const size_t HCI_LENGTH_OFFSET_ACL = 2;
+
+// 2 bytes for handle, 1 byte for data length (Volume 2, Part E, 5.4.3)
+const size_t HCI_SCO_PREAMBLE_SIZE = 3;
+const size_t HCI_LENGTH_OFFSET_SCO = 2;
+
+// 1 byte for event code, 1 byte for parameter length (Volume 2, Part E, 5.4.4)
+const size_t HCI_EVENT_PREAMBLE_SIZE = 2;
+const size_t HCI_LENGTH_OFFSET_EVT = 1;
+
+const size_t HCI_PREAMBLE_SIZE_MAX = HCI_ACL_PREAMBLE_SIZE;
+
+// Event codes (Volume 2, Part E, 7.7.14)
+const uint8_t HCI_COMMAND_COMPLETE_EVENT = 0x0E;
diff --git a/bluetooth/hci_packetizer.cc b/bluetooth/hci_packetizer.cc
new file mode 100644
index 0000000..9549858
--- /dev/null
+++ b/bluetooth/hci_packetizer.cc
@@ -0,0 +1,91 @@
+//
+// Copyright 2017 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 <android-base/logging.h>
+#include <utils/Log.h>
+
+#include <dlfcn.h>
+#include <fcntl.h>
+
+namespace {
+
+const size_t preamble_size_for_type[] = {
+    0, HCI_COMMAND_PREAMBLE_SIZE, HCI_ACL_PREAMBLE_SIZE, HCI_SCO_PREAMBLE_SIZE,
+    HCI_EVENT_PREAMBLE_SIZE};
+const size_t packet_length_offset_for_type[] = {
+    0, HCI_LENGTH_OFFSET_CMD, HCI_LENGTH_OFFSET_ACL, HCI_LENGTH_OFFSET_SCO,
+    HCI_LENGTH_OFFSET_EVT};
+
+size_t HciGetPacketLengthForType(HciPacketType type, const uint8_t* preamble) {
+  size_t offset = packet_length_offset_for_type[type];
+  if (type != HCI_PACKET_TYPE_ACL_DATA) return preamble[offset];
+  return (((preamble[offset + 1]) << 8) | preamble[offset]);
+}
+
+}  // namespace
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace hci {
+
+const hidl_vec<uint8_t>& HciPacketizer::GetPacket() const { return packet_; }
+
+void HciPacketizer::OnDataReady(int fd, HciPacketType packet_type) {
+  switch (state_) {
+    case HCI_PREAMBLE: {
+      size_t bytes_read = TEMP_FAILURE_RETRY(
+          read(fd, preamble_ + bytes_read_,
+               preamble_size_for_type[packet_type] - bytes_read_));
+      CHECK(bytes_read > 0);
+      bytes_read_ += bytes_read;
+      if (bytes_read_ == preamble_size_for_type[packet_type]) {
+        size_t packet_length =
+            HciGetPacketLengthForType(packet_type, preamble_);
+        packet_.resize(preamble_size_for_type[packet_type] + packet_length);
+        memcpy(packet_.data(), preamble_, preamble_size_for_type[packet_type]);
+        bytes_remaining_ = packet_length;
+        state_ = HCI_PAYLOAD;
+        bytes_read_ = 0;
+      }
+      break;
+    }
+
+    case HCI_PAYLOAD: {
+      size_t bytes_read = TEMP_FAILURE_RETRY(read(
+          fd,
+          packet_.data() + preamble_size_for_type[packet_type] + bytes_read_,
+          bytes_remaining_));
+      CHECK(bytes_read > 0);
+      bytes_remaining_ -= bytes_read;
+      bytes_read_ += bytes_read;
+      if (bytes_remaining_ == 0) {
+        packet_ready_cb_();
+        state_ = HCI_PREAMBLE;
+        bytes_read_ = 0;
+      }
+      break;
+    }
+  }
+}
+
+}  // namespace hci
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/hci_packetizer.h b/bluetooth/hci_packetizer.h
new file mode 100644
index 0000000..90579bd
--- /dev/null
+++ b/bluetooth/hci_packetizer.h
@@ -0,0 +1,53 @@
+//
+// Copyright 2017 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 <hidl/HidlSupport.h>
+
+#include "hci_internals.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace hci {
+
+using ::android::hardware::hidl_vec;
+using HciPacketReadyCallback = std::function<void(void)>;
+
+class HciPacketizer {
+ public:
+  HciPacketizer(HciPacketReadyCallback packet_cb)
+      : packet_ready_cb_(packet_cb){};
+  void OnDataReady(int fd, HciPacketType packet_type);
+  const hidl_vec<uint8_t>& GetPacket() const;
+
+ protected:
+  enum State { HCI_PREAMBLE, HCI_PAYLOAD };
+  State state_{HCI_PREAMBLE};
+  uint8_t preamble_[HCI_PREAMBLE_SIZE_MAX];
+  hidl_vec<uint8_t> packet_;
+  size_t bytes_remaining_{0};
+  size_t bytes_read_{0};
+  HciPacketReadyCallback packet_ready_cb_;
+};
+
+}  // namespace hci
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/hci_packetizer_hikey.cc b/bluetooth/hci_packetizer_hikey.cc
deleted file mode 100644
index 9b54b8e..0000000
--- a/bluetooth/hci_packetizer_hikey.cc
+++ /dev/null
@@ -1,121 +0,0 @@
-//
-// Copyright 2017 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_hikey.h"
-
-#include <sys/ioctl.h>
-#include <utils/Log.h>
-
-#include <android-base/logging.h>
-
-namespace {
-
-const size_t preamble_size_for_type[] = {
-    0, HCI_COMMAND_PREAMBLE_SIZE, HCI_ACL_PREAMBLE_SIZE, HCI_SCO_PREAMBLE_SIZE,
-    HCI_EVENT_PREAMBLE_SIZE};
-const size_t packet_length_offset_for_type[] = {
-    0, HCI_LENGTH_OFFSET_CMD, HCI_LENGTH_OFFSET_ACL, HCI_LENGTH_OFFSET_SCO,
-    HCI_LENGTH_OFFSET_EVT};
-
-size_t HciGetPacketLengthForType(HciPacketType type, const uint8_t* preamble) {
-  size_t offset = packet_length_offset_for_type[type];
-  if (type != HCI_PACKET_TYPE_ACL_DATA) return preamble[offset];
-  return (((preamble[offset + 1]) << 8) | preamble[offset]);
-}
-
-}  // namespace
-
-namespace android {
-namespace hardware {
-namespace bluetooth {
-namespace V1_0 {
-namespace hikey {
-
-void HciPacketizerHikey::OnDataReadyHikey(int fd) {
-  int tty_bytes = 0;
-  if (ioctl(fd, FIONREAD, &tty_bytes) == -1)
-    ALOGE("%s:FIONREAD %s", __func__, strerror(errno));
-  ALOGV("%s:tty_bytes = %d", __func__, tty_bytes);
-  uint8_t* tmp_buffer = new uint8_t[tty_bytes];
-  size_t bytes_read = TEMP_FAILURE_RETRY(read(fd, tmp_buffer, tty_bytes));
-  CHECK(static_cast<int>(bytes_read) == tty_bytes);
-
-  size_t index = 0;
-  while (index < bytes_read - 1) {
-    switch (hci_parser_state_) {
-      case HCI_IDLE: {
-        hci_packet_type_ = static_cast<HciPacketType>(tmp_buffer[index++]);
-        CHECK(hci_packet_type_ >= HCI_PACKET_TYPE_ACL_DATA &&
-              hci_packet_type_ <= HCI_PACKET_TYPE_EVENT)
-            << "hci_packet_type_ = "
-            << static_cast<unsigned int>(hci_packet_type_);
-        hci_parser_state_ = HCI_TYPE_READY;
-        hci_packet_bytes_remaining_ = preamble_size_for_type[hci_packet_type_];
-        hci_packet_bytes_read_ = 0;
-        break;
-      }
-
-      case HCI_TYPE_READY: {
-        size_t bytes_to_copy = (bytes_read - index < hci_packet_bytes_remaining_
-                                    ? bytes_read - index
-                                    : hci_packet_bytes_remaining_);
-        memcpy(hci_packet_preamble_ + hci_packet_bytes_read_,
-               &tmp_buffer[index], bytes_to_copy);
-        hci_packet_bytes_remaining_ -= bytes_to_copy;
-        hci_packet_bytes_read_ += bytes_to_copy;
-        index += bytes_to_copy;
-        ALOGV("%s:TYPE index = %d", __func__, static_cast<int>(index));
-        if (hci_packet_bytes_remaining_ == 0) {
-          size_t packet_length =
-              HciGetPacketLengthForType(hci_packet_type_, hci_packet_preamble_);
-          hci_packet_.resize(preamble_size_for_type[hci_packet_type_] +
-                             packet_length);
-          memcpy(hci_packet_.data(), hci_packet_preamble_,
-                 preamble_size_for_type[hci_packet_type_]);
-          hci_packet_bytes_remaining_ = packet_length;
-          hci_parser_state_ = HCI_PAYLOAD;
-          hci_packet_bytes_read_ = 0;
-        }
-        break;
-      }
-
-      case HCI_PAYLOAD: {
-        size_t bytes_to_copy = (bytes_read - index < hci_packet_bytes_remaining_
-                                    ? bytes_read - index
-                                    : hci_packet_bytes_remaining_);
-        memcpy(hci_packet_.data() + preamble_size_for_type[hci_packet_type_] +
-                   hci_packet_bytes_read_,
-               &tmp_buffer[index], bytes_to_copy);
-        hci_packet_bytes_remaining_ -= bytes_to_copy;
-        hci_packet_bytes_read_ += bytes_to_copy;
-        index += bytes_to_copy;
-        ALOGV("%s:PAYLOAD index = %d", __func__, static_cast<int>(index));
-        if (hci_packet_bytes_remaining_ == 0) {
-          hci_packet_ready_cb_();
-          hci_parser_state_ = HCI_IDLE;
-        }
-        break;
-      }
-    }
-  }
-  delete[] tmp_buffer;
-}
-
-}  // namespace hikey
-}  // namespace V1_0
-}  // namespace bluetooth
-}  // namespace hardware
-}  // namespace android
diff --git a/bluetooth/hci_packetizer_hikey.h b/bluetooth/hci_packetizer_hikey.h
deleted file mode 100644
index 12fdde2..0000000
--- a/bluetooth/hci_packetizer_hikey.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// Copyright 2017 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 "hci_packetizer.h"
-
-namespace android {
-namespace hardware {
-namespace bluetooth {
-namespace V1_0 {
-namespace hikey {
-
-class HciPacketizerHikey : public hci::HciPacketizer {
- public:
-  HciPacketizerHikey(hci::HciPacketReadyCallback packet_cb)
-      : HciPacketizer(packet_cb){};
-  void OnDataReadyHikey(int fd);
-};
-
-}  // namespace hikey
-}  // namespace V1_0
-}  // namespace bluetooth
-}  // namespace hardware
-}  // namespace android
diff --git a/bluetooth/hci_protocol.cc b/bluetooth/hci_protocol.cc
new file mode 100644
index 0000000..cd709b4
--- /dev/null
+++ b/bluetooth/hci_protocol.cc
@@ -0,0 +1,74 @@
+//
+// Copyright 2017 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_protocol.h"
+
+#define LOG_TAG "android.hardware.bluetooth-hci-hci_protocol"
+#include <android-base/logging.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <utils/Log.h>
+
+namespace {
+
+const size_t preamble_size_for_type[] = {
+    0, HCI_COMMAND_PREAMBLE_SIZE, HCI_ACL_PREAMBLE_SIZE, HCI_SCO_PREAMBLE_SIZE,
+    HCI_EVENT_PREAMBLE_SIZE};
+const size_t packet_length_offset_for_type[] = {
+    0, HCI_LENGTH_OFFSET_CMD, HCI_LENGTH_OFFSET_ACL, HCI_LENGTH_OFFSET_SCO,
+    HCI_LENGTH_OFFSET_EVT};
+
+size_t HciGetPacketLengthForType(HciPacketType type, const uint8_t* preamble) {
+  size_t offset = packet_length_offset_for_type[type];
+  if (type != HCI_PACKET_TYPE_ACL_DATA) return preamble[offset];
+  return (((preamble[offset + 1]) << 8) | preamble[offset]);
+}
+
+}  // namespace
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace hci {
+
+size_t HciProtocol::WriteSafely(int fd, const uint8_t* data, size_t length) {
+  size_t transmitted_length = 0;
+  while (length > 0) {
+    ssize_t ret =
+        TEMP_FAILURE_RETRY(write(fd, data + transmitted_length, length));
+
+    if (ret == -1) {
+      if (errno == EAGAIN) continue;
+      ALOGE("%s error writing to UART (%s)", __func__, strerror(errno));
+      break;
+
+    } else if (ret == 0) {
+      // Nothing written :(
+      ALOGE("%s zero bytes written - something went wrong...", __func__);
+      break;
+    }
+
+    transmitted_length += ret;
+    length -= ret;
+  }
+
+  return transmitted_length;
+}
+
+}  // namespace hci
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/hci_protocol.h b/bluetooth/hci_protocol.h
new file mode 100644
index 0000000..f76cddf
--- /dev/null
+++ b/bluetooth/hci_protocol.h
@@ -0,0 +1,48 @@
+//
+// Copyright 2017 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 <hidl/HidlSupport.h>
+
+#include "hci_internals.h"
+#include "hci_packetizer.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace hci {
+
+using ::android::hardware::hidl_vec;
+using PacketReadCallback = std::function<void(const hidl_vec<uint8_t>&)>;
+
+// Implementation of HCI protocol bits common to different transports
+class HciProtocol {
+ public:
+  HciProtocol() = default;
+  virtual ~HciProtocol(){};
+
+  // Protocol-specific implementation of sending packets.
+  virtual size_t Send(uint8_t type, const uint8_t* data, size_t length) = 0;
+
+ protected:
+  static size_t WriteSafely(int fd, const uint8_t* data, size_t length);
+};
+
+}  // namespace hci
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android