NIAP: Add a new argument to determine single user mode status.
am: 1a88ace397

Change-Id: Ie2866d3a9a83a766fed52e2974fbfd08306536ef
diff --git a/OWNERS b/OWNERS
index 5596ce1..aeb2b83 100644
--- a/OWNERS
+++ b/OWNERS
@@ -2,7 +2,6 @@
 
 # Project owners
 zachoverflow@google.com
-apanicke@google.com
 cmanton@google.com
 hsz@google.com
 jpawlowski@google.com
diff --git a/audio_bluetooth_hw/utils_unittest.cc b/audio_bluetooth_hw/utils_unittest.cc
index a457797..665dea6 100644
--- a/audio_bluetooth_hw/utils_unittest.cc
+++ b/audio_bluetooth_hw/utils_unittest.cc
@@ -25,8 +25,8 @@
 
 class UtilsTest : public testing::Test {
  protected:
-  virtual void SetUp() {}
-  virtual void TearDown() { map_.clear(); }
+  void SetUp() override {}
+  void TearDown() override { map_.clear(); }
 
   std::unordered_map<std::string, std::string> map_;
 };
diff --git a/audio_hal_interface/client_interface.cc b/audio_hal_interface/client_interface.cc
index e6d6a6b..bf3d068 100644
--- a/audio_hal_interface/client_interface.cc
+++ b/audio_hal_interface/client_interface.cc
@@ -73,7 +73,7 @@
                          const android::sp<IBluetoothAudioProvider>& provider)
       : sink_(sink), provider_(provider){};
 
-  Return<void> startStream() {
+  Return<void> startStream() override {
     BluetoothAudioCtrlAck ack = sink_->StartRequest();
     if (ack != BluetoothAudioCtrlAck::PENDING) {
       auto hidl_retval =
@@ -85,7 +85,7 @@
     return Void();
   }
 
-  Return<void> suspendStream() {
+  Return<void> suspendStream() override {
     BluetoothAudioCtrlAck ack = sink_->SuspendRequest();
     if (ack != BluetoothAudioCtrlAck::PENDING) {
       auto hidl_retval =
@@ -97,12 +97,13 @@
     return Void();
   }
 
-  Return<void> stopStream() {
+  Return<void> stopStream() override {
     sink_->StopRequest();
     return Void();
   }
 
-  Return<void> getPresentationPosition(getPresentationPosition_cb _hidl_cb) {
+  Return<void> getPresentationPosition(
+      getPresentationPosition_cb _hidl_cb) override {
     uint64_t remote_delay_report_ns;
     uint64_t total_bytes_read;
     timespec data_position;
@@ -128,7 +129,7 @@
     return Void();
   }
 
-  Return<void> updateMetadata(const SourceMetadata& sourceMetadata) {
+  Return<void> updateMetadata(const SourceMetadata& sourceMetadata) override {
     LOG(INFO) << __func__ << ": " << sourceMetadata.tracks.size()
               << " track(s)";
     // refer to StreamOut.impl.h within Audio HAL (AUDIO_HAL_VERSION_5_0)
@@ -166,7 +167,8 @@
       : bluetooth_audio_clientif_(clientif), message_loop_(message_loop) {}
   void serviceDied(
       uint64_t /*cookie*/,
-      const ::android::wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
+      const ::android::wp<::android::hidl::base::V1_0::IBase>& /*who*/)
+      override {
     LOG(WARNING) << __func__ << ": restarting connection with new Audio Hal";
     if (bluetooth_audio_clientif_ != nullptr && message_loop_ != nullptr) {
       // restart the session on the correct thread
diff --git a/audio_hal_interface/client_interface_unittest.cc b/audio_hal_interface/client_interface_unittest.cc
index de6ba52..fd8c8dc 100644
--- a/audio_hal_interface/client_interface_unittest.cc
+++ b/audio_hal_interface/client_interface_unittest.cc
@@ -65,16 +65,16 @@
  public:
   TestTransport(SessionType session_type)
       : bluetooth::audio::IBluetoothTransportInstance(session_type, {}){};
-  bluetooth::audio::BluetoothAudioCtrlAck StartRequest() {
+  bluetooth::audio::BluetoothAudioCtrlAck StartRequest() override {
     return bluetooth::audio::BluetoothAudioCtrlAck::SUCCESS_FINISHED;
   }
-  bluetooth::audio::BluetoothAudioCtrlAck SuspendRequest() {
+  bluetooth::audio::BluetoothAudioCtrlAck SuspendRequest() override {
     return bluetooth::audio::BluetoothAudioCtrlAck::SUCCESS_FINISHED;
   }
-  void StopRequest() {}
+  void StopRequest() override {}
   bool GetPresentationPosition(uint64_t* remote_delay_report_ns,
                                uint64_t* total_bytes_readed,
-                               timespec* data_position) {
+                               timespec* data_position) override {
     if (remote_delay_report_ns) {
       *remote_delay_report_ns = kRemoteDelayReportMs * 1000000;
     }
@@ -86,9 +86,10 @@
     }
     return true;
   }
-  void MetadataChanged(const source_metadata_t& source_metadata __unused) {}
-  void ResetPresentationPosition(){};
-  void LogBytesRead(size_t bytes_readed __unused){};
+  void MetadataChanged(
+      const source_metadata_t& source_metadata __unused) override {}
+  void ResetPresentationPosition() override{};
+  void LogBytesRead(size_t bytes_readed __unused) override{};
 };
 
 class BluetoothAudioClientInterfaceTest : public Test {
@@ -98,9 +99,9 @@
 
   static constexpr int kClientIfReturnSuccess = 0;
 
-  virtual void SetUp() override {}
+  void SetUp() override {}
 
-  virtual void TearDown() override {
+  void TearDown() override {
     clientif_ = nullptr;
     test_transport_ = nullptr;
   }
diff --git a/bta/hearing_aid/hearing_aid.cc b/bta/hearing_aid/hearing_aid.cc
index 552d65e..59553ef 100644
--- a/bta/hearing_aid/hearing_aid.cc
+++ b/bta/hearing_aid/hearing_aid.cc
@@ -232,7 +232,7 @@
   uint16_t overwrite_min_ce_len;
 
  public:
-  virtual ~HearingAidImpl() = default;
+  ~HearingAidImpl() override = default;
 
   HearingAidImpl(bluetooth::hearing_aid::HearingAidCallbacks* callbacks,
                  Closure initCb)
diff --git a/btif/src/btif_a2dp_audio_interface.cc b/btif/src/btif_a2dp_audio_interface.cc
index 7e46818..026baa1 100644
--- a/btif/src/btif_a2dp_audio_interface.cc
+++ b/btif/src/btif_a2dp_audio_interface.cc
@@ -137,15 +137,15 @@
 
 class BluetoothAudioHost : public IBluetoothAudioHost {
  public:
-  Return<void> startStream() {
+  Return<void> startStream() override {
     btif_a2dp_audio_send_start_req();
     return Void();
   }
-  Return<void> suspendStream() {
+  Return<void> suspendStream() override {
     btif_a2dp_audio_send_suspend_req();
     return Void();
   }
-  Return<void> stopStream() {
+  Return<void> stopStream() override {
     btif_a2dp_audio_send_stop_req();
     return Void();
   }
@@ -160,9 +160,9 @@
 
 class BluetoothAudioDeathRecipient : public hidl_death_recipient {
  public:
-  virtual void serviceDied(
+  void serviceDied(
       uint64_t /*cookie*/,
-      const wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
+      const wp<::android::hidl::base::V1_0::IBase>& /*who*/) override {
     LOG_ERROR(LOG_TAG, "%s", __func__);
     // Restart the session on the correct thread
     do_in_main_thread(FROM_HERE,
diff --git a/btif/src/btif_ble_advertiser.cc b/btif/src/btif_ble_advertiser.cc
index dcbe080..d1923bd 100644
--- a/btif/src/btif_ble_advertiser.cc
+++ b/btif/src/btif_ble_advertiser.cc
@@ -83,7 +83,7 @@
 }
 
 class BleAdvertiserInterfaceImpl : public BleAdvertiserInterface {
-  ~BleAdvertiserInterfaceImpl(){};
+  ~BleAdvertiserInterfaceImpl() override{};
 
   void RegisterAdvertiserCb(IdStatusCallback cb, uint8_t advertiser_id,
                             uint8_t status) {
diff --git a/btif/src/btif_ble_scanner.cc b/btif/src/btif_ble_scanner.cc
index fa6fd4c..b314993 100644
--- a/btif/src/btif_ble_scanner.cc
+++ b/btif/src/btif_ble_scanner.cc
@@ -203,7 +203,7 @@
 void bta_cback(tBTA_GATTC_EVT, tBTA_GATTC*) {}
 
 class BleScannerInterfaceImpl : public BleScannerInterface {
-  ~BleScannerInterfaceImpl(){};
+  ~BleScannerInterfaceImpl() override{};
 
   void RegisterScanner(RegisterCallback cb) override {
     do_in_main_thread(FROM_HERE,
diff --git a/btif/src/btif_hearing_aid.cc b/btif/src/btif_hearing_aid.cc
index 943017e..52cce27 100644
--- a/btif/src/btif_hearing_aid.cc
+++ b/btif/src/btif_hearing_aid.cc
@@ -53,7 +53,7 @@
 class HearingAidInterfaceImpl
     : public bluetooth::hearing_aid::HearingAidInterface,
       public HearingAidCallbacks {
-  ~HearingAidInterfaceImpl() = default;
+  ~HearingAidInterfaceImpl() override = default;
 
   void Init(HearingAidCallbacks* callbacks) override {
     DVLOG(2) << __func__;
diff --git a/common/leaky_bonded_queue_unittest.cc b/common/leaky_bonded_queue_unittest.cc
index fcd55cd..b2ff35b 100644
--- a/common/leaky_bonded_queue_unittest.cc
+++ b/common/leaky_bonded_queue_unittest.cc
@@ -42,7 +42,7 @@
 class MockItem : public Item {
  public:
   MockItem(int i) : Item(i) {}
-  ~MockItem() { Destruct(); }
+  ~MockItem() override { Destruct(); }
   MOCK_METHOD0(Destruct, void());
 };
 
diff --git a/common/metrics_unittest.cc b/common/metrics_unittest.cc
index 03a427a..a46c896 100644
--- a/common/metrics_unittest.cc
+++ b/common/metrics_unittest.cc
@@ -400,12 +400,12 @@
     bt_log_->Clear();
   }
 
-  void SetUp() {
+  void SetUp() override {
     bt_log_ = new BluetoothLog();
     // Clear existing metrics entries, if any
     BluetoothMetricsLogger::GetInstance()->Reset();
   }
-  void TearDown() {
+  void TearDown() override {
     // Clear remaining metrics entries, if any
     BluetoothMetricsLogger::GetInstance()->Reset();
     ClearLog();
diff --git a/gd/.gitignore b/gd/.gitignore
new file mode 100644
index 0000000..106fe1e
--- /dev/null
+++ b/gd/.gitignore
@@ -0,0 +1,2 @@
+**/default.profraw
+**/__pycache__/
diff --git a/gd/Android.bp b/gd/Android.bp
index e0b8d5d..b7d853f 100644
--- a/gd/Android.bp
+++ b/gd/Android.bp
@@ -24,6 +24,9 @@
         "-fvisibility=hidden",
         "-DLOG_NDEBUG=1",
         "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-Wno-unused-parameter",
+        "-Wno-implicit-fallthrough",
+        "-Wno-unused-result",
     ],
     conlyflags: [
         "-std=c99",
@@ -77,13 +80,121 @@
         linux: {
             srcs: [
                 ":BluetoothOsSources_linux_generic",
-            ]
-        }
+            ],
+        },
+        host: {
+            srcs: [
+                ":BluetoothHalSources_hci_rootcanal",
+            ],
+        },
+        android: {
+            srcs: [
+                ":BluetoothHalSources_hci_android_hidl",
+            ],
+            shared_libs: [
+                "android.hardware.bluetooth@1.0",
+                "libhwbinder",
+                "libhidlbase",
+                "libhidltransport",
+                "libutils",
+            ],
+        },
     },
     srcs: [
+        "stack_manager.cc",
+        "module.cc",
         ":BluetoothCommonSources",
+        ":BluetoothHalSources",
+        ":BluetoothHciSources",
         ":BluetoothPacketSources",
-    ]
+    ],
+    generated_headers: [
+        "BluetoothGeneratedPackets_h",
+    ],
+}
+
+cc_binary {
+    name: "stack_with_facade",
+    defaults: [
+        "gd_defaults",
+    ],
+    host_supported: true,
+    srcs: [
+        "facade/facade_main.cc",
+        "facade/grpc_root_server.cc",
+        "grpc/grpc_module.cc",
+        ":BluetoothFacade_hci_hal",
+    ],
+    generated_headers: [
+        "BluetoothGeneratedPackets_h",
+        "BluetoothFacadeGeneratedStub_h",
+    ],
+    generated_sources: [
+        "BluetoothFacadeGeneratedStub_cc",
+    ],
+    static_libs: [
+        "libbluetooth_gd",
+    ],
+    shared_libs: [
+        "libgrpc++_unsecure",
+        "libprotobuf-cpp-full",
+    ],
+    target: {
+        android: {
+            shared_libs: [
+                "android.hardware.bluetooth@1.0",
+                "libhwbinder",
+                "libhidlbase",
+                "libhidltransport",
+                "libutils",
+            ],
+        },
+    },
+    sanitize: {
+        address: true,
+    },
+}
+
+cc_binary {
+    name: "bluetooth_cert_stack",
+    defaults: [
+        "gd_defaults",
+    ],
+    host_supported: true,
+    srcs: [
+        "cert/cert_main.cc",
+        "cert/grpc_root_server.cc",
+        "grpc/grpc_module.cc",
+        ":BluetoothCertSource_hci_hal",
+    ],
+    generated_headers: [
+        "BluetoothGeneratedPackets_h",
+        "BluetoothCertStackGeneratedStub_h",
+    ],
+    generated_sources: [
+        "BluetoothCertStackGeneratedStub_cc",
+    ],
+    static_libs: [
+        "libbluetooth_gd",
+    ],
+    shared_libs: [
+        "libgrpc++_unsecure",
+        "libprotobuf-cpp-full",
+    ],
+    target: {
+        android: {
+            shared_libs: [
+                "android.hardware.bluetooth@1.0",
+                "libhwbinder",
+                "libhidlbase",
+                "libhidltransport",
+                "libutils",
+            ],
+        },
+    },
+    sanitize: {
+        address: true,
+    },
 }
 
 cc_test {
@@ -98,18 +209,63 @@
         linux: {
             srcs: [
                 ":BluetoothOsTestSources_linux_generic",
-            ]
-        }
+            ],
+        },
+        host: {
+            srcs: [
+                ":BluetoothHalTestSources_hci_rootcanal",
+            ],
+        },
+        android: {
+            srcs: [
+                ":BluetoothHalTestSources_hci_android_hidl",
+            ],
+            shared_libs: [
+                "android.hardware.bluetooth@1.0",
+                "libhwbinder",
+                "libhidlbase",
+                "libhidltransport",
+                "libutils",
+            ],
+        },
     },
     srcs: [
+        "module_unittest.cc",
         ":BluetoothCommonTestSources",
+        ":BluetoothHciTestSources",
+        ":BluetoothL2capTestSources",
         ":BluetoothPacketTestSources",
     ],
+    generated_headers : [
+        "BluetoothGeneratedPackets_h",
+    ],
     static_libs : [
         "libbluetooth_gd",
     ],
     sanitize: {
-        cfi: false,
+        address: true,
+    },
+}
+
+cc_test {
+    name: "bluetooth_packet_parser_test",
+    test_suites: ["device-tests"],
+    defaults: [
+        "gd_defaults",
+        "gd_clang_coverage_bin",
+    ],
+    host_supported: true,
+    srcs: [
+        ":BluetoothCommonSources",
+        ":BluetoothPacketSources",
+        ":BluetoothPacketParserTestPacketTestSources",
+    ],
+    generated_headers : [
+        "BluetoothPacketParserTestPacketPdlGen_h",
+    ],
+    sanitize: {
+        address: true,
+        cfi: true,
     },
 }
 
@@ -122,6 +278,152 @@
         ":BluetoothOsBenchmarkSources",
     ],
     static_libs : [
-            "libbluetooth_gd",
+        "libbluetooth_gd",
+    ],
+}
+
+genrule {
+    name: "BluetoothGeneratedPackets_h",
+    tools: [
+        "bluetooth_packetgen",
+    ],
+    cmd: "$(location bluetooth_packetgen) --include=system/bt/gd --out=$(genDir) $(in)",
+    srcs: [
+        "hci/hci_packets.pdl",
+        "l2cap/l2cap_packets.pdl",
+    ],
+    out: [
+        "hci/hci_packets.h",
+        "l2cap/l2cap_packets.h",
+    ],
+}
+
+filegroup {
+    name: "BluetoothFacadeProto",
+    srcs: [
+        "facade/common.proto",
+        "facade/rootservice.proto",
+        "hal/facade.proto",
+    ],
+}
+
+genrule {
+    name: "BluetoothFacadeGeneratedStub_h",
+    tools: [
+        "aprotoc",
+        "protoc-gen-grpc-cpp-plugin",
+    ],
+    cmd: "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
+    srcs: [
+        ":BluetoothFacadeProto",
+    ],
+    out: [
+        "facade/common.grpc.pb.h",
+        "facade/common.pb.h",
+        "facade/rootservice.grpc.pb.h",
+        "facade/rootservice.pb.h",
+        "hal/facade.grpc.pb.h",
+        "hal/facade.pb.h",
+    ],
+}
+
+genrule {
+    name: "BluetoothFacadeGeneratedStub_cc",
+    tools: [
+        "aprotoc",
+        "protoc-gen-grpc-cpp-plugin",
+    ],
+    cmd: "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
+    srcs: [
+        ":BluetoothFacadeProto",
+    ],
+    out: [
+        "facade/common.grpc.pb.cc",
+        "facade/common.pb.cc",
+        "facade/rootservice.grpc.pb.cc",
+        "facade/rootservice.pb.cc",
+        "hal/facade.grpc.pb.cc",
+        "hal/facade.pb.cc",
+    ],
+}
+
+genrule {
+    name: "BluetoothFacadeAndCertGeneratedStub_py",
+    tools: [
+        "aprotoc",
+        "protoc-gen-grpc-python-plugin",
+    ],
+    cmd: "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-python-plugin) $(in) --grpc_out=$(genDir) --python_out=$(genDir); " +
+        "touch $(genDir)/facade/__init__.py; " +
+        "touch $(genDir)/hal/__init__.py; " +
+        "touch $(genDir)/hal/cert/__init__.py; ",
+    srcs: [
+        ":BluetoothFacadeProto",
+        ":BluetoothCertStackProto",
+    ],
+    out: [
+        "cert/rootservice_pb2_grpc.py",
+        "cert/rootservice_pb2.py",
+        "facade/__init__.py",
+        "facade/common_pb2_grpc.py",
+        "facade/common_pb2.py",
+        "facade/rootservice_pb2_grpc.py",
+        "facade/rootservice_pb2.py",
+        "hal/__init__.py",
+        "hal/facade_pb2_grpc.py",
+        "hal/facade_pb2.py",
+        "hal/cert/__init__.py",
+        "hal/cert/api_pb2_grpc.py",
+        "hal/cert/api_pb2.py",
+    ],
+}
+
+filegroup {
+    name: "BluetoothCertStackProto",
+    srcs: [
+        "cert/rootservice.proto",
+        "hal/cert/api.proto",
+    ],
+}
+
+genrule {
+    name: "BluetoothCertStackGeneratedStub_h",
+    tools: [
+        "aprotoc",
+        "protoc-gen-grpc-cpp-plugin",
+    ],
+    cmd: "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
+    srcs: [
+        ":BluetoothCertStackProto",
+        ":BluetoothFacadeProto",  // we need to use facade/common.proto
+    ],
+    out: [
+        "cert/rootservice.grpc.pb.h",
+        "cert/rootservice.pb.h",
+        "facade/common.grpc.pb.h",
+        "facade/common.pb.h",
+        "hal/cert/api.grpc.pb.h",
+        "hal/cert/api.pb.h",
+    ],
+}
+
+genrule {
+    name: "BluetoothCertStackGeneratedStub_cc",
+    tools: [
+        "aprotoc",
+        "protoc-gen-grpc-cpp-plugin",
+    ],
+    cmd: "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
+    srcs: [
+        ":BluetoothCertStackProto",
+        ":BluetoothFacadeProto",  // we need to use facade/common.proto
+    ],
+    out: [
+        "cert/rootservice.grpc.pb.cc",
+        "cert/rootservice.pb.cc",
+        "facade/common.grpc.pb.cc",
+        "facade/common.pb.cc",
+        "hal/cert/api.grpc.pb.cc",
+        "hal/cert/api.pb.cc",
     ],
 }
diff --git a/gd/TEST_MAPPING b/gd/TEST_MAPPING
index 471210b..f0ddeaf 100644
--- a/gd/TEST_MAPPING
+++ b/gd/TEST_MAPPING
@@ -3,6 +3,10 @@
     {
       "name" : "bluetooth_test_gd",
       "host" : true
+    },
+    {
+      "name" : "bluetooth_packet_parser_test",
+      "host" : true
     }
   ]
 }
diff --git a/gd/cert/cert_main.cc b/gd/cert/cert_main.cc
new file mode 100644
index 0000000..623a45c
--- /dev/null
+++ b/gd/cert/cert_main.cc
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2019 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 "stack_manager.h"
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <csignal>
+#include <cstring>
+#include <string>
+#include <thread>
+
+#include "cert/grpc_root_server.h"
+#include "grpc/grpc_module.h"
+#include "hal/cert/cert.h"
+#include "hal/hci_hal.h"
+#include "hal/hci_hal_host_rootcanal.h"
+#include "hal/snoop_logger.h"
+
+using ::bluetooth::Module;
+using ::bluetooth::ModuleList;
+using ::bluetooth::StackManager;
+using ::bluetooth::grpc::GrpcModule;
+using ::bluetooth::os::Thread;
+
+namespace {
+::bluetooth::cert::GrpcRootServer grpc_root_server;
+
+void interrupt_handler(int) {
+  grpc_root_server.StopServer();
+}
+}  // namespace
+
+int main(int argc, const char** argv) {
+  int root_server_port = 8897;
+  int grpc_port = 8899;
+
+  const std::string arg_grpc_root_server_port = "--root-server-port=";
+  const std::string arg_grpc_server_port = "--grpc-port=";
+  const std::string arg_rootcanal_port = "--rootcanal-port=";
+  const std::string arg_btsnoop_path = "--btsnoop=";
+  std::string btsnoop_path;
+  const std::string arg_tester_signal_socket = "--tester-signal-socket=";
+  std::string tester_signal_path;
+  for (int i = 1; i < argc; i++) {
+    std::string arg = argv[i];
+    if (arg.find(arg_grpc_root_server_port) == 0) {
+      auto port_number = arg.substr(arg_grpc_root_server_port.size());
+      root_server_port = std::stoi(port_number);
+    }
+    if (arg.find(arg_grpc_server_port) == 0) {
+      auto port_number = arg.substr(arg_grpc_server_port.size());
+      grpc_port = std::stoi(port_number);
+    }
+    if (arg.find(arg_rootcanal_port) == 0) {
+      auto port_number = arg.substr(arg_rootcanal_port.size());
+      ::bluetooth::hal::HciHalHostRootcanalConfig::Get()->SetPort(std::stoi(port_number));
+    }
+    if (arg.find(arg_btsnoop_path) == 0) {
+      btsnoop_path = arg.substr(arg_btsnoop_path.size());
+      ::bluetooth::hal::SnoopLogger::SetFilePath(btsnoop_path);
+    }
+    if (arg.find(arg_tester_signal_socket) == 0) {
+      tester_signal_path = arg.substr(arg_tester_signal_socket.size());
+    }
+  }
+
+  signal(SIGINT, interrupt_handler);
+  grpc_root_server.StartServer("0.0.0.0", root_server_port, grpc_port);
+  int tester_signal_socket = socket(AF_UNIX, SOCK_STREAM, 0);
+  struct sockaddr_un addr;
+  memset(&addr, 0, sizeof(addr));
+  addr.sun_family = AF_UNIX;
+  strncpy(addr.sun_path, tester_signal_path.c_str(), tester_signal_path.size() + 1);
+  connect(tester_signal_socket, (sockaddr*)&addr, sizeof(addr));
+  close(tester_signal_socket);
+  auto wait_thread = std::thread([] { grpc_root_server.RunGrpcLoop(); });
+  wait_thread.join();
+
+  return 0;
+}
diff --git a/gd/cert/cert_testcases b/gd/cert/cert_testcases
new file mode 100644
index 0000000..777be8e
--- /dev/null
+++ b/gd/cert/cert_testcases
@@ -0,0 +1 @@
+SimpleHalTest
diff --git a/gd/cert/event_stream.py b/gd/cert/event_stream.py
new file mode 100644
index 0000000..0342131
--- /dev/null
+++ b/gd/cert/event_stream.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - 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.
+
+from acts import asserts
+
+from facade import common_pb2
+from datetime import datetime
+from datetime import timedelta
+from grpc import RpcError
+from grpc import StatusCode
+
+class EventStream(object):
+
+  event_buffer = []
+
+  def __init__(self, stream_stub_fn):
+    self.stream_stub_fn = stream_stub_fn
+
+  def clear_event_buffer(self):
+    self.event_buffer.clear()
+
+  def subscribe(self):
+    return self.stream_stub_fn(
+        common_pb2.EventStreamRequest(
+          subscription_mode=common_pb2.SUBSCRIBE,
+          fetch_mode=common_pb2.NONE
+        )
+    )
+
+  def unsubscribe(self):
+    return self.stream_stub_fn(
+        common_pb2.EventStreamRequest(
+            subscription_mode=common_pb2.UNSUBSCRIBE,
+            fetch_mode=common_pb2.NONE
+        )
+    )
+
+  def assert_none(self):
+    response = self.stream_stub_fn(
+        common_pb2.EventStreamRequest(
+            subscription_mode=common_pb2.NONE,
+            fetch_mode=common_pb2.ALL_CURRENT
+        )
+    )
+
+    try:
+      for event in response:
+        self.event_buffer.append(event)
+    except RpcError:
+        pass
+
+    if len(self.event_buffer) != 0:
+      asserts.fail("event_buffer is not empty \n%s" % self.event_buffer)
+
+  def assert_none_matching(self, match_fn):
+    response = self.stream_stub_fn(
+        common_pb2.EventStreamRequest(
+            subscription_mode=common_pb2.NONE,
+            fetch_mode=common_pb2.ALL_CURRENT
+        )
+    )
+
+    try:
+      for event in response:
+        self.event_buffer.append(event)
+    except RpcError:
+      pass
+
+    for event in self.event_buffer:
+      if match_fn(event):
+        asserts.fail("event %s occurs" % event)
+
+  def assert_event_occurs(self, match_fn, timeout=timedelta(seconds=3)):
+    expiration_time = datetime.now() + timeout
+
+    while len(self.event_buffer):
+      element = self.event_buffer.pop(0)
+      if match_fn(element):
+        return
+
+    while (True):
+      if datetime.now() > expiration_time:
+        asserts.fail("timeout of %s exceeded" % str(timeout))
+
+      response = self.stream_stub_fn(
+          common_pb2.EventStreamRequest(
+              subscription_mode=common_pb2.NONE,
+              fetch_mode=common_pb2.AT_LEAST_ONE,
+              timeout_ms = int((expiration_time - datetime.now()).total_seconds() * 1000)
+          )
+      )
+
+      try:
+        for event in response:
+          if (match_fn(event)):
+            for remain_event in response:
+              self.event_buffer.append(remain_event)
+            return
+      except RpcError:
+        if response.code() == StatusCode.DEADLINE_EXCEEDED:
+          continue
+        raise
diff --git a/gd/cert/gd_base_test.py b/gd/cert/gd_base_test.py
new file mode 100644
index 0000000..af4becb
--- /dev/null
+++ b/gd/cert/gd_base_test.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - 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.
+
+from acts.base_test import BaseTestClass
+
+import importlib
+import logging
+import os
+import signal
+import sys
+import subprocess
+
+ANDROID_BUILD_TOP = os.environ.get('ANDROID_BUILD_TOP')
+
+sys.path.append(ANDROID_BUILD_TOP + '/out/soong/.intermediates/system/bt/gd/BluetoothFacadeAndCertGeneratedStub_py/gen')
+
+ANDROID_HOST_OUT = os.environ.get('ANDROID_HOST_OUT')
+ROOTCANAL = ANDROID_HOST_OUT + "/nativetest64/root-canal/root-canal"
+
+class GdBaseTestClass(BaseTestClass):
+    def __init__(self, configs):
+        BaseTestClass.__init__(self, configs)
+
+        log_path_base = configs.get('log_path', '/tmp/logs')
+        rootcanal_logpath = os.path.join(log_path_base, 'rootcanal_logs.txt')
+        self.rootcanal_logs = open(rootcanal_logpath, 'w')
+
+        rootcanal_config = configs["testbed_configs"]['rootcanal']
+        rootcanal_hci_port = str(rootcanal_config.get("hci_port", "6402"))
+        self.rootcanal_process = subprocess.Popen(
+            [
+                ROOTCANAL,
+                str(rootcanal_config.get("test_port", "6401")),
+                rootcanal_hci_port,
+                str(rootcanal_config.get("link_layer_port", "6403"))
+            ],
+            cwd=ANDROID_BUILD_TOP,
+            env=os.environ.copy(),
+            stdout=self.rootcanal_logs,
+            stderr=self.rootcanal_logs
+        )
+
+        gd_devices = self.testbed_configs.get("GdDevice")
+        for gd_device in gd_devices:
+            gd_device["rootcanal_port"] = rootcanal_hci_port
+        gd_cert_devices = self.testbed_configs.get("GdCertDevice")
+        for gd_cert_device in gd_cert_devices:
+            gd_cert_device["rootcanal_port"] = rootcanal_hci_port
+
+        self.register_controller(
+            importlib.import_module('cert.gd_device'),
+            builtin=True)
+        self.register_controller(
+            importlib.import_module('cert.gd_cert_device'),
+            builtin=True)
+
+    def teardown_class(self):
+        self.unregister_controllers()
+        self.rootcanal_process.send_signal(signal.SIGINT)
+        rootcanal_return_code = self.rootcanal_process.wait()
+        self.rootcanal_logs.close()
+        if rootcanal_return_code != 0 and rootcanal_return_code != -signal.SIGINT:
+            logging.error("rootcanal stopped with code: %d" %
+                          rootcanal_return_code)
+            return False
diff --git a/gd/cert/gd_cert_device.py b/gd/cert/gd_cert_device.py
new file mode 100644
index 0000000..7a35bd4
--- /dev/null
+++ b/gd/cert/gd_cert_device.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - 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.
+
+from gd_device_base import GdDeviceBase
+from gd_device_base import replace_vars
+
+from cert import rootservice_pb2_grpc as cert_rootservice_pb2_grpc
+from hal.cert import api_pb2_grpc as hal_cert_pb2_grpc
+
+ACTS_CONTROLLER_CONFIG_NAME = "GdCertDevice"
+ACTS_CONTROLLER_REFERENCE_NAME = "gd_cert_devices"
+
+def create(configs):
+    if not configs:
+        raise GdDeviceConfigError("Configuration is empty")
+    elif not isinstance(configs, list):
+        raise GdDeviceConfigError("Configuration should be a list")
+    return get_instances_with_configs(configs)
+
+
+def destroy(devices):
+    for device in devices:
+        try:
+            device.clean_up()
+        except:
+            device.log.exception("Failed to clean up properly.")
+
+
+def get_info(devices):
+    return []
+
+
+def get_instances_with_configs(configs):
+    print(configs)
+    devices = []
+    for config in configs:
+        resolved_cmd = []
+        for entry in config["cmd"]:
+            resolved_cmd.append(replace_vars(entry, config))
+        devices.append(GdCertDevice(config["grpc_port"], config["grpc_root_server_port"], resolved_cmd, config["label"]))
+    return devices
+
+class GdCertDevice(GdDeviceBase):
+    def __init__(self, grpc_port, grpc_root_server_port, cmd, label):
+        super().__init__(grpc_port, grpc_root_server_port, cmd, label, ACTS_CONTROLLER_CONFIG_NAME)
+        self.rootservice = cert_rootservice_pb2_grpc.RootCertStub(self.grpc_root_server_channel)
+        self.hal = hal_cert_pb2_grpc.HciHalCertStub(self.grpc_channel)
+
diff --git a/gd/cert/gd_device.py b/gd/cert/gd_device.py
new file mode 100644
index 0000000..a306d20
--- /dev/null
+++ b/gd/cert/gd_device.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - 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.
+
+from gd_device_base import GdDeviceBase
+from gd_device_base import replace_vars
+
+from cert.event_stream import EventStream
+from facade import rootservice_pb2_grpc as facade_rootservice_pb2_grpc
+from hal import facade_pb2_grpc as hal_facade_pb2_grpc
+
+ACTS_CONTROLLER_CONFIG_NAME = "GdDevice"
+ACTS_CONTROLLER_REFERENCE_NAME = "gd_devices"
+
+def create(configs):
+    if not configs:
+        raise GdDeviceConfigError("Configuration is empty")
+    elif not isinstance(configs, list):
+        raise GdDeviceConfigError("Configuration should be a list")
+    return get_instances_with_configs(configs)
+
+
+def destroy(devices):
+    for device in devices:
+        try:
+            device.clean_up()
+        except:
+            device.log.exception("Failed to clean up properly.")
+
+
+def get_info(devices):
+    return []
+
+
+def get_instances_with_configs(configs):
+    print(configs)
+    devices = []
+    for config in configs:
+        resolved_cmd = []
+        for entry in config["cmd"]:
+            resolved_cmd.append(replace_vars(entry, config))
+        devices.append(GdDevice(config["grpc_port"], config["grpc_root_server_port"], resolved_cmd, config["label"]))
+    return devices
+
+class GdDevice(GdDeviceBase):
+    def __init__(self, grpc_port, grpc_root_server_port, cmd, label):
+        super().__init__(grpc_port, grpc_root_server_port, cmd, label, ACTS_CONTROLLER_CONFIG_NAME)
+        self.rootservice = facade_rootservice_pb2_grpc.RootFacadeStub(self.grpc_root_server_channel)
+        self.hal = hal_facade_pb2_grpc.HciHalFacadeStub(self.grpc_channel)
+        self.hal.hci_event_stream = EventStream(self.hal.FetchHciEvent)
+        self.hal.hci_acl_stream = EventStream(self.hal.FetchHciAcl)
+        self.hal.hci_sco_stream = EventStream(self.hal.FetchHciSco)
diff --git a/gd/cert/gd_device_base.py b/gd/cert/gd_device_base.py
new file mode 100644
index 0000000..938db1a
--- /dev/null
+++ b/gd/cert/gd_device_base.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - 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.
+
+import logging
+import os
+from builtins import open
+import signal
+import socket
+import subprocess
+import time
+
+from acts import error
+from acts import tracelogger
+
+import grpc
+
+ANDROID_BUILD_TOP = os.environ.get('ANDROID_BUILD_TOP')
+ANDROID_HOST_OUT = os.environ.get('ANDROID_HOST_OUT')
+
+def replace_vars(string, config):
+    return string.replace("$ANDROID_HOST_OUT", ANDROID_HOST_OUT) \
+                 .replace("$(grpc_port)", config.get("grpc_port")) \
+                 .replace("$(grpc_root_server_port)", config.get("grpc_root_server_port")) \
+                 .replace("$(rootcanal_port)", config.get("rootcanal_port"))
+
+class GdDeviceBase:
+    def __init__(self, grpc_port, grpc_root_server_port, cmd, label, type_identifier):
+        self.label = label if label is not None else grpc_port
+        # logging.log_path only exists when this is used in an ACTS test run.
+        log_path_base = getattr(logging, 'log_path', '/tmp/logs')
+        self.log = tracelogger.TraceLogger(
+            GdDeviceBaseLoggerAdapter(logging.getLogger(), {
+                'device': label,
+                'type_identifier' : type_identifier
+            }))
+
+        backing_process_logpath = os.path.join(
+            log_path_base, '%s_%s_backing_logs.txt' % (type_identifier, label))
+        self.backing_process_logs = open(backing_process_logpath, 'w')
+
+        btsnoop_path = os.path.join(log_path_base, '%s_btsnoop_hci.log' % label)
+        cmd.append("--btsnoop=" + btsnoop_path)
+
+        tester_signal_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        socket_address = os.path.join(
+            log_path_base, '%s_socket' % type_identifier)
+        tester_signal_socket.bind(socket_address)
+        tester_signal_socket.listen(1)
+
+        cmd.append("--tester-signal-socket=" + socket_address)
+
+        self.backing_process = subprocess.Popen(
+            cmd,
+            cwd=ANDROID_BUILD_TOP,
+            env=os.environ.copy(),
+            stdout=self.backing_process_logs,
+            stderr=self.backing_process_logs)
+        tester_signal_socket.accept()
+        tester_signal_socket.close()
+
+        self.grpc_root_server_channel = grpc.insecure_channel("localhost:" + grpc_root_server_port)
+        self.grpc_port = int(grpc_port)
+        self.grpc_channel = grpc.insecure_channel("localhost:" + grpc_port)
+
+    def clean_up(self):
+        self.grpc_channel.close()
+        self.grpc_root_server_channel.close()
+        self.backing_process.send_signal(signal.SIGINT)
+        backing_process_return_code = self.backing_process.wait()
+        self.backing_process_logs.close()
+        if backing_process_return_code != 0:
+            logging.error("backing process stopped with code: %d" %
+                          backing_process_return_code)
+            return False
+
+
+class GdDeviceBaseLoggerAdapter(logging.LoggerAdapter):
+    def process(self, msg, kwargs):
+        msg = "[%s|%s] %s" % (self.extra["type_identifier"], self.extra["device"], msg)
+        return (msg, kwargs)
+
+class GdDeviceConfigError(Exception):
+    """Raised when GdDevice configs are malformatted."""
+
+
+class GdDeviceError(error.ActsError):
+    """Raised when there is an error in GdDevice."""
+
diff --git a/gd/cert/grpc_root_server.cc b/gd/cert/grpc_root_server.cc
new file mode 100644
index 0000000..fdbf85a
--- /dev/null
+++ b/gd/cert/grpc_root_server.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2019 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 "cert/grpc_root_server.h"
+
+#include <string>
+
+#include "cert/rootservice.grpc.pb.h"
+#include "grpc/grpc_module.h"
+#include "hal/cert/cert.h"
+#include "os/log.h"
+#include "os/thread.h"
+#include "stack_manager.h"
+
+namespace bluetooth {
+namespace cert {
+
+using ::bluetooth::grpc::GrpcModule;
+using ::bluetooth::os::Thread;
+
+namespace {
+class RootCertService : public ::bluetooth::cert::RootCert::Service {
+ public:
+  RootCertService(int grpc_port) : grpc_port_(grpc_port) {}
+
+  ::grpc::Status StartStack(::grpc::ServerContext* context, const ::bluetooth::cert::StartStackRequest* request,
+                            ::bluetooth::cert::StartStackResponse* response) override {
+    if (is_running_) {
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "stack is running");
+    }
+
+    ModuleList modules;
+    modules.add<::bluetooth::grpc::GrpcModule>();
+
+    BluetoothModule module_to_test = request->module_to_test();
+    switch (module_to_test) {
+      case BluetoothModule::HAL:
+        modules.add<::bluetooth::hal::cert::HalCertModule>();
+        break;
+      default:
+        return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "invalid module under test");
+    }
+
+    stack_thread_ = new Thread("stack_thread", Thread::Priority::NORMAL);
+    stack_manager_.StartUp(&modules, stack_thread_);
+
+    GrpcModule* grpc_module = stack_manager_.GetInstance<GrpcModule>();
+    grpc_module->StartServer("0.0.0.0", grpc_port_);
+
+    grpc_loop_thread_ = new std::thread([grpc_module] { grpc_module->RunGrpcLoop(); });
+    is_running_ = true;
+
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status StopStack(::grpc::ServerContext* context, const ::bluetooth::cert::StopStackRequest* request,
+                           ::bluetooth::cert::StopStackResponse* response) override {
+    if (!is_running_) {
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "stack is not running");
+    }
+
+    stack_manager_.GetInstance<GrpcModule>()->StopServer();
+    grpc_loop_thread_->join();
+
+    stack_manager_.ShutDown();
+    delete stack_thread_;
+    is_running_ = false;
+    return ::grpc::Status::OK;
+  }
+
+ private:
+  Thread* stack_thread_ = nullptr;
+  bool is_running_ = false;
+  std::thread* grpc_loop_thread_ = nullptr;
+  StackManager stack_manager_;
+  int grpc_port_ = 8898;
+};
+
+RootCertService* root_cert_service;
+}  // namespace
+
+void GrpcRootServer::StartServer(const std::string& address, int grpc_root_server_port, int grpc_port) {
+  ASSERT(!started_);
+  started_ = true;
+
+  std::string listening_port = address + ":" + std::to_string(grpc_root_server_port);
+  ::grpc::ServerBuilder builder;
+  root_cert_service = new RootCertService(grpc_port);
+  builder.RegisterService(root_cert_service);
+  builder.AddListeningPort(listening_port, ::grpc::InsecureServerCredentials());
+  server_ = builder.BuildAndStart();
+
+  ASSERT(server_ != nullptr);
+}
+
+void GrpcRootServer::StopServer() {
+  ASSERT(started_);
+  server_->Shutdown();
+  started_ = false;
+  server_.reset();
+  delete root_cert_service;
+}
+
+void GrpcRootServer::RunGrpcLoop() {
+  ASSERT(started_);
+  server_->Wait();
+}
+
+}  // namespace cert
+}  // namespace bluetooth
diff --git a/gd/cert/grpc_root_server.h b/gd/cert/grpc_root_server.h
new file mode 100644
index 0000000..2ebfe79
--- /dev/null
+++ b/gd/cert/grpc_root_server.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 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 <string>
+
+#include <grpc++/grpc++.h>
+
+namespace bluetooth {
+namespace cert {
+
+class GrpcRootServer {
+ public:
+  void StartServer(const std::string& address, int grpc_root_server_port, int grpc_port);
+
+  void StopServer();
+
+  void RunGrpcLoop();
+
+ private:
+  bool started_ = false;
+  std::unique_ptr<::grpc::Server> server_ = nullptr;
+};
+
+}  // namespace cert
+}  // namespace bluetooth
diff --git a/gd/cert/host_only_config.json b/gd/cert/host_only_config.json
new file mode 100644
index 0000000..052685a
--- /dev/null
+++ b/gd/cert/host_only_config.json
@@ -0,0 +1,46 @@
+{   "_description": "Bluetooth cert testing",
+    "testbed":
+    [
+        {
+            "_description": "Host only cert testbed",
+            "name": "HostOnlyCert",
+            "rootcanal":
+            {
+                "test_port": 6401,
+                "hci_port": 6402,
+                "link_layer_port": 6403
+            },
+            "GdDevice":
+            [
+                {
+                    "grpc_port": "8899",
+                    "grpc_root_server_port": "8897",
+                    "label": "stack_under_test",
+                    "cmd":
+                    [
+                        "$ANDROID_HOST_OUT/bin/stack_with_facade",
+                        "--grpc-port=$(grpc_port)",
+                        "--root-server-port=$(grpc_root_server_port)",
+                        "--rootcanal-port=$(rootcanal_port)"
+                    ]
+                }
+            ],
+            "GdCertDevice":
+            [
+                {
+                    "grpc_port": "8898",
+                    "grpc_root_server_port": "8896",
+                    "label": "cert_stack",
+                    "cmd":
+                    [
+                        "$ANDROID_HOST_OUT/bin/bluetooth_cert_stack",
+                        "--grpc-port=$(grpc_port)",
+                        "--root-server-port=$(grpc_root_server_port)",
+                        "--rootcanal-port=$(rootcanal_port)"
+                    ]
+                }
+            ]
+        }
+    ],
+    "logpath": "/tmp/logs"
+}
diff --git a/gd/cert/rootservice.proto b/gd/cert/rootservice.proto
new file mode 100644
index 0000000..02bd078
--- /dev/null
+++ b/gd/cert/rootservice.proto
@@ -0,0 +1,25 @@
+syntax = "proto3";
+
+package bluetooth.cert;
+
+service RootCert {
+  rpc StartStack(StartStackRequest) returns (StartStackResponse) {}
+  rpc StopStack(StopStackRequest) returns (StopStackResponse) {}
+}
+
+enum BluetoothModule {
+  HAL = 0;
+  HCI = 1;
+  L2CAP = 2;
+  SECURITY = 3;
+}
+
+message StartStackRequest {
+  BluetoothModule module_to_test = 1;
+}
+
+message StartStackResponse {}
+
+message StopStackRequest {}
+
+message StopStackResponse {}
diff --git a/gd/cert/run_cert.sh b/gd/cert/run_cert.sh
new file mode 100755
index 0000000..81fe91e
--- /dev/null
+++ b/gd/cert/run_cert.sh
@@ -0,0 +1,3 @@
+#! /bin/bash
+
+act.py -c $ANDROID_BUILD_TOP/system/bt/gd/cert/host_only_config.json -tf $ANDROID_BUILD_TOP/system/bt/gd/cert/cert_testcases -tp $ANDROID_BUILD_TOP/system/bt/gd
diff --git a/gd/cert/set_up_acts.sh b/gd/cert/set_up_acts.sh
new file mode 100755
index 0000000..2d3ad3a
--- /dev/null
+++ b/gd/cert/set_up_acts.sh
@@ -0,0 +1,6 @@
+#! /bin/bash
+
+# for more info, see go/acts
+
+cd $ANDROID_BUILD_TOP/tools/test/connectivity/acts/framework/
+sudo python3 setup.py develop
diff --git a/gd/common/Android.bp b/gd/common/Android.bp
index 9d8c16d..656aa16 100644
--- a/gd/common/Android.bp
+++ b/gd/common/Android.bp
@@ -10,6 +10,8 @@
     name: "BluetoothCommonTestSources",
     srcs: [
         "address_unittest.cc",
+        "blocking_queue_unittest.cc",
         "class_of_device_unittest.cc",
+        "bidi_queue_unittest.cc"
     ]
 }
diff --git a/gd/common/bidi_queue.h b/gd/common/bidi_queue.h
new file mode 100644
index 0000000..17baa60
--- /dev/null
+++ b/gd/common/bidi_queue.h
@@ -0,0 +1,89 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 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 "os/queue.h"
+
+namespace bluetooth {
+namespace common {
+
+template <typename TENQUEUE, typename TDEQUEUE>
+class BidiQueueEnd
+    : public ::bluetooth::os::IQueueEnqueue<TENQUEUE>,
+      public ::bluetooth::os::IQueueDequeue<TDEQUEUE> {
+ public:
+  using EnqueueCallback = std::function<std::unique_ptr<TENQUEUE>()>;
+  using DequeueCallback = std::function<void()>;
+
+  BidiQueueEnd(::bluetooth::os::IQueueEnqueue<TENQUEUE>* tx, ::bluetooth::os::IQueueDequeue<TDEQUEUE>* rx)
+      : tx_(tx), rx_(rx) {
+  }
+
+  void RegisterEnqueue(::bluetooth::os::Handler* handler, EnqueueCallback callback) override {
+    tx_->RegisterEnqueue(handler, callback);
+  }
+
+  void UnregisterEnqueue() override {
+    tx_->UnregisterEnqueue();
+  }
+
+  void RegisterDequeue(::bluetooth::os::Handler* handler, DequeueCallback callback) override {
+    rx_->RegisterDequeue(handler, callback);
+  }
+
+  void UnregisterDequeue() override {
+    rx_->UnregisterDequeue();
+  }
+
+  std::unique_ptr<TDEQUEUE> TryDequeue() override {
+    return rx_->TryDequeue();
+  }
+
+ private:
+  ::bluetooth::os::IQueueEnqueue<TENQUEUE>* tx_;
+  ::bluetooth::os::IQueueDequeue<TDEQUEUE>* rx_;
+};
+
+template <typename TUP, typename TDOWN>
+class BidiQueue {
+ public:
+  explicit BidiQueue(size_t capacity)
+      : up_queue_(capacity),
+        down_queue_(capacity),
+        up_end_(&down_queue_, &up_queue_),
+        down_end_(&up_queue_, &down_queue_) {
+  }
+
+  BidiQueueEnd<TDOWN, TUP>* GetUpEnd() {
+    return &up_end_;
+  }
+
+  BidiQueueEnd<TUP, TDOWN>* GetDownEnd() {
+    return &down_end_;
+  }
+
+ private:
+  ::bluetooth::os::Queue<TUP> up_queue_;
+  ::bluetooth::os::Queue<TDOWN> down_queue_;
+  BidiQueueEnd<TDOWN, TUP> up_end_;
+  BidiQueueEnd<TUP, TDOWN> down_end_;
+};
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/common/bidi_queue_unittest.cc b/gd/common/bidi_queue_unittest.cc
new file mode 100644
index 0000000..18642e8
--- /dev/null
+++ b/gd/common/bidi_queue_unittest.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2019 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 "common/bidi_queue.h"
+
+#include <future>
+
+#include "gtest/gtest.h"
+#include "os/thread.h"
+#include "os/handler.h"
+
+using ::bluetooth::os::Thread;
+using ::bluetooth::os::Handler;
+
+namespace bluetooth {
+namespace common {
+namespace {
+
+class BidiQueueTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    up_thread_ = new Thread("up_thread", Thread::Priority::NORMAL);
+    up_handler_ = new Handler(up_thread_);
+    down_thread_ = new Thread("down_thread", Thread::Priority::NORMAL);
+    down_handler_ = new Handler(down_thread_);
+  }
+
+  void TearDown() override {
+    delete up_handler_;
+    delete up_thread_;
+    delete down_handler_;
+    delete down_thread_;
+  }
+
+  Thread* up_thread_;
+  Handler* up_handler_;
+  Thread* down_thread_;
+  Handler* down_handler_;
+};
+
+class A {
+};
+
+class B {
+};
+
+template <typename TA, typename TB>
+class TestBidiQueueEnd {
+ public:
+  explicit TestBidiQueueEnd(BidiQueueEnd<TA, TB>* end, Handler* handler)
+      : handler_(handler), end_(end) {}
+
+  ~TestBidiQueueEnd() {
+  }
+
+  std::promise<void>* Send(TA* value) {
+    std::promise<void>* promise = new std::promise<void>();
+    handler_->Post([this, value, promise] {
+      end_->RegisterEnqueue(handler_, [this, value, promise]() -> std::unique_ptr<TA> {
+        end_->UnregisterEnqueue();
+        promise->set_value();
+        return std::unique_ptr<TA>(value);
+      });
+    });
+
+    return promise;
+  }
+
+  std::promise<TB*>* Receive() {
+    std::promise<TB*>* promise = new std::promise<TB*>();
+    handler_->Post([this, promise] {
+      end_->RegisterDequeue(handler_, [this, promise] {
+        promise->set_value(end_->TryDequeue().get());
+        end_->UnregisterDequeue();
+      });
+    });
+
+    return promise;
+  }
+
+ private:
+  Handler* handler_;
+  BidiQueueEnd<TA, TB>* end_;
+};
+
+TEST_F(BidiQueueTest, simple_test) {
+  BidiQueue<A, B> queue(100);
+  TestBidiQueueEnd<B, A> test_up(queue.GetUpEnd(), up_handler_);
+  TestBidiQueueEnd<A, B> test_down(queue.GetDownEnd(), down_handler_);
+
+  auto sending_b = new B();
+  auto promise_sending_b = test_up.Send(sending_b);
+  promise_sending_b->get_future().wait();
+  auto promise_receive_b = test_down.Receive();
+  EXPECT_EQ(promise_receive_b->get_future().get(), sending_b);
+  delete promise_receive_b;
+  delete promise_sending_b;
+
+  auto sending_a = new A();
+  auto promise_sending_a = test_down.Send(sending_a);
+  promise_sending_a->get_future().wait();
+  auto promise_receive_a = test_up.Receive();
+  EXPECT_EQ(promise_receive_a->get_future().get(), sending_a);
+  delete promise_receive_a;
+  delete promise_sending_a;
+}
+
+}  // namespace
+}  // namespace os
+}  // namespace bluetooth
diff --git a/gd/common/blocking_queue.h b/gd/common/blocking_queue.h
new file mode 100644
index 0000000..8d006d5
--- /dev/null
+++ b/gd/common/blocking_queue.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2019 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 <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+
+namespace bluetooth {
+namespace common {
+
+template <typename T>
+class BlockingQueue {
+ public:
+  void push(T data) {
+    std::unique_lock<std::mutex> lock(mutex_);
+    queue_.push(std::move(data));
+    if (queue_.size() == 1) {
+      not_empty_.notify_all();
+    }
+  };
+
+  T take() {
+    std::unique_lock<std::mutex> lock(mutex_);
+    while (queue_.empty()) {
+      not_empty_.wait(lock);
+    }
+    T data = queue_.front();
+    queue_.pop();
+    return data;
+  };
+
+  bool take_for(std::chrono::milliseconds time, T& data) {
+    std::unique_lock<std::mutex> lock(mutex_);
+    while (queue_.empty()) {
+      if (not_empty_.wait_for(lock, time) == std::cv_status::timeout) {
+        return false;
+      }
+    }
+    data = queue_.front();
+    queue_.pop();
+
+    return true;
+  }
+
+  bool empty() const {
+    std::unique_lock<std::mutex> lock(mutex_);
+    return queue_.empty();
+  };
+
+  void clear() {
+    std::unique_lock<std::mutex> lock(mutex_);
+    std::queue<T> empty;
+    std::swap(queue_, empty);
+  };
+
+ private:
+  std::queue<T> queue_;
+  mutable std::mutex mutex_;
+  std::condition_variable not_empty_;
+};
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/common/blocking_queue_unittest.cc b/gd/common/blocking_queue_unittest.cc
new file mode 100644
index 0000000..da1d9c6
--- /dev/null
+++ b/gd/common/blocking_queue_unittest.cc
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2019 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 "common/blocking_queue.h"
+
+#include <thread>
+
+#include <gtest/gtest.h>
+
+namespace bluetooth {
+namespace common {
+namespace {
+class BlockingQueueTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    EXPECT_TRUE(queue_.empty());
+  }
+
+  // Postcondition for each test case: clear the blocking queue
+  void TearDown() override {
+    EXPECT_TRUE(queue_.empty());
+  }
+
+  BlockingQueue<int> queue_;
+};
+
+TEST_F(BlockingQueueTest, initial_empty) {
+  EXPECT_TRUE(queue_.empty());
+}
+
+TEST_F(BlockingQueueTest, same_thread_push_and_pop) {
+  int data = 1;
+  queue_.push(data);
+  EXPECT_FALSE(queue_.empty());
+  EXPECT_EQ(queue_.take(), data);
+  EXPECT_TRUE(queue_.empty());
+}
+
+TEST_F(BlockingQueueTest, same_thread_push_and_pop_sequential) {
+  for (int data = 0; data < 10; data++) {
+    queue_.push(data);
+    EXPECT_FALSE(queue_.empty());
+    EXPECT_EQ(queue_.take(), data);
+    EXPECT_TRUE(queue_.empty());
+  }
+}
+
+TEST_F(BlockingQueueTest, same_thread_push_and_pop_batch) {
+  for (int data = 0; data < 10; data++) {
+    queue_.push(data);
+  }
+  EXPECT_FALSE(queue_.empty());
+  for (int data = 0; data < 10; data++) {
+    EXPECT_EQ(queue_.take(), data);
+  }
+  EXPECT_TRUE(queue_.empty());
+}
+
+TEST_F(BlockingQueueTest, clear_queue) {
+  for (int data = 0; data < 10; data++) {
+    queue_.push(data);
+  }
+  EXPECT_FALSE(queue_.empty());
+  queue_.clear();
+  EXPECT_TRUE(queue_.empty());
+}
+
+TEST_F(BlockingQueueTest, wait_for_non_empty) {
+  int data = 1;
+  std::thread waiter_thread([this, data] { EXPECT_EQ(queue_.take(), data); });
+  queue_.push(data);
+  waiter_thread.join();
+  EXPECT_TRUE(queue_.empty());
+}
+
+TEST_F(BlockingQueueTest, wait_for_non_empty_batch) {
+  std::thread waiter_thread([this] {
+    for (int data = 0; data < 10; data++) {
+      EXPECT_EQ(queue_.take(), data);
+    }
+  });
+  for (int data = 0; data < 10; data++) {
+    queue_.push(data);
+  }
+  waiter_thread.join();
+  EXPECT_TRUE(queue_.empty());
+}
+
+class VectorBlockingQueueTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    EXPECT_TRUE(queue_.empty());
+  }
+
+  // Postcondition for each test case: clear the blocking queue
+  void TearDown() override {
+    EXPECT_TRUE(queue_.empty());
+  }
+
+  BlockingQueue<std::vector<uint8_t>> queue_;
+};
+
+TEST_F(VectorBlockingQueueTest, same_thread_push_and_pop) {
+  std::vector<uint8_t> data = {1, 2, 3, 4, 5, 6};
+  queue_.push(data);
+  EXPECT_FALSE(queue_.empty());
+  EXPECT_EQ(queue_.take(), data);
+  EXPECT_TRUE(queue_.empty());
+}
+
+}  // namespace
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/facade/common.proto b/gd/facade/common.proto
new file mode 100644
index 0000000..1c4a764
--- /dev/null
+++ b/gd/facade/common.proto
@@ -0,0 +1,21 @@
+syntax = "proto3";
+
+package bluetooth.facade;
+
+enum EventSubscriptionMode {
+  UNCHANGED = 0;
+  SUBSCRIBE = 1;
+  UNSUBSCRIBE = 2;
+}
+
+enum EventFetchMode {
+  NONE = 0;
+  ALL_CURRENT = 1;
+  AT_LEAST_ONE = 2;
+}
+
+message EventStreamRequest {
+  EventSubscriptionMode subscription_mode = 1;
+  EventFetchMode fetch_mode = 2;
+  uint32 timeout_ms = 3;
+}
diff --git a/gd/facade/facade_main.cc b/gd/facade/facade_main.cc
new file mode 100644
index 0000000..4236923
--- /dev/null
+++ b/gd/facade/facade_main.cc
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2019 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 "stack_manager.h"
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <csignal>
+#include <cstring>
+#include <string>
+#include <thread>
+
+#include "facade/grpc_root_server.h"
+#include "grpc/grpc_module.h"
+#include "hal/hci_hal.h"
+#include "hal/hci_hal_host_rootcanal.h"
+#include "hal/snoop_logger.h"
+
+using ::bluetooth::hal::HciHalHostRootcanalConfig;
+using ::bluetooth::StackManager;
+using ::bluetooth::grpc::GrpcModule;
+using ::bluetooth::ModuleList;
+using ::bluetooth::os::Thread;
+
+namespace {
+::bluetooth::facade::GrpcRootServer grpc_root_server;
+
+void interrupt_handler(int) {
+  grpc_root_server.StopServer();
+}
+}  // namespace
+
+// The entry point for the binary with libbluetooth + facades
+int main(int argc, const char** argv) {
+  int root_server_port = 8897;
+  int grpc_port = 8899;
+
+  const std::string arg_grpc_root_server_port = "--root-server-port=";
+  const std::string arg_grpc_server_port = "--grpc-port=";
+  const std::string arg_rootcanal_port = "--rootcanal-port=";
+  const std::string arg_btsnoop_path = "--btsnoop=";
+  std::string btsnoop_path;
+  const std::string arg_tester_signal_socket = "--tester-signal-socket=";
+  std::string tester_signal_path;
+  for (int i = 1; i < argc; i++) {
+    std::string arg = argv[i];
+    if (arg.find(arg_grpc_root_server_port) == 0) {
+      auto port_number = arg.substr(arg_grpc_root_server_port.size());
+      root_server_port = std::stoi(port_number);
+    }
+    if (arg.find(arg_grpc_server_port) == 0) {
+      auto port_number = arg.substr(arg_grpc_server_port.size());
+      grpc_port = std::stoi(port_number);
+    }
+    if (arg.find(arg_rootcanal_port) == 0) {
+      auto port_number = arg.substr(arg_rootcanal_port.size());
+      HciHalHostRootcanalConfig::Get()->SetPort(std::stoi(port_number));
+    }
+    if (arg.find(arg_btsnoop_path) == 0) {
+      btsnoop_path = arg.substr(arg_btsnoop_path.size());
+      ::bluetooth::hal::SnoopLogger::SetFilePath(btsnoop_path);
+    }
+    if (arg.find(arg_tester_signal_socket) == 0) {
+      tester_signal_path = arg.substr(arg_tester_signal_socket.size());
+    }
+  }
+
+  signal(SIGINT, interrupt_handler);
+  grpc_root_server.StartServer("0.0.0.0", root_server_port, grpc_port);
+  int tester_signal_socket = socket(AF_UNIX, SOCK_STREAM, 0);
+  struct sockaddr_un addr;
+  memset(&addr, 0, sizeof(addr));
+  addr.sun_family = AF_UNIX;
+  strncpy(addr.sun_path, tester_signal_path.c_str(), tester_signal_path.size() + 1);
+  connect(tester_signal_socket, (sockaddr*)&addr, sizeof(addr));
+  close(tester_signal_socket);
+  auto wait_thread = std::thread([] { grpc_root_server.RunGrpcLoop(); });
+  wait_thread.join();
+
+  return 0;
+}
diff --git a/gd/facade/grpc_root_server.cc b/gd/facade/grpc_root_server.cc
new file mode 100644
index 0000000..e03cded
--- /dev/null
+++ b/gd/facade/grpc_root_server.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2019 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 "facade/grpc_root_server.h"
+
+#include <string>
+
+#include "facade/rootservice.grpc.pb.h"
+#include "grpc/grpc_module.h"
+#include "hal/facade.h"
+#include "os/log.h"
+#include "os/thread.h"
+#include "stack_manager.h"
+
+namespace bluetooth {
+namespace facade {
+
+using ::bluetooth::grpc::GrpcModule;
+using ::bluetooth::os::Thread;
+
+namespace {
+class RootFacadeService : public ::bluetooth::facade::RootFacade::Service {
+ public:
+  RootFacadeService(int grpc_port) : grpc_port_(grpc_port) {}
+
+  ::grpc::Status StartStack(::grpc::ServerContext* context, const ::bluetooth::facade::StartStackRequest* request,
+                            ::bluetooth::facade::StartStackResponse* response) override {
+    if (is_running_) {
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "stack is running");
+    }
+
+    ModuleList modules;
+    modules.add<::bluetooth::grpc::GrpcModule>();
+
+    BluetoothModule module_under_test = request->module_under_test();
+    switch (module_under_test) {
+      case BluetoothModule::HAL:
+        modules.add<::bluetooth::hal::HciHalFacadeModule>();
+        break;
+      default:
+        return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "invalid module under test");
+    }
+
+    stack_thread_ = new Thread("stack_thread", Thread::Priority::NORMAL);
+    stack_manager_.StartUp(&modules, stack_thread_);
+
+    GrpcModule* grpc_module = stack_manager_.GetInstance<GrpcModule>();
+    grpc_module->StartServer("0.0.0.0", grpc_port_);
+
+    grpc_loop_thread_ = new std::thread([grpc_module] { grpc_module->RunGrpcLoop(); });
+    is_running_ = true;
+
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status StopStack(::grpc::ServerContext* context, const ::bluetooth::facade::StopStackRequest* request,
+                           ::bluetooth::facade::StopStackResponse* response) override {
+    if (!is_running_) {
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "stack is not running");
+    }
+
+    stack_manager_.GetInstance<GrpcModule>()->StopServer();
+    grpc_loop_thread_->join();
+
+    stack_manager_.ShutDown();
+    delete stack_thread_;
+    is_running_ = false;
+    return ::grpc::Status::OK;
+  }
+
+ private:
+  Thread* stack_thread_ = nullptr;
+  bool is_running_ = false;
+  std::thread* grpc_loop_thread_ = nullptr;
+  StackManager stack_manager_;
+  int grpc_port_ = 8898;
+};
+
+RootFacadeService* root_facade_service;
+}  // namespace
+
+void GrpcRootServer::StartServer(const std::string& address, int grpc_root_server_port, int grpc_port) {
+  ASSERT(!started_);
+  started_ = true;
+
+  std::string listening_port = address + ":" + std::to_string(grpc_root_server_port);
+  ::grpc::ServerBuilder builder;
+  root_facade_service = new RootFacadeService(grpc_port);
+  builder.RegisterService(root_facade_service);
+  builder.AddListeningPort(listening_port, ::grpc::InsecureServerCredentials());
+  server_ = builder.BuildAndStart();
+
+  ASSERT(server_ != nullptr);
+}
+
+void GrpcRootServer::StopServer() {
+  ASSERT(started_);
+  server_->Shutdown();
+  started_ = false;
+  server_.reset();
+  delete root_facade_service;
+}
+
+void GrpcRootServer::RunGrpcLoop() {
+  ASSERT(started_);
+  server_->Wait();
+}
+
+}  // namespace facade
+}  // namespace bluetooth
diff --git a/gd/facade/grpc_root_server.h b/gd/facade/grpc_root_server.h
new file mode 100644
index 0000000..9deba35
--- /dev/null
+++ b/gd/facade/grpc_root_server.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 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 <string>
+
+#include <grpc++/grpc++.h>
+
+namespace bluetooth {
+namespace facade {
+
+class GrpcRootServer {
+ public:
+  void StartServer(const std::string& address, int grpc_root_server_port, int grpc_port);
+
+  void StopServer();
+
+  void RunGrpcLoop();
+
+ private:
+  bool started_ = false;
+  std::unique_ptr<::grpc::Server> server_ = nullptr;
+};
+
+}  // namespace facade
+}  // namespace bluetooth
diff --git a/gd/facade/rootservice.proto b/gd/facade/rootservice.proto
new file mode 100644
index 0000000..7bf45ca
--- /dev/null
+++ b/gd/facade/rootservice.proto
@@ -0,0 +1,25 @@
+syntax = "proto3";
+
+package bluetooth.facade;
+
+service RootFacade {
+  rpc StartStack(StartStackRequest) returns (StartStackResponse) {}
+  rpc StopStack(StopStackRequest) returns (StopStackResponse) {}
+}
+
+enum BluetoothModule {
+  HAL = 0;
+  HCI = 1;
+  L2CAP = 2;
+  SECURITY = 3;
+}
+
+message StartStackRequest {
+  BluetoothModule module_under_test = 1;
+}
+
+message StartStackResponse {}
+
+message StopStackRequest {}
+
+message StopStackResponse {}
diff --git a/gd/grpc/async_grpc.h b/gd/grpc/async_grpc.h
new file mode 100644
index 0000000..7cde95d
--- /dev/null
+++ b/gd/grpc/async_grpc.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2019 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 <future>
+#include <memory>
+#include <mutex>
+
+#include <grpc++/grpc++.h>
+
+#include "os/log.h"
+
+namespace bluetooth {
+namespace grpc {
+
+// To be passed to gRPC async invocations as tag.
+// Function is called when the CompletionQueue.Next() returns this tag.
+// Then, user needs to delete this object.
+using GrpcAsyncEventCallback = std::function<void(bool)>;
+
+template <typename REQ, typename RES>
+class GrpcAsyncServerStreamingHandler {
+ public:
+  virtual ~GrpcAsyncServerStreamingHandler() = default;
+
+  // Implementation for requesting the next specific type RPC, using provided parameters.
+  virtual void OnReadyForNextRequest(::grpc::ServerContext*, REQ* req, ::grpc::ServerAsyncWriter<RES>* res,
+                                     ::grpc::CompletionQueue* new_call_cq,
+                                     ::grpc::ServerCompletionQueue* notification_cq, void* tag) = 0;
+
+  virtual void OnRpcRequestReceived(REQ req) = 0;
+
+  virtual void OnRpcRequestFailed() {}
+
+  virtual void OnRpcFinished() {}
+
+  virtual void OnWriteSuccess() {}
+};
+
+// Provides API to upper layer users to control (request, write, finish) a server-streaming asynchronous RPC.
+// When each API is done, callback will be sent to the given GrpcAsyncServerStreamingHandler.
+// Each control box can take one active RPC at one time.
+
+// TODO: problems with this control box:
+//  1. RequestNewRpc is async, but Write and Stop is blocking users. Do we want to do this?
+//  2. Callback to user is done in the gRPC thread. Let's create a pool thread to give it to user?
+//  3. Currently it uses promise to synchronize between events. If we use os/handler it should be easier.
+template <typename REQ, typename RES>
+class GrpcAsyncServerStreamingControlBox {
+ public:
+  GrpcAsyncServerStreamingControlBox(GrpcAsyncServerStreamingHandler<REQ, RES>* async_handler,
+                                     ::grpc::ServerCompletionQueue* cq)
+      : async_handler_(async_handler), cq_(cq) {}
+
+  void RequestNewRpc() {
+    ASSERT(my_state_ == MyState::IDLE);
+    context_ = std::make_unique<::grpc::ServerContext>();
+    req_ = std::make_unique<REQ>();
+    res_ = std::make_unique<::grpc::ServerAsyncWriter<RES>>(context_.get());
+    request_done_ = std::make_unique<GrpcAsyncEventCallback>([this](bool ok) { this->RequestDone(ok); });
+    async_handler_->OnReadyForNextRequest(context_.get(), req_.get(), res_.get(), cq_, cq_, request_done_.get());
+    my_state_ = MyState::REQUESTING;
+  }
+
+  void Write(const RES& res) {
+    std::unique_lock<std::mutex> lock(mutex_);
+    if (my_state_ == MyState::IDLE || my_state_ == MyState::REQUESTING) {
+      LOG_INFO("stream already stopped");
+      return;
+    }
+    ASSERT(my_state_ == MyState::OPEN);
+    write_done_ = std::make_unique<GrpcAsyncEventCallback>([this](bool ok) { this->WriteDone(ok); });
+    my_state_ = MyState::WRITING;
+    res_->Write(res, write_done_.get());
+    promise_ = new std::promise<void>();
+    auto future = promise_->get_future();
+    future.wait();
+  }
+
+  void StopStreaming() {
+    std::unique_lock<std::mutex> lock(mutex_);
+    ASSERT(my_state_ == MyState::OPEN);
+    rpc_finish_ = std::make_unique<GrpcAsyncEventCallback>([this](bool ok) { this->RpcFinish(ok); });
+    my_state_ = MyState::FINISHING;
+    res_->Finish(::grpc::Status::OK, rpc_finish_.get());
+    promise_ = new std::promise<void>();
+    auto future = promise_->get_future();
+    future.wait();
+  }
+
+ private:
+  void RequestDone(bool ok) {
+    ASSERT(my_state_ == MyState::REQUESTING);
+    if (ok) {
+      async_handler_->OnRpcRequestReceived(*req_);
+      my_state_ = MyState::OPEN;
+    } else {
+      clean_up();
+      async_handler_->OnRpcRequestFailed();
+      my_state_ = MyState::IDLE;
+    }
+  }
+
+  void WriteDone(bool ok) {
+    ASSERT(my_state_ == MyState::WRITING);
+    if (ok) {
+      my_state_ = MyState::OPEN;
+      async_handler_->OnWriteSuccess();
+    } else {
+      clean_up();
+      my_state_ = MyState::IDLE;
+      async_handler_->OnRpcFinished();
+    }
+    promise_->set_value();
+  }
+
+  void RpcFinish(bool ok) {
+    ASSERT(ok);
+    ASSERT(my_state_ == MyState::FINISHING);
+    clean_up();
+    my_state_ = MyState::IDLE;
+    async_handler_->OnRpcFinished();
+    promise_->set_value();
+  }
+
+  void clean_up() {
+    context_ = nullptr;
+    req_ = nullptr;
+    res_ = nullptr;
+  }
+
+  mutable std::mutex mutex_;
+  std::promise<void>* promise_ = nullptr;
+
+  GrpcAsyncServerStreamingHandler<REQ, RES>* async_handler_;
+  ::grpc::ServerCompletionQueue* cq_;
+
+  std::unique_ptr<::grpc::ServerContext> context_ = nullptr;
+  std::unique_ptr<REQ> req_ = nullptr;
+  std::unique_ptr<::grpc::ServerAsyncWriter<RES>> res_ = nullptr;
+
+  std::unique_ptr<GrpcAsyncEventCallback> request_done_ = nullptr;
+  std::unique_ptr<GrpcAsyncEventCallback> write_done_ = nullptr;
+  std::unique_ptr<GrpcAsyncEventCallback> rpc_finish_ = nullptr;
+
+  enum class MyState { IDLE, REQUESTING, OPEN, WRITING, FINISHING } my_state_ = MyState::IDLE;
+};
+
+}  // namespace grpc
+}  // namespace bluetooth
diff --git a/gd/grpc/grpc_event_stream.h b/gd/grpc/grpc_event_stream.h
new file mode 100644
index 0000000..3b313f3
--- /dev/null
+++ b/gd/grpc/grpc_event_stream.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2019 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 <grpc++/grpc++.h>
+
+#include <chrono>
+
+#include "common/blocking_queue.h"
+#include "facade/common.pb.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace grpc {
+
+template <typename RES, typename EVENT>
+class GrpcEventStreamCallback {
+ public:
+  virtual ~GrpcEventStreamCallback() = default;
+  virtual void OnSubscribe() {}
+  virtual void OnUnsubscribe() {}
+  virtual void OnWriteResponse(RES* response, const EVENT& event) = 0;
+};
+
+template <typename RES, typename EVENT>
+class GrpcEventStream {
+ public:
+  explicit GrpcEventStream(GrpcEventStreamCallback<RES, EVENT>* callback) : callback_(callback) {}
+
+  void OnIncomingEvent(const EVENT& event) {
+    if (subscribed_) {
+      event_queue_.push(event);
+    }
+  }
+
+  ::grpc::Status HandleRequest(::grpc::ServerContext* context, const ::bluetooth::facade::EventStreamRequest* request,
+                               ::grpc::ServerWriter<RES>* writer) {
+    ::bluetooth::facade::EventSubscriptionMode subscription_mode = request->subscription_mode();
+    ::bluetooth::facade::EventFetchMode fetch_mode = request->fetch_mode();
+    uint32_t timeout_ms = request->timeout_ms();
+    if (timeout_ms == 0) {
+      timeout_ms = 3000;
+    }
+
+    if (subscription_mode == ::bluetooth::facade::SUBSCRIBE) {
+      event_queue_.clear();
+      callback_->OnSubscribe();
+      subscribed_ = true;
+    }
+
+    if (fetch_mode == ::bluetooth::facade::AT_LEAST_ONE) {
+      RES response;
+      EVENT event;
+      if (!event_queue_.take_for(std::chrono::milliseconds(timeout_ms), event)) {
+        return ::grpc::Status(::grpc::StatusCode::DEADLINE_EXCEEDED, "timeout exceeded");
+      }
+      callback_->OnWriteResponse(&response, event);
+      writer->Write(response);
+    }
+
+    // fetch all current remaining items and append to AT_LEAST_ONE query if present
+    if (fetch_mode == ::bluetooth::facade::ALL_CURRENT || fetch_mode == ::bluetooth::facade::AT_LEAST_ONE) {
+      while (!event_queue_.empty()) {
+        RES response;
+        EVENT event = event_queue_.take();
+        callback_->OnWriteResponse(&response, event);
+        writer->Write(response);
+      }
+    }
+
+    if (subscription_mode == ::bluetooth::facade::UNSUBSCRIBE) {
+      subscribed_ = false;
+      event_queue_.clear();
+      callback_->OnUnsubscribe();
+    }
+
+    return ::grpc::Status::OK;
+  }
+
+ private:
+  common::BlockingQueue<EVENT> event_queue_;
+  GrpcEventStreamCallback<RES, EVENT>* callback_;
+  bool subscribed_ = false;
+};
+
+}  // namespace grpc
+}  // namespace bluetooth
diff --git a/gd/grpc/grpc_module.cc b/gd/grpc/grpc_module.cc
new file mode 100644
index 0000000..5f4c862
--- /dev/null
+++ b/gd/grpc/grpc_module.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2019 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 "grpc/grpc_module.h"
+
+#include "os/log.h"
+#include "grpc/async_grpc.h"
+
+using ::grpc::Server;
+using ::grpc::ServerBuilder;
+
+namespace bluetooth {
+namespace grpc {
+
+void GrpcModule::ListDependencies(ModuleList* list) {
+}
+
+void GrpcModule::Start() {
+  ASSERT(!started_);
+}
+
+void GrpcModule::Stop() {
+  ASSERT(!started_);
+}
+
+void GrpcModule::StartServer(const std::string& address, int port) {
+  ASSERT(!started_);
+  started_ = true;
+
+  std::string listening_port = address + ":" + std::to_string(port);
+  ServerBuilder builder;
+
+  for (const auto& facade : facades_) {
+    builder.RegisterService(facade->GetService());
+  }
+
+  builder.AddListeningPort(listening_port, ::grpc::InsecureServerCredentials());
+  completion_queue_ = builder.AddCompletionQueue();
+  server_ = builder.BuildAndStart();
+  ASSERT(server_ != nullptr);
+
+  for (const auto& facade : facades_) {
+    facade->OnServerStarted(completion_queue_.get());
+  }
+}
+
+void GrpcModule::StopServer() {
+  ASSERT(started_);
+
+  server_->Shutdown();
+  completion_queue_->Shutdown();
+
+  for (const auto& facade : facades_) {
+    facade->OnServerStopped();
+  }
+
+  started_ = false;
+}
+
+void GrpcModule::Register(GrpcFacadeModule* facade) {
+  ASSERT(!started_);
+
+  facades_.push_back(facade);
+}
+
+void GrpcModule::Unregister(GrpcFacadeModule* facade) {
+  ASSERT(!started_);
+
+  for (auto it = facades_.begin(); it != facades_.end(); it++) {
+    if (*it == facade) {
+      facades_.erase(it);
+      return;
+    }
+  }
+
+  ASSERT(false);
+}
+
+void GrpcModule::RunGrpcLoop() {
+  void* tag;
+  bool ok;
+  while (true) {
+    if (!completion_queue_->Next(&tag, &ok)) {
+      LOG_INFO("gRPC is shutdown");
+      break;
+    }
+    auto* data = static_cast<GrpcAsyncEventCallback*>(tag);
+    (*data)(ok);
+  }
+}
+
+const ::bluetooth::ModuleFactory GrpcModule::Factory = ::bluetooth::ModuleFactory([]() {
+  return new GrpcModule();
+});
+
+
+void GrpcFacadeModule::ListDependencies(ModuleList* list) {
+  list->add<GrpcModule>();
+}
+
+void GrpcFacadeModule::Start() {
+  GetDependency<GrpcModule>()->Register(this);
+}
+
+void GrpcFacadeModule::Stop() {
+  GetDependency<GrpcModule>()->Unregister(this);
+}
+
+}  // namespace grpc
+}  // namespace bluetooth
diff --git a/gd/grpc/grpc_module.h b/gd/grpc/grpc_module.h
new file mode 100644
index 0000000..4730b06
--- /dev/null
+++ b/gd/grpc/grpc_module.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2019 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 <vector>
+
+#include <grpc++/grpc++.h>
+#include <module.h>
+
+namespace bluetooth {
+namespace grpc {
+
+class GrpcFacadeModule;
+
+class GrpcModule : public ::bluetooth::Module {
+ public:
+  static const ModuleFactory Factory;
+
+  void StartServer(const std::string& address, int port);
+
+  void StopServer();
+
+  void Register(GrpcFacadeModule* facade);
+
+  void Unregister(GrpcFacadeModule* facade);
+
+  // Blocks for incoming gRPC requests
+  void RunGrpcLoop();
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+
+  void Stop() override;
+
+ private:
+  bool started_;
+  std::unique_ptr<::grpc::Server> server_ = nullptr;
+  std::unique_ptr<::grpc::ServerCompletionQueue> completion_queue_ = nullptr;
+  std::vector<GrpcFacadeModule*> facades_;
+};
+
+class GrpcFacadeModule : public ::bluetooth::Module {
+ friend GrpcModule;
+ protected:
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+
+  void Stop() override;
+
+  virtual ::grpc::Service* GetService() const = 0;
+
+  virtual void OnServerStarted(::grpc::ServerCompletionQueue* cq) {}
+
+  virtual void OnServerStopped() {}
+};
+
+}  // namespace grpc
+}  // namespace bluetooth
diff --git a/gd/hal/Android.bp b/gd/hal/Android.bp
new file mode 100644
index 0000000..4c9fa2a
--- /dev/null
+++ b/gd/hal/Android.bp
@@ -0,0 +1,48 @@
+filegroup {
+    name: "BluetoothHalSources",
+    srcs: [
+        "snoop_logger.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothHalSources_hci_rootcanal",
+    srcs: [
+        "hci_hal_host_rootcanal.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothHalSources_hci_android_hidl",
+    srcs: [
+        "hci_hal_android_hidl.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothHalTestSources_hci_rootcanal",
+    srcs: [
+        "hci_hal_host_rootcanal_test.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothHalTestSources_hci_android_hidl",
+    srcs: [
+        "hci_hal_android_hidl_test.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothFacade_hci_hal",
+    srcs: [
+        "facade.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothCertSource_hci_hal",
+    srcs: [
+        "cert/cert.cc",
+    ],
+}
diff --git a/gd/hal/cert/api.proto b/gd/hal/cert/api.proto
new file mode 100644
index 0000000..6071dd1
--- /dev/null
+++ b/gd/hal/cert/api.proto
@@ -0,0 +1,38 @@
+syntax = "proto3";
+
+package bluetooth.hal.cert;
+
+import "google/protobuf/empty.proto";
+import "facade/common.proto";
+
+service HciHalCert {
+  rpc SendHciResetCommand(google.protobuf.Empty) returns (google.protobuf.Empty) {}
+  rpc SetScanMode(ScanModeSettings) returns (google.protobuf.Empty) {}
+  rpc SendHciCommand(HciCommandPacket) returns (google.protobuf.Empty) {}
+  rpc SendHciAcl(HciAclPacket) returns (google.protobuf.Empty) {}
+  rpc SendHciSco(HciScoPacket) returns (google.protobuf.Empty) {}
+
+  rpc FetchHciEvent(bluetooth.facade.EventStreamRequest) returns (stream HciEventPacket) {}
+  rpc FetchHciAcl(bluetooth.facade.EventStreamRequest) returns (stream HciAclPacket) {}
+  rpc FetchHciSco(bluetooth.facade.EventStreamRequest) returns (stream HciScoPacket) {}
+}
+
+message ScanModeSettings {
+  uint32 mode = 1;
+}
+
+message HciEventPacket {
+  bytes payload = 1;
+}
+
+message HciCommandPacket {
+  bytes payload = 1;
+}
+
+message HciAclPacket {
+  bytes payload = 1;
+}
+
+message HciScoPacket {
+  bytes payload = 1;
+}
diff --git a/gd/hal/cert/cert.cc b/gd/hal/cert/cert.cc
new file mode 100644
index 0000000..d637f13
--- /dev/null
+++ b/gd/hal/cert/cert.cc
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2019 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 "hal/cert/cert.h"
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+
+#include "common/blocking_queue.h"
+#include "grpc/grpc_event_stream.h"
+#include "hal/cert/api.grpc.pb.h"
+#include "hal/hci_hal.h"
+#include "hci/hci_packets.h"
+
+namespace bluetooth {
+namespace hal {
+namespace cert {
+
+class HciHalCertService : public HciHalCert::Service, public ::bluetooth::hal::HciHalCallbacks {
+ public:
+  HciHalCertService(HciHal* hal)
+      : hal_(hal), hci_event_stream_(&hci_event_stream_callback_), hci_acl_stream_(&hci_acl_stream_callback_),
+        hci_sco_stream_(&hci_sco_stream_callback_) {
+    hal->registerIncomingPacketCallback(this);
+  }
+
+  ::grpc::Status SendHciResetCommand(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                                     ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(mutex_);
+    can_send_hci_command_ = false;
+    auto packet = hci::ResetBuilder::Create();
+    std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+    hci::BitInserter it(*packet_bytes);
+    packet->Serialize(it);
+    hal_->sendHciCommand(*packet_bytes);
+    std::this_thread::sleep_for(std::chrono::milliseconds(300));
+    while (!can_send_hci_command_) {
+      cv_.wait(lock);
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SetScanMode(::grpc::ServerContext* context, const ScanModeSettings* request,
+                             ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(mutex_);
+    can_send_hci_command_ = false;
+    unsigned int mode = request->mode();
+    hci::ScanEnable scan_enable;
+    switch (mode) {
+      case 0x00:
+        scan_enable = hci::ScanEnable::NO_SCANS;
+        break;
+      case 0x01:
+        scan_enable = hci::ScanEnable::INQUIRY_SCAN_ONLY;
+        break;
+      case 0x02:
+        scan_enable = hci::ScanEnable::PAGE_SCAN_ONLY;
+        break;
+      case 0x03:
+        scan_enable = hci::ScanEnable::INQUIRY_AND_PAGE_SCAN;
+        break;
+    }
+
+    auto packet = hci::WriteScanEnableBuilder::Create(scan_enable);
+    std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+    hci::BitInserter it(*packet_bytes);
+    packet->Serialize(it);
+    hal_->sendHciCommand(*packet_bytes);
+
+    while (!can_send_hci_command_) {
+      cv_.wait(lock);
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SendHciCommand(::grpc::ServerContext* context, const ::bluetooth::hal::cert::HciCommandPacket* request,
+                                ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(mutex_);
+    can_send_hci_command_ = false;
+    std::string req_string = request->payload();
+    hal_->sendHciCommand(std::vector<uint8_t>(req_string.begin(), req_string.end()));
+    while (!can_send_hci_command_) {
+      cv_.wait(lock);
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SendHciAcl(::grpc::ServerContext* context, const ::bluetooth::hal::cert::HciAclPacket* request,
+                            ::google::protobuf::Empty* response) override {
+    std::string req_string = request->payload();
+    hal_->sendAclData(std::vector<uint8_t>(req_string.begin(), req_string.end()));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SendHciSco(::grpc::ServerContext* context, const ::bluetooth::hal::cert::HciScoPacket* request,
+                            ::google::protobuf::Empty* response) override {
+    std::string req_string = request->payload();
+    hal_->sendScoData(std::vector<uint8_t>(req_string.begin(), req_string.end()));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status FetchHciEvent(::grpc::ServerContext* context, const ::bluetooth::facade::EventStreamRequest* request,
+                               ::grpc::ServerWriter<HciEventPacket>* writer) override {
+    return hci_event_stream_.HandleRequest(context, request, writer);
+  };
+
+  ::grpc::Status FetchHciAcl(::grpc::ServerContext* context, const ::bluetooth::facade::EventStreamRequest* request,
+                             ::grpc::ServerWriter<HciAclPacket>* writer) override {
+    return hci_acl_stream_.HandleRequest(context, request, writer);
+  };
+
+  ::grpc::Status FetchHciSco(::grpc::ServerContext* context, const ::bluetooth::facade::EventStreamRequest* request,
+                             ::grpc::ServerWriter<HciScoPacket>* writer) override {
+    return hci_sco_stream_.HandleRequest(context, request, writer);
+  };
+
+  void hciEventReceived(bluetooth::hal::HciPacket event) override {
+    std::string response_str = std::string(event.begin(), event.end());
+    hci_event_stream_.OnIncomingEvent(event);
+    can_send_hci_command_ = true;
+    cv_.notify_one();
+  }
+
+  void aclDataReceived(bluetooth::hal::HciPacket data) override {
+    hci_acl_stream_.OnIncomingEvent(data);
+  }
+
+  void scoDataReceived(bluetooth::hal::HciPacket data) override {
+    hci_sco_stream_.OnIncomingEvent(data);
+  }
+
+ private:
+  HciHal* hal_;
+  bool can_send_hci_command_ = true;
+  mutable std::mutex mutex_;
+  std::condition_variable cv_;
+
+  class HciEventStreamCallback : public ::bluetooth::grpc::GrpcEventStreamCallback<HciEventPacket, HciPacket> {
+   public:
+    void OnWriteResponse(HciEventPacket* response, const HciPacket& event) override {
+      std::string response_str = std::string(event.begin(), event.end());
+      response->set_payload(std::string(event.begin(), event.end()));
+    }
+  } hci_event_stream_callback_;
+  ::bluetooth::grpc::GrpcEventStream<HciEventPacket, HciPacket> hci_event_stream_;
+
+  class HciAclStreamCallback : public ::bluetooth::grpc::GrpcEventStreamCallback<HciAclPacket, HciPacket> {
+   public:
+    void OnWriteResponse(HciAclPacket* response, const HciPacket& event) override {
+      response->set_payload(std::string(event.begin(), event.end()));
+    }
+  } hci_acl_stream_callback_;
+  ::bluetooth::grpc::GrpcEventStream<HciAclPacket, HciPacket> hci_acl_stream_;
+
+  class HciScoStreamCallback : public ::bluetooth::grpc::GrpcEventStreamCallback<HciScoPacket, HciPacket> {
+   public:
+    void OnWriteResponse(HciScoPacket* response, const HciPacket& event) override {
+      response->set_payload(std::string(event.begin(), event.end()));
+    }
+  } hci_sco_stream_callback_;
+  ::bluetooth::grpc::GrpcEventStream<HciScoPacket, HciPacket> hci_sco_stream_;
+};
+
+void HalCertModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<HciHal>();
+}
+
+void HalCertModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new HciHalCertService(GetDependency<HciHal>());
+}
+
+void HalCertModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* HalCertModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory HalCertModule::Factory = ::bluetooth::ModuleFactory([]() {
+  return new HalCertModule();
+});
+
+}  // namespace cert
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/cert/cert.h b/gd/hal/cert/cert.h
new file mode 100644
index 0000000..7dba43d
--- /dev/null
+++ b/gd/hal/cert/cert.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 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 <grpc++/grpc++.h>
+
+#include "grpc/grpc_module.h"
+
+namespace bluetooth {
+namespace hal {
+namespace cert {
+
+class HciHalCertService;
+
+class HalCertModule : public ::bluetooth::grpc::GrpcFacadeModule {
+ public:
+  static const ModuleFactory Factory;
+
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+  void Stop() override;
+
+  ::grpc::Service* GetService() const override;
+
+ private:
+  HciHalCertService* service_;
+};
+
+}  // namespace cert
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/cert/simple_hal_test.py b/gd/hal/cert/simple_hal_test.py
new file mode 100644
index 0000000..e7b7bab
--- /dev/null
+++ b/gd/hal/cert/simple_hal_test.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - 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.
+
+from __future__ import print_function
+
+import os
+import sys
+sys.path.append(os.environ['ANDROID_BUILD_TOP'] + '/system/bt/gd')
+
+from cert.gd_base_test import GdBaseTestClass
+from cert.event_stream import EventStream
+from cert import rootservice_pb2 as cert_rootservice_pb2
+from facade import common_pb2
+from google.protobuf import empty_pb2
+from facade import rootservice_pb2 as facade_rootservice_pb2
+from hal.cert import api_pb2 as hal_cert_pb2
+from hal import facade_pb2 as hal_facade_pb2
+
+class SimpleHalTest(GdBaseTestClass):
+
+    def setup_test(self):
+        self.device_under_test = self.gd_devices[0]
+        self.cert_device = self.gd_cert_devices[0]
+
+        self.device_under_test.rootservice.StartStack(
+            facade_rootservice_pb2.StartStackRequest(
+                module_under_test=facade_rootservice_pb2.BluetoothModule.Value('HAL'),
+            )
+        )
+        self.cert_device.rootservice.StartStack(
+            cert_rootservice_pb2.StartStackRequest(
+                module_to_test=cert_rootservice_pb2.BluetoothModule.Value('HAL'),
+            )
+        )
+
+        self.device_under_test.hal.SendHciResetCommand(empty_pb2.Empty())
+        self.cert_device.hal.SendHciResetCommand(empty_pb2.Empty())
+
+    def teardown_test(self):
+        self.device_under_test.rootservice.StopStack(
+            facade_rootservice_pb2.StopStackRequest()
+        )
+        self.cert_device.rootservice.StopStack(
+            cert_rootservice_pb2.StopStackRequest()
+        )
+
+    def test_none_event(self):
+        self.device_under_test.hal.hci_event_stream.clear_event_buffer()
+
+        self.device_under_test.hal.hci_event_stream.subscribe()
+        self.device_under_test.hal.hci_event_stream.assert_none()
+        self.device_under_test.hal.hci_event_stream.unsubscribe()
+
+    def test_example(self):
+        response = self.device_under_test.hal.SetLoopbackMode(
+            hal_facade_pb2.LoopbackModeSettings(enable=True)
+        )
+
+    def test_fetch_hci_event(self):
+        self.device_under_test.hal.SetLoopbackMode(
+            hal_facade_pb2.LoopbackModeSettings(enable=True)
+        )
+
+        self.device_under_test.hal.hci_event_stream.subscribe()
+
+        self.device_under_test.hal.SendHciCommand(
+            hal_facade_pb2.HciCommandPacket(
+                payload=b'\x01\x04\x053\x8b\x9e0\x01'
+            )
+        )
+
+        self.device_under_test.hal.hci_event_stream.assert_event_occurs(
+            lambda packet: packet.payload == b'\x19\x08\x01\x04\x053\x8b\x9e0\x01'
+        )
+        self.device_under_test.hal.hci_event_stream.unsubscribe()
+
+    def test_inquiry_from_dut(self):
+        self.device_under_test.hal.hci_event_stream.subscribe()
+
+        self.cert_device.hal.SetScanMode(
+            hal_cert_pb2.ScanModeSettings(mode=3)
+        )
+        self.device_under_test.hal.SetInquiry(
+            hal_facade_pb2.InquirySettings(length=0x30, num_responses=0xff)
+        )
+        self.device_under_test.hal.hci_event_stream.assert_event_occurs(
+            lambda packet: b'\x02\x0f' in packet.payload
+            # Expecting an HCI Event (code 0x02, length 0x0f)
+        )
+        self.device_under_test.hal.hci_event_stream.unsubscribe()
diff --git a/gd/hal/facade.cc b/gd/hal/facade.cc
new file mode 100644
index 0000000..1072d59
--- /dev/null
+++ b/gd/hal/facade.cc
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2019 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 "hal/facade.h"
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+
+#include "common/blocking_queue.h"
+#include "grpc/grpc_event_stream.h"
+#include "hal/facade.grpc.pb.h"
+#include "hal/hci_hal.h"
+#include "hci/hci_packets.h"
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+
+using ::bluetooth::facade::EventStreamRequest;
+
+namespace bluetooth {
+namespace hal {
+
+class HciHalFacadeService
+    : public HciHalFacade::Service,
+      public ::bluetooth::hal::HciHalCallbacks {
+ public:
+  HciHalFacadeService(HciHal* hal)
+      : hal_(hal), hci_event_stream_(&hci_event_stream_callback_), hci_acl_stream_(&hci_acl_stream_callback_),
+        hci_sco_stream_(&hci_sco_stream_callback_) {
+    hal->registerIncomingPacketCallback(this);
+  }
+
+  ::grpc::Status SendHciResetCommand(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                                     ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(mutex_);
+    can_send_hci_command_ = false;
+    auto packet = hci::ResetBuilder::Create();
+    std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+    hci::BitInserter it(*packet_bytes);
+    packet->Serialize(it);
+    hal_->sendHciCommand(*packet_bytes);
+    while (!can_send_hci_command_) {
+      cv_.wait(lock);
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SetLoopbackMode(::grpc::ServerContext* context,
+                                 const ::bluetooth::hal::LoopbackModeSettings* request,
+                                 ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(mutex_);
+    can_send_hci_command_ = false;
+    bool enable = request->enable();
+    auto packet = hci::WriteLoopbackModeBuilder::Create(enable ? hci::LoopbackMode::ENABLE_LOCAL
+                                                               : hci::LoopbackMode::NO_LOOPBACK);
+    std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+    hci::BitInserter it(*packet_bytes);
+    packet->Serialize(it);
+    hal_->sendHciCommand(*packet_bytes);
+    while (!can_send_hci_command_) {
+      cv_.wait(lock);
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SetInquiry(::grpc::ServerContext* context, const ::bluetooth::hal::InquirySettings* request,
+                            ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(mutex_);
+    can_send_hci_command_ = false;
+    auto packet = hci::InquiryBuilder::Create(0x33 /* LAP=0x9e8b33 */, static_cast<uint8_t>(request->length()),
+                                              static_cast<uint8_t>(request->num_responses()));
+    std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+    hci::BitInserter it(*packet_bytes);
+    packet->Serialize(it);
+    hal_->sendHciCommand(*packet_bytes);
+    while (!can_send_hci_command_) {
+      cv_.wait(lock);
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SendHciCommand(::grpc::ServerContext* context, const ::bluetooth::hal::HciCommandPacket* request,
+                                ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(mutex_);
+    can_send_hci_command_ = false;
+    std::string req_string = request->payload();
+    hal_->sendHciCommand(std::vector<uint8_t>(req_string.begin(), req_string.end()));
+    while (!can_send_hci_command_) {
+      cv_.wait(lock);
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SendHciAcl(::grpc::ServerContext* context, const ::bluetooth::hal::HciAclPacket* request,
+                            ::google::protobuf::Empty* response) override {
+    std::string req_string = request->payload();
+    hal_->sendAclData(std::vector<uint8_t>(req_string.begin(), req_string.end()));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SendHciSco(::grpc::ServerContext* context, const ::bluetooth::hal::HciScoPacket* request,
+                            ::google::protobuf::Empty* response) override {
+    std::string req_string = request->payload();
+    hal_->sendScoData(std::vector<uint8_t>(req_string.begin(), req_string.end()));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status FetchHciEvent(::grpc::ServerContext* context, const EventStreamRequest* request,
+                               ::grpc::ServerWriter<HciEventPacket>* writer) override {
+    return hci_event_stream_.HandleRequest(context, request, writer);
+  };
+
+  ::grpc::Status FetchHciAcl(::grpc::ServerContext* context, const EventStreamRequest* request,
+                             ::grpc::ServerWriter<HciAclPacket>* writer) override {
+    return hci_acl_stream_.HandleRequest(context, request, writer);
+  };
+
+  ::grpc::Status FetchHciSco(::grpc::ServerContext* context, const EventStreamRequest* request,
+                             ::grpc::ServerWriter<HciScoPacket>* writer) override {
+    return hci_sco_stream_.HandleRequest(context, request, writer);
+  };
+
+  void hciEventReceived(bluetooth::hal::HciPacket event) override {
+    std::string response_str = std::string(event.begin(), event.end());
+    hci_event_stream_.OnIncomingEvent(event);
+    can_send_hci_command_ = true;
+    cv_.notify_one();
+  }
+
+  void aclDataReceived(bluetooth::hal::HciPacket data) override {
+    hci_acl_stream_.OnIncomingEvent(data);
+  }
+
+  void scoDataReceived(bluetooth::hal::HciPacket data) override {
+    hci_sco_stream_.OnIncomingEvent(data);
+  }
+
+ private:
+  HciHal* hal_;
+  bool can_send_hci_command_ = true;
+  mutable std::mutex mutex_;
+  std::condition_variable cv_;
+
+  class HciEventStreamCallback : public ::bluetooth::grpc::GrpcEventStreamCallback<HciEventPacket, HciPacket> {
+   public:
+    void OnWriteResponse(HciEventPacket* response, const HciPacket& event) override {
+      std::string response_str = std::string(event.begin(), event.end());
+      response->set_payload(std::string(event.begin(), event.end()));
+    }
+  } hci_event_stream_callback_;
+  ::bluetooth::grpc::GrpcEventStream<HciEventPacket, HciPacket> hci_event_stream_;
+
+  class HciAclStreamCallback : public ::bluetooth::grpc::GrpcEventStreamCallback<HciAclPacket, HciPacket> {
+   public:
+    void OnWriteResponse(HciAclPacket* response, const HciPacket& event) override {
+      response->set_payload(std::string(event.begin(), event.end()));
+    }
+  } hci_acl_stream_callback_;
+  ::bluetooth::grpc::GrpcEventStream<HciAclPacket, HciPacket> hci_acl_stream_;
+
+  class HciScoStreamCallback : public ::bluetooth::grpc::GrpcEventStreamCallback<HciScoPacket, HciPacket> {
+   public:
+    void OnWriteResponse(HciScoPacket* response, const HciPacket& event) override {
+      response->set_payload(std::string(event.begin(), event.end()));
+    }
+  } hci_sco_stream_callback_;
+  ::bluetooth::grpc::GrpcEventStream<HciScoPacket, HciPacket> hci_sco_stream_;
+};
+
+void HciHalFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<HciHal>();
+}
+
+void HciHalFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new HciHalFacadeService(GetDependency<HciHal>());
+}
+
+void HciHalFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* HciHalFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory HciHalFacadeModule::Factory = ::bluetooth::ModuleFactory([]() {
+  return new HciHalFacadeModule();
+});
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/facade.h b/gd/hal/facade.h
new file mode 100644
index 0000000..fa28d10
--- /dev/null
+++ b/gd/hal/facade.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019 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 <list>
+#include <mutex>
+
+#include <grpc++/grpc++.h>
+
+#include "grpc/grpc_module.h"
+#include "hal/hci_hal.h"
+
+namespace bluetooth {
+namespace hal {
+
+class HciHalFacadeService;
+
+class HciHalFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
+ public:
+  static const ModuleFactory Factory;
+
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+  void Stop() override;
+
+  ::grpc::Service* GetService() const override;
+
+ private:
+  HciHalFacadeService* service_;
+};
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/facade.proto b/gd/hal/facade.proto
new file mode 100644
index 0000000..45919fe
--- /dev/null
+++ b/gd/hal/facade.proto
@@ -0,0 +1,48 @@
+syntax = "proto3";
+
+package bluetooth.hal;
+
+import "google/protobuf/empty.proto";
+import "facade/common.proto";
+
+service HciHalFacade {
+  rpc SendHciResetCommand(google.protobuf.Empty) returns (google.protobuf.Empty) {}
+  rpc SetLoopbackMode(LoopbackModeSettings) returns (google.protobuf.Empty) {}
+  rpc SetInquiry(InquirySettings) returns (google.protobuf.Empty) {}
+  rpc SendHciCommand(HciCommandPacket) returns (google.protobuf.Empty) {}
+  rpc SendHciAcl(HciAclPacket) returns (google.protobuf.Empty) {}
+  rpc SendHciSco(HciScoPacket) returns (google.protobuf.Empty) {}
+
+  rpc FetchHciEvent(bluetooth.facade.EventStreamRequest) returns (stream HciEventPacket) {}
+  rpc FetchHciAcl(bluetooth.facade.EventStreamRequest) returns (stream HciAclPacket) {}
+  rpc FetchHciSco(bluetooth.facade.EventStreamRequest) returns (stream HciScoPacket) {}
+}
+
+message LoopbackModeSettings {
+  bool enable = 1;
+}
+
+message ScanModeSettings {
+  uint32 mode = 1;
+}
+
+message InquirySettings {
+  uint32 length = 1;
+  uint32 num_responses = 2;
+}
+
+message HciEventPacket {
+  bytes payload = 1;
+}
+
+message HciCommandPacket {
+  bytes payload = 1;
+}
+
+message HciAclPacket {
+  bytes payload = 1;
+}
+
+message HciScoPacket {
+  bytes payload = 1;
+}
diff --git a/gd/hal/hci_hal.h b/gd/hal/hci_hal.h
new file mode 100644
index 0000000..88c0d9e
--- /dev/null
+++ b/gd/hal/hci_hal.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2019 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 <vector>
+
+#include "module.h"
+
+namespace bluetooth {
+namespace hal {
+
+using HciPacket = std::vector<uint8_t>;
+
+enum class Status : int32_t { SUCCESS, TRANSPORT_ERROR, INITIALIZATION_ERROR, UNKNOWN };
+
+// Mirrors hardware/interfaces/bluetooth/1.0/IBluetoothHciCallbacks.hal in Android, but moved initializationComplete
+// callback to BluetoothInitializationCompleteCallback
+
+// The interface from the Bluetooth Controller to the stack
+class HciHalCallbacks {
+ public:
+  virtual ~HciHalCallbacks() = default;
+
+  // This function is invoked when an HCI event is received from the
+  // Bluetooth controller to be forwarded to the Bluetooth stack
+  // @param event is the HCI event to be sent to the Bluetooth stack
+  virtual void hciEventReceived(HciPacket event) = 0;
+
+  // Send an ACL data packet form the controller to the host
+  // @param data the ACL HCI packet to be passed to the host stack
+  virtual void aclDataReceived(HciPacket data) = 0;
+
+  // Send a SCO data packet form the controller to the host
+  // @param data the SCO HCI packet to be passed to the host stack
+  virtual void scoDataReceived(HciPacket data) = 0;
+};
+
+// Mirrors hardware/interfaces/bluetooth/1.0/IBluetoothHci.hal in Android
+// The Host Controller Interface (HCI) is the layer defined by the Bluetooth
+// specification between the software that runs on the host and the Bluetooth
+// controller chip. This boundary is the natural choice for a Hardware
+// Abstraction Layer (HAL). Dealing only in HCI packets and events simplifies
+// the stack and abstracts away power management, initialization, and other
+// implementation-specific details related to the hardware.
+class HciHal : public ::bluetooth::Module {
+ public:
+  static const ModuleFactory Factory;
+
+  virtual ~HciHal() = default;
+
+  // Register the callback for incoming packets. All incoming packets are dropped before
+  // this callback is registered. Callback can only be registered once, but will be reset
+  // after close().
+  //
+  // Call this function before initialize() to guarantee all incoming packets are received.
+  //
+  // @param callback implements BluetoothHciHalCallbacks which will
+  //    receive callbacks when incoming HCI packets are received
+  //    from the controller to be sent to the host.
+  virtual void registerIncomingPacketCallback(HciHalCallbacks* callback) = 0;
+
+  // Send an HCI command (as specified in the Bluetooth Specification
+  // V4.2, Vol 2, Part 5, Section 5.4.1) to the Bluetooth controller.
+  // Commands must be executed in order.
+  virtual void sendHciCommand(HciPacket command) = 0;
+
+  // Send an HCI ACL data packet (as specified in the Bluetooth Specification
+  // V4.2, Vol 2, Part 5, Section 5.4.2) to the Bluetooth controller.
+  // Packets must be processed in order.
+  virtual void sendAclData(HciPacket data) = 0;
+
+  // Send an SCO data packet (as specified in the Bluetooth Specification
+  // V4.2, Vol 2, Part 5, Section 5.4.3) to the Bluetooth controller.
+  // Packets must be processed in order.
+  virtual void sendScoData(HciPacket data) = 0;
+};
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/hci_hal_android_hidl.cc b/gd/hal/hci_hal_android_hidl.cc
new file mode 100644
index 0000000..8935837
--- /dev/null
+++ b/gd/hal/hci_hal_android_hidl.cc
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2019 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 "hal/hci_hal.h"
+
+#include <stdlib.h>
+#include <vector>
+#include <future>
+
+#include <android/hardware/bluetooth/1.0/IBluetoothHci.h>
+#include <android/hardware/bluetooth/1.0/IBluetoothHciCallbacks.h>
+#include <android/hardware/bluetooth/1.0/types.h>
+
+#include "hal/snoop_logger.h"
+#include "os/log.h"
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::V1_0::IBluetoothHci;
+using ::android::hardware::bluetooth::V1_0::IBluetoothHciCallbacks;
+using HidlStatus = ::android::hardware::bluetooth::V1_0::Status;
+
+namespace bluetooth {
+namespace hal {
+namespace {
+
+class HciDeathRecipient : public ::android::hardware::hidl_death_recipient {
+ public:
+  virtual void serviceDied(uint64_t /*cookie*/, const android::wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
+    LOG_ERROR("Bluetooth HAL service died!");
+    abort();
+  }
+};
+
+android::sp<HciDeathRecipient> hci_death_recipient_ = new HciDeathRecipient();
+
+class InternalHciCallbacks : public IBluetoothHciCallbacks {
+ public:
+  InternalHciCallbacks(SnoopLogger* btsnoop_logger)
+      : btsnoop_logger_(btsnoop_logger) {
+    init_promise_ = new std::promise<void>();
+  }
+
+  void SetCallback(HciHalCallbacks* callback) {
+    ASSERT(callback_ == nullptr && callback != nullptr);
+    callback_ = callback;
+  }
+
+  void ResetCallback() {
+    callback_ = nullptr;
+  }
+
+  std::promise<void>* GetInitPromise() {
+    return init_promise_;
+  }
+
+  Return<void> initializationComplete(HidlStatus status) {
+    ASSERT(status == HidlStatus::SUCCESS);
+    init_promise_->set_value();
+    return Void();
+  }
+
+  Return<void> hciEventReceived(const hidl_vec<uint8_t>& event) {
+    std::vector<uint8_t> received_hci_packet(event.begin(), event.end());
+    btsnoop_logger_->capture(received_hci_packet, SnoopLogger::Direction::INCOMING,
+                             SnoopLogger::PacketType::EVT);
+    if (callback_ != nullptr) {
+      callback_->hciEventReceived(std::move(received_hci_packet));
+    }
+    return Void();
+  }
+
+  Return<void> aclDataReceived(const hidl_vec<uint8_t>& data) {
+    std::vector<uint8_t> received_hci_packet(data.begin(), data.end());
+    btsnoop_logger_->capture(received_hci_packet, SnoopLogger::Direction::INCOMING,
+                             SnoopLogger::PacketType::ACL);
+    if (callback_ != nullptr) {
+      callback_->aclDataReceived(std::move(received_hci_packet));
+    }
+    return Void();
+  }
+
+  Return<void> scoDataReceived(const hidl_vec<uint8_t>& data) {
+    std::vector<uint8_t> received_hci_packet(data.begin(), data.end());
+    btsnoop_logger_->capture(received_hci_packet, SnoopLogger::Direction::INCOMING,
+                             SnoopLogger::PacketType::SCO);
+    if (callback_ != nullptr) {
+      callback_->scoDataReceived(std::move(received_hci_packet));
+    }
+    return Void();
+  }
+
+ private:
+  std::promise<void>* init_promise_ = nullptr;
+  HciHalCallbacks* callback_ = nullptr;
+  SnoopLogger* btsnoop_logger_ = nullptr;
+};
+
+}  // namespace
+
+const std::string SnoopLogger::DefaultFilePath = "/data/misc/bluetooth/logs/btsnoop_hci.log";
+
+class HciHalHidl : public HciHal {
+ public:
+  void registerIncomingPacketCallback(HciHalCallbacks* callback) override {
+    callbacks_->SetCallback(callback);
+  }
+
+  void sendHciCommand(HciPacket command) override {
+    btsnoop_logger_->capture(command, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::CMD);
+    bt_hci_->sendHciCommand(command);
+  }
+
+  void sendAclData(HciPacket packet) override {
+    btsnoop_logger_->capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::ACL);
+    bt_hci_->sendAclData(packet);
+  }
+
+  void sendScoData(HciPacket packet) override {
+    btsnoop_logger_->capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::SCO);
+    bt_hci_->sendScoData(packet);
+  }
+
+ protected:
+  void ListDependencies(ModuleList* list) override {
+    list->add<SnoopLogger>();
+  }
+
+  void Start() override {
+    btsnoop_logger_ = GetDependency<SnoopLogger>();
+    bt_hci_ = IBluetoothHci::getService();
+    ASSERT(bt_hci_ != nullptr);
+    auto death_link = bt_hci_->linkToDeath(hci_death_recipient_, 0);
+    ASSERT_LOG(death_link.isOk(), "Unable to set the death recipient for the Bluetooth HAL");
+    // Block allows allocation of a variable that might be bypassed by goto.
+    {
+      callbacks_ = new InternalHciCallbacks(btsnoop_logger_);
+      bt_hci_->initialize(callbacks_);
+      // Don't timeout here, time out at a higher layer
+      callbacks_->GetInitPromise()->get_future().wait();
+    }
+  }
+
+  void Stop() override {
+    ASSERT(bt_hci_ != nullptr);
+    auto death_unlink = bt_hci_->unlinkToDeath(hci_death_recipient_);
+    if (!death_unlink.isOk()) {
+      LOG_ERROR("Error unlinking death recipient from the Bluetooth HAL");
+    }
+    bt_hci_->close();
+    callbacks_->ResetCallback();
+    bt_hci_ = nullptr;
+  }
+
+ private:
+  android::sp<InternalHciCallbacks> callbacks_;
+  android::sp<IBluetoothHci> bt_hci_;
+  SnoopLogger* btsnoop_logger_;
+};
+
+const ModuleFactory HciHal::Factory = ModuleFactory([]() {
+  return new HciHalHidl();
+});
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/hci_hal_android_hidl_test.cc b/gd/hal/hci_hal_android_hidl_test.cc
new file mode 100644
index 0000000..5b8b68d
--- /dev/null
+++ b/gd/hal/hci_hal_android_hidl_test.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 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 "hal/hci_hal.h"
+
+#include <chrono>
+#include <future>
+
+#include <gtest/gtest.h>
+
+#include "os/thread.h"
+
+using ::bluetooth::os::Thread;
+
+namespace bluetooth {
+namespace hal {
+namespace {
+
+class HciHalHidlTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new Thread("test_thread", Thread::Priority::NORMAL);
+  }
+
+  void TearDown() override {
+    delete thread_;
+  }
+
+  ModuleRegistry fake_registry_;
+  Thread* thread_;
+};
+
+TEST_F(HciHalHidlTest, init_and_close) {
+  fake_registry_.Start<HciHal>(thread_);
+  fake_registry_.StopAll();
+}
+}  // namespace
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/hci_hal_host_rootcanal.cc b/gd/hal/hci_hal_host_rootcanal.cc
new file mode 100644
index 0000000..3279bd5
--- /dev/null
+++ b/gd/hal/hci_hal_host_rootcanal.cc
@@ -0,0 +1,269 @@
+/*
+ * Copyright 2019 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 "hal/hci_hal_host_rootcanal.h"
+#include "hal/hci_hal.h"
+
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <csignal>
+#include <mutex>
+#include <queue>
+
+#include "hal/snoop_logger.h"
+#include "os/log.h"
+#include "os/reactor.h"
+#include "os/thread.h"
+
+namespace {
+constexpr int INVALID_FD = -1;
+
+constexpr uint8_t kH4Command = 0x01;
+constexpr uint8_t kH4Acl = 0x02;
+constexpr uint8_t kH4Sco = 0x03;
+constexpr uint8_t kH4Event = 0x04;
+
+constexpr uint8_t kH4HeaderSize = 1;
+constexpr uint8_t kHciAclHeaderSize = 4;
+constexpr uint8_t kHciScoHeaderSize = 3;
+constexpr uint8_t kHciEvtHeaderSize = 2;
+constexpr int kBufSize = 1024;
+
+int ConnectToRootCanal(const std::string& server, int port) {
+  int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
+  if (socket_fd < 1) {
+    LOG_ERROR("can't create socket: %s", strerror(errno));
+    return INVALID_FD;
+  }
+
+  struct hostent* host;
+  host = gethostbyname(server.c_str());
+  if (host == nullptr) {
+    LOG_ERROR("can't get server name");
+    return INVALID_FD;
+  }
+
+  struct sockaddr_in serv_addr;
+  memset((void*)&serv_addr, 0, sizeof(serv_addr));
+  serv_addr.sin_family = AF_INET;
+  serv_addr.sin_addr.s_addr = INADDR_ANY;
+  serv_addr.sin_port = htons(port);
+
+  int result = connect(socket_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
+  if (result < 0) {
+    LOG_ERROR("can't connect: %s", strerror(errno));
+    return INVALID_FD;
+  }
+
+  timeval socket_timeout{
+      .tv_sec = 3,
+      .tv_usec = 0,
+  };
+  int ret = setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &socket_timeout, sizeof(socket_timeout));
+  if (ret == -1) {
+    LOG_ERROR("can't control socket fd: %s", strerror(errno));
+    return INVALID_FD;
+  }
+  return socket_fd;
+}
+}  // namespace
+
+namespace bluetooth {
+namespace hal {
+
+const std::string SnoopLogger::DefaultFilePath = "/tmp/btsnoop_hci.log";
+
+class HciHalHostRootcanal : public HciHal {
+ public:
+  void registerIncomingPacketCallback(HciHalCallbacks* callback) override {
+    std::lock_guard<std::mutex> lock(mutex_);
+    ASSERT(incoming_packet_callback_ == nullptr && callback != nullptr);
+    incoming_packet_callback_ = callback;
+  }
+
+  void sendHciCommand(HciPacket command) override {
+    std::lock_guard<std::mutex> lock(mutex_);
+    ASSERT(sock_fd_ != INVALID_FD);
+    std::vector<uint8_t> packet = std::move(command);
+    btsnoop_logger_->capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::CMD);
+    packet.insert(packet.cbegin(), kH4Command);
+    write_to_rootcanal_fd(packet);
+  }
+
+  void sendAclData(HciPacket data) override {
+    std::lock_guard<std::mutex> lock(mutex_);
+    ASSERT(sock_fd_ != INVALID_FD);
+    std::vector<uint8_t> packet = std::move(data);
+    btsnoop_logger_->capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::ACL);
+    packet.insert(packet.cbegin(), kH4Acl);
+    write_to_rootcanal_fd(packet);
+  }
+
+  void sendScoData(HciPacket data) override {
+    std::lock_guard<std::mutex> lock(mutex_);
+    ASSERT(sock_fd_ != INVALID_FD);
+    std::vector<uint8_t> packet = std::move(data);
+    btsnoop_logger_->capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::SCO);
+    packet.insert(packet.cbegin(), kH4Sco);
+    write_to_rootcanal_fd(packet);
+  }
+
+ protected:
+  void ListDependencies(ModuleList* list) override {
+    list->add<SnoopLogger>();
+  }
+
+  void Start() override {
+    std::lock_guard<std::mutex> lock(mutex_);
+    ASSERT(sock_fd_ == INVALID_FD);
+    sock_fd_ = ConnectToRootCanal(config_->GetServerAddress(), config_->GetPort());
+    ASSERT(sock_fd_ != INVALID_FD);
+    reactable_ =
+        hci_incoming_thread_.GetReactor()->Register(sock_fd_, [this]() { this->incoming_packet_received(); }, nullptr);
+    btsnoop_logger_ = GetDependency<SnoopLogger>();
+    LOG_INFO("Rootcanal HAL opened successfully");
+  }
+
+  void Stop() override {
+    std::lock_guard<std::mutex> lock(mutex_);
+    if (reactable_ != nullptr) {
+      hci_incoming_thread_.GetReactor()->Unregister(reactable_);
+      ASSERT(sock_fd_ != INVALID_FD);
+    }
+    reactable_ = nullptr;
+    incoming_packet_callback_ = nullptr;
+    ::close(sock_fd_);
+    sock_fd_ = INVALID_FD;
+    LOG_INFO("Rootcanal HAL is closed");
+  }
+
+ private:
+  std::mutex mutex_;
+  HciHalHostRootcanalConfig* config_ = HciHalHostRootcanalConfig::Get();
+  HciHalCallbacks* incoming_packet_callback_ = nullptr;
+  int sock_fd_ = INVALID_FD;
+  bluetooth::os::Thread hci_incoming_thread_ =
+      bluetooth::os::Thread("hci_incoming_thread", bluetooth::os::Thread::Priority::NORMAL);
+  bluetooth::os::Reactor::Reactable* reactable_ = nullptr;
+  std::queue<std::vector<uint8_t>> hci_outgoing_queue_;
+  SnoopLogger* btsnoop_logger_;
+
+  void write_to_rootcanal_fd(HciPacket packet) {
+    // TODO: replace this with new queue when it's ready
+    hci_outgoing_queue_.emplace(packet);
+    if (hci_outgoing_queue_.size() == 1) {
+      hci_incoming_thread_.GetReactor()->ModifyRegistration(
+          reactable_, [this] { this->incoming_packet_received(); },
+          [this] {
+            std::lock_guard<std::mutex> lock(this->mutex_);
+            auto packet_to_send = this->hci_outgoing_queue_.front();
+            auto bytes_written = write(this->sock_fd_, (void*)packet_to_send.data(), packet_to_send.size());
+            this->hci_outgoing_queue_.pop();
+            if (bytes_written == -1) {
+              abort();
+            }
+            if (hci_outgoing_queue_.empty()) {
+              this->hci_incoming_thread_.GetReactor()->ModifyRegistration(
+                  this->reactable_, [this] { this->incoming_packet_received(); }, nullptr);
+            }
+          });
+    }
+  }
+
+  void incoming_packet_received() {
+    ASSERT(incoming_packet_callback_ != nullptr);
+
+    uint8_t buf[kBufSize] = {};
+
+    ssize_t received_size;
+    RUN_NO_INTR(received_size = recv(sock_fd_, buf, kH4HeaderSize, 0));
+    ASSERT_LOG(received_size != -1, "Can't receive from socket: %s", strerror(errno));
+    if (received_size == 0) {
+      LOG_WARN("Can't read H4 header.");
+      raise(SIGINT);
+      return;
+    }
+
+    if (buf[0] == kH4Event) {
+      RUN_NO_INTR(received_size = recv(sock_fd_, buf + kH4HeaderSize, kHciEvtHeaderSize, 0));
+      ASSERT_LOG(received_size != -1, "Can't receive from socket: %s", strerror(errno));
+      ASSERT_LOG(received_size == kHciEvtHeaderSize, "malformed HCI event header received");
+
+      uint8_t hci_evt_parameter_total_length = buf[2];
+      ssize_t payload_size;
+      RUN_NO_INTR(payload_size =
+                      recv(sock_fd_, buf + kH4HeaderSize + kHciEvtHeaderSize, hci_evt_parameter_total_length, 0));
+      ASSERT_LOG(payload_size != -1, "Can't receive from socket: %s", strerror(errno));
+      ASSERT_LOG(payload_size == hci_evt_parameter_total_length,
+                 "malformed HCI event total parameter size received: %zu != %d", payload_size,
+                 hci_evt_parameter_total_length);
+
+      HciPacket receivedHciPacket;
+      receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciEvtHeaderSize + payload_size);
+      btsnoop_logger_->capture(receivedHciPacket, SnoopLogger::Direction::INCOMING,
+                               SnoopLogger::PacketType::EVT);
+      incoming_packet_callback_->hciEventReceived(receivedHciPacket);
+    }
+
+    if (buf[0] == kH4Acl) {
+      RUN_NO_INTR(received_size = recv(sock_fd_, buf + kH4HeaderSize, kHciAclHeaderSize, 0));
+      ASSERT_LOG(received_size != -1, "Can't receive from socket: %s", strerror(errno));
+      ASSERT_LOG(received_size == kHciAclHeaderSize, "malformed ACL header received");
+
+      uint16_t hci_acl_data_total_length = buf[4] * 256 + buf[3];
+      int payload_size;
+      RUN_NO_INTR(payload_size = recv(sock_fd_, buf + kH4HeaderSize + kHciAclHeaderSize, hci_acl_data_total_length, 0));
+      ASSERT_LOG(payload_size != -1, "Can't receive from socket: %s", strerror(errno));
+      ASSERT_LOG(payload_size == hci_acl_data_total_length, "malformed ACL length received: %d != %d", payload_size,
+                 hci_acl_data_total_length);
+      ASSERT_LOG(hci_acl_data_total_length <= kBufSize - kH4HeaderSize - kHciAclHeaderSize, "packet too long");
+
+      HciPacket receivedHciPacket;
+      receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciAclHeaderSize + payload_size);
+      btsnoop_logger_->capture(receivedHciPacket, SnoopLogger::Direction::INCOMING,
+                               SnoopLogger::PacketType::ACL);
+      incoming_packet_callback_->aclDataReceived(receivedHciPacket);
+    }
+
+    if (buf[0] == kH4Sco) {
+      RUN_NO_INTR(received_size = recv(sock_fd_, buf + kH4HeaderSize, kHciScoHeaderSize, 0));
+      ASSERT_LOG(received_size != -1, "Can't receive from socket: %s", strerror(errno));
+      ASSERT_LOG(received_size == kHciScoHeaderSize, "malformed SCO header received");
+
+      uint8_t hci_sco_data_total_length = buf[3];
+      int payload_size;
+      RUN_NO_INTR(payload_size = recv(sock_fd_, buf + kH4HeaderSize + kHciScoHeaderSize, hci_sco_data_total_length, 0));
+      ASSERT_LOG(payload_size != -1, "Can't receive from socket: %s", strerror(errno));
+      ASSERT_LOG(payload_size == hci_sco_data_total_length, "malformed SCO packet received: size mismatch");
+
+      HciPacket receivedHciPacket;
+      receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciScoHeaderSize + payload_size);
+      btsnoop_logger_->capture(receivedHciPacket, SnoopLogger::Direction::INCOMING,
+                               SnoopLogger::PacketType::SCO);
+      incoming_packet_callback_->scoDataReceived(receivedHciPacket);
+    }
+    memset(buf, 0, kBufSize);
+  }
+};
+
+const ModuleFactory HciHal::Factory = ModuleFactory([]() {
+  return new HciHalHostRootcanal();
+});
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/hci_hal_host_rootcanal.h b/gd/hal/hci_hal_host_rootcanal.h
new file mode 100644
index 0000000..9b73b15
--- /dev/null
+++ b/gd/hal/hci_hal_host_rootcanal.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019 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 <string>
+
+namespace bluetooth {
+namespace hal {
+
+// Singleton object to store runtime configuration for rootcanal
+class HciHalHostRootcanalConfig {
+ public:
+  static HciHalHostRootcanalConfig* Get() {
+    static HciHalHostRootcanalConfig instance;
+    return &instance;
+  }
+
+  // Get the listening TCP port for rootcanal HCI socket
+  uint16_t GetPort() {
+    return port_;
+  }
+
+  // Set the listening TCP port for rootcanal HCI socket
+  void SetPort(uint16_t port) {
+    port_ = port;
+  }
+
+  // Get the server address for rootcanal HCI socket
+  std::string GetServerAddress() {
+    return server_address_;
+  }
+
+  // Set the server address for rootcanal HCI socket
+  void SetServerAddress(const std::string& address) {
+    server_address_ = address;
+  }
+
+ private:
+  HciHalHostRootcanalConfig() = default;
+  uint16_t port_ = 6402;                      // Default server TCP port
+  std::string server_address_ = "127.0.0.1";  // Default server address
+};
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/hci_hal_host_rootcanal_test.cc b/gd/hal/hci_hal_host_rootcanal_test.cc
new file mode 100644
index 0000000..ae98737
--- /dev/null
+++ b/gd/hal/hci_hal_host_rootcanal_test.cc
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2019 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 "hal/hci_hal_host_rootcanal.h"
+#include "hal/hci_hal.h"
+
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <cstring>
+#include <queue>
+#include <thread>
+#include <utility>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "os/log.h"
+#include "os/thread.h"
+#include "os/utils.h"
+
+using ::bluetooth::os::Thread;
+
+namespace bluetooth {
+namespace hal {
+namespace {
+
+uint16_t kTestPort = 6537;
+
+constexpr uint8_t kH4Command = 0x01;
+constexpr uint8_t kH4Acl = 0x02;
+constexpr uint8_t kH4Sco = 0x03;
+constexpr uint8_t kH4Event = 0x04;
+
+using H4Packet = std::vector<uint8_t>;
+
+std::queue<std::pair<uint8_t, HciPacket>> incoming_packets_queue_;
+
+class TestHciHalCallbacks : public HciHalCallbacks {
+ public:
+  void hciEventReceived(HciPacket packet) override {
+    incoming_packets_queue_.emplace(kH4Event, packet);
+  }
+
+  void aclDataReceived(HciPacket packet) override {
+    incoming_packets_queue_.emplace(kH4Acl, packet);
+  }
+
+  void scoDataReceived(HciPacket packet) override {
+    incoming_packets_queue_.emplace(kH4Sco, packet);
+  }
+};
+
+// An implementation of rootcanal desktop HCI server which listens on localhost:kListeningPort
+class FakeRootcanalDesktopHciServer {
+ public:
+  FakeRootcanalDesktopHciServer() {
+    struct sockaddr_in listen_address;
+    socklen_t sockaddr_in_size = sizeof(struct sockaddr_in);
+    memset(&listen_address, 0, sockaddr_in_size);
+
+    RUN_NO_INTR(listen_fd_ = socket(AF_INET, SOCK_STREAM, 0));
+    if (listen_fd_ < 0) {
+      LOG_WARN("Error creating socket for test channel.");
+      return;
+    }
+
+    listen_address.sin_family = AF_INET;
+    listen_address.sin_port = htons(HciHalHostRootcanalConfig::Get()->GetPort());
+    listen_address.sin_addr.s_addr = htonl(INADDR_ANY);
+
+    if (bind(listen_fd_, reinterpret_cast<sockaddr*>(&listen_address), sockaddr_in_size) < 0) {
+      LOG_WARN("Error binding test channel listener socket to address.");
+      close(listen_fd_);
+      return;
+    }
+
+    if (listen(listen_fd_, 1) < 0) {
+      LOG_WARN("Error listening for test channel.");
+      close(listen_fd_);
+      return;
+    }
+  }
+
+  ~FakeRootcanalDesktopHciServer() {
+    close(listen_fd_);
+  }
+
+  int Accept() {
+    int accept_fd;
+
+    RUN_NO_INTR(accept_fd = accept(listen_fd_, nullptr, nullptr));
+
+    int flags = fcntl(accept_fd, F_GETFL, NULL);
+    int ret = fcntl(accept_fd, F_SETFL, flags | O_NONBLOCK);
+    if (ret == -1) {
+      LOG_ERROR("Can't fcntl");
+      return -1;
+    }
+
+    if (accept_fd < 0) {
+      LOG_WARN("Error accepting test channel connection errno=%d (%s).", errno, strerror(errno));
+
+      if (errno != EAGAIN && errno != EWOULDBLOCK) {
+        LOG_ERROR("Closing listen_fd_ (won't try again).");
+        close(listen_fd_);
+        return -1;
+      }
+    }
+
+    return accept_fd;
+  }
+
+ private:
+  int listen_fd_ = -1;
+};
+
+class HciHalRootcanalTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new Thread("test_thread", Thread::Priority::NORMAL);
+
+    HciHalHostRootcanalConfig::Get()->SetPort(kTestPort);
+    fake_server_ = new FakeRootcanalDesktopHciServer;
+    hal_ = fake_registry_.Start<HciHal>(thread_);
+    hal_->registerIncomingPacketCallback(&callbacks_);
+    fake_server_socket_ = fake_server_->Accept();  // accept() after client is connected to avoid blocking
+    std::queue<std::pair<uint8_t, HciPacket>> empty;
+    std::swap(incoming_packets_queue_, empty);
+  }
+
+  void TearDown() override {
+    fake_registry_.StopAll();
+    close(fake_server_socket_);
+    delete fake_server_;
+    delete thread_;
+  }
+
+  void SetFakeServerSocketToBlocking() {
+    int flags = fcntl(fake_server_socket_, F_GETFL, NULL);
+    int ret = fcntl(fake_server_socket_, F_SETFL, flags & ~O_NONBLOCK);
+    EXPECT_NE(ret, -1) << "Can't set accept fd to blocking";
+  }
+
+  FakeRootcanalDesktopHciServer* fake_server_ = nullptr;
+  HciHal* hal_ = nullptr;
+  ModuleRegistry fake_registry_;
+  TestHciHalCallbacks callbacks_;
+  int fake_server_socket_ = -1;
+  Thread* thread_;
+};
+
+void check_packet_equal(std::pair<uint8_t, HciPacket> hci_packet1_type_data_pair, H4Packet h4_packet2) {
+  auto packet1_hci_size = hci_packet1_type_data_pair.second.size();
+  EXPECT_EQ(packet1_hci_size + 1, h4_packet2.size());
+  EXPECT_EQ(hci_packet1_type_data_pair.first, h4_packet2[0]);
+  EXPECT_EQ(memcmp(hci_packet1_type_data_pair.second.data(), h4_packet2.data() + 1, packet1_hci_size), 0);
+}
+
+HciPacket make_sample_hci_cmd_pkt(uint8_t parameter_total_length) {
+  HciPacket pkt;
+  pkt.assign(2 + 1 + parameter_total_length, 0x01);
+  pkt[2] = parameter_total_length;
+  return pkt;
+}
+
+HciPacket make_sample_hci_acl_pkt(uint8_t payload_size) {
+  HciPacket pkt;
+  pkt.assign(2 + 2 + payload_size, 0x01);
+  pkt[2] = payload_size;
+  return pkt;
+}
+
+HciPacket make_sample_hci_sco_pkt(uint8_t payload_size) {
+  HciPacket pkt;
+  pkt.assign(3 + payload_size, 0x01);
+  pkt[2] = payload_size;
+  return pkt;
+}
+
+H4Packet make_sample_h4_evt_pkt(uint8_t parameter_total_length) {
+  H4Packet pkt;
+  pkt.assign(1 + 1 + 1 + parameter_total_length, 0x01);
+  pkt[0] = kH4Event;
+  pkt[2] = parameter_total_length;
+  return pkt;
+}
+
+HciPacket make_sample_h4_acl_pkt(uint8_t payload_size) {
+  HciPacket pkt;
+  pkt.assign(1 + 2 + 2 + payload_size, 0x01);
+  pkt[0] = kH4Acl;
+  pkt[3] = payload_size;
+  pkt[4] = 0;
+  return pkt;
+}
+
+HciPacket make_sample_h4_sco_pkt(uint8_t payload_size) {
+  HciPacket pkt;
+  pkt.assign(1 + 3 + payload_size, 0x01);
+  pkt[0] = kH4Sco;
+  pkt[3] = payload_size;
+  return pkt;
+}
+
+TEST_F(HciHalRootcanalTest, init_and_close) {}
+
+TEST_F(HciHalRootcanalTest, receive_hci_evt) {
+  H4Packet incoming_packet = make_sample_h4_evt_pkt(3);
+  write(fake_server_socket_, incoming_packet.data(), incoming_packet.size());
+  while (incoming_packets_queue_.size() != 1) {
+  }
+  auto packet = incoming_packets_queue_.front();
+  incoming_packets_queue_.pop();
+  check_packet_equal(packet, incoming_packet);
+}
+
+TEST_F(HciHalRootcanalTest, receive_hci_acl) {
+  H4Packet incoming_packet = make_sample_h4_acl_pkt(3);
+  write(fake_server_socket_, incoming_packet.data(), incoming_packet.size());
+  while (incoming_packets_queue_.size() != 1) {
+  }
+  auto packet = incoming_packets_queue_.front();
+  incoming_packets_queue_.pop();
+  check_packet_equal(packet, incoming_packet);
+}
+
+TEST_F(HciHalRootcanalTest, receive_hci_sco) {
+  H4Packet incoming_packet = make_sample_h4_sco_pkt(3);
+  write(fake_server_socket_, incoming_packet.data(), incoming_packet.size());
+  while (incoming_packets_queue_.size() != 1) {
+  }
+  auto packet = incoming_packets_queue_.front();
+  incoming_packets_queue_.pop();
+  check_packet_equal(packet, incoming_packet);
+}
+
+TEST_F(HciHalRootcanalTest, receive_two_hci_evts) {
+  H4Packet incoming_packet = make_sample_h4_evt_pkt(3);
+  H4Packet incoming_packet2 = make_sample_h4_evt_pkt(5);
+  write(fake_server_socket_, incoming_packet.data(), incoming_packet.size());
+  write(fake_server_socket_, incoming_packet2.data(), incoming_packet2.size());
+  while (incoming_packets_queue_.size() != 2) {
+  }
+  auto packet = incoming_packets_queue_.front();
+  incoming_packets_queue_.pop();
+  check_packet_equal(packet, incoming_packet);
+  packet = incoming_packets_queue_.front();
+  incoming_packets_queue_.pop();
+  check_packet_equal(packet, incoming_packet2);
+}
+
+TEST_F(HciHalRootcanalTest, receive_evt_and_acl) {
+  H4Packet incoming_packet = make_sample_h4_evt_pkt(3);
+  H4Packet incoming_packet2 = make_sample_h4_acl_pkt(5);
+  write(fake_server_socket_, incoming_packet.data(), incoming_packet.size());
+  write(fake_server_socket_, incoming_packet2.data(), incoming_packet2.size());
+  while (incoming_packets_queue_.size() != 2) {
+  }
+  auto packet = incoming_packets_queue_.front();
+  incoming_packets_queue_.pop();
+  check_packet_equal(packet, incoming_packet);
+  packet = incoming_packets_queue_.front();
+  incoming_packets_queue_.pop();
+  check_packet_equal(packet, incoming_packet2);
+}
+
+TEST_F(HciHalRootcanalTest, receive_multiple_acl_batch) {
+  H4Packet incoming_packet = make_sample_h4_acl_pkt(5);
+  int num_packets = 1000;
+  for (int i = 0; i < num_packets; i++) {
+    write(fake_server_socket_, incoming_packet.data(), incoming_packet.size());
+  }
+  while (incoming_packets_queue_.size() != num_packets) {
+  }
+  for (int i = 0; i < num_packets; i++) {
+    auto packet = incoming_packets_queue_.front();
+    incoming_packets_queue_.pop();
+    check_packet_equal(packet, incoming_packet);
+  }
+}
+
+TEST_F(HciHalRootcanalTest, receive_multiple_acl_sequential) {
+  H4Packet incoming_packet = make_sample_h4_acl_pkt(5);
+  int num_packets = 1000;
+  for (int i = 0; i < num_packets; i++) {
+    write(fake_server_socket_, incoming_packet.data(), incoming_packet.size());
+    while (incoming_packets_queue_.empty()) {
+    }
+    auto packet = incoming_packets_queue_.front();
+    incoming_packets_queue_.pop();
+    check_packet_equal(packet, incoming_packet);
+  }
+}
+
+TEST_F(HciHalRootcanalTest, send_hci_cmd) {
+  uint8_t hci_cmd_param_size = 2;
+  HciPacket hci_data = make_sample_hci_cmd_pkt(hci_cmd_param_size);
+  hal_->sendHciCommand(hci_data);
+  H4Packet read_buf(1 + 2 + 1 + hci_cmd_param_size);
+  SetFakeServerSocketToBlocking();
+  auto size_read = read(fake_server_socket_, read_buf.data(), read_buf.size());
+
+  EXPECT_EQ(size_read, 1 + hci_data.size());
+  check_packet_equal({kH4Command, hci_data}, read_buf);
+}
+
+TEST_F(HciHalRootcanalTest, send_acl) {
+  uint8_t acl_payload_size = 200;
+  HciPacket acl_packet = make_sample_hci_acl_pkt(acl_payload_size);
+  hal_->sendAclData(acl_packet);
+  H4Packet read_buf(1 + 2 + 2 + acl_payload_size);
+  SetFakeServerSocketToBlocking();
+  auto size_read = read(fake_server_socket_, read_buf.data(), read_buf.size());
+
+  EXPECT_EQ(size_read, 1 + acl_packet.size());
+  check_packet_equal({kH4Acl, acl_packet}, read_buf);
+}
+
+TEST_F(HciHalRootcanalTest, send_sco) {
+  uint8_t sco_payload_size = 200;
+  HciPacket sco_packet = make_sample_hci_sco_pkt(sco_payload_size);
+  hal_->sendScoData(sco_packet);
+  H4Packet read_buf(1 + 3 + sco_payload_size);
+  SetFakeServerSocketToBlocking();
+  auto size_read = read(fake_server_socket_, read_buf.data(), read_buf.size());
+
+  EXPECT_EQ(size_read, 1 + sco_packet.size());
+  check_packet_equal({kH4Sco, sco_packet}, read_buf);
+}
+
+TEST_F(HciHalRootcanalTest, send_multiple_acl_batch) {
+  uint8_t acl_payload_size = 200;
+  int num_packets = 1000;
+  HciPacket acl_packet = make_sample_hci_acl_pkt(acl_payload_size);
+  for (int i = 0; i < num_packets; i++) {
+    hal_->sendAclData(acl_packet);
+  }
+  H4Packet read_buf(1 + 2 + 2 + acl_payload_size);
+  SetFakeServerSocketToBlocking();
+  for (int i = 0; i < num_packets; i++) {
+    auto size_read = read(fake_server_socket_, read_buf.data(), read_buf.size());
+    EXPECT_EQ(size_read, 1 + acl_packet.size());
+    check_packet_equal({kH4Acl, acl_packet}, read_buf);
+  }
+}
+
+TEST_F(HciHalRootcanalTest, send_multiple_acl_sequential) {
+  uint8_t acl_payload_size = 200;
+  int num_packets = 1000;
+  HciPacket acl_packet = make_sample_hci_acl_pkt(acl_payload_size);
+  SetFakeServerSocketToBlocking();
+  for (int i = 0; i < num_packets; i++) {
+    hal_->sendAclData(acl_packet);
+    H4Packet read_buf(1 + 2 + 2 + acl_payload_size);
+    auto size_read = read(fake_server_socket_, read_buf.data(), read_buf.size());
+    EXPECT_EQ(size_read, 1 + acl_packet.size());
+    check_packet_equal({kH4Acl, acl_packet}, read_buf);
+  }
+}
+
+}  // namespace
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/snoop_logger.cc b/gd/hal/snoop_logger.cc
new file mode 100644
index 0000000..874fb31
--- /dev/null
+++ b/gd/hal/snoop_logger.cc
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2019 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 "hal/snoop_logger.h"
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <bitset>
+#include <chrono>
+
+#include "os/log.h"
+
+namespace bluetooth {
+namespace hal {
+
+namespace {
+typedef struct {
+  uint32_t length_original;
+  uint32_t length_captured;
+  uint32_t flags;
+  uint32_t dropped_packets;
+  uint64_t timestamp;
+  uint8_t type;
+} __attribute__((__packed__)) btsnoop_packet_header_t;
+
+typedef struct {
+  uint8_t identification_pattern[8];
+  uint32_t version_number;
+  uint32_t datalink_type;
+} __attribute__((__packed__)) btsnoop_file_header_t;
+
+constexpr uint64_t BTSNOOP_EPOCH_DELTA = 0x00dcddb30f2f8000ULL;
+
+constexpr uint32_t kBytesToTest = 0x12345678;
+constexpr uint8_t kFirstByte = (const uint8_t&)kBytesToTest;
+constexpr bool isLittleEndian = kFirstByte == 0x78;
+constexpr bool isBigEndian = kFirstByte == 0x12;
+static_assert(isLittleEndian || isBigEndian && isLittleEndian != isBigEndian);
+
+constexpr uint32_t BTSNOOP_VERSION_NUMBER = isLittleEndian ? 0x01000000 : 1;
+constexpr uint32_t BTSNOOP_DATALINK_TYPE =
+    isLittleEndian ? 0xea030000 : 0x03ea;  // Datalink Type code for HCI UART (H4) is 1002
+uint64_t htonll(uint64_t ll) {
+  if constexpr (isLittleEndian) {
+    return static_cast<uint64_t>(htonl(ll & 0xffffffff)) << 32 | htonl(ll >> 32);
+  } else {
+    return ll;
+  }
+}
+
+constexpr btsnoop_file_header_t BTSNOOP_FILE_HEADER = {
+    .identification_pattern = {'b', 't', 's', 'n', 'o', 'o', 'p', 0x00},
+    .version_number = BTSNOOP_VERSION_NUMBER,
+    .datalink_type = BTSNOOP_DATALINK_TYPE};
+}  // namespace
+
+SnoopLogger::SnoopLogger() {
+  bool file_exists;
+  {
+    std::ifstream btsnoop_istream(file_path);
+    file_exists = btsnoop_istream.is_open();
+  }
+  btsnoop_ostream_.open(file_path, std::ios::binary | std::ios::app | std::ios::out);
+  if (!file_exists) {
+    LOG_INFO("Creating new BTSNOOP");
+    btsnoop_ostream_.write(reinterpret_cast<const char*>(&BTSNOOP_FILE_HEADER), sizeof(btsnoop_file_header_t));
+  } else {
+    LOG_INFO("Appending to old BTSNOOP");
+  }
+}
+
+void SnoopLogger::SetFilePath(const std::string& filename) {
+  file_path = filename;
+}
+
+void SnoopLogger::capture(const HciPacket& packet, Direction direction, PacketType type) {
+  uint64_t timestamp_us =
+      std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch())
+          .count();
+  std::lock_guard<std::mutex> lock(file_mutex_);
+  std::bitset<32> flags = 0;
+  switch (type) {
+    case PacketType::CMD:
+      flags.set(0, false);
+      flags.set(1, true);
+      break;
+    case PacketType::ACL:
+      flags.set(0, direction == Direction::INCOMING);
+      flags.set(1, false);
+      break;
+    case PacketType::SCO:
+      flags.set(0, direction == Direction::INCOMING);
+      flags.set(1, false);
+      break;
+    case PacketType::EVT:
+      flags.set(0, true);
+      flags.set(1, true);
+      break;
+  }
+  uint32_t length = packet.size() + /* type byte */ 1;
+  btsnoop_packet_header_t header = {.length_original = htonl(length),
+                                    .length_captured = htonl(length),
+                                    .flags = htonl(static_cast<uint32_t>(flags.to_ulong())),
+                                    .dropped_packets = 0,
+                                    .timestamp = htonll(timestamp_us + BTSNOOP_EPOCH_DELTA),
+                                    .type = static_cast<uint8_t>(type)};
+  btsnoop_ostream_.write(reinterpret_cast<const char*>(&header), sizeof(btsnoop_packet_header_t));
+  btsnoop_ostream_.write(reinterpret_cast<const char*>(packet.data()), packet.size());
+}
+
+void SnoopLogger::ListDependencies(ModuleList* list) {
+  // We have no dependencies
+}
+
+void SnoopLogger::Start() {}
+
+void SnoopLogger::Stop() {}
+
+std::string SnoopLogger::file_path = SnoopLogger::DefaultFilePath;
+
+const ModuleFactory SnoopLogger::Factory = ModuleFactory([]() {
+  return new SnoopLogger();
+});
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/snoop_logger.h b/gd/hal/snoop_logger.h
new file mode 100644
index 0000000..4e0b90d
--- /dev/null
+++ b/gd/hal/snoop_logger.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2019 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 <fstream>
+#include <iostream>
+#include <mutex>
+#include <string>
+
+#include "hal/hci_hal.h"
+#include "module.h"
+
+namespace bluetooth {
+namespace hal {
+
+class SnoopLogger : public ::bluetooth::Module {
+ public:
+  static const ModuleFactory Factory;
+
+  // Each transport using SnoopLogger should define its own DefaultFilepath
+  static const std::string DefaultFilePath;
+  // Set File Path before module is started to ensure all packets are written to the right file
+  static void SetFilePath(const std::string& filename);
+
+  enum class PacketType {
+    CMD = 1,
+    ACL = 2,
+    SCO = 3,
+    EVT = 4,
+  };
+
+  enum class Direction {
+    INCOMING,
+    OUTGOING,
+  };
+
+  void capture(const HciPacket& packet, Direction direction, PacketType type);
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+
+ private:
+  SnoopLogger();
+  static std::string file_path;
+  std::ofstream btsnoop_ostream_;
+  std::mutex file_mutex_;
+};
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hci/Android.bp b/gd/hci/Android.bp
new file mode 100644
index 0000000..69589ca
--- /dev/null
+++ b/gd/hci/Android.bp
@@ -0,0 +1,14 @@
+filegroup {
+    name: "BluetoothHciSources",
+    srcs: [
+        "hci_layer.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothHciTestSources",
+    srcs: [
+        "acl_builder_test.cc",
+        "hci_layer_test.cc",
+    ],
+}
diff --git a/gd/hci/acl_builder_test.cc b/gd/hci/acl_builder_test.cc
new file mode 100644
index 0000000..7fc59c6
--- /dev/null
+++ b/gd/hci/acl_builder_test.cc
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2018 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/hci_packets.h"
+
+#include <gtest/gtest.h>
+#include <memory>
+
+#include "os/log.h"
+#include "packet/bit_inserter.h"
+#include "packet/raw_builder.h"
+
+using bluetooth::packet::BitInserter;
+using bluetooth::packet::RawBuilder;
+using std::vector;
+
+namespace {
+vector<uint8_t> information_request = {
+    0xfe, 0x2e, 0x0a, 0x00, 0x06, 0x00, 0x01, 0x00, 0x0a, 0x02, 0x02, 0x00, 0x02, 0x00,
+};
+// 0x00, 0x01, 0x02, 0x03, ...
+vector<uint8_t> counting_bytes;
+// 0xFF, 0xFE, 0xFD, 0xFC, ...
+vector<uint8_t> counting_down_bytes;
+const size_t count_size = 0x8;
+
+}  // namespace
+
+namespace bluetooth {
+namespace hci {
+
+class AclBuilderTest : public ::testing::Test {
+ public:
+  AclBuilderTest() {
+    counting_bytes.reserve(count_size);
+    counting_down_bytes.reserve(count_size);
+    for (size_t i = 0; i < count_size; i++) {
+      counting_bytes.push_back(i);
+      counting_down_bytes.push_back(~i);
+    }
+  }
+  ~AclBuilderTest() = default;
+};
+
+TEST(AclBuilderTest, buildAclCount) {
+  uint16_t handle = 0x0314;
+  PacketBoundaryFlag packet_boundary_flag = PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE;
+  BroadcastFlag broadcast_flag = BroadcastFlag::ACTIVE_SLAVE_BROADCAST;
+
+  std::unique_ptr<RawBuilder> count_payload = std::make_unique<RawBuilder>();
+  count_payload->AddOctets(counting_bytes);
+  ASSERT_EQ(counting_bytes.size(), count_payload->size());
+
+  std::unique_ptr<AclPacketBuilder> count_packet =
+      AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(count_payload));
+
+  ASSERT_EQ(counting_bytes.size() + 4, count_packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> count_packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*count_packet_bytes);
+  count_packet->Serialize(it);
+
+  PacketView<true> count_packet_bytes_view(count_packet_bytes);
+  AclPacketView count_packet_view = AclPacketView::Create(count_packet_bytes_view);
+  ASSERT_TRUE(count_packet_view.IsValid());
+
+  ASSERT_EQ(handle, count_packet_view.GetHandle());
+  ASSERT_EQ(packet_boundary_flag, count_packet_view.GetPacketBoundaryFlag());
+  ASSERT_EQ(broadcast_flag, count_packet_view.GetBroadcastFlag());
+  PacketView<true> count_view = count_packet_view.GetPayload();
+
+  ASSERT_EQ(count_view.size(), counting_bytes.size());
+  for (size_t i = 0; i < count_view.size(); i++) {
+    ASSERT_EQ(count_view[i], counting_bytes[i]);
+  }
+}
+
+TEST(AclBuilderTest, buildAclCountInverted) {
+  uint16_t handle = 0x0304;
+  PacketBoundaryFlag packet_boundary_flag = PacketBoundaryFlag::CONTINUING_FRAGMENT;
+  BroadcastFlag broadcast_flag = BroadcastFlag::POINT_TO_POINT;
+
+  std::unique_ptr<RawBuilder> counting_down_bytes_payload = std::make_unique<RawBuilder>();
+  counting_down_bytes_payload->AddOctets(counting_down_bytes);
+  ASSERT_EQ(counting_down_bytes.size(), counting_down_bytes_payload->size());
+
+  std::unique_ptr<AclPacketBuilder> counting_down_bytes_packet =
+      AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(counting_down_bytes_payload));
+
+  ASSERT_EQ(counting_down_bytes.size() + 4, counting_down_bytes_packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> counting_down_bytes_packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*counting_down_bytes_packet_bytes);
+  counting_down_bytes_packet->Serialize(it);
+  PacketView<true> counting_down_bytes_packet_bytes_view(counting_down_bytes_packet_bytes);
+  AclPacketView counting_down_bytes_packet_view = AclPacketView::Create(counting_down_bytes_packet_bytes_view);
+  ASSERT_TRUE(counting_down_bytes_packet_view.IsValid());
+
+  ASSERT_EQ(handle, counting_down_bytes_packet_view.GetHandle());
+  ASSERT_EQ(packet_boundary_flag, counting_down_bytes_packet_view.GetPacketBoundaryFlag());
+  ASSERT_EQ(broadcast_flag, counting_down_bytes_packet_view.GetBroadcastFlag());
+  PacketView<true> counting_down_bytes_view = counting_down_bytes_packet_view.GetPayload();
+
+  ASSERT_EQ(counting_down_bytes_view.size(), counting_down_bytes.size());
+  for (size_t i = 0; i < counting_down_bytes_view.size(); i++) {
+    ASSERT_EQ(counting_down_bytes_view[i], counting_down_bytes[i]);
+  }
+}
+
+TEST(AclBuilderTest, buildInformationRequest) {
+  uint16_t handle = 0x0efe;
+  PacketBoundaryFlag packet_boundary_flag = PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE;
+  BroadcastFlag broadcast_flag = BroadcastFlag::POINT_TO_POINT;
+
+  std::vector<uint8_t> payload_bytes(information_request.begin() + 4, information_request.end());
+  std::unique_ptr<RawBuilder> payload = std::make_unique<RawBuilder>();
+  payload->AddOctets(payload_bytes);
+  ASSERT_EQ(payload_bytes.size(), payload->size());
+
+  std::unique_ptr<AclPacketBuilder> packet =
+      AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(payload));
+
+  ASSERT_EQ(information_request.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+  PacketView<true> packet_bytes_view(packet_bytes);
+  AclPacketView packet_view = AclPacketView::Create(packet_bytes_view);
+  ASSERT_TRUE(packet_view.IsValid());
+
+  ASSERT_EQ(packet_bytes->size(), information_request.size());
+  for (size_t i = 0; i < packet_bytes->size(); i++) {
+    ASSERT_EQ((*packet_bytes)[i], information_request[i]);
+  }
+
+  ASSERT_EQ(handle, packet_view.GetHandle());
+  ASSERT_EQ(packet_boundary_flag, packet_view.GetPacketBoundaryFlag());
+  ASSERT_EQ(broadcast_flag, packet_view.GetBroadcastFlag());
+  PacketView<true> payload_view = packet_view.GetPayload();
+
+  ASSERT_EQ(payload_view.size(), payload_bytes.size());
+  for (size_t i = 0; i < payload_view.size(); i++) {
+    ASSERT_EQ(payload_view[i], payload_bytes[i]);
+  }
+
+  ASSERT_EQ(packet_view.size(), information_request.size());
+  for (size_t i = 0; i < packet_view.size(); i++) {
+    ASSERT_EQ(packet_view[i], information_request[i]);
+  }
+}
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/hci_layer.cc b/gd/hci/hci_layer.cc
new file mode 100644
index 0000000..46a35eb
--- /dev/null
+++ b/gd/hci/hci_layer.cc
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2019 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/hci_layer.h"
+
+#include "packet/packet_builder.h"
+
+namespace {
+using bluetooth::hci::CommandCompleteView;
+using bluetooth::hci::CommandPacketBuilder;
+using bluetooth::hci::CommandStatusView;
+using bluetooth::hci::EventPacketView;
+using bluetooth::os::Handler;
+
+class EventHandler {
+ public:
+  EventHandler() : event_handler(), handler(nullptr) {}
+  EventHandler(std::function<void(EventPacketView)> on_event, Handler* on_event_handler)
+      : event_handler(on_event), handler(on_event_handler) {}
+  std::function<void(EventPacketView)> event_handler;
+  Handler* handler;
+};
+
+class CommandQueueEntry {
+ public:
+  CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet,
+                    std::function<void(CommandStatusView)> on_status_function,
+                    std::function<void(CommandCompleteView)> on_complete_function, Handler* handler)
+      : command(std::move(command_packet)), on_status(on_status_function), on_complete(on_complete_function),
+        caller_handler(handler) {}
+
+  std::unique_ptr<CommandPacketBuilder> command;
+  std::function<void(CommandStatusView)> on_status;
+  std::function<void(CommandCompleteView)> on_complete;
+  Handler* caller_handler;
+};
+}  // namespace
+
+namespace bluetooth {
+namespace hci {
+
+using common::Address;
+using common::BidiQueue;
+using common::BidiQueueEnd;
+using os::Handler;
+
+struct HciLayer::impl : public hal::HciHalCallbacks {
+  impl(HciLayer& module) : hal_(nullptr), module_(module) {
+    RegisterEventHandler(EventCode::COMMAND_COMPLETE, [this](EventPacketView event) { CommandCompleteCallback(event); },
+                         module_.GetHandler());
+    RegisterEventHandler(EventCode::COMMAND_STATUS, [this](EventPacketView event) { CommandStatusCallback(event); },
+                         module_.GetHandler());
+  }
+
+  void Start(hal::HciHal* hal) {
+    hal_ = hal;
+    hal_->registerIncomingPacketCallback(this);
+
+    send_acl_ = [this](std::unique_ptr<hci::BasePacketBuilder> packet) {
+      std::vector<uint8_t> bytes;
+      BitInserter bi(bytes);
+      packet->Serialize(bi);
+      hal_->sendAclData(bytes);
+    };
+    send_sco_ = [this](std::unique_ptr<hci::BasePacketBuilder> packet) {
+      std::vector<uint8_t> bytes;
+      BitInserter bi(bytes);
+      packet->Serialize(bi);
+      hal_->sendScoData(bytes);
+    };
+    auto queue_end = acl_queue_.GetDownEnd();
+    Handler* handler = module_.GetHandler();
+    queue_end->RegisterDequeue(handler, [queue_end, this]() { send_acl_(queue_end->TryDequeue()); });
+  }
+
+  void Stop() {
+    acl_queue_.GetDownEnd()->UnregisterDequeue();
+    hal_ = nullptr;
+  }
+
+  void CommandStatusCallback(EventPacketView event) {
+    CommandStatusView status_view = CommandStatusView::Create(event);
+    ASSERT(status_view.IsValid());
+    if (command_queue_.size() == 0) {
+      ASSERT_LOG(status_view.GetCommandOpCode() == OpCode::NONE, "Unexpected status event with OpCode 0x%02hx",
+                 status_view.GetCommandOpCode());
+      return;
+    }
+    // TODO: Check whether this is the CommandOpCode we're looking for.
+    auto caller_handler = command_queue_.front().caller_handler;
+    auto on_status = command_queue_.front().on_status;
+    caller_handler->Post([on_status, status_view]() { on_status(status_view); });
+    command_queue_.pop();
+  }
+
+  void CommandCompleteCallback(EventPacketView event) {
+    CommandCompleteView complete_view = CommandCompleteView::Create(event);
+    ASSERT(complete_view.IsValid());
+    if (command_queue_.size() == 0) {
+      ASSERT_LOG(complete_view.GetCommandOpCode() == OpCode::NONE,
+                 "Unexpected command complete event with OpCode 0x%02hx", complete_view.GetCommandOpCode());
+      return;
+    }
+    // TODO: Check whether this is the CommandOpCode we're looking for.
+    auto caller_handler = command_queue_.front().caller_handler;
+    auto on_complete = command_queue_.front().on_complete;
+    caller_handler->Post([on_complete, complete_view]() { on_complete(complete_view); });
+    command_queue_.pop();
+  }
+
+  void hciEventReceived(hal::HciPacket event_bytes) override {
+    auto packet = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(event_bytes));
+    EventPacketView event = EventPacketView::Create(packet);
+    ASSERT(event.IsValid());
+    EventCode event_code = event.GetEventCode();
+
+    Handler* hci_handler = module_.GetHandler();
+    hci_handler->Post([this, event, event_code]() {
+      ASSERT_LOG(event_handlers_.find(event_code) != event_handlers_.end(), "Unhandled event of type 0x%02hhx",
+                 event.GetEventCode());
+      auto& registered_handler = event_handlers_[event_code].event_handler;
+      event_handlers_[event_code].handler->Post([event, registered_handler]() { registered_handler(event); });
+    });
+    // TODO: Credits
+  }
+
+  void aclDataReceived(hal::HciPacket data_bytes) override {
+    module_.GetHandler()->Post([this, data_bytes]() {
+      auto queue_end = acl_queue_.GetDownEnd();
+      Handler* hci_handler = module_.GetHandler();
+      queue_end->RegisterEnqueue(hci_handler, [queue_end, data_bytes]() {
+        auto packet = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(data_bytes));
+        AclPacketView acl2 = AclPacketView::Create(packet);
+        queue_end->UnregisterEnqueue();
+        return std::make_unique<AclPacketView>(acl2);
+      });
+    });
+  }
+
+  void scoDataReceived(hal::HciPacket data_bytes) override {
+    auto packet = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(data_bytes));
+    ScoPacketView sco = ScoPacketView::Create(packet);
+  }
+
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, std::function<void(CommandStatusView)> on_status,
+                      std::function<void(CommandCompleteView)> on_complete, Handler* handler) {
+    command_queue_.emplace(std::move(command), on_status, on_complete, handler);
+
+    if (command_queue_.size() == 1) {
+      std::vector<uint8_t> bytes;
+      BitInserter bi(bytes);
+      command_queue_.front().command->Serialize(bi);
+      hal_->sendHciCommand(bytes);
+    }
+  }
+
+  BidiQueueEnd<AclPacketBuilder, AclPacketView>* GetAclQueueEnd() {
+    return acl_queue_.GetUpEnd();
+  }
+
+  void RegisterEventHandler(EventCode event_code, std::function<void(EventPacketView)> event_handler,
+                            Handler* handler) {
+    ASSERT_LOG(event_handlers_.count(event_code) == 0, "Can not register a second handler for event_code %02hhx",
+               event_code);
+    EventHandler to_save(event_handler, handler);
+    event_handlers_[event_code] = to_save;
+  }
+
+  void UnregisterEventHandler(EventCode event_code) {
+    event_handlers_.erase(event_code);
+  }
+
+  // The HAL
+  hal::HciHal* hal_;
+
+  // A reference to the HciLayer module
+  HciLayer& module_;
+
+  // Conversion functions for sending bytes from Builders
+  std::function<void(std::unique_ptr<hci::BasePacketBuilder>)> send_acl_;
+  std::function<void(std::unique_ptr<hci::BasePacketBuilder>)> send_sco_;
+
+  // Command Handling
+  std::queue<CommandQueueEntry> command_queue_;
+
+  std::map<EventCode, EventHandler> event_handlers_;
+  OpCode waiting_command_;
+
+  // Acl packets
+  BidiQueue<AclPacketView, AclPacketBuilder> acl_queue_{3 /* TODO: Set queue depth */};
+};
+
+HciLayer::HciLayer() : impl_(std::make_unique<impl>(*this)) {}
+
+HciLayer::~HciLayer() {
+  impl_.reset();
+}
+
+void HciLayer::EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                              std::function<void(CommandStatusView)> on_status,
+                              std::function<void(CommandCompleteView)> on_complete, Handler* handler) {
+  impl_->EnqueueCommand(std::move(command), on_status, on_complete, handler);
+}
+
+common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* HciLayer::GetAclQueueEnd() {
+  return impl_->GetAclQueueEnd();
+}
+
+void HciLayer::RegisterEventHandler(EventCode event_code, std::function<void(EventPacketView)> event_handler,
+                                    Handler* handler) {
+  impl_->RegisterEventHandler(event_code, event_handler, handler);
+}
+
+void HciLayer::UnregisterEventHandler(EventCode event_code) {
+  impl_->UnregisterEventHandler(event_code);
+}
+
+const ModuleFactory HciLayer::Factory = ModuleFactory([]() { return new HciLayer(); });
+
+void HciLayer::ListDependencies(ModuleList* list) {
+  list->add<hal::HciHal>();
+}
+
+void HciLayer::Start() {
+  impl_->Start(GetDependency<hal::HciHal>());
+}
+
+void HciLayer::Stop() {
+  impl_->Stop();
+}
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/hci_layer.h b/gd/hci/hci_layer.h
new file mode 100644
index 0000000..a841a0d
--- /dev/null
+++ b/gd/hci/hci_layer.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 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 "common/address.h"
+#include "common/bidi_queue.h"
+#include "common/class_of_device.h"
+#include "hal/hci_hal.h"
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "os/utils.h"
+
+namespace bluetooth {
+namespace hci {
+
+class HciLayer : public Module {
+ public:
+  HciLayer();
+  virtual ~HciLayer();
+  DISALLOW_COPY_AND_ASSIGN(HciLayer);
+
+  virtual void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                              std::function<void(CommandStatusView)> on_status,
+                              std::function<void(CommandCompleteView)> on_complete, os::Handler* handler);
+
+  virtual common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* GetAclQueueEnd();
+
+  virtual void RegisterEventHandler(EventCode event_code, std::function<void(EventPacketView)> event_handler,
+                                    os::Handler* handler);
+
+  virtual void UnregisterEventHandler(EventCode event_code);
+
+  static const ModuleFactory Factory;
+
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+
+  void Stop() override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> impl_;
+};
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/hci_layer_test.cc b/gd/hci/hci_layer_test.cc
new file mode 100644
index 0000000..7644ad9
--- /dev/null
+++ b/gd/hci/hci_layer_test.cc
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2019 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/hci_layer.h"
+
+#include <gtest/gtest.h>
+#include <list>
+#include <memory>
+
+#include "hal/hci_hal.h"
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "os/log.h"
+#include "os/thread.h"
+#include "packet/bit_inserter.h"
+#include "packet/raw_builder.h"
+
+using bluetooth::os::Thread;
+using bluetooth::packet::BitInserter;
+using bluetooth::packet::RawBuilder;
+using std::vector;
+
+namespace {
+vector<uint8_t> information_request = {
+    0xfe, 0x2e, 0x0a, 0x00, 0x06, 0x00, 0x01, 0x00, 0x0a, 0x02, 0x02, 0x00, 0x02, 0x00,
+};
+// 0x00, 0x01, 0x02, 0x03, ...
+vector<uint8_t> counting_bytes;
+// 0xFF, 0xFE, 0xFD, 0xFC, ...
+vector<uint8_t> counting_down_bytes;
+const size_t count_size = 0x8;
+
+}  // namespace
+
+namespace bluetooth {
+namespace hci {
+
+class TestHciHal : public hal::HciHal {
+ public:
+  TestHciHal() : hal::HciHal() {}
+
+  virtual void registerIncomingPacketCallback(hal::HciHalCallbacks* callback) {
+    callbacks = callback;
+  }
+
+  virtual void sendHciCommand(hal::HciPacket command) {
+    outgoing_commands_.push_back(std::move(command));
+  }
+
+  virtual void sendAclData(hal::HciPacket data) {
+    outgoing_acl_.push_front(std::move(data));
+  }
+
+  virtual void sendScoData(hal::HciPacket data) {
+    outgoing_sco_.push_front(std::move(data));
+  }
+
+  hal::HciHalCallbacks* callbacks = nullptr;
+
+  PacketView<kLittleEndian> GetPacketView(hal::HciPacket data) {
+    auto shared = std::make_shared<std::vector<uint8_t>>(data);
+    return PacketView<kLittleEndian>(shared);
+  }
+
+  PacketView<kLittleEndian> GetSentCommand() {
+    while (outgoing_commands_.size() == 0)
+      ;
+    auto packetview = GetPacketView(std::move(outgoing_commands_.front()));
+    outgoing_commands_.pop_front();
+    return packetview;
+  }
+
+  PacketView<kLittleEndian> GetSentAcl() {
+    while (outgoing_acl_.size() == 0)
+      ;
+    auto packetview = GetPacketView(std::move(outgoing_acl_.front()));
+    outgoing_acl_.pop_front();
+    return packetview;
+  }
+
+  void Start() {}
+
+  void Stop() {}
+
+  void ListDependencies(ModuleList*) {}
+
+  static const ModuleFactory Factory;
+
+ private:
+  std::list<hal::HciPacket> outgoing_commands_;
+  std::list<hal::HciPacket> outgoing_acl_;
+  std::list<hal::HciPacket> outgoing_sco_;
+};
+
+const ModuleFactory TestHciHal::Factory = ModuleFactory([]() { return new TestHciHal(); });
+
+class DependsOnHci : public Module {
+ public:
+  DependsOnHci() : Module() {}
+
+  void SendHciCommand(std::unique_ptr<CommandPacketBuilder> command) {
+    hci_->EnqueueCommand(std::move(command), [this](CommandStatusView status) { incoming_events_.push_back(status); },
+                         [this](CommandCompleteView complete) { incoming_events_.push_back(complete); }, GetHandler());
+  }
+
+  void SendAclData(std::unique_ptr<AclPacketBuilder> acl) {
+    AclPacketBuilder* raw_acl_pointer = acl.release();
+    auto queue_end = hci_->GetAclQueueEnd();
+    queue_end->RegisterEnqueue(GetHandler(), [queue_end, raw_acl_pointer]() -> std::unique_ptr<AclPacketBuilder> {
+      queue_end->UnregisterEnqueue();
+      return std::unique_ptr<AclPacketBuilder>(raw_acl_pointer);
+    });
+  }
+
+  EventPacketView GetReceivedEvent() {
+    while (incoming_events_.size() == 0)
+      ;
+    EventPacketView packetview = incoming_events_.front();
+    incoming_events_.pop_front();
+    return packetview;
+  }
+
+  AclPacketView GetReceivedAcl() {
+    auto queue_end = hci_->GetAclQueueEnd();
+    std::unique_ptr<AclPacketView> incoming_acl_ptr;
+    while (incoming_acl_ptr == nullptr) {
+      incoming_acl_ptr = queue_end->TryDequeue();
+    }
+    AclPacketView packetview = *incoming_acl_ptr;
+    return packetview;
+  }
+
+  void Start() {
+    hci_ = GetDependency<HciLayer>();
+    hci_->RegisterEventHandler(EventCode::CONNECTION_COMPLETE,
+                               [this](EventPacketView event) { incoming_events_.push_back(event); }, GetHandler());
+  }
+
+  void Stop() {}
+
+  void ListDependencies(ModuleList* list) {
+    list->add<HciLayer>();
+  }
+
+  static const ModuleFactory Factory;
+
+ private:
+  HciLayer* hci_ = nullptr;
+  std::list<EventPacketView> incoming_events_;
+};
+
+const ModuleFactory DependsOnHci::Factory = ModuleFactory([]() { return new DependsOnHci(); });
+
+class HciTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    counting_bytes.reserve(count_size);
+    counting_down_bytes.reserve(count_size);
+    for (size_t i = 0; i < count_size; i++) {
+      counting_bytes.push_back(i);
+      counting_down_bytes.push_back(~i);
+    }
+    thread_ = new Thread("test_thread", Thread::Priority::NORMAL);
+    hal = new TestHciHal();
+    fake_registry_.inject_test_module(&hal::HciHal::Factory, hal, thread_);
+    fake_registry_.Start<DependsOnHci>(thread_);
+    hci = fake_registry_.get_module_under_test<HciLayer>();
+    upper = fake_registry_.get_module_under_test<DependsOnHci>();
+    ASSERT(fake_registry_.IsStarted<HciLayer>());
+  }
+
+  void TearDown() override {
+    fake_registry_.StopAll();
+    delete thread_;
+  }
+
+  std::vector<uint8_t> GetPacketBytes(std::unique_ptr<packet::BasePacketBuilder> packet) {
+    std::vector<uint8_t> bytes;
+    BitInserter i(bytes);
+    bytes.reserve(packet->size());
+    packet->Serialize(i);
+    return bytes;
+  }
+
+  DependsOnHci* upper = nullptr;
+  TestHciHal* hal = nullptr;
+  HciLayer* hci = nullptr;
+  ModuleRegistry fake_registry_;
+  Thread* thread_ = nullptr;
+};
+
+TEST_F(HciTest, initAndClose) {}
+
+TEST_F(HciTest, createConnectionTest) {
+  // Send CreateConnection to the controller
+  common::Address bd_addr;
+  ASSERT_TRUE(common::Address::FromString("A1:A2:A3:A4:A5:A6", bd_addr));
+  uint16_t packet_type = 0x1234;
+  PageScanRepetitionMode page_scan_repetition_mode = PageScanRepetitionMode::R0;
+  uint16_t clock_offset = 0x3456;
+  ClockOffsetValid clock_offset_valid = ClockOffsetValid::VALID;
+  CreateConnectionRoleSwitch allow_role_switch = CreateConnectionRoleSwitch::ALLOW_ROLE_SWITCH;
+  upper->SendHciCommand(CreateConnectionBuilder::Create(bd_addr, packet_type, page_scan_repetition_mode, clock_offset,
+                                                        clock_offset_valid, allow_role_switch));
+
+  // Check the command
+  auto sent_command = hal->GetSentCommand();
+  ASSERT_LT(0, sent_command.size());
+  CreateConnectionView view =
+      CreateConnectionView::Create(ConnectionManagementCommandView::Create(CommandPacketView::Create(sent_command)));
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(bd_addr, view.GetBdAddr());
+  ASSERT_EQ(packet_type, view.GetPacketType());
+  ASSERT_EQ(page_scan_repetition_mode, view.GetPageScanRepetitionMode());
+  ASSERT_EQ(clock_offset, view.GetClockOffset());
+  ASSERT_EQ(clock_offset_valid, view.GetClockOffsetValid());
+  ASSERT_EQ(allow_role_switch, view.GetAllowRoleSwitch());
+
+  // Send a ConnectionComplete to the host
+  ErrorCode status = ErrorCode::SUCCESS;
+  uint16_t handle = 0x123;
+  LinkType link_type = LinkType::ACL;
+  Enable encryption_enabled = Enable::DISABLED;
+  hal->callbacks->hciEventReceived(
+      GetPacketBytes(ConnectionCompleteBuilder::Create(status, handle, bd_addr, link_type, encryption_enabled)));
+
+  // Verify the event
+  auto event = upper->GetReceivedEvent();
+  ASSERT_TRUE(event.IsValid());
+  ASSERT_EQ(EventCode::CONNECTION_COMPLETE, event.GetEventCode());
+  ConnectionCompleteView connection_complete_view = ConnectionCompleteView::Create(event);
+  ASSERT_TRUE(connection_complete_view.IsValid());
+  ASSERT_EQ(status, connection_complete_view.GetStatus());
+  ASSERT_EQ(handle, connection_complete_view.GetConnectionHandle());
+  ASSERT_EQ(link_type, connection_complete_view.GetLinkType());
+  ASSERT_EQ(encryption_enabled, connection_complete_view.GetEncryptionEnabled());
+
+  // Send an ACL packet from the remote
+  PacketBoundaryFlag packet_boundary_flag = PacketBoundaryFlag::COMPLETE_PDU;
+  BroadcastFlag broadcast_flag = BroadcastFlag::POINT_TO_POINT;
+  auto acl_payload = std::make_unique<RawBuilder>();
+  acl_payload->AddAddress(bd_addr);
+  acl_payload->AddOctets2(handle);
+  hal->callbacks->aclDataReceived(
+      GetPacketBytes(AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(acl_payload))));
+
+  // Verify the ACL packet
+  auto acl_view = upper->GetReceivedAcl();
+  ASSERT_TRUE(acl_view.IsValid());
+  ASSERT_EQ(sizeof(bd_addr) + sizeof(handle), acl_view.GetPayload().size());
+  auto itr = acl_view.GetPayload().begin();
+  ASSERT_EQ(bd_addr, itr.extract<Address>());
+  ASSERT_EQ(handle, itr.extract<uint16_t>());
+
+  // Send an ACL packet from DependsOnHci
+  PacketBoundaryFlag packet_boundary_flag2 = PacketBoundaryFlag::COMPLETE_PDU;
+  BroadcastFlag broadcast_flag2 = BroadcastFlag::POINT_TO_POINT;
+  auto acl_payload2 = std::make_unique<RawBuilder>();
+  acl_payload2->AddOctets2(handle);
+  acl_payload2->AddAddress(bd_addr);
+  upper->SendAclData(AclPacketBuilder::Create(handle, packet_boundary_flag2, broadcast_flag2, std::move(acl_payload2)));
+
+  // Verify the ACL packet
+  auto sent_acl = hal->GetSentAcl();
+  ASSERT_LT(0, sent_acl.size());
+  AclPacketView sent_acl_view = AclPacketView::Create(sent_acl);
+  ASSERT_TRUE(sent_acl_view.IsValid());
+  ASSERT_EQ(sizeof(bd_addr) + sizeof(handle), sent_acl_view.GetPayload().size());
+  auto sent_itr = sent_acl_view.GetPayload().begin();
+  ASSERT_EQ(handle, sent_itr.extract<uint16_t>());
+  ASSERT_EQ(bd_addr, sent_itr.extract<Address>());
+}
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/hci_packets.pdl b/gd/hci/hci_packets.pdl
new file mode 100644
index 0000000..58aa5cb
--- /dev/null
+++ b/gd/hci/hci_packets.pdl
@@ -0,0 +1,2545 @@
+little_endian_packets
+
+custom_field Address : 48 "common/"
+custom_field ClassOfDevice : 24 "common/"
+
+enum Enable : 8 {
+  DISABLED = 0x00,
+  ENABLED = 0x01,
+}
+
+// https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
+enum GapDataTypes : 8 {
+  FLAGS = 0x01,
+  INCOMPLETE_LIST_16_BIT_UUIDS = 0x02,
+  COMPLETE_LIST_16_BIT_UUIDS = 0x03,
+  INCOMPLETE_LIST_32_BIT_UUIDS = 0x04,
+  COMPLETE_LIST_32_BIT_UUIDS = 0x05,
+  INCOMPLETE_LIST_128_BIT_UUIDS = 0x06,
+  COMPLETE_LIST_128_BIT_UUIDS = 0x07,
+  SHORTENED_LOCAL_NAME = 0x08,
+  COMPLETE_LOCAL_NAME = 0x09,
+  TX_POWER_LEVEL = 0x0A,
+  CLASS_OF_DEVICE = 0x0D,
+}
+
+// HCI ACL Packets
+
+enum PacketBoundaryFlag : 2 {
+  FIRST_NON_AUTOMATICALLY_FLUSHABLE = 0,
+  CONTINUING_FRAGMENT = 1,
+  FIRST_AUTOMATICALLY_FLUSHABLE = 2,
+  COMPLETE_PDU = 3,
+}
+
+enum BroadcastFlag : 2 {
+  POINT_TO_POINT = 0,
+  ACTIVE_SLAVE_BROADCAST = 1,
+}
+
+packet AclPacket {
+  handle : 12,
+  packet_boundary_flag : PacketBoundaryFlag,
+  broadcast_flag : BroadcastFlag,
+  _size_(_payload_) : 16,
+  _payload_,
+}
+
+// HCI SCO Packets
+
+enum PacketStatusFlag : 2 {
+  CORRECTLY_RECEIVED = 0,
+  POSSIBLY_INCOMPLETE = 1,
+  NO_DATA = 2,
+  PARTIALLY_LOST = 3,
+}
+
+packet ScoPacket {
+  handle : 12,
+  packet_status_flag : PacketStatusFlag,
+  _reserved_ : 2, // BroadcastFlag
+  _size_(_payload_) : 8,
+  _payload_,
+}
+
+// HCI Command Packets
+
+enum OpCode : 16 {
+  NONE = 0x0000,
+
+  // LINK_CONTROL
+  INQUIRY = 0x0401,
+  INQUIRY_CANCEL = 0x0402,
+  PERIODIC_INQUIRY_MODE = 0x0403,
+  EXIT_PERIODIC_INQUIRY_MODE = 0x0404,
+  CREATE_CONNECTION = 0x0405,
+  DISCONNECT = 0x0406,
+  CREATE_CONNECTION_CANCEL = 0x0408,
+  ACCEPT_CONNECTION_REQUEST = 0x0409,
+  REJECT_CONNECTION_REQUEST = 0x040A,
+  LINK_KEY_REQUEST_REPLY = 0x040B,
+  LINK_KEY_REQUEST_NEGATIVE_REPLY = 0x040C,
+  PIN_CODE_REQUEST_REPLY = 0x040D,
+  PIN_CODE_REQUEST_NEGATIVE_REPLY = 0x040E,
+  CHANGE_CONNECTION_PACKET_TYPE = 0x040F,
+  AUTHENTICATION_REQUESTED = 0x0411,
+  SET_CONNECTION_ENCRYPTION = 0x0413,
+  CHANGE_CONNECTION_LINK_KEY = 0x0415,
+  MASTER_LINK_KEY = 0x0417,
+  REMOTE_NAME_REQUEST = 0x0419,
+  REMOTE_NAME_REQUEST_CANCEL = 0x041A,
+  READ_REMOTE_SUPPORTED_FEATURES = 0x041B,
+  READ_REMOTE_EXTENDED_FEATURES = 0x041C,
+  READ_REMOTE_VERSION_INFORMATION = 0x041D,
+  READ_CLOCK_OFFSET = 0x041F,
+  READ_LMP_HANDLE = 0x0420,
+  SETUP_SYNCHRONOUS_CONNECTION = 0x0428,
+  ACCEPT_SYNCHRONOUS_CONNECTION = 0x0429,
+  REJECT_SYNCHRONOUS_CONNECTION = 0x042A,
+  IO_CAPABILITY_REQUEST_REPLY = 0x042B,
+  USER_CONFIRMATION_REQUEST_REPLY = 0x042C,
+  USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY = 0x042D,
+  USER_PASSKEY_REQUEST_REPLY = 0x042E,
+  USER_PASSKEY_REQUEST_NEGATIVE_REPLY = 0x042F,
+  REMOTE_OOB_DATA_REQUEST_REPLY = 0x0430,
+  REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY = 0x0433,
+  IO_CAPABILITY_REQUEST_NEGATIVE_REPLY = 0x0434,
+  ENHANCED_SETUP_SYNCHRONOUS_CONNECTION = 0x043D,
+  ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION = 0x043E,
+
+  // LINK_POLICY
+  HOLD_MODE = 0x0801,
+  SNIFF_MODE = 0x0803,
+  EXIT_SNIFF_MODE = 0x0804,
+  QOS_SETUP = 0x0807,
+  ROLE_DISCOVERY = 0x0809,
+  SWITCH_ROLE = 0x080B,
+  READ_LINK_POLICY_SETTINGS = 0x080C,
+  WRITE_LINK_POLICY_SETTINGS = 0x080D,
+  READ_DEFAULT_LINK_POLICY_SETTINGS = 0x080E,
+  WRITE_DEFAULT_LINK_POLICY_SETTINGS = 0x080F,
+  FLOW_SPECIFICATION = 0x0810,
+  SNIFF_SUBRATING = 0x0811,
+
+  // CONTROLLER_AND_BASEBAND
+  SET_EVENT_MASK = 0x0C01,
+  RESET = 0x0C03,
+  SET_EVENT_FILTER = 0x0C05,
+  FLUSH = 0x0C08,
+  READ_PIN_TYPE = 0x0C09,
+  WRITE_PIN_TYPE = 0x0C0A,
+  CREATE_NEW_UNIT_KEY = 0x0C0B,
+  READ_STORED_LINK_KEY = 0x0C0D,
+  WRITE_STORED_LINK_KEY = 0x0C11,
+  DELETE_STORED_LINK_KEY = 0x0C12,
+  WRITE_LOCAL_NAME = 0x0C13,
+  READ_LOCAL_NAME = 0x0C14,
+  READ_CONNECTION_ACCEPT_TIMEOUT = 0x0C15,
+  WRITE_CONNECTION_ACCEPT_TIMEOUT = 0x0C16,
+  READ_PAGE_TIMEOUT = 0x0C17,
+  WRITE_PAGE_TIMEOUT = 0x0C18,
+  READ_SCAN_ENABLE = 0x0C19,
+  WRITE_SCAN_ENABLE = 0x0C1A,
+  READ_PAGE_SCAN_ACTIVITY = 0x0C1B,
+  WRITE_PAGE_SCAN_ACTIVITY = 0x0C1C,
+  READ_INQUIRY_SCAN_ACTIVITY = 0x0C1D,
+  WRITE_INQUIRY_SCAN_ACTIVITY = 0x0C1E,
+  READ_AUTHENTICATION_ENABLE = 0x0C1F,
+  WRITE_AUTHENTICATION_ENABLE = 0x0C20,
+  READ_CLASS_OF_DEVICE = 0x0C23,
+  WRITE_CLASS_OF_DEVICE = 0x0C24,
+  READ_VOICE_SETTING = 0x0C25,
+  WRITE_VOICE_SETTING = 0x0C26,
+  READ_AUTOMATIC_FLUSH_TIMEOUT = 0x0C27,
+  WRITE_AUTOMATIC_FLUSH_TIMEOUT = 0x0C28,
+  READ_NUM_BROADCAST_RETRANSMITS = 0x0C29,
+  WRITE_NUM_BROADCAST_RETRANSMITS = 0x0C2A,
+  READ_HOLD_MODE_ACTIVITY = 0x0C2B,
+  WRITE_HOLD_MODE_ACTIVITY = 0x0C2C,
+  READ_TRANSMIT_POWER_LEVEL = 0x0C2D,
+  READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE = 0x0C2E,
+  WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE = 0x0C2F,
+  SET_CONTROLLER_TO_HOST_FLOW_CONTROL = 0x0C31,
+  HOST_BUFFER_SIZE = 0x0C33,
+  HOST_NUM_COMPLETED_PACKETS = 0x0C35,
+  READ_LINK_SUPERVISION_TIMEOUT = 0x0C36,
+  WRITE_LINK_SUPERVISION_TIMEOUT = 0x0C37,
+  READ_NUMBER_OF_SUPPORTED_IAC = 0x0C38,
+  READ_CURRENT_IAC_LAP = 0x0C39,
+  WRITE_CURRENT_IAC_LAP = 0x0C3A,
+  SET_AFH_HOST_CHANNEL_CLASSIFICATION = 0x0C3F,
+  READ_INQUIRY_SCAN_TYPE = 0x0C42,
+  WRITE_INQUIRY_SCAN_TYPE = 0x0C43,
+  READ_INQUIRY_MODE = 0x0C44,
+  WRITE_INQUIRY_MODE = 0x0C45,
+  READ_PAGE_SCAN_TYPE = 0x0C46,
+  WRITE_PAGE_SCAN_TYPE = 0x0C47,
+  READ_AFH_CHANNEL_ASSESSMENT_MODE = 0x0C48,
+  WRITE_AFH_CHANNEL_ASSESSMENT_MODE = 0x0C49,
+  READ_EXTENDED_INQUIRY_RESPONSE = 0x0C51,
+  WRITE_EXTENDED_INQUIRY_RESPONSE = 0x0C52,
+  REFRESH_ENCRYPTION_KEY = 0x0C53,
+  READ_SIMPLE_PAIRING_MODE = 0x0C55,
+  WRITE_SIMPLE_PAIRING_MODE = 0x0C56,
+  READ_LOCAL_OOB_DATA = 0x0C57,
+  READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL = 0x0C58,
+  WRITE_INQUIRY_TRANSMIT_POWER_LEVEL = 0x0C59,
+  SEND_KEYPRESS_NOTIFICATION = 0x0C60,
+
+  READ_LE_HOST_SUPPORT = 0x0C6C,
+  WRITE_LE_HOST_SUPPORT = 0x0C6D,
+
+  READ_SECURE_CONNECTIONS_HOST_SUPPORT = 0x0C79,
+  WRITE_SECURE_CONNECTIONS_HOST_SUPPORT = 0x0C7A,
+
+  // INFORMATIONAL_PARAMETERS
+  READ_LOCAL_VERSION_INFORMATION = 0x1001,
+  READ_LOCAL_SUPPORTED_COMMANDS = 0x1002,
+  READ_LOCAL_SUPPORTED_FEATURES = 0x1003,
+  READ_LOCAL_EXTENDED_FEATURES = 0x1004,
+  READ_BUFFER_SIZE = 0x1005,
+  READ_BD_ADDR = 0x1009,
+  READ_DATA_BLOCK_SIZE = 0x100A,
+  READ_LOCAL_SUPPORTED_CODECS = 0x100B,
+
+  // STATUS_PARAMETERS
+  READ_FAILED_CONTACT_COUNTER = 0x1401,
+  RESET_FAILED_CONTACT_COUNTER = 0x1402,
+  READ_LINK_QUALITY = 0x1403,
+  READ_RSSI = 0x1405,
+  READ_AFH_CHANNEL_MAP = 0x1406,
+  READ_CLOCK = 0x1407,
+  READ_ENCRYPTION_KEY_SIZE = 0x1408,
+
+  // TESTING
+  READ_LOOPBACK_MODE = 0x1801,
+  WRITE_LOOPBACK_MODE = 0x1802,
+  ENABLE_DEVICE_UNDER_TEST_MODE = 0x1803,
+  WRITE_SIMPLE_PAIRING_DEBUG_MODE = 0x1804,
+  WRITE_SECURE_CONNECTIONS_TEST_MODE = 0x180A,
+
+  // LE_CONTROLLER
+  LE_SET_EVENT_MASK = 0x2001,
+  LE_READ_BUFFER_SIZE = 0x2002,
+  LE_READ_LOCAL_SUPPORTED_FEATURES = 0x2003,
+  LE_SET_RANDOM_ADDRESS = 0x2005,
+  LE_SET_ADVERTISING_PARAMETERS = 0x2006,
+  LE_READ_ADVERTISING_CHANNEL_TX_POWER = 0x2007,
+  LE_SET_ADVERTISING_DATA = 0x2008,
+  LE_SET_SCAN_RESPONSE_DATA = 0x2009,
+  LE_SET_ADVERTISING_ENABLE = 0x200A,
+  LE_SET_SCAN_PARAMETERS = 0x200B,
+  LE_SET_SCAN_ENABLE = 0x200C,
+  LE_CREATE_CONNECTION = 0x200D,
+  LE_CREATE_CONNECTION_CANCEL = 0x200E,
+  LE_READ_WHITE_LIST_SIZE = 0x200F,
+  LE_CLEAR_WHITE_LIST = 0x2010,
+  LE_ADD_DEVICE_TO_WHITE_LIST = 0x2011,
+  LE_REMOVE_DEVICE_FROM_WHITE_LIST = 0x2012,
+  LE_CONNECTION_UPDATE = 0x2013,
+  LE_SET_HOST_CHANNEL_CLASSIFICATION = 0x2014,
+  LE_READ_CHANNEL_MAP = 0x2015,
+  LE_READ_REMOTE_FEATURES = 0x2016,
+  LE_ENCRYPT = 0x2017,
+  LE_RAND = 0x2018,
+  LE_START_ENCRYPTION = 0x2019,
+  LE_LONG_TERM_KEY_REQUEST_REPLY = 0x201A,
+  LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY = 0x201B,
+  LE_READ_SUPPORTED_STATES = 0x201C,
+  LE_RECEIVER_TEST = 0x201D,
+  LE_TRANSMITTER_TEST = 0x201E,
+  LE_TEST_END = 0x201F,
+  LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY = 0x2020,
+  LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY = 0x2021,
+
+  LE_SET_DATA_LENGTH = 0x2022,
+  LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH = 0x2023,
+  LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH = 0x2024,
+  LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND = 0x2025,
+  LE_GENERATE_DHKEY_COMMAND = 0x2026,
+  LE_ADD_DEVICE_TO_RESOLVING_LIST = 0x2027,
+  LE_REMOVE_DEVICE_FROM_RESOLVING_LIST = 0x2028,
+  LE_CLEAR_RESOLVING_LIST = 0x2029,
+  LE_READ_RESOLVING_LIST_SIZE = 0x202A,
+  LE_READ_PEER_RESOLVABLE_ADDRESS = 0x202B,
+  LE_READ_LOCAL_RESOLVABLE_ADDRESS = 0x202C,
+  LE_SET_ADDRESS_RESOLUTION_ENABLE = 0x202D,
+  LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT = 0x202E,
+  LE_READ_MAXIMUM_DATA_LENGTH = 0x202F,
+  LE_READ_PHY = 0x2030,
+  LE_SET_DEFAULT_PHY = 0x2031,
+  LE_SET_PHY = 0x2032,
+  LE_ENHANCED_RECEIVER_TEST = 0x2033,
+  LE_ENHANCED_TRANSMITTER_TEST = 0x2034,
+  LE_SET_EXTENDED_ADVERTISING_RANDOM_ADDRESS = 0x2035,
+  LE_SET_EXTENDED_ADVERTISING_PARAMETERS = 0x2036,
+  LE_SET_EXTENDED_ADVERTISING_DATA = 0x2037,
+  LE_SET_EXTENDED_ADVERTISING_SCAN_RESPONSE = 0x2038,
+  LE_SET_EXTENDED_ADVERTISING_ENABLE = 0x2039,
+  LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH = 0x203A,
+  LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS = 0x203B,
+  LE_REMOVE_ADVERTISING_SET = 0x203C,
+  LE_CLEAR_ADVERTISING_SETS = 0x203D,
+  LE_SET_PERIODIC_ADVERTISING_PARAM = 0x203E,
+  LE_SET_PERIODIC_ADVERTISING_DATA = 0x203F,
+  LE_SET_PERIODIC_ADVERTISING_ENABLE = 0x2040,
+  LE_SET_EXTENDED_SCAN_PARAMETERS = 0x2041,
+  LE_SET_EXTENDED_SCAN_ENABLE = 0x2042,
+  LE_EXTENDED_CREATE_CONNECTION = 0x2043,
+  LE_PERIODIC_ADVERTISING_CREATE_SYNC = 0x2044,
+  LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL = 0x2045,
+  LE_PERIODIC_ADVERTISING_TERMINATE_SYNC = 0x2046,
+  LE_ADD_DEVICE_TO_PERIODIC_ADVERTISING_LIST = 0x2047,
+  LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISING_LIST = 0x2048,
+  LE_CLEAR_PERIODIC_ADVERTISING_LIST = 0x2049,
+  LE_READ_PERIODIC_ADVERTISING_LIST_SIZE = 0x204A,
+  LE_READ_TRANSMIT_POWER = 0x204B,
+  LE_READ_RF_PATH_COMPENSATION_POWER = 0x204C,
+  LE_WRITE_RF_PATH_COMPENSATION_POWER = 0x204D,
+  LE_SET_PRIVACY_MODE = 0x204E,
+
+  // VENDOR_SPECIFIC
+  LE_GET_VENDOR_CAPABILITIES = 0xFD53,
+  LE_MULTI_ADVT = 0xFD54,
+  LE_BATCH_SCAN = 0xFD56,
+  LE_ADV_FILTER = 0xFD57,
+  LE_TRACK_ADV = 0xFD58,
+  LE_ENERGY_INFO = 0xFD59,
+  LE_EXTENDED_SCAN_PARAMS = 0xFD5A,
+  CONTROLLER_DEBUG_INFO = 0xFD5B,
+  CONTROLLER_A2DP_OPCODE = 0xFD5D,
+}
+
+packet CommandPacket {
+  op_code : OpCode,
+  _size_(_payload_) : 8,
+  _payload_,
+}
+
+// Packets for interfaces
+
+packet DiscoveryCommand : CommandPacket { _payload_, }
+packet ConnectionManagementCommand : CommandPacket { _payload_, }
+packet SecurityCommand : CommandPacket { _payload_, }
+packet ScoConnectionCommand : CommandPacket { _payload_, }
+packet LeAdvertisingCommand : CommandPacket { _payload_, }
+packet LeConnectionManagementCommand : CommandPacket { _payload_, }
+packet LeSecurityCommand : CommandPacket { _payload_, }
+packet VendorCommand : CommandPacket { _payload_, }
+
+// HCI Event Packets
+
+enum EventCode : 8 {
+  INQUIRY_COMPLETE = 0x01,
+  INQUIRY_RESULT = 0x02,
+  CONNECTION_COMPLETE = 0x03,
+  CONNECTION_REQUEST = 0x04,
+  DISCONNECTION_COMPLETE = 0x05,
+  AUTHENTICATION_COMPLETE = 0x06,
+  REMOTE_NAME_REQUEST_COMPLETE = 0x07,
+  ENCRYPTION_CHANGE = 0x08,
+  CHANGE_CONNECTION_LINK_KEY_COMPLETE = 0x09,
+  MASTER_LINK_KEY_COMPLETE = 0x0A,
+  READ_REMOTE_SUPPORTED_FEATURES_COMPLETE = 0x0B,
+  READ_REMOTE_VERSION_INFORMATION_COMPLETE = 0x0C,
+  QOS_SETUP_COMPLETE = 0x0D,
+  COMMAND_COMPLETE = 0x0E,
+  COMMAND_STATUS = 0x0F,
+  HARDWARE_ERROR = 0x10,
+  FLUSH_OCCURRED = 0x11,
+  ROLE_CHANGE = 0x12,
+  NUMBER_OF_COMPLETED_PACKETS = 0x13,
+  MODE_CHANGE = 0x14,
+  RETURN_LINK_KEYS = 0x15,
+  PIN_CODE_REQUEST = 0x16,
+  LINK_KEY_REQUEST = 0x17,
+  LINK_KEY_NOTIFICATION = 0x18,
+  LOOPBACK_COMMAND = 0x19,
+  DATA_BUFFER_OVERFLOW = 0x1A,
+  MAX_SLOTS_CHANGE = 0x1B,
+  READ_CLOCK_OFFSET_COMPLETE = 0x1C,
+  CONNECTION_PACKET_TYPE_CHANGE = 0x1D,
+  QOS_VIOLATION = 0x1E,
+  PAGE_SCAN_REPETITION_MODE_CHANGE = 0x20,
+  FLOW_SPECIFICATION_COMPLETE = 0x21,
+  INQUIRY_RESULT_WITH_RSSI = 0x22,
+  READ_REMOTE_EXTENDED_FEATURES_COMPLETE = 0x23,
+  SYNCHRONOUS_CONNECTION_COMPLETE = 0x2C,
+  SYNCHRONOUS_CONNECTION_CHANGED = 0x2D,
+  SNIFF_SUBRATING = 0x2E,
+  EXTENDED_INQUIRY_RESULT = 0x2F,
+  ENCRYPTION_KEY_REFRESH_COMPLETE = 0x30,
+  IO_CAPABILITY_REQUEST = 0x31,
+  IO_CAPABILITY_RESPONSE = 0x32,
+  USER_CONFIRMATION_REQUEST = 0x33,
+  USER_PASSKEY_REQUEST = 0x34,
+  REMOTE_OOB_DATA_REQUEST = 0x35,
+  SIMPLE_PAIRING_COMPLETE = 0x36,
+  LINK_SUPERVISION_TIMEOUT_CHANGED = 0x38,
+  ENHANCED_FLUSH_COMPLETE = 0x39,
+  USER_PASSKEY_NOTIFICATION = 0x3B,
+  KEYPRESS_NOTIFICATION = 0x3C,
+  REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION = 0x3D,
+  LE_META_EVENT = 0x3e,
+  NUMBER_OF_COMPLETED_DATA_BLOCKS = 0x48,
+}
+
+packet EventPacket {
+  event_code : EventCode,
+  _size_(_payload_) : 8,
+  _payload_,
+}
+
+// LE Events
+
+enum SubeventCode : 8 {
+  CONNECTION_COMPLETE = 0x01,
+  ADVERTISING_REPORT = 0x02,
+  CONNECTION_UPDATE_COMPLETE = 0x03,
+  READ_REMOTE_FEATURES_COMPLETE = 0x04,
+  LONG_TERM_KEY_REQUEST = 0x05,
+  REMOTE_CONNECTION_PARAMETER_REQUEST = 0x06,
+  DATA_LENGTH_CHANGE = 0x07,
+  READ_LOCAL_P256_PUBLIC_KEY_COMPLETE = 0x08,
+  GENERATE_DHKEY_COMPLETE = 0x09,
+  ENHANCED_CONNECTION_COMPLETE = 0x0a,
+  DIRECTED_ADVERTISING_REPORT = 0x0b,
+  PHY_UPDATE_COMPLETE = 0x0c,
+  EXTENDED_ADVERTISING_REPORT = 0x0D,
+  PERIODIC_ADVERTISING_SYNC_ESTABLISHED = 0x0E,
+  PERIODIC_ADVERTISING_REPORT = 0x0F,
+  PERIODIC_ADVERTISING_SYNC_LOST = 0x10,
+  SCAN_TIMEOUT = 0x11,
+  ADVERTISING_SET_TERMINATED = 0x12,
+  SCAN_REQUEST_RECEIVED = 0x13,
+}
+
+// Common definitions for commands and events
+
+enum FeatureFlag : 1 {
+  UNSUPPORTED = 0,
+  SUPPORTED = 1,
+}
+
+enum ErrorCode: 8 {
+  SUCCESS = 0x00,
+  UNKNOWN_HCI_COMMAND = 0x01,
+  UNKNOWN_CONNECTION = 0x02,
+  HARDWARE_FAILURE = 0x03,
+  PAGE_TIMEOUT = 0x04,
+  AUTHENTICATION_FAILURE = 0x05,
+  PIN_OR_KEY_MISSING = 0x06,
+  MEMORY_CAPACITY_EXCEEDED = 0x07,
+  CONNECTION_TIMEOUT = 0x08,
+  CONNECTION_LIMIT_EXCEEDED = 0x09,
+  SYNCHRONOUS_CONNECTION_LIMIT_EXCEEDED = 0x0A,
+  CONNECTION_ALREADY_EXISTS = 0x0B,
+  COMMAND_DISALLOWED = 0x0C,
+  CONNECTION_REJECTED_LIMITED_RESOURCES = 0x0D,
+  CONNECTION_REJECTED_SECURITY_REASONS = 0x0E,
+  CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR = 0x0F,
+  CONNECTION_ACCEPT_TIMEOUT = 0x10,
+  UNSUPORTED_FEATURE_OR_PARAMETER_VALUE = 0x11,
+  INVALID_HCI_COMMAND_PARAMETERS = 0x12,
+  REMOTE_USER_TERMINATED_CONNECTION = 0x13,
+  REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES = 0x14,
+  REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF = 0x15,
+  CONNECTION_TERMINATED_BY_LOCAL_HOST = 0x16,
+  REPEATED_ATTEMPTS = 0x17,
+  PAIRING_NOT_ALLOWED = 0x18,
+  UNKNOWN_LMP_PDU = 0x19,
+  UNSUPPORTED_REMOTE_OR_LMP_FEATURE = 0x1A,
+  SCO_OFFSET_REJECTED = 0x1B,
+  SCO_INTERVAL_REJECTED = 0x1C,
+  SCO_AIR_MODE_REJECTED = 0x1D,
+  INVALID_LMP_OR_LL_PARAMETERS = 0x1E,
+  UNSPECIFIED_ERROR = 0x1F,
+  UNSUPPORTED_LMP_OR_LL_PARAMETER = 0x20,
+  ROLE_CHANGE_NOT_ALLOWED = 0x21,
+}
+
+// Events that are defined with their respective commands
+
+packet CommandComplete : EventPacket (event_code = COMMAND_COMPLETE){
+  num_hci_command_packets : 8,
+  command_op_code : OpCode,
+  _payload_,
+}
+
+packet CommandStatus : EventPacket (event_code = COMMAND_STATUS){
+  status : ErrorCode, // SUCCESS means PENDING
+  num_hci_command_packets : 8,
+  command_op_code : OpCode,
+  _payload_,
+}
+
+  // LINK_CONTROL
+packet Inquiry : DiscoveryCommand (op_code = INQUIRY) {
+  lap0 : 8, // 0x00 - 0x3F
+  _fixed_ = 0x9E8B : 16, // LAP upper bits are _fixed_
+  inquiry_length : 8, // 0x1 - 0x30 (times 1.28s)
+  num_responses : 8, // 0x00 unlimited
+}
+
+packet InquiryStatus : CommandStatus (command_op_code = INQUIRY) {
+}
+
+packet InquiryCancel : DiscoveryCommand (op_code = INQUIRY_CANCEL) {
+}
+
+packet InquiryCancelComplete : CommandComplete (command_op_code = INQUIRY_CANCEL) {
+  status : ErrorCode,
+}
+
+packet PeriodicInquiryMode : DiscoveryCommand (op_code = PERIODIC_INQUIRY_MODE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet PeriodicInquiryModeComplete : CommandComplete (command_op_code = PERIODIC_INQUIRY_MODE) {
+  status : ErrorCode,
+}
+
+packet ExitPeriodicInquiryMode : DiscoveryCommand (op_code = EXIT_PERIODIC_INQUIRY_MODE) {
+}
+
+packet ExitPeriodicInquiryModeComplete : CommandComplete (command_op_code = EXIT_PERIODIC_INQUIRY_MODE) {
+  status : ErrorCode,
+}
+
+enum PageScanRepetitionMode : 8 {
+  R0 = 0x00,
+  R1 = 0x01,
+  R2 = 0x02,
+}
+
+enum ClockOffsetValid : 1 {
+  INVALID = 0,
+  VALID = 1,
+}
+
+enum CreateConnectionRoleSwitch : 8 {
+  REMAIN_MASTER = 0x00,
+  ALLOW_ROLE_SWITCH = 0x01,
+}
+
+packet CreateConnection : ConnectionManagementCommand (op_code = CREATE_CONNECTION) {
+  bd_addr : Address,
+  packet_type : 16,
+  page_scan_repetition_mode : PageScanRepetitionMode,
+  _reserved_ : 8,
+  clock_offset : 15,
+  clock_offset_valid : ClockOffsetValid,
+  allow_role_switch : CreateConnectionRoleSwitch,
+}
+
+packet CreateConnectionStatus : CommandStatus (command_op_code = CREATE_CONNECTION) {
+}
+
+enum DisconnectReason : 8 {
+  AUTHENTICATION_FAILURE = 0x05,
+  REMOTE_USER_TERMINATED_CONNECTION = 0x13,
+  REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES = 0x14,
+  REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF = 0x15,
+  UNSUPPORTED_REMOTE_FEATURE = 0x1A,
+  PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED = 0x29,
+  UNACCEPTABLE_CONNECTION_PARAMETERS = 0x3B,
+}
+
+packet Disconnect : ConnectionManagementCommand (op_code = DISCONNECT) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  reason : DisconnectReason,
+}
+
+packet DisconnectStatus : CommandStatus (command_op_code = DISCONNECT) {
+}
+
+packet CreateConnectionCancel : ConnectionManagementCommand (op_code = CREATE_CONNECTION_CANCEL) {
+  bd_addr : Address,
+}
+
+packet CreateConnectionCancelComplete : CommandComplete (command_op_code = CREATE_CONNECTION_CANCEL) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+enum AcceptConnectionRequestRole : 8 {
+  BECOME_MASTER = 0x00,
+  REMAIN_SLAVE = 0x01,
+}
+
+packet AcceptConnectionRequest : ConnectionManagementCommand (op_code = ACCEPT_CONNECTION_REQUEST) {
+  bd_addr : Address,
+  role : AcceptConnectionRequestRole,
+}
+
+packet AcceptConnectionRequestStatus : CommandStatus (command_op_code = ACCEPT_CONNECTION_REQUEST) {
+}
+
+enum RejectConnectionReason : 8 {
+  LIMITED_RESOURCES = 0x0D,
+  SECURITY_REASONS = 0x0E,
+  UNACCEPTABLE_BD_ADDR = 0x0F,
+}
+
+packet RejectConnectionRequest : ConnectionManagementCommand (op_code = REJECT_CONNECTION_REQUEST) {
+  bd_addr : Address,
+  reason : RejectConnectionReason,
+}
+
+packet RejectConnectionRequestStatus : CommandStatus (command_op_code = REJECT_CONNECTION_REQUEST) {
+}
+
+packet LinkKeyRequestReply : SecurityCommand (op_code = LINK_KEY_REQUEST_REPLY) {
+  bd_addr : Address,
+  link_key_lo : 64,
+  link_key_hi : 64,
+}
+
+packet LinkKeyRequestReplyComplete : CommandComplete (command_op_code = LINK_KEY_REQUEST_REPLY) {
+  status : ErrorCode,
+}
+
+packet LinkKeyRequestNegativeReply : SecurityCommand (op_code = LINK_KEY_REQUEST_NEGATIVE_REPLY) {
+  bd_addr : Address,
+}
+
+packet LinkKeyRequestNegativeReplyComplete : CommandComplete (command_op_code = LINK_KEY_REQUEST_NEGATIVE_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet PinCodeRequestReply : SecurityCommand (op_code = PIN_CODE_REQUEST_REPLY) {
+  bd_addr : Address,
+  pin_code_length : 5, // 0x01 - 0x10
+  _reserved_ : 3,
+  pin_code_1 : 8, // string parameter, first octet first
+  pin_code_2 : 8,
+  pin_code_3 : 8,
+  pin_code_4 : 8,
+  pin_code_5 : 8,
+  pin_code_6 : 8,
+  pin_code_7 : 8,
+  pin_code_8 : 8,
+  pin_code_9 : 8,
+  pin_code_10 : 8,
+  pin_code_11 : 8,
+  pin_code_12 : 8,
+  pin_code_13 : 8,
+  pin_code_14 : 8,
+  pin_code_15 : 8,
+}
+
+packet PinCodeRequestReplyComplete : CommandComplete (command_op_code = PIN_CODE_REQUEST_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet PinCodeRequestNegativeReply : SecurityCommand (op_code = PIN_CODE_REQUEST_NEGATIVE_REPLY) {
+  bd_addr : Address,
+}
+
+packet PinCodeRequestNegativeReplyComplete : CommandComplete (command_op_code = PIN_CODE_REQUEST_NEGATIVE_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet ChangeConnectionPacketType : ConnectionManagementCommand (op_code = CHANGE_CONNECTION_PACKET_TYPE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  packet_type : 16,
+}
+
+packet ChangeConnectionPacketTypeStatus : CommandStatus (command_op_code = CHANGE_CONNECTION_PACKET_TYPE) {
+}
+
+packet AuthenticationRequested : ConnectionManagementCommand (op_code = AUTHENTICATION_REQUESTED) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet AuthenticationRequestedStatus : CommandStatus (command_op_code = AUTHENTICATION_REQUESTED) {
+}
+
+packet SetConnectionEncryption : ConnectionManagementCommand (op_code = SET_CONNECTION_ENCRYPTION) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  encryption_enable : Enable,
+}
+
+packet SetConnectionEncryptionStatus : CommandStatus (command_op_code = SET_CONNECTION_ENCRYPTION) {
+}
+
+packet ChangeConnectionLinkKey : ConnectionManagementCommand (op_code = CHANGE_CONNECTION_LINK_KEY) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ChangeConnectionLinkKeyStatus : CommandStatus (command_op_code = CHANGE_CONNECTION_LINK_KEY) {
+}
+
+enum KeyFlag : 8 {
+  SEMI_PERMANENT = 0x00,
+  TEMPORARY = 0x01,
+}
+
+packet MasterLinkKey : ConnectionManagementCommand (op_code = MASTER_LINK_KEY) {
+  key_flag : KeyFlag,
+}
+
+packet MasterLinkKeyStatus : CommandStatus (command_op_code = MASTER_LINK_KEY) {
+}
+
+packet RemoteNameRequest : DiscoveryCommand (op_code = REMOTE_NAME_REQUEST) {
+  bd_addr : Address,
+  page_scan_repetition_mode : PageScanRepetitionMode,
+  _reserved_ : 8,
+  clock_offset : 15,
+  clock_offset_valid : ClockOffsetValid,
+}
+
+packet RemoteNameRequestStatus : CommandStatus (command_op_code = REMOTE_NAME_REQUEST) {
+}
+
+packet RemoteNameRequestCancel : DiscoveryCommand (op_code = REMOTE_NAME_REQUEST_CANCEL) {
+  bd_addr : Address,
+}
+
+packet RemoteNameRequestCancelComplete : CommandComplete (command_op_code = REMOTE_NAME_REQUEST_CANCEL) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet ReadRemoteSupportedFeatures : DiscoveryCommand (op_code = READ_REMOTE_SUPPORTED_FEATURES) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadRemoteSupportedFeaturesStatus : CommandStatus (command_op_code = READ_REMOTE_SUPPORTED_FEATURES) {
+}
+
+packet ReadRemoteExtendedFeatures : DiscoveryCommand (op_code = READ_REMOTE_EXTENDED_FEATURES) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  page_number : 8,
+}
+
+packet ReadRemoteExtendedFeaturesStatus : CommandStatus (command_op_code = READ_REMOTE_EXTENDED_FEATURES) {
+}
+
+packet ReadRemoteVersionInformation : DiscoveryCommand (op_code = READ_REMOTE_VERSION_INFORMATION) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadRemoteVersionInformationStatus : CommandStatus (command_op_code = READ_REMOTE_VERSION_INFORMATION) {
+}
+
+packet ReadClockOffset : ConnectionManagementCommand (op_code = READ_CLOCK_OFFSET) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadClockOffsetStatus : CommandStatus (command_op_code = READ_CLOCK_OFFSET) {
+}
+
+packet ReadLmpHandle : ConnectionManagementCommand (op_code = READ_LMP_HANDLE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadLmpHandleComplete : CommandComplete (command_op_code = READ_LMP_HANDLE) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  lmp_handle : 8,
+  _reserved_ : 32,
+}
+
+packet SetupSynchronousConnection : ScoConnectionCommand (op_code = SETUP_SYNCHRONOUS_CONNECTION) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet AcceptSynchronousConnection : ScoConnectionCommand (op_code = ACCEPT_SYNCHRONOUS_CONNECTION) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet RejectSynchronousConnection : ScoConnectionCommand (op_code = REJECT_SYNCHRONOUS_CONNECTION) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+enum IoCapability : 8 {
+  DISPLAY_ONLY = 0x00,
+  DISPLAY_YES_NO = 0x01,
+  KEYBOARD_ONLY = 0x02,
+  NO_INPUT_NO_OUTPUT = 0x03,
+}
+
+enum OobDataPresent : 8 {
+  NOT_PRESENT = 0x00,
+  P_192_PRESENT = 0x01,
+  P_256_PRESENT = 0x02,
+  P_192_AND_256_PRESENT = 0x03,
+}
+
+enum AuthenticationRequirements : 8 {
+  NO_BONDING = 0x00,
+  NO_BONDING_MITM_PROTECTION = 0x01,
+  DEDICATED_BONDING = 0x02,
+  DEDICATED_BONDING_MITM_PROTECTION = 0x03,
+  GENERAL_BONDING = 0x04,
+  GENERAL_BONDING_MITM_PROTECTION = 0x05,
+}
+
+packet IoCapabilityRequestReply : SecurityCommand (op_code = IO_CAPABILITY_REQUEST_REPLY) {
+  bd_addr : Address,
+  io_capability : IoCapability,
+  oob_present : OobDataPresent,
+  authentication_requirements : AuthenticationRequirements,
+}
+
+packet IoCapabilityRequestReplyComplete : CommandComplete (command_op_code = IO_CAPABILITY_REQUEST_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet UserConfirmationRequestReply : SecurityCommand (op_code = USER_CONFIRMATION_REQUEST_REPLY) {
+  bd_addr : Address,
+}
+
+packet UserConfirmationRequestReplyComplete : CommandComplete (command_op_code = USER_CONFIRMATION_REQUEST_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet UserConfirmationRequestNegativeReply : SecurityCommand (op_code = USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY) {
+  bd_addr : Address,
+}
+
+packet UserConfirmationRequestNegativeReplyComplete : CommandComplete (command_op_code = USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet UserPasskeyRequestReply : SecurityCommand (op_code = USER_PASSKEY_REQUEST_REPLY) {
+  bd_addr : Address,
+  numeric_value : 32, // 000000-999999 decimal or 0x0-0xF423F
+}
+
+packet UserPasskeyReplyComplete : CommandComplete (command_op_code = USER_PASSKEY_REQUEST_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet UserPasskeyRequestNegativeReply : SecurityCommand (op_code = USER_PASSKEY_REQUEST_NEGATIVE_REPLY) {
+  bd_addr : Address,
+}
+
+packet UserPasskeyRequestNegativeReplyComplete : CommandComplete (command_op_code = USER_PASSKEY_REQUEST_NEGATIVE_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet RemoteOobDataRequestReply : SecurityCommand (op_code = REMOTE_OOB_DATA_REQUEST_REPLY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet RemoteOobDataRequestReplyComplete : CommandComplete (command_op_code = REMOTE_OOB_DATA_REQUEST_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet RemoteOobDataRequestNegativeReply : SecurityCommand (op_code = REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY) {
+  bd_addr : Address,
+}
+
+packet RemoteOobDataRequestNegativeReplyComplete : CommandComplete (command_op_code = REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet IoCapabilityRequestNegativeReply : SecurityCommand (op_code = IO_CAPABILITY_REQUEST_NEGATIVE_REPLY) {
+  bd_addr : Address,
+  reason : ErrorCode,
+}
+
+packet IoCapabilityRequestNegativeReplyComplete : CommandComplete (command_op_code = IO_CAPABILITY_REQUEST_NEGATIVE_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet EnhancedSetupSynchronousConnection : ScoConnectionCommand (op_code = ENHANCED_SETUP_SYNCHRONOUS_CONNECTION) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet EnhancedAcceptSynchronousConnection : ScoConnectionCommand (op_code = ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+
+  // LINK_POLICY
+packet HoldMode : ConnectionManagementCommand (op_code = HOLD_MODE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet SniffMode : ConnectionManagementCommand (op_code = SNIFF_MODE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet ExitSniffMode : ConnectionManagementCommand (op_code = EXIT_SNIFF_MODE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet QosSetup : ConnectionManagementCommand (op_code = QOS_SETUP) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet RoleDiscovery : ConnectionManagementCommand (op_code = ROLE_DISCOVERY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet SwitchRole : ConnectionManagementCommand (op_code = SWITCH_ROLE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet ReadLinkPolicySettings : ConnectionManagementCommand (op_code = READ_LINK_POLICY_SETTINGS) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteLinkPolicySettings : ConnectionManagementCommand (op_code = WRITE_LINK_POLICY_SETTINGS) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet ReadDefaultLinkPolicySettings : ConnectionManagementCommand (op_code = READ_DEFAULT_LINK_POLICY_SETTINGS) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteDefaultLinkPolicySettings : ConnectionManagementCommand (op_code = WRITE_DEFAULT_LINK_POLICY_SETTINGS) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet FlowSpecification : ConnectionManagementCommand (op_code = FLOW_SPECIFICATION) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet SniffSubrating : ConnectionManagementCommand (op_code = SNIFF_SUBRATING) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+
+  // CONTROLLER_AND_BASEBAND
+packet SetEventMask : CommandPacket (op_code = SET_EVENT_MASK) {
+  event_mask : 64,
+}
+
+packet SetEventMaskComplete : CommandComplete (command_op_code = SET_EVENT_MASK) {
+  status : ErrorCode,
+}
+
+packet Reset : CommandPacket (op_code = RESET) {
+}
+
+packet ResetComplete : CommandComplete (command_op_code = RESET) {
+  status : ErrorCode,
+}
+
+packet SetEventFilter : CommandPacket (op_code = SET_EVENT_FILTER) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet Flush : ConnectionManagementCommand (op_code = FLUSH) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet ReadPinType : CommandPacket (op_code = READ_PIN_TYPE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WritePinType : CommandPacket (op_code = WRITE_PIN_TYPE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet CreateNewUnitKey : CommandPacket (op_code = CREATE_NEW_UNIT_KEY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+enum ReadStoredLinkKeyReadAllFlag : 8 {
+  SPECIFIED_BD_ADDR = 0x00,
+  ALL = 0x01,
+}
+
+packet ReadStoredLinkKey : SecurityCommand (op_code = READ_STORED_LINK_KEY) {
+  bd_addr : Address,
+  read_all_flag : ReadStoredLinkKeyReadAllFlag,
+}
+
+packet ReadStoredLinkKeyComplete : CommandComplete (command_op_code = READ_STORED_LINK_KEY) {
+  status : ErrorCode,
+  max_num_keys : 16,
+  num_keys_read : 16,
+}
+
+packet WriteStoredLinkKey : SecurityCommand (op_code = WRITE_STORED_LINK_KEY) {
+  _payload_,
+}
+
+packet WriteStoredLinkKeyComplete : CommandComplete (command_op_code = WRITE_STORED_LINK_KEY) {
+  status : ErrorCode,
+  num_keys_written : 8,
+}
+
+enum DeleteStoredLinkKeyDeleteAllFlag : 8 {
+  SPECIFIED_BD_ADDR = 0x00,
+  ALL = 0x01,
+}
+
+packet DeleteStoredLinkKey : SecurityCommand (op_code = DELETE_STORED_LINK_KEY) {
+  bd_addr : Address,
+  delete_all_flag : DeleteStoredLinkKeyDeleteAllFlag,
+}
+
+packet DeleteStoredLinkKeyComplete : CommandComplete (command_op_code = DELETE_STORED_LINK_KEY) {
+  status : ErrorCode,
+  num_keys_deleted : 8,
+}
+
+packet WriteLocalName : CommandPacket (op_code = WRITE_LOCAL_NAME) {
+  _payload_,
+}
+
+packet WriteLocalNameComplete : CommandComplete (command_op_code = WRITE_LOCAL_NAME) {
+  status : ErrorCode,
+}
+
+packet ReadLocalName : CommandPacket (op_code = READ_LOCAL_NAME) {
+}
+
+packet ReadLocalNameComplete : CommandComplete (command_op_code = READ_LOCAL_NAME) {
+  status : ErrorCode,
+  _payload_,
+}
+
+packet ReadConnectionAcceptTimeout : ConnectionManagementCommand (op_code = READ_CONNECTION_ACCEPT_TIMEOUT) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteConnectionAcceptTimeout : ConnectionManagementCommand (op_code = WRITE_CONNECTION_ACCEPT_TIMEOUT) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet ReadPageTimeout : DiscoveryCommand (op_code = READ_PAGE_TIMEOUT) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WritePageTimeout : DiscoveryCommand (op_code = WRITE_PAGE_TIMEOUT) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+enum ScanEnable : 8 {
+  NO_SCANS = 0x00,
+  INQUIRY_SCAN_ONLY = 0x01,
+  PAGE_SCAN_ONLY = 0x02,
+  INQUIRY_AND_PAGE_SCAN = 0x03,
+}
+
+packet ReadScanEnable : DiscoveryCommand (op_code = READ_SCAN_ENABLE) {
+}
+
+packet ReadScanEnableComplete : CommandComplete (command_op_code = READ_SCAN_ENABLE) {
+  status : ErrorCode,
+  scan_enable : ScanEnable,
+}
+
+packet WriteScanEnable : DiscoveryCommand (op_code = WRITE_SCAN_ENABLE) {
+  scan_enable : ScanEnable,
+}
+
+packet WriteScanEnableComplete : CommandComplete (command_op_code = WRITE_SCAN_ENABLE) {
+  status : ErrorCode,
+}
+
+packet ReadPageScanActivity : DiscoveryCommand (op_code = READ_PAGE_SCAN_ACTIVITY) {
+}
+
+packet ReadPageScanActivityComplete : CommandComplete (command_op_code = READ_PAGE_SCAN_ACTIVITY) {
+  status : ErrorCode,
+  page_scan_interval : 16, // Range: 0x0012 to 0x1000; only even values are valid * 0x625 ms
+  page_scan_window : 16, // 0x0011 to PageScanInterval
+}
+
+packet WritePageScanActivity : DiscoveryCommand (op_code = WRITE_PAGE_SCAN_ACTIVITY) {
+  page_scan_interval : 16, // Range: 0x0012 to 0x1000; only even values are valid * 0x625 ms
+  page_scan_window : 16, // 0x0011 to PageScanInterval
+}
+
+packet WritePageScanActivityComplete : CommandComplete (command_op_code = WRITE_PAGE_SCAN_ACTIVITY) {
+  status : ErrorCode,
+}
+
+packet ReadInquiryScanActivity : DiscoveryCommand (op_code = READ_INQUIRY_SCAN_ACTIVITY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteInquiryScanActivity : DiscoveryCommand (op_code = WRITE_INQUIRY_SCAN_ACTIVITY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+enum AuthenticationEnable : 8 {
+  NOT_REQUIRED = 0x00,
+  REQUIRED = 0x01,
+}
+
+packet ReadAuthenticationEnable : CommandPacket (op_code = READ_AUTHENTICATION_ENABLE) {
+}
+
+packet ReadAuthenticationEnableComplete : CommandComplete (command_op_code = READ_AUTHENTICATION_ENABLE) {
+  status : ErrorCode,
+  authentication_enable : AuthenticationEnable,
+}
+
+packet WriteAuthenticationEnable : CommandPacket (op_code = WRITE_AUTHENTICATION_ENABLE) {
+  authentication_enable : AuthenticationEnable,
+}
+
+packet WriteAuthenticationEnableComplete : CommandComplete (command_op_code = WRITE_AUTHENTICATION_ENABLE) {
+  status : ErrorCode,
+}
+
+packet ReadClassOfDevice : DiscoveryCommand (op_code = READ_CLASS_OF_DEVICE) {
+}
+
+packet ReadClassOfDeviceComplete : CommandComplete (command_op_code = READ_CLASS_OF_DEVICE) {
+  status : ErrorCode,
+  class_of_device : ClassOfDevice,
+}
+
+packet WriteClassOfDevice : DiscoveryCommand (op_code = WRITE_CLASS_OF_DEVICE) {
+  class_of_device : ClassOfDevice,
+}
+
+packet WriteClassOfDeviceComplete : CommandComplete (command_op_code = WRITE_CLASS_OF_DEVICE) {
+  status : ErrorCode,
+}
+
+packet ReadVoiceSetting : CommandPacket (op_code = READ_VOICE_SETTING) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteVoiceSetting : CommandPacket (op_code = WRITE_VOICE_SETTING) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet ReadAutomaticFlushTimeout : ConnectionManagementCommand (op_code = READ_AUTOMATIC_FLUSH_TIMEOUT) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteAutomaticFlushTimeout : ConnectionManagementCommand (op_code = WRITE_AUTOMATIC_FLUSH_TIMEOUT) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet ReadNumBroadcastRetransmits : CommandPacket (op_code = READ_NUM_BROADCAST_RETRANSMITS) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteNumBroadcastRetransmits : CommandPacket (op_code = WRITE_NUM_BROADCAST_RETRANSMITS) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet ReadHoldModeActivity : CommandPacket (op_code = READ_HOLD_MODE_ACTIVITY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteHoldModeActivity : CommandPacket (op_code = WRITE_HOLD_MODE_ACTIVITY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet ReadTransmitPowerLevel : ConnectionManagementCommand (op_code = READ_TRANSMIT_POWER_LEVEL) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet ReadSynchronousFlowControlEnable : CommandPacket (op_code = READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteSynchronousFlowControlEnable : CommandPacket (op_code = WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet SetControllerToHostFlowControl : CommandPacket (op_code = SET_CONTROLLER_TO_HOST_FLOW_CONTROL) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet HostBufferSize : CommandPacket (op_code = HOST_BUFFER_SIZE) {
+  host_acl_data_packet_length : 16,
+  host_synchronous_data_packet_length : 8,
+  host_total_num_acl_data_packets : 16,
+  host_total_num_synchronous_data_packets : 16,
+}
+
+packet HostBufferSizeComplete : CommandComplete (command_op_code = HOST_BUFFER_SIZE) {
+  status : ErrorCode,
+}
+
+packet HostNumCompletedPackets : CommandPacket (op_code = HOST_NUM_COMPLETED_PACKETS) {
+  _payload_,
+}
+
+packet HostNumCompletedPacketsError : CommandComplete (command_op_code = HOST_NUM_COMPLETED_PACKETS) {
+  error_code : ErrorCode,
+}
+
+packet ReadLinkSupervisionTimeout : ConnectionManagementCommand (op_code = READ_LINK_SUPERVISION_TIMEOUT) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadLinkSupervisionTimeoutComplete : CommandComplete (command_op_code = READ_LINK_SUPERVISION_TIMEOUT) {
+  status : ErrorCode,
+  handle : 12,
+  _reserved_ : 4,
+  link_supervision_timeout : 16, // 0x001-0xFFFF (0.625ms-40.9s)
+}
+
+packet WriteLinkSupervisionTimeout : ConnectionManagementCommand (op_code = WRITE_LINK_SUPERVISION_TIMEOUT) {
+  handle : 12,
+  _reserved_ : 4,
+  link_supervision_timeout : 16, // 0x001-0xFFFF (0.625ms-40.9s)
+}
+
+packet WriteLinkSupervisionTimeoutComplete : CommandComplete (command_op_code = WRITE_LINK_SUPERVISION_TIMEOUT) {
+  status : ErrorCode,
+  handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadNumberOfSupportedIac : DiscoveryCommand (op_code = READ_NUMBER_OF_SUPPORTED_IAC) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet ReadCurrentIacLap : DiscoveryCommand (op_code = READ_CURRENT_IAC_LAP) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteCurrentIacLap : DiscoveryCommand (op_code = WRITE_CURRENT_IAC_LAP) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet SetAfhHostChannelClassification : CommandPacket (op_code = SET_AFH_HOST_CHANNEL_CLASSIFICATION) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+enum InquiryScanType : 8 {
+  STANDARD = 0x00,
+  INTERLACED = 0x01,
+}
+
+packet ReadInquiryScanType : DiscoveryCommand (op_code = READ_INQUIRY_SCAN_TYPE) {
+}
+
+packet ReadInquiryScanTypeComplete : CommandComplete (command_op_code = READ_INQUIRY_SCAN_TYPE) {
+  status : ErrorCode,
+  inquiry_scan_type : InquiryScanType,
+}
+
+packet WriteInquiryScanType : DiscoveryCommand (op_code = WRITE_INQUIRY_SCAN_TYPE) {
+  inquiry_scan_type : InquiryScanType,
+}
+
+packet WriteInquiryScanTypeComplete : CommandComplete (command_op_code = WRITE_INQUIRY_SCAN_TYPE) {
+  status : ErrorCode,
+}
+
+enum InquiryMode : 8 {
+  STANDARD = 0x00,
+  RSSI = 0x01,
+  RSSI_OR_EXTENDED = 0x02,
+}
+
+packet ReadInquiryMode : DiscoveryCommand (op_code = READ_INQUIRY_MODE) {
+}
+
+packet ReadInquiryModeComplete : CommandComplete (command_op_code = READ_INQUIRY_MODE) {
+  status : ErrorCode,
+  inquiry_mode : InquiryMode,
+}
+
+packet WriteInquiryMode : DiscoveryCommand (op_code = WRITE_INQUIRY_MODE) {
+  inquiry_mode : InquiryMode,
+}
+
+packet WriteInquiryModeComplete : CommandComplete (command_op_code = WRITE_INQUIRY_MODE) {
+  status : ErrorCode,
+}
+
+enum PageScanType : 8 {
+  STANDARD = 0x00,
+  INTERLACED = 0x01,
+}
+
+packet ReadPageScanType : DiscoveryCommand (op_code = READ_PAGE_SCAN_TYPE) {
+}
+
+packet ReadPageScanTypeComplete : CommandComplete (command_op_code = READ_PAGE_SCAN_TYPE) {
+  status : ErrorCode,
+  page_scan_type : PageScanType,
+}
+
+packet WritePageScanType : DiscoveryCommand (op_code = WRITE_PAGE_SCAN_TYPE) {
+  page_scan_type : PageScanType,
+}
+
+packet WritePageScanTypeComplete : CommandComplete (command_op_code = WRITE_PAGE_SCAN_TYPE) {
+  status : ErrorCode,
+}
+
+packet ReadAfhChannelAssessmentMode : CommandPacket (op_code = READ_AFH_CHANNEL_ASSESSMENT_MODE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteAfhChannelAssessmentMode : CommandPacket (op_code = WRITE_AFH_CHANNEL_ASSESSMENT_MODE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+enum FecRequired : 8 {
+  NOT_REQUIRED = 0x00,
+  REQUIRED = 0x01,
+}
+
+packet ReadExtendedInquiryResponse : CommandPacket (op_code = READ_EXTENDED_INQUIRY_RESPONSE) {
+}
+
+packet ReadExtendedInquiryResponseComplete : CommandComplete (command_op_code = READ_EXTENDED_INQUIRY_RESPONSE) {
+  status : ErrorCode,
+  fec_required : FecRequired,
+  _payload_,
+}
+
+packet WriteExtendedInquiryResponse : CommandPacket (op_code = WRITE_EXTENDED_INQUIRY_RESPONSE) {
+  fec_required : FecRequired,
+  _payload_,
+}
+
+packet WriteExtendedInquiryResponseComplete : CommandComplete (command_op_code = WRITE_EXTENDED_INQUIRY_RESPONSE) {
+  status : ErrorCode,
+}
+
+packet RefreshEncryptionKey : SecurityCommand (op_code = REFRESH_ENCRYPTION_KEY) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet RefreshEncryptionKeyStatus : CommandStatus (command_op_code = REFRESH_ENCRYPTION_KEY) {
+}
+
+packet ReadSimplePairingMode : SecurityCommand (op_code = READ_SIMPLE_PAIRING_MODE) {
+}
+
+packet ReadSimplePairingModeComplete : CommandComplete (command_op_code = READ_SIMPLE_PAIRING_MODE) {
+  status : ErrorCode,
+  simple_pairing_mode : Enable,
+}
+
+packet WriteSimplePairingMode : SecurityCommand (op_code = WRITE_SIMPLE_PAIRING_MODE) {
+  simple_pairing_mode : Enable,
+}
+
+packet WriteSimplePairingModeComplete : CommandComplete (command_op_code = WRITE_SIMPLE_PAIRING_MODE) {
+  status : ErrorCode,
+}
+
+packet ReadLocalOobData : SecurityCommand (op_code = READ_LOCAL_OOB_DATA) {
+}
+
+packet ReadLocalOobDataComplete : CommandComplete (command_op_code = READ_LOCAL_OOB_DATA) {
+  status : ErrorCode,
+  clo : 64,
+  chi : 64,
+  rlo : 64,
+  rhi : 64,
+}
+
+packet ReadInquiryResponseTransmitPowerLevel : DiscoveryCommand (op_code = READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL) {
+}
+
+packet ReadInquiryResponseTransmitPowerLevelComplete : CommandComplete (command_op_code = READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL) {
+  status : ErrorCode,
+  tx_power : 8, // (-70dBm to 20dBm)
+}
+
+packet WriteInquiryTransmitPowerLevel : DiscoveryCommand (op_code = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL) {
+  tx_power : 8,
+}
+
+packet WriteInquiryResponseTransmitPowerLevelComplete : CommandComplete (command_op_code = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL) {
+  status : ErrorCode,
+}
+
+enum KeypressNotificationType : 8 {
+  ENTRY_STARTED = 0,
+  DIGIT_ENTERED = 1,
+  DIGIT_ERASED = 2,
+  CLEARED = 3,
+  ENTRY_COMPLETED = 4,
+}
+
+packet SendKeypressNotification : SecurityCommand (op_code = SEND_KEYPRESS_NOTIFICATION) {
+  bd_addr : Address,
+  notification_type : KeypressNotificationType,
+}
+
+packet SendKeypressNotificationComplete : CommandComplete (command_op_code = SEND_KEYPRESS_NOTIFICATION) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+enum SimultaneousLeHost : 8 {
+  DISABLED = 0x00,
+}
+
+packet ReadLeHostSupport : CommandPacket (op_code = READ_LE_HOST_SUPPORT) {
+}
+
+packet ReadLeHostSupportComplete : CommandComplete (command_op_code = READ_LE_HOST_SUPPORT) {
+  status : ErrorCode,
+  le_supported_host : Enable,
+  simultaneous_le_host : SimultaneousLeHost,
+}
+
+packet WriteLeHostSupport : CommandPacket (op_code = WRITE_LE_HOST_SUPPORT) {
+  le_supported_host : Enable,
+  simultaneous_le_host : SimultaneousLeHost,
+}
+
+packet WriteLeHostSupportComplete : CommandComplete (command_op_code = WRITE_LE_HOST_SUPPORT) {
+  status : ErrorCode,
+}
+
+packet ReadSecureConnectionsHostSupport : CommandPacket (op_code = READ_SECURE_CONNECTIONS_HOST_SUPPORT) {
+}
+
+packet ReadSecureConnectionsHostSupportComplete : CommandComplete (command_op_code = READ_SECURE_CONNECTIONS_HOST_SUPPORT) {
+  status : ErrorCode,
+  secure_connections_host_support : Enable,
+}
+
+packet WriteSecureConnectionsHostSupport : CommandPacket (op_code = WRITE_SECURE_CONNECTIONS_HOST_SUPPORT) {
+  secure_connections_host_support : Enable,
+}
+
+packet WriteSecureConnectionsHostSupportComplete : CommandComplete (command_op_code = WRITE_SECURE_CONNECTIONS_HOST_SUPPORT) {
+  status : ErrorCode,
+}
+
+  // INFORMATIONAL_PARAMETERS
+packet ReadLocalVersionInformation : CommandPacket (op_code = READ_LOCAL_VERSION_INFORMATION) {
+}
+
+packet ReadLocalSupportedCommands : CommandPacket (op_code = READ_LOCAL_SUPPORTED_COMMANDS) {
+}
+
+packet ReadLocalSupportedFeatures : CommandPacket (op_code = READ_LOCAL_SUPPORTED_FEATURES) {
+}
+
+packet ReadLocalExtendedFeatures : CommandPacket (op_code = READ_LOCAL_EXTENDED_FEATURES) {
+  page_number : 8,
+}
+
+packet ReadBufferSize : CommandPacket (op_code = READ_BUFFER_SIZE) {
+}
+
+packet ReadBdAddr : CommandPacket (op_code = READ_BD_ADDR) {
+}
+
+packet ReadDataBlockSize : CommandPacket (op_code = READ_DATA_BLOCK_SIZE) {
+}
+
+packet ReadLocalSupportedCodecs : CommandPacket (op_code = READ_LOCAL_SUPPORTED_CODECS) {
+}
+
+
+  // STATUS_PARAMETERS
+packet ReadFailedContactCounter : ConnectionManagementCommand (op_code = READ_FAILED_CONTACT_COUNTER) {
+  handle : 12,
+  _reserved_ : 4,
+}
+
+packet ResetFailedContactCounter : ConnectionManagementCommand (op_code = RESET_FAILED_CONTACT_COUNTER) {
+  handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadLinkQuality : ConnectionManagementCommand (op_code = READ_LINK_QUALITY) {
+  handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadRssi : ConnectionManagementCommand (op_code = READ_RSSI) {
+  handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadAfhChannelMap : ConnectionManagementCommand (op_code = READ_AFH_CHANNEL_MAP) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+enum WhichClock : 8 {
+  LOCAL = 0x00,
+  PICONET = 0x01,
+}
+
+packet ReadClock : ConnectionManagementCommand (op_code = READ_CLOCK) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  which_clock : WhichClock,
+}
+
+packet ReadEncryptionKeySize : SecurityCommand (op_code = READ_ENCRYPTION_KEY_SIZE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+
+  // TESTING
+enum LoopbackMode : 8 {
+  NO_LOOPBACK = 0x00,
+  ENABLE_LOCAL = 0x01,
+  ENABLE_REMOTE = 0x02,
+}
+
+packet ReadLoopbackMode : CommandPacket (op_code = READ_LOOPBACK_MODE) {
+}
+
+packet ReadLoopbackModeComplete : CommandComplete (command_op_code = READ_LOOPBACK_MODE) {
+  status : ErrorCode,
+  loopback_mode : LoopbackMode,
+}
+
+packet WriteLoopbackMode : CommandPacket (op_code = WRITE_LOOPBACK_MODE) {
+  loopback_mode : LoopbackMode,
+}
+
+packet WriteLoopbackModeComplete : CommandComplete (command_op_code = WRITE_LOOPBACK_MODE) {
+  status : ErrorCode,
+}
+
+packet EnableDeviceUnderTestMode : CommandPacket (op_code = ENABLE_DEVICE_UNDER_TEST_MODE) {
+}
+
+packet EnableDeviceUnderTestModeComplete : CommandComplete (command_op_code = ENABLE_DEVICE_UNDER_TEST_MODE) {
+  status : ErrorCode,
+}
+
+packet WriteSimplePairingDebugMode : CommandPacket (op_code = WRITE_SIMPLE_PAIRING_DEBUG_MODE) {
+  simple_pairing_debug_mode : Enable,
+}
+
+packet WriteSimplePairingDebugModeComplete : CommandComplete (command_op_code = WRITE_SIMPLE_PAIRING_DEBUG_MODE) {
+  status : ErrorCode,
+}
+
+packet WriteSecureConnectionsTestMode : CommandPacket (op_code = WRITE_SECURE_CONNECTIONS_TEST_MODE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  dm1_aclu_mode : Enable,
+  esco_loopback_mode : Enable,
+}
+
+packet WriteSecureConnectionsTestModeComplete : CommandComplete (command_op_code = WRITE_SECURE_CONNECTIONS_TEST_MODE) {
+  status : ErrorCode,
+}
+
+  // LE_CONTROLLER
+packet LeSetEventMask : CommandPacket (op_code = LE_SET_EVENT_MASK) {
+  le_event_mask : 64,
+}
+
+packet LeSetEventMaskComplete : CommandComplete (command_op_code = LE_SET_EVENT_MASK) {
+  status : ErrorCode,
+}
+
+packet LeReadBufferSize : CommandPacket (op_code = LE_READ_BUFFER_SIZE) {
+}
+
+packet LeReadBufferSizeComplete : CommandComplete (command_op_code = LE_READ_BUFFER_SIZE) {
+  status : ErrorCode,
+  hc_le_data_packet_length : 16,
+  hc_total_num_le_packets : 8,
+}
+
+packet LeReadLocalSupportedFeatures : CommandPacket (op_code = LE_READ_LOCAL_SUPPORTED_FEATURES) {
+}
+
+packet LeReadLocalSupportedFeaturesComplete : CommandComplete (command_op_code = LE_READ_LOCAL_SUPPORTED_FEATURES) {
+  status : ErrorCode,
+  le_features : 64,
+}
+
+packet LeSetRandomAddress : CommandPacket (op_code = LE_SET_RANDOM_ADDRESS) {
+  random_address : Address,
+}
+
+packet LeSetRandomAddressComplete : CommandComplete (command_op_code = LE_SET_RANDOM_ADDRESS) {
+  status : ErrorCode,
+}
+
+enum AdvertisingFilterPolicy : 1 {
+  ALL_DEVICES = 0, // Default
+  ONLY_WHITE_LISTED_DEVICES = 1,
+}
+
+enum PeerAddressType : 8 {
+  PUBLIC_DEVICE_OR_IDENTITY_ADDRESS = 0x00,
+  RANDOM_DEVICE_OR_IDENTITY_ADDRESS = 0x01,
+}
+
+enum AdvertisingEventType : 8 {
+  ADV_IND = 0x00,
+  ADV_DIRECT_IND = 0x01,
+  ADV_SCAN_IND = 0x02,
+  ADV_NONCONN_IND = 0x03,
+  SCAN_RSP = 0x04,
+}
+
+enum AddressType : 8 {
+  PUBLIC_DEVICE_ADDRESS = 0x00,
+  RANDOM_DEVICE_ADDRESS = 0x01,
+  PUBLIC_IDENTITY_ADDRESS = 0x02,
+  RANDOM_IDENTITY_ADDRESS = 0x03,
+}
+
+packet LeSetAdvertisingParameters : LeAdvertisingCommand (op_code = LE_SET_ADVERTISING_PARAMETERS) {
+  advertising_interval_min : 16,
+  advertising_interval_max : 16,
+  advertising_type : AdvertisingEventType,
+  own_address_type : AddressType,
+  peer_address_type : PeerAddressType,
+  peer_address : Address,
+  advertising_channel_map : 8,
+  connection_filter_policy : AdvertisingFilterPolicy,
+  scan_filter_policy : AdvertisingFilterPolicy,
+  _reserved_ : 6,
+}
+
+packet LeSetAdvertisingParametersComplete : CommandComplete (command_op_code = LE_SET_ADVERTISING_PARAMETERS) {
+  status : ErrorCode,
+}
+
+packet LeReadAdvertisingChannelTxPower : LeAdvertisingCommand (op_code = LE_READ_ADVERTISING_CHANNEL_TX_POWER) {
+}
+
+packet LeReadAdvertisingChannelTxPowerComplete : CommandComplete (command_op_code = LE_READ_ADVERTISING_CHANNEL_TX_POWER) {
+  status : ErrorCode,
+  transmit_power_level : 8, // (-20dBm to 10dBm) Accuracy: +/-4dB
+}
+
+packet LeSetAdvertisingData : LeAdvertisingCommand (op_code = LE_SET_ADVERTISING_DATA) {
+  _payload_,
+}
+
+packet LeSetAdvertisingDataComplete : CommandComplete (command_op_code = LE_SET_ADVERTISING_DATA) {
+  status : ErrorCode,
+}
+
+packet LeSetScanResponseData : LeAdvertisingCommand (op_code = LE_SET_SCAN_RESPONSE_DATA) {
+  _payload_,
+}
+
+packet LeSetScanResponseDataComplete : CommandComplete (command_op_code = LE_SET_SCAN_RESPONSE_DATA) {
+  status : ErrorCode,
+}
+
+packet LeSetAdvertisingEnable : LeAdvertisingCommand (op_code = LE_SET_ADVERTISING_ENABLE) {
+  advertising_enable : Enable, // Default DISABLED
+}
+
+packet LeSetAdvertisingEnableComplete : CommandComplete (command_op_code = LE_SET_ADVERTISING_ENABLE) {
+  status : ErrorCode,
+}
+
+enum LeScanType : 8 {
+  PASSIVE = 0x00, // Default
+  ACTIVE = 0x01,
+}
+
+enum LeSetScanningFilterPolicy : 8 {
+  ACCEPT_ALL = 0x00, // Default
+  WHITE_LIST_ONLY = 0x01,
+  CHECK_INITIATORS_IDENTITY = 0x02,
+  WHITE_LIST_AND_INITIATORS_IDENTITY = 0x03,
+}
+
+packet LeSetScanParameters : LeAdvertisingCommand (op_code = LE_SET_SCAN_PARAMETERS) {
+  le_scan_type : LeScanType,
+  le_scan_interval : 16, // 0x0004-0x4000 Default 0x10 (10ms)
+  le_scan_window : 16, // Default 0x10 (10ms)
+  own_address_type : AddressType,
+  scanning_filter_policy : LeSetScanningFilterPolicy,
+}
+
+packet LeSetScanParametersComplete : CommandComplete (command_op_code = LE_SET_SCAN_PARAMETERS) {
+  status : ErrorCode,
+}
+
+packet LeSetScanEnable : LeAdvertisingCommand (op_code = LE_SET_SCAN_ENABLE) {
+  le_scan_enable : Enable,
+  filter_duplicates : Enable,
+}
+
+packet LeSetScanEnableComplete : CommandComplete (command_op_code = LE_SET_SCAN_ENABLE) {
+  status : ErrorCode,
+}
+
+enum InitiatorFilterPolicy : 8 {
+  USE_PEER_ADDRESS = 0x00,
+  USE_WHITE_LIST = 0x01,
+}
+
+enum OwnAddressType : 8 {
+  PUBLIC_DEVICE_ADDRESS = 0x00,
+  RANDOM_DEVICE_ADDRESS = 0x01,
+  RESOLVABLE_OR_PUBLIC_ADDRESS = 0x02,
+  RESOLVABLE_OR_RANDOM_ADDRESS = 0x03,
+}
+
+packet LeCreateConnection : LeConnectionManagementCommand (op_code = LE_CREATE_CONNECTION) {
+  le_scan_interval : 16, // 0x0004-0x4000
+  le_scan_window : 16, // < = LeScanInterval
+  initiator_filter_policy : InitiatorFilterPolicy,
+  peer_address_type : AddressType,
+  peer_address : Address,
+  own_address_type : OwnAddressType,
+  conn_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+  conn_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+  conn_latency : 16, // 0x0006-0x01F3
+  supervision_timeout : 16, // 0x00A to 0x0C80 (100ms to 32s)
+  minimum_ce_length : 16, // 0.625ms
+  maximum_ce_length : 16, // 0.625ms
+}
+
+packet LeCreateConnectionStatus : CommandStatus (command_op_code = LE_CREATE_CONNECTION) {
+}
+
+packet LeCreateConnectionCancel : LeConnectionManagementCommand (op_code = LE_CREATE_CONNECTION_CANCEL) {
+}
+
+packet LeCreateConnectionCancelComplete : CommandComplete (command_op_code = LE_CREATE_CONNECTION_CANCEL) {
+  status : ErrorCode,
+}
+
+packet LeReadWhiteListSize : LeConnectionManagementCommand (op_code = LE_READ_WHITE_LIST_SIZE) {
+}
+
+packet LeReadWhiteListSizeComplete : CommandComplete (command_op_code = LE_READ_WHITE_LIST_SIZE) {
+  status : ErrorCode,
+  white_list_size : 8,
+}
+
+packet LeClearWhiteList : LeConnectionManagementCommand (op_code = LE_CLEAR_WHITE_LIST) {
+}
+
+packet LeClearWhiteListComplete : CommandComplete (command_op_code = LE_CLEAR_WHITE_LIST) {
+  status : ErrorCode,
+}
+
+enum WhiteListAddressType : 8 {
+  PUBLIC = 0x00,
+  RANDOM = 0x01,
+  ANONYMOUS_ADVERTISERS = 0xFF,
+}
+
+packet LeAddDeviceToWhiteList : LeConnectionManagementCommand (op_code = LE_ADD_DEVICE_TO_WHITE_LIST) {
+  address_type : WhiteListAddressType,
+  address : Address,
+}
+
+packet LeAddDeviceToWhiteListComplete : CommandComplete (command_op_code = LE_ADD_DEVICE_TO_WHITE_LIST) {
+  status : ErrorCode,
+}
+
+packet LeRemoveDeviceFromWhiteList : LeConnectionManagementCommand (op_code = LE_REMOVE_DEVICE_FROM_WHITE_LIST) {
+  address_type : WhiteListAddressType,
+  address : Address,
+}
+
+packet LeRemoveDeviceFromWhiteListComplete : CommandComplete (command_op_code = LE_REMOVE_DEVICE_FROM_WHITE_LIST) {
+  status : ErrorCode,
+}
+
+packet LeConnectionUpdate : LeConnectionManagementCommand (op_code = LE_CONNECTION_UPDATE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  conn_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+  conn_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+  conn_latency : 16, // 0x0006-0x01F3
+  supervision_timeout : 16, // 0x00A to 0x0C80 (100ms to 32s)
+  minimum_ce_length : 16, // 0.625ms
+  maximum_ce_length : 16, // 0.625ms
+}
+
+packet LeConnectionUpdateStatus : CommandStatus (command_op_code = LE_CONNECTION_UPDATE) {
+}
+
+packet LeSetHostChannelClassification : LeConnectionManagementCommand (op_code = LE_SET_HOST_CHANNEL_CLASSIFICATION) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadChannelMap : LeConnectionManagementCommand (op_code = LE_READ_CHANNEL_MAP) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadRemoteFeatures : LeConnectionManagementCommand (op_code = LE_READ_REMOTE_FEATURES) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeEncrypt : LeSecurityCommand (op_code = LE_ENCRYPT) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeRand : LeSecurityCommand (op_code = LE_RAND) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeStartEncryption : LeSecurityCommand (op_code = LE_START_ENCRYPTION) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeLongTermKeyRequestReply : LeSecurityCommand (op_code = LE_LONG_TERM_KEY_REQUEST_REPLY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeLongTermKeyRequestNegativeReply : LeSecurityCommand (op_code = LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadSupportedStates : CommandPacket (op_code = LE_READ_SUPPORTED_STATES) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReceiverTest : CommandPacket (op_code = LE_RECEIVER_TEST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeTransmitterTest : CommandPacket (op_code = LE_TRANSMITTER_TEST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeTestEnd : CommandPacket (op_code = LE_TEST_END) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeRemoteConnectionParameterRequestReply : LeConnectionManagementCommand (op_code = LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeRemoteConnectionParameterRequestNegativeReply : LeConnectionManagementCommand (op_code = LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+
+packet LeSetDataLength : LeConnectionManagementCommand (op_code = LE_SET_DATA_LENGTH) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadSuggestedDefaultDataLength : LeConnectionManagementCommand (op_code = LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeWriteSuggestedDefaultDataLength : LeConnectionManagementCommand (op_code = LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadLocalP256PublicKeyCommand : LeSecurityCommand (op_code = LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeGenerateDhkeyCommand : LeSecurityCommand (op_code = LE_GENERATE_DHKEY_COMMAND) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeAddDeviceToResolvingList : LeSecurityCommand (op_code = LE_ADD_DEVICE_TO_RESOLVING_LIST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeRemoveDeviceFromResolvingList : LeSecurityCommand (op_code = LE_REMOVE_DEVICE_FROM_RESOLVING_LIST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeClearResolvingList : LeSecurityCommand (op_code = LE_CLEAR_RESOLVING_LIST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadResolvingListSize : LeSecurityCommand (op_code = LE_READ_RESOLVING_LIST_SIZE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadPeerResolvableAddress : LeSecurityCommand (op_code = LE_READ_PEER_RESOLVABLE_ADDRESS) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadLocalResolvableAddress : LeSecurityCommand (op_code = LE_READ_LOCAL_RESOLVABLE_ADDRESS) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetAddressResolutionEnable : LeSecurityCommand (op_code = LE_SET_ADDRESS_RESOLUTION_ENABLE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetResolvablePrivateAddressTimeout : LeSecurityCommand (op_code = LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadMaximumDataLength : CommandPacket (op_code = LE_READ_MAXIMUM_DATA_LENGTH) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadPhy : LeConnectionManagementCommand (op_code = LE_READ_PHY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetDefaultPhy : LeConnectionManagementCommand (op_code = LE_SET_DEFAULT_PHY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetPhy : LeConnectionManagementCommand (op_code = LE_SET_PHY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeEnhancedReceiverTest : CommandPacket (op_code = LE_ENHANCED_RECEIVER_TEST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeEnhancedTransmitterTest : CommandPacket (op_code = LE_ENHANCED_TRANSMITTER_TEST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetExtendedAdvertisingRandomAddress : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_RANDOM_ADDRESS) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetExtendedAdvertisingParameters : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_PARAMETERS) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetExtendedAdvertisingData : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_DATA) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetExtendedAdvertisingScanResponse : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_SCAN_RESPONSE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetExtendedAdvertisingEnable : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadMaximumAdvertisingDataLength : CommandPacket (op_code = LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadNumberOfSupportedAdvertisingSets : CommandPacket (op_code = LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeRemoveAdvertisingSet : LeAdvertisingCommand (op_code = LE_REMOVE_ADVERTISING_SET) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeClearAdvertisingSets : LeAdvertisingCommand (op_code = LE_CLEAR_ADVERTISING_SETS) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetPeriodicAdvertisingParam : LeAdvertisingCommand (op_code = LE_SET_PERIODIC_ADVERTISING_PARAM) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetPeriodicAdvertisingData : LeAdvertisingCommand (op_code = LE_SET_PERIODIC_ADVERTISING_DATA) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetPeriodicAdvertisingEnable : LeAdvertisingCommand (op_code = LE_SET_PERIODIC_ADVERTISING_ENABLE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetExtendedScanParameters : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_SCAN_PARAMETERS) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetExtendedScanEnable : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_SCAN_ENABLE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeExtendedCreateConnection : LeConnectionManagementCommand (op_code = LE_EXTENDED_CREATE_CONNECTION) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LePeriodicAdvertisingCreateSync : LeAdvertisingCommand (op_code = LE_PERIODIC_ADVERTISING_CREATE_SYNC) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LePeriodicAdvertisingCreateSyncCancel : LeAdvertisingCommand (op_code = LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LePeriodicAdvertisingTerminateSync : LeAdvertisingCommand (op_code = LE_PERIODIC_ADVERTISING_TERMINATE_SYNC) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeAddDeviceToPeriodicAdvertisingList : LeAdvertisingCommand (op_code = LE_ADD_DEVICE_TO_PERIODIC_ADVERTISING_LIST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeRemoveDeviceFromPeriodicAdvertisingList : LeAdvertisingCommand (op_code = LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISING_LIST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeClearPeriodicAdvertisingList : LeAdvertisingCommand (op_code = LE_CLEAR_PERIODIC_ADVERTISING_LIST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadPeriodicAdvertisingListSize : LeAdvertisingCommand (op_code = LE_READ_PERIODIC_ADVERTISING_LIST_SIZE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadTransmitPower : LeAdvertisingCommand (op_code = LE_READ_TRANSMIT_POWER) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadRfPathCompensationPower : LeAdvertisingCommand (op_code = LE_READ_RF_PATH_COMPENSATION_POWER) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeWriteRfPathCompensationPower : LeAdvertisingCommand (op_code = LE_WRITE_RF_PATH_COMPENSATION_POWER) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetPrivacyMode : LeSecurityCommand (op_code = LE_SET_PRIVACY_MODE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+
+  // VENDOR_SPECIFIC
+packet LeGetVendorCapabilities : VendorCommand (op_code = LE_GET_VENDOR_CAPABILITIES) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeMultiAdvt : VendorCommand (op_code = LE_MULTI_ADVT) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeBatchScan : VendorCommand (op_code = LE_BATCH_SCAN) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeAdvFilter : VendorCommand (op_code = LE_ADV_FILTER) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeTrackAdv : VendorCommand (op_code = LE_TRACK_ADV) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeEnergyInfo : VendorCommand (op_code = LE_ENERGY_INFO) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeExtendedScanParams : VendorCommand (op_code = LE_EXTENDED_SCAN_PARAMS) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet ControllerDebugInfo : VendorCommand (op_code = CONTROLLER_DEBUG_INFO) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet ControllerA2DPOpcode : VendorCommand (op_code = CONTROLLER_A2DP_OPCODE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+// HCI Event Packets
+
+packet InquiryComplete : EventPacket (event_code = INQUIRY_COMPLETE){
+  status : ErrorCode,
+}
+
+packet InquiryResult : EventPacket (event_code = INQUIRY_RESULT){
+  num_responses : 8,
+  bd_addr : Address,
+  page_scan_repetition_mode : PageScanRepetitionMode,
+  _reserved_ : 8,
+  _reserved_ : 8,
+  class_of_device : ClassOfDevice,
+  clock_offset : 15,
+  _reserved_ : 1,
+}
+
+enum LinkType : 8 {
+  SCO = 0x00,
+  ACL = 0x01,
+}
+
+packet ConnectionComplete : EventPacket (event_code = CONNECTION_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  bd_addr : Address,
+  link_type : LinkType,
+  encryption_enabled : Enable,
+}
+
+enum ConnectionRequestLinkType : 8 {
+  SCO = 0x00,
+  ACL = 0x01,
+  ESCO = 0x02,
+}
+
+packet ConnectionRequest : EventPacket (event_code = CONNECTION_REQUEST){
+  bd_addr : Address,
+  class_of_device : ClassOfDevice,
+  link_type : ConnectionRequestLinkType,
+}
+
+packet DisconnectionComplete : EventPacket (event_code = DISCONNECTION_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  reason : ErrorCode,
+}
+
+packet AuthenticationComplete : EventPacket (event_code = AUTHENTICATION_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet RemoteNameRequestComplete : EventPacket (event_code = REMOTE_NAME_REQUEST_COMPLETE){
+  status : ErrorCode,
+  bd_addr : Address,
+  _payload_,
+}
+
+enum EncryptionEnabled : 8 {
+  OFF = 0x00,
+  ON = 0x01, // E0 for BR/EDR and AES-CCM for LE
+  BR_EDR_AES_CCM = 0x02,
+}
+
+packet EncryptionChange : EventPacket (event_code = ENCRYPTION_CHANGE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  encryption_enabled : EncryptionEnabled,
+}
+
+packet ChangeConnectionLinkKeyComplete : EventPacket (event_code = CHANGE_CONNECTION_LINK_KEY_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet MasterLinkKeyComplete : EventPacket (event_code = MASTER_LINK_KEY_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  key_flag : KeyFlag,
+}
+
+packet ReadRemoteSupportedFeaturesComplete : EventPacket (event_code = READ_REMOTE_SUPPORTED_FEATURES_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  lmp_features : 64,
+}
+
+packet ReadRemoteVersionInformationComplete : EventPacket (event_code = READ_REMOTE_VERSION_INFORMATION_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  version : 8,
+  manufacturer_name : 8,
+  sub_version : 8,
+}
+
+enum ServiceType : 8 {
+  NO_TRAFFIC = 0x00,
+  BEST_EFFORT = 0x01,
+  GUARANTEED = 0x02,
+}
+
+packet QosSetupComplete : EventPacket (event_code = QOS_SETUP_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  _reserved_ : 8,
+  service_type : ServiceType,
+  token_rate : 32, // Octets/s
+  peak_bandwidth : 32, // Octets/s
+  latency : 32, // Octets/s
+  delay_variation : 32, // microseconds
+}
+
+// Command Complete and Command Status Events are implemented above Commands.
+
+packet HardwareError : EventPacket (event_code = HARDWARE_ERROR){
+  hardware_code : 8,
+}
+
+packet FlushOccurred : EventPacket (event_code = FLUSH_OCCURRED){
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+enum Role : 8 {
+  MASTER = 0x00,
+  SLAVE = 0x01,
+}
+
+packet RoleChange : EventPacket (event_code = ROLE_CHANGE){
+  status : ErrorCode,
+  bd_addr : Address,
+  new_role : Role,
+}
+
+packet NumberOfCompletedPackets : EventPacket (event_code = NUMBER_OF_COMPLETED_PACKETS){
+  _payload_,
+}
+
+enum Mode : 8 {
+  ACTIVE = 0x00,
+  HOLD = 0x01,
+  SNIFF = 0x02,
+}
+
+packet ModeChange : EventPacket (event_code = MODE_CHANGE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  current_mode : Mode,
+  interval : 16, // 0x002 - 0xFFFE (1.25ms - 40.9s)
+}
+
+packet ReturnLinkKeys : EventPacket (event_code = RETURN_LINK_KEYS){
+  _payload_, // placeholder (unimplemented)
+}
+
+packet PinCodeRequest : EventPacket (event_code = PIN_CODE_REQUEST){
+  bd_addr : Address,
+}
+
+packet LinkKeyRequest : EventPacket (event_code = LINK_KEY_REQUEST){
+  bd_addr : Address,
+}
+
+packet LinkKeyNotification : EventPacket (event_code = LINK_KEY_NOTIFICATION){
+  _payload_, // placeholder (unimplemented)
+}
+
+packet LoopbackCommand : EventPacket (event_code = LOOPBACK_COMMAND){
+  _payload_, // placeholder (unimplemented)
+}
+
+packet DataBufferOverflow : EventPacket (event_code = DATA_BUFFER_OVERFLOW){
+  link_type : LinkType,
+}
+
+packet MaxSlotsChange : EventPacket (event_code = MAX_SLOTS_CHANGE){
+  connection_handle : 12,
+  _reserved_ : 4,
+  lmp_max_slots : 8,
+}
+
+packet ReadClockOffsetComplete : EventPacket (event_code = READ_CLOCK_OFFSET_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  clock_offset : 15,
+  _reserved_ : 1,
+}
+
+packet ConnectionPacketTypeChange : EventPacket (event_code = CONNECTION_PACKET_TYPE_CHANGE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  packet_type : 16,
+}
+
+packet QosViolation : EventPacket (event_code = QOS_VIOLATION){
+  _payload_, // placeholder (unimplemented)
+}
+
+packet PageScanRepetitionModeChange : EventPacket (event_code = PAGE_SCAN_REPETITION_MODE_CHANGE){
+  _payload_, // placeholder (unimplemented)
+}
+
+packet FlowSpecificationComplete : EventPacket (event_code = FLOW_SPECIFICATION_COMPLETE){
+  _payload_, // placeholder (unimplemented)
+}
+
+packet InquiryResultWithRssi : EventPacket (event_code = INQUIRY_RESULT_WITH_RSSI){
+  num_responses : 8,
+  address : Address,
+  page_scan_repetition_mode : PageScanRepetitionMode,
+  _reserved_ : 8,
+  class_of_device : ClassOfDevice,
+  clock_offset : 15,
+  _reserved_ : 1,
+  rssi : 8,
+}
+
+packet ReadRemoteExtendedFeaturesComplete : EventPacket (event_code = READ_REMOTE_EXTENDED_FEATURES_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  page_number : 8,
+  maximum_page_number : 8,
+  extended_lmp_features : 64,
+}
+
+packet SynchronousConnectionComplete : EventPacket (event_code = SYNCHRONOUS_CONNECTION_COMPLETE){
+  _payload_, // placeholder (unimplemented)
+}
+
+packet SynchronousConnectionChanged : EventPacket (event_code = SYNCHRONOUS_CONNECTION_CHANGED){
+  _payload_, // placeholder (unimplemented)
+}
+
+packet SniffSubratingEvent : EventPacket (event_code = SNIFF_SUBRATING){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  maximum_transmit_latency : 16, // 0x000 - 0xFFFE (0s - 40.9s)
+  maximum_receive_latency : 16, // 0x000 - 0xFFFE (0s - 40.9s)
+  minimum_remote_timeout : 16, // 0x000 - 0xFFFE (0s - 40.9s)
+  MInimumLocalTimeout : 16, // 0x000 - 0xFFFE (0s - 40.9s)
+}
+
+packet ExtendedInquiryResult : EventPacket (event_code = EXTENDED_INQUIRY_RESULT) {
+  _fixed_ = 0x01 : 8,
+  address : Address,
+  page_scan_repetition_mode : PageScanRepetitionMode,
+  _reserved_ : 8,
+  class_of_device : ClassOfDevice,
+  clock_offset : 15,
+  _reserved_ : 1,
+  rssi : 8,
+  _payload_,
+}
+
+packet EncryptionKeyRefreshComplete : EventPacket (event_code = ENCRYPTION_KEY_REFRESH_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet IoCapabilityRequest : EventPacket (event_code = IO_CAPABILITY_REQUEST){
+  bd_addr : Address,
+}
+
+packet IoCapabilityResponse : EventPacket (event_code = IO_CAPABILITY_RESPONSE){
+  bd_addr : Address,
+  io_capability : IoCapability,
+  oob_data_present : OobDataPresent,
+  authentication_requirements : AuthenticationRequirements,
+}
+
+packet UserConfirmationRequest : EventPacket (event_code = USER_CONFIRMATION_REQUEST){
+  bd_addr : Address,
+  numeric_value : 20, // 0x00000-0xF423F (000000 - 999999)
+  _reserved_ : 12,
+}
+
+packet UserPasskeyRequest : EventPacket (event_code = USER_PASSKEY_REQUEST){
+  bd_addr : Address,
+}
+
+packet RemoteOobDataRequest : EventPacket (event_code = REMOTE_OOB_DATA_REQUEST){
+  bd_addr : Address,
+}
+
+packet SimplePairingComplete : EventPacket (event_code = SIMPLE_PAIRING_COMPLETE){
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet LinkSupervisionTimeoutChanged : EventPacket (event_code = LINK_SUPERVISION_TIMEOUT_CHANGED){
+  connection_handle : 12,
+  _reserved_ : 4,
+  link_supervision_timeout : 16, // 0x001-0xFFFF (0.625ms-40.9s)
+}
+
+packet EnhancedFlushComplete : EventPacket (event_code = ENHANCED_FLUSH_COMPLETE){
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet UserPasskeyNotification : EventPacket (event_code = USER_PASSKEY_NOTIFICATION){
+  bd_addr : Address,
+  passkey : 20, // 0x00000-0xF423F (000000 - 999999)
+  _reserved_ : 12,
+}
+
+packet KeypressNotification : EventPacket (event_code = KEYPRESS_NOTIFICATION){
+  bd_addr : Address,
+  notification_type : KeypressNotificationType,
+}
+
+packet RemoteHostSupportedFeaturesNotification : EventPacket (event_code = REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION){
+  bd_addr : Address,
+  host_supported_features : 64,
+}
+
+packet LeMetaEvent : EventPacket (event_code = LE_META_EVENT) {
+  subevent_code : SubeventCode,
+  _payload_,
+}
+
+packet NumberOfCompletedDataBlocks : EventPacket (event_code = NUMBER_OF_COMPLETED_DATA_BLOCKS){
+  _payload_, // placeholder (unimplemented)
+}
+
+// LE Events
+
+enum MasterClockAccuracy : 8 {
+  PPM_500 = 0x00,
+  PPM_250 = 0x01,
+  PPM_150 = 0x02,
+  PPM_100 = 0x03,
+  PPM_75 = 0x04,
+  PPM_50 = 0x05,
+  PPM_30 = 0x06,
+  PPM_20 = 0x07,
+}
+
+packet LeConnectionComplete : LeMetaEvent (subevent_code = CONNECTION_COMPLETE) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  role : Role,
+  peer_address_type : PeerAddressType,
+  peer_address : Address,
+  conn_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
+  conn_latency : 16,  // Number of connection events
+  supervision_timeout : 16,  // 0x000A to 0x0C80 (100ms to 32s)
+  master_clock_accuracy : MasterClockAccuracy,
+}
+
+packet LeAdvertisingReport : LeMetaEvent (subevent_code = ADVERTISING_REPORT) {
+  _payload_,
+}
+
+packet LeConnectionUpdateComplete : LeMetaEvent (subevent_code = CONNECTION_UPDATE_COMPLETE) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  conn_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
+  conn_latency : 16,  // Number of connection events
+  supervision_timeout : 16,  // 0x000A to 0x0C80 (100ms to 32s)
+}
+
+packet LeReadRemoteFeaturesComplete : LeMetaEvent (subevent_code = READ_REMOTE_FEATURES_COMPLETE) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  le_features : 64,
+}
+
+packet LeLongTermKeyRequest : LeMetaEvent (subevent_code = LONG_TERM_KEY_REQUEST) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  random_number : 64,
+  encrypted_diversifier : 16,
+}
+
+packet LeRemoteConnectionParameterRequest : LeMetaEvent (subevent_code = REMOTE_CONNECTION_PARAMETER_REQUEST) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  interval_min : 16, // 0x006 - 0x0C80 (7.5ms - 4s)
+  interval_max : 16, // 0x006 - 0x0C80 (7.5ms - 4s)
+  latency : 16,  // Number of connection events (0x0000 to 0x01f3 (499)
+  timeout : 16,  // 0x000A to 0x0C80 (100ms to 32s)
+}
+
+packet LeDataLengthChange : LeMetaEvent (subevent_code = DATA_LENGTH_CHANGE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  max_tx_octets : 16, // 0x001B - 0x00FB
+  max_tx_time : 16, // 0x0148 - 0x4290
+  max_rx_octets : 16, // 0x001B - 0x00FB
+  max_rx_time : 16, // 0x0148 - 0x4290
+}
+
+packet ReadLocalP256PublicKeyComplete : LeMetaEvent (subevent_code = READ_LOCAL_P256_PUBLIC_KEY_COMPLETE) {
+  status : ErrorCode,
+  _payload_,
+}
+
+packet GenerateDhKeyComplete : LeMetaEvent (subevent_code = GENERATE_DHKEY_COMPLETE) {
+  status : ErrorCode,
+  _payload_,
+}
+
+packet LeEnhancedConnectionComplete : LeMetaEvent (subevent_code = ENHANCED_CONNECTION_COMPLETE) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  role : Role,
+  peer_address_type : PeerAddressType,
+  peer_address : Address,
+  local_resolvable_private_address : Address,
+  peer_resolvable_private_address : Address,
+  conn_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
+  conn_latency : 16,  // Number of connection events
+  supervision_timeout : 16,  // 0x000A to 0x0C80 (100ms to 32s)
+  master_clock_accuracy : MasterClockAccuracy,
+}
+
+enum DirectAdvertisingAddressType : 8 {
+  PUBLIC_DEVICE_ADDRESS = 0x00,
+  RANDOM_DEVICE_ADDRESS = 0x01,
+  PUBLIC_IDENTITY_ADDRESS = 0x02,
+  RANDOM_IDENTITY_ADDRESS = 0x03,
+  NO_ADDRESS = 0xFF,
+}
+
+enum DirectAdvertisingEventType : 8 {
+  ADV_DIRECT_IND = 0x01,
+}
+
+enum DirectAddressType : 8 {
+  RANDOM_DEVICE_ADDRESS = 0x01,
+}
+
+packet LeDirectedAdvertisingReport : LeMetaEvent (subevent_code = DIRECTED_ADVERTISING_REPORT) {
+  _payload_, // placeholder (unimplemented)
+}
+
+packet LePhyUpdateComplete : LeMetaEvent (subevent_code = PHY_UPDATE_COMPLETE) {
+  _payload_, // placeholder (unimplemented)
+}
+
+packet LeExtendedAdvertisingReport : LeMetaEvent (subevent_code = EXTENDED_ADVERTISING_REPORT) {
+  _payload_, // placeholder (unimplemented)
+}
+
+packet LePeriodicAdvertisingSyncEstablished : LeMetaEvent (subevent_code = PERIODIC_ADVERTISING_SYNC_ESTABLISHED) {
+  _payload_, // placeholder (unimplemented)
+}
+
+packet LePeriodicAdvertisingReport : LeMetaEvent (subevent_code = PERIODIC_ADVERTISING_REPORT) {
+  _payload_, // placeholder (unimplemented)
+}
+
+packet LePeriodicAdvertisingSyncLost : LeMetaEvent (subevent_code = PERIODIC_ADVERTISING_SYNC_LOST) {
+  _payload_, // placeholder (unimplemented)
+}
+
+packet LeScanTimeout : LeMetaEvent (subevent_code = SCAN_TIMEOUT) {
+}
+
+packet LeAdvertisingSetTerminated : LeMetaEvent (subevent_code = ADVERTISING_SET_TERMINATED) {
+  _payload_, // placeholder (unimplemented)
+}
+
+packet LeScanRequestReceived : LeMetaEvent (subevent_code = SCAN_REQUEST_RECEIVED) {
+  _payload_, // placeholder (unimplemented)
+}
diff --git a/gd/l2cap/Android.bp b/gd/l2cap/Android.bp
new file mode 100644
index 0000000..07808f5
--- /dev/null
+++ b/gd/l2cap/Android.bp
@@ -0,0 +1,7 @@
+filegroup {
+    name: "BluetoothL2capTestSources",
+    srcs: [
+        "l2cap_packet_test.cc",
+        "fcs.cc",
+    ],
+}
diff --git a/gd/l2cap/fcs.cc b/gd/l2cap/fcs.cc
new file mode 100644
index 0000000..380ff66
--- /dev/null
+++ b/gd/l2cap/fcs.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 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 "l2cap/fcs.h"
+
+namespace {
+// Table for optimizing the CRC calculation, which is a bitwise operation.
+static const uint16_t crctab[256] = {
+    0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1,
+    0xc481, 0x0440, 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 0x0a00, 0xcac1, 0xcb81, 0x0b40,
+    0xc901, 0x09c0, 0x0880, 0xc841, 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, 0x1e00, 0xdec1,
+    0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
+    0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1,
+    0xf281, 0x3240, 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 0x3c00, 0xfcc1, 0xfd81, 0x3d40,
+    0xff01, 0x3fc0, 0x3e80, 0xfe41, 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 0x2800, 0xe8c1,
+    0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
+    0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0,
+    0x2080, 0xe041, 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, 0x6600, 0xa6c1, 0xa781, 0x6740,
+    0xa501, 0x65c0, 0x6480, 0xa441, 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 0xaa01, 0x6ac0,
+    0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
+    0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1,
+    0xb681, 0x7640, 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, 0x5000, 0x90c1, 0x9181, 0x5140,
+    0x9301, 0x53c0, 0x5280, 0x9241, 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, 0x9c01, 0x5cc0,
+    0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
+    0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0,
+    0x4c80, 0x8c41, 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 0x8201, 0x42c0, 0x4380, 0x8341,
+    0x4100, 0x81c1, 0x8081, 0x4040,
+};
+}  // namespace
+
+namespace bluetooth {
+namespace l2cap {
+
+void Fcs::Initialize(Fcs& s) {
+  s.crc = 0;
+}
+
+void Fcs::AddByte(Fcs& s, uint8_t byte) {
+  s.crc = ((s.crc >> 8) & 0x00ff) ^ crctab[(s.crc & 0x00ff) ^ byte];
+}
+
+uint16_t Fcs::GetChecksum(const Fcs& s) {
+  return s.crc;
+}
+
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/fcs.h b/gd/l2cap/fcs.h
new file mode 100644
index 0000000..42dd7dc
--- /dev/null
+++ b/gd/l2cap/fcs.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019 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 <cstdint>
+
+namespace bluetooth {
+namespace l2cap {
+
+// Frame Check Sequence from the L2CAP spec.
+class Fcs {
+ public:
+  static void Initialize(Fcs& s);
+
+  static void AddByte(Fcs& s, uint8_t byte);
+
+  static uint16_t GetChecksum(const Fcs& s);
+
+ private:
+  uint16_t crc;
+};
+
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/l2cap_packet_test.cc b/gd/l2cap/l2cap_packet_test.cc
new file mode 100644
index 0000000..683d060
--- /dev/null
+++ b/gd/l2cap/l2cap_packet_test.cc
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2019 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 "l2cap/l2cap_packets.h"
+
+#include <gtest/gtest.h>
+#include <forward_list>
+#include <memory>
+
+#include "os/log.h"
+#include "packet/bit_inserter.h"
+#include "packet/raw_builder.h"
+
+using bluetooth::packet::BitInserter;
+using bluetooth::packet::RawBuilder;
+using std::vector;
+
+namespace {
+vector<uint8_t> extended_information_start_frame = {
+    0x0B /* First size byte */,
+    0x00 /* Second size byte */,
+    0xc1 /* First ChannelId byte */,
+    0xc2,
+    0x4A /* 0x12 ReqSeq, Final, IFrame */,
+    0xD0 /* 0x13 ReqSeq */,
+    0x89 /* 0x21 TxSeq sar = START */,
+    0x8C /* 0x23 TxSeq  */,
+    0x10 /* first length byte */,
+    0x11,
+    0x01 /* first payload byte */,
+    0x02,
+    0x03,
+    0x04,
+    0x05,
+};
+}  // namespace
+
+namespace bluetooth {
+namespace l2cap {
+
+TEST(L2capPacketTest, extendedInformationStartFrameTest) {
+  uint16_t channel_id = 0xc2c1;
+  uint16_t l2cap_sdu_length = 0x1110;
+  Final f = Final::POLL_RESPONSE;
+  uint16_t req_seq = 0x1312;
+  uint16_t tx_seq = 0x2321;
+
+  std::unique_ptr<RawBuilder> payload = std::make_unique<RawBuilder>();
+  payload->AddOctets4(0x04030201);
+  payload->AddOctets1(0x05);
+
+  auto packet = ExtendedInformationStartFrameBuilder::Create(channel_id, f, req_seq, tx_seq, l2cap_sdu_length,
+                                                             std::move(payload));
+
+  ASSERT_EQ(extended_information_start_frame.size(), packet->size());
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+  PacketView<true> packet_bytes_view(packet_bytes);
+  ASSERT_EQ(extended_information_start_frame.size(), packet_bytes_view.size());
+
+  BasicFrameView basic_frame_view = BasicFrameView::Create(packet_bytes_view);
+  ASSERT_TRUE(basic_frame_view.IsValid());
+  ASSERT_EQ(channel_id, basic_frame_view.GetChannelId());
+
+  StandardFrameView standard_frame_view = StandardFrameView::Create(basic_frame_view);
+  ASSERT_TRUE(standard_frame_view.IsValid());
+  ASSERT_EQ(FrameType::I_FRAME, standard_frame_view.GetFrameType());
+}
+
+vector<uint8_t> i_frame_with_fcs = {
+    0x0E, 0x00, 0x40, 0x00, 0x02, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x38, 0x61,
+};
+TEST(L2capPacketTest, iFrameWithFcsTest) {
+  uint16_t channel_id = 0x0040;
+  SegmentationAndReassembly sar = SegmentationAndReassembly::UNSEGMENTED;  // 0
+  uint16_t req_seq = 0;
+  uint16_t tx_seq = 1;
+  RetransmissionDisable r = RetransmissionDisable::NORMAL;  // 0
+
+  std::unique_ptr<RawBuilder> payload = std::make_unique<RawBuilder>();
+  vector<uint8_t> payload_bytes = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
+  payload->AddOctets(payload_bytes);
+
+  auto packet = StandardInformationFrameWithFcsBuilder::Create(channel_id, tx_seq, r, req_seq, sar, std::move(payload));
+
+  ASSERT_EQ(i_frame_with_fcs.size(), packet->size());
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+  PacketView<true> packet_bytes_view(packet_bytes);
+  ASSERT_EQ(i_frame_with_fcs.size(), packet_bytes_view.size());
+
+  for (size_t i = 0; i < i_frame_with_fcs.size(); i++) {
+    ASSERT_EQ(i_frame_with_fcs[i], packet_bytes_view[i]);
+  }
+
+  BasicFrameWithFcsView basic_frame_view = BasicFrameWithFcsView::Create(packet_bytes_view);
+  ASSERT_TRUE(basic_frame_view.IsValid());
+  ASSERT_EQ(channel_id, basic_frame_view.GetChannelId());
+
+  StandardFrameWithFcsView standard_frame_view = StandardFrameWithFcsView::Create(basic_frame_view);
+  ASSERT_TRUE(standard_frame_view.IsValid());
+  ASSERT_EQ(FrameType::I_FRAME, standard_frame_view.GetFrameType());
+
+  StandardInformationFrameWithFcsView information_frame_view =
+      StandardInformationFrameWithFcsView::Create(standard_frame_view);
+  ASSERT_TRUE(information_frame_view.IsValid());
+  ASSERT_EQ(sar, information_frame_view.GetSar());
+  ASSERT_EQ(req_seq, information_frame_view.GetReqSeq());
+  ASSERT_EQ(tx_seq, information_frame_view.GetTxSeq());
+  ASSERT_EQ(r, information_frame_view.GetR());
+}
+
+vector<uint8_t> rr_frame_with_fcs = {
+    0x04, 0x00, 0x40, 0x00, 0x01, 0x01, 0xD4, 0x14,
+};
+TEST(L2capPacketTest, rrFrameWithFcsTest) {
+  uint16_t channel_id = 0x0040;
+  SupervisoryFunction s = SupervisoryFunction::RECEIVER_READY;  // 0
+  RetransmissionDisable r = RetransmissionDisable::NORMAL;      // 0
+  uint16_t req_seq = 1;
+
+  auto packet = StandardSupervisoryFrameWithFcsBuilder::Create(channel_id, s, r, req_seq);
+
+  ASSERT_EQ(rr_frame_with_fcs.size(), packet->size());
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+  PacketView<true> packet_bytes_view(packet_bytes);
+  ASSERT_EQ(rr_frame_with_fcs.size(), packet_bytes_view.size());
+
+  for (size_t i = 0; i < rr_frame_with_fcs.size(); i++) {
+    ASSERT_EQ(rr_frame_with_fcs[i], packet_bytes_view[i]);
+  }
+
+  BasicFrameWithFcsView basic_frame_view = BasicFrameWithFcsView::Create(packet_bytes_view);
+  ASSERT_TRUE(basic_frame_view.IsValid());
+  ASSERT_EQ(channel_id, basic_frame_view.GetChannelId());
+
+  StandardFrameWithFcsView standard_frame_view = StandardFrameWithFcsView::Create(basic_frame_view);
+  ASSERT_TRUE(standard_frame_view.IsValid());
+  ASSERT_EQ(FrameType::S_FRAME, standard_frame_view.GetFrameType());
+
+  StandardSupervisoryFrameWithFcsView supervisory_frame_view =
+      StandardSupervisoryFrameWithFcsView::Create(standard_frame_view);
+  ASSERT_TRUE(supervisory_frame_view.IsValid());
+  ASSERT_EQ(s, supervisory_frame_view.GetS());
+  ASSERT_EQ(r, supervisory_frame_view.GetR());
+  ASSERT_EQ(req_seq, supervisory_frame_view.GetReqSeq());
+}
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/l2cap_packets.pdl b/gd/l2cap/l2cap_packets.pdl
new file mode 100644
index 0000000..b61d834
--- /dev/null
+++ b/gd/l2cap/l2cap_packets.pdl
@@ -0,0 +1,631 @@
+little_endian_packets
+
+checksum Fcs : 16 "l2cap/"
+
+packet BasicFrame {
+  _size_(_payload_) : 16,
+  channel_id : 16,
+  _payload_,
+}
+
+packet BasicFrameWithFcs {
+  _checksum_start_(fcs),
+  _size_(_payload_) : 16,
+  channel_id : 16,
+  _payload_ : [+2*8], // Include Fcs in the _size_
+  fcs : Fcs,
+}
+
+// ChannelId 2 is connectionless
+packet GroupFrame : BasicFrame (channel_id = 0x02) {
+  psm : 16,
+  _payload_,
+}
+
+enum FrameType : 1 {
+  I_FRAME = 0,
+  S_FRAME = 1,
+}
+
+enum SupervisoryFunction : 2 {
+  RECEIVER_READY = 0,
+  REJECT = 1,
+  RECEIVER_NOT_READY = 2,
+  SELECT_REJECT = 3,
+}
+
+enum RetransmissionDisable : 1 {
+  NORMAL = 0,
+  DISABLE = 1,
+}
+
+enum SegmentationAndReassembly : 2 {
+  UNSEGMENTED = 0,
+  START = 1,
+  END = 2,
+  CONTINUATION = 3,
+}
+
+packet StandardFrame : BasicFrame {
+  frame_type : FrameType,
+  _body_,
+}
+
+packet StandardFrameWithFcs : BasicFrameWithFcs {
+  frame_type : FrameType,
+  _body_,
+}
+
+group StandardSupervisoryControl {
+  _fixed_ = 0 : 1,
+  S : SupervisoryFunction,
+  _reserved_ : 3,
+  R : RetransmissionDisable,
+  req_seq : 6,
+  _reserved_ : 2,
+}
+
+group StandardInformationControl {
+  tx_seq : 6,
+  R : RetransmissionDisable,
+  req_seq : 6,
+  sar : SegmentationAndReassembly,
+}
+
+packet StandardSupervisoryFrame : StandardFrame (frame_type = S_FRAME) {
+  StandardSupervisoryControl,
+}
+
+packet StandardSupervisoryFrameWithFcs : StandardFrameWithFcs (frame_type = S_FRAME) {
+  StandardSupervisoryControl,
+}
+
+packet StandardInformationFrame : StandardFrame (frame_type = I_FRAME) {
+  StandardInformationControl,
+  _payload_,
+}
+
+packet StandardInformationFrameWithFcs : StandardFrameWithFcs (frame_type = I_FRAME) {
+  StandardInformationControl,
+  _payload_,
+}
+
+packet StandardInformationStartFrame : StandardInformationFrame (sar = START) {
+  L2capSduLength : 16,
+  _payload_,
+}
+
+packet StandardInformationStartFrameWithFcs : StandardInformationFrameWithFcs (sar = START) {
+  L2capSduLength : 16,
+  _payload_,
+}
+
+enum Poll : 1 {
+  NOT_SET = 0,
+  POLL = 1,
+}
+
+enum Final : 1 {
+  NOT_SET = 0,
+  POLL_RESPONSE = 1,
+}
+
+group EnhancedSupervisoryControl {
+  _fixed_ = 0 : 1,
+  S : SupervisoryFunction,
+  P : Poll,
+  _reserved_ : 2,
+  F : Final,
+  req_seq : 6,
+  _reserved_ : 2,
+}
+
+group EnhancedInformationControl {
+  tx_seq : 6,
+  F : Final,
+  req_seq : 6,
+  sar : SegmentationAndReassembly,
+}
+
+packet EnhancedSupervisoryFrame : StandardFrame (frame_type = S_FRAME) {
+  EnhancedSupervisoryControl,
+}
+
+packet EnhancedSupervisoryFrameWithFcs : StandardFrameWithFcs (frame_type = S_FRAME) {
+  EnhancedSupervisoryControl,
+}
+
+packet EnhancedInformationFrame : StandardFrame (frame_type = I_FRAME) {
+  EnhancedInformationControl,
+  _payload_,
+}
+
+packet EnhancedInformationFrameWithFcs : StandardFrameWithFcs (frame_type = I_FRAME) {
+  EnhancedInformationControl,
+  _payload_,
+}
+
+packet EnhancedInformationStartFrame : EnhancedInformationFrame (sar = START) {
+  L2capSduLength : 16,
+  _payload_,
+}
+
+packet EnhancedInformationStartFrameWithFcs : EnhancedInformationFrameWithFcs (sar = START) {
+  L2capSduLength : 16,
+  _payload_,
+}
+
+group ExtendedSupervisoryControl {
+  F : Final,
+  req_seq : 14,
+  S : SupervisoryFunction,
+  P : Poll,
+  _reserved_ : 5,
+  _reserved_ : 8,
+}
+
+group ExtendedInformationControl {
+  F : Final,
+  req_seq : 14,
+  sar : SegmentationAndReassembly,
+  tx_seq : 14,
+}
+
+packet ExtendedSupervisoryFrame : StandardFrame (frame_type = S_FRAME) {
+  ExtendedSupervisoryControl,
+}
+
+packet ExtendedSupervisoryFrameWithFcs : StandardFrameWithFcs (frame_type = S_FRAME) {
+  ExtendedSupervisoryControl,
+}
+
+packet ExtendedInformationFrame : StandardFrame (frame_type = I_FRAME) {
+  ExtendedInformationControl,
+  _payload_,
+}
+
+packet ExtendedInformationFrameWithFcs : StandardFrameWithFcs (frame_type = I_FRAME) {
+  ExtendedInformationControl,
+  _payload_,
+}
+
+packet ExtendedInformationStartFrame : ExtendedInformationFrame (sar = START) {
+  L2capSduLength : 16,
+  _payload_,
+}
+
+packet ExtendedInformationStartFrameWithFcs : ExtendedInformationFrameWithFcs (sar = START) {
+  L2capSduLength : 16,
+  _payload_,
+}
+
+packet FirstLeInformationFrame : BasicFrame {
+  L2capSduLength : 16,
+  _payload_,
+}
+
+enum CommandCode : 8 {
+  COMMAND_REJECT = 0x01,
+  CONNECTION_REQUEST = 0x02,
+  CONNECTION_RESPONSE = 0x03,
+  CONFIGURATION_REQUEST = 0x04,
+  CONFIGURATION_RESPONSE = 0x05,
+  DISCONNECTION_REQUEST = 0x06,
+  DISCONNECTION_RESPONSE = 0x07,
+  ECHO_REQUEST = 0x08,
+  ECHO_RESPONSE = 0x09,
+  INFORMATION_REQUEST = 0x0A,
+  INFORMATION_RESPONSE = 0x0B,
+  CREATE_CHANNEL_REQUEST = 0x0C,
+  CREATE_CHANNEL_RESPONSE = 0x0D,
+  MOVE_CHANNEL_REQUEST = 0x0E,
+  MOVE_CHANNEL_RESPONSE = 0x0F,
+  MOVE_CHANNEL_CONFIRMATION_REQUEST = 0x10,
+  MOVE_CHANNEL_CONFIRMATION_RESPONSE = 0x11,
+}
+
+packet ControlFrame : BasicFrame (channel_id = 0x0001) {
+  code : CommandCode,
+  identifier : 8,
+  _size_(_payload_) : 16,
+  _payload_,
+}
+
+
+enum CommandRejectReason : 16 {
+  COMMAND_NOT_UNDERSTOOD = 0x0000,
+  SIGNALING_MTU_EXCEEDED = 0x0001,
+  INVALID_CID_IN_REQUEST = 0x0002,
+}
+
+packet CommandReject : ControlFrame (code = COMMAND_REJECT) {
+  reason : CommandRejectReason,
+  _body_,
+}
+
+packet CommandRejectNotUnderstood : CommandReject (reason = COMMAND_NOT_UNDERSTOOD) {
+}
+
+packet CommandRejectMtuExceeded : CommandReject (reason = SIGNALING_MTU_EXCEEDED) {
+  actual_mtu : 16,
+}
+
+packet CommandRejectInvalidCid : CommandReject (reason = INVALID_CID_IN_REQUEST) {
+  local_channel : 16, // Relative to the sender of the CommandReject
+  remote_channel : 16,
+}
+
+packet ConnectionRequest : ControlFrame (code = CONNECTION_REQUEST) {
+  psm : 16,
+  source_cid : 16,
+}
+
+enum ConnectionResponseResult : 16 {
+  SUCCESS = 0x0000,
+  PENDING = 0x0001,
+  PSM_NOT_SUPPORTED = 0x0002,
+  SECURITY_BLOCK = 0x0003,
+  NO_RESOURCES_AVAILABLE = 0x0004,
+  INVALID_CID = 0x0006,
+  SOURCE_CID_ALREADY_ALLOCATED = 0x0007,
+}
+
+enum ConnectionResponseStatus : 16 {
+  NO_FURTHER_INFORMATION_AVAILABLE = 0x0000,
+  AUTHENTICATION_PENDING = 0x0001,
+  AUTHORIZATION_PENDING = 0x0002,
+}
+
+packet ConnectionResponse : ControlFrame (code = CONNECTION_RESPONSE) {
+  destination_cid : 16,
+  source_cid : 16,
+  result : ConnectionResponseResult,
+  status : ConnectionResponseStatus,
+}
+
+enum ConfigurationContinuation : 1 {
+  END = 0,
+  CONTINUES = 1,
+}
+
+packet ConfigurationRequestBase : ControlFrame (code = CONFIGURATION_REQUEST) {
+  destination_cid : 16,
+  continuation : ConfigurationContinuation,
+  _reserved_ : 15,
+  _payload_,
+}
+
+enum ConfigurationOptionsType : 7 {
+  MTU = 0x01,
+  FLUSH_TIMEOUT = 0x02,
+  QUALITY_OF_SERVICE = 0x03,
+  RETRANSMISSION_AND_FLOW_CONTROL = 0x04,
+  FRAME_CHECK_SEQUENCE = 0x05,
+  EXTENDED_FLOW_SPECIFICATION = 0x06,
+  EXTENDED_WINDOW_SIZE = 0x07,
+}
+
+enum ConfigurationOptionIsHint : 1 {
+  OPTION_MUST_BE_RECOGNIZED = 0,
+  OPTION_IS_A_HINT = 1,
+}
+
+packet ConfigurationOptions {
+  type : ConfigurationOptionsType,
+  is_hint : ConfigurationOptionIsHint,
+  _size_(_payload_) : 8,
+  _payload_,
+}
+
+packet MtuConfigurationOption : ConfigurationOptions (type = MTU) {
+  mtu : 16,
+}
+
+packet FlushTimeoutConfigurationOption : ConfigurationOptions (type = FLUSH_TIMEOUT) {
+  flush_timeout : 16,
+}
+
+enum QosServiceType : 8 {
+  NO_TRAFFIC = 0x00,
+  BEST_EFFORT = 0x01, // Default
+  GUARANTEED = 0x02,
+}
+
+packet QualityOfServiceConfigurationOption : ConfigurationOptions (type = QUALITY_OF_SERVICE) {
+  _reserved_ : 8, // Flags
+  service_type : QosServiceType,
+  token_rate : 32,        // 0 = ignore, 0xffffffff = max available
+  token_bucket_size : 32,  // 0 = ignore, 0xffffffff = max available
+  peak_bandwidth : 32,    // Octets/second 0 = ignore
+  latency : 32,          // microseconds 0xffffffff = ignore
+  delay_variation : 32,   // microseconds 0xffffffff = ignore
+}
+
+enum RetransmissionAndFlowControlModeOption : 8 {
+  L2CAP_BASIC = 0x00,
+  RETRANSMISSION = 0x01,
+  FLOW_CONTROL = 0x02,
+  ENHANCED_RETRANSMISSION = 0x03,
+  STREAMING = 0x04,
+}
+
+
+packet RetransmissionAndFlowControlConfigurationOption : ConfigurationOptions (type = RETRANSMISSION_AND_FLOW_CONTROL) {
+  mode : RetransmissionAndFlowControlModeOption,
+  tx_window_size : 8, // 1-32 for Flow Control and Retransmission, 1-63 for Enhanced
+  max_transmit : 8,
+  retransmission_time_out : 8,
+  monitor_time_out : 16,
+  maximum_pdu_size : 16,
+}
+
+enum FcsType : 8 {
+  NO_FCS = 0,
+  DEFAULT = 1,  // 16-bit FCS
+}
+
+packet FrameCheckSequenceOption : ConfigurationOptions (type = FRAME_CHECK_SEQUENCE) {
+  fcs_type : FcsType,
+}
+
+
+packet ExtendedFlowSpecificationOption : ConfigurationOptions (type = EXTENDED_FLOW_SPECIFICATION) {
+  identifier : 8, // Default 0x01, must be 0x01 for Extended Flow-Best-Effort
+  service_type : QosServiceType,
+  maximum_sdu_size : 16, // Octets
+  sdu_interarrival_time : 32, // in microseconds
+  access_latency : 32, // in microseconds, without HCI and stack overheads
+  flush_timeout : 32, // in microseconds 0x0 = no retransmissions 0xFFFFFFFF = never flushed
+}
+
+packet ExtendedWindowSizeOption : ConfigurationOptions (type = EXTENDED_WINDOW_SIZE) {
+  max_window_size : 16, // 0x0000 = Valid for streaming, 0x0001-0x3FFF Valid for Enhanced Retransmission
+}
+
+packet ConfigurationRequest : ConfigurationRequestBase {
+  _payload_,
+}
+
+enum ConfigurationResponseResult : 16 {
+  SUCCESS = 0x0000,
+  UNACCEPTABLE_PARAMETERS = 0x0001,
+  REJECTED = 0x0002,
+  UNKNOWN_OPTIONS = 0x0003,
+  PENDING = 0x0004,
+  FLOW_SPEC_REJECTED = 0x0005,
+}
+
+packet ConfigurationResponseBase : ControlFrame (code = CONFIGURATION_RESPONSE) {
+  source_cid : 16,
+  continuation : ConfigurationContinuation,
+  _reserved_ : 15,
+  result : ConfigurationResponseResult,
+  _payload_,
+}
+
+packet ConfigurationResponse : ConfigurationResponseBase {
+  _payload_,
+}
+
+packet DisconnectionRequest : ControlFrame (code = DISCONNECTION_REQUEST) {
+  destination_cid : 16,
+  source_cid : 16,
+}
+
+packet DisconnectionResponse : ControlFrame (code = DISCONNECTION_RESPONSE) {
+  destination_cid : 16,
+  source_cid : 16,
+}
+
+packet EchoRequest : ControlFrame (code = ECHO_REQUEST) {
+  _payload_, // Optional and implementation specific
+}
+
+packet EchoResponse : ControlFrame (code = ECHO_RESPONSE) {
+  _payload_, // Optional and implementation specific
+}
+
+enum InformationRequestInfoType : 16 {
+  CONNECTIONLESS_MTU = 0x0001,
+  EXTENDED_FEATURES_SUPPORTED = 0x0002,
+  FIXED_CHANNELS_SUPPORTED = 0x0003,
+}
+
+packet InformationRequest : ControlFrame (code = INFORMATION_REQUEST) {
+  info_type : InformationRequestInfoType,
+}
+
+enum InformationRequestResult : 16 {
+  SUCCESS = 0x0000,
+  NOT_SUPPORTED = 0x0001,
+}
+
+packet InformationResponse : ControlFrame (code = INFORMATION_RESPONSE) {
+  info_type : InformationRequestInfoType,
+  result : InformationRequestResult,
+  _body_,
+}
+
+packet InformationResponseConnectionlessMtu : InformationResponse (info_type = CONNECTIONLESS_MTU) {
+  connectionless_mtu : 16,
+}
+
+packet InformationResponseExtendedFeatures : InformationResponse (info_type = EXTENDED_FEATURES_SUPPORTED) {
+  // ExtendedFeatureMask : 32,
+  flow_control_mode : 1,
+  retransmission_mode : 1,
+  bi_directional_qoS : 1,
+  enhanced_retransmission_mode : 1,
+  streaming_mode : 1,
+  fcs_option : 1,
+  extended_flow_specification_for_br_edr : 1,
+  fixed_channels : 1,
+  extended_window_size : 1,
+  unicast_connectionless_data_reception : 1,
+  _reserved_ : 22,
+}
+
+packet InformationResponseFixedChannels : InformationResponse (info_type = FIXED_CHANNELS_SUPPORTED) {
+  fixed_channels : 64, // bit 0 must be 0, bit 1 must be 1, all others 1 = supported
+}
+
+packet CreateChannelRequest : ControlFrame (code = CREATE_CHANNEL_REQUEST) {
+  psm : 16,
+  source_cid : 16,
+  controller_id : 8,
+}
+
+enum CreateChannelResponseResult : 16 {
+  SUCCESS = 0x0000,
+  PENDING = 0x0001,
+  PSM_NOT_SUPPORTED = 0x0002,
+  SECURITY_BLOCK = 0x0003,
+  NO_RESOURCES_AVAILABLE = 0x0004,
+  CONTROLLER_ID_NOT_SUPPORTED = 0x0005,
+  INVALID_CID = 0x0006,
+  SOURCE_CID_ALREADY_ALLOCATED = 0x0007,
+}
+
+enum CreateChannelResponseStatus : 16 {
+  NO_FURTHER_INFORMATION_AVAILABLE = 0x0000,
+  AUTHENTICATION_PENDING = 0x0001,
+  AUTHORIZATION_PENDING = 0x0002,
+}
+
+packet CreateChannelResponse : ControlFrame (code = CREATE_CHANNEL_RESPONSE) {
+  destination_cid : 16,
+  source_cid : 16,
+  result : CreateChannelResponseResult,
+  status : CreateChannelResponseStatus,
+}
+
+// AMP Only ?
+packet MoveChannelRequest : ControlFrame (code = MOVE_CHANNEL_REQUEST) {
+  initiator_cid : 16,
+  dest_controller_id : 8,
+}
+
+enum MoveChannelResponseResult : 16 {
+  SUCCESS = 0x0000,
+  PENDING = 0x0001,
+  CONTROLLER_ID_NOT_SUPPORTED = 0x0002,
+  NEW_CONTROLLER_ID_IS_SAME = 0x0003,
+  CONFIGURATION_NOT_SUPPORTED = 0x0004,
+  CHANNEL_COLLISION = 0x0005,
+  CHANNEL_NOT_ALLOWED_TO_BE_MOVED = 0x0006,
+}
+
+packet MoveChannelResponse : ControlFrame (code = MOVE_CHANNEL_RESPONSE) {
+  initiator_cid : 16,
+  result : MoveChannelResponseResult,
+}
+
+enum MoveChannelConfirmationResult : 16 {
+  SUCCESS = 0x0000,
+  FAILURE = 0x0001,
+}
+
+packet MoveChannelConfirmationRequest : ControlFrame (code = MOVE_CHANNEL_CONFIRMATION_REQUEST) {
+  initiator_cid : 16,
+  result : MoveChannelConfirmationResult,
+}
+
+packet MoveChannelConfirmationResponse : ControlFrame (code = MOVE_CHANNEL_CONFIRMATION_RESPONSE) {
+  initiator_cid : 16,
+}
+
+enum LeCommandCode : 8 {
+  COMMAND_REJECT = 0x01,
+  DISCONNECTION_REQUEST = 0x06,
+  DISCONNECTION_RESPONSE = 0x07,
+  CONNECTION_PARAMETER_UPDATE_REQUEST = 0x12,
+  CONNECTION_PARAMETER_UPDATE_RESPONSE = 0x13,
+  LE_CREDIT_BASED_CONNECTION_REQUEST = 0x14,
+  LE_CREDIT_BASED_CONNECTION_RESPONSE = 0x15,
+  LE_FLOW_CONTROL_CREDIT = 0x16,
+}
+
+packet LeControlFrame : BasicFrame (channel_id = 0x0005) {
+  code : LeCommandCode,
+  identifier : 8, // Must be non-zero
+  _size_(_payload_) : 16,
+  _payload_,
+}
+
+packet LeCommandReject : LeControlFrame (code = COMMAND_REJECT) {
+  reason : CommandRejectReason,
+  _payload_,
+}
+
+packet LeCommandRejectNotUnderstood : LeCommandReject (reason = COMMAND_NOT_UNDERSTOOD) {
+}
+
+packet LeCommandRejectMtuExceeded : LeCommandReject (reason = SIGNALING_MTU_EXCEEDED) {
+  actual_mtu : 16,
+}
+
+packet LeCommandRejectInvalidCid : LeCommandReject (reason = INVALID_CID_IN_REQUEST) {
+  local_channel : 16, // Relative to the sender of the CommandReject
+  remote_channel : 16,
+}
+
+packet LeDisconnectionRequest : LeControlFrame (code = DISCONNECTION_REQUEST) {
+  destination_cid : 16,
+  source_cid : 16,
+}
+
+packet LeDisconnectionResponse : LeControlFrame (code = DISCONNECTION_RESPONSE) {
+  destination_cid : 16,
+  source_cid : 16,
+}
+
+packet ConnectionParameterUpdateRequest : LeControlFrame (code = CONNECTION_PARAMETER_UPDATE_REQUEST) {
+  interval_min : 16,
+  interval_max : 16,
+  slave_latency : 16,
+  timeout_multiplier : 16,
+}
+
+enum ConnectionParameterUpdateResponseResult : 16 {
+  ACCEPTED = 0,
+  REJECTED = 1,
+}
+
+packet ConnectionParameterUpdateResponse : LeControlFrame (code = CONNECTION_PARAMETER_UPDATE_RESPONSE) {
+  result : ConnectionParameterUpdateResponseResult,
+}
+
+packet LeCreditBasedConnectionRequest : LeControlFrame (code = LE_CREDIT_BASED_CONNECTION_REQUEST) {
+  le_psm : 16, // 0x0001-0x007F Fixed, 0x0080-0x00FF Dynamic
+  source_cid : 16,
+  mtu : 16,
+  mps : 16,
+  initial_credits : 16,
+}
+
+enum LeCreditBasedConnectionResponseResult : 16 {
+  SUCCESS = 0x0000,
+  LE_PSM_NOT_SUPPORTED = 0x0002,
+  NO_RESOURCES_AVAILABLE = 0x0004,
+  INSUFFICIENT_AUTHENTICATION = 0x0005,
+  INSUFFICIENT_AUTHORIZATION = 0x0006,
+  INSUFFICIENT_ENCRYPTION_KEY_SIZE = 0x0007,
+  INSUFFICIENT_ENCRYPTION = 0x0008,
+  INVALID_SOURCE_CID = 0x0009,
+  SOURCE_CID_ALREADY_ALLOCATED = 0x000A,
+  UNACCEPTABLE_PARAMETERS = 0x000B,
+}
+
+packet LeCreditBasedConnectionResponse : LeControlFrame (code = LE_CREDIT_BASED_CONNECTION_RESPONSE) {
+  destination_cid : 16,
+  mtu : 16,
+  mps : 16,
+  initial_credits : 16,
+  result : LeCreditBasedConnectionResponseResult,
+}
+
+packet LeFlowControlCredit : LeControlFrame (code = LE_FLOW_CONTROL_CREDIT) {
+  cid : 16, // Receiver's destination CID
+  credits : 16,
+}
+
diff --git a/gd/module.cc b/gd/module.cc
new file mode 100644
index 0000000..7f2dd32
--- /dev/null
+++ b/gd/module.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2019 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 "module.h"
+
+using ::bluetooth::os::Handler;
+using ::bluetooth::os::Thread;
+
+namespace bluetooth {
+
+ModuleFactory::ModuleFactory(std::function<Module*()> ctor) : ctor_(ctor) {
+}
+
+Handler* Module::GetHandler() {
+  return handler_;
+}
+
+ModuleRegistry* Module::GetModuleRegistry() {
+  return registry_;
+}
+
+Module* Module::GetDependency(const ModuleFactory* module) const {
+  for (auto& dependency : dependencies_.list_) {
+    if (dependency == module) {
+      return registry_->Get(module);
+    }
+  }
+
+  ASSERT_LOG(false, "Module was not listed as a dependency in ListDependencies");
+}
+
+Module* ModuleRegistry::Get(const ModuleFactory* module) const {
+  auto instance = started_modules_.find(module);
+  ASSERT(instance != started_modules_.end());
+  return instance->second;
+}
+
+bool ModuleRegistry::IsStarted(const ModuleFactory* module) const {
+  return started_modules_.find(module) != started_modules_.end();
+}
+
+void ModuleRegistry::Start(ModuleList* modules, Thread* thread) {
+  for (auto it = modules->list_.begin(); it != modules->list_.end(); it++) {
+    Start(*it, thread);
+  }
+}
+
+Module* ModuleRegistry::Start(const ModuleFactory* module, Thread* thread) {
+  auto started_instance = started_modules_.find(module);
+  if (started_instance != started_modules_.end()) {
+    return started_instance->second;
+  }
+
+  Module* instance = module->ctor_();
+  instance->registry_ = this;
+  instance->handler_ = new Handler(thread);
+  instance->ListDependencies(&instance->dependencies_);
+
+  Start(&instance->dependencies_, thread);
+
+  instance->Start();
+  start_order_.push_back(module);
+  started_modules_[module] = instance;
+  return instance;
+}
+
+void ModuleRegistry::StopAll() {
+  // Since modules were brought up in dependency order,
+  // it is safe to tear down by going in reverse order.
+  for (auto it = start_order_.rbegin(); it != start_order_.rend(); it++) {
+    auto instance = started_modules_.find(*it);
+    ASSERT(instance != started_modules_.end());
+
+    instance->second->Stop();
+
+    delete instance->second->handler_;
+    delete instance->second;
+    started_modules_.erase(instance);
+  }
+
+  ASSERT(started_modules_.empty());
+  start_order_.clear();
+}
+
+void ModuleRegistry::inject_test_module(const ModuleFactory* module, Module* instance, os::Thread* thread) {
+  instance->registry_ = this;
+  instance->handler_ = new Handler(thread);
+  start_order_.push_back(module);
+  started_modules_[module] = instance;
+  instance->Start();
+}
+
+}  // namespace bluetooth
diff --git a/gd/module.h b/gd/module.h
new file mode 100644
index 0000000..33e0325
--- /dev/null
+++ b/gd/module.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2019 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 <vector>
+#include <map>
+
+#include "os/log.h"
+#include "os/handler.h"
+#include "os/thread.h"
+
+namespace bluetooth {
+
+class Module;
+class ModuleRegistry;
+
+class ModuleFactory {
+ friend ModuleRegistry;
+ public:
+  ModuleFactory(std::function<Module*()> ctor);
+
+ private:
+  std::function<Module*()> ctor_;
+};
+
+class ModuleList {
+ friend ModuleRegistry;
+ friend Module;
+ public:
+  template <class T>
+  void add() {
+    list_.push_back(&T::Factory);
+  }
+
+ private:
+  std::vector<const ModuleFactory*> list_;
+};
+
+// Each leaf node module must have a factory like so:
+//
+// static const ModuleFactory Factory;
+//
+// which will provide a constructor for the module registry to call.
+// The module registry will also use the factory as the identifier
+// for that module.
+class Module {
+ friend ModuleRegistry;
+ public:
+  virtual ~Module() = default;
+ protected:
+  // Populate the provided list with modules that must start before yours
+  virtual void ListDependencies(ModuleList* list) = 0;
+
+  // You can grab your started dependencies during or after this call
+  // using GetDependency(), or access the module registry via GetModuleRegistry()
+  virtual void Start() = 0;
+
+  // Release all resources, you're about to be deleted
+  virtual void Stop() = 0;
+
+  ::bluetooth::os::Handler* GetHandler();
+
+  ModuleRegistry* GetModuleRegistry();
+
+  template <class T>
+  T* GetDependency() const {
+    return static_cast<T*>(GetDependency(&T::Factory));
+  }
+
+ private:
+  Module* GetDependency(const ModuleFactory* module) const;
+
+  ::bluetooth::os::Handler* handler_;
+  ModuleList dependencies_;
+  ModuleRegistry* registry_;
+};
+
+class ModuleRegistry {
+ friend Module;
+ friend class StackManager;
+ public:
+  template <class T>
+  bool IsStarted() const {
+    return IsStarted(&T::Factory);
+  }
+
+  bool IsStarted(const ModuleFactory* factory) const;
+
+  // Start all the modules on this list and their dependencies
+  // in dependency order
+  void Start(ModuleList* modules, ::bluetooth::os::Thread* thread);
+
+  template <class T>
+  T* Start(::bluetooth::os::Thread* thread) {
+    return static_cast<T*>(Start(&T::Factory, thread));
+  }
+
+  Module* Start(const ModuleFactory* id, ::bluetooth::os::Thread* thread);
+
+  // Stop all running modules in reverse order of start
+  void StopAll();
+
+  // Helper for dependency injection in test code. DO NOT USE in prod code!
+  // Ownership of |instance| is transferred to the registry.
+  void inject_test_module(const ModuleFactory* module, Module* instance, os::Thread* thread);
+
+  // Helper for dependency injection in test code. DO NOT USE in prod code!
+  template <class T>
+  T* get_module_under_test() const {
+    return static_cast<T*>(Get(&T::Factory));
+  }
+
+ private:
+  Module* Get(const ModuleFactory* module) const;
+  std::map<const ModuleFactory*, Module*> started_modules_;
+  std::vector<const ModuleFactory*> start_order_;
+};
+
+}  // namespace bluetooth
diff --git a/gd/module_unittest.cc b/gd/module_unittest.cc
new file mode 100644
index 0000000..d0efb5d
--- /dev/null
+++ b/gd/module_unittest.cc
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2019 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 "module.h"
+
+#include "gtest/gtest.h"
+
+using ::bluetooth::os::Thread;
+
+namespace bluetooth {
+namespace {
+
+class ModuleTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new Thread("test_thread", Thread::Priority::NORMAL);
+    registry_ = new ModuleRegistry();
+  }
+
+  void TearDown() override {
+    delete registry_;
+    delete thread_;
+  }
+
+  ModuleRegistry* registry_;
+  Thread* thread_;
+};
+
+class TestModuleNoDependency : public Module {
+ public:
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override {
+  }
+
+  void Start() override {
+    // A module is not considered started until Start() finishes
+    EXPECT_FALSE(GetModuleRegistry()->IsStarted<TestModuleNoDependency>());
+  }
+
+  void Stop() override {
+    // A module is not considered stopped until after Stop() finishes
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleNoDependency>());
+  }
+};
+
+const ModuleFactory TestModuleNoDependency::Factory = ModuleFactory([]() {
+  return new TestModuleNoDependency();
+});
+
+class TestModuleOneDependency : public Module {
+ public:
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override {
+    list->add<TestModuleNoDependency>();
+  }
+
+  void Start() override {
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleNoDependency>());
+
+    // A module is not considered started until Start() finishes
+    EXPECT_FALSE(GetModuleRegistry()->IsStarted<TestModuleOneDependency>());
+  }
+
+  void Stop() override {
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleNoDependency>());
+
+    // A module is not considered stopped until after Stop() finishes
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleOneDependency>());
+  }
+};
+
+const ModuleFactory TestModuleOneDependency::Factory = ModuleFactory([]() {
+  return new TestModuleOneDependency();
+});
+
+
+class TestModuleNoDependencyTwo : public Module {
+ public:
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override {
+  }
+
+  void Start() override {
+    // A module is not considered started until Start() finishes
+    EXPECT_FALSE(GetModuleRegistry()->IsStarted<TestModuleNoDependencyTwo>());
+  }
+
+  void Stop() override {
+    // A module is not considered stopped until after Stop() finishes
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleNoDependencyTwo>());
+  }
+};
+
+const ModuleFactory TestModuleNoDependencyTwo::Factory = ModuleFactory([]() {
+  return new TestModuleNoDependencyTwo();
+});
+
+class TestModuleTwoDependencies : public Module {
+ public:
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override {
+    list->add<TestModuleOneDependency>();
+    list->add<TestModuleNoDependencyTwo>();
+  }
+
+  void Start() override {
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleOneDependency>());
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleNoDependencyTwo>());
+
+    // A module is not considered started until Start() finishes
+    EXPECT_FALSE(GetModuleRegistry()->IsStarted<TestModuleTwoDependencies>());
+  }
+
+  void Stop() override {
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleOneDependency>());
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleNoDependencyTwo>());
+
+    // A module is not considered stopped until after Stop() finishes
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleTwoDependencies>());
+  }
+};
+
+const ModuleFactory TestModuleTwoDependencies::Factory = ModuleFactory([]() {
+  return new TestModuleTwoDependencies();
+});
+
+TEST_F(ModuleTest, no_dependency) {
+  ModuleList list;
+  list.add<TestModuleNoDependency>();
+  registry_->Start(&list, thread_);
+
+  EXPECT_TRUE(registry_->IsStarted<TestModuleNoDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleOneDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleTwoDependencies>());
+
+  registry_->StopAll();
+
+  EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleOneDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleTwoDependencies>());
+}
+
+TEST_F(ModuleTest, one_dependency) {
+  ModuleList list;
+  list.add<TestModuleOneDependency>();
+  registry_->Start(&list, thread_);
+
+  EXPECT_TRUE(registry_->IsStarted<TestModuleNoDependency>());
+  EXPECT_TRUE(registry_->IsStarted<TestModuleOneDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleTwoDependencies>());
+
+  registry_->StopAll();
+
+  EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleOneDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleTwoDependencies>());
+}
+
+TEST_F(ModuleTest, two_dependencies) {
+  ModuleList list;
+  list.add<TestModuleTwoDependencies>();
+  registry_->Start(&list, thread_);
+
+  EXPECT_TRUE(registry_->IsStarted<TestModuleNoDependency>());
+  EXPECT_TRUE(registry_->IsStarted<TestModuleOneDependency>());
+  EXPECT_TRUE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+  EXPECT_TRUE(registry_->IsStarted<TestModuleTwoDependencies>());
+
+  registry_->StopAll();
+
+  EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleOneDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleTwoDependencies>());
+}
+
+}  // namespace
+}  // namespace bluetooth
diff --git a/gd/os/Android.bp b/gd/os/Android.bp
index 535f144..8cd26fb 100644
--- a/gd/os/Android.bp
+++ b/gd/os/Android.bp
@@ -5,6 +5,7 @@
         "linux_generic/handler.cc",
         "linux_generic/reactor.cc",
         "linux_generic/repeating_alarm.cc",
+        "linux_generic/reactive_semaphore.cc",
         "linux_generic/thread.cc",
     ]
 }
@@ -14,6 +15,7 @@
     srcs: [
         "linux_generic/alarm_unittest.cc",
         "linux_generic/handler_unittest.cc",
+        "linux_generic/queue_unittest.cc",
         "linux_generic/reactor_unittest.cc",
         "linux_generic/repeating_alarm_unittest.cc",
         "linux_generic/thread_unittest.cc",
@@ -25,5 +27,6 @@
     srcs: [
         "alarm_benchmark.cc",
         "thread_benchmark.cc",
+        "queue_benchmark.cc",
     ]
 }
diff --git a/gd/os/handler.h b/gd/os/handler.h
index dc6d510..509723e 100644
--- a/gd/os/handler.h
+++ b/gd/os/handler.h
@@ -46,6 +46,9 @@
   // Remove all pending events from the queue of this handler
   void Clear();
 
+  template <typename T>
+  friend class Queue;
+
  private:
   std::queue<Closure> tasks_;
   Thread* thread_;
diff --git a/gd/os/linux_generic/linux.h b/gd/os/linux_generic/linux.h
new file mode 100644
index 0000000..524aa7d
--- /dev/null
+++ b/gd/os/linux_generic/linux.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2019 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
+
+#ifndef EFD_SEMAPHORE
+#define EFD_SEMAPHORE 1
+#endif
diff --git a/gd/os/linux_generic/queue.tpp b/gd/os/linux_generic/queue.tpp
new file mode 100644
index 0000000..419c162
--- /dev/null
+++ b/gd/os/linux_generic/queue.tpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2019 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.
+ */
+
+template <typename T>
+Queue<T>::Queue(size_t capacity)
+    : enqueue_callback_(nullptr), dequeue_callback_(nullptr), enqueue_(capacity), dequeue_(0){};
+
+template <typename T>
+Queue<T>::~Queue() {
+  ASSERT(enqueue_callback_ == nullptr);
+  ASSERT(dequeue_callback_ == nullptr);
+};
+
+template <typename T>
+void Queue<T>::RegisterEnqueue(Handler* handler, EnqueueCallback callback) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ASSERT(enqueue_.handler_ == nullptr);
+  ASSERT(enqueue_callback_ == nullptr);
+  ASSERT(enqueue_.reactable_ == nullptr);
+  enqueue_.handler_ = handler;
+  enqueue_callback_ = callback;
+  enqueue_.reactable_ = enqueue_.handler_->thread_->GetReactor()->Register(
+      enqueue_.reactive_semaphore_.GetFd(), [this] { EnqueueCallbackInternal(); }, nullptr);
+}
+
+template <typename T>
+void Queue<T>::UnregisterEnqueue() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ASSERT(enqueue_.reactable_ != nullptr);
+  enqueue_.handler_->thread_->GetReactor()->Unregister(enqueue_.reactable_);
+  enqueue_.reactable_ = nullptr;
+  enqueue_callback_ = nullptr;
+  enqueue_.handler_ = nullptr;
+}
+
+template <typename T>
+void Queue<T>::RegisterDequeue(Handler* handler, DequeueCallback callback) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ASSERT(dequeue_.handler_ == nullptr);
+  ASSERT(dequeue_callback_ == nullptr);
+  ASSERT(dequeue_.reactable_ == nullptr);
+  dequeue_.handler_ = handler;
+  dequeue_callback_ = callback;
+  dequeue_.reactable_ = dequeue_.handler_->thread_->GetReactor()->Register(dequeue_.reactive_semaphore_.GetFd(),
+                                                                           [this] { dequeue_callback_(); }, nullptr);
+}
+
+template <typename T>
+void Queue<T>::UnregisterDequeue() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ASSERT(dequeue_.reactable_ != nullptr);
+  dequeue_.handler_->thread_->GetReactor()->Unregister(dequeue_.reactable_);
+  dequeue_.reactable_ = nullptr;
+  dequeue_callback_ = nullptr;
+  dequeue_.handler_ = nullptr;
+}
+
+template <typename T>
+std::unique_ptr<T> Queue<T>::TryDequeue() {
+  std::lock_guard<std::mutex> lock(mutex_);
+
+  if (queue_.empty()) {
+    return nullptr;
+  }
+
+  dequeue_.reactive_semaphore_.Decrease();
+
+  std::unique_ptr<T> data = std::move(queue_.front());
+  queue_.pop();
+
+  enqueue_.reactive_semaphore_.Increase();
+
+  return data;
+}
+
+template <typename T>
+void Queue<T>::EnqueueCallbackInternal() {
+  enqueue_.reactive_semaphore_.Decrease();
+
+  {
+    std::unique_ptr<T> data = enqueue_callback_();
+    ASSERT(data != nullptr);
+    std::lock_guard<std::mutex> lock(mutex_);
+    queue_.push(std::move(data));
+  }
+
+  dequeue_.reactive_semaphore_.Increase();
+}
diff --git a/gd/os/linux_generic/queue_unittest.cc b/gd/os/linux_generic/queue_unittest.cc
new file mode 100644
index 0000000..a4dc6d7
--- /dev/null
+++ b/gd/os/linux_generic/queue_unittest.cc
@@ -0,0 +1,683 @@
+/*
+ * Copyright 2019 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 "os/queue.h"
+
+#include <sys/eventfd.h>
+#include <future>
+#include <unordered_map>
+
+#include "gtest/gtest.h"
+#include "os/reactor.h"
+
+namespace bluetooth {
+namespace os {
+namespace {
+
+constexpr int kQueueSize = 10;
+constexpr int kHalfOfQueueSize = kQueueSize / 2;
+constexpr int kDoubleOfQueueSize = kQueueSize * 2;
+constexpr int kQueueSizeOne = 1;
+
+class QueueTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    enqueue_thread_ = new Thread("enqueue_thread", Thread::Priority::NORMAL);
+    enqueue_handler_ = new Handler(enqueue_thread_);
+    dequeue_thread_ = new Thread("dequeue_thread", Thread::Priority::NORMAL);
+    dequeue_handler_ = new Handler(dequeue_thread_);
+  }
+  void TearDown() override {
+    delete enqueue_handler_;
+    delete enqueue_thread_;
+    delete dequeue_handler_;
+    delete dequeue_thread_;
+    enqueue_handler_ = nullptr;
+    enqueue_thread_ = nullptr;
+    dequeue_handler_ = nullptr;
+    dequeue_thread_ = nullptr;
+  }
+
+  Thread* enqueue_thread_;
+  Handler* enqueue_handler_;
+  Thread* dequeue_thread_;
+  Handler* dequeue_handler_;
+};
+
+class TestEnqueueEnd {
+ public:
+  explicit TestEnqueueEnd(Queue<std::string>* queue, Handler* handler)
+      : count(0), handler_(handler), queue_(queue), delay_(0) {}
+
+  ~TestEnqueueEnd() {
+    LOG_INFO("~TestEnqueueEnd");  // Debug log, will be removed
+  }
+
+  void RegisterEnqueue(std::unordered_map<int, std::promise<int>>* promise_map) {
+    LOG_INFO("RegisterEnqueue");  // Debug log, will be removed
+    promise_map_ = promise_map;
+    handler_->Post([this] { queue_->RegisterEnqueue(handler_, [this] { return EnqueueCallbackForTest(); }); });
+  }
+
+  void UnregisterEnqueue() {
+    LOG_INFO("UnregisterEnqueue");  // Debug log, will be removed
+    std::promise<void> promise;
+    auto feature = promise.get_future();
+
+    handler_->Post([this, &promise] {
+      queue_->UnregisterEnqueue();
+      promise.set_value();
+    });
+    feature.wait();
+  }
+
+  std::unique_ptr<std::string> EnqueueCallbackForTest() {
+    if (delay_ != 0) {
+      std::this_thread::sleep_for(std::chrono::milliseconds(delay_));
+    }
+
+    count++;
+    std::unique_ptr<std::string> data = std::move(buffer_.front());
+    buffer_.pop();
+    std::string copy = *data;
+    LOG_INFO(": pop %s, size %d", copy.c_str(), (int)buffer_.size());  // Debug log, will be removed
+    if (buffer_.empty()) {
+      queue_->UnregisterEnqueue();
+    }
+
+    auto pair = promise_map_->find(buffer_.size());
+    if (pair != promise_map_->end()) {
+      LOG_INFO("promises : %d", pair->first);  // Debug log, will be removed
+      pair->second.set_value(pair->first);
+      promise_map_->erase(pair->first);
+    }
+    return data;
+  }
+
+  void setDelay(int value) {
+    delay_ = value;
+  }
+
+  std::queue<std::unique_ptr<std::string>> buffer_;
+  int count;
+
+ private:
+  Handler* handler_;
+  Queue<std::string>* queue_;
+  std::unordered_map<int, std::promise<int>>* promise_map_;
+  int delay_;
+};
+
+class TestDequeueEnd {
+ public:
+  explicit TestDequeueEnd(Queue<std::string>* queue, Handler* handler, int capacity)
+      : count(0), handler_(handler), queue_(queue), capacity_(capacity), delay_(0) {}
+
+  ~TestDequeueEnd() {
+    LOG_INFO("~TestDequeueEnd");  // Debug log, will be removed
+  }
+
+  void RegisterDequeue(std::unordered_map<int, std::promise<int>>* promise_map) {
+    LOG_INFO("RegisterDequeue");  // Debug log, will be removed
+    promise_map_ = promise_map;
+    handler_->Post([this] { queue_->RegisterDequeue(handler_, [this] { DequeueCallbackForTest(); }); });
+  }
+
+  void UnregisterDequeue() {
+    LOG_INFO("UnregisterDequeue");  // Debug log, will be removed
+    std::promise<void> promise;
+    auto feature = promise.get_future();
+
+    handler_->Post([this, &promise] {
+      queue_->UnregisterDequeue();
+      promise.set_value();
+    });
+    feature.wait();
+  }
+
+  void DequeueCallbackForTest() {
+    if (delay_ != 0) {
+      std::this_thread::sleep_for(std::chrono::milliseconds(delay_));
+    }
+
+    count++;
+    std::unique_ptr<std::string> data = queue_->TryDequeue();
+    std::string copy = *data;  // Debug log, will be removed
+    buffer_.push(std::move(data));
+    LOG_INFO("push %s, size %d", copy.c_str(), (int)buffer_.size());  // Debug log, will be removed
+
+    auto pair = promise_map_->find(buffer_.size());
+    if (pair != promise_map_->end()) {
+      LOG_INFO("promises : %d", pair->first);  // Debug log, will be removed
+      pair->second.set_value(pair->first);
+      promise_map_->erase(pair->first);
+    }
+
+    if (buffer_.size() == capacity_) {
+      queue_->UnregisterDequeue();
+    }
+  }
+
+  void setDelay(int value) {
+    delay_ = value;
+  }
+
+  std::queue<std::unique_ptr<std::string>> buffer_;
+  int count;
+
+ private:
+  Handler* handler_;
+  Queue<std::string>* queue_;
+  std::unordered_map<int, std::promise<int>>* promise_map_;
+  int capacity_;
+  int delay_;
+};
+
+// Enqueue end level : 0 -> queue is full, 1 - >  queue isn't full
+// Dequeue end level : 0 -> queue is empty, 1 - >  queue isn't empty
+
+// Test 1 : Queue is empty
+
+// Enqueue end level : 1
+// Dequeue end level : 0
+// Test 1-1 EnqueueCallback should continually be invoked when queue isn't full
+TEST_F(QueueTest, register_enqueue_with_empty_queue) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+
+  // Push kQueueSize data to enqueue_end buffer
+  for (int i = 0; i < kQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  EXPECT_EQ(test_enqueue_end.buffer_.size(), (size_t)kQueueSize);
+
+  // Register enqueue and expect data move to Queue
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+  std::this_thread::sleep_for(std::chrono::milliseconds(20));
+}
+
+// Enqueue end level : 1
+// Dequeue end level : 0
+// Test 1-2 DequeueCallback shouldn't be invoked when queue is empty
+TEST_F(QueueTest, register_dequeue_with_empty_queue) {
+  Queue<std::string> queue(kQueueSize);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kQueueSize);
+
+  // Register dequeue, DequeueCallback shouldn't be invoked
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+  std::this_thread::sleep_for(std::chrono::milliseconds(20));
+  EXPECT_EQ(test_dequeue_end.count, 0);
+
+  test_dequeue_end.UnregisterDequeue();
+}
+
+// Test 2 : Queue is full
+
+// Enqueue end level : 0
+// Dequeue end level : 1
+// Test 2-1 EnqueueCallback shouldn't be invoked when queue is full
+TEST_F(QueueTest, register_enqueue_with_full_queue) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+
+  // make Queue full
+  for (int i = 0; i < kQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+
+  // push some data to enqueue_end buffer and register enqueue;
+  for (int i = 0; i < kHalfOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+
+  // EnqueueCallback shouldn't be invoked
+  std::this_thread::sleep_for(std::chrono::milliseconds(20));
+  EXPECT_EQ(test_enqueue_end.buffer_.size(), (size_t)kHalfOfQueueSize);
+  EXPECT_EQ(test_enqueue_end.count, kQueueSize);
+
+  test_enqueue_end.UnregisterEnqueue();
+}
+
+// Enqueue end level : 0
+// Dequeue end level : 1
+// Test 2-2 DequeueCallback should continually be invoked when queue isn't empty
+TEST_F(QueueTest, register_dequeue_with_full_queue) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kDoubleOfQueueSize);
+
+  // make Queue full
+  for (int i = 0; i < kQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+
+  // Register dequeue and expect data move to dequeue end buffer
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+  auto dequeue_future = dequeue_promise_map[kQueueSize].get_future();
+  dequeue_future.wait();
+  EXPECT_EQ(dequeue_future.get(), kQueueSize);
+
+  test_dequeue_end.UnregisterDequeue();
+}
+
+// Test 3 : Queue is non-empty and non-full
+
+// Enqueue end level : 1
+// Dequeue end level : 1
+// Test 3-1 Register enqueue with half empty queue, EnqueueCallback should continually be invoked
+TEST_F(QueueTest, register_enqueue_with_half_empty_queue) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+
+  // make Queue half empty
+  for (int i = 0; i < kHalfOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+
+  // push some data to enqueue_end buffer and register enqueue;
+  for (int i = 0; i < kHalfOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+
+  // Register enqueue and expect data move to Queue
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  enqueue_future = enqueue_promise_map[0].get_future();
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+}
+
+// Enqueue end level : 1
+// Dequeue end level : 1
+// Test 3-2 Register dequeue with half empty queue, DequeueCallback should continually be invoked
+TEST_F(QueueTest, register_dequeue_with_half_empty_queue) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kQueueSize);
+
+  // make Queue half empty
+  for (int i = 0; i < kHalfOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+
+  // Register dequeue and expect data move to dequeue end buffer
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize),
+                              std::forward_as_tuple());
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+  auto dequeue_future = dequeue_promise_map[kHalfOfQueueSize].get_future();
+  dequeue_future.wait();
+  EXPECT_EQ(dequeue_future.get(), kHalfOfQueueSize);
+
+  test_dequeue_end.UnregisterDequeue();
+}
+
+// Dynamic level test
+
+// Test 4 : Queue becomes full during test, EnqueueCallback should stop to be invoked
+
+// Enqueue end level : 1 -> 0
+// Dequeue end level : 1
+// Test 4-1 Queue becomes full due to only register EnqueueCallback
+TEST_F(QueueTest, queue_becomes_full_enqueue_callback_only) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+
+  // push double of kQueueSize to enqueue end buffer
+  for (int i = 0; i < kDoubleOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+
+  // Register enqueue and expect kQueueSize data move to Queue
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  auto enqueue_future = enqueue_promise_map[kQueueSize].get_future();
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), kQueueSize);
+
+  // EnqueueCallback shouldn't be invoked and buffer size stay in kQueueSize
+  std::this_thread::sleep_for(std::chrono::milliseconds(20));
+  EXPECT_EQ(test_enqueue_end.buffer_.size(), (size_t)kQueueSize);
+  EXPECT_EQ(test_enqueue_end.count, kQueueSize);
+
+  test_enqueue_end.UnregisterEnqueue();
+}
+
+// Enqueue end level : 1 -> 0
+// Dequeue end level : 1
+// Test 4-2 Queue becomes full due to DequeueCallback unregister during test
+TEST_F(QueueTest, queue_becomes_full_dequeue_callback_unregister) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kHalfOfQueueSize);
+
+  // push double of kQueueSize to enqueue end buffer
+  for (int i = 0; i < kDoubleOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+
+  // Register dequeue
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize),
+                              std::forward_as_tuple());
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+  auto dequeue_future = dequeue_promise_map[kHalfOfQueueSize].get_future();
+
+  // Register enqueue
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize),
+                              std::forward_as_tuple());
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  auto enqueue_future = enqueue_promise_map[kHalfOfQueueSize].get_future();
+
+  // Dequeue end will unregister when buffer size is kHalfOfQueueSize
+  dequeue_future.wait();
+  EXPECT_EQ(dequeue_future.get(), kHalfOfQueueSize);
+
+  // EnqueueCallback shouldn't be invoked and buffer size stay in kHalfOfQueueSize
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), kHalfOfQueueSize);
+  std::this_thread::sleep_for(std::chrono::milliseconds(20));
+  EXPECT_EQ(test_enqueue_end.buffer_.size(), (size_t)kHalfOfQueueSize);
+  EXPECT_EQ(test_enqueue_end.count, kQueueSize + kHalfOfQueueSize);
+
+  test_enqueue_end.UnregisterEnqueue();
+}
+
+// Enqueue end level : 1 -> 0
+// Dequeue end level : 1
+// Test 4-3 Queue becomes full due to DequeueCallback is slower
+TEST_F(QueueTest, queue_becomes_full_dequeue_callback_slower) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kDoubleOfQueueSize);
+
+  // push double of kDoubleOfQueueSize to enqueue end buffer
+  for (int i = 0; i < kDoubleOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+
+  // Set 20 ms delay for callback and register dequeue
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  test_dequeue_end.setDelay(20);
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+  auto dequeue_future = dequeue_promise_map[kHalfOfQueueSize].get_future();
+
+  // Register enqueue
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+
+  // Wait for enqueue buffer empty and expect queue is full
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+  EXPECT_EQ(test_dequeue_end.buffer_.size(), kQueueSize);
+
+  test_dequeue_end.UnregisterDequeue();
+}
+
+// Enqueue end level : 0 -> 1
+// Dequeue end level : 1 -> 0
+// Test 5 Queue becomes full and non empty at same time.
+TEST_F(QueueTest, queue_becomes_full_and_non_empty_at_same_time) {
+  Queue<std::string> queue(kQueueSizeOne);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kDoubleOfQueueSize);
+
+  // push double of kQueueSize to enqueue end buffer
+  for (int i = 0; i < kQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+
+  // Register dequeue
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+  auto dequeue_future = dequeue_promise_map[kQueueSize].get_future();
+
+  // Register enqueue
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+
+  // Wait for all data move from enqueue end buffer to dequeue end buffer
+  dequeue_future.wait();
+  EXPECT_EQ(dequeue_future.get(), kQueueSize);
+
+  test_dequeue_end.UnregisterDequeue();
+}
+
+// Enqueue end level : 1 -> 0
+// Dequeue end level : 1
+// Test 6 Queue becomes not full during test, EnqueueCallback should start to be invoked
+TEST_F(QueueTest, queue_becomes_non_full_during_test) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kQueueSize * 3);
+
+  // make Queue full
+  for (int i = 0; i < kDoubleOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  auto enqueue_future = enqueue_promise_map[kQueueSize].get_future();
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), kQueueSize);
+
+  // Expect kQueueSize data block in enqueue end buffer
+  std::this_thread::sleep_for(std::chrono::milliseconds(20));
+  EXPECT_EQ(test_enqueue_end.buffer_.size(), kQueueSize);
+
+  // Register dequeue
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+
+  // Expect enqueue end will empty
+  enqueue_future = enqueue_promise_map[0].get_future();
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+
+  test_dequeue_end.UnregisterDequeue();
+}
+
+// Enqueue end level : 0 -> 1
+// Dequeue end level : 1 -> 0
+// Test 7 Queue becomes non full and empty at same time. (Exactly same as Test 5)
+TEST_F(QueueTest, queue_becomes_non_full_and_empty_at_same_time) {
+  Queue<std::string> queue(kQueueSizeOne);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kDoubleOfQueueSize);
+
+  // push double of kQueueSize to enqueue end buffer
+  for (int i = 0; i < kQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+
+  // Register dequeue
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+  auto dequeue_future = dequeue_promise_map[kQueueSize].get_future();
+
+  // Register enqueue
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+
+  // Wait for all data move from enqueue end buffer to dequeue end buffer
+  dequeue_future.wait();
+  EXPECT_EQ(dequeue_future.get(), kQueueSize);
+
+  test_dequeue_end.UnregisterDequeue();
+}
+
+// Test 8 : Queue becomes empty during test, DequeueCallback should stop to be invoked
+
+// Enqueue end level : 1
+// Dequeue end level : 1 -> 0
+// Test 8-1 Queue becomes empty due to only register DequeueCallback
+TEST_F(QueueTest, queue_becomes_empty_dequeue_callback_only) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kHalfOfQueueSize);
+
+  // make Queue half empty
+  for (int i = 0; i < kHalfOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+
+  // Register dequeue, expect kHalfOfQueueSize data move to dequeue end buffer
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize),
+                              std::forward_as_tuple());
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+  auto dequeue_future = dequeue_promise_map[kHalfOfQueueSize].get_future();
+  dequeue_future.wait();
+  EXPECT_EQ(dequeue_future.get(), kHalfOfQueueSize);
+
+  // Expect DequeueCallback should stop to be invoked
+  std::this_thread::sleep_for(std::chrono::milliseconds(20));
+  EXPECT_EQ(test_dequeue_end.count, kHalfOfQueueSize);
+}
+
+// Enqueue end level : 1
+// Dequeue end level : 1 -> 0
+// Test 8-2 Queue becomes empty due to EnqueueCallback unregister during test
+TEST_F(QueueTest, queue_becomes_empty_enqueue_callback_unregister) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kQueueSize);
+
+  // make Queue half empty
+  for (int i = 0; i < kHalfOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+
+  // push kHalfOfQueueSize to enqueue end buffer and register enqueue.
+  for (int i = 0; i < kHalfOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+
+  // Register dequeue, expect kQueueSize move to dequeue end buffer
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+  auto dequeue_future = dequeue_promise_map[kQueueSize].get_future();
+  dequeue_future.wait();
+  EXPECT_EQ(dequeue_future.get(), kQueueSize);
+
+  // Expect DequeueCallback should stop to be invoked
+  std::this_thread::sleep_for(std::chrono::milliseconds(20));
+  EXPECT_EQ(test_dequeue_end.count, kQueueSize);
+}
+
+// Enqueue end level : 1
+// Dequeue end level : 0 -> 1
+// Test 9 Queue becomes not empty during test, DequeueCallback should start to be invoked
+TEST_F(QueueTest, queue_becomes_non_empty_during_test) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kQueueSize);
+
+  // Register dequeue
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+
+  // push kQueueSize data to enqueue end buffer and register enqueue
+  for (int i = 0; i < kQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+
+  // Expect kQueueSize data move to dequeue end buffer
+  auto dequeue_future = dequeue_promise_map[kQueueSize].get_future();
+  dequeue_future.wait();
+  EXPECT_EQ(dequeue_future.get(), kQueueSize);
+}
+
+}  // namespace
+}  // namespace os
+}  // namespace bluetooth
diff --git a/gd/os/linux_generic/reactive_semaphore.cc b/gd/os/linux_generic/reactive_semaphore.cc
new file mode 100644
index 0000000..4cc0b4c
--- /dev/null
+++ b/gd/os/linux_generic/reactive_semaphore.cc
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019 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 "reactive_semaphore.h"
+
+#include <sys/eventfd.h>
+#include <unistd.h>
+#include <functional>
+
+#include "os/linux_generic/linux.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace os {
+
+ReactiveSemaphore::ReactiveSemaphore(unsigned int value) : fd_(eventfd(value, EFD_SEMAPHORE | EFD_NONBLOCK)) {
+  ASSERT(fd_ != -1);
+}
+
+ReactiveSemaphore::~ReactiveSemaphore() {
+  int close_status;
+  RUN_NO_INTR(close_status = close(fd_));
+  ASSERT(close_status != -1);
+}
+
+void ReactiveSemaphore::Decrease() {
+  uint64_t val = 0;
+  auto read_result = eventfd_read(fd_, &val);
+  ASSERT(read_result != -1);
+}
+
+void ReactiveSemaphore::Increase() {
+  uint64_t val = 1;
+  auto write_result = eventfd_write(fd_, val);
+  ASSERT(write_result != -1);
+}
+
+int ReactiveSemaphore::GetFd() {
+  return fd_;
+}
+
+}  // namespace os
+}  // namespace bluetooth
diff --git a/gd/os/linux_generic/reactive_semaphore.h b/gd/os/linux_generic/reactive_semaphore.h
new file mode 100644
index 0000000..718666c
--- /dev/null
+++ b/gd/os/linux_generic/reactive_semaphore.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 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 "os/utils.h"
+
+namespace bluetooth {
+namespace os {
+
+// A event_fd work in non-blocking and Semaphore mode
+class ReactiveSemaphore {
+ public:
+  // Creates a new ReactiveSemaphore with an initial value of |value|.
+  explicit ReactiveSemaphore(unsigned int value);
+  ~ReactiveSemaphore();
+  // Decrements the value of |fd_|, this will cause a crash if |fd_| unreadable.
+  void Decrease();
+  // Increase the value of |fd_|, this will cause a crash if |fd_| unwritable.
+  void Increase();
+  int GetFd();
+
+  DISALLOW_COPY_AND_ASSIGN(ReactiveSemaphore);
+
+ private:
+  int fd_;
+};
+
+}  // namespace os
+}  // namespace bluetooth
diff --git a/gd/os/linux_generic/reactor.cc b/gd/os/linux_generic/reactor.cc
index dfc6757..1980f4f 100644
--- a/gd/os/linux_generic/reactor.cc
+++ b/gd/os/linux_generic/reactor.cc
@@ -123,6 +123,7 @@
       }
       if (reactable_removed_) {
         delete reactable;
+        reactable_removed_ = false;
       }
     }
   }
@@ -133,7 +134,7 @@
     LOG_WARN("not running, will stop once it's started");
   }
   auto control = eventfd_write(control_fd_, 1);
-  ASSERT(control != -1)
+  ASSERT(control != -1);
 }
 
 Reactor::Reactable* Reactor::Register(int fd, Closure on_read_ready, Closure on_write_ready) {
@@ -151,7 +152,7 @@
   };
   int register_fd;
   RUN_NO_INTR(register_fd = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event));
-  ASSERT(register_fd != -1)
+  ASSERT(register_fd != -1);
   return reactable;
 }
 
@@ -161,6 +162,7 @@
     std::lock_guard<std::mutex> lock(mutex_);
     invalidation_list_.push_back(reactable);
   }
+  bool delaying_delete_until_callback_finished = false;
   {
     int result;
     std::lock_guard<std::recursive_mutex> reactable_lock(reactable->lock_);
@@ -170,14 +172,16 @@
     } else {
       ASSERT(result != -1);
     }
+
     // If we are unregistering during the callback event from this reactable, we delete it after the callback is executed.
     // reactable->is_executing_ is protected by reactable->lock_, so it's thread safe.
     if (reactable->is_executing_) {
       reactable_removed_ = true;
+      delaying_delete_until_callback_finished = true;
     }
   }
   // If we are unregistering outside of the callback event from this reactable, we delete it now
-  if (!reactable_removed_) {
+  if (!delaying_delete_until_callback_finished) {
     delete reactable;
   }
 }
diff --git a/gd/os/linux_generic/reactor_unittest.cc b/gd/os/linux_generic/reactor_unittest.cc
index 7fda234..9143b29 100644
--- a/gd/os/linux_generic/reactor_unittest.cc
+++ b/gd/os/linux_generic/reactor_unittest.cc
@@ -51,7 +51,7 @@
 class SampleReactable {
  public:
   SampleReactable() : fd_(eventfd(0, EFD_NONBLOCK)) {
-    EXPECT_NE(fd_, 0);
+    EXPECT_NE(fd_, -1);
   }
 
   ~SampleReactable() {
@@ -74,11 +74,11 @@
     kSampleOutputValue,
   };
   FakeReactable() : fd_(eventfd(0, 0)), reactor_(nullptr) {
-    EXPECT_NE(fd_, 0);
+    EXPECT_NE(fd_, -1);
   }
 
   FakeReactable(Reactor* reactor) : fd_(eventfd(0, 0)), reactor_(reactor) {
-    EXPECT_NE(fd_, 0);
+    EXPECT_NE(fd_, -1);
   }
 
   ~FakeReactable() {
@@ -109,6 +109,14 @@
     EXPECT_EQ(write_result, 0);
   }
 
+  void UnregisterInCallback() {
+    uint64_t value = 0;
+    auto read_result = eventfd_read(fd_, &value);
+    EXPECT_EQ(read_result, 0);
+    g_promise->set_value(kReadReadyValue);
+    reactor_->Unregister(reactable_);
+  }
+
   SampleReactable sample_reactable_;
   Reactor::Reactable* reactable_ = nullptr;
   int fd_;
@@ -205,6 +213,11 @@
   auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kRegisterSampleReactable);
   EXPECT_EQ(write_result, 0);
   EXPECT_EQ(future.get(), kReadReadyValue);
+  delete g_promise;
+  g_promise = new std::promise<int>;
+  future = g_promise->get_future();
+  write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kUnregisterSampleReactable);
+  EXPECT_EQ(write_result, 0);
   reactor_->Stop();
   reactor_thread.join();
 
@@ -233,6 +246,46 @@
   reactor_->Unregister(reactable);
 }
 
+TEST_F(ReactorTest, hot_unregister_from_callback) {
+  auto reactor_thread = std::thread(&Reactor::Run, reactor_);
+
+  FakeReactable fake_reactable1(reactor_);
+  auto* reactable1 =
+      reactor_->Register(fake_reactable1.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable1), nullptr);
+
+  FakeReactable fake_reactable2(reactor_);
+  auto* reactable2 = reactor_->Register(fake_reactable2.fd_,
+                                        std::bind(&FakeReactable::UnregisterInCallback, &fake_reactable2), nullptr);
+  fake_reactable2.reactable_ = reactable2;
+  auto write_result = eventfd_write(fake_reactable2.fd_, 1);
+  EXPECT_EQ(write_result, 0);
+  reactor_->Stop();
+  reactor_thread.join();
+
+  reactor_->Unregister(reactable1);
+}
+
+TEST_F(ReactorTest, hot_unregister_during_unregister_from_callback) {
+  auto reactor_thread = std::thread(&Reactor::Run, reactor_);
+  auto future = g_promise->get_future();
+
+  FakeReactable fake_reactable1(reactor_);
+  auto* reactable1 =
+      reactor_->Register(fake_reactable1.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable1), nullptr);
+
+  FakeReactable fake_reactable2(reactor_);
+  auto* reactable2 = reactor_->Register(fake_reactable2.fd_,
+                                        std::bind(&FakeReactable::UnregisterInCallback, &fake_reactable2), nullptr);
+  fake_reactable2.reactable_ = reactable2;
+  auto write_result = eventfd_write(fake_reactable2.fd_, 1);
+  EXPECT_EQ(write_result, 0);
+  EXPECT_EQ(future.get(), kReadReadyValue);
+  reactor_->Unregister(reactable1);
+
+  reactor_->Stop();
+  reactor_thread.join();
+}
+
 TEST_F(ReactorTest, start_and_stop_multi_times) {
   auto reactor_thread = std::thread(&Reactor::Run, reactor_);
   reactor_->Stop();
@@ -273,6 +326,8 @@
 
   reactor_->Stop();
   reactor_thread.join();
+
+  reactor_->Unregister(reactable);
 }
 
 }  // namespace
diff --git a/gd/os/log.h b/gd/os/log.h
index 291b17b..d7c6a8b 100644
--- a/gd/os/log.h
+++ b/gd/os/log.h
@@ -18,6 +18,8 @@
 
 #pragma once
 
+#include <stdlib.h>
+
 #ifndef LOG_TAG
 #define LOG_TAG "bt"
 #endif
@@ -37,8 +39,7 @@
 /* syslog didn't work well here since we would be redefining LOG_DEBUG. */
 #include <stdio.h>
 
-#define LOGWRAPPER(fmt, args...) \
-  fprintf(stderr, "%s - %s: " fmt "\n", LOG_TAG, __PRETTY_FUNCTION__, ##args)
+#define LOGWRAPPER(fmt, args...) fprintf(stderr, "%s - %s: " fmt "\n", LOG_TAG, __PRETTY_FUNCTION__, ##args)
 
 #define LOG_VERBOSE(...) LOGWRAPPER(__VA_ARGS__)
 #define LOG_DEBUG(...) LOGWRAPPER(__VA_ARGS__)
@@ -48,15 +49,18 @@
 
 #endif /* defined(OS_ANDROID) */
 
-#define ASSERT(condition) \
-  if (!(condition)) { \
-    LOG_ERROR("%s:%d assertion '" #condition "' failed", __FILE__, __LINE__); \
-    abort(); \
-  }
+#define ASSERT(condition)                                                       \
+  do {                                                                          \
+    if (!(condition)) {                                                         \
+      LOG_ERROR("%s:%d assertion '" #condition "' failed", __FILE__, __LINE__); \
+      abort();                                                                  \
+    }                                                                           \
+  } while (false)
 
-#define ASSERT_LOG(condition, fmt, args...) \
-  if (!(condition)) { \
-    LOG_ERROR("%s:%d assertion '" #condition "' failed - " fmt, __FILE__, __LINE__, ##args); \
-    abort(); \
-  }
-
+#define ASSERT_LOG(condition, fmt, args...)                                                    \
+  do {                                                                                         \
+    if (!(condition)) {                                                                        \
+      LOG_ERROR("%s:%d assertion '" #condition "' failed - " fmt, __FILE__, __LINE__, ##args); \
+      abort();                                                                                 \
+    }                                                                                          \
+  } while (false)
diff --git a/gd/os/queue.h b/gd/os/queue.h
new file mode 100644
index 0000000..0e68583
--- /dev/null
+++ b/gd/os/queue.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2019 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 <unistd.h>
+#include <functional>
+#include <mutex>
+#include <queue>
+
+#include "os/handler.h"
+#ifdef OS_LINUX_GENERIC
+#include "os/linux_generic/reactive_semaphore.h"
+#endif
+#include "os/log.h"
+
+namespace bluetooth {
+namespace os {
+
+// See documentation for |Queue|
+template <typename T>
+class IQueueEnqueue {
+ public:
+  using EnqueueCallback = std::function<std::unique_ptr<T>()>;
+  virtual ~IQueueEnqueue() = default;
+  virtual void RegisterEnqueue(Handler* handler, EnqueueCallback callback) = 0;
+  virtual void UnregisterEnqueue() = 0;
+};
+
+// See documentation for |Queue|
+template <typename T>
+class IQueueDequeue {
+ public:
+  using DequeueCallback = std::function<void()>;
+  virtual ~IQueueDequeue() = default;
+  virtual void RegisterDequeue(Handler* handler, DequeueCallback callback) = 0;
+  virtual void UnregisterDequeue() = 0;
+  virtual std::unique_ptr<T> TryDequeue() = 0;
+};
+
+template <typename T>
+class Queue : public IQueueEnqueue<T>, public IQueueDequeue<T> {
+ public:
+  // A function moving data from enqueue end buffer to queue, it will be continually be invoked until queue
+  // is full. Enqueue end should make sure buffer isn't empty and UnregisterEnqueue when buffer become empty.
+  using EnqueueCallback = std::function<std::unique_ptr<T>()>;
+  // A function moving data form queue to dequeue end buffer, it will be continually be invoked until queue
+  // is empty. TryDequeue should be use in this function to get data from queue.
+  using DequeueCallback = std::function<void()>;
+  // Create a queue with |capacity| is the maximum number of messages a queue can contain
+  explicit Queue(size_t capacity);
+  ~Queue();
+  // Register |callback| that will be called on |handler| when the queue is able to enqueue one piece of data.
+  // This will cause a crash if handler or callback has already been registered before.
+  void RegisterEnqueue(Handler* handler, EnqueueCallback callback) override;
+  // Unregister current EnqueueCallback from this queue, this will cause a crash if not registered yet.
+  void UnregisterEnqueue() override;
+  // Register |callback| that will be called on |handler| when the queue has at least one piece of data ready
+  // for dequeue. This will cause a crash if handler or callback has already been registered before.
+  void RegisterDequeue(Handler* handler, DequeueCallback callback) override;
+  // Unregister current DequeueCallback from this queue, this will cause a crash if not registered yet.
+  void UnregisterDequeue() override;
+
+  // Try to dequeue an item from this queue. Return nullptr when there is nothing in the queue.
+  std::unique_ptr<T> TryDequeue() override;
+
+ private:
+  void EnqueueCallbackInternal();
+  // An internal queue that holds at most |capacity| pieces of data
+  std::queue<std::unique_ptr<T>> queue_;
+  // A mutex that guards data in this queue
+  std::mutex mutex_;
+  // Current dequeue callback
+  EnqueueCallback enqueue_callback_;
+  // Current enqueue callback
+  DequeueCallback dequeue_callback_;
+
+  class QueueEndpoint {
+   public:
+#ifdef OS_LINUX_GENERIC
+    explicit QueueEndpoint(unsigned int initial_value)
+        : reactive_semaphore_(initial_value), handler_(nullptr), reactable_(nullptr) {}
+    ReactiveSemaphore reactive_semaphore_;
+#endif
+    Handler* handler_;
+    Reactor::Reactable* reactable_;
+  };
+
+  QueueEndpoint enqueue_;
+  QueueEndpoint dequeue_;
+};
+
+#ifdef OS_LINUX_GENERIC
+#include "os/linux_generic/queue.tpp"
+#endif
+
+}  // namespace os
+}  // namespace bluetooth
diff --git a/gd/os/queue_benchmark.cc b/gd/os/queue_benchmark.cc
new file mode 100644
index 0000000..c241489
--- /dev/null
+++ b/gd/os/queue_benchmark.cc
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2019 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 "benchmark/benchmark.h"
+
+#include <future>
+
+#include "os/handler.h"
+#include "os/queue.h"
+#include "os/thread.h"
+
+using ::benchmark::State;
+
+namespace bluetooth {
+namespace os {
+
+class BM_QueuePerformance : public ::benchmark::Fixture {
+ protected:
+  void SetUp(State& st) override {
+    ::benchmark::Fixture::SetUp(st);
+    enqueue_thread_ = new Thread("enqueue_thread", Thread::Priority::NORMAL);
+    enqueue_handler_ = new Handler(enqueue_thread_);
+    dequeue_thread_ = new Thread("dequeue_thread", Thread::Priority::NORMAL);
+    dequeue_handler_ = new Handler(dequeue_thread_);
+  }
+
+  void TearDown(State& st) override {
+    delete enqueue_handler_;
+    delete enqueue_thread_;
+    delete dequeue_handler_;
+    delete dequeue_thread_;
+    enqueue_handler_ = nullptr;
+    enqueue_thread_ = nullptr;
+    dequeue_handler_ = nullptr;
+    dequeue_thread_ = nullptr;
+    benchmark::Fixture::TearDown(st);
+  }
+
+  Thread* enqueue_thread_;
+  Handler* enqueue_handler_;
+  Thread* dequeue_thread_;
+  Handler* dequeue_handler_;
+};
+
+class TestEnqueueEnd {
+ public:
+  explicit TestEnqueueEnd(int64_t count, Queue<std::string>* queue, Handler* handler, std::promise<void>* promise)
+      : count_(count), handler_(handler), queue_(queue), promise_(promise) {}
+
+  void RegisterEnqueue() {
+    handler_->Post([this] { queue_->RegisterEnqueue(handler_, [this] { return EnqueueCallbackForTest(); }); });
+  }
+
+  void push(std::string data) {
+    {
+      std::lock_guard<std::mutex> lock(mutex_);
+      buffer_.push(std::move(data));
+    }
+    if (buffer_.size() == 1) {
+      RegisterEnqueue();
+    }
+  }
+
+  std::unique_ptr<std::string> EnqueueCallbackForTest() {
+    std::lock_guard<std::mutex> lock(mutex_);
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::move(buffer_.front()));
+    buffer_.pop();
+
+    if (buffer_.empty()) {
+      queue_->UnregisterEnqueue();
+    }
+
+    count_--;
+    if (count_ == 0) {
+      promise_->set_value();
+    }
+
+    return data;
+  }
+
+  std::queue<std::string> buffer_;
+  int64_t count_;
+
+ private:
+  Handler* handler_;
+  Queue<std::string>* queue_;
+  std::promise<void>* promise_;
+  std::mutex mutex_;
+};
+
+class TestDequeueEnd {
+ public:
+  explicit TestDequeueEnd(int64_t count, Queue<std::string>* queue, Handler* handler, std::promise<void>* promise)
+      : count_(count), handler_(handler), queue_(queue), promise_(promise) {}
+
+  void RegisterDequeue() {
+    handler_->Post([this] { queue_->RegisterDequeue(handler_, [this] { DequeueCallbackForTest(); }); });
+  }
+
+  void DequeueCallbackForTest() {
+    std::string data = *(queue_->TryDequeue());
+    buffer_.push(data);
+
+    count_--;
+    if (count_ == 0) {
+      queue_->UnregisterDequeue();
+      promise_->set_value();
+    }
+  }
+
+  std::queue<std::string> buffer_;
+  int64_t count_;
+
+ private:
+  Handler* handler_;
+  Queue<std::string>* queue_;
+  std::promise<void>* promise_;
+};
+
+BENCHMARK_DEFINE_F(BM_QueuePerformance, send_packet_vary_by_packet_num)(State& state) {
+  for (auto _ : state) {
+    int64_t num_data_to_send_ = state.range(0);
+    Queue<std::string> queue(num_data_to_send_);
+
+    // register dequeue
+    std::promise<void> dequeue_promise;
+    auto dequeue_future = dequeue_promise.get_future();
+    TestDequeueEnd test_dequeue_end(num_data_to_send_, &queue, enqueue_handler_, &dequeue_promise);
+    test_dequeue_end.RegisterDequeue();
+
+    // Push data to enqueue end buffer and register enqueue
+    std::promise<void> enqueue_promise;
+    TestEnqueueEnd test_enqueue_end(num_data_to_send_, &queue, enqueue_handler_, &enqueue_promise);
+    for (int i = 0; i < num_data_to_send_; i++) {
+      std::string data = std::to_string(1);
+      test_enqueue_end.push(std::move(data));
+    }
+    dequeue_future.wait();
+  }
+
+  state.SetBytesProcessed(static_cast<int_fast64_t>(state.iterations()) * state.range(0));
+};
+
+BENCHMARK_REGISTER_F(BM_QueuePerformance, send_packet_vary_by_packet_num)
+    ->Arg(10)
+    ->Arg(100)
+    ->Arg(1000)
+    ->Arg(10000)
+    ->Arg(100000)
+    ->Iterations(100)
+    ->UseRealTime();
+
+BENCHMARK_DEFINE_F(BM_QueuePerformance, send_10000_packet_vary_by_packet_size)(State& state) {
+  for (auto _ : state) {
+    int64_t num_data_to_send_ = 10000;
+    int64_t packet_size = state.range(0);
+    Queue<std::string> queue(num_data_to_send_);
+
+    // register dequeue
+    std::promise<void> dequeue_promise;
+    auto dequeue_future = dequeue_promise.get_future();
+    TestDequeueEnd test_dequeue_end(num_data_to_send_, &queue, enqueue_handler_, &dequeue_promise);
+    test_dequeue_end.RegisterDequeue();
+
+    // Push data to enqueue end buffer and register enqueue
+    std::promise<void> enqueue_promise;
+    TestEnqueueEnd test_enqueue_end(num_data_to_send_, &queue, enqueue_handler_, &enqueue_promise);
+    for (int i = 0; i < num_data_to_send_; i++) {
+      std::string data = std::string(packet_size, 'x');
+      test_enqueue_end.push(std::move(data));
+    }
+    dequeue_future.wait();
+  }
+
+  state.SetBytesProcessed(static_cast<int_fast64_t>(state.iterations()) * state.range(0) * 10000);
+};
+
+BENCHMARK_REGISTER_F(BM_QueuePerformance, send_10000_packet_vary_by_packet_size)
+    ->Arg(10)
+    ->Arg(100)
+    ->Arg(1000)
+    ->Iterations(100)
+    ->UseRealTime();
+
+}  // namespace os
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/os/utils.h b/gd/os/utils.h
index 26e8f23..89fca66 100644
--- a/gd/os/utils.h
+++ b/gd/os/utils.h
@@ -15,6 +15,7 @@
  */
 
 #pragma once
+#include <errno.h>
 
 // A macro to re-try a syscall when it receives EINTR
 #ifndef RUN_NO_INTR
diff --git a/gd/packet/Android.bp b/gd/packet/Android.bp
index be5a3c9..155fb4a 100644
--- a/gd/packet/Android.bp
+++ b/gd/packet/Android.bp
@@ -2,6 +2,8 @@
     name: "BluetoothPacketSources",
     srcs: [
         "bit_inserter.cc",
+	"byte_inserter.cc",
+	"byte_observer.cc",
         "iterator.cc",
         "packet_view.cc",
         "raw_builder.cc",
diff --git a/gd/packet/bit_inserter.cc b/gd/packet/bit_inserter.cc
index 5167806..0180cd8 100644
--- a/gd/packet/bit_inserter.cc
+++ b/gd/packet/bit_inserter.cc
@@ -14,147 +14,40 @@
  * limitations under the License.
  */
 
-#include "packet/iterator.h"
+#include "packet/bit_inserter.h"
 
 #include "os/log.h"
 
 namespace bluetooth {
 namespace packet {
 
-template <bool little_endian>
-Iterator<little_endian>::Iterator(std::forward_list<View> data, size_t offset) {
-  data_ = data;
-  index_ = offset;
-  length_ = 0;
-  for (auto& view : data) {
-    length_ += view.size();
+BitInserter::BitInserter(std::vector<uint8_t>& vector) : ByteInserter(vector) {}
+
+BitInserter::~BitInserter() {
+  ASSERT(num_saved_bits_ == 0);
+}
+
+void BitInserter::insert_bits(uint8_t byte, size_t num_bits) {
+  size_t total_bits = num_bits + num_saved_bits_;
+  uint16_t new_value = saved_bits_ | (static_cast<uint16_t>(byte) << num_saved_bits_);
+  if (total_bits >= 8) {
+    uint8_t new_byte = static_cast<uint8_t>(new_value);
+    ByteInserter::insert_byte(new_byte);
+    total_bits -= 8;
+    new_value = new_value >> 8;
   }
+  num_saved_bits_ = total_bits;
+  uint8_t mask = 0xff >> (8 - num_saved_bits_);
+  saved_bits_ = static_cast<uint8_t>(new_value) & mask;
 }
 
-template <bool little_endian>
-Iterator<little_endian> Iterator<little_endian>::operator+(int offset) {
-  auto itr(*this);
-
-  return itr += offset;
+void BitInserter::insert_byte(uint8_t byte) {
+  insert_bits(byte, 8);
 }
 
-template <bool little_endian>
-Iterator<little_endian>& Iterator<little_endian>::operator+=(int offset) {
-  index_ += offset;
-  return *this;
+bool BitInserter::IsByteAligned() {
+  return num_saved_bits_ == 0;
 }
 
-template <bool little_endian>
-Iterator<little_endian> Iterator<little_endian>::operator++(int) {
-  auto itr(*this);
-  index_++;
-  return itr;
-}
-
-template <bool little_endian>
-Iterator<little_endian>& Iterator<little_endian>::operator++() {
-  index_++;
-  return *this;
-}
-
-template <bool little_endian>
-Iterator<little_endian> Iterator<little_endian>::operator-(int offset) {
-  auto itr(*this);
-
-  return itr -= offset;
-}
-
-template <bool little_endian>
-int Iterator<little_endian>::operator-(Iterator<little_endian>& itr) {
-  return index_ - itr.index_;
-}
-
-template <bool little_endian>
-Iterator<little_endian>& Iterator<little_endian>::operator-=(int offset) {
-  index_ -= offset;
-
-  return *this;
-}
-
-template <bool little_endian>
-Iterator<little_endian> Iterator<little_endian>::operator--(int) {
-  auto itr(*this);
-  if (index_ != 0) index_--;
-
-  return itr;
-}
-
-template <bool little_endian>
-Iterator<little_endian>& Iterator<little_endian>::operator--() {
-  if (index_ != 0) index_--;
-
-  return *this;
-}
-
-template <bool little_endian>
-Iterator<little_endian>& Iterator<little_endian>::operator=(const Iterator<little_endian>& itr) {
-  data_ = itr.data_;
-  index_ = itr.index_;
-
-  return *this;
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator==(const Iterator<little_endian>& itr) const {
-  return index_ == itr.index_;
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator!=(const Iterator<little_endian>& itr) const {
-  return !(*this == itr);
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator<(const Iterator<little_endian>& itr) const {
-  return index_ < itr.index_;
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator>(const Iterator<little_endian>& itr) const {
-  return index_ > itr.index_;
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator<=(const Iterator<little_endian>& itr) const {
-  return index_ <= itr.index_;
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator>=(const Iterator<little_endian>& itr) const {
-  return index_ >= itr.index_;
-}
-
-template <bool little_endian>
-uint8_t Iterator<little_endian>::operator*() const {
-  ASSERT_LOG(index_ < length_, "Index %zu out of bounds: %zu", index_, length_);
-  size_t index = index_;
-
-  for (auto view : data_) {
-    if (index < view.size()) {
-      return view[index];
-    }
-    index -= view.size();
-  }
-  ASSERT_LOG(false, "Out of fragments searching for index %zu", index_);
-  return 0;
-}
-
-template <bool little_endian>
-size_t Iterator<little_endian>::NumBytesRemaining() const {
-  if (length_ > index_) {
-    return length_ - index_;
-  } else {
-    return 0;
-  }
-}
-
-// Explicit instantiations for both types of Iterators.
-template class Iterator<true>;
-template class Iterator<false>;
 }  // namespace packet
 }  // namespace bluetooth
diff --git a/gd/packet/bit_inserter.h b/gd/packet/bit_inserter.h
index 9034e38..2aa1ad6 100644
--- a/gd/packet/bit_inserter.h
+++ b/gd/packet/bit_inserter.h
@@ -21,39 +21,21 @@
 #include <memory>
 #include <vector>
 
-#include "os/log.h"
+#include "packet/byte_inserter.h"
 
 namespace bluetooth {
 namespace packet {
 
-class BitInserter : public std::back_insert_iterator<std::vector<uint8_t>> {
+class BitInserter : public ByteInserter {
  public:
-  BitInserter(std::vector<uint8_t>& vector) : std::back_insert_iterator<std::vector<uint8_t>>(vector) {}
-  virtual ~BitInserter() {
-    ASSERT(num_saved_bits_ == 0);
-  }
+  BitInserter(std::vector<uint8_t>& vector);
+  virtual ~BitInserter();
 
-  void insert_bits(uint8_t byte, size_t num_bits) {
-    size_t total_bits = num_bits + num_saved_bits_;
-    uint16_t new_value = saved_bits_ | (static_cast<uint16_t>(byte) << num_saved_bits_);
-    if (total_bits >= 8) {
-      uint8_t new_byte = static_cast<uint8_t>(new_value);
-      std::back_insert_iterator<std::vector<uint8_t>>::operator=(new_byte);
-      total_bits -= 8;
-      new_value = new_value >> 8;
-    }
-    num_saved_bits_ = total_bits;
-    uint8_t mask = 0xff >> (8 - num_saved_bits_);
-    saved_bits_ = static_cast<uint8_t>(new_value) & mask;
-  }
+  void insert_bits(uint8_t byte, size_t num_bits);
 
-  void insert_byte(uint8_t byte) {
-    insert_bits(byte, 8);
-  }
+  void insert_byte(uint8_t byte);
 
-  bool IsByteAligned() {
-    return num_saved_bits_ == 0;
-  }
+  bool IsByteAligned();
 
  private:
   size_t num_saved_bits_{0};
diff --git a/gd/packet/bit_inserter_unittest.cc b/gd/packet/bit_inserter_unittest.cc
index 6a3b668..fae19f8 100644
--- a/gd/packet/bit_inserter_unittest.cc
+++ b/gd/packet/bit_inserter_unittest.cc
@@ -27,12 +27,6 @@
 namespace bluetooth {
 namespace packet {
 
-class BitInserterTest : public ::testing::Test {
- public:
-  BitInserterTest() {}
-  ~BitInserterTest() = default;
-};
-
 TEST(BitInserterTest, addMoreBits) {
   std::vector<uint8_t> bytes;
   BitInserter it(bytes);
@@ -41,12 +35,8 @@
     it.insert_bits(static_cast<uint8_t>(i), i);
   }
   it.insert_bits(static_cast<uint8_t>(0b1010), 4);
-  std::vector<uint8_t> result = {
-      0b00011101 /* 3 2 1 */,
-      0b00010101 /* 5 4 */,
-      0b11100011 /* 7 6 */,
-      0b10000000 /* 8 */,
-      0b10100000 /* filled with 1010 */};
+  std::vector<uint8_t> result = {0b00011101 /* 3 2 1 */, 0b00010101 /* 5 4 */, 0b11100011 /* 7 6 */, 0b10000000 /* 8 */,
+                                 0b10100000 /* filled with 1010 */};
 
   ASSERT_EQ(result.size(), bytes.size());
   for (size_t i = 0; i < bytes.size(); i++) {
@@ -54,5 +44,39 @@
   }
 }
 
+TEST(BitInserterTest, observerTest) {
+  std::vector<uint8_t> bytes;
+  BitInserter it(bytes);
+  std::vector<uint8_t> copy;
+
+  uint64_t checksum = 0x0123456789abcdef;
+  it.RegisterObserver(ByteObserver([&copy](uint8_t byte) { copy.push_back(byte); }, [checksum]() { return checksum; }));
+
+  for (size_t i = 0; i < 9; i++) {
+    it.insert_bits(static_cast<uint8_t>(i), i);
+  }
+  it.insert_bits(static_cast<uint8_t>(0b1010), 4);
+  std::vector<uint8_t> result = {0b00011101 /* 3 2 1 */, 0b00010101 /* 5 4 */, 0b11100011 /* 7 6 */, 0b10000000 /* 8 */,
+                                 0b10100000 /* filled with 1010 */};
+
+  ASSERT_EQ(result.size(), bytes.size());
+  for (size_t i = 0; i < bytes.size(); i++) {
+    ASSERT_EQ(result[i], bytes[i]);
+  }
+
+  ASSERT_EQ(result.size(), copy.size());
+  for (size_t i = 0; i < copy.size(); i++) {
+    ASSERT_EQ(result[i], copy[i]);
+  }
+
+  ByteObserver observer = it.UnregisterObserver();
+  ASSERT_EQ(checksum, observer.GetValue());
+  uint8_t another_byte = 0xef;
+  it.insert_bits(another_byte, 8);
+  ASSERT_EQ(bytes.back(), another_byte);
+  ASSERT_EQ(result.size() + 1, bytes.size());
+  ASSERT_EQ(result.size(), copy.size());
+}
+
 }  // namespace packet
 }  // namespace bluetooth
diff --git a/gd/packet/byte_inserter.cc b/gd/packet/byte_inserter.cc
new file mode 100644
index 0000000..f7fc9c0
--- /dev/null
+++ b/gd/packet/byte_inserter.cc
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019 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 "packet/byte_inserter.h"
+
+#include "os/log.h"
+
+namespace bluetooth {
+namespace packet {
+
+ByteInserter::ByteInserter(std::vector<uint8_t>& vector) : std::back_insert_iterator<std::vector<uint8_t>>(vector) {}
+
+ByteInserter::~ByteInserter() {
+  ASSERT(registered_observers_.size() == 0);
+}
+
+void ByteInserter::RegisterObserver(ByteObserver observer) {
+  registered_observers_.push_back(observer);
+}
+
+ByteObserver ByteInserter::UnregisterObserver() {
+  ByteObserver observer = registered_observers_.back();
+  registered_observers_.pop_back();
+  return observer;
+}
+
+void ByteInserter::insert_byte(uint8_t byte) {
+  for (auto& observer : registered_observers_) {
+    observer.OnByte(byte);
+  }
+  std::back_insert_iterator<std::vector<uint8_t>>::operator=(byte);
+}
+
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/byte_inserter.h b/gd/packet/byte_inserter.h
new file mode 100644
index 0000000..2d49b33
--- /dev/null
+++ b/gd/packet/byte_inserter.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 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 <cstdint>
+#include <iterator>
+#include <memory>
+#include <vector>
+
+#include "packet/byte_observer.h"
+
+namespace bluetooth {
+namespace packet {
+
+class ByteInserter : public std::back_insert_iterator<std::vector<uint8_t>> {
+ public:
+  ByteInserter(std::vector<uint8_t>& vector);
+  virtual ~ByteInserter();
+
+  void insert_byte(uint8_t byte);
+
+  void RegisterObserver(ByteObserver observer);
+
+  ByteObserver UnregisterObserver();
+
+ private:
+  std::vector<ByteObserver> registered_observers_;
+};
+
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/byte_observer.cc b/gd/packet/byte_observer.cc
new file mode 100644
index 0000000..358a230
--- /dev/null
+++ b/gd/packet/byte_observer.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019 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 "packet/byte_observer.h"
+
+namespace bluetooth {
+namespace packet {
+
+ByteObserver::ByteObserver(const std::function<void(uint8_t)>& on_byte, const std::function<uint64_t()>& get_value)
+    : on_byte_(on_byte), get_value_(get_value) {}
+
+void ByteObserver::OnByte(uint8_t byte) {
+  on_byte_(byte);
+}
+
+uint64_t ByteObserver::GetValue() {
+  return get_value_();
+}
+
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/byte_observer.h b/gd/packet/byte_observer.h
new file mode 100644
index 0000000..adfcfc1
--- /dev/null
+++ b/gd/packet/byte_observer.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 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 <cstdint>
+#include <functional>
+
+namespace bluetooth {
+namespace packet {
+
+class ByteObserver {
+ public:
+  ByteObserver(const std::function<void(uint8_t)>& on_byte_, const std::function<uint64_t()>& get_value_);
+
+  void OnByte(uint8_t byte);
+
+  uint64_t GetValue();
+
+ private:
+  std::function<void(uint8_t)> on_byte_;
+  std::function<uint64_t()> get_value_;
+};
+
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/iterator.h b/gd/packet/iterator.h
index 56d8708..422c025 100644
--- a/gd/packet/iterator.h
+++ b/gd/packet/iterator.h
@@ -63,7 +63,7 @@
   // Get the next sizeof(FixedWidthPODType) bytes and return the filled type
   template <typename FixedWidthPODType>
   FixedWidthPODType extract() {
-    static_assert(std::is_pod<FixedWidthPODType>::value, "Iterator::extract requires an fixed type.");
+    static_assert(std::is_pod<FixedWidthPODType>::value, "Iterator::extract requires a fixed-width type.");
     FixedWidthPODType extracted_value;
     uint8_t* value_ptr = (uint8_t*)&extracted_value;
 
diff --git a/gd/packet/packet_builder.h b/gd/packet/packet_builder.h
index 2a27cc4..dec67db 100644
--- a/gd/packet/packet_builder.h
+++ b/gd/packet/packet_builder.h
@@ -22,8 +22,6 @@
 #include <memory>
 #include <vector>
 
-#include "common/address.h"
-#include "common/class_of_device.h"
 #include "os/log.h"
 #include "packet/base_packet_builder.h"
 #include "packet/bit_inserter.h"
@@ -44,21 +42,21 @@
 
  protected:
   // Write sizeof(FixedWidthIntegerType) bytes using the iterator
-  template <typename FixedWidthIntegerType,
-            typename std::enable_if<std::is_integral<FixedWidthIntegerType>::value, int>::type = 0>
-  void insert(FixedWidthIntegerType value, BitInserter& it) const {
-    for (size_t i = 0; i < sizeof(FixedWidthIntegerType); i++) {
+  template <typename FixedWidthPODType, typename std::enable_if<std::is_pod<FixedWidthPODType>::value, int>::type = 0>
+  void insert(FixedWidthPODType value, BitInserter& it) const {
+    uint8_t* raw_bytes = (uint8_t*)&value;
+    for (size_t i = 0; i < sizeof(FixedWidthPODType); i++) {
       if (little_endian == true) {
-        it.insert_byte(static_cast<uint8_t>(value >> (i * 8)));
+        it.insert_byte(raw_bytes[i]);
       } else {
-        it.insert_byte(static_cast<uint8_t>(value >> ((sizeof(FixedWidthIntegerType) - i - 1) * 8)));
+        it.insert_byte(raw_bytes[sizeof(FixedWidthPODType) - i - 1]);
       }
     }
   }
 
   // Write num_bits bits using the iterator
   template <typename FixedWidthIntegerType,
-            typename std::enable_if<std::is_integral<FixedWidthIntegerType>::value, int>::type = 0>
+            typename std::enable_if<std::is_pod<FixedWidthIntegerType>::value, int>::type = 0>
   void insert(FixedWidthIntegerType value, BitInserter& it, size_t num_bits) const {
     ASSERT(num_bits <= (sizeof(FixedWidthIntegerType) * 8));
 
@@ -85,24 +83,12 @@
   // Write a vector of FixedWidthIntegerType using the iterator
   template <typename FixedWidthIntegerType>
   void insert_vector(const std::vector<FixedWidthIntegerType>& vec, BitInserter& it) const {
-    static_assert(std::is_integral<FixedWidthIntegerType>::value,
-                  "PacketBuilder::insert requires an integral type vector.");
+    static_assert(std::is_pod<FixedWidthIntegerType>::value,
+                  "PacketBuilder::insert requires a vector with elements of a fixed-size.");
     for (const auto& element : vec) {
       insert(element, it);
     }
   }
-
-  void insert_address(const common::Address& addr, BitInserter& it) const {
-    for (const auto& element : addr.address) {
-      insert(element, it);
-    }
-  }
-
-  void insert_class_of_device(const common::ClassOfDevice& cod, BitInserter& it) const {
-    for (const auto& element : cod.cod) {
-      insert(element, it);
-    }
-  }
 };
 
 }  // namespace packet
diff --git a/gd/packet/packet_builder_unittest.cc b/gd/packet/packet_builder_unittest.cc
index 74a27e6..46647a3 100644
--- a/gd/packet/packet_builder_unittest.cc
+++ b/gd/packet/packet_builder_unittest.cc
@@ -99,9 +99,9 @@
 template <typename T>
 class VectorBuilder : public PacketBuilder<true> {
  public:
-  VectorBuilder(std::vector<uint64_t> vect) {
+  VectorBuilder(const std::vector<uint64_t> vect) {
     for (uint64_t element : vect) {
-      vect.push_back(static_cast<T>(element));
+      vect_.push_back(static_cast<T>(element));
     }
   }
   ~VectorBuilder() = default;
@@ -129,9 +129,9 @@
 template <typename T>
 class InsertElementsBuilder : public PacketBuilder<true> {
  public:
-  InsertElementsBuilder(std::vector<uint64_t> vect) {
+  InsertElementsBuilder(const std::vector<uint64_t> vect) {
     for (uint64_t element : vect) {
-      vect.push_back(static_cast<T>(element));
+      vect_.push_back(static_cast<T>(element));
     }
   }
   virtual ~InsertElementsBuilder() = default;
diff --git a/gd/packet/parser/Android.bp b/gd/packet/parser/Android.bp
new file mode 100644
index 0000000..4b24d2e
--- /dev/null
+++ b/gd/packet/parser/Android.bp
@@ -0,0 +1,41 @@
+cc_binary_host  {
+  name: "bluetooth_packetgen",
+  srcs: [
+    "fields/body_field.cc",
+    "fields/checksum_field.cc",
+    "fields/checksum_start_field.cc",
+    "fields/custom_field.cc",
+    "fields/enum_field.cc",
+    "fields/fixed_field.cc",
+    "fields/group_field.cc",
+    "fields/packet_field.cc",
+    "fields/payload_field.cc",
+    "fields/reserved_field.cc",
+    "fields/scalar_field.cc",
+    "fields/size_field.cc",
+    "checksum_def.cc",
+    "custom_field_def.cc",
+    "enum_def.cc",
+    "enum_gen.cc",
+    "packet_def.cc",
+    "main.cc",
+    "language_y.yy",
+    "language_l.ll",
+  ],
+  static_libs: [
+    "libc++fs",
+  ],
+  cppflags: [
+    "-Wno-implicit-fallthrough",
+    "-fno-exceptions",
+    "-O0",
+  ],
+  ldflags: [
+    "-fuse-ld=ld",
+    "-O0",
+  ],
+  yacc: {
+    gen_location_hh: true,
+    gen_position_hh: true,
+  },
+}
diff --git a/gd/packet/parser/README b/gd/packet/parser/README
new file mode 100644
index 0000000..f3d9dfb
--- /dev/null
+++ b/gd/packet/parser/README
@@ -0,0 +1,53 @@
+This file just contains some notes about the design and usage of the PDL language.
+
+-------
+ TERMS
+-------
+.pdl
+  The file type that defines packet definitions. You may think of each pdl file
+  as its own translation unit.
+
+Packet Views and Builders
+  Generated from a packet definition. Views are used to validate packets and
+  extract the fields that are defined in the pdl file.  Builders check the input
+  arguments and can be serialized.
+
+Checksum types
+  checksum MyChecksumClass : 16 "path/to/the/class/"
+  Checksum fields need to implement the following three static methods:
+    static void Initialize(MyChecksumClass&);
+    static void AddByte(MyChecksumClass&, uint8_t);
+    // Assuming a 16-bit (uint16_t) checksum:
+    static uint16_t GetChecksum(MyChecksumClass&);
+-------------
+ LIMITATIONS
+-------------
+  - Size fields for a variable length field MUST come before the definition
+    of said field.
+
+  - Payload fields must be byte-aligned unless they have an unknown size.
+    Body fields are allowed to not be byte aligned.
+
+  - No conditionals
+
+  - Can not have to fields with the same name anywhere in the in an inheritence chain
+
+  - Can't handle size for Body type fields yet since they might not be byte aligned.
+
+
+-------
+ NOTES
+-------
+All field names should be in snake_case.  Types should be in CamelCase.
+
+The _payload_ keyword generates a getter but _body_ doesn't. Therefore, a
+_payload_ must be byte aligned.
+
+Supports constraints on grandparents
+Supports multiple constraints
+Every field handles its own generation.
+One pdl file will result in one header file with all the packets
+
+Things to cover -
+  Constraints
+  Inheritence vs Contains
diff --git a/gd/packet/parser/checksum_def.cc b/gd/packet/parser/checksum_def.cc
new file mode 100644
index 0000000..38bd946
--- /dev/null
+++ b/gd/packet/parser/checksum_def.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 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 "checksum_def.h"
+
+#include "util.h"
+
+ChecksumDef::ChecksumDef(std::string name, std::string include, int size) : CustomFieldDef(name, include, size) {}
+
+PacketField* ChecksumDef::GetNewField(const std::string& name, ParseLocation loc) const {
+  return new ChecksumField(name, name_, size_, loc);
+}
+
+TypeDef::Type ChecksumDef::GetDefinitionType() const {
+  return TypeDef::Type::CHECKSUM;
+}
+
+void ChecksumDef::GenInclude(std::ostream& s) const {
+  CustomFieldDef::GenInclude(s);
+}
+
+void ChecksumDef::GenUsing(std::ostream& s) const {
+  CustomFieldDef::GenUsing(s);
+}
+
+void ChecksumDef::GenChecksumCheck(std::ostream& s) const {
+  s << "static_assert(ChecksumTypeChecker<" << name_ << "," << util::GetTypeForSize(size_) << ">::value, \"";
+  s << name_ << " is not a valid checksum type. Please see README for more details.\");";
+}
diff --git a/gd/packet/parser/checksum_def.h b/gd/packet/parser/checksum_def.h
new file mode 100644
index 0000000..d8f9142
--- /dev/null
+++ b/gd/packet/parser/checksum_def.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 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 <iostream>
+
+#include "checksum_type_checker.h"
+#include "custom_field_def.h"
+#include "fields/checksum_field.h"
+#include "parse_location.h"
+#include "type_def.h"
+
+class ChecksumDef : public CustomFieldDef {
+ public:
+  ChecksumDef(std::string name, std::string include, int size);
+
+  virtual PacketField* GetNewField(const std::string& name, ParseLocation loc) const override;
+
+  virtual TypeDef::Type GetDefinitionType() const override;
+
+  virtual void GenInclude(std::ostream& s) const override;
+
+  virtual void GenUsing(std::ostream& s) const override;
+
+  void GenChecksumCheck(std::ostream& s) const;
+
+  const std::string include_;
+};
diff --git a/gd/packet/parser/checksum_type_checker.h b/gd/packet/parser/checksum_type_checker.h
new file mode 100644
index 0000000..c0f35bc
--- /dev/null
+++ b/gd/packet/parser/checksum_type_checker.h
@@ -0,0 +1,36 @@
+#include <optional>
+
+namespace bluetooth {
+namespace packet {
+namespace parser {
+
+// Checks for Initialize(), AddByte(), and GetChecksum().
+// T and TRET are the checksum class Type and the checksum return type
+// C and CRET are the substituted types for T and TRET
+template <typename T, typename TRET>
+class ChecksumTypeChecker {
+ public:
+  template <class C, void (*)(C&)>
+  struct InitializeChecker {};
+
+  template <class C, void (*)(C&, uint8_t byte)>
+  struct AddByteChecker {};
+
+  template <class C, typename CRET, CRET (*)(const C&)>
+  struct GetChecksumChecker {};
+
+  // If all the methods are defined, this one matches
+  template <class C, typename CRET>
+  static int Test(InitializeChecker<C, &C::Initialize>*, AddByteChecker<C, &C::AddByte>*,
+                  GetChecksumChecker<C, CRET, &C::GetChecksum>*);
+
+  // This one matches everything else
+  template <class C, typename CRET>
+  static char Test(...);
+
+  // This checks which template was matched
+  static constexpr bool value = (sizeof(Test<T, TRET>(0, 0, 0)) == sizeof(int));
+};
+}  // namespace parser
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/parser/custom_field_def.cc b/gd/packet/parser/custom_field_def.cc
new file mode 100644
index 0000000..9429e3f
--- /dev/null
+++ b/gd/packet/parser/custom_field_def.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 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 "custom_field_def.h"
+
+#include "util.h"
+
+CustomFieldDef::CustomFieldDef(std::string name, std::string include, int size)
+    : TypeDef(name, size), include_(include) {
+  if (size % 8 != 0) {
+    ERROR() << "Custom fields must be byte aligned.";
+  }
+}
+
+PacketField* CustomFieldDef::GetNewField(const std::string& name, ParseLocation loc) const {
+  return new CustomField(name, name_, size_, loc);
+}
+
+TypeDef::Type CustomFieldDef::GetDefinitionType() const {
+  return TypeDef::Type::CUSTOM;
+}
+
+void CustomFieldDef::GenInclude(std::ostream& s) const {
+  s << "#include \"" << include_ << util::CamelCaseToUnderScore(GetTypeName()) << ".h\"\n";
+}
+
+void CustomFieldDef::GenUsing(std::ostream& s) const {
+  s << "using ::bluetooth::";
+  for (const auto& c : include_) {
+    switch (c) {
+      case '/':
+        s << "::";
+        break;
+      default:
+        s << c;
+    }
+  }
+  s << GetTypeName() << ";";
+}
diff --git a/gd/packet/parser/custom_field_def.h b/gd/packet/parser/custom_field_def.h
new file mode 100644
index 0000000..156ac8a
--- /dev/null
+++ b/gd/packet/parser/custom_field_def.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019 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 <iostream>
+
+#include "fields/custom_field.h"
+#include "parse_location.h"
+#include "type_def.h"
+
+class CustomFieldDef : public TypeDef {
+ public:
+  CustomFieldDef(std::string name, std::string include, int size);
+
+  virtual PacketField* GetNewField(const std::string& name, ParseLocation loc) const override;
+
+  virtual Type GetDefinitionType() const override;
+
+  virtual void GenInclude(std::ostream& s) const;
+
+  virtual void GenUsing(std::ostream& s) const;
+
+  const std::string include_;
+};
diff --git a/gd/packet/parser/declarations.h b/gd/packet/parser/declarations.h
new file mode 100644
index 0000000..1a344f0
--- /dev/null
+++ b/gd/packet/parser/declarations.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2019 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 <deque>
+#include <map>
+#include <optional>
+
+#include "checksum_def.h"
+#include "custom_field_def.h"
+#include "enum_def.h"
+#include "enum_gen.h"
+#include "packet_def.h"
+
+class Declarations {
+ public:
+  void AddTypeDef(std::string name, TypeDef* def) {
+    type_defs_.insert(std::pair(name, def));
+    type_defs_queue_.push_back(std::pair(name, def));
+  }
+
+  TypeDef* GetTypeDef(const std::string& name) {
+    auto it = type_defs_.find(name);
+    if (it == type_defs_.end()) {
+      return nullptr;
+    }
+
+    return it->second;
+  }
+
+  void AddPacketDef(std::string name, PacketDef def) {
+    packet_defs_.insert(std::pair(name, def));
+    packet_defs_queue_.push_back(std::pair(name, def));
+  }
+
+  PacketDef* GetPacketDef(const std::string& name) {
+    auto it = packet_defs_.find(name);
+    if (it == packet_defs_.end()) {
+      return nullptr;
+    }
+
+    return &(it->second);
+  }
+
+  void AddGroupDef(std::string name, FieldList* group_def) {
+    group_defs_.insert(std::pair(name, group_def));
+  }
+
+  FieldList* GetGroupDef(std::string name) {
+    if (group_defs_.find(name) == group_defs_.end()) {
+      return nullptr;
+    }
+
+    return group_defs_.at(name);
+  }
+
+  std::map<std::string, FieldList*> group_defs_;
+
+  std::map<std::string, TypeDef*> type_defs_;
+  std::deque<std::pair<std::string, TypeDef*>> type_defs_queue_;
+  std::map<std::string, PacketDef> packet_defs_;
+  std::deque<std::pair<std::string, PacketDef>> packet_defs_queue_;
+  bool is_little_endian;
+};
diff --git a/gd/packet/parser/enum_def.cc b/gd/packet/parser/enum_def.cc
new file mode 100644
index 0000000..410af15
--- /dev/null
+++ b/gd/packet/parser/enum_def.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 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 "enum_def.h"
+
+#include <iostream>
+#include <map>
+
+#include "fields/enum_field.h"
+#include "util.h"
+
+EnumDef::EnumDef(std::string name, int size) : TypeDef(name, size){};
+
+void EnumDef::AddEntry(std::string name, uint32_t value) {
+  if (value > util::GetMaxValueForBits(size_)) {
+    std::cerr << __func__ << ": Value provided is greater than max possible value for enum. " << name_ << "\n";
+    abort();
+  }
+
+  constants_.insert(std::pair(value, name));
+  entries_.insert(name);
+}
+
+PacketField* EnumDef::GetNewField(const std::string& name, ParseLocation loc) const {
+  return new EnumField(name, *this, "What is this for", loc);
+}
+
+bool EnumDef::HasEntry(std::string name) const {
+  return entries_.count(name) != 0;
+}
+
+TypeDef::Type EnumDef::GetDefinitionType() const {
+  return TypeDef::Type::ENUM;
+}
+
+void EnumDef::GenInclude(std::ostream&) const {}
+
+void EnumDef::GenUsing(std::ostream&) const {}
diff --git a/gd/packet/parser/enum_def.h b/gd/packet/parser/enum_def.h
new file mode 100644
index 0000000..97989e6
--- /dev/null
+++ b/gd/packet/parser/enum_def.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 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 <set>
+#include <string>
+
+#include "fields/packet_field.h"
+#include "type_def.h"
+
+// Holds the definition of an enum.
+class EnumDef : public TypeDef {
+ public:
+  EnumDef(std::string name, int size);
+
+  virtual PacketField* GetNewField(const std::string& name, ParseLocation loc) const;
+
+  void AddEntry(std::string name, uint32_t value);
+
+  bool HasEntry(std::string name) const;
+
+  virtual Type GetDefinitionType() const override;
+
+  virtual void GenInclude(std::ostream& s) const override;
+
+  virtual void GenUsing(std::ostream& s) const override;
+
+  // data
+  std::map<uint32_t, std::string> constants_;
+  std::set<std::string> entries_;
+};
diff --git a/gd/packet/parser/enum_gen.cc b/gd/packet/parser/enum_gen.cc
new file mode 100644
index 0000000..769a43a
--- /dev/null
+++ b/gd/packet/parser/enum_gen.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 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 "enum_gen.h"
+
+#include <iostream>
+
+#include "util.h"
+
+EnumGen::EnumGen(EnumDef e) : e_(e){};
+
+void EnumGen::GenDefinition(std::ostream& stream) {
+  stream << "enum class ";
+  stream << e_.name_;
+  stream << " : " << util::GetTypeForSize(e_.size_);
+  stream << " {";
+  for (const auto& pair : e_.constants_) {
+    stream << pair.second << " = 0x" << std::hex << pair.first << std::dec << ",";
+  }
+  stream << "};\n";
+}
+
+void EnumGen::GenLogging(std::ostream& stream) {
+  // Print out the switch statement that converts all the constants to strings.
+  stream << "inline std::string " << e_.name_ << "Text(const " << e_.name_ << "& param) {";
+  stream << "switch (param) {";
+  for (const auto& pair : e_.constants_) {
+    stream << "case " << e_.name_ << "::" << pair.second << ":";
+    stream << "  return \"" << pair.second << "\";";
+  }
+  stream << "default:";
+  stream << "  return std::string(\"Unknown " << e_.name_ << ": \") + std::to_string(static_cast<int>(param));";
+  stream << "}";
+  stream << "}\n\n";
+
+  // Print out the stream operator so that the constant can be written to streams.
+  stream << "inline std::ostream& operator<<(std::ostream& os, const " << e_.name_ << "& param) {";
+  stream << "  return os << " << e_.name_ << "Text(param);";
+  stream << "}\n";
+}
diff --git a/gd/packet/parser/enum_gen.h b/gd/packet/parser/enum_gen.h
new file mode 100644
index 0000000..f3483dd
--- /dev/null
+++ b/gd/packet/parser/enum_gen.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 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 <iostream>
+
+#include "enum_def.h"
+
+// Generates the C++ code for an enum.
+class EnumGen {
+ public:
+  EnumGen(EnumDef e);
+
+  void GenDefinition(std::ostream& stream);
+
+  void GenLogging(std::ostream& stream);
+
+  EnumDef e_;
+};
diff --git a/gd/packet/parser/field_list.h b/gd/packet/parser/field_list.h
new file mode 100644
index 0000000..1cd7df4
--- /dev/null
+++ b/gd/packet/parser/field_list.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2019 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 <set>
+#include <vector>
+
+#include "fields/packet_field.h"
+
+using FieldListIterator = std::vector<PacketField*>::const_iterator;
+using ReverseFieldListIterator = std::vector<PacketField*>::const_reverse_iterator;
+
+class FieldList {
+ public:
+  FieldList() = default;
+
+  FieldList(std::vector<PacketField*> fields) {
+    for (PacketField* field : fields) {
+      AppendField(field);
+    }
+  }
+
+  template <class Iterator>
+  FieldList(Iterator begin, Iterator end) {
+    while (begin != end) {
+      AppendField(*begin);
+      begin++;
+    }
+  }
+
+  PacketField* operator[](int index) const {
+    return field_list_[index];
+  }
+
+  PacketField* GetField(std::string field_name) const {
+    auto it = field_map_.find(field_name);
+    if (it == field_map_.end()) {
+      return nullptr;
+    }
+
+    return it->second;
+  }
+
+  void AppendField(PacketField* field) {
+    AddField(field);
+    field_list_.push_back(field);
+  }
+
+  void PrependField(PacketField* field) {
+    AddField(field);
+    field_list_.insert(field_list_.begin(), field);
+  }
+
+  FieldList GetFieldsBeforePayloadOrBody() const {
+    FieldList ret;
+    for (auto it = begin(); it != end(); it++) {
+      const auto& field = *it;
+      if (field->GetFieldType() == PacketField::Type::PAYLOAD || field->GetFieldType() == PacketField::Type::BODY) {
+        break;
+      }
+      ret.AppendField(*it);
+    }
+
+    return ret;
+  }
+
+  FieldList GetFieldsAfterPayloadOrBody() const {
+    FieldListIterator it;
+    for (it = begin(); it != end(); it++) {
+      const auto& field = *it;
+      if (field->GetFieldType() == PacketField::Type::PAYLOAD || field->GetFieldType() == PacketField::Type::BODY) {
+        // Increment it once to get first field after payload/body.
+        it++;
+        break;
+      }
+    }
+
+    return FieldList(it, end());
+  }
+
+  FieldList GetFieldsWithTypes(std::set<PacketField::Type> field_types) const {
+    FieldList ret;
+
+    for (const auto& field : field_list_) {
+      if (field_types.find(field->GetFieldType()) != field_types.end()) {
+        ret.AppendField(field);
+      }
+    }
+
+    return ret;
+  }
+
+  FieldList GetFieldsWithoutTypes(std::set<PacketField::Type> field_types) const {
+    FieldList ret;
+
+    for (const auto& field : field_list_) {
+      if (field_types.find(field->GetFieldType()) == field_types.end()) {
+        ret.AppendField(field);
+      }
+    }
+
+    return ret;
+  }
+
+  // Appends header fields of param to header fields of the current and
+  // prepends footer fields of the param to footer fields of the current.
+  // Ex. Assuming field_list_X has the layout:
+  //   field_list_X_header
+  //   payload/body
+  //   field_list_X_footer
+  // The call to field_list_1.Merge(field_list_2) would result in
+  //   field_list_1_header
+  //   field_list_2_header
+  //   payload/body (uses whatever was in field_list_2)
+  //   field_list_2_footer
+  //   field_list_1_footer
+  FieldList Merge(FieldList nested) const {
+    FieldList ret;
+
+    for (const auto& field : GetFieldsBeforePayloadOrBody()) {
+      ret.AppendField(field);
+    }
+
+    for (const auto& field : nested) {
+      ret.AppendField(field);
+    }
+
+    for (const auto& field : GetFieldsAfterPayloadOrBody()) {
+      ret.AppendField(field);
+    }
+
+    return ret;
+  }
+
+  bool HasPayloadOrBody() const {
+    return has_payload_ || has_body_;
+  }
+
+  bool HasPayload() const {
+    return has_payload_;
+  }
+
+  bool HasBody() const {
+    return has_body_;
+  }
+
+  FieldListIterator begin() const {
+    return field_list_.begin();
+  }
+
+  FieldListIterator end() const {
+    return field_list_.end();
+  }
+
+  ReverseFieldListIterator rbegin() const {
+    return field_list_.rbegin();
+  }
+
+  ReverseFieldListIterator rend() const {
+    return field_list_.rend();
+  }
+
+  size_t size() const {
+    return field_list_.size();
+  }
+
+ private:
+  void AddField(PacketField* field) {
+    if (field_map_.find(field->GetName()) != field_map_.end()) {
+      ERROR(field) << "Field with name \"" << field->GetName() << "\" was "
+                   << "previously defined.\n";
+    }
+
+    if (field->GetFieldType() == PacketField::Type::PAYLOAD) {
+      if (HasBody()) {
+        ERROR(field) << "Can not have payload field in packet that already has a body.";
+      }
+      has_payload_ = true;
+    }
+
+    if (field->GetFieldType() == PacketField::Type::BODY) {
+      if (HasPayload()) {
+        ERROR(field) << "Can not have body field in packet that already has a payload.";
+      }
+      has_body_ = true;
+    }
+
+    field_map_.insert(std::pair(field->GetName(), field));
+  }
+
+  std::vector<PacketField*> field_list_;
+  std::map<std::string, PacketField*> field_map_;
+  bool has_payload_ = false;
+  bool has_body_ = false;
+};
diff --git a/gd/packet/parser/fields/all_fields.h b/gd/packet/parser/fields/all_fields.h
new file mode 100644
index 0000000..50cc659
--- /dev/null
+++ b/gd/packet/parser/fields/all_fields.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 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 "fields/body_field.h"
+#include "fields/checksum_field.h"
+#include "fields/checksum_start_field.h"
+#include "fields/custom_field.h"
+#include "fields/enum_field.h"
+#include "fields/fixed_field.h"
+#include "fields/group_field.h"
+#include "fields/payload_field.h"
+#include "fields/reserved_field.h"
+#include "fields/scalar_field.h"
+#include "fields/size_field.h"
diff --git a/gd/packet/parser/fields/body_field.cc b/gd/packet/parser/fields/body_field.cc
new file mode 100644
index 0000000..9a0fe25
--- /dev/null
+++ b/gd/packet/parser/fields/body_field.cc
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 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 "fields/body_field.h"
+
+BodyField::BodyField(ParseLocation loc) : PacketField(loc, "Body") {}
+
+PacketField::Type BodyField::GetFieldType() const {
+  return PacketField::Type::BODY;
+}
+
+Size BodyField::GetSize() const {
+  return Size(0);
+}
+
+std::string BodyField::GetType() const {
+  ERROR(this) << "No need to know the type of a body field.";
+  return "BodyType";
+}
+
+void BodyField::GenGetter(std::ostream&, Size, Size) const {}
+
+bool BodyField::GenBuilderParameter(std::ostream&) const {
+  return false;
+}
+
+bool BodyField::HasParameterValidator() const {
+  return false;
+}
+
+void BodyField::GenParameterValidator(std::ostream&) const {
+  // There is no validation needed for a payload
+}
+
+void BodyField::GenInserter(std::ostream&) const {
+  // Do nothing
+}
+
+void BodyField::GenValidator(std::ostream&) const {
+  // Do nothing
+}
diff --git a/gd/packet/parser/fields/body_field.h b/gd/packet/parser/fields/body_field.h
new file mode 100644
index 0000000..9cc2301
--- /dev/null
+++ b/gd/packet/parser/fields/body_field.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 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 "fields/packet_field.h"
+#include "fields/size_field.h"
+#include "parse_location.h"
+
+class BodyField : public PacketField {
+ public:
+  BodyField(ParseLocation loc);
+
+  virtual PacketField::Type GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetType() const override;
+
+  virtual void GenGetter(std::ostream&, Size, Size) const override;
+
+  virtual bool GenBuilderParameter(std::ostream&) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream&) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+};
diff --git a/gd/packet/parser/fields/checksum_field.cc b/gd/packet/parser/fields/checksum_field.cc
new file mode 100644
index 0000000..4a29e4d
--- /dev/null
+++ b/gd/packet/parser/fields/checksum_field.cc
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019 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 "fields/checksum_field.h"
+#include "util.h"
+
+ChecksumField::ChecksumField(std::string name, std::string type_name, int size, ParseLocation loc)
+    : PacketField(loc, name), type_name_(type_name), size_(size) {}
+
+PacketField::Type ChecksumField::GetFieldType() const {
+  return PacketField::Type::CHECKSUM;
+}
+
+Size ChecksumField::GetSize() const {
+  return size_;
+}
+
+std::string ChecksumField::GetType() const {
+  return type_name_;
+}
+
+void ChecksumField::GenGetter(std::ostream&, Size, Size) const {}
+
+bool ChecksumField::GenBuilderParameter(std::ostream&) const {
+  return false;
+}
+
+bool ChecksumField::HasParameterValidator() const {
+  return false;
+}
+
+void ChecksumField::GenParameterValidator(std::ostream&) const {
+  // Do nothing.
+}
+
+void ChecksumField::GenInserter(std::ostream& s) const {
+  s << "packet::ByteObserver observer = i.UnregisterObserver();";
+  s << "insert(static_cast<" << util::GetTypeForSize(size_) << ">(observer.GetValue()), i);";
+}
+
+void ChecksumField::GenValidator(std::ostream&) const {
+  // Done in packet_def.cc
+}
diff --git a/gd/packet/parser/fields/checksum_field.h b/gd/packet/parser/fields/checksum_field.h
new file mode 100644
index 0000000..5bf3923
--- /dev/null
+++ b/gd/packet/parser/fields/checksum_field.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 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 "fields/packet_field.h"
+#include "parse_location.h"
+
+class ChecksumField : public PacketField {
+ public:
+  ChecksumField(std::string name, std::string type_name, int size, ParseLocation loc);
+
+  virtual PacketField::Type GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetType() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual bool GenBuilderParameter(std::ostream& s) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+ private:
+  std::string type_name_;
+
+ public:
+  const int size_{-1};
+};
diff --git a/gd/packet/parser/fields/checksum_start_field.cc b/gd/packet/parser/fields/checksum_start_field.cc
new file mode 100644
index 0000000..75438f2
--- /dev/null
+++ b/gd/packet/parser/fields/checksum_start_field.cc
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019 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 "fields/checksum_start_field.h"
+#include "util.h"
+
+ChecksumStartField::ChecksumStartField(std::string name, ParseLocation loc)
+    : PacketField(loc, name + "_start"), started_field_name_(name) {}
+
+PacketField::Type ChecksumStartField::GetFieldType() const {
+  return PacketField::Type::CHECKSUM_START;
+}
+
+Size ChecksumStartField::GetSize() const {
+  return Size(0);
+}
+
+std::string ChecksumStartField::GetType() const {
+  return "There's no type for Checksum Start fields";
+}
+
+void ChecksumStartField::GenGetter(std::ostream&, Size, Size) const {}
+
+bool ChecksumStartField::GenBuilderParameter(std::ostream&) const {
+  // There is no builder parameter for a size field
+  return false;
+}
+
+bool ChecksumStartField::HasParameterValidator() const {
+  return false;
+}
+
+void ChecksumStartField::GenParameterValidator(std::ostream&) const {}
+
+void ChecksumStartField::GenInserter(std::ostream&) const {
+  ERROR(this) << __func__ << ": This should not be called for checksum start fields";
+}
+
+void ChecksumStartField::GenValidator(std::ostream&) const {}
+
+std::string ChecksumStartField::GetStartedFieldName() const {
+  return started_field_name_;
+}
diff --git a/gd/packet/parser/fields/checksum_start_field.h b/gd/packet/parser/fields/checksum_start_field.h
new file mode 100644
index 0000000..248c146
--- /dev/null
+++ b/gd/packet/parser/fields/checksum_start_field.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 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 "fields/packet_field.h"
+#include "parse_location.h"
+
+class ChecksumStartField : public PacketField {
+ public:
+  ChecksumStartField(std::string name, ParseLocation loc);
+
+  std::string GetField() const;
+
+  virtual PacketField::Type GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetType() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual bool GenBuilderParameter(std::ostream&) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream&) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+  virtual std::string GetStartedFieldName() const;
+
+ private:
+  std::string started_field_name_;
+};
diff --git a/gd/packet/parser/fields/custom_field.cc b/gd/packet/parser/fields/custom_field.cc
new file mode 100644
index 0000000..05952ab
--- /dev/null
+++ b/gd/packet/parser/fields/custom_field.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2019 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 "fields/custom_field.h"
+#include "util.h"
+
+CustomField::CustomField(std::string name, std::string type_name, ParseLocation loc)
+    : PacketField(loc, name), type_name_(type_name) {}
+
+// Fixed size custom fields.
+CustomField::CustomField(std::string name, std::string type_name, int size, ParseLocation loc)
+    : PacketField(loc, name), type_name_(type_name), size_(size) {}
+
+PacketField::Type CustomField::GetFieldType() const {
+  return PacketField::Type::CUSTOM;
+}
+
+Size CustomField::GetSize() const {
+  return size_;
+}
+
+std::string CustomField::GetType() const {
+  return type_name_;
+}
+
+void CustomField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const {
+  s << GetType();
+  s << " Get" << util::UnderscoreToCamelCase(GetName()) << "() const {";
+
+  s << "auto it = ";
+  if (!start_offset.empty()) {
+    // Default to start if available.
+    if (start_offset.bits() % 8 != 0) {
+      ERROR(this) << "Custom Field must be byte aligned.";
+    }
+    s << "begin()";
+    if (start_offset.bits() / 8 != 0) s << " + " << start_offset.bits() / 8;
+    if (start_offset.has_dynamic()) s << " + " << start_offset.dynamic_string();
+  } else if (size_ != -1) {
+    // If the size of the custom field is already known, we can determine it's offset based on end().
+    if (!end_offset.empty()) {
+      if (end_offset.bits() % 8) {
+        ERROR(this) << "Custom Field must be byte aligned.";
+      }
+
+      int byte_offset = (end_offset.bits() + size_) / 8;
+      s << "end() - " << byte_offset;
+      if (end_offset.has_dynamic()) s << " - (" << end_offset.dynamic_string() << ")";
+    } else {
+      ERROR(this) << "Ambiguous offset for fixed size custom field.";
+    }
+  } else {
+    ERROR(this) << "Custom Field offset can not be determined from begin().";
+  }
+  s << ";";
+
+  s << "return it.extract<" << GetType() << ">();";
+  s << "}\n";
+}
+
+bool CustomField::GenBuilderParameter(std::ostream& s) const {
+  s << GetType() << " " << GetName();
+  return true;
+}
+
+bool CustomField::HasParameterValidator() const {
+  return false;
+}
+
+void CustomField::GenParameterValidator(std::ostream&) const {
+  // Do nothing.
+}
+
+void CustomField::GenInserter(std::ostream& s) const {
+  s << "insert(" << GetName() << "_, i);";
+}
+
+void CustomField::GenValidator(std::ostream&) const {
+  // Do nothing.
+}
diff --git a/gd/packet/parser/fields/custom_field.h b/gd/packet/parser/fields/custom_field.h
new file mode 100644
index 0000000..95a67a1
--- /dev/null
+++ b/gd/packet/parser/fields/custom_field.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 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 "fields/packet_field.h"
+#include "parse_location.h"
+
+class CustomField : public PacketField {
+ public:
+  CustomField(std::string name, std::string type_name, ParseLocation loc);
+
+  CustomField(std::string name, std::string type_name, int size, ParseLocation loc);
+
+  virtual PacketField::Type GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetType() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual bool GenBuilderParameter(std::ostream& s) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+ private:
+  std::string type_name_;
+
+ public:
+  const int size_{-1};
+};
diff --git a/gd/packet/parser/fields/enum_field.cc b/gd/packet/parser/fields/enum_field.cc
new file mode 100644
index 0000000..32a31d9
--- /dev/null
+++ b/gd/packet/parser/fields/enum_field.cc
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2019 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 "fields/enum_field.h"
+
+#include "util.h"
+
+EnumField::EnumField(std::string name, EnumDef enum_def, std::string value, ParseLocation loc)
+    : PacketField(loc, name), enum_def_(enum_def), value_(value) {}
+
+EnumDef EnumField::GetEnumDef() {
+  return enum_def_;
+}
+
+PacketField::Type EnumField::GetFieldType() const {
+  return PacketField::Type::ENUM;
+}
+
+Size EnumField::GetSize() const {
+  return enum_def_.size_;
+}
+
+std::string EnumField::GetType() const {
+  return enum_def_.name_;
+}
+
+void EnumField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const {
+  s << GetType();
+  s << " Get" << util::UnderscoreToCamelCase(GetName()) << "() const {";
+  s << "ASSERT(was_validated_);";
+
+  // Write the Getter Function Body
+  int num_leading_bits = 0;
+  int field_size = enum_def_.size_;
+
+  // Start from the beginning, if possible.
+  if (!start_offset.empty()) {
+    num_leading_bits = start_offset.bits() % 8;
+    s << "auto it = begin()"
+      << " + " << start_offset.bytes() << " + (" << start_offset.dynamic_string() << ");";
+  } else if (!end_offset.empty()) {
+    int offset_from_end = end_offset.bits() + field_size;
+    num_leading_bits = 8 - (offset_from_end % 8);
+    int byte_offset = (7 + offset_from_end) / 8;
+    s << "auto it = end() - " << byte_offset << " - (" << end_offset.dynamic_string() << ");";
+  } else {
+    ERROR(this) << "Ambiguous offset for field.";
+  }
+
+  // We don't need any masking, just return the extracted value.
+  if (num_leading_bits == 0 && util::RoundSizeUp(field_size) == field_size) {
+    s << "return it.extract<" << GetType() << ">();";
+    s << "}\n";
+    return;
+  }
+
+  // Extract the correct number of bytes. The return type could be different
+  // from the extract type if an earlier field causes the beginning of the
+  // current field to start in the middle of a byte.
+  std::string extract_type = util::GetTypeForSize(field_size + num_leading_bits);
+  s << "auto value = it.extract<" << extract_type << ">();";
+
+  // Right shift the result if necessary.
+  int shift_amount = num_leading_bits;
+  if (shift_amount != 0) {
+    s << "value >>= " << shift_amount << ";";
+  }
+
+  // Mask the result if necessary.
+  if (util::RoundSizeUp(field_size) != field_size) {
+    uint64_t mask = 0;
+    for (int i = 0; i < field_size; i++) {
+      mask <<= 1;
+      mask |= 1;
+    }
+    s << "value &= 0x" << std::hex << mask << std::dec << ";";
+  }
+
+  s << "return static_cast<" << GetType() << ">(value);";
+  s << "}\n";
+}
+
+bool EnumField::GenBuilderParameter(std::ostream& s) const {
+  s << GetType() << " " << GetName();
+  return true;
+}
+
+bool EnumField::HasParameterValidator() const {
+  return false;
+}
+
+void EnumField::GenParameterValidator(std::ostream&) const {
+  // Validated at compile time.
+}
+
+void EnumField::GenInserter(std::ostream& s) const {
+  s << "insert(static_cast<" << util::GetTypeForSize(GetSize().bits()) << ">(";
+  s << GetName() << "_), i, " << GetSize().bits() << ");";
+}
+
+void EnumField::GenValidator(std::ostream&) const {
+  // Do nothing
+}
diff --git a/gd/packet/parser/fields/enum_field.h b/gd/packet/parser/fields/enum_field.h
new file mode 100644
index 0000000..2ab77f1
--- /dev/null
+++ b/gd/packet/parser/fields/enum_field.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 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 "enum_def.h"
+#include "fields/packet_field.h"
+#include "parse_location.h"
+
+class EnumField : public PacketField {
+ public:
+  EnumField(std::string name, EnumDef enum_def, std::string value, ParseLocation loc);
+
+  EnumDef GetEnumDef();
+
+  virtual PacketField::Type GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetType() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual bool GenBuilderParameter(std::ostream& s) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+ private:
+  EnumDef enum_def_;
+  std::string value_;
+};
diff --git a/gd/packet/parser/fields/fixed_field.cc b/gd/packet/parser/fields/fixed_field.cc
new file mode 100644
index 0000000..1a30cc9
--- /dev/null
+++ b/gd/packet/parser/fields/fixed_field.cc
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2019 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 "fields/fixed_field.h"
+#include "util.h"
+
+int FixedField::unique_id_ = 0;
+
+FixedField::FixedField(int size, int64_t value, ParseLocation loc)
+    : PacketField(loc, "FixedScalar" + std::to_string(unique_id_++)), type_(Type::FIXED_SCALAR), size_(size),
+      value_(value) {}
+
+FixedField::FixedField(EnumDef* enum_def, std::string value, ParseLocation loc)
+    : PacketField(loc, "FixedScalar" + std::to_string(unique_id_++)), type_(Type::FIXED_ENUM), enum_(enum_def),
+      value_(value) {}
+
+PacketField::Type FixedField::GetFieldType() const {
+  return type_;
+}
+
+Size FixedField::GetSize() const {
+  if (type_ == PacketField::Type::FIXED_SCALAR) {
+    return size_;
+  }
+
+  return enum_->size_;
+}
+
+std::string FixedField::GetType() const {
+  if (type_ == PacketField::Type::FIXED_SCALAR) {
+    return util::GetTypeForSize(size_);
+  }
+
+  return enum_->name_;
+}
+
+void FixedField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const {
+  s << GetType();
+  s << " Get" << util::UnderscoreToCamelCase(GetName()) << "() const {";
+  s << "ASSERT(was_validated_);";
+
+  // Write the Getter Function Body
+  int num_leading_bits = 0;
+  int field_size = GetSize().bits();
+
+  // Handle if to start the iterator at begin or end.
+  if (!start_offset.empty()) {
+    // Default to start if available.
+    num_leading_bits = start_offset.bits() % 8;
+    s << "auto it = begin() + " << start_offset.bytes() << " + (" << start_offset.dynamic_string() << ");";
+  } else if (!end_offset.empty()) {
+    int offset_from_end = end_offset.bits() + field_size;
+    num_leading_bits = 8 - (offset_from_end % 8);
+    // Add 7 so it rounds up
+    int byte_offset = (7 + offset_from_end) / 8;
+    s << "auto it = end() - " << byte_offset << " - (" << end_offset.dynamic_string() << ");";
+  } else {
+    ERROR(this) << "Ambiguous offset for field.\n";
+  }
+
+  // We don't need any masking, just return the extracted value.
+  if (num_leading_bits == 0 && util::RoundSizeUp(field_size) == field_size) {
+    s << "return it.extract<" << GetType() << ">();";
+    s << "}\n";
+    return;
+  }
+
+  // Extract the correct number of bytes. The return type could be different
+  // from the extract type if an earlier field causes the beginning of the
+  // current field to start in the middle of a byte.
+  std::string extract_type = util::GetTypeForSize(field_size + num_leading_bits);
+  s << "auto value = it.extract<" << extract_type << ">();";
+
+  // Right shift to remove leading bits.
+  if (num_leading_bits != 0) {
+    s << "value >>= " << num_leading_bits << ";";
+  }
+
+  // Mask the result if necessary.
+  if (util::RoundSizeUp(field_size) != field_size) {
+    uint64_t mask = 0;
+    for (int i = 0; i < field_size; i++) {
+      mask <<= 1;
+      mask |= 1;
+    }
+    s << "value &= 0x" << std::hex << mask << std::dec << ";";
+  }
+
+  // Cast the result if necessary.
+  if (extract_type != GetType()) {
+    s << "return static_cast<" << GetType() << ">(value);";
+  } else {
+    s << "return value;";
+  }
+  s << "}\n";
+}
+
+bool FixedField::GenBuilderParameter(std::ostream&) const {
+  // No parameter needed for a fixed field.
+  return false;
+}
+
+bool FixedField::HasParameterValidator() const {
+  return false;
+}
+
+void FixedField::GenParameterValidator(std::ostream&) const {
+  // No parameter validator needed for a fixed field.
+}
+
+void FixedField::GenInserter(std::ostream& s) const {
+  s << "insert(";
+  if (type_ == PacketField::Type::FIXED_SCALAR) {
+    GenValue(s);
+  } else {
+    s << "static_cast<" << util::GetTypeForSize(GetSize().bits()) << ">(";
+    GenValue(s);
+    s << ")";
+  }
+  s << ", i , " << GetSize().bits() << ");";
+}
+
+void FixedField::GenValidator(std::ostream& s) const {
+  s << "if (Get" << util::UnderscoreToCamelCase(GetName()) << "() != ";
+  GenValue(s);
+  s << ") return false;";
+}
+
+void FixedField::GenValue(std::ostream& s) const {
+  if (type_ == PacketField::Type::FIXED_SCALAR) {
+    s << std::get<int64_t>(value_);
+  } else {
+    s << enum_->name_ << "::" << std::get<std::string>(value_);
+  }
+}
diff --git a/gd/packet/parser/fields/fixed_field.h b/gd/packet/parser/fields/fixed_field.h
new file mode 100644
index 0000000..f91e50c
--- /dev/null
+++ b/gd/packet/parser/fields/fixed_field.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2019 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 <variant>
+
+#include "enum_def.h"
+#include "fields/packet_field.h"
+#include "parse_location.h"
+
+class FixedField : public PacketField {
+ public:
+  FixedField(int size, int64_t value, ParseLocation loc);
+
+  FixedField(EnumDef* enum_def, std::string value, ParseLocation loc);
+
+  virtual PacketField::Type GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetType() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual bool GenBuilderParameter(std::ostream&) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream& s) const override;
+
+ private:
+  void GenValue(std::ostream& s) const;
+
+  PacketField::Type type_;
+  int size_;
+  EnumDef* enum_;
+  std::variant<int64_t, std::string> value_;
+
+  static int unique_id_;
+};
diff --git a/gd/packet/parser/fields/group_field.cc b/gd/packet/parser/fields/group_field.cc
new file mode 100644
index 0000000..7e4caba
--- /dev/null
+++ b/gd/packet/parser/fields/group_field.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2019 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 "fields/group_field.h"
+
+GroupField::GroupField(ParseLocation loc, std::list<PacketField*>* fields)
+    : PacketField(loc, "Groups have no name"), fields_(fields) {}
+
+GroupField::~GroupField() {
+  delete fields_;
+}
+
+PacketField::Type GroupField::GetFieldType() const {
+  return PacketField::Type::GROUP;
+}
+
+std::string GroupField::GetName() const {
+  ERROR(this) << "GetName should never be called.";
+  return "";
+}
+
+Size GroupField::GetSize() const {
+  ERROR(this) << "GetSize should never be called.";
+  return Size();
+}
+
+std::string GroupField::GetType() const {
+  ERROR(this) << "GetType should never be called.";
+  return "";
+}
+
+void GroupField::GenGetter(std::ostream&, Size, Size) const {
+  ERROR(this) << "GenGetter should never be called.";
+}
+
+bool GroupField::GenBuilderParameter(std::ostream&) const {
+  ERROR(this) << "GenBuilderParameter should never be called";
+  return false;
+}
+
+bool GroupField::HasParameterValidator() const {
+  ERROR(this) << "HasParameterValidator should never be called";
+  return false;
+}
+
+void GroupField::GenParameterValidator(std::ostream&) const {
+  ERROR(this) << "Not implemented";
+}
+
+void GroupField::GenInserter(std::ostream&) const {
+  ERROR(this) << "GenInserter should never be called.";
+}
+
+void GroupField::GenValidator(std::ostream&) const {
+  ERROR(this) << "GenValidator should never be called.";
+}
+
+const std::list<PacketField*>* GroupField::GetFields() const {
+  return fields_;
+}
diff --git a/gd/packet/parser/fields/group_field.h b/gd/packet/parser/fields/group_field.h
new file mode 100644
index 0000000..73e7e35
--- /dev/null
+++ b/gd/packet/parser/fields/group_field.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 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 <list>
+
+#include "fields/packet_field.h"
+#include "parse_location.h"
+
+class GroupField : public PacketField {
+ public:
+  GroupField(ParseLocation loc, std::list<PacketField*>* fields);
+
+  ~GroupField();
+
+  virtual PacketField::Type GetFieldType() const override;
+
+  virtual std::string GetName() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetType() const override;
+
+  virtual void GenGetter(std::ostream&, Size, Size) const override;
+
+  virtual bool GenBuilderParameter(std::ostream&) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream&) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+  const std::list<PacketField*>* GetFields() const;
+
+ private:
+  std::list<PacketField*>* fields_;
+};
diff --git a/gd/packet/parser/fields/packet_field.cc b/gd/packet/parser/fields/packet_field.cc
new file mode 100644
index 0000000..677ebfe
--- /dev/null
+++ b/gd/packet/parser/fields/packet_field.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2019 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 "fields/packet_field.h"
+
+PacketField::PacketField(ParseLocation loc, std::string name) : loc_(loc), name_(name) {}
+
+std::string PacketField::GetDebugName() const {
+  std::string ret = "";
+  switch (GetFieldType()) {
+    case Type::GROUP:
+      ret = "GROUP";
+      break;
+    case Type::FIXED_SCALAR:
+      ret = "FIXED SCALAR";
+      break;
+    case Type::FIXED_ENUM:
+      ret = "FIXED ENUM";
+      break;
+    case Type::RESERVED_SCALAR:
+      ret = "RESERVED SCALAR";
+      break;
+    case Type::SCALAR:
+      ret = "SCALAR";
+      break;
+    case Type::ENUM:
+      ret = "ENUM";
+      break;
+    case Type::SIZE:
+      ret = "SIZE";
+      break;
+    case Type::COUNT:
+      ret = "COUNT";
+      break;
+    case Type::BODY:
+      ret = "BODY";
+      break;
+    case Type::PAYLOAD:
+      ret = "PAYLOAD";
+      break;
+    case Type::CUSTOM:
+      ret = "CUSTOM";
+      break;
+    default:
+      std::cerr << "UNKNOWN DEBUG NAME TYPE\n";
+      abort();
+  }
+
+  return "Field{Type:" + ret + ", Name:" + GetName() + "}";
+}
+
+ParseLocation PacketField::GetLocation() const {
+  return loc_;
+}
+
+std::string PacketField::GetName() const {
+  return name_;
+}
diff --git a/gd/packet/parser/fields/packet_field.h b/gd/packet/parser/fields/packet_field.h
new file mode 100644
index 0000000..5f78c69
--- /dev/null
+++ b/gd/packet/parser/fields/packet_field.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2019 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 <iostream>
+#include <string>
+
+#include "logging.h"
+#include "parse_location.h"
+#include "size.h"
+
+// The base field that every packet needs to inherit from.
+class PacketField : public Loggable {
+ public:
+  virtual ~PacketField() = default;
+
+  PacketField(ParseLocation loc, std::string name);
+
+  enum class Type {
+    GROUP,
+    FIXED_SCALAR,
+    FIXED_ENUM,
+    RESERVED_SCALAR,
+    SCALAR,
+    ENUM,
+    SIZE,
+    COUNT,
+    BODY,
+    PAYLOAD,
+    CUSTOM,
+    CHECKSUM,
+    CHECKSUM_START,
+  };
+
+  // Get the field type for the field.
+  virtual Type GetFieldType() const = 0;
+
+  // Returns the size of the field in bits and a string that evaluates into
+  // bytes for dynamically sized arrays.
+  virtual Size GetSize() const = 0;
+
+  // Get the type of the field to be used in the builders constructor and
+  // variables.
+  virtual std::string GetType() const = 0;
+
+  // Get parser getter definition. Start_offset points to the first bit of the
+  // field. end_offset is the first bit after the field. If an offset is empty
+  // that means that there was a field with an unknown size when trying to
+  // calculate the offset.
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const = 0;
+
+  // Generate the parameter for Create(), return true if a parameter was added.
+  virtual bool GenBuilderParameter(std::ostream& s) const = 0;
+
+  // Returns whether or not the field must be validated.
+  virtual bool HasParameterValidator() const = 0;
+
+  // Fail if the value doesn't fit in the field.
+  virtual void GenParameterValidator(std::ostream& s) const = 0;
+
+  // Generate the inserter for pushing the data in the builder.
+  virtual void GenInserter(std::ostream& s) const = 0;
+
+  // Generate the validator for a field for the IsValid() function.
+  //
+  // The way this function works is by assuming that there is an iterator |it|
+  // that was defined earlier. The implementer of the function will then move
+  // it forward based on the dynamic size of the field and then check to see if
+  // its past the end of the packet.
+  // It should be unused for fixed size fields unless special consideration is
+  // needed. This is because all fixed size fields are tallied together with
+  // GetSize() and used as an initial offset. One special consideration is for
+  // enums where instead of checking if they can be read, they are checked to
+  // see if they contain the correct value.
+  virtual void GenValidator(std::ostream& s) const = 0;
+
+  std::string GetDebugName() const override;
+
+  ParseLocation GetLocation() const override;
+
+  virtual std::string GetName() const;
+
+ private:
+  ParseLocation loc_;
+  std::string name_;
+};
diff --git a/gd/packet/parser/fields/payload_field.cc b/gd/packet/parser/fields/payload_field.cc
new file mode 100644
index 0000000..e070ea4
--- /dev/null
+++ b/gd/packet/parser/fields/payload_field.cc
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2019 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 "fields/payload_field.h"
+#include "util.h"
+
+PayloadField::PayloadField(std::string modifier, ParseLocation loc)
+    : PacketField(loc, "Payload"), size_field_(nullptr), size_modifier_(modifier) {}
+
+void PayloadField::SetSizeField(const SizeField* size_field) {
+  if (size_field_ != nullptr) {
+    ERROR(this, size_field_, size_field) << "The size field for the payload has already been assigned.";
+  }
+
+  if (size_field->GetFieldType() == PacketField::Type::COUNT) {
+    ERROR(this, size_field) << "Can not use count field to describe a payload.";
+  }
+
+  size_field_ = size_field;
+}
+
+PacketField::Type PayloadField::GetFieldType() const {
+  return PacketField::Type::PAYLOAD;
+}
+
+Size PayloadField::GetSize() const {
+  if (size_field_ == nullptr) {
+    // Require a size field if there is a modifier.
+    if (!size_modifier_.empty()) {
+      ERROR(this) << "Missing size field for payload with size modifier.";
+    }
+
+    return Size();
+  }
+
+  std::string dynamic_size = "Get" + util::UnderscoreToCamelCase(size_field_->GetName()) + "()";
+  if (!size_modifier_.empty()) {
+    dynamic_size += "- (" + size_modifier_ + ") / 8";
+  }
+
+  return dynamic_size;
+}
+
+std::string PayloadField::GetType() const {
+  return "PacketView";
+}
+
+void PayloadField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const {
+  if (start_offset.empty()) {
+    ERROR(this) << "Can not have a payload that has an ambiguous start offset. "
+                << "Is there a field with an unknown length before the "
+                << "payload?\n";
+  }
+
+  if (start_offset.bits() % 8 != 0 && !GetSize().empty()) {
+    ERROR(this) << "Can not have a sized payload field "
+                << "at a non byte-aligned offset.\n";
+  }
+
+  if (GetSize().empty() && end_offset.empty()) {
+    ERROR(this) << "Ambiguous end offset for payload with no defined size.";
+  }
+
+  s << "PacketView<kLittleEndian> GetPayload() {";
+  s << "ASSERT(was_validated_);";
+
+  s << "size_t payload_begin = " << start_offset.bits() / 8 << " + (" << start_offset.dynamic_string() << ");";
+
+  // If the payload is sized, use the size + payload_begin for payload_end, otherwise use the end_offset.
+  if (!GetSize().empty()) {
+    // If the size isn't empty then it must have a dynamic string only.
+    s << "size_t payload_end = payload_begin + (" << GetSize().dynamic_string() << ");";
+  } else {
+    s << "size_t payload_end = size() - " << end_offset.bits() / 8 << " - (" << end_offset.dynamic_string() << ");";
+  }
+
+  s << "return GetLittleEndianSubview(payload_begin, payload_end);";
+  s << "}\n\n";
+
+  s << "PacketView<!kLittleEndian> GetPayloadBigEndian() {";
+
+  s << "size_t payload_begin = " << start_offset.bits() / 8 << " + (" << start_offset.dynamic_string() << ");";
+
+  // If the payload is sized, use the size + payload_begin for payload_end, otherwise use the end_offset.
+  if (!GetSize().empty()) {
+    // If the size isn't empty then it must have a dynamic string only.
+    s << "size_t payload_end = payload_begin + (" << GetSize().dynamic_string() << ");";
+  } else {
+    s << "size_t payload_end = size() - " << end_offset.bits() / 8 << " - (" << end_offset.dynamic_string() << ");";
+  }
+
+  s << "return GetBigEndianSubview(payload_begin, payload_end);";
+  s << "}\n";
+}
+
+bool PayloadField::GenBuilderParameter(std::ostream& s) const {
+  s << "std::unique_ptr<BasePacketBuilder> payload";
+  return true;
+}
+
+bool PayloadField::HasParameterValidator() const {
+  return false;
+}
+
+void PayloadField::GenParameterValidator(std::ostream&) const {
+  // There is no validation needed for a payload
+}
+
+void PayloadField::GenInserter(std::ostream&) const {
+  ERROR() << __func__ << " Should never be called.";
+}
+
+void PayloadField::GenValidator(std::ostream&) const {
+  // Do nothing
+}
diff --git a/gd/packet/parser/fields/payload_field.h b/gd/packet/parser/fields/payload_field.h
new file mode 100644
index 0000000..8787b41
--- /dev/null
+++ b/gd/packet/parser/fields/payload_field.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 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 "fields/packet_field.h"
+#include "fields/size_field.h"
+#include "parse_location.h"
+
+class PayloadField : public PacketField {
+ public:
+  PayloadField(std::string modifier, ParseLocation loc);
+
+  void SetSizeField(const SizeField* size_field);
+
+  virtual PacketField::Type GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetType() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual bool GenBuilderParameter(std::ostream& s) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream&) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+  // Payload fields can only be dynamically sized.
+  const SizeField* size_field_;
+  // Only used if the size of the payload is based on another field.
+  std::string size_modifier_;
+};
diff --git a/gd/packet/parser/fields/reserved_field.cc b/gd/packet/parser/fields/reserved_field.cc
new file mode 100644
index 0000000..3db1527
--- /dev/null
+++ b/gd/packet/parser/fields/reserved_field.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 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 "fields/reserved_field.h"
+#include "util.h"
+
+int ReservedField::unique_id_ = 0;
+
+ReservedField::ReservedField(int size, ParseLocation loc)
+    : PacketField(loc, "ReservedScalar" + std::to_string(unique_id_++)), type_(PacketField::Type::RESERVED_SCALAR),
+      size_(size) {}
+
+PacketField::Type ReservedField::GetFieldType() const {
+  return type_;
+}
+
+Size ReservedField::GetSize() const {
+  return size_;
+}
+
+std::string ReservedField::GetType() const {
+  return util::GetTypeForSize(size_);
+}
+
+void ReservedField::GenGetter(std::ostream&, Size, Size) const {
+  // There is no Getter for a reserved field
+}
+
+bool ReservedField::GenBuilderParameter(std::ostream&) const {
+  // There is no builder parameter for a reserved field
+  return false;
+}
+
+bool ReservedField::HasParameterValidator() const {
+  return false;
+}
+
+void ReservedField::GenParameterValidator(std::ostream&) const {
+  // There is no builder parameter for a reserved field
+}
+
+void ReservedField::GenInserter(std::ostream& s) const {
+  s << "insert(static_cast<" << util::GetTypeForSize(GetSize().bits()) << ">(0) /* Reserved */, i, " << GetSize().bits()
+    << " );\n";
+}
+
+void ReservedField::GenValidator(std::ostream&) const {
+  // There is no need to validate the value of a reserved field
+}
diff --git a/gd/packet/parser/fields/reserved_field.h b/gd/packet/parser/fields/reserved_field.h
new file mode 100644
index 0000000..46f7f7b
--- /dev/null
+++ b/gd/packet/parser/fields/reserved_field.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 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 "fields/packet_field.h"
+#include "parse_location.h"
+
+class ReservedField : public PacketField {
+ public:
+  ReservedField(int size, ParseLocation loc);
+
+  virtual PacketField::Type GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetType() const override;
+
+  virtual void GenGetter(std::ostream&, Size, Size) const override;
+
+  virtual bool GenBuilderParameter(std::ostream&) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+ private:
+  PacketField::Type type_;
+  std::string name_;
+  int size_;
+  static int unique_id_;
+};
diff --git a/gd/packet/parser/fields/scalar_field.cc b/gd/packet/parser/fields/scalar_field.cc
new file mode 100644
index 0000000..91907b2
--- /dev/null
+++ b/gd/packet/parser/fields/scalar_field.cc
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2019 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 "fields/scalar_field.h"
+#include "util.h"
+
+ScalarField::ScalarField(std::string name, int size, ParseLocation loc) : PacketField(loc, name), size_(size) {}
+
+PacketField::Type ScalarField::GetFieldType() const {
+  return PacketField::Type::SCALAR;
+}
+
+Size ScalarField::GetSize() const {
+  return size_;
+}
+
+std::string ScalarField::GetType() const {
+  return util::GetTypeForSize(size_);
+}
+
+void ScalarField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const {
+  s << GetType();
+  s << " Get" << util::UnderscoreToCamelCase(GetName()) << "() const {";
+  s << "ASSERT(was_validated_);";
+
+  // Write the Getter Function Body
+  int num_leading_bits = 0;
+  int field_size = size_;
+
+  // Handle if to start the iterator at begin or end.
+  s << "auto it = ";
+  if (!start_offset.empty()) {
+    // Default to start if available.
+    num_leading_bits = start_offset.bits() % 8;
+    s << "begin()";
+    if (start_offset.bits() / 8 != 0) s << " + " << start_offset.bits() / 8;
+    if (start_offset.has_dynamic()) s << " + " << start_offset.dynamic_string();
+  } else if (!end_offset.empty()) {
+    num_leading_bits = (8 - ((end_offset.bits() + field_size) % 8)) % 8;
+    // Add 7 so it rounds up
+    int byte_offset = (7 + end_offset.bits() + field_size) / 8;
+    s << "end() - " << byte_offset;
+    if (end_offset.has_dynamic()) s << " - (" << end_offset.dynamic_string() << ")";
+  } else {
+    ERROR(this) << "Ambiguous offset for field.";
+  }
+  s << ";";
+
+  // We don't need any masking, just return the extracted value.
+  if (num_leading_bits == 0 && util::RoundSizeUp(field_size) == field_size) {
+    s << "return it.extract<" << util::GetTypeForSize(field_size) << ">();";
+    s << "}\n";
+    return;
+  }
+
+  // Extract the correct number of bytes. The return type could be different
+  // from the extract type if an earlier field causes the beginning of the
+  // current field to start in the middle of a byte.
+  std::string extract_type = util::GetTypeForSize(field_size + num_leading_bits);
+  s << "auto value = it.extract<" << extract_type << ">();";
+
+  // Right shift the result if necessary.
+  int shift_amount = num_leading_bits;
+  if (shift_amount != 0) {
+    s << "value >>= " << shift_amount << ";";
+  }
+
+  // Mask the result if necessary.
+  if (util::RoundSizeUp(field_size) != field_size) {
+    uint64_t mask = 0;
+    for (int i = 0; i < field_size; i++) {
+      mask <<= 1;
+      mask |= 1;
+    }
+    s << "value &= 0x" << std::hex << mask << std::dec << ";";
+  }
+
+  // Cast the result if necessary.
+  if (extract_type != util::GetTypeForSize(field_size)) {
+    s << "return static_cast<" << GetType() << ">(value);";
+  } else {
+    s << "return value;";
+  }
+  s << "}\n";
+}
+
+bool ScalarField::GenBuilderParameter(std::ostream& s) const {
+  if (size_ > 64 || size_ < 0) {
+    ERROR(this) << "Not implemented";
+  }
+  std::string param_type = util::GetTypeForSize(size_);
+  s << param_type << " " << GetName();
+  return true;
+}
+
+bool ScalarField::HasParameterValidator() const {
+  const auto bits = GetSize().bits();
+  return util::RoundSizeUp(bits) != bits;
+}
+
+void ScalarField::GenParameterValidator(std::ostream& s) const {
+  const auto bits = GetSize().bits();
+  if (util::RoundSizeUp(bits) == bits) {
+    return;
+  }
+  s << "ASSERT(" << GetName() << " < "
+    << "(static_cast<uint64_t>(1) << " << bits << "));";
+}
+
+void ScalarField::GenInserter(std::ostream& s) const {
+  if (GetSize().bits() == 8) {
+    s << "i.insert_byte(" << GetName() << "_);";
+  } else if (GetSize().bits() % 8 == 0) {
+    s << "insert(" << GetName() << "_, i);";
+  } else {
+    s << "insert(" << GetName() << "_, i," << GetSize().bits() << ");";
+  }
+}
+
+void ScalarField::GenValidator(std::ostream&) const {
+  // Do nothing since the fixed size fields will be handled seperatly.
+}
diff --git a/gd/packet/parser/fields/scalar_field.h b/gd/packet/parser/fields/scalar_field.h
new file mode 100644
index 0000000..4e52fd1
--- /dev/null
+++ b/gd/packet/parser/fields/scalar_field.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 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 "fields/packet_field.h"
+#include "parse_location.h"
+
+class ScalarField : public PacketField {
+ public:
+  ScalarField(std::string name, int size, ParseLocation loc);
+
+  virtual PacketField::Type GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetType() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual bool GenBuilderParameter(std::ostream& s) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream& s) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+ private:
+  int size_;
+};
diff --git a/gd/packet/parser/fields/size_field.cc b/gd/packet/parser/fields/size_field.cc
new file mode 100644
index 0000000..bbb6a73
--- /dev/null
+++ b/gd/packet/parser/fields/size_field.cc
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2019 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 "fields/size_field.h"
+#include "util.h"
+
+SizeField::SizeField(std::string name, int size, bool is_count, ParseLocation loc)
+    : PacketField(loc, name + (is_count ? "_count" : "_size")), size_(size), is_count_(is_count),
+      sized_field_name_(name) {}
+
+PacketField::Type SizeField::GetFieldType() const {
+  return (is_count_ ? PacketField::Type::COUNT : PacketField::Type::SIZE);
+}
+
+Size SizeField::GetSize() const {
+  return size_;
+}
+
+std::string SizeField::GetType() const {
+  return util::GetTypeForSize(size_);
+}
+
+void SizeField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const {
+  s << "protected:";
+  s << GetType();
+  s << " Get" << util::UnderscoreToCamelCase(GetName()) << "() const {";
+  s << "ASSERT(was_validated_);";
+
+  // Write the Getter Function Body
+  int num_leading_bits = 0;
+
+  // Handle if to start the iterator at begin or end.
+  if (!start_offset.empty()) {
+    // Default to start if available.
+    num_leading_bits = start_offset.bits() % 8;
+    s << "auto it = begin() + " << start_offset.bits() / 8 << " + (" << start_offset.dynamic_string() << ");";
+  } else if (!end_offset.empty()) {
+    int offset_from_end = end_offset.bits() + size_;
+    num_leading_bits = 8 - (offset_from_end % 8);
+    // Add 7 so it rounds up
+    int byte_offset = (7 + offset_from_end) / 8;
+    s << "auto it = end() - " << byte_offset << " - (" << end_offset.dynamic_string() << ");";
+
+  } else {
+    ERROR(this) << "Ambiguous offset for field.";
+  }
+
+  // We don't need any masking, just return the extracted value.
+  if (num_leading_bits == 0 && util::RoundSizeUp(size_) == size_) {
+    s << "return it.extract<" << GetType() << ">();";
+    s << "}\n";
+    s << "public:\n";
+    return;
+  }
+
+  // Extract the correct number of bytes. The return type could be different
+  // from the extract type if an earlier field causes the beginning of the
+  // current field to start in the middle of a byte.
+  std::string extract_type = util::GetTypeForSize(size_ + num_leading_bits);
+  s << "auto value = it.extract<" << extract_type << ">();";
+
+  // Right shift the result to remove leading bits.
+  if (num_leading_bits != 0) {
+    s << "value >>= " << num_leading_bits << ";";
+  }
+
+  // Mask the result if necessary.
+  if (util::RoundSizeUp(size_) != size_) {
+    uint64_t mask = 0;
+    for (int i = 0; i < size_; i++) {
+      mask <<= 1;
+      mask |= 1;
+    }
+    s << "value &= 0x" << std::hex << mask << std::dec << ";";
+  }
+
+  // Cast the result if necessary.
+  if (extract_type != util::GetTypeForSize(size_)) {
+    s << "return static_cast<" << GetType() << ">(value);";
+  } else {
+    s << "return value;";
+  }
+  s << "}\n";
+  s << "public:\n";
+}
+
+bool SizeField::GenBuilderParameter(std::ostream&) const {
+  // There is no builder parameter for a size field
+  return false;
+}
+
+bool SizeField::HasParameterValidator() const {
+  return false;
+}
+
+void SizeField::GenParameterValidator(std::ostream&) const {
+  // There is no builder parameter for a size field
+  // TODO: Check if the payload fits in the packet?
+}
+
+void SizeField::GenInserter(std::ostream&) const {
+  ERROR(this) << __func__ << ": This should not be called for size fields";
+}
+
+void SizeField::GenValidator(std::ostream&) const {
+  // Do nothing since the fixed size fields will be handled specially.
+}
+
+std::string SizeField::GetSizedFieldName() const {
+  return sized_field_name_;
+}
diff --git a/gd/packet/parser/fields/size_field.h b/gd/packet/parser/fields/size_field.h
new file mode 100644
index 0000000..61e8666
--- /dev/null
+++ b/gd/packet/parser/fields/size_field.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 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 "fields/packet_field.h"
+#include "parse_location.h"
+
+class SizeField : public PacketField {
+ public:
+  SizeField(std::string name, int size, bool is_count, ParseLocation loc);
+
+  std::string GetField() const;
+
+  virtual PacketField::Type GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetType() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual bool GenBuilderParameter(std::ostream&) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream&) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+  virtual std::string GetSizedFieldName() const;
+
+ private:
+  int size_;
+  bool is_count_;
+  std::string sized_field_name_;
+};
diff --git a/gd/packet/parser/language_l.ll b/gd/packet/parser/language_l.ll
new file mode 100644
index 0000000..1914ae9
--- /dev/null
+++ b/gd/packet/parser/language_l.ll
@@ -0,0 +1,119 @@
+%{
+
+#include <string>
+#include <map>
+#include <iostream>
+
+#include "declarations.h"
+#include "language_y.h"
+
+using token = yy::parser::token;
+
+#define YY_USER_ACTION yylloc->step(); yylloc->columns(yyleng);
+
+%}
+
+%option debug
+
+%option yylineno
+%option noyywrap
+%option nounput
+%option noinput
+%option reentrant
+%option bison-bridge
+%option bison-locations
+
+identifier [a-zA-Z][_a-zA-Z0-9]*
+size_modifier [+*/-][ +*/\-0-9]*
+intvalue (0|[1-9][0-9]*)
+hexvalue 0[x|X][0-9a-fA-F]+
+string_literal \".*\"
+
+%x COMMENT_STATE
+
+%%
+  /* NOTE:
+   * Rule ordering is important in order to establist priority. Some
+   * rules are a superset of other rules and will cause the sub rules to
+   * never match. Ex. Keywords must always go before identifiers, otherwise
+   * all keywords will be treated as an identifier.
+   */
+
+  /* Block Comment */
+"/*"                    { BEGIN(COMMENT_STATE); }
+<COMMENT_STATE>"*/"     { BEGIN(INITIAL); }
+<COMMENT_STATE>[\n]+    { yylloc->lines(yyleng); }
+<COMMENT_STATE>.        { /* do nothing */ }
+
+  /* Line Comment */
+"//"[^\r\n]*            { /* do nothing */ }
+
+  /* Begin reserved keyword definitions */
+  /* Fields */
+"_body_"                { return(token::BODY); }
+"_payload_"             { return(token::PAYLOAD); }
+"_size_"                { return(token::SIZE); }
+"_count_"               { return(token::COUNT); }
+"_fixed_"               { return(token::FIXED); }
+"_reserved_"            { return(token::RESERVED); }
+"_checksum_start_"      { return(token::CHECKSUM_START); }
+  /* Types */
+"checksum"              { return(token::CHECKSUM); }
+"custom_field"          { return(token::CUSTOM_FIELD); }
+"enum"                  { return(token::ENUM); }
+"group"                 { return(token::GROUP); }
+"packet"                { return(token::PACKET); }
+"little_endian_packets" {
+                          yylval->integer = 1;
+                          return token::IS_LITTLE_ENDIAN;
+                        }
+"big_endian_packets"    {
+                          yylval->integer = 0;
+                          return token::IS_LITTLE_ENDIAN;
+                        }
+
+  /* Begin identifier definitions */
+{string_literal}        {
+                          std::string with_quotes = std::string(yytext);
+                          yylval->string = new std::string(with_quotes.begin() + 1, with_quotes.end() - 1);
+                          return token::STRING;
+                        }
+
+{size_modifier}         {
+                          yylval->string = new std::string(yytext);
+                          return token::SIZE_MODIFIER;
+                        }
+
+{identifier}            {
+                          yylval->string = new std::string(yytext);
+                          return token::IDENTIFIER;
+                        }
+
+{intvalue}              {
+                          yylval->integer = std::stoi(std::string(yytext), nullptr, 10);
+                          return token::INTEGER;
+                        }
+
+{hexvalue}              {
+                          yylval->integer = std::stoi(std::string(yytext), nullptr, 16);
+                          return token::INTEGER;
+                        }
+
+  /* Begin token definitions */
+":"            { return(':'); }
+"{"            { return('{'); }
+"}"            { return('}'); }
+"["            { return('['); }
+"]"            { return(']'); }
+"("            { return('('); }
+")"            { return(')'); }
+"<"            { return('<'); }
+">"            { return('>'); }
+"="            { return('='); }
+","            { return(','); }
+
+(\n|\r\n)+     { yylloc->lines(yyleng); }
+[ \t\f\v]+     { /* Ignore all other whitespace */ }
+
+%%
+
diff --git a/gd/packet/parser/language_y.yy b/gd/packet/parser/language_y.yy
new file mode 100644
index 0000000..2add719
--- /dev/null
+++ b/gd/packet/parser/language_y.yy
@@ -0,0 +1,562 @@
+%{
+  #include <iostream>
+  #include <vector>
+  #include <list>
+  #include <map>
+
+  #include "declarations.h"
+  #include "logging.h"
+  #include "language_y.h"
+  #include "field_list.h"
+  #include "fields/all_fields.h"
+
+  extern int yylex(yy::parser::semantic_type*, yy::parser::location_type*, void *);
+
+  ParseLocation toParseLocation(yy::parser::location_type loc) {
+    return ParseLocation(loc.begin.line);
+  }
+  #define LOC toParseLocation(yylloc)
+%}
+
+%parse-param { void* scanner }
+%parse-param { Declarations* decls }
+%lex-param { void* scanner }
+
+%pure-parser
+%glr-parser
+%skeleton "glr.cc"
+
+%expect-rr 0
+
+%debug
+%error-verbose
+%verbose
+
+%union {
+  int integer;
+  std::string* string;
+
+  EnumDef* enum_definition;
+  std::map<int, std::string>* enumeration_values;
+  std::pair<int, std::string>* enumeration_value;
+
+  PacketDef* packet_definition_value;
+  FieldList* packet_field_definitions;
+  PacketField* packet_field_type;
+
+  std::map<std::string, std::variant<int64_t, std::string>>* constraint_list_t;
+  std::pair<std::string, std::variant<int64_t, std::string>>* constraint_t;
+}
+
+%token <integer> INTEGER
+%token <integer> IS_LITTLE_ENDIAN
+%token <string> IDENTIFIER
+%token <string> SIZE_MODIFIER
+%token <string> STRING
+
+%token ENUM "enum"
+%token PACKET "packet"
+%token PAYLOAD "payload"
+%token BODY "body"
+%token SIZE "size"
+%token COUNT "count"
+%token FIXED "fixed"
+%token RESERVED "reserved"
+%token GROUP "group"
+%token CUSTOM_FIELD "custom_field"
+%token CHECKSUM "checksum"
+%token CHECKSUM_START "checksum_start"
+
+%type<enum_definition> enum_definition
+%type<enumeration_values> enumeration_list
+%type<enumeration_value> enumeration
+
+%type<packet_definition_value> packet_definition;
+%type<packet_field_definitions> field_definition_list;
+%type<packet_field_type> field_definition;
+%type<packet_field_type> group_field_definition;
+%type<packet_field_type> type_def_field_definition;
+%type<packet_field_type> scalar_field_definition;
+%type<packet_field_type> checksum_start_field_definition;
+%type<packet_field_type> size_field_definition;
+%type<packet_field_type> payload_field_definition;
+%type<packet_field_type> body_field_definition;
+%type<packet_field_type> fixed_field_definition;
+%type<packet_field_type> reserved_field_definition;
+
+%type<constraint_list_t> constraint_list;
+%type<constraint_t> constraint;
+%destructor { std::cout << "DESTROYING STRING " << *$$ << "\n"; delete $$; } IDENTIFIER STRING SIZE_MODIFIER
+
+%%
+
+file
+  : IS_LITTLE_ENDIAN declarations
+  {
+    decls->is_little_endian = ($1 == 1);
+    if (decls->is_little_endian) {
+      DEBUG() << "LITTLE ENDIAN ";
+    } else {
+      DEBUG() << "BIG ENDIAN ";
+    }
+  }
+
+declarations
+  : /* empty */
+  | declarations declaration
+
+declaration
+  : enum_definition
+    {
+      std::cerr << "FOUND ENUM\n\n";
+      decls->AddTypeDef($1->name_, $1);
+    }
+  | packet_definition
+    {
+      std::cerr << "FOUND PACKET\n\n";
+      decls->AddPacketDef($1->name_, std::move(*$1));
+      delete $1;
+    }
+  | group_definition
+    {
+      // All actions are handled in group_definition
+    }
+  | checksum_definition
+    {
+      // All actions are handled in checksum_definition
+    }
+  | custom_field_definition
+    {
+      // All actions are handled in custom_field_definition
+    }
+
+enum_definition
+  : ENUM IDENTIFIER ':' INTEGER '{' enumeration_list ',' '}'
+    {
+      std::cerr << "Enum Declared: name=" << *$2
+                << " size=" << $4 << "\n";
+
+      $$ = new EnumDef(std::move(*$2), $4);
+      for (const auto& e : *$6) {
+        $$->AddEntry(e.second, e.first);
+      }
+      delete $2;
+      delete $6;
+    }
+
+enumeration_list
+  : enumeration
+    {
+      std::cerr << "Enumerator with comma\n";
+      $$ = new std::map<int, std::string>();
+      $$->insert(std::move(*$1));
+      delete $1;
+    }
+  | enumeration_list ',' enumeration
+    {
+      std::cerr << "Enumerator with list\n";
+      $$ = $1;
+      $$->insert(std::move(*$3));
+      delete $3;
+    }
+
+enumeration
+  : IDENTIFIER '=' INTEGER
+    {
+      std::cerr << "Enumerator: name=" << *$1
+                << " value=" << $3 << "\n";
+      $$ = new std::pair($3, std::move(*$1));
+      delete $1;
+    }
+
+group_definition
+  : GROUP IDENTIFIER '{' field_definition_list '}'
+    {
+      decls->AddGroupDef(*$2, $4);
+      delete $2;
+    }
+
+checksum_definition
+  : CHECKSUM IDENTIFIER ':' INTEGER STRING
+    {
+      std::cerr << "Checksum field defined\n";
+      decls->AddTypeDef(*$2, new ChecksumDef(*$2, *$5, $4));
+      delete $2;
+      delete $5;
+    }
+
+custom_field_definition
+  : CUSTOM_FIELD IDENTIFIER ':' INTEGER STRING
+    {
+      decls->AddTypeDef(*$2, new CustomFieldDef(*$2, *$5, $4));
+      delete $2;
+      delete $5;
+    }
+
+packet_definition
+  : PACKET IDENTIFIER '{' field_definition_list '}'  /* Packet with no parent */
+    {
+      auto&& packet_name = *$2;
+      auto&& field_definition_list = *$4;
+
+      DEBUG() << "Packet " << packet_name << " with no parent";
+      DEBUG() << "PACKET FIELD LIST SIZE: " << field_definition_list.size();
+      auto packet_definition = new PacketDef(std::move(packet_name), std::move(field_definition_list));
+      packet_definition->AssignSizeFields();
+
+      $$ = packet_definition;
+      delete $2;
+      delete $4;
+    }
+  | PACKET IDENTIFIER ':' IDENTIFIER '{' field_definition_list '}'
+    {
+      auto&& packet_name = *$2;
+      auto&& parent_packet_name = *$4;
+      auto&& field_definition_list = *$6;
+
+      DEBUG() << "Packet " << packet_name << " with parent " << parent_packet_name << "\n";
+      DEBUG() << "PACKET FIELD LIST SIZE: " << field_definition_list.size() << "\n";
+
+      auto parent_packet = decls->GetPacketDef(parent_packet_name);
+      if (parent_packet == nullptr) {
+        ERRORLOC(LOC) << "Could not find packet " << parent_packet_name
+                  << " used as parent for " << packet_name;
+      }
+
+      auto packet_definition = new PacketDef(std::move(packet_name), std::move(field_definition_list), parent_packet);
+      packet_definition->AssignSizeFields();
+
+      $$ = packet_definition;
+      delete $2;
+      delete $4;
+      delete $6;
+    }
+  | PACKET IDENTIFIER ':' IDENTIFIER '(' constraint_list ')' '{' field_definition_list '}'
+    {
+      auto&& packet_name = *$2;
+      auto&& parent_packet_name = *$4;
+      auto&& constraints = *$6;
+      auto&& field_definition_list = *$9;
+
+      DEBUG() << "Packet " << packet_name << " with parent " << parent_packet_name << "\n";
+      DEBUG() << "PACKET FIELD LIST SIZE: " << field_definition_list.size() << "\n";
+      DEBUG() << "CONSTRAINT LIST SIZE: " << constraints.size() << "\n";
+
+      auto parent_packet = decls->GetPacketDef(parent_packet_name);
+      if (parent_packet == nullptr) {
+        ERRORLOC(LOC) << "Could not find packet " << parent_packet_name
+                  << " used as parent for " << packet_name;
+      }
+
+      auto packet_definition = new PacketDef(std::move(packet_name), std::move(field_definition_list), parent_packet);
+      packet_definition->AssignSizeFields();
+
+      for (const auto& constraint : constraints) {
+        const auto& constraint_name = constraint.first;
+        const auto& constraint_value = constraint.second;
+        DEBUG() << "Parent constraint on " << constraint_name;
+        packet_definition->AddParentConstraint(constraint_name, constraint_value);
+      }
+
+      $$ = packet_definition;
+
+      delete $2;
+      delete $4;
+      delete $6;
+      delete $9;
+    }
+
+field_definition_list
+  : /* empty */
+    {
+      std::cerr << "Empty Field definition\n";
+      $$ = new FieldList();
+    }
+  | field_definition
+    {
+      std::cerr << "Field definition\n";
+      $$ = new FieldList();
+
+      if ($1->GetFieldType() == PacketField::Type::GROUP) {
+        auto group_fields = static_cast<GroupField*>($1)->GetFields();
+	FieldList reversed_fields(group_fields->rbegin(), group_fields->rend());
+        for (auto& field : reversed_fields) {
+          $$->PrependField(field);
+        }
+	delete $1;
+        break;
+      }
+
+      $$->PrependField($1);
+    }
+  | field_definition ',' field_definition_list
+    {
+      std::cerr << "Field definition with list\n";
+      $$ = $3;
+
+      if ($1->GetFieldType() == PacketField::Type::GROUP) {
+        auto group_fields = static_cast<GroupField*>($1)->GetFields();
+	FieldList reversed_fields(group_fields->rbegin(), group_fields->rend());
+        for (auto& field : reversed_fields) {
+          $$->PrependField(field);
+        }
+	delete $1;
+        break;
+      }
+
+      $$->PrependField($1);
+    }
+
+field_definition
+  : group_field_definition
+    {
+      DEBUG() << "Group Field";
+      $$ = $1;
+    }
+  | type_def_field_definition
+    {
+      std::cerr << "Field with a pre-defined type\n";
+      $$ = $1;
+    }
+  | scalar_field_definition
+    {
+      std::cerr << "Scalar field\n";
+      $$ = $1;
+    }
+  | checksum_start_field_definition
+    {
+      std::cerr << "Checksum start field\n";
+      $$ = $1;
+    }
+  | size_field_definition
+    {
+      std::cerr << "Size field\n";
+      $$ = $1;
+    }
+  | body_field_definition
+    {
+      std::cerr << "Body field\n";
+      $$ = $1;
+    }
+  | payload_field_definition
+    {
+      std::cerr << "Payload field\n";
+      $$ = $1;
+    }
+  | fixed_field_definition
+    {
+      std::cerr << "Fixed field\n";
+      $$ = $1;
+    }
+  | reserved_field_definition
+    {
+      std::cerr << "Reserved field\n";
+      $$ = $1;
+    }
+
+group_field_definition
+  : IDENTIFIER
+    {
+      auto group = decls->GetGroupDef(*$1);
+      if (group == nullptr) {
+        ERRORLOC(LOC) << "Could not find group with name " << *$1;
+      }
+
+      std::list<PacketField*>* expanded_fields;
+      expanded_fields = new std::list<PacketField*>(group->begin(), group->end());
+      $$ = new GroupField(LOC, expanded_fields);
+      delete $1;
+    }
+  | IDENTIFIER '{' constraint_list '}'
+    {
+      std::cerr << "Group with fixed field(s) " << *$1 << "\n";
+      auto group = decls->GetGroupDef(*$1);
+      if (group == nullptr) {
+        ERRORLOC(LOC) << "Could not find group with name " << *$1;
+      }
+
+      std::list<PacketField*>* expanded_fields = new std::list<PacketField*>();
+      for (const auto field : *group) {
+        const auto constraint = $3->find(field->GetName());
+        if (constraint != $3->end()) {
+          if (field->GetFieldType() == PacketField::Type::SCALAR) {
+            std::cerr << "Fixing group scalar value\n";
+            expanded_fields->push_back(new FixedField(field->GetSize().bits(), std::get<int64_t>(constraint->second), LOC));
+          } else if (field->GetFieldType() == PacketField::Type::ENUM) {
+            std::cerr << "Fixing group enum value\n";
+
+            auto type_def = decls->GetTypeDef(field->GetType());
+            EnumDef* enum_def = (type_def->GetDefinitionType() == TypeDef::Type::ENUM ? (EnumDef*)type_def : nullptr);
+            if (enum_def == nullptr) {
+              ERRORLOC(LOC) << "No enum found of type " << field->GetType();
+            }
+            if (!enum_def->HasEntry(std::get<std::string>(constraint->second))) {
+              ERRORLOC(LOC) << "Enum " << field->GetType() << " has no enumeration " << std::get<std::string>(constraint->second);
+            }
+
+            expanded_fields->push_back(new FixedField(enum_def, std::get<std::string>(constraint->second), LOC));
+          } else {
+            ERRORLOC(LOC) << "Unimplemented constraint of type " << field->GetType();
+          }
+          $3->erase(constraint);
+        } else {
+          expanded_fields->push_back(field);
+        }
+      }
+      if ($3->size() > 0) {
+        ERRORLOC(LOC) << "Could not find member " << $3->begin()->first << " in group " << *$1;
+      }
+
+      $$ = new GroupField(LOC, expanded_fields);
+      delete $1;
+      delete $3;
+    }
+
+constraint_list
+  : constraint ',' constraint_list
+    {
+      std::cerr << "Group field value list\n";
+      $3->insert(*$1);
+      $$ = $3;
+      delete($1);
+    }
+  | constraint
+    {
+      std::cerr << "Group field value\n";
+      $$ = new std::map<std::string, std::variant<int64_t, std::string>>();
+      $$->insert(*$1);
+      delete($1);
+    }
+
+constraint
+  : IDENTIFIER '=' INTEGER
+    {
+      std::cerr << "Group with a fixed integer value=" << $1 << " value=" << $3 << "\n";
+
+      $$ = new std::pair(*$1, std::variant<int64_t,std::string>($3));
+      delete $1;
+    }
+  | IDENTIFIER '=' IDENTIFIER
+    {
+      DEBUG() << "Group with a fixed enum field value=" << *$3 << " enum=" << *$1;
+
+      $$ = new std::pair(*$1, std::variant<int64_t,std::string>(*$3));
+      delete $1;
+      delete $3;
+    }
+
+type_def_field_definition
+  : IDENTIFIER ':' IDENTIFIER
+    {
+      std::cerr << "Predefined type field " << *$1 << " : " << *$3 << "\n";
+      if (auto type_def = decls->GetTypeDef(*$3)) {
+        $$ = type_def->GetNewField(*$1, LOC);
+      } else {
+        ERRORLOC(LOC) << "No type with this name\n";
+      }
+      delete $1;
+      delete $3;
+    }
+
+scalar_field_definition
+  : IDENTIFIER ':' INTEGER
+    {
+      std::cerr << "Scalar field " << *$1 << " : " << $3 << "\n";
+      $$ = new ScalarField(*$1, $3, LOC);
+      delete $1;
+    }
+
+body_field_definition
+  : BODY
+    {
+      std::cerr << "Body field\n";
+      $$ = new BodyField(LOC);
+    }
+
+payload_field_definition
+  : PAYLOAD ':' '[' SIZE_MODIFIER ']'
+    {
+      std::cerr << "Payload field with modifier " << *$4 << "\n";
+      $$ = new PayloadField(*$4, LOC);
+      delete $4;
+    }
+  | PAYLOAD ':' '[' INTEGER ']'
+    {
+      ERRORLOC(LOC) << "Payload fields can only be dynamically sized.";
+    }
+  | PAYLOAD
+    {
+      std::cerr << "Payload field\n";
+      $$ = new PayloadField("", LOC);
+    }
+
+checksum_start_field_definition
+  : CHECKSUM_START '(' IDENTIFIER ')'
+    {
+      std::cerr << "ChecksumStart field defined\n";
+      $$ = new ChecksumStartField(*$3, LOC);
+      delete $3;
+    }
+
+size_field_definition
+  : SIZE '(' IDENTIFIER ')' ':' INTEGER
+    {
+      std::cerr << "Size field defined\n";
+      $$ = new SizeField(*$3, $6, false, LOC);
+      delete $3;
+    }
+  | SIZE '(' PAYLOAD ')' ':' INTEGER
+    {
+      std::cerr << "Size for payload defined\n";
+      $$ = new SizeField("Payload", $6, false, LOC);
+    }
+  | COUNT '(' IDENTIFIER ')' ':' INTEGER
+    {
+      std::cerr << "Count field defined\n";
+      $$ = new SizeField(*$3, $6, true, LOC);
+      delete $3;
+    }
+  | COUNT '(' PAYLOAD ')' ':' INTEGER
+    {
+      ERRORLOC(LOC) << "Can not use count to describe payload fields.";
+    }
+
+fixed_field_definition
+  : FIXED '=' INTEGER ':' INTEGER
+    {
+      std::cerr << "Fixed field defined value=" << $3 << " size=" << $5 << "\n";
+      $$ = new FixedField($5, $3, LOC);
+    }
+  | FIXED '=' IDENTIFIER ':' IDENTIFIER
+    {
+      DEBUG() << "Fixed enum field defined value=" << *$3 << " enum=" << *$5;
+      auto type_def = decls->GetTypeDef(*$5);
+      if (type_def != nullptr) {
+        EnumDef* enum_def = (type_def->GetDefinitionType() == TypeDef::Type::ENUM ? (EnumDef*)type_def : nullptr);
+        if (!enum_def->HasEntry(*$3)) {
+          ERRORLOC(LOC) << "Previously defined enum " << enum_def->GetTypeName() << " has no entry for " << *$3;
+        }
+
+        $$ = new FixedField(enum_def, *$3, LOC);
+      } else {
+        ERRORLOC(LOC) << "No enum found with name " << *$5;
+      }
+
+      delete $3;
+      delete $5;
+    }
+
+reserved_field_definition
+  : RESERVED ':' INTEGER
+    {
+      std::cerr << "Reserved field of size=" << $3 << "\n";
+      $$ = new ReservedField($3, LOC);
+    }
+
+%%
+
+
+void yy::parser::error(const yy::parser::location_type& loc, const std::string& error) {
+  std::cerr << error << " at location " << loc << "\n";
+  abort();
+}
diff --git a/gd/packet/parser/logging.h b/gd/packet/parser/logging.h
new file mode 100644
index 0000000..8d9e467
--- /dev/null
+++ b/gd/packet/parser/logging.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2019 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 <initializer_list>
+#include <iostream>
+#include <sstream>
+#include <vector>
+
+#include "parse_location.h"
+
+class Loggable {
+ public:
+  virtual ~Loggable() = default;
+  virtual std::string GetDebugName() const = 0;
+  virtual ParseLocation GetLocation() const = 0;
+};
+
+class LogMessage {
+ public:
+  LogMessage(ParseLocation loc, std::initializer_list<const Loggable*> tokens)
+      : debug_(false), loc_(loc), tokens_(tokens) {
+    Init();
+  }
+
+  LogMessage(bool debug, std::initializer_list<const Loggable*> tokens) : debug_(debug), tokens_(tokens) {
+    Init();
+  }
+
+  void Init() {
+    if (loc_.GetLine() != -1) {
+      stream_ << "\033[1mLine " << loc_.GetLine() << ": ";
+    }
+
+    if (!debug_) {
+      stream_ << "\033[1;31m";
+      stream_ << "ERROR: ";
+      stream_ << "\033[0m";
+    } else {
+      stream_ << "\033[1;m";
+      stream_ << "DEBUG: ";
+      stream_ << "\033[0m";
+    }
+  }
+
+  ~LogMessage() {
+    std::cerr << stream_.str() << "\n";
+    for (const auto& token : tokens_) {
+      // Bold line number
+      std::cerr << "\033[1m";
+      std::cerr << "  Line " << token->GetLocation().GetLine() << ": ";
+      std::cerr << "\033[0m";
+      std::cerr << token->GetDebugName() << "\n";
+    }
+
+    if (!debug_) {
+      abort();
+    }
+  }
+
+  std::ostream& stream() {
+    return stream_;
+  }
+
+ private:
+  std::ostringstream stream_;
+  bool debug_;
+  ParseLocation loc_;
+  std::vector<const Loggable*> tokens_;
+};
+
+// Error Log stream. Aborts the program after the message is printed.
+// The arguments to the macro is a list of Loggable objects that are printed when the error is printed.
+#define ERROR(...) LogMessage(false, {__VA_ARGS__}).stream()
+
+// ParseLocation error log, the first argument is a location.
+#define ERRORLOC(_1, ...) LogMessage(_1, {__VA_ARGS__}).stream()
+
+#define DEBUG(...) LogMessage(true, {__VA_ARGS__}).stream()
diff --git a/gd/packet/parser/main.cc b/gd/packet/parser/main.cc
new file mode 100644
index 0000000..e3fe16a
--- /dev/null
+++ b/gd/packet/parser/main.cc
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2019 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 <errno.h>
+#include <unistd.h>
+#include <cstdio>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <queue>
+#include <regex>
+#include <sstream>
+#include <vector>
+
+#include "declarations.h"
+
+#include "language_y.h"
+
+void yylex_init(void**);
+void yylex_destroy(void*);
+void yyset_debug(int, void*);
+void yyset_in(FILE*, void*);
+
+namespace {
+
+const std::string kBluetoothTopNamespace = "bluetooth";
+
+void parse_namespace(std::filesystem::path input_file_relative_path, std::vector<std::string>& token) {
+  std::filesystem::path gen_namespace = kBluetoothTopNamespace / input_file_relative_path;
+  std::string gen_namespace_str = gen_namespace;
+  std::regex path_tokenizer("/");
+  auto it = std::sregex_token_iterator(gen_namespace_str.cbegin(), gen_namespace_str.cend(), path_tokenizer, -1);
+  std::sregex_token_iterator it_end;
+  for (; it != it_end; ++it) {
+    token.push_back(it->str());
+  }
+}
+
+void generate_namespace_open(const std::vector<std::string>& token, std::ostream& output) {
+  for (auto it = token.begin(); it != token.end(); ++it) {
+    output << "namespace " << *it << " {" << std::endl;
+  }
+}
+
+void generate_namespace_close(const std::vector<std::string>& token, std::ostream& output) {
+  for (auto it = token.rbegin(); it != token.rend(); ++it) {
+    output << "}  //namespace " << *it << std::endl;
+  }
+}
+
+bool parse_one_file(std::filesystem::path input_file, std::filesystem::path include_dir,
+                    std::filesystem::path out_dir) {
+  auto gen_relative_path = input_file.lexically_relative(include_dir).parent_path();
+
+  auto input_filename = input_file.filename().string().substr(0, input_file.filename().string().find(".pdl"));
+  auto gen_path = out_dir / gen_relative_path;
+
+  std::filesystem::create_directories(gen_path);
+
+  auto gen_file = gen_path / (input_filename + ".h");
+
+  void* scanner;
+  yylex_init(&scanner);
+
+  FILE* in_file = fopen(input_file.string().c_str(), "r");
+  if (in_file == nullptr) {
+    std::cerr << "can't open " << input_file << ": " << strerror(errno) << std::endl;
+    return false;
+  }
+
+  yyset_in(in_file, scanner);
+
+  std::ofstream out_file;
+  out_file.open(gen_file);
+  if (!out_file.is_open()) {
+    std::cerr << "can't open " << gen_file << std::endl;
+    return false;
+  }
+
+  Declarations decls;
+  int ret = yy::parser(scanner, &decls).parse();
+
+  yylex_destroy(scanner);
+
+  if (ret != 0) {
+    std::cerr << "yylex parsing failed: returned " << ret << std::endl;
+    return false;
+  }
+
+  out_file << "\n\n";
+  out_file << "#pragma once\n";
+  out_file << "\n\n";
+  out_file << "#include <stdint.h>\n";
+  out_file << "#include <string>\n";
+  out_file << "#include <functional>\n";
+  out_file << "\n\n";
+  out_file << "#include \"os/log.h\"\n";
+  out_file << "#include \"packet/base_packet_builder.h\"\n";
+  out_file << "#include \"packet/bit_inserter.h\"\n";
+  out_file << "#include \"packet/packet_builder.h\"\n";
+  out_file << "#include \"packet/packet_view.h\"\n";
+  out_file << "#include \"packet/parser/checksum_type_checker.h\"\n";
+  out_file << "\n\n";
+
+  for (const auto& c : decls.type_defs_queue_) {
+    c.second->GenInclude(out_file);
+  }
+  out_file << "\n\n";
+
+  std::vector<std::string> namespace_list;
+  parse_namespace(gen_relative_path, namespace_list);
+  generate_namespace_open(namespace_list, out_file);
+  out_file << "\n\n";
+
+  for (const auto& c : decls.type_defs_queue_) {
+    c.second->GenUsing(out_file);
+  }
+  out_file << "\n\n";
+
+  out_file << "using ::bluetooth::packet::BasePacketBuilder;";
+  out_file << "using ::bluetooth::packet::BitInserter;";
+  out_file << "using ::bluetooth::packet::kLittleEndian;";
+  out_file << "using ::bluetooth::packet::PacketBuilder;";
+  out_file << "using ::bluetooth::packet::PacketView;";
+  out_file << "using ::bluetooth::packet::parser::ChecksumTypeChecker;";
+  out_file << "\n\n";
+
+  for (const auto& e : decls.type_defs_queue_) {
+    if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
+      EnumGen gen(*(EnumDef*)e.second);
+      gen.GenDefinition(out_file);
+      out_file << "\n\n";
+    }
+  }
+  for (const auto& e : decls.type_defs_queue_) {
+    if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
+      EnumGen gen(*(EnumDef*)e.second);
+      gen.GenLogging(out_file);
+      out_file << "\n\n";
+    }
+  }
+  for (const auto& ch : decls.type_defs_queue_) {
+    if (ch.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
+      ((ChecksumDef*)ch.second)->GenChecksumCheck(out_file);
+    }
+    out_file << "\n/* Done ChecksumChecks */\n";
+  }
+
+  for (size_t i = 0; i < decls.packet_defs_queue_.size(); i++) {
+    decls.packet_defs_queue_[i].second.SetEndianness(decls.is_little_endian);
+    decls.packet_defs_queue_[i].second.GenParserDefinition(out_file);
+    out_file << "\n\n";
+  }
+
+  for (const auto p : decls.packet_defs_queue_) {
+    p.second.GenBuilderDefinition(out_file);
+    out_file << "\n\n";
+  }
+
+  generate_namespace_close(namespace_list, out_file);
+
+  out_file.close();
+  fclose(in_file);
+
+  return true;
+}
+
+}  // namespace
+
+int main(int argc, const char** argv) {
+  std::filesystem::path out_dir;
+  std::filesystem::path include_dir;
+  std::queue<std::filesystem::path> input_files;
+  const std::string arg_out = "--out=";
+  const std::string arg_include = "--include=";
+
+  for (int i = 1; i < argc; i++) {
+    std::string arg = argv[i];
+    if (arg.find(arg_out) == 0) {
+      auto out_path = std::filesystem::path(arg.substr(arg_out.size()));
+      out_dir = std::filesystem::current_path() / std::filesystem::path(arg.substr(arg_out.size()));
+    } else if (arg.find(arg_include) == 0) {
+      auto include_path = std::filesystem::path(arg.substr(arg_out.size()));
+      include_dir = std::filesystem::current_path() / std::filesystem::path(arg.substr(arg_include.size()));
+    } else {
+      input_files.emplace(std::filesystem::current_path() / std::filesystem::path(arg));
+    }
+  }
+  if (out_dir == std::filesystem::path() || include_dir == std::filesystem::path()) {
+    std::cerr << "Usage: bt-packetgen --out=OUT --include=INCLUDE input_files..." << std::endl;
+    return 1;
+  }
+
+  while (!input_files.empty()) {
+    if (!parse_one_file(input_files.front(), include_dir, out_dir)) {
+      std::cerr << "Didn't parse " << input_files.front() << " correctly" << std::endl;
+      return 2;
+    }
+    input_files.pop();
+  }
+
+  return 0;
+}
diff --git a/gd/packet/parser/packet_def.cc b/gd/packet/parser/packet_def.cc
new file mode 100644
index 0000000..7173c3c
--- /dev/null
+++ b/gd/packet/parser/packet_def.cc
@@ -0,0 +1,756 @@
+/*
+ * Copyright 2019 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 "packet_def.h"
+
+#include <list>
+#include <set>
+
+#include "fields/all_fields.h"
+#include "util.h"
+
+PacketDef::PacketDef(std::string name, FieldList fields) : name_(name), fields_(fields), parent_(nullptr){};
+PacketDef::PacketDef(std::string name, FieldList fields, PacketDef* parent)
+    : name_(name), fields_(fields), parent_(parent){};
+
+void PacketDef::AddParentConstraint(std::string field_name, std::variant<int64_t, std::string> value) {
+  // NOTE: This could end up being very slow if there are a lot of constraints.
+  const auto& parent_params = parent_->GetParamList();
+  const auto& constrained_field = parent_params.GetField(field_name);
+  if (constrained_field == nullptr) {
+    ERROR() << "Attempting to constrain field " << field_name << " in parent packet " << parent_->name_
+            << ", but no such field exists.";
+  }
+
+  if (constrained_field->GetFieldType() == PacketField::Type::SCALAR) {
+    if (!std::holds_alternative<int64_t>(value)) {
+      ERROR(constrained_field) << "Attemting to constrain a scalar field using an enum value in packet "
+                               << parent_->name_ << ".";
+    }
+  } else if (constrained_field->GetFieldType() == PacketField::Type::ENUM) {
+    if (!std::holds_alternative<std::string>(value)) {
+      ERROR(constrained_field) << "Attemting to constrain an enum field using an scalar value in packet "
+                               << parent_->name_ << ".";
+    }
+    const auto& enum_def = static_cast<EnumField*>(constrained_field)->GetEnumDef();
+    if (!enum_def.HasEntry(std::get<std::string>(value))) {
+      ERROR(constrained_field) << "No matching enumeration \"" << std::get<std::string>(value)
+                               << "for constraint on enum in parent packet " << parent_->name_ << ".";
+    }
+
+    // For enums, we have to qualify the value using the enum type name.
+    value = enum_def.GetTypeName() + "::" + std::get<std::string>(value);
+  } else {
+    ERROR(constrained_field) << "Field in parent packet " << parent_->name_ << " is not viable for constraining.";
+  }
+
+  parent_constraints_.insert(std::pair(field_name, value));
+}
+
+// Assign all size fields to their corresponding variable length fields.
+// Will crash if
+//  - there aren't any fields that don't match up to a field.
+//  - the size field points to a fixed size field.
+//  - if the size field comes after the variable length field.
+void PacketDef::AssignSizeFields() {
+  for (const auto& field : fields_) {
+    DEBUG() << "field name: " << field->GetName();
+
+    if (field->GetFieldType() != PacketField::Type::SIZE && field->GetFieldType() != PacketField::Type::COUNT) {
+      continue;
+    }
+
+    const SizeField* size_field = nullptr;
+    size_field = static_cast<SizeField*>(field);
+    // Check to see if a corresponding field can be found.
+    const auto& var_len_field = fields_.GetField(size_field->GetSizedFieldName());
+    if (var_len_field == nullptr) {
+      ERROR(field) << "Could not find corresponding field for size/count field.";
+    }
+
+    // Do the ordering check to ensure the size field comes before the
+    // variable length field.
+    for (auto it = fields_.begin(); *it != size_field; it++) {
+      DEBUG() << "field name: " << (*it)->GetName();
+      if (*it == var_len_field) {
+        ERROR(var_len_field, size_field) << "Size/count field must come before the variable length field it describes.";
+      }
+    }
+
+    if (var_len_field->GetFieldType() == PacketField::Type::PAYLOAD) {
+      const auto& payload_field = static_cast<PayloadField*>(var_len_field);
+      payload_field->SetSizeField(size_field);
+      continue;
+    }
+
+    // If we've reached this point then the field wasn't a variable length field.
+    // Check to see if the field is a variable length field
+    std::cerr << "Can not use size/count in reference to a fixed size field.\n";
+    abort();
+  }
+}
+
+void PacketDef::SetEndianness(bool is_little_endian) {
+  is_little_endian_ = is_little_endian;
+}
+
+// Get the size for the packet. You scan specify without_payload in order
+// to exclude payload fields as child packets will be overriding it.
+Size PacketDef::GetSize(bool without_payload) const {
+  auto size = Size();
+
+  for (const auto& field : fields_) {
+    if (without_payload && field->GetFieldType() == PacketField::Type::PAYLOAD) {
+      continue;
+    }
+
+    size += field->GetSize();
+  }
+
+  if (parent_ != nullptr) {
+    size += parent_->GetSize(true);
+  }
+
+  return size;
+}
+
+// Get the offset until the field is reached, if there is no field
+// returns an empty Size. from_end requests the offset to the field
+// starting from the end() iterator. If there is a field with an unknown
+// size along the traversal, then an empty size is returned.
+Size PacketDef::GetOffsetForField(std::string field_name, bool from_end) const {
+  // Check first if the field exists.
+  if (fields_.GetField(field_name) == nullptr) {
+    if (field_name != "Payload" && field_name != "Body") {
+      ERROR() << "Can't find a field offset for nonexistent field named: " << field_name;
+    } else {
+      return Size();
+    }
+  }
+
+  // We have to use a generic lambda to conditionally change iteration direction
+  // due to iterator and reverse_iterator being different types.
+  auto size_lambda = [&](auto from, auto to) -> Size {
+    auto size = Size(0);
+    for (auto it = from; it != to; it++) {
+      // We've reached the field, end the loop.
+      if ((*it)->GetName() == field_name) break;
+      const auto& field = *it;
+      // If there was a field that wasn't the payload with an unknown size,
+      // return an empty Size.
+      if (field->GetSize().empty()) {
+        return Size();
+      }
+      size += field->GetSize();
+    }
+    return size;
+  };
+
+  // Change iteration direction based on from_end.
+  auto size = Size();
+  if (from_end)
+    size = size_lambda(fields_.rbegin(), fields_.rend());
+  else
+    size = size_lambda(fields_.begin(), fields_.end());
+  if (size.empty()) return Size();
+
+  // For parent packets we need the offset until a payload or body field.
+  if (parent_ != nullptr) {
+    auto parent_payload_offset = parent_->GetOffsetForField("Payload", from_end);
+    if (!parent_payload_offset.empty()) {
+      size += parent_payload_offset;
+    } else {
+      parent_payload_offset = parent_->GetOffsetForField("Body", from_end);
+      if (!parent_payload_offset.empty()) {
+        size += parent_payload_offset;
+      } else {
+        return Size();
+      }
+    }
+  }
+
+  return size;
+}
+
+void PacketDef::GenParserDefinition(std::ostream& s) const {
+  s << "class " << name_ << "View";
+  if (parent_ != nullptr) {
+    s << " : public " << parent_->name_ << "View {";
+  } else {
+    s << " : public PacketView<" << (is_little_endian_ ? "" : "!") << "kLittleEndian> {";
+  }
+  s << " public:";
+
+  // Specialize function
+  if (parent_ != nullptr) {
+    s << "static " << name_ << "View Create(" << parent_->name_ << "View parent)";
+    s << "{ return " << name_ << "View(parent); }";
+  } else {
+    s << "static " << name_ << "View Create(PacketView<" << (is_little_endian_ ? "" : "!") << "kLittleEndian> packet) ";
+    s << "{ return " << name_ << "View(packet); }";
+  }
+
+  std::set<PacketField::Type> fixed_types = {
+      PacketField::Type::FIXED_SCALAR,
+      PacketField::Type::FIXED_ENUM,
+  };
+
+  // Print all of the public fields which are all the fields minus the fixed fields.
+  const auto& public_fields = fields_.GetFieldsWithoutTypes(fixed_types);
+  bool has_fixed_fields = public_fields.size() != fields_.size();
+  for (const auto& field : public_fields) {
+    GenParserFieldGetter(s, field);
+    s << "\n";
+  }
+  GenValidator(s);
+  s << "\n";
+
+  s << " protected:\n";
+  // Constructor from a View
+  if (parent_ != nullptr) {
+    s << name_ << "View(" << parent_->name_ << "View parent)";
+    s << " : " << parent_->name_ << "View(parent) { was_validated_ = false; }";
+  } else {
+    s << name_ << "View(PacketView<" << (is_little_endian_ ? "" : "!") << "kLittleEndian> packet) ";
+    s << " : PacketView<" << (is_little_endian_ ? "" : "!") << "kLittleEndian>(packet) { was_validated_ = false;}";
+  }
+
+  // Print the private fields which are the fixed fields.
+  if (has_fixed_fields) {
+    const auto& private_fields = fields_.GetFieldsWithTypes(fixed_types);
+    s << " private:\n";
+    for (const auto& field : private_fields) {
+      GenParserFieldGetter(s, field);
+      s << "\n";
+    }
+  }
+  s << "};\n";
+}
+
+void PacketDef::GenParserFieldGetter(std::ostream& s, const PacketField* field) const {
+  // Start field offset
+  auto start_field_offset = GetOffsetForField(field->GetName(), false);
+  auto end_field_offset = GetOffsetForField(field->GetName(), true);
+
+  if (start_field_offset.empty() && end_field_offset.empty()) {
+    std::cerr << "Field location for " << field->GetName() << " is ambiguous, "
+              << "no method exists to determine field location from begin() or end().\n";
+    abort();
+  }
+
+  field->GenGetter(s, start_field_offset, end_field_offset);
+}
+
+void PacketDef::GenSerialize(std::ostream& s) const {
+  auto header_fields = fields_.GetFieldsBeforePayloadOrBody();
+  auto footer_fields = fields_.GetFieldsAfterPayloadOrBody();
+
+  s << "protected:";
+  s << "void SerializeHeader(BitInserter&";
+  if (parent_ != nullptr || header_fields.size() != 0) {
+    s << " i ";
+  }
+  s << ") const {";
+
+  if (parent_ != nullptr) {
+    s << parent_->name_ << "Builder::SerializeHeader(i);";
+  }
+
+  for (const auto& field : header_fields) {
+    if (field->GetFieldType() == PacketField::Type::SIZE) {
+      const auto& field_name = ((SizeField*)field)->GetSizedFieldName();
+      const auto& sized_field = fields_.GetField(field_name);
+      if (sized_field == nullptr) {
+        ERROR(field) << __func__ << "Can't find sized field named " << field_name;
+      }
+      if (sized_field->GetFieldType() == PacketField::Type::PAYLOAD) {
+        s << "size_t payload_bytes = GetPayloadSize();";
+        std::string modifier = ((PayloadField*)sized_field)->size_modifier_;
+        if (modifier != "") {
+          s << "static_assert((" << modifier << ")%8 == 0, \"Modifiers must be byte-aligned\");";
+          s << "payload_bytes = payload_bytes + (" << modifier << ") / 8;";
+        }
+        s << "ASSERT(payload_bytes < (static_cast<size_t>(1) << " << field->GetSize().bits() << "));";
+        s << "insert(static_cast<" << field->GetType() << ">(payload_bytes), i," << field->GetSize().bits() << ");";
+      } else {
+        ERROR(field) << __func__ << "Unhandled sized field type for " << field_name;
+      }
+    } else if (field->GetFieldType() == PacketField::Type::CHECKSUM_START) {
+      const auto& field_name = ((ChecksumStartField*)field)->GetStartedFieldName();
+      const auto& started_field = fields_.GetField(field_name);
+      if (started_field == nullptr) {
+        ERROR(field) << __func__ << ": Can't find checksum field named " << field_name << "(" << field->GetName()
+                     << ")";
+      }
+      s << "auto shared_checksum_ptr = std::make_shared<" << started_field->GetType() << ">();";
+      s << started_field->GetType() << "::Initialize(*shared_checksum_ptr);";
+      s << "i.RegisterObserver(packet::ByteObserver(";
+      s << "[shared_checksum_ptr](uint8_t byte){" << started_field->GetType()
+        << "::AddByte(*shared_checksum_ptr, byte);},";
+      s << "[shared_checksum_ptr](){ return static_cast<uint64_t>(" << started_field->GetType()
+        << "::GetChecksum(*shared_checksum_ptr));}));";
+    } else {
+      field->GenInserter(s);
+    }
+  }
+  s << "}\n\n";
+
+  s << "void SerializeFooter(BitInserter&";
+  if (parent_ != nullptr || footer_fields.size() != 0) {
+    s << " i ";
+  }
+  s << ") const {";
+
+  for (const auto& field : footer_fields) {
+    field->GenInserter(s);
+  }
+  if (parent_ != nullptr) {
+    s << parent_->name_ << "Builder::SerializeFooter(i);";
+  }
+  s << "}\n\n";
+
+  s << "public:";
+  s << "virtual void Serialize(BitInserter& i) const override {";
+  s << "SerializeHeader(i);";
+  if (fields_.HasPayload()) {
+    s << "payload_->Serialize(i);";
+  }
+  s << "SerializeFooter(i);";
+
+  s << "}\n";
+}
+
+void PacketDef::GenBuilderSize(std::ostream& s) const {
+  auto header_fields = fields_.GetFieldsBeforePayloadOrBody();
+  auto footer_fields = fields_.GetFieldsAfterPayloadOrBody();
+
+  s << "protected:";
+  s << "size_t BitsOfHeader() const {";
+  s << "return ";
+
+  if (parent_ != nullptr) {
+    s << parent_->name_ << "Builder::BitsOfHeader() + ";
+  }
+
+  size_t header_bits = 0;
+  for (const auto& field : header_fields) {
+    header_bits += field->GetSize().bits();
+  }
+  s << header_bits << ";";
+
+  s << "}\n\n";
+
+  s << "size_t BitsOfFooter() const {";
+  s << "return ";
+  size_t footer_bits = 0;
+  for (const auto& field : footer_fields) {
+    footer_bits += field->GetSize().bits();
+  }
+
+  if (parent_ != nullptr) {
+    s << parent_->name_ << "Builder::BitsOfFooter() + ";
+  }
+  s << footer_bits << ";";
+  s << "}\n\n";
+
+  if (fields_.HasPayload()) {
+    s << "size_t GetPayloadSize() const {";
+    s << "if (payload_ != nullptr) {return payload_->size();}";
+    s << "else { return size() - (BitsOfHeader() + BitsOfFooter()) / 8;}";
+    s << ";}\n\n";
+  }
+
+  s << "public:";
+  s << "virtual size_t size() const override {";
+  s << "return (BitsOfHeader() / 8)";
+  if (fields_.HasPayload()) {
+    s << "+ payload_->size()";
+  }
+  s << " + (BitsOfFooter() / 8);";
+  s << "}\n";
+}
+
+void PacketDef::GenValidator(std::ostream& s) const {
+  // Get the static offset for all of our fields.
+  int bits_size = 0;
+  for (const auto& field : fields_) {
+    bits_size += field->GetSize().bits();
+  }
+
+  // Write the function declaration.
+  s << "virtual bool IsValid() " << (parent_ != nullptr ? " override" : "") << " {";
+  s << "if (was_validated_) { return true; } ";
+  s << "else { was_validated_ = true; was_validated_ = IsValid_(); return was_validated_; }";
+  s << "}";
+
+  s << "protected:";
+  s << "virtual bool IsValid_() const {";
+
+  // Offset by the parents known size. We know that any dynamic fields can
+  // already be called since the parent must have already been validated by
+  // this point.
+  auto parent_size = Size();
+  if (parent_ != nullptr) {
+    parent_size = parent_->GetSize(true);
+  }
+
+  s << "auto it = begin() + " << parent_size.bytes() << " + (" << parent_size.dynamic_string() << ");";
+
+  // Check if you can extract the static fields.
+  // At this point you know you can use the size getters without crashing
+  // as long as they follow the instruction that size fields cant come before
+  // their corrisponding variable length field.
+  s << "it += " << ((bits_size + 7) / 8) << " /* Total size of the fixed fields */;";
+  s << "if (it > end()) return false;";
+
+  // For any variable length fields, use their size check.
+  for (const auto& field : fields_) {
+    if (field->GetFieldType() == PacketField::Type::CHECKSUM_START) {
+      auto offset = GetOffsetForField(field->GetName(), false);
+      if (!offset.empty()) {
+        s << "size_t sum_index = " << offset.bytes() << " + (" << offset.dynamic_string() << ");";
+      } else {
+        offset = GetOffsetForField(field->GetName(), true);
+        if (offset.empty()) {
+          ERROR(field) << "Checksum Start Field offset can not be determined.";
+        }
+        s << "size_t sum_index = size() - " << offset.bytes() << " - (" << offset.dynamic_string() << ");";
+      }
+
+      const auto& field_name = ((ChecksumStartField*)field)->GetStartedFieldName();
+      const auto& started_field = fields_.GetField(field_name);
+      if (started_field == nullptr) {
+        ERROR(field) << __func__ << ": Can't find checksum field named " << field_name << "(" << field->GetName()
+                     << ")";
+      }
+      auto end_offset = GetOffsetForField(started_field->GetName(), false);
+      if (!end_offset.empty()) {
+        s << "size_t end_sum_index = " << end_offset.bytes() << " + (" << end_offset.dynamic_string() << ");";
+      } else {
+        end_offset = GetOffsetForField(started_field->GetName(), true);
+        if (end_offset.empty()) {
+          ERROR(started_field) << "Checksum Field end_offset can not be determined.";
+        }
+        s << "size_t end_sum_index = size() - " << started_field->GetSize().bytes() << " - " << end_offset.bytes()
+          << " - (" << end_offset.dynamic_string() << ");";
+      }
+      if (is_little_endian_) {
+        s << "auto checksum_view = GetLittleEndianSubview(sum_index, end_sum_index);";
+      } else {
+        s << "auto checksum_view = GetBigEndianSubview(sum_index, end_sum_index);";
+      }
+      s << started_field->GetType() << " checksum;";
+      s << started_field->GetType() << "::Initialize(checksum);";
+      s << "for (uint8_t byte : checksum_view) { ";
+      s << started_field->GetType() << "::AddByte(checksum, byte);}";
+      s << "if (" << started_field->GetType() << "::GetChecksum(checksum) != (begin() + end_sum_index).extract<"
+        << util::GetTypeForSize(started_field->GetSize().bits()) << ">()) { return false; }";
+
+      continue;
+    }
+
+    auto field_size = field->GetSize();
+    // Fixed size fields have already been handled.
+    if (!field_size.has_dynamic()) {
+      continue;
+    }
+
+    // Custom fields with dynamic size must have the offset for the field passed in as well
+    // as the end iterator so that they may ensure that they don't try to read past the end.
+    // Custom fields with fixed sizes will be handled in the static offset checking.
+    if (field->GetFieldType() == PacketField::Type::CUSTOM) {
+      const auto& custom_size_var = field->GetName() + "_size";
+
+      // Check if we can determine offset from begin(), otherwise error because by this point,
+      // the size of the custom field is unknown and can't be subtracted from end() to get the
+      // offset.
+      auto offset = GetOffsetForField(field->GetName(), false);
+      if (offset.empty()) {
+        ERROR(field) << "Custom Field offset can not be determined from begin().";
+      }
+
+      if (offset.bits() % 8 != 0) {
+        ERROR(field) << "Custom fields must be byte aligned.";
+      }
+
+      // Custom fields are special as their size field takes an argument.
+      s << "const auto& " << custom_size_var << " = " << field_size.dynamic_string();
+      s << "(begin() + " << offset.bytes() << " + (" << offset.dynamic_string() << "));";
+
+      s << "if (!" << custom_size_var << ".has_value()) { return false; }";
+      s << "it += *" << custom_size_var << ";";
+      s << "if (it > end()) return false;";
+      continue;
+    } else {
+      s << "it += " << field_size.dynamic_string() << ";";
+      s << "if (it > end()) return false;";
+    }
+  }
+
+  // Validate constraints after validating the size
+  if (parent_constraints_.size() > 0 && parent_ == nullptr) {
+    ERROR() << "Can't have a constraint on a NULL parent";
+  }
+
+  for (const auto& constraint : parent_constraints_) {
+    s << "if (Get" << util::UnderscoreToCamelCase(constraint.first) << "() != ";
+    const auto& field = parent_->GetParamList().GetField(constraint.first);
+    if (field->GetFieldType() == PacketField::Type::SCALAR) {
+      s << std::get<int64_t>(constraint.second);
+    } else {
+      s << std::get<std::string>(constraint.second);
+    }
+    s << ") return false;";
+  }
+
+  // Validate the packets fields last
+  for (const auto& field : fields_) {
+    field->GenValidator(s);
+    s << "\n";
+  }
+
+  s << "return true;";
+  s << "}\n";
+  if (parent_ == nullptr) {
+    s << "bool was_validated_{false};\n";
+  }
+}
+
+void PacketDef::GenBuilderDefinition(std::ostream& s) const {
+  s << "class " << name_ << "Builder";
+  if (parent_ != nullptr) {
+    s << " : public " << parent_->name_ << "Builder";
+  } else {
+    if (is_little_endian_) {
+      s << " : public PacketBuilder<kLittleEndian>";
+    } else {
+      s << " : public PacketBuilder<!kLittleEndian>";
+    }
+  }
+  s << " {";
+  s << " public:";
+  s << "  virtual ~" << name_ << "Builder()" << (parent_ != nullptr ? " override" : "") << " = default;";
+
+  if (!fields_.HasBody()) {
+    GenBuilderCreate(s);
+    s << "\n";
+  }
+
+  GenSerialize(s);
+  s << "\n";
+
+  GenBuilderSize(s);
+  s << "\n";
+
+  s << " protected:\n";
+  GenBuilderConstructor(s);
+  s << "\n";
+
+  GenBuilderParameterChecker(s);
+  s << "\n";
+
+  GenBuilderMembers(s);
+  s << "};\n";
+}
+
+FieldList PacketDef::GetParamList() const {
+  FieldList params;
+
+  std::set<PacketField::Type> param_types = {
+      PacketField::Type::SCALAR, PacketField::Type::ENUM,    PacketField::Type::CUSTOM,
+      PacketField::Type::BODY,   PacketField::Type::PAYLOAD,
+  };
+
+  if (parent_ != nullptr) {
+    auto parent_params = parent_->GetParamList().GetFieldsWithTypes(param_types);
+
+    // Do not include constrained fields in the params
+    for (const auto& field : parent_params) {
+      if (parent_constraints_.find(field->GetName()) == parent_constraints_.end()) {
+        params.AppendField(field);
+      }
+    }
+  }
+
+  // Add the parameters for this packet.
+  return params.Merge(fields_.GetFieldsWithTypes(param_types));
+}
+
+FieldList PacketDef::GetParametersToValidate() const {
+  FieldList params_to_validate;
+  for (const auto& field : GetParamList()) {
+    if (field->HasParameterValidator()) {
+      params_to_validate.AppendField(field);
+    }
+  }
+  return params_to_validate;
+}
+
+void PacketDef::GenBuilderCreate(std::ostream& s) const {
+  s << "static std::unique_ptr<" << name_ << "Builder> Create(";
+
+  auto params = GetParamList();
+  for (int i = 0; i < params.size(); i++) {
+    params[i]->GenBuilderParameter(s);
+    if (i != params.size() - 1) {
+      s << ", ";
+    }
+  }
+  s << ") {";
+
+  // Call the constructor
+  s << "auto builder = std::unique_ptr<" << name_ << "Builder>(new " << name_ << "Builder(";
+
+  params = params.GetFieldsWithoutTypes({
+      PacketField::Type::PAYLOAD,
+      PacketField::Type::BODY,
+  });
+  // Add the parameters.
+  for (int i = 0; i < params.size(); i++) {
+    s << params[i]->GetName();
+    if (i != params.size() - 1) {
+      s << ", ";
+    }
+  }
+
+  s << "));";
+  if (fields_.HasPayload()) {
+    s << "builder->payload_ = std::move(payload);";
+  }
+  s << "return builder;";
+  s << "}\n";
+}
+
+void PacketDef::GenBuilderParameterChecker(std::ostream& s) const {
+  FieldList params_to_validate = GetParametersToValidate();
+
+  // Skip writing this function if there is nothing to validate.
+  if (params_to_validate.size() == 0) {
+    return;
+  }
+
+  // Generate function arguments.
+  s << "void CheckParameterValues(";
+  for (int i = 0; i < params_to_validate.size(); i++) {
+    params_to_validate[i]->GenBuilderParameter(s);
+    if (i != params_to_validate.size() - 1) {
+      s << ", ";
+    }
+  }
+  s << ") {";
+
+  // Check the parameters.
+  for (const auto& field : params_to_validate) {
+    field->GenParameterValidator(s);
+  }
+  s << "}\n";
+}
+
+void PacketDef::GenBuilderConstructor(std::ostream& s) const {
+  s << name_ << "Builder(";
+
+  // Generate the constructor parameters.
+  auto params = GetParamList().GetFieldsWithoutTypes({
+      PacketField::Type::PAYLOAD,
+      PacketField::Type::BODY,
+  });
+  for (int i = 0; i < params.size(); i++) {
+    params[i]->GenBuilderParameter(s);
+    if (i != params.size() - 1) {
+      s << ", ";
+    }
+  }
+  s << ") :";
+
+  // Get the list of parent params to call the parent constructor with.
+  FieldList parent_params;
+  if (parent_ != nullptr) {
+    // Pass parameters to the parent constructor
+    s << parent_->name_ << "Builder(";
+    parent_params = parent_->GetParamList().GetFieldsWithoutTypes({
+        PacketField::Type::PAYLOAD,
+        PacketField::Type::BODY,
+    });
+
+    // Go through all the fields and replace constrained fields with fixed values
+    // when calling the parent constructor.
+    for (int i = 0; i < parent_params.size(); i++) {
+      const auto& field = parent_params[i];
+      const auto& constraint = parent_constraints_.find(field->GetName());
+      if (constraint != parent_constraints_.end()) {
+        if (field->GetFieldType() == PacketField::Type::SCALAR) {
+          s << std::get<int64_t>(constraint->second);
+        } else if (field->GetFieldType() == PacketField::Type::ENUM) {
+          s << std::get<std::string>(constraint->second);
+        } else {
+          ERROR(field) << "Constraints on non enum/scalar fields should be impossible.";
+        }
+
+        s << "/* " << field->GetName() << "_ */";
+      } else {
+        s << field->GetName();
+      }
+
+      if (i != parent_params.size() - 1) {
+        s << ", ";
+      }
+    }
+    s << ") ";
+  }
+
+  // Build a list of parameters that excludes all parent parameters.
+  FieldList saved_params;
+  for (const auto& field : params) {
+    if (parent_params.GetField(field->GetName()) == nullptr) {
+      saved_params.AppendField(field);
+    }
+  }
+  if (parent_ != nullptr && saved_params.size() > 0) {
+    s << ",";
+  }
+  for (int i = 0; i < saved_params.size(); i++) {
+    const auto& saved_param_name = saved_params[i]->GetName();
+    s << saved_param_name << "_(" << saved_param_name << ")";
+    if (i != saved_params.size() - 1) {
+      s << ",";
+    }
+  }
+  s << " {";
+
+  FieldList params_to_validate = GetParametersToValidate();
+
+  if (params_to_validate.size() > 0) {
+    s << "CheckParameterValues(";
+    for (int i = 0; i < params_to_validate.size(); i++) {
+      s << params_to_validate[i]->GetName() << "_";
+      if (i != params_to_validate.size() - 1) {
+        s << ", ";
+      }
+    }
+    s << ");";
+  }
+
+  s << "}\n";
+}
+
+void PacketDef::GenBuilderMembers(std::ostream& s) const {
+  // Add the parameter list.
+  for (int i = 0; i < fields_.size(); i++) {
+    if (fields_[i]->GenBuilderParameter(s)) {
+      s << "_;";
+    }
+  }
+}
diff --git a/gd/packet/parser/packet_def.h b/gd/packet/parser/packet_def.h
new file mode 100644
index 0000000..647fc4d
--- /dev/null
+++ b/gd/packet/parser/packet_def.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2019 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 <variant>
+
+#include "enum_def.h"
+#include "field_list.h"
+#include "fields/packet_field.h"
+
+class PacketDef {
+ public:
+  PacketDef(std::string name, FieldList fields);
+  PacketDef(std::string name, FieldList fields, PacketDef* parent);
+
+  void AddParentConstraint(std::string field_name, std::variant<int64_t, std::string> value);
+
+  // Assign all size fields to their corresponding variable length fields.
+  // Will crash if
+  //  - there aren't any fields that don't match up to a field.
+  //  - the size field points to a fixed size field.
+  //  - if the size field comes after the variable length field.
+  void AssignSizeFields();
+
+  void SetEndianness(bool is_little_endian);
+
+  // Get the size for the packet. You scan specify without_payload in order
+  // to exclude payload fields as child packets will be overriding it.
+  Size GetSize(bool without_payload = false) const;
+
+  // Get the offset until the field is reached, if there is no field
+  // returns an empty Size. from_end requests the offset to the field
+  // starting from the end() iterator. If there is a field with an unknown
+  // size along the traversal, then an empty size is returned.
+  Size GetOffsetForField(std::string field_name, bool from_end = false) const;
+
+  void GenParserDefinition(std::ostream& s) const;
+
+  void GenParserFieldGetter(std::ostream& s, const PacketField* field) const;
+
+  void GenSerialize(std::ostream& s) const;
+
+  void GenBuilderSize(std::ostream& s) const;
+
+  void GenValidator(std::ostream& s) const;
+
+  void GenBuilderDefinition(std::ostream& s) const;
+
+  FieldList GetParamList() const;
+
+  FieldList GetParametersToValidate() const;
+
+  void GenBuilderCreate(std::ostream& s) const;
+
+  void GenBuilderParameterChecker(std::ostream& s) const;
+
+  void GenBuilderConstructor(std::ostream& s) const;
+
+  void GenBuilderMembers(std::ostream& s) const;
+
+  std::string name_;
+  FieldList fields_;
+
+  std::variant<std::monostate, std::string, EnumDef*> specialize_on_;
+  std::variant<std::monostate, int, std::string> specialization_value_;
+
+  PacketDef* parent_;  // Parent packet type
+
+  std::map<std::string, std::variant<int64_t, std::string>> parent_constraints_;
+  bool is_little_endian_;
+};
diff --git a/gd/packet/parser/parse_location.h b/gd/packet/parser/parse_location.h
new file mode 100644
index 0000000..80bdc72
--- /dev/null
+++ b/gd/packet/parser/parse_location.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2019 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
+
+class ParseLocation {
+ public:
+  ParseLocation() : line_(-1) {}
+
+  ParseLocation(int line) : line_(line) {}
+
+  int GetLine() {
+    return line_;
+  }
+
+ private:
+  int line_;
+};
diff --git a/gd/packet/parser/size.h b/gd/packet/parser/size.h
new file mode 100644
index 0000000..926d12c
--- /dev/null
+++ b/gd/packet/parser/size.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2019 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 <iterator>
+#include <sstream>
+#include <string>
+#include <vector>
+
+class Size {
+ public:
+  Size() {}
+
+  Size(int bits) {
+    is_valid_ = true;
+    bits_ = bits;
+  }
+
+  Size(std::string dynamic) {
+    is_valid_ = true;
+    dynamic_.push_back(dynamic);
+  }
+
+  Size(int bits, std::string dynamic) {
+    is_valid_ = true;
+    bits_ = bits;
+    dynamic_.push_back(dynamic);
+  }
+
+  Size(const Size& size) {
+    is_valid_ = size.is_valid_;
+    bits_ = size.bits_;
+    dynamic_ = size.dynamic_;
+  }
+
+  std::string dynamic_string() {
+    if (dynamic_.empty()) return " 0 /* dynamic */ ";
+
+    std::stringstream result;
+    // Print everything but the last element then append it manually to avoid
+    // the trailing "+" operator.
+    std::copy(dynamic_.begin(), dynamic_.end() - 1, std::ostream_iterator<std::string>(result, " + "));
+    result << dynamic_.back();
+    return result.str();
+  }
+
+  std::vector<std::string> dynamic_string_list() {
+    return dynamic_;
+  }
+
+  bool empty() {
+    return !is_valid_;
+  }
+
+  bool has_bits() {
+    return bits_ != 0;
+  }
+
+  bool has_dynamic() {
+    return !dynamic_.empty();
+  }
+
+  int bits() {
+    return bits_;
+  }
+
+  int bytes() {
+    return bits_ / 8;
+  }
+
+  Size operator+(int rhs) {
+    return Size(bits_ + rhs);
+  }
+
+  Size operator+(std::string rhs) {
+    auto ret = Size();
+    ret.is_valid_ = true;
+    ret.dynamic_.insert(ret.dynamic_.end(), dynamic_.begin(), dynamic_.end());
+    ret.dynamic_.push_back(rhs);
+    return ret;
+  }
+
+  Size operator+(const Size& rhs) {
+    auto ret = Size(bits_ += rhs.bits_);
+    ret.is_valid_ = true;
+    ret.dynamic_.insert(ret.dynamic_.end(), dynamic_.begin(), dynamic_.end());
+    ret.dynamic_.insert(ret.dynamic_.end(), rhs.dynamic_.begin(), rhs.dynamic_.end());
+    return ret;
+  }
+
+  Size& operator+=(int rhs) {
+    is_valid_ = true;
+    bits_ += rhs;
+    return *this;
+  }
+
+  Size& operator+=(std::string rhs) {
+    is_valid_ = true;
+    dynamic_.push_back(rhs);
+    return *this;
+  }
+
+  Size& operator+=(const Size& rhs) {
+    is_valid_ = true;
+    bits_ += rhs.bits_;
+    dynamic_.insert(dynamic_.end(), rhs.dynamic_.begin(), rhs.dynamic_.end());
+    return *this;
+  }
+
+  std::string ToString() {
+    std::stringstream str;
+    str << "Bits: " << bits_ << " | "
+        << "Dynamic: " << dynamic_string();
+    return str.str();
+  }
+
+ private:
+  bool is_valid_ = false;
+  int bits_ = 0;
+  std::vector<std::string> dynamic_;
+};
diff --git a/gd/packet/parser/test/Android.bp b/gd/packet/parser/test/Android.bp
new file mode 100644
index 0000000..c74351e
--- /dev/null
+++ b/gd/packet/parser/test/Android.bp
@@ -0,0 +1,20 @@
+genrule {
+    name: "BluetoothPacketParserTestPacketPdlGen_h",
+    tools: [
+        "bluetooth_packetgen",
+    ],
+    cmd: "$(location bluetooth_packetgen) --include=system/bt/gd --out=$(genDir) $(in)",
+    srcs: [
+        "test_packets.pdl",
+    ],
+    out: [
+        "packet/parser/test/test_packets.h",
+    ],
+}
+
+filegroup {
+    name: "BluetoothPacketParserTestPacketTestSources",
+    srcs: [
+        "generated_packet_test.cc",
+    ],
+}
diff --git a/gd/packet/parser/test/generated_packet_test.cc b/gd/packet/parser/test/generated_packet_test.cc
new file mode 100644
index 0000000..ab3ab81
--- /dev/null
+++ b/gd/packet/parser/test/generated_packet_test.cc
@@ -0,0 +1,398 @@
+/*
+ * Copyright 2019 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 "packet/parser/test/test_packets.h"
+
+#include <gtest/gtest.h>
+#include <forward_list>
+#include <memory>
+
+#include "common/address.h"
+#include "os/log.h"
+#include "packet/bit_inserter.h"
+#include "packet/raw_builder.h"
+
+using ::bluetooth::common::Address;
+using ::bluetooth::packet::BitInserter;
+using ::bluetooth::packet::kLittleEndian;
+using ::bluetooth::packet::RawBuilder;
+using std::vector;
+
+namespace {
+vector<uint8_t> child_two_two_three = {
+    0x20 /* Reserved : 4, FourBits::TWO */,
+    0x03 /* FourBits::THREE, Reserved : 4 */,
+};
+vector<uint8_t> child = {
+    0x12 /* fixed */, 0x02 /* Size of the payload */, 0xa1 /* First byte of the payload */, 0xa2, 0xb1 /* footer */,
+};
+vector<uint8_t> child_with_address = {
+    0x34 /* TwoBytes */,
+    0x12,
+    0xa6 /* First byte of the address */,
+    0xa5,
+    0xa4,
+    0xa3,
+    0xa2,
+    0xa1,
+    0xb6 /* Second address*/,
+    0xb5,
+    0xb4,
+    0xb3,
+    0xb2,
+    0xb1,
+};
+
+}  // namespace
+
+namespace bluetooth {
+namespace packet {
+namespace parser {
+using namespace test;
+
+TEST(GeneratedPacketTest, testChildTwoTwoThree) {
+  auto packet = ChildTwoTwoThreeBuilder::Create();
+
+  ASSERT_EQ(child_two_two_three.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(packet_bytes->size(), child_two_two_three.size());
+  for (size_t i = 0; i < child_two_two_three.size(); i++) {
+    ASSERT_EQ(packet_bytes->at(i), child_two_two_three[i]);
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentView wrong_view = ParentView::Create(packet_bytes_view);
+  ASSERT_FALSE(wrong_view.IsValid());
+
+  ParentTwoView parent_view = ParentTwoView::Create(packet_bytes_view);
+  ASSERT_TRUE(parent_view.IsValid());
+  ASSERT_EQ(FourBits::TWO, parent_view.GetFourBits());
+
+  ChildTwoTwoView child_view = ChildTwoTwoView::Create(parent_view);
+  ASSERT_TRUE(child_view.IsValid());
+  ASSERT_EQ(FourBits::THREE, child_view.GetMoreBits());
+
+  ChildTwoTwoThreeView grandchild_view = ChildTwoTwoThreeView::Create(child_view);
+  ASSERT_TRUE(grandchild_view.IsValid());
+}
+
+TEST(GeneratedPacketTest, testChild) {
+  uint16_t field_name = 0xa2a1;
+  uint8_t footer = 0xb1;
+  auto packet = ChildBuilder::Create(field_name, footer);
+
+  ASSERT_EQ(child.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(packet_bytes->size(), child.size());
+  for (size_t i = 0; i < child.size(); i++) {
+    ASSERT_EQ(packet_bytes->at(i), child[i]);
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentView parent_view = ParentView::Create(packet_bytes_view);
+  ASSERT_TRUE(parent_view.IsValid());
+  auto payload = parent_view.GetPayload();
+
+  ASSERT_EQ(child[1 /* skip fixed field */], payload.size());
+  for (size_t i = 0; i < payload.size(); i++) {
+    ASSERT_EQ(child[i + 2 /* fixed & size */], payload[i]);
+  }
+
+  ChildView child_view = ChildView::Create(parent_view);
+  ASSERT_TRUE(child_view.IsValid());
+
+  ASSERT_EQ(field_name, child_view.GetFieldName());
+}
+
+TEST(GeneratedPacketTest, testValidateWayTooSmall) {
+  std::vector<uint8_t> too_small_bytes = {0x34};
+  auto too_small = std::make_shared<std::vector<uint8_t>>(too_small_bytes.begin(), too_small_bytes.end());
+
+  ParentWithAddressView invalid_parent = ParentWithAddressView::Create(too_small);
+  ASSERT_FALSE(invalid_parent.IsValid());
+  ChildWithAddressView invalid = ChildWithAddressView::Create(ParentWithAddressView::Create(too_small));
+  ASSERT_FALSE(invalid.IsValid());
+}
+
+TEST(GeneratedPacketTest, testValidateTooSmall) {
+  std::vector<uint8_t> too_small_bytes = {0x34, 0x12, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x11};
+  auto too_small = std::make_shared<std::vector<uint8_t>>(too_small_bytes.begin(), too_small_bytes.end());
+
+  ParentWithAddressView valid_parent = ParentWithAddressView::Create(too_small);
+  ASSERT_TRUE(valid_parent.IsValid());
+  ChildWithAddressView invalid = ChildWithAddressView::Create(ParentWithAddressView::Create(too_small));
+  ASSERT_FALSE(invalid.IsValid());
+}
+
+TEST(GeneratedPacketTest, testValidateJustRight) {
+  std::vector<uint8_t> just_right_bytes = {0x34, 0x12, 0x01, 0x02, 0x03, 0x04, 0x05,
+                                           0x06, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16};
+  auto just_right = std::make_shared<std::vector<uint8_t>>(just_right_bytes.begin(), just_right_bytes.end());
+
+  ChildWithAddressView valid = ChildWithAddressView::Create(ParentWithAddressView::Create(just_right));
+  ASSERT_TRUE(valid.IsValid());
+}
+
+TEST(GeneratedPacketTest, testValidateTooBig) {
+  std::vector<uint8_t> too_big_bytes = {0x34, 0x12, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+                                        0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x20};
+  auto too_big = std::make_shared<std::vector<uint8_t>>(too_big_bytes.begin(), too_big_bytes.end());
+
+  ChildWithAddressView lenient = ChildWithAddressView::Create(ParentWithAddressView::Create(too_big));
+  ASSERT_TRUE(lenient.IsValid());
+}
+
+TEST(GeneratedPacketTest, testValidateDeath) {
+  auto packet = ChildTwoTwoThreeBuilder::Create();
+
+  ASSERT_EQ(child_two_two_three.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(packet_bytes->size(), child_two_two_three.size());
+  for (size_t i = 0; i < child_two_two_three.size(); i++) {
+    ASSERT_EQ(packet_bytes->at(i), child_two_two_three[i]);
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentView wrong_view = ParentView::Create(packet_bytes_view);
+  ASSERT_DEATH(wrong_view.GetPayload(), "validated");
+}
+
+TEST(GeneratedPacketTest, testValidatedParentDeath) {
+  uint16_t field_name = 0xa2a1;
+  uint8_t footer = 0xb1;
+  auto packet = ChildBuilder::Create(field_name, footer);
+
+  ASSERT_EQ(child.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(packet_bytes->size(), child.size());
+  for (size_t i = 0; i < child.size(); i++) {
+    ASSERT_EQ(packet_bytes->at(i), child[i]);
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentView parent_view = ParentView::Create(packet_bytes_view);
+  ASSERT_TRUE(parent_view.IsValid());
+  auto payload = parent_view.GetPayload();
+
+  ASSERT_EQ(child[1 /* skip fixed field */], payload.size());
+  for (size_t i = 0; i < payload.size(); i++) {
+    ASSERT_EQ(child[i + 2 /* fixed & size */], payload[i]);
+  }
+
+  ChildView child_view = ChildView::Create(parent_view);
+  ASSERT_DEATH(child_view.GetFieldName(), "validated");
+}
+
+TEST(GeneratedPacketTest, testChildWithAddress) {
+  Address address_a;
+  ASSERT_TRUE(Address::FromString("A1:A2:A3:A4:A5:A6", address_a));
+  Address address_b;
+  ASSERT_TRUE(Address::FromString("B1:B2:B3:B4:B5:B6", address_b));
+  auto packet = ChildWithAddressBuilder::Create(address_a, address_b);
+
+  ASSERT_EQ(child_with_address.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(packet_bytes->size(), child_with_address.size());
+  for (size_t i = 0; i < child_with_address.size(); i++) {
+    ASSERT_EQ(packet_bytes->at(i), child_with_address[i]);
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentWithAddressView parent_view = ParentWithAddressView::Create(packet_bytes_view);
+  ASSERT_TRUE(parent_view.IsValid());
+  ASSERT_EQ(address_a, parent_view.GetAddress());
+
+  ChildWithAddressView child_view = ChildWithAddressView::Create(parent_view);
+  ASSERT_TRUE(child_view.IsValid());
+
+  ASSERT_EQ(address_a, child_view.GetAddress());
+  ASSERT_EQ(address_a, ((ParentWithAddressView)child_view).GetAddress());
+  ASSERT_EQ(address_b, child_view.GetChildAddress());
+}
+
+namespace {
+vector<uint8_t> parent_with_sum = {
+    0x11 /* TwoBytes */, 0x12, 0x21 /* Sum Bytes */, 0x22, 0x43 /* Sum, excluding TwoBytes */, 0x00,
+};
+
+}  // namespace
+
+TEST(GeneratedPacketTest, testParentWithSum) {
+  uint16_t two_bytes = 0x1211;
+  uint16_t sum_bytes = 0x2221;
+  auto packet = ParentWithSumBuilder::Create(two_bytes, sum_bytes, std::make_unique<packet::RawBuilder>());
+
+  ASSERT_EQ(parent_with_sum.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(packet_bytes->size(), parent_with_sum.size());
+  for (size_t i = 0; i < parent_with_sum.size(); i++) {
+    ASSERT_EQ(packet_bytes->at(i), parent_with_sum[i]);
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentWithSumView parent_view = ParentWithSumView::Create(packet_bytes_view);
+  ASSERT_TRUE(parent_view.IsValid());
+  ASSERT_EQ(two_bytes, parent_view.GetTwoBytes());
+
+  // Corrupt checksum
+  packet_bytes->back()++;
+  PacketView<kLittleEndian> corrupted_bytes_view(packet_bytes);
+  ParentWithSumView corrupted_view = ParentWithSumView::Create(corrupted_bytes_view);
+  ASSERT_FALSE(corrupted_view.IsValid());
+}
+
+namespace {
+vector<uint8_t> child_with_nested_sum = {
+    0x11 /* TwoBytes */,
+    0x12,
+    0x21 /* Sum Bytes */,
+    0x22,
+    0x31 /* More Bytes */,
+    0x32,
+    0x33,
+    0x34,
+    0xca /* Nested Sum */,
+    0x00,
+    0xd7 /* Sum, excluding TwoBytes */,
+    0x01,
+};
+
+}  // namespace
+
+TEST(GeneratedPacketTest, testChildWithNestedSum) {
+  uint16_t two_bytes = 0x1211;
+  uint16_t sum_bytes = 0x2221;
+  uint32_t more_bytes = 0x34333231;
+  auto packet = ChildWithNestedSumBuilder::Create(two_bytes, sum_bytes, more_bytes);
+
+  ASSERT_EQ(child_with_nested_sum.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(packet_bytes->size(), child_with_nested_sum.size());
+  for (size_t i = 0; i < child_with_nested_sum.size(); i++) {
+    ASSERT_EQ(packet_bytes->at(i), child_with_nested_sum[i]);
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentWithSumView parent_view = ParentWithSumView::Create(packet_bytes_view);
+  ASSERT_TRUE(parent_view.IsValid());
+  ASSERT_EQ(two_bytes, parent_view.GetTwoBytes());
+
+  ChildWithNestedSumView child_view = ChildWithNestedSumView::Create(parent_view);
+  ASSERT_TRUE(child_view.IsValid());
+
+  ASSERT_EQ(more_bytes, child_view.GetMoreBytes());
+}
+
+namespace {
+vector<uint8_t> parent_size_modifier = {
+    0x02 /* Size */,
+    0x11 /* TwoBytes */,
+    0x12,
+};
+
+}  // namespace
+
+TEST(GeneratedPacketTest, testParentSizeModifier) {
+  uint16_t two_bytes = 0x1211;
+  auto packet = ParentSizeModifierBuilder::Create(std::make_unique<RawBuilder>(), two_bytes);
+
+  ASSERT_EQ(parent_size_modifier.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(parent_size_modifier.size(), packet_bytes->size());
+  for (size_t i = 0; i < parent_size_modifier.size(); i++) {
+    ASSERT_EQ(parent_size_modifier[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentSizeModifierView parent_view = ParentSizeModifierView::Create(packet_bytes_view);
+  ASSERT_TRUE(parent_view.IsValid());
+  ASSERT_EQ(two_bytes, parent_view.GetTwoBytes());
+}
+
+namespace {
+vector<uint8_t> child_size_modifier = {
+    0x06 /* PayloadSize (TwoBytes + MoreBytes)*/,
+    0x31 /* MoreBytes */,
+    0x32,
+    0x33,
+    0x34,
+    0x11 /* TwoBytes = 0x1211 */,
+    0x12,
+};
+
+}  // namespace
+
+TEST(GeneratedPacketTest, testChildSizeModifier) {
+  uint16_t two_bytes = 0x1211;
+  uint32_t more_bytes = 0x34333231;
+  auto packet = ChildSizeModifierBuilder::Create(more_bytes);
+
+  ASSERT_EQ(child_size_modifier.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(child_size_modifier.size(), packet_bytes->size());
+  for (size_t i = 0; i < child_size_modifier.size(); i++) {
+    ASSERT_EQ(child_size_modifier[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentSizeModifierView parent_view = ParentSizeModifierView::Create(packet_bytes_view);
+  ASSERT_TRUE(parent_view.IsValid());
+  ASSERT_EQ(two_bytes, parent_view.GetTwoBytes());
+
+  ChildSizeModifierView child_view = ChildSizeModifierView::Create(parent_view);
+  ASSERT_TRUE(child_view.IsValid());
+
+  ASSERT_EQ(more_bytes, child_view.GetMoreBytes());
+}
+}  // namespace parser
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/parser/test/simple_sum.h b/gd/packet/parser/test/simple_sum.h
new file mode 100644
index 0000000..b66cb9a
--- /dev/null
+++ b/gd/packet/parser/test/simple_sum.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 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 <cstdint>
+
+namespace bluetooth {
+namespace packet {
+namespace parser {
+namespace test {
+
+class SimpleSum {
+ public:
+  static void Initialize(SimpleSum& s) {
+    s.sum = 0;
+  }
+
+  static void AddByte(SimpleSum& s, uint8_t byte) {
+    s.sum += byte;
+  }
+
+  static uint16_t GetChecksum(const SimpleSum& s) {
+    return s.sum;
+  }
+
+ private:
+  uint16_t sum;
+};
+
+}  // namespace test
+}  // namespace parser
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/parser/test/test_packets.pdl b/gd/packet/parser/test/test_packets.pdl
new file mode 100644
index 0000000..a6abed5
--- /dev/null
+++ b/gd/packet/parser/test/test_packets.pdl
@@ -0,0 +1,89 @@
+little_endian_packets
+
+custom_field Address : 48 "common/"
+
+packet Parent {
+  _fixed_ = 0x12 : 8,
+  _size_(_payload_) : 8,
+  _payload_,
+  footer : 8,
+}
+
+packet Child : Parent {
+  field_name : 16,
+}
+
+enum FourBits : 4 {
+  ONE = 1,
+  TWO = 2,
+  THREE = 3,
+  LAZY_ME = 15,
+}
+
+packet ParentTwo {
+  _reserved_ : 4,
+  four_bits : FourBits,
+  _payload_,
+}
+
+packet ChildTwoThree : ParentTwo (four_bits = THREE) {
+  more_bits : FourBits,
+  _reserved_ : 4,
+  sixteen_bits : 16
+}
+
+packet ChildTwoTwo : ParentTwo (four_bits = TWO) {
+  more_bits : FourBits,
+  _reserved_ : 4,
+}
+
+packet ChildTwoTwoThree :ChildTwoTwo (more_bits = THREE) {
+}
+
+packet ParentWithAddress {
+  two_bytes : 16,
+  address : Address,
+  _payload_,
+}
+
+packet ChildWithAddress : ParentWithAddress (two_bytes = 0x1234) {
+  child_address : Address,
+}
+
+checksum SimpleSum : 16 "packet/parser/test/"
+
+packet ParentWithSum {
+  two_bytes : 16,
+  _checksum_start_(example_checksum),
+  sum_bytes : 16,
+  _payload_,
+  example_checksum : SimpleSum,
+}
+
+packet ChildWithSum : ParentWithSum {
+  more_bytes : 32,
+  another_byte : 8,
+}
+
+packet ChildWithNestedSum : ParentWithSum {
+  _checksum_start_(nested_checksum),
+  more_bytes : 32,
+  nested_checksum : SimpleSum,
+}
+
+packet ParentSizeModifier {
+  _size_(_payload_) : 8,
+  _payload_ : [+2*8], // Include two_bytes in the size
+  two_bytes : 16,
+}
+
+packet ChildSizeModifier : ParentSizeModifier (two_bytes = 0x1211) {
+  more_bytes : 32,
+}
+
+packet FieldsEndWithNumbers {
+  field_1 : 16,
+  field_2 : 16,
+  field_10 : 16,
+  field_11 : 16,
+}
diff --git a/gd/packet/parser/type_def.h b/gd/packet/parser/type_def.h
new file mode 100644
index 0000000..ceaf069
--- /dev/null
+++ b/gd/packet/parser/type_def.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 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 <iostream>
+
+#include "fields/packet_field.h"
+#include "fields/reserved_field.h"
+
+class TypeDef {
+ public:
+  TypeDef(std::string name) : name_(name) {}
+
+  TypeDef(std::string name, int size) : name_(name), size_(size) {}
+
+  virtual ~TypeDef() = default;
+
+  std::string GetTypeName() const {
+    return name_;
+  }
+
+  enum class Type {
+    INVALID,
+    ENUM,
+    CHECKSUM,
+    CUSTOM,
+  };
+
+  virtual Type GetDefinitionType() const = 0;
+
+  virtual PacketField* GetNewField(const std::string& name, ParseLocation loc) const = 0;
+
+  virtual void GenInclude(std::ostream& s) const = 0;
+
+  virtual void GenUsing(std::ostream& s) const = 0;
+
+  const std::string name_;
+  const int size_{-1};
+};
diff --git a/gd/packet/parser/util.h b/gd/packet/parser/util.h
new file mode 100644
index 0000000..42f68f9
--- /dev/null
+++ b/gd/packet/parser/util.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2019 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 <algorithm>
+#include <iostream>
+#include <regex>
+#include <string>
+
+#include "logging.h"
+
+namespace util {
+
+inline std::string GetTypeForSize(int size) {
+  if (size > 64) {
+    ERROR() << __func__ << ": Cannot use a type larger than 64 bits. (" << size << ")\n";
+  }
+
+  if (size <= 8) return "uint8_t";
+
+  if (size <= 16) return "uint16_t";
+
+  if (size <= 32) return "uint32_t";
+
+  return "uint64_t";
+}
+
+inline int RoundSizeUp(int size) {
+  if (size > 64) {
+    ERROR() << __func__ << ": Cannot use a type larger than 64 bits. (" << size << ")\n";
+  }
+
+  if (size <= 8) return 8;
+  if (size <= 16) return 16;
+  if (size <= 32) return 32;
+  return 64;
+}
+
+// Returns the max value that can be contained unsigned in a number of bits.
+inline uint64_t GetMaxValueForBits(int bits) {
+  if (bits > 64) {
+    ERROR() << __func__ << ": Cannot use a type larger than 64 bits. (" << bits << ")\n";
+  }
+
+  uint64_t max = 0;
+  for (int i = 0; i < bits; i++) {
+    max <<= 1;
+    max |= 1;
+  }
+
+  return max;
+}
+
+inline std::string CamelCaseToUnderScore(std::string value) {
+  // Use static to avoid compiling the regex more than once.
+  static const std::regex camel_case_regex("[A-Z][a-z0-9]*");
+
+  // Add an underscore to the end of each pattern match.
+  value = std::regex_replace(value, camel_case_regex, "$&_");
+
+  // Remove the last underscore at the end of the string.
+  value.pop_back();
+
+  // Convert all characters to lowercase.
+  std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) { return std::tolower(c); });
+
+  return value;
+}
+
+inline std::string UnderscoreToCamelCase(std::string value) {
+  std::ostringstream camel_case;
+
+  bool capitalize = true;
+  for (unsigned char c : value) {
+    if (c == '_') {
+      capitalize = true;
+    } else {
+      if (capitalize) {
+        c = std::toupper(c);
+        capitalize = false;
+      }
+      camel_case << c;
+    }
+  }
+
+  return camel_case.str();
+}
+
+}  // namespace util
diff --git a/gd/stack_manager.cc b/gd/stack_manager.cc
new file mode 100644
index 0000000..ad83674
--- /dev/null
+++ b/gd/stack_manager.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2019 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 "stack_manager.h"
+
+#include <chrono>
+#include <future>
+#include <queue>
+
+#include "hal/hci_hal.h"
+#include "os/thread.h"
+#include "os/handler.h"
+#include "os/log.h"
+#include "module.h"
+
+using ::bluetooth::os::Handler;
+using ::bluetooth::os::Thread;
+
+namespace bluetooth {
+
+void StackManager::StartUp(ModuleList* modules, Thread* stack_thread) {
+  management_thread_ = new Thread("management_thread", Thread::Priority::NORMAL);
+  handler_ = new Handler(management_thread_);
+
+  std::promise<void>* promise = new std::promise<void>();
+  handler_->Post([this, promise, modules, stack_thread]() {
+    registry_.Start(modules, stack_thread);
+    promise->set_value();
+  });
+
+  auto future = promise->get_future();
+  auto init_status = future.wait_for(std::chrono::seconds(3));
+  ASSERT_LOG(init_status == std::future_status::ready, "Can't start stack");
+  delete promise;
+
+  LOG_INFO("init complete");
+}
+
+void StackManager::ShutDown() {
+  std::promise<void>* promise = new std::promise<void>();
+  handler_->Post([this, promise]() {
+    registry_.StopAll();
+    promise->set_value();
+  });
+
+  auto future = promise->get_future();
+  auto stop_status = future.wait_for(std::chrono::seconds(3));
+  ASSERT_LOG(stop_status == std::future_status::ready, "Can't stop stack");
+
+  delete promise;
+  delete handler_;
+  delete management_thread_;
+}
+}  // namespace bluetooth
diff --git a/gd/stack_manager.h b/gd/stack_manager.h
new file mode 100644
index 0000000..2f971cb
--- /dev/null
+++ b/gd/stack_manager.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 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 "module.h"
+#include "os/thread.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+
+class StackManager {
+ public:
+  void StartUp(ModuleList *modules, os::Thread* stack_thread);
+  void ShutDown();
+
+  template <class T>
+  T* GetInstance() const {
+    return static_cast<T*>(registry_.Get(&T::Factory));
+  }
+
+ private:
+  os::Thread* management_thread_;
+  os::Handler* handler_;
+  ModuleRegistry registry_;
+};
+
+}  // namespace bluetooth
diff --git a/hci/src/hci_layer_android.cc b/hci/src/hci_layer_android.cc
index 92be0df..d3fa526 100644
--- a/hci/src/hci_layer_android.cc
+++ b/hci/src/hci_layer_android.cc
@@ -83,25 +83,25 @@
     return packet;
   }
 
-  Return<void> initializationComplete(Status status) {
+  Return<void> initializationComplete(Status status) override {
     CHECK(status == Status::SUCCESS);
     initialization_complete();
     return Void();
   }
 
-  Return<void> hciEventReceived(const hidl_vec<uint8_t>& event) {
+  Return<void> hciEventReceived(const hidl_vec<uint8_t>& event) override {
     BT_HDR* packet = WrapPacketAndCopy(MSG_HC_TO_STACK_HCI_EVT, event);
     hci_event_received(FROM_HERE, packet);
     return Void();
   }
 
-  Return<void> aclDataReceived(const hidl_vec<uint8_t>& data) {
+  Return<void> aclDataReceived(const hidl_vec<uint8_t>& data) override {
     BT_HDR* packet = WrapPacketAndCopy(MSG_HC_TO_STACK_HCI_ACL, data);
     acl_event_received(packet);
     return Void();
   }
 
-  Return<void> scoDataReceived(const hidl_vec<uint8_t>& data) {
+  Return<void> scoDataReceived(const hidl_vec<uint8_t>& data) override {
     BT_HDR* packet = WrapPacketAndCopy(MSG_HC_TO_STACK_HCI_SCO, data);
     sco_data_received(packet);
     return Void();
diff --git a/hci/test/packet_fragmenter_test.cc b/hci/test/packet_fragmenter_test.cc
index 483a66d..ca83dfa 100644
--- a/hci/test/packet_fragmenter_test.cc
+++ b/hci/test/packet_fragmenter_test.cc
@@ -291,7 +291,7 @@
 
 class PacketFragmenterTest : public AllocationTestHarness {
  protected:
-  virtual void SetUp() {
+  void SetUp() override {
     AllocationTestHarness::SetUp();
     fragmenter =
         packet_fragmenter_get_test_interface(&controller, &allocator_malloc);
@@ -309,7 +309,7 @@
     fragmenter->init(&callbacks);
   }
 
-  virtual void TearDown() {
+  void TearDown() override {
     fragmenter->cleanup();
     AllocationTestHarness::TearDown();
   }
diff --git a/osi/test/alarm_test.cc b/osi/test/alarm_test.cc
index dad54ef..fb55dc7 100644
--- a/osi/test/alarm_test.cc
+++ b/osi/test/alarm_test.cc
@@ -46,7 +46,7 @@
 
 class AlarmTest : public AlarmTestHarness {
  protected:
-  virtual void SetUp() {
+  void SetUp() override {
     AlarmTestHarness::SetUp();
     cb_counter = 0;
     cb_misordered_counter = 0;
@@ -54,7 +54,7 @@
     semaphore = semaphore_new(0);
   }
 
-  virtual void TearDown() {
+  void TearDown() override {
     semaphore_free(semaphore);
     AlarmTestHarness::TearDown();
   }
diff --git a/osi/test/config_test.cc b/osi/test/config_test.cc
index 7cf4db8..32858fd 100644
--- a/osi/test/config_test.cc
+++ b/osi/test/config_test.cc
@@ -52,7 +52,7 @@
 
 class ConfigTest : public AllocationTestHarness {
  protected:
-  virtual void SetUp() {
+  void SetUp() override {
     AllocationTestHarness::SetUp();
     FILE* fp = fopen(CONFIG_FILE, "wt");
     fwrite(CONFIG_FILE_CONTENT, 1, sizeof(CONFIG_FILE_CONTENT), fp);
diff --git a/osi/test/hash_map_utils_test.cc b/osi/test/hash_map_utils_test.cc
index 1e312d4..d5df2d7 100644
--- a/osi/test/hash_map_utils_test.cc
+++ b/osi/test/hash_map_utils_test.cc
@@ -27,8 +27,8 @@
 
 class HashMapUtilsTest : public AllocationTestHarness {
  protected:
-  virtual void SetUp() { AllocationTestHarness::SetUp(); }
-  virtual void TearDown() {
+  void SetUp() override { AllocationTestHarness::SetUp(); }
+  void TearDown() override {
     map.clear();
     AllocationTestHarness::TearDown();
   }
diff --git a/osi/test/wakelock_test.cc b/osi/test/wakelock_test.cc
index 0e027be..a1dfc05 100644
--- a/osi/test/wakelock_test.cc
+++ b/osi/test/wakelock_test.cc
@@ -45,7 +45,7 @@
 
 class WakelockTest : public AllocationTestHarness {
  protected:
-  virtual void SetUp() {
+  void SetUp() override {
     AllocationTestHarness::SetUp();
 
 // TODO (jamuraa): maybe use base::CreateNewTempDirectory instead?
@@ -69,7 +69,7 @@
     creat(unlock_path_.c_str(), S_IRWXU);
   }
 
-  virtual void TearDown() {
+  void TearDown() override {
     is_wake_lock_acquired = false;
     wakelock_cleanup();
     wakelock_set_os_callouts(NULL);
diff --git a/packets/smp.pdl b/packets/smp.pdl
new file mode 100644
index 0000000..39e6813
--- /dev/null
+++ b/packets/smp.pdl
@@ -0,0 +1,158 @@
+enum CodeT : 8 {
+  PAIRING_REQUEST = 0x01,
+  PAIRING_RESPONSE = 0x02,
+  PAIRING_CONFIRM = 0x03,
+  PAIRING_RANDOM = 0x04,
+  PAIRING_FAILED = 0x05,
+  ENCRYPTION_INFORMATION = 0x06,
+  MASTER_IDENTIFICATION = 0x07,
+  IDENTITY_INFORMATION = 0x08,
+  IDENTITY_ADDRESS_INFORMATION = 0x09,
+  SIGNING_INFORMATION = 0x0A,
+  SECURITY_REQUEST = 0x0B,
+  PAIRING_PUBLIC_KEY = 0x0C,
+  PAIRING_DH_KEY_CHECK = 0x0D,
+  PAIRING_KEYPRESS_NOTIFICATION = 0x0E,
+}
+
+packet Command {
+  Code : CodeT,
+  payload,
+}
+
+enum IoCapabilityT : 8 {
+  DISPLAY_ONLY = 0x00,
+  DISPLAY_YES_NO = 0x01,
+  KEYBOARD_ONLY = 0x02,
+  NO_INPUT_NO_OUTPUT = 0x03,
+  KEYBOARD_DISPLAY = 0x04,
+}
+
+enum OobDataFlagT : 8 {
+  NOT_PRESENT = 0x00,
+  PRESENT = 0x01,
+}
+
+enum BondingFlagsT : 2 {
+  NO_BONDING = 0,
+  BONDING = 1,
+}
+
+group AuthReqT {
+  BondingFlags : BondingFlagsT,
+  Mitm : 1, // Man-in-the-middle protection required
+  SC : 1, // Secure Connections
+  Keypress : 1,  // Only used in Passkey Entry
+  Ct2 : 1, // Support for the h7 function.
+  reserved : 2,
+}
+
+group PairingInfoT {
+  IoCapability : IoCapabilityT,
+  OobDataFlag : OobDataFlagT,
+  AuthReqT,
+  MaximumEncryptionKeySize : 5, // 7 - 16
+  reserved : 3,
+  // InitiatorKeyDistribution
+  InitiatorEncKey : 1,
+  InitiatorIdKey : 1,
+  InitiatorSignKey : 1,
+  InitiatorLinkKey : 1,
+  reserved : 4,
+  // ResponderKeyDistribution
+  ResponderEncKey : 1,
+  ResponderIdKey : 1,
+  ResponderSignKey : 1,
+  ResponderLinkKey : 1,
+  reserved : 4,
+}
+
+packet PairingRequest : Command (Code = PAIRING_REQUEST) {
+  PairingInfoT,
+}
+
+packet PairingResponse : Command (Code = PAIRING_RESPONSE) {
+  PairingInfoT,
+}
+
+packet PairingConfirm : Command (Code = PAIRING_CONFIRM) {
+  ConfirmValue : 8[16],  // Initiating device sends Mconfirm, responding device sends Sconfirm
+}
+
+packet PairingRandom : Command (Code = PAIRING_RANDOM) {
+  RandomValue : 8[16],  // Initiating device sends Mrand, responding device sends Srand
+}
+
+enum PairingFailedReasonT : 8 {
+  PASSKEY_ENTRY_FAILED = 0x01,
+  OOB_NOT_AVAILABLE = 0x02,
+  AUTHENTICATION_REQUIREMENTS = 0x03,
+  CONFIRM_VALUE_FAILED = 0x04,
+  PAIRING_NOT_SUPPORTED = 0x05,
+  ENCRYPTION_KEY_SIZE = 0x06,
+  COMMAND_NOT_SUPPORTED = 0x07,
+  UNSPECIFIED_REASON = 0x08,
+  REPEATED_ATTEMPTS = 0x09,
+  INVALID_PARAMETERS = 0x0A,
+  DHKEY_CHECK_FAILED = 0x0B,
+  NUMERIC_COMPARISON_FAILED = 0x0C,
+  BR_EDR_PAIRING_IN_PROGRESS = 0x0D,
+  CROSS_TRANSPORT_KEY_DERIVATION_NOT_ALLOWED = 0x0E,
+}
+
+packet PairingFailed : Command (Code = PAIRING_FAILED) {
+  Reason : PairingFailedReasonT,
+}
+
+packet EncryptionInformation : Command (Code = ENCRYPTION_INFORMATION) {
+  LongTermKey : 8[16],
+}
+
+packet MasterIdentification : Command (Code = MASTER_IDENTIFICATION) {
+  Ediv : 16,
+  Rand : 64,
+}
+
+packet IdentityInformation : Command (Code = IDENTITY_INFORMATION) {
+  IdentityResolvingKey : 8[16],
+}
+
+enum AddrTypeT : 8 {
+  PUBLIC = 0x00,
+  STATIC_RANDOM = 0x01,
+}
+
+packet IdentityAddressInformation : Command (Code = IDENTITY_ADDRESS_INFORMATION) {
+  AddrType : AddrTypeT,
+  BdAddr : 8[6],
+}
+
+packet SigningInformation : Command (Code = SIGNING_INFORMATION) {
+  SignatureKey : 8[16],
+}
+
+packet SecurityRequest : Command (Code = SECURITY_REQUEST) {
+  AuthReqT,
+}
+
+packet PairingPublicKey : Command (Code = PAIRING_PUBLIC_KEY) {
+  PublicKeyX : 8[32],
+  PublicKeyY : 8[32],
+}
+
+packet PairingDhKeyCheck : Command (Code = PAIRING_DH_KEY_CHECK) {
+  DhKeyCheck : 8[16],
+}
+
+enum KeypressNotificationTypeT : 8 {
+  ENTRY_STARTED = 0,
+  DIGIT_ENTERED = 1,
+  DIGIT_ERASED = 2,
+  CLEARED = 3,
+  ENTRY_COMPLETED = 4,
+}
+
+packet PairingKeypressNotification : Command (Code = PAIRING_KEYPRESS_NOTIFICATION) {
+  NotificationType : KeypressNotificationTypeT,
+}
+
diff --git a/profile/avrcp/tests/avrcp_device_test.cc b/profile/avrcp/tests/avrcp_device_test.cc
index c608da6..d221e1b 100644
--- a/profile/avrcp/tests/avrcp_device_test.cc
+++ b/profile/avrcp/tests/avrcp_device_test.cc
@@ -57,7 +57,7 @@
 // Add more tests to increase code coverage.
 class AvrcpDeviceTest : public ::testing::Test {
  public:
-  virtual void SetUp() override {
+  void SetUp() override {
     // NOTE: We use a wrapper lambda for the MockFunction in order to
     // add a const qualifier to the response. Otherwise the MockFunction
     // type doesn't match the callback type and a compiler error occurs.
@@ -71,7 +71,7 @@
     test_device = new Device(RawAddress::kAny, true, cb, 0xFFFF, 0xFFFF);
   }
 
-  virtual void TearDown() override {
+  void TearDown() override {
     delete test_device;
     Mock::VerifyAndClear(&response_cb);
   }
diff --git a/service/test/low_energy_client_unittest.cc b/service/test/low_energy_client_unittest.cc
index 3bf566c..ce3c7c0 100644
--- a/service/test/low_energy_client_unittest.cc
+++ b/service/test/low_energy_client_unittest.cc
@@ -58,13 +58,13 @@
   int connection_state_count() const { return connection_state_count_; }
 
   void OnConnectionState(LowEnergyClient* client, int status,
-                         const char* address, bool connected) {
+                         const char* address, bool connected) override {
     ASSERT_TRUE(client);
     connection_state_count_++;
   }
 
   void OnMtuChanged(LowEnergyClient* client, int status, const char* address,
-                    int mtu) {
+                    int mtu) override {
     ASSERT_TRUE(client);
     last_mtu_ = mtu;
   }
diff --git a/service/test/low_energy_scanner_unittest.cc b/service/test/low_energy_scanner_unittest.cc
index c8b16f3..9a7875d 100644
--- a/service/test/low_energy_scanner_unittest.cc
+++ b/service/test/low_energy_scanner_unittest.cc
@@ -72,12 +72,12 @@
   MOCK_METHOD1(StopSync, void(uint16_t));
 
   void ScanFilterAdd(int filter_index, std::vector<ApcfCommand> filters,
-                     FilterConfigCallback cb){};
+                     FilterConfigCallback cb) override{};
 
   void ScanFilterParamSetup(
       uint8_t client_if, uint8_t action, uint8_t filt_index,
       std::unique_ptr<btgatt_filt_param_setup_t> filt_param,
-      FilterParamSetupCallback cb) {
+      FilterParamSetupCallback cb) override {
     ScanFilterParamSetupImpl(client_if, action, filt_index, filt_param.get(),
                              std::move(cb));
   }
@@ -92,7 +92,8 @@
   int scan_result_count() const { return scan_result_count_; }
   const ScanResult& last_scan_result() const { return last_scan_result_; }
 
-  void OnScanResult(LowEnergyScanner* scanner, const ScanResult& scan_result) {
+  void OnScanResult(LowEnergyScanner* scanner,
+                    const ScanResult& scan_result) override {
     ASSERT_TRUE(scanner);
     scan_result_count_++;
     last_scan_result_ = scan_result;
diff --git a/stack/btm/btm_ble_multi_adv.cc b/stack/btm/btm_ble_multi_adv.cc
index 22d2e17..120b384 100644
--- a/stack/btm/btm_ble_multi_adv.cc
+++ b/stack/btm/btm_ble_multi_adv.cc
@@ -170,7 +170,7 @@
                    weak_factory_.GetWeakPtr()));
   }
 
-  ~BleAdvertisingManagerImpl() { adv_inst.clear(); }
+  ~BleAdvertisingManagerImpl() override { adv_inst.clear(); }
 
   void GetOwnAddress(uint8_t inst_id, GetAddressCallback cb) override {
     cb.Run(adv_inst[inst_id].own_address_type, adv_inst[inst_id].own_address);
diff --git a/stack/test/ble_advertiser_test.cc b/stack/test/ble_advertiser_test.cc
index 9c7d5a5..952aa41 100644
--- a/stack/test/ble_advertiser_test.cc
+++ b/stack/test/ble_advertiser_test.cc
@@ -131,7 +131,7 @@
                    cmd_complete);
   };
 
-  bool QuirkAdvertiserZeroHandle() { return false; }
+  bool QuirkAdvertiserZeroHandle() override { return false; }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(AdvertiserHciMock);
@@ -153,7 +153,7 @@
 
   std::unique_ptr<AdvertiserHciMock> hci_mock;
 
-  virtual void SetUp() {
+  void SetUp() override {
     hci_mock.reset(new AdvertiserHciMock());
 
     base::Callback<void(uint8_t)> inst_cnt_Cb;
@@ -168,7 +168,7 @@
     inst_cnt_Cb.Run(num_adv_instances);
   }
 
-  virtual void TearDown() {
+  void TearDown() override {
     BleAdvertisingManager::CleanUp();
     hci_mock.reset();
   }
diff --git a/stack/test/gatt_connection_manager_test.cc b/stack/test/gatt_connection_manager_test.cc
index eed3776..0997aa8 100644
--- a/stack/test/gatt_connection_manager_test.cc
+++ b/stack/test/gatt_connection_manager_test.cc
@@ -61,11 +61,11 @@
 
 namespace connection_manager {
 class BleConnectionManager : public testing::Test {
-  virtual void SetUp() {
+  void SetUp() override {
     localWhiteListMock = std::make_unique<WhiteListMock>();
   }
 
-  virtual void TearDown() {
+  void TearDown() override {
     connection_manager::reset(true);
     AlarmMock::Reset();
     localWhiteListMock.reset();
diff --git a/stack/test/stack_btu_test.cc b/stack/test/stack_btu_test.cc
index 5976bb2..2502646 100644
--- a/stack/test/stack_btu_test.cc
+++ b/stack/test/stack_btu_test.cc
@@ -89,7 +89,7 @@
   MOCK_METHOD0(TestCallback, void(void));
   base::MessageLoop* message_loop;
 
-  virtual void SetUp() {
+  void SetUp() override {
     // Initialize alarms to prevent btu_task_shut_down from crashing
     alarm_new("test alarm");
     bt_startup_thread.StartUp();
@@ -100,7 +100,7 @@
                               "BTU startup timed out"));
   }
 
-  virtual void TearDown() {
+  void TearDown() override {
     btu_task_shut_down(nullptr);
     alarm_cleanup();
     bt_startup_thread.ShutDown();
diff --git a/stack/test/stack_smp_test.cc b/stack/test/stack_smp_test.cc
index 0b1f0a9..1bb51ea 100644
--- a/stack/test/stack_smp_test.cc
+++ b/stack/test/stack_smp_test.cc
@@ -131,7 +131,7 @@
   Octet16 rand_{0x57, 0x83, 0xD5, 0x21, 0x56, 0xAD, 0x6F, 0x0E,
                 0x63, 0x88, 0x27, 0x4E, 0xC6, 0x70, 0x2E, 0xE0};
 
-  void SetUp() {
+  void SetUp() override {
     p_cb_.tk = {0};
     // Set pairing request packet to 0x070710000001(01)
     p_cb_.local_io_capability = 0x01;
@@ -151,7 +151,7 @@
     p_cb_.role = HCI_ROLE_MASTER;
     std::reverse(rand_.begin(), rand_.end());
   }
-  void TearDown() {}
+  void TearDown() override {}
 
  public:
 };
diff --git a/test/gen_coverage.py b/test/gen_coverage.py
index 3697c37..c699d3b 100755
--- a/test/gen_coverage.py
+++ b/test/gen_coverage.py
@@ -89,7 +89,7 @@
 
 WORKING_DIR = '/tmp/coverage'
 SOONG_UI_BASH = 'build/soong/soong_ui.bash'
-LLVM_DIR = 'prebuilts/clang/host/linux-x86/clang-r328903/bin'
+LLVM_DIR = 'prebuilts/clang/host/linux-x86/clang-r353983b/bin'
 LLVM_MERGE = LLVM_DIR + '/llvm-profdata'
 LLVM_COV = LLVM_DIR + '/llvm-cov'
 
diff --git a/test/rootcanal/bluetooth_hci.cc b/test/rootcanal/bluetooth_hci.cc
index 3e4297b..c8352da 100644
--- a/test/rootcanal/bluetooth_hci.cc
+++ b/test/rootcanal/bluetooth_hci.cc
@@ -51,7 +51,9 @@
  public:
   BluetoothDeathRecipient(const sp<IBluetoothHci> hci) : mHci(hci) {}
 
-  virtual void serviceDied(uint64_t /* cookie */, const wp<::android::hidl::base::V1_0::IBase>& /* who */) {
+  void serviceDied(
+      uint64_t /* cookie */,
+      const wp<::android::hidl::base::V1_0::IBase>& /* who */) override {
     ALOGE("BluetoothDeathRecipient::serviceDied - Bluetooth service died");
     has_died_ = true;
     mHci->close();
@@ -79,42 +81,63 @@
   }
 
   death_recipient_->setHasDied(false);
-  cb->linkToDeath(death_recipient_, 0);
+  auto link_ret = cb->linkToDeath(death_recipient_, 0);
+  CHECK(link_ret.isOk()) << "Error calling linkToDeath.";
 
-  test_channel_transport_.RegisterCommandHandler([this](const std::string& name, const std::vector<std::string>& args) {
-    async_manager_.ExecAsync(std::chrono::milliseconds(0),
-                             [this, name, args]() { test_channel_.HandleCommand(name, args); });
-  });
+  test_channel_transport_.RegisterCommandHandler(
+      [this](const std::string& name, const std::vector<std::string>& args) {
+        async_manager_.ExecAsync(
+            std::chrono::milliseconds(0),
+            [this, name, args]() { test_channel_.HandleCommand(name, args); });
+      });
 
   controller_ = std::make_shared<DualModeController>();
 
   controller_->Initialize({"dmc", "3C:5A:B4:01:02:03"});
 
-  controller_->RegisterEventChannel([cb](std::shared_ptr<std::vector<uint8_t>> packet) {
-    hidl_vec<uint8_t> hci_event(packet->begin(), packet->end());
-    cb->hciEventReceived(hci_event);
-  });
+  controller_->RegisterEventChannel(
+      [this, cb](std::shared_ptr<std::vector<uint8_t>> packet) {
+        hidl_vec<uint8_t> hci_event(packet->begin(), packet->end());
+        auto ret = cb->hciEventReceived(hci_event);
+        if (!ret.isOk()) {
+          CHECK(death_recipient_->getHasDied())
+              << "Error sending event callback, but no death notification.";
+        }
+      });
 
-  controller_->RegisterAclChannel([cb](std::shared_ptr<std::vector<uint8_t>> packet) {
-    hidl_vec<uint8_t> acl_packet(packet->begin(), packet->end());
-    cb->aclDataReceived(acl_packet);
-  });
+  controller_->RegisterAclChannel(
+      [this, cb](std::shared_ptr<std::vector<uint8_t>> packet) {
+        hidl_vec<uint8_t> acl_packet(packet->begin(), packet->end());
+        auto ret = cb->aclDataReceived(acl_packet);
+        if (!ret.isOk()) {
+          CHECK(death_recipient_->getHasDied())
+              << "Error sending acl callback, but no death notification.";
+        }
+      });
 
-  controller_->RegisterScoChannel([cb](std::shared_ptr<std::vector<uint8_t>> packet) {
-    hidl_vec<uint8_t> sco_packet(packet->begin(), packet->end());
-    cb->aclDataReceived(sco_packet);
-  });
+  controller_->RegisterScoChannel(
+      [this, cb](std::shared_ptr<std::vector<uint8_t>> packet) {
+        hidl_vec<uint8_t> sco_packet(packet->begin(), packet->end());
+        auto ret = cb->aclDataReceived(sco_packet);
+        if (!ret.isOk()) {
+          CHECK(death_recipient_->getHasDied())
+              << "Error sending sco callback, but no death notification.";
+        }
+      });
 
-  controller_->RegisterTaskScheduler([this](std::chrono::milliseconds delay, const TaskCallback& task) {
-    return async_manager_.ExecAsync(delay, task);
-  });
+  controller_->RegisterTaskScheduler(
+      [this](std::chrono::milliseconds delay, const TaskCallback& task) {
+        return async_manager_.ExecAsync(delay, task);
+      });
 
   controller_->RegisterPeriodicTaskScheduler(
-      [this](std::chrono::milliseconds delay, std::chrono::milliseconds period, const TaskCallback& task) {
+      [this](std::chrono::milliseconds delay, std::chrono::milliseconds period,
+             const TaskCallback& task) {
         return async_manager_.ExecAsyncPeriodically(delay, period, task);
       });
 
-  controller_->RegisterTaskCancel([this](AsyncTaskId task) { async_manager_.CancelAsyncTask(task); });
+  controller_->RegisterTaskCancel(
+      [this](AsyncTaskId task) { async_manager_.CancelAsyncTask(task); });
 
   test_model_.Reset();
   // Add the controller as a device in the model.
@@ -128,14 +151,23 @@
         6311, [this](int fd) { test_model_.IncomingLinkLayerConnection(fd); });
   }
 
-  unlink_cb_ = [cb](sp<BluetoothDeathRecipient>& death_recipient) {
+  unlink_cb_ = [this, cb](sp<BluetoothDeathRecipient>& death_recipient) {
     if (death_recipient->getHasDied())
       ALOGI("Skipping unlink call, service died.");
-    else
-      cb->unlinkToDeath(death_recipient);
+    else {
+      auto ret = cb->unlinkToDeath(death_recipient);
+      if (!ret.isOk()) {
+        CHECK(death_recipient_->getHasDied())
+            << "Error calling unlink, but no death notification.";
+      }
+    }
   };
 
-  cb->initializationComplete(Status::SUCCESS);
+  auto init_ret = cb->initializationComplete(Status::SUCCESS);
+  if (!init_ret.isOk()) {
+    CHECK(death_recipient_->getHasDied())
+        << "Error sending init callback, but no death notification.";
+  }
   return Void();
 }
 
diff --git a/vendor_libs/linux/interface/bluetooth_hci.cc b/vendor_libs/linux/interface/bluetooth_hci.cc
index 8d7e3f7..5ed2ff6 100644
--- a/vendor_libs/linux/interface/bluetooth_hci.cc
+++ b/vendor_libs/linux/interface/bluetooth_hci.cc
@@ -245,9 +245,9 @@
  public:
   BluetoothDeathRecipient(const sp<IBluetoothHci> hci) : mHci(hci) {}
 
-  virtual void serviceDied(
+  void serviceDied(
       uint64_t /*cookie*/,
-      const wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
+      const wp<::android::hidl::base::V1_0::IBase>& /*who*/) override {
     ALOGE("BluetoothDeathRecipient::serviceDied - Bluetooth service died");
     has_died_ = true;
     mHci->close();
diff --git a/vendor_libs/test_vendor_lib/desktop/test_environment.cc b/vendor_libs/test_vendor_lib/desktop/test_environment.cc
index 542738d..2822335 100644
--- a/vendor_libs/test_vendor_lib/desktop/test_environment.cc
+++ b/vendor_libs/test_vendor_lib/desktop/test_environment.cc
@@ -143,6 +143,10 @@
 
 void TestEnvironment::SetUpTestChannel() {
   int socket_fd = test_channel_transport_.SetUp(test_port_);
+  test_channel_.AddPhy({"BR_EDR"});
+  test_channel_.AddPhy({"LOW_ENERGY"});
+  test_channel_.SetTimerPeriod({"100"});
+  test_channel_.StartTimer({});
 
   test_channel_.RegisterSendResponse(
       [](const std::string& response) { ALOGI("No test channel: %s", response.c_str()); });
diff --git a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
index 273dd09..0c55e47 100644
--- a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
+++ b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
@@ -305,6 +305,9 @@
 void DualModeController::HciReset(packets::PacketView<true> args) {
   CHECK(args.size() == 0) << __func__ << " size=" << args.size();
   link_layer_controller_.Reset();
+  if (loopback_mode_ == hci::LoopbackMode::LOCAL) {
+    loopback_mode_ = hci::LoopbackMode::NO;
+  }
 
   SendCommandCompleteSuccess(OpCode::RESET);
 }
diff --git a/vendor_libs/test_vendor_lib/packets/hci/event_packet_builder.cc b/vendor_libs/test_vendor_lib/packets/hci/event_packet_builder.cc
index 010decd..8d095a7 100644
--- a/vendor_libs/test_vendor_lib/packets/hci/event_packet_builder.cc
+++ b/vendor_libs/test_vendor_lib/packets/hci/event_packet_builder.cc
@@ -412,6 +412,7 @@
   std::unique_ptr<EventPacketBuilder> evt_ptr =
       std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::LOOPBACK_COMMAND));
   CHECK(evt_ptr->AddPayloadOctets2(static_cast<uint16_t>(opcode)));
+  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(payload.size())));
   for (const auto& payload_byte : payload)  // Fill the packet.
     evt_ptr->AddPayloadOctets1(payload_byte);
   return evt_ptr;
diff --git a/vendor_libs/test_vendor_lib/packets/hci/test/acl_builder_test.cc b/vendor_libs/test_vendor_lib/packets/hci/test/acl_builder_test.cc
index 61f8415..fb34489 100644
--- a/vendor_libs/test_vendor_lib/packets/hci/test/acl_builder_test.cc
+++ b/vendor_libs/test_vendor_lib/packets/hci/test/acl_builder_test.cc
@@ -46,7 +46,7 @@
 class AclBuilderTest : public ::testing::Test {
  public:
   AclBuilderTest() = default;
-  ~AclBuilderTest() = default;
+  ~AclBuilderTest() override = default;
 };
 
 TEST(AclBuilderTest, buildAclCountTest) {
diff --git a/vendor_libs/test_vendor_lib/packets/hci/test/event_builder_test.cc b/vendor_libs/test_vendor_lib/packets/hci/test/event_builder_test.cc
index e63c866..8a11d45 100644
--- a/vendor_libs/test_vendor_lib/packets/hci/test/event_builder_test.cc
+++ b/vendor_libs/test_vendor_lib/packets/hci/test/event_builder_test.cc
@@ -38,7 +38,7 @@
 class EventBuilderTest : public ::testing::Test {
  public:
   EventBuilderTest() = default;
-  ~EventBuilderTest() = default;
+  ~EventBuilderTest() override = default;
 };
 
 TEST(EventBuilderTest, buildLeAdvertisementSmallTest) {
diff --git a/vendor_libs/test_vendor_lib/packets/test/counted_builder_test.cc b/vendor_libs/test_vendor_lib/packets/test/counted_builder_test.cc
index 8d4d53a..bedc228 100644
--- a/vendor_libs/test_vendor_lib/packets/test/counted_builder_test.cc
+++ b/vendor_libs/test_vendor_lib/packets/test/counted_builder_test.cc
@@ -38,7 +38,7 @@
 class CountedBuilderTest : public ::testing::Test {
  public:
   CountedBuilderTest() = default;
-  ~CountedBuilderTest() = default;
+  ~CountedBuilderTest() override = default;
 };
 
 TEST(CountedBuilderTest, buildCountTest) {
diff --git a/vendor_libs/test_vendor_lib/packets/test/link_layer_packet_builder_test.cc b/vendor_libs/test_vendor_lib/packets/test/link_layer_packet_builder_test.cc
index cc6c460..22ef70f 100644
--- a/vendor_libs/test_vendor_lib/packets/test/link_layer_packet_builder_test.cc
+++ b/vendor_libs/test_vendor_lib/packets/test/link_layer_packet_builder_test.cc
@@ -51,7 +51,7 @@
 class LinkLayerPacketBuilderTest : public ::testing::Test {
  public:
   LinkLayerPacketBuilderTest() = default;
-  ~LinkLayerPacketBuilderTest() = default;
+  ~LinkLayerPacketBuilderTest() override = default;
 
   Address source_{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
   Address dest_{{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}};
diff --git a/vendor_libs/test_vendor_lib/packets/test/packet_builder_test.cc b/vendor_libs/test_vendor_lib/packets/test/packet_builder_test.cc
index 60e5d95..6fab56d 100644
--- a/vendor_libs/test_vendor_lib/packets/test/packet_builder_test.cc
+++ b/vendor_libs/test_vendor_lib/packets/test/packet_builder_test.cc
@@ -51,9 +51,9 @@
  public:
   EndianBuilder(uint8_t byte, uint16_t two_bytes, uint32_t four_bytes, uint64_t eight_bytes)
       : byte_(byte), two_bytes_(two_bytes), four_bytes_(four_bytes), eight_bytes_(eight_bytes) {}
-  ~EndianBuilder() = default;
+  ~EndianBuilder() override = default;
 
-  virtual size_t size() const override {
+  size_t size() const override {
     return sizeof(signature_) + sizeof(byte_) + sizeof(two_bytes_) + sizeof(four_bytes_) + sizeof(eight_bytes_);
   }
 
@@ -65,7 +65,8 @@
     return packet;
   }
 
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
+  void Serialize(
+      std::back_insert_iterator<std::vector<uint8_t>> it) const override {
     PacketBuilder<little_endian>::insert(signature_, it);
     PacketBuilder<little_endian>::insert(byte_, it);
     PacketBuilder<little_endian>::insert(two_bytes_, it);
@@ -84,7 +85,7 @@
 class PacketBuilderEndianTest : public ::testing::Test {
  public:
   PacketBuilderEndianTest() = default;
-  ~PacketBuilderEndianTest() = default;
+  ~PacketBuilderEndianTest() override = default;
 };
 
 TEST(PacketBuilderEndianTest, insertTest) {
@@ -101,11 +102,9 @@
       vect.push_back(static_cast<T>(element));
     }
   }
-  ~VectorBuilder() = default;
+  ~VectorBuilder() override = default;
 
-  virtual size_t size() const override {
-    return vect_.size() * sizeof(T);
-  }
+  size_t size() const override { return vect_.size() * sizeof(T); }
 
   virtual const std::unique_ptr<std::vector<uint8_t>> FinalPacket() {
     std::unique_ptr<std::vector<uint8_t>> packet = std::make_unique<std::vector<uint8_t>>();
@@ -115,7 +114,8 @@
     return packet;
   }
 
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
+  void Serialize(
+      std::back_insert_iterator<std::vector<uint8_t>> it) const override {
     PacketBuilder<true>::insert_vector(vect_, it);
   }
 
@@ -131,11 +131,9 @@
       vect.push_back(static_cast<T>(element));
     }
   }
-  virtual ~InsertElementsBuilder() = default;
+  ~InsertElementsBuilder() override = default;
 
-  virtual size_t size() const override {
-    return vect_.size() * sizeof(T);
-  }
+  size_t size() const override { return vect_.size() * sizeof(T); }
 
   virtual const std::unique_ptr<std::vector<uint8_t>> FinalPacket() {
     std::unique_ptr<std::vector<uint8_t>> packet = std::make_unique<std::vector<uint8_t>>();
@@ -145,7 +143,8 @@
     return packet;
   }
 
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
+  void Serialize(
+      std::back_insert_iterator<std::vector<uint8_t>> it) const override {
     for (T elem : vect_) {
       PacketBuilder<true>::insert(elem, it);
     }
@@ -164,14 +163,14 @@
 class VectorBuilderTest : public ::testing::Test {
  public:
   VectorBuilderTest() = default;
-  ~VectorBuilderTest() = default;
+  ~VectorBuilderTest() override = default;
 
-  void SetUp() {
+  void SetUp() override {
     packet_1_ = std::shared_ptr<VectorBuilder<T>>(new VectorBuilder<T>(vector_data));
     packet_2_ = std::shared_ptr<InsertElementsBuilder<T>>(new InsertElementsBuilder<T>(vector_data));
   }
 
-  void TearDown() {
+  void TearDown() override {
     packet_1_.reset();
     packet_2_.reset();
   }
@@ -189,9 +188,9 @@
 
 class NestedBuilder : public PacketBuilder<true> {
  public:
-  ~NestedBuilder() = default;
+  ~NestedBuilder() override = default;
 
-  virtual size_t size() const override {
+  size_t size() const override {
     size_t payload_size = (payload_ ? payload_->size() : 0);
     return 1 + payload_size;
   }
@@ -212,7 +211,8 @@
     return packet;
   }
 
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
+  void Serialize(
+      std::back_insert_iterator<std::vector<uint8_t>> it) const override {
     PacketBuilder<true>::insert(level_, it);
     if (payload_) {
       payload_->Serialize(it);
diff --git a/vendor_libs/test_vendor_lib/packets/test/packet_view_test.cc b/vendor_libs/test_vendor_lib/packets/test/packet_view_test.cc
index 65cd305..b234c20 100644
--- a/vendor_libs/test_vendor_lib/packets/test/packet_view_test.cc
+++ b/vendor_libs/test_vendor_lib/packets/test/packet_view_test.cc
@@ -52,15 +52,13 @@
 class IteratorTest : public ::testing::Test {
  public:
   IteratorTest() = default;
-  ~IteratorTest() = default;
+  ~IteratorTest() override = default;
 
-  void SetUp() {
+  void SetUp() override {
     packet = std::shared_ptr<T>(new T({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())}));
   }
 
-  void TearDown() {
-    packet.reset();
-  }
+  void TearDown() override { packet.reset(); }
 
   std::shared_ptr<T> packet;
 };
@@ -71,14 +69,14 @@
 class IteratorExtractTest : public ::testing::Test {
  public:
   IteratorExtractTest() = default;
-  ~IteratorExtractTest() = default;
+  ~IteratorExtractTest() override = default;
 };
 
 template <typename T>
 class PacketViewTest : public IteratorTest<T> {
  public:
   PacketViewTest() = default;
-  ~PacketViewTest() = default;
+  ~PacketViewTest() override = default;
 };
 
 using PacketViewTypes = ::testing::Types<PacketView<true>, PacketView<false>>;
@@ -87,13 +85,13 @@
 class PacketViewMultiViewTest : public ::testing::Test {
  public:
   PacketViewMultiViewTest() = default;
-  ~PacketViewMultiViewTest() = default;
+  ~PacketViewMultiViewTest() override = default;
 };
 
 class ViewTest : public ::testing::Test {
  public:
   ViewTest() = default;
-  ~ViewTest() = default;
+  ~ViewTest() override = default;
 };
 
 TEST(IteratorExtractTest, extractLeTest) {
diff --git a/vendor_libs/test_vendor_lib/packets/test/raw_builder_test.cc b/vendor_libs/test_vendor_lib/packets/test/raw_builder_test.cc
index f67ba0b..a179b8b 100644
--- a/vendor_libs/test_vendor_lib/packets/test/raw_builder_test.cc
+++ b/vendor_libs/test_vendor_lib/packets/test/raw_builder_test.cc
@@ -38,7 +38,7 @@
 class RawBuilderTest : public ::testing::Test {
  public:
   RawBuilderTest() = default;
-  ~RawBuilderTest() = default;
+  ~RawBuilderTest() override = default;
 };
 
 TEST(RawBuilderTest, buildCountTest) {
diff --git a/vendor_libs/test_vendor_lib/test/iterator_test.cc b/vendor_libs/test_vendor_lib/test/iterator_test.cc
index 4a85068..b65b60f 100644
--- a/vendor_libs/test_vendor_lib/test/iterator_test.cc
+++ b/vendor_libs/test_vendor_lib/test/iterator_test.cc
@@ -46,15 +46,13 @@
 class IteratorTest : public ::testing::Test {
  public:
   IteratorTest() = default;
-  ~IteratorTest() = default;
+  ~IteratorTest() override = default;
 
-  void SetUp() {
+  void SetUp() override {
     packet = TestPacket::make_new_packet(complete_l2cap_packet);
   }
 
-  void TearDown() {
-    packet.reset();
-  }
+  void TearDown() override { packet.reset(); }
 
   std::shared_ptr<TestPacket> packet;
 };
diff --git a/vendor_libs/test_vendor_lib/test/l2cap_sdu_test.cc b/vendor_libs/test_vendor_lib/test/l2cap_sdu_test.cc
index 3a1cd5c..7aa0ec6 100644
--- a/vendor_libs/test_vendor_lib/test/l2cap_sdu_test.cc
+++ b/vendor_libs/test_vendor_lib/test/l2cap_sdu_test.cc
@@ -39,7 +39,7 @@
  public:
   L2capSduTest(){};
 
-  ~L2capSduTest() = default;
+  ~L2capSduTest() override = default;
 
 };  // L2capSduTest
 
diff --git a/vendor_libs/test_vendor_lib/test/link_layer_socket_device_test.cc b/vendor_libs/test_vendor_lib/test/link_layer_socket_device_test.cc
index f24bf0e..0ddd2fc 100644
--- a/vendor_libs/test_vendor_lib/test/link_layer_socket_device_test.cc
+++ b/vendor_libs/test_vendor_lib/test/link_layer_socket_device_test.cc
@@ -60,11 +60,11 @@
    public:
     MockPhyLayer(const std::function<void(std::shared_ptr<LinkLayerPacketBuilder>)>& on_receive)
         : PhyLayer(Phy::Type::LOW_ENERGY, 0, [](LinkLayerPacketView) {}), on_receive_(on_receive) {}
-    virtual void Send(const std::shared_ptr<LinkLayerPacketBuilder> packet) override {
+    void Send(const std::shared_ptr<LinkLayerPacketBuilder> packet) override {
       on_receive_(packet);
     }
-    virtual void Receive(LinkLayerPacketView) override {}
-    virtual void TimerTick() override {}
+    void Receive(LinkLayerPacketView) override {}
+    void TimerTick() override {}
 
    private:
     std::function<void(std::shared_ptr<LinkLayerPacketBuilder>)> on_receive_;
diff --git a/vendor_libs/test_vendor_lib/test/packet_builder_test.cc b/vendor_libs/test_vendor_lib/test/packet_builder_test.cc
index 60e5d95..6fab56d 100644
--- a/vendor_libs/test_vendor_lib/test/packet_builder_test.cc
+++ b/vendor_libs/test_vendor_lib/test/packet_builder_test.cc
@@ -51,9 +51,9 @@
  public:
   EndianBuilder(uint8_t byte, uint16_t two_bytes, uint32_t four_bytes, uint64_t eight_bytes)
       : byte_(byte), two_bytes_(two_bytes), four_bytes_(four_bytes), eight_bytes_(eight_bytes) {}
-  ~EndianBuilder() = default;
+  ~EndianBuilder() override = default;
 
-  virtual size_t size() const override {
+  size_t size() const override {
     return sizeof(signature_) + sizeof(byte_) + sizeof(two_bytes_) + sizeof(four_bytes_) + sizeof(eight_bytes_);
   }
 
@@ -65,7 +65,8 @@
     return packet;
   }
 
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
+  void Serialize(
+      std::back_insert_iterator<std::vector<uint8_t>> it) const override {
     PacketBuilder<little_endian>::insert(signature_, it);
     PacketBuilder<little_endian>::insert(byte_, it);
     PacketBuilder<little_endian>::insert(two_bytes_, it);
@@ -84,7 +85,7 @@
 class PacketBuilderEndianTest : public ::testing::Test {
  public:
   PacketBuilderEndianTest() = default;
-  ~PacketBuilderEndianTest() = default;
+  ~PacketBuilderEndianTest() override = default;
 };
 
 TEST(PacketBuilderEndianTest, insertTest) {
@@ -101,11 +102,9 @@
       vect.push_back(static_cast<T>(element));
     }
   }
-  ~VectorBuilder() = default;
+  ~VectorBuilder() override = default;
 
-  virtual size_t size() const override {
-    return vect_.size() * sizeof(T);
-  }
+  size_t size() const override { return vect_.size() * sizeof(T); }
 
   virtual const std::unique_ptr<std::vector<uint8_t>> FinalPacket() {
     std::unique_ptr<std::vector<uint8_t>> packet = std::make_unique<std::vector<uint8_t>>();
@@ -115,7 +114,8 @@
     return packet;
   }
 
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
+  void Serialize(
+      std::back_insert_iterator<std::vector<uint8_t>> it) const override {
     PacketBuilder<true>::insert_vector(vect_, it);
   }
 
@@ -131,11 +131,9 @@
       vect.push_back(static_cast<T>(element));
     }
   }
-  virtual ~InsertElementsBuilder() = default;
+  ~InsertElementsBuilder() override = default;
 
-  virtual size_t size() const override {
-    return vect_.size() * sizeof(T);
-  }
+  size_t size() const override { return vect_.size() * sizeof(T); }
 
   virtual const std::unique_ptr<std::vector<uint8_t>> FinalPacket() {
     std::unique_ptr<std::vector<uint8_t>> packet = std::make_unique<std::vector<uint8_t>>();
@@ -145,7 +143,8 @@
     return packet;
   }
 
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
+  void Serialize(
+      std::back_insert_iterator<std::vector<uint8_t>> it) const override {
     for (T elem : vect_) {
       PacketBuilder<true>::insert(elem, it);
     }
@@ -164,14 +163,14 @@
 class VectorBuilderTest : public ::testing::Test {
  public:
   VectorBuilderTest() = default;
-  ~VectorBuilderTest() = default;
+  ~VectorBuilderTest() override = default;
 
-  void SetUp() {
+  void SetUp() override {
     packet_1_ = std::shared_ptr<VectorBuilder<T>>(new VectorBuilder<T>(vector_data));
     packet_2_ = std::shared_ptr<InsertElementsBuilder<T>>(new InsertElementsBuilder<T>(vector_data));
   }
 
-  void TearDown() {
+  void TearDown() override {
     packet_1_.reset();
     packet_2_.reset();
   }
@@ -189,9 +188,9 @@
 
 class NestedBuilder : public PacketBuilder<true> {
  public:
-  ~NestedBuilder() = default;
+  ~NestedBuilder() override = default;
 
-  virtual size_t size() const override {
+  size_t size() const override {
     size_t payload_size = (payload_ ? payload_->size() : 0);
     return 1 + payload_size;
   }
@@ -212,7 +211,8 @@
     return packet;
   }
 
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
+  void Serialize(
+      std::back_insert_iterator<std::vector<uint8_t>> it) const override {
     PacketBuilder<true>::insert(level_, it);
     if (payload_) {
       payload_->Serialize(it);
diff --git a/vendor_libs/test_vendor_lib/test/packet_stream_unittest.cc b/vendor_libs/test_vendor_lib/test/packet_stream_unittest.cc
index 375c090..7481235 100644
--- a/vendor_libs/test_vendor_lib/test/packet_stream_unittest.cc
+++ b/vendor_libs/test_vendor_lib/test/packet_stream_unittest.cc
@@ -48,7 +48,7 @@
     CheckSocketpairInit();
   }
 
-  ~PacketStreamTest() {
+  ~PacketStreamTest() override {
     close(socketpair_fds_[0]);
     close(socketpair_fds_[1]);
   }
diff --git a/vendor_libs/test_vendor_lib/test/security_manager_unittest.cc b/vendor_libs/test_vendor_lib/test/security_manager_unittest.cc
index 8fa3a03..13aab3c 100644
--- a/vendor_libs/test_vendor_lib/test/security_manager_unittest.cc
+++ b/vendor_libs/test_vendor_lib/test/security_manager_unittest.cc
@@ -39,7 +39,7 @@
 class SecurityManagerTest : public ::testing::Test {
  public:
   SecurityManagerTest() {}
-  ~SecurityManagerTest() {}
+  ~SecurityManagerTest() override {}
 };
 
 TEST_F(SecurityManagerTest, WriteKey) {