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([©](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) {