Merge "L2CAP rename Segmenter to Sender"
diff --git a/gd/Android.bp b/gd/Android.bp
index e7a389a..19eaec3 100644
--- a/gd/Android.bp
+++ b/gd/Android.bp
@@ -358,6 +358,24 @@
     ],
 }
 
+genrule {
+    name: "BluetoothGeneratedPackets_python3_cc",
+    tools: [
+        "bluetooth_packetgen",
+    ],
+    cmd: "$(location bluetooth_packetgen) --include=system/bt/gd --out=$(genDir) $(in)",
+    srcs: [
+        "hci/hci_packets.pdl",
+        "l2cap/l2cap_packets.pdl",
+        "security/smp_packets.pdl",
+    ],
+    out: [
+        "hci/hci_packets_python3.cc",
+        "l2cap/l2cap_packets_python3.cc",
+        "security/smp_packets_python3.cc",
+    ],
+}
+
 filegroup {
     name: "BluetoothFacadeProto",
     srcs: [
@@ -523,3 +541,87 @@
         "l2cap/classic/cert/api.pb.cc",
     ],
 }
+
+cc_defaults {
+  name: "bluetooth_py3_native_extension_defaults",
+  include_dirs: [
+    "external/python/cpython3/Include",
+  ],
+  target: {
+      android: {
+          include_dirs: ["external/python/cpython3/android/bionic/pyconfig"],
+      },
+      android_arm: {
+          cflags: ["-DSOABI=\"cpython-38android-arm-android-bionic\""],
+          suffix: ".cpython-38android-arm-android-bionic",
+      },
+      android_arm64: {
+          cflags: ["-DSOABI=\"cpython-38android-arm64-android-bionic\""],
+          suffix: ".cpython-38android-arm64-android-bionic",
+      },
+      android_x86: {
+          cflags: ["-DSOABI=\"cpython-38android-x86-android-bionic\""],
+          suffix: ".cpython-38android-x86-android-bionic",
+      },
+      android_x86_64: {
+          cflags: ["-DSOABI=\"cpython-38android-x86_64-android-bionic\""],
+          suffix: ".cpython-38android-x86_64-android-bionic",
+      },
+      // Regenerate include dirs with android_regen.sh
+      darwin_x86_64: {
+          include_dirs: ["external/python/cpython3/android/darwin_x86_64/pyconfig"],
+          cflags: [
+              "-Wno-deprecated-declarations",
+              "-Wno-pointer-arith",
+              "-DSOABI=\"cpython-38android-x86_64-darwin\"",
+          ],
+          suffix: ".cpython-38android-x86_64-darwin",
+      },
+      linux_bionic: {
+          // NB linux_bionic is a 'host' architecture but it uses the bionic libc like 'android'
+          // targets so use the android pyconfig.
+          include_dirs: ["external/python/cpython3/android/bionic/pyconfig"],
+          cflags: ["-DSOABI=\"cpython-38android-x86_64-linux-bionic\""],
+          suffix: ".cpython-38android-x86_64-linux-bionic",
+      },
+      linux_glibc_x86: {
+          enabled: false,
+      },
+      linux_glibc_x86_64: {
+          include_dirs: ["external/python/cpython3/android/linux_x86_64/pyconfig"],
+          cflags: ["-DSOABI=\"cpython-38android-x86_64-linux-gnu\""],
+          suffix: ".cpython-38android-x86_64-linux-gnu",
+      },
+      windows: {
+          enabled: false,
+      },
+  },
+  allow_undefined_symbols: true,
+}
+
+cc_library{
+  name: "bluetooth_packets_python3",
+  defaults: [
+    "gd_defaults",
+    "bluetooth_py3_native_extension_defaults"
+  ],
+  host_supported: true,
+  srcs: [
+    "packet/python3_module.cc",
+    "l2cap/fcs.cc",
+    ":BluetoothPacketSources",
+  ],
+  generated_headers: [
+    "BluetoothGeneratedPackets_h",
+  ],
+  generated_sources: [
+    "BluetoothGeneratedPackets_python3_cc",
+  ],
+  header_libs: [
+    "pybind11_headers",
+  ],
+  cflags: [
+    "-fexceptions",
+  ],
+  rtti: true,
+}
diff --git a/gd/hci/hci_packets.pdl b/gd/hci/hci_packets.pdl
index 00d1c9d..b591ec9 100644
--- a/gd/hci/hci_packets.pdl
+++ b/gd/hci/hci_packets.pdl
@@ -1562,6 +1562,10 @@
   _payload_,  // placeholder (unimplemented)
 }
 
+packet WriteVoiceSettingComplete : CommandComplete (command_op_code = WRITE_VOICE_SETTING) {
+  status : ErrorCode,
+}
+
 packet ReadAutomaticFlushTimeout : ConnectionManagementCommand (op_code = READ_AUTOMATIC_FLUSH_TIMEOUT) {
   connection_handle : 12,
   _reserved_ : 4,
@@ -2363,6 +2367,9 @@
 packet LeCreateConnectionCancel : LeConnectionManagementCommand (op_code = LE_CREATE_CONNECTION_CANCEL) {
 }
 
+packet LeCreateConnectionCancelStatus : CommandStatus (command_op_code = LE_CREATE_CONNECTION_CANCEL) {
+}
+
 packet LeCreateConnectionCancelComplete : CommandComplete (command_op_code = LE_CREATE_CONNECTION_CANCEL) {
   status : ErrorCode,
 }
@@ -2451,6 +2458,9 @@
   ltk: 8[16],
 }
 
+packet LeStartEncryptionStatus : CommandStatus (command_op_code = LE_START_ENCRYPTION) {
+}
+
 packet LeLongTermKeyRequestReply : LeSecurityCommand (op_code = LE_LONG_TERM_KEY_REQUEST_REPLY) {
   connection_handle: 16,
   long_term_key: 8[16],
@@ -2513,14 +2523,26 @@
   _payload_,  // placeholder (unimplemented)
 }
 
+packet LeAddDeviceToResolvingListComplete : CommandComplete (command_op_code = LE_ADD_DEVICE_TO_RESOLVING_LIST) {
+  status : ErrorCode,
+}
+
 packet LeRemoveDeviceFromResolvingList : LeSecurityCommand (op_code = LE_REMOVE_DEVICE_FROM_RESOLVING_LIST) {
   _payload_,  // placeholder (unimplemented)
 }
 
+packet LeRemoveDeviceFromResolvingListComplete : CommandComplete (command_op_code = LE_REMOVE_DEVICE_FROM_RESOLVING_LIST) {
+  status : ErrorCode,
+}
+
 packet LeClearResolvingList : LeSecurityCommand (op_code = LE_CLEAR_RESOLVING_LIST) {
   _payload_,  // placeholder (unimplemented)
 }
 
+packet LeClearResolvingListComplete : CommandComplete (command_op_code = LE_CLEAR_RESOLVING_LIST) {
+  status : ErrorCode,
+}
+
 packet LeReadResolvingListSize : LeSecurityCommand (op_code = LE_READ_RESOLVING_LIST_SIZE) {
   _payload_,  // placeholder (unimplemented)
 }
@@ -2710,6 +2732,9 @@
   _payload_,  // placeholder (unimplemented)
 }
 
+packet LeSetPrivacyModeComplete : CommandComplete (command_op_code = LE_SET_PRIVACY_MODE) {
+  status : ErrorCode,
+}
 
   // VENDOR_SPECIFIC
 packet LeGetVendorCapabilities : VendorCommand (op_code = LE_GET_VENDOR_CAPABILITIES) {
diff --git a/gd/packet/parser/enum_gen.cc b/gd/packet/parser/enum_gen.cc
index 50d5f81..4bd5a26 100644
--- a/gd/packet/parser/enum_gen.cc
+++ b/gd/packet/parser/enum_gen.cc
@@ -20,7 +20,7 @@
 
 #include "util.h"
 
-EnumGen::EnumGen(EnumDef e) : e_(e) {}
+EnumGen::EnumGen(EnumDef e) : e_(std::move(e)) {}
 
 void EnumGen::GenDefinition(std::ostream& stream) {
   stream << "enum class ";
@@ -33,6 +33,14 @@
   stream << "};\n";
 }
 
+void EnumGen::GenDefinitionPybind11(std::ostream& stream) {
+  stream << "py::enum_<" << e_.name_ << ">(m, \"" << e_.name_ << "\")";
+  for (const auto& pair : e_.constants_) {
+    stream << ".value(\"" << pair.second << "\", " << e_.name_ << "::" << pair.second << ")";
+  }
+  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) {";
diff --git a/gd/packet/parser/enum_gen.h b/gd/packet/parser/enum_gen.h
index f3483dd..be16559 100644
--- a/gd/packet/parser/enum_gen.h
+++ b/gd/packet/parser/enum_gen.h
@@ -27,6 +27,8 @@
 
   void GenDefinition(std::ostream& stream);
 
+  void GenDefinitionPybind11(std::ostream& stream);
+
   void GenLogging(std::ostream& stream);
 
   EnumDef e_;
diff --git a/gd/packet/parser/main.cc b/gd/packet/parser/main.cc
index a53d71d..05475b6 100644
--- a/gd/packet/parser/main.cc
+++ b/gd/packet/parser/main.cc
@@ -230,6 +230,100 @@
   return true;
 }
 
+bool generate_pybind11_sources_one_file(const Declarations& decls, const std::filesystem::path& input_file,
+                                        const std::filesystem::path& include_dir, const std::filesystem::path& out_dir,
+                                        const std::string& root_namespace) {
+  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_relative_header = gen_relative_path / (input_filename + ".h");
+  auto gen_file = gen_path / (input_filename + "_python3.cc");
+
+  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;
+  }
+
+  out_file << "#include <pybind11/pybind11.h>\n";
+  out_file << "#include <pybind11/stl.h>\n";
+  out_file << "\n\n";
+  out_file << "#include " << gen_relative_header << "\n";
+  out_file << "\n\n";
+
+  std::vector<std::string> namespace_list;
+  parse_namespace(root_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_) {
+    if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM ||
+        c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
+      const auto* custom_def = dynamic_cast<const CustomFieldDef*>(c.second);
+      custom_def->GenUsing(out_file);
+    }
+  }
+  out_file << "\n\n";
+
+  out_file << "using ::bluetooth::packet::BasePacketBuilder;";
+  out_file << "using ::bluetooth::packet::BitInserter;";
+  out_file << "using ::bluetooth::packet::CustomTypeChecker;";
+  out_file << "using ::bluetooth::packet::Iterator;";
+  out_file << "using ::bluetooth::packet::kLittleEndian;";
+  out_file << "using ::bluetooth::packet::PacketBuilder;";
+  out_file << "using ::bluetooth::packet::BaseStruct;";
+  out_file << "using ::bluetooth::packet::PacketStruct;";
+  out_file << "using ::bluetooth::packet::PacketView;";
+  out_file << "using ::bluetooth::packet::parser::ChecksumTypeChecker;";
+  out_file << "\n\n";
+
+  out_file << "namespace py = pybind11;\n\n";
+
+  out_file << "void define_" << input_filename << "_submodule(py::module& parent) {\n\n";
+  out_file << "py::module m = parent.def_submodule(\"" << input_filename << "\", \"A submodule of " << input_filename
+           << "\");\n\n";
+
+  for (const auto& e : decls.type_defs_queue_) {
+    if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
+      const auto* enum_def = dynamic_cast<const EnumDef*>(e.second);
+      EnumGen gen(*enum_def);
+      gen.GenDefinitionPybind11(out_file);
+      out_file << "\n\n";
+    }
+  }
+
+  for (const auto& s : decls.type_defs_queue_) {
+    if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
+      const auto* struct_def = dynamic_cast<const StructDef*>(s.second);
+      struct_def->GenDefinitionPybind11(out_file);
+      out_file << "\n";
+    }
+  }
+
+  for (const auto& packet_def : decls.packet_defs_queue_) {
+    packet_def.second.GenParserDefinitionPybind11(out_file);
+    out_file << "\n\n";
+  }
+
+  for (const auto& p : decls.packet_defs_queue_) {
+    p.second.GenBuilderDefinitionPybind11(out_file);
+    out_file << "\n\n";
+  }
+
+  out_file << "}\n\n";
+
+  generate_namespace_close(namespace_list, out_file);
+
+  out_file.close();
+
+  return true;
+}
+
 }  // namespace
 
 // TODO(b/141583809): stop leaks
@@ -273,6 +367,10 @@
       std::cerr << "Didn't generate cpp headers for " << input_files.front() << std::endl;
       return 3;
     }
+    if (!generate_pybind11_sources_one_file(declarations, input_files.front(), include_dir, out_dir, root_namespace)) {
+      std::cerr << "Didn't generate pybind11 sources for " << input_files.front() << std::endl;
+      return 4;
+    }
     input_files.pop();
   }
 
diff --git a/gd/packet/parser/packet_def.cc b/gd/packet/parser/packet_def.cc
index 1252a1b..2fd4df2 100644
--- a/gd/packet/parser/packet_def.cc
+++ b/gd/packet/parser/packet_def.cc
@@ -84,6 +84,42 @@
   s << "};\n";
 }
 
+void PacketDef::GenParserDefinitionPybind11(std::ostream& s) const {
+  s << "py::class_<" << name_ << "View";
+  if (parent_ != nullptr) {
+    s << ", " << parent_->name_ << "View";
+  } else {
+    s << ", PacketView<" << (is_little_endian_ ? "" : "!") << "kLittleEndian>";
+  }
+  s << ">(m, \"" << name_ << "View\")";
+  if (parent_ != nullptr) {
+    s << ".def(py::init([](" << parent_->name_ << "View parent) {";
+  } else {
+    s << ".def(py::init([](PacketView<" << (is_little_endian_ ? "" : "!") << "kLittleEndian> parent) {";
+  }
+  s << "auto view =" << name_ << "View::Create(std::move(parent));";
+  s << "if (!view.IsValid()) { throw std::invalid_argument(\"Bad packet view\"); }";
+  s << "return view; }))";
+
+  s << ".def(py::init(&" << name_ << "View::Create))";
+  std::set<std::string> protected_field_types = {
+      FixedScalarField::kFieldType,
+      FixedEnumField::kFieldType,
+      SizeField::kFieldType,
+      CountField::kFieldType,
+  };
+  const auto& public_fields = fields_.GetFieldsWithoutTypes(protected_field_types);
+  for (const auto& field : public_fields) {
+    auto getter_func_name = field->GetGetterFunctionName();
+    if (getter_func_name.empty()) {
+      continue;
+    }
+    s << ".def(\"" << getter_func_name << "\", &" << name_ << "View::" << getter_func_name << ")";
+  }
+  s << ".def(\"IsValid\", &" << name_ << "View::IsValid)";
+  s << ";\n";
+}
+
 void PacketDef::GenParserFieldGetter(std::ostream& s, const PacketField* field) const {
   // Start field offset
   auto start_field_offset = GetOffsetForField(field->GetName(), false);
@@ -290,6 +326,29 @@
   s << "\n";
 }
 
+void PacketDef::GenBuilderDefinitionPybind11(std::ostream& s) const {
+  s << "py::class_<" << name_ << "Builder";
+  if (parent_ != nullptr) {
+    s << ", " << parent_->name_ << "Builder";
+  } else {
+    if (is_little_endian_) {
+      s << ", PacketBuilder<kLittleEndian>";
+    } else {
+      s << ", PacketBuilder<!kLittleEndian>";
+    }
+  }
+  s << ">(m, \"" << name_ << "Builder\")";
+  if (!fields_.HasBody()) {
+    GenBuilderCreatePybind11(s);
+  }
+  s << ".def(\"Serialize\", [](" << name_ << "Builder& builder){";
+  s << "std::vector<uint8_t> bytes;";
+  s << "BitInserter bi(bytes);";
+  s << "builder.Serialize(bi);";
+  s << "return bytes;})";
+  s << ";\n";
+}
+
 void PacketDef::GenTestDefine(std::ostream& s) const {
   s << "#ifdef PACKET_TESTING\n";
   s << "#define DEFINE_AND_INSTANTIATE_" << name_ << "ReflectionTest(...)";
@@ -433,6 +492,81 @@
   s << "}\n";
 }
 
+void PacketDef::GenBuilderCreatePybind11(std::ostream& s) const {
+  s << ".def(py::init([](";
+  auto params = GetParamList();
+  std::vector<std::string> constructor_args;
+  std::vector<std::string> keep_alive_args;
+  int i = 1;
+  for (const auto& param : params) {
+    i++;
+    std::stringstream ss;
+    auto param_type = param->GetBuilderParameterType();
+    if (param_type.empty()) {
+      continue;
+    }
+    // Use shared_ptr instead of unique_ptr for the Python interface
+    if (param->BuilderParameterMustBeMoved()) {
+      param_type = util::StringFindAndReplaceAll(param_type, "unique_ptr", "shared_ptr");
+      keep_alive_args.push_back(std::to_string(i));
+    }
+    ss << param_type << " " << param->GetName();
+    constructor_args.push_back(ss.str());
+  }
+  s << util::StringJoin(",", constructor_args) << "){";
+
+  // Deal with move only args
+  for (const auto& param : params) {
+    std::stringstream ss;
+    auto param_type = param->GetBuilderParameterType();
+    if (param_type.empty()) {
+      continue;
+    }
+    if (!param->BuilderParameterMustBeMoved()) {
+      continue;
+    }
+    auto move_only_param_name = param->GetName() + "_move_only";
+    s << param_type << " " << move_only_param_name << ";";
+    if (param->IsContainerField()) {
+      // Assume single layer container
+      s << "for (size_t i = 0; i < " << param->GetName() << ".size(); i++) {";
+      if (param->GetFieldType() == VectorField::kFieldType) {
+        s << move_only_param_name << ".emplace_back(" << param->GetName() << "[i].get());";
+      } else if (param->GetFieldType() == ArrayField::kFieldType) {
+        s << move_only_param_name << "[i].reset(" << param->GetName() << "[i].get());";
+      } else {
+        ERROR() << param << " is not supported by Pybind11";
+      }
+      s << "}";
+    } else {
+      // Release shared_ptr to unique_ptr and leave the Python copy as nullptr and to be garbage collected by Python
+      s << move_only_param_name << ".reset(" << param->GetName() << ".get());";
+    }
+  }
+  s << "return " << name_ << "Builder::Create(";
+  std::vector<std::string> builder_vars;
+  for (const auto& param : params) {
+    std::stringstream ss;
+    auto param_type = param->GetBuilderParameterType();
+    if (param_type.empty()) {
+      continue;
+    }
+    auto param_name = param->GetName();
+    if (param->BuilderParameterMustBeMoved()) {
+      ss << "std::move(" << param_name << "_move_only)";
+    } else {
+      ss << param_name;
+    }
+    builder_vars.push_back(ss.str());
+  }
+  s << util::StringJoin(",", builder_vars) << ");}";
+  if (keep_alive_args.empty()) {
+    s << "))";
+  } else {
+    s << "), py::keep_alive<1," << util::StringJoin(",", keep_alive_args) << ">())";
+  }
+}
+
 void PacketDef::GenBuilderParameterChecker(std::ostream& s) const {
   FieldList params_to_validate = GetParametersToValidate();
 
diff --git a/gd/packet/parser/packet_def.h b/gd/packet/parser/packet_def.h
index 1dc2f8a..e8acdc3 100644
--- a/gd/packet/parser/packet_def.h
+++ b/gd/packet/parser/packet_def.h
@@ -33,6 +33,8 @@
 
   void GenParserDefinition(std::ostream& s) const;
 
+  void GenParserDefinitionPybind11(std::ostream& s) const;
+
   void GenParserFieldGetter(std::ostream& s, const PacketField* field) const;
 
   void GenValidator(std::ostream& s) const;
@@ -41,6 +43,8 @@
 
   void GenBuilderDefinition(std::ostream& s) const;
 
+  void GenBuilderDefinitionPybind11(std::ostream& s) const;
+
   void GenTestDefine(std::ostream& s) const;
 
   void GenFuzzTestDefine(std::ostream& s) const;
@@ -49,6 +53,8 @@
 
   void GenBuilderCreate(std::ostream& s) const;
 
+  void GenBuilderCreatePybind11(std::ostream& s) const;
+
   void GenBuilderParameterChecker(std::ostream& s) const;
 
   void GenBuilderConstructor(std::ostream& s) const;
diff --git a/gd/packet/parser/struct_def.cc b/gd/packet/parser/struct_def.cc
index 3691192..bdeb780 100644
--- a/gd/packet/parser/struct_def.cc
+++ b/gd/packet/parser/struct_def.cc
@@ -180,6 +180,24 @@
   s << "\n";
 }
 
+void StructDef::GenDefinitionPybind11(std::ostream& s) const {
+  s << "py::class_<" << name_;
+  if (parent_ != nullptr) {
+    s << ", " << parent_->name_;
+  } else {
+    if (is_little_endian_) {
+      s << ", PacketStruct<kLittleEndian>";
+    } else {
+      s << ", PacketStruct<!kLittleEndian>";
+    }
+  }
+  s << ">(m, \"" << name_ << "\")";
+  s << ".def(py::init<>())";
+  s << ".def(\"Serialize\", &" << GetTypeName() << "::Serialize)";
+  s << ".def(\"Parse\", &" << name_ << "::Parse)";
+  s << ";\n";
+}
+
 void StructDef::GenConstructor(std::ostream& s) const {
   if (parent_ != nullptr) {
     s << name_ << "(const " << parent_->name_ << "& parent) : " << parent_->name_ << "(parent) {}";
diff --git a/gd/packet/parser/struct_def.h b/gd/packet/parser/struct_def.h
index c06c472..74c1b04 100644
--- a/gd/packet/parser/struct_def.h
+++ b/gd/packet/parser/struct_def.h
@@ -42,6 +42,8 @@
 
   void GenDefinition(std::ostream& s) const;
 
+  void GenDefinitionPybind11(std::ostream& s) const;
+
   void GenConstructor(std::ostream& s) const;
 
   Size GetStructOffsetForField(std::string field_name) const;
diff --git a/gd/packet/parser/util.h b/gd/packet/parser/util.h
index 982d39a..a8b881d 100644
--- a/gd/packet/parser/util.h
+++ b/gd/packet/parser/util.h
@@ -114,4 +114,24 @@
   return std::regex_match(value, enum_regex);
 }
 
+inline std::string StringJoin(const std::string& delimiter, const std::vector<std::string>& vec) {
+  std::stringstream ss;
+  for (size_t i = 0; i < vec.size(); i++) {
+    ss << vec[i];
+    if (i != (vec.size() - 1)) {
+      ss << delimiter;
+    }
+  }
+  return ss.str();
+}
+
+inline std::string StringFindAndReplaceAll(std::string text, const std::string& old, const std::string& replacement) {
+  auto pos = text.find(old);
+  while (pos != std::string::npos) {
+    text.replace(pos, old.size(), replacement);
+    pos = text.find(old, pos + replacement.size());
+  }
+  return text;
+}
+
 }  // namespace util
diff --git a/gd/packet/python3_module.cc b/gd/packet/python3_module.cc
new file mode 100644
index 0000000..879732b
--- /dev/null
+++ b/gd/packet/python3_module.cc
@@ -0,0 +1,84 @@
+/*
+ * 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 <cstring>
+#include <memory>
+
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+
+#include "packet/base_packet_builder.h"
+#include "packet/bit_inserter.h"
+#include "packet/iterator.h"
+#include "packet/packet_builder.h"
+#include "packet/packet_struct.h"
+#include "packet/packet_view.h"
+#include "packet/parser/checksum_type_checker.h"
+#include "packet/parser/custom_type_checker.h"
+
+namespace py = pybind11;
+
+namespace bluetooth {
+
+namespace hci {
+void define_hci_packets_submodule(py::module&);
+}
+namespace l2cap {
+void define_l2cap_packets_submodule(py::module&);
+}
+namespace security {
+void define_smp_packets_submodule(py::module&);
+}
+
+namespace packet {
+
+using ::bluetooth::packet::BasePacketBuilder;
+using ::bluetooth::packet::BaseStruct;
+using ::bluetooth::packet::BitInserter;
+using ::bluetooth::packet::CustomTypeChecker;
+using ::bluetooth::packet::Iterator;
+using ::bluetooth::packet::kLittleEndian;
+using ::bluetooth::packet::PacketBuilder;
+using ::bluetooth::packet::PacketStruct;
+using ::bluetooth::packet::PacketView;
+using ::bluetooth::packet::parser::ChecksumTypeChecker;
+
+PYBIND11_MODULE(bluetooth_packets_python3, m) {
+  py::class_<BasePacketBuilder>(m, "BasePacketBuilder");
+  py::class_<PacketBuilder<kLittleEndian>, BasePacketBuilder>(m, "PacketBuilderLittleEndian");
+  py::class_<PacketBuilder<!kLittleEndian>, BasePacketBuilder>(m, "PacketBuilderBigEndian");
+  py::class_<BaseStruct>(m, "BaseStruct");
+  py::class_<PacketStruct<kLittleEndian>, BaseStruct>(m, "PacketStructLittleEndian");
+  py::class_<PacketStruct<!kLittleEndian>, BaseStruct>(m, "PacketStructBigEndian");
+  py::class_<Iterator<kLittleEndian>>(m, "IteratorLittleEndian");
+  py::class_<Iterator<!kLittleEndian>>(m, "IteratorBigEndian");
+  py::class_<PacketView<kLittleEndian>>(m, "PacketViewLittleEndian").def(py::init([](std::vector<uint8_t> bytes) {
+    // Make a copy
+    auto bytes_shared = std::make_shared<std::vector<uint8_t>>(bytes);
+    return std::make_unique<PacketView<kLittleEndian>>(bytes_shared);
+  }));
+  py::class_<PacketView<!kLittleEndian>>(m, "PacketViewBigEndian").def(py::init([](std::vector<uint8_t> bytes) {
+    // Make a copy
+    auto bytes_shared = std::make_shared<std::vector<uint8_t>>(bytes);
+    return std::make_unique<PacketView<!kLittleEndian>>(bytes_shared);
+  }));
+
+  bluetooth::hci::define_hci_packets_submodule(m);
+  bluetooth::l2cap::define_l2cap_packets_submodule(m);
+  bluetooth::security::define_smp_packets_submodule(m);
+}
+
+}  // namespace packet
+}  // namespace bluetooth
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 961eae7..b729b38 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
@@ -674,6 +674,7 @@
 void DualModeController::HciWriteSecureConnectionHostSupport(
     packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 1, "%s  size=%zu", __func__, args.size());
+  properties_.SetExtendedFeatures(properties_.GetExtendedFeatures(1) | 0x8, 1);
   SendCommandCompleteSuccess(
       bluetooth::hci::OpCode::WRITE_SECURE_CONNECTIONS_HOST_SUPPORT);
 }
diff --git a/vendor_libs/test_vendor_lib/model/devices/beacon.cc b/vendor_libs/test_vendor_lib/model/devices/beacon.cc
index 77491d9..96f6099 100644
--- a/vendor_libs/test_vendor_lib/model/devices/beacon.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/beacon.cc
@@ -70,8 +70,7 @@
     std::shared_ptr<model::packets::LinkLayerPacketBuilder> to_send =
         std::move(ad);
 
-    std::vector<std::shared_ptr<PhyLayer>> le_phys = phy_layers_[Phy::Type::LOW_ENERGY];
-    for (std::shared_ptr<PhyLayer> phy : le_phys) {
+    for (auto phy : phy_layers_[Phy::Type::LOW_ENERGY]) {
       phy->Send(to_send);
     }
   }
@@ -88,8 +87,7 @@
     std::shared_ptr<model::packets::LinkLayerPacketBuilder> to_send =
         std::move(scan_response);
 
-    std::vector<std::shared_ptr<PhyLayer>> le_phys = phy_layers_[Phy::Type::LOW_ENERGY];
-    for (auto phy : le_phys) {
+    for (auto phy : phy_layers_[Phy::Type::LOW_ENERGY]) {
       phy->Send(to_send);
     }
   }
diff --git a/vendor_libs/test_vendor_lib/model/devices/car_kit.cc b/vendor_libs/test_vendor_lib/model/devices/car_kit.cc
index 0445312..f3fc3e6 100644
--- a/vendor_libs/test_vendor_lib/model/devices/car_kit.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/car_kit.cc
@@ -43,7 +43,7 @@
 
   properties_.SetPageScanRepetitionMode(0);
   properties_.SetClassOfDevice(0x600420);
-  properties_.SetSupportedFeatures(0x8779ff9bfe8defff);
+  properties_.SetExtendedFeatures(0x8779ff9bfe8defff, 0);
   properties_.SetExtendedInquiryData({
       16,  // length
       9,   // Type: Device Name
diff --git a/vendor_libs/test_vendor_lib/model/devices/classic.cc b/vendor_libs/test_vendor_lib/model/devices/classic.cc
index 0838bd5..e8df43a 100644
--- a/vendor_libs/test_vendor_lib/model/devices/classic.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/classic.cc
@@ -32,7 +32,7 @@
                                       'g', 'D', 'e', 'v', 'i', 'c', 'e', '-', 'c', 'l', 'a', 's', 's', 'i', 'c',
                                       '\0'});  // End of data
   properties_.SetPageScanRepetitionMode(0);
-  properties_.SetSupportedFeatures(0x87593F9bFE8FFEFF);
+  properties_.SetExtendedFeatures(0x87593F9bFE8FFEFF, 0);
 
   page_scan_delay_ms_ = std::chrono::milliseconds(600);
 }
diff --git a/vendor_libs/test_vendor_lib/model/devices/device.cc b/vendor_libs/test_vendor_lib/model/devices/device.cc
index 4a03da0..9fdcd5e 100644
--- a/vendor_libs/test_vendor_lib/model/devices/device.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/device.cc
@@ -42,10 +42,10 @@
 }
 
 void Device::UnregisterPhyLayer(Phy::Type phy_type, uint32_t factory_id) {
-  for (size_t i = 0; i < phy_layers_[phy_type].size(); i++) {
-    if (phy_layers_[phy_type][i]->IsFactoryId(factory_id)) {
-      phy_layers_[phy_type][i]->Unregister();
-      phy_layers_[phy_type].erase(phy_layers_[phy_type].begin() + i);
+  for (const auto phy_layer : phy_layers_[phy_type]) {
+    if (phy_layer->IsFactoryId(factory_id)) {
+      phy_layer->Unregister();
+      phy_layers_[phy_type].remove(phy_layer);
     }
   }
 }
diff --git a/vendor_libs/test_vendor_lib/model/devices/device.h b/vendor_libs/test_vendor_lib/model/devices/device.h
index 90901df..b985888 100644
--- a/vendor_libs/test_vendor_lib/model/devices/device.h
+++ b/vendor_libs/test_vendor_lib/model/devices/device.h
@@ -18,6 +18,7 @@
 
 #include <chrono>
 #include <cstdint>
+#include <list>
 #include <map>
 #include <string>
 #include <vector>
@@ -86,7 +87,7 @@
                                    Phy::Type phy_type);
 
  protected:
-  std::map<Phy::Type, std::vector<std::shared_ptr<PhyLayer>>> phy_layers_;
+  std::map<Phy::Type, std::list<std::shared_ptr<PhyLayer>>> phy_layers_;
 
   std::chrono::steady_clock::time_point last_advertisement_;
 
diff --git a/vendor_libs/test_vendor_lib/model/devices/device_properties.h b/vendor_libs/test_vendor_lib/model/devices/device_properties.h
index d787bb9..b9d03ba 100644
--- a/vendor_libs/test_vendor_lib/model/devices/device_properties.h
+++ b/vendor_libs/test_vendor_lib/model/devices/device_properties.h
@@ -49,8 +49,9 @@
     return extended_features_[0];
   }
 
-  void SetSupportedFeatures(uint64_t features) {
-    extended_features_[0] = features;
+  void SetExtendedFeatures(uint64_t features, uint8_t page_number) {
+    ASSERT(page_number < extended_features_.size());
+    extended_features_[page_number] = features;
   }
 
   // Specification Version 4.2, Volume 2, Part E, Section 7.4.4
@@ -311,7 +312,7 @@
   std::vector<uint8_t> supported_codecs_;
   std::vector<uint32_t> vendor_specific_codecs_;
   std::vector<uint8_t> supported_commands_;
-  std::vector<uint64_t> extended_features_{{0x875b3fd8fe8ffeff, 0x07}};
+  std::vector<uint64_t> extended_features_{{0x875b3fd8fe8ffeff, 0x0f}};
   ClassOfDevice class_of_device_{{0, 0, 0}};
   std::vector<uint8_t> extended_inquiry_data_;
   std::vector<uint8_t> name_;
diff --git a/vendor_libs/test_vendor_lib/model/devices/loopback.cc b/vendor_libs/test_vendor_lib/model/devices/loopback.cc
index dd8d3ed..df0c762 100644
--- a/vendor_libs/test_vendor_lib/model/devices/loopback.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/loopback.cc
@@ -80,8 +80,7 @@
     std::shared_ptr<model::packets::LinkLayerPacketBuilder> to_send =
         std::move(scan_response);
 
-    std::vector<std::shared_ptr<PhyLayer>> le_phys = phy_layers_[Phy::Type::LOW_ENERGY];
-    for (auto phy : le_phys) {
+    for (auto phy : phy_layers_[Phy::Type::LOW_ENERGY]) {
       LOG_INFO("Sending a Scan Response on a Phy");
       phy->Send(to_send);
     }
diff --git a/vendor_libs/test_vendor_lib/model/devices/sniffer.cc b/vendor_libs/test_vendor_lib/model/devices/sniffer.cc
index c4586d1..efdca87 100644
--- a/vendor_libs/test_vendor_lib/model/devices/sniffer.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/sniffer.cc
@@ -47,8 +47,10 @@
   if (!match_source && !match_dest) {
     return;
   }
-  LOG_INFO("%s %s -> %s (Type %d)", (match_source ? (match_dest ? "<->" : "<--") : "-->"), source.ToString().c_str(),
-           dest.ToString().c_str(), static_cast<int>(packet.GetType()));
+  LOG_INFO("%s %s -> %s (Type %s)",
+           (match_source ? (match_dest ? "<->" : "<--") : "-->"),
+           source.ToString().c_str(), dest.ToString().c_str(),
+           model::packets::PacketTypeText(packet.GetType()).c_str());
 }
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/setup/phy_layer.h b/vendor_libs/test_vendor_lib/model/setup/phy_layer.h
index 9f2bc7a..7f0fa33 100644
--- a/vendor_libs/test_vendor_lib/model/setup/phy_layer.h
+++ b/vendor_libs/test_vendor_lib/model/setup/phy_layer.h
@@ -25,8 +25,12 @@
  public:
   PhyLayer(Phy::Type phy_type, uint32_t id,
            const std::function<void(model::packets::LinkLayerPacketView)>&
-               device_receive)
-      : phy_type_(phy_type), id_(id), transmit_to_device_(device_receive) {}
+               device_receive,
+           uint32_t device_id)
+      : phy_type_(phy_type),
+        id_(id),
+        device_id_(device_id),
+        transmit_to_device_(device_receive) {}
 
   virtual void Send(
       const std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet) = 0;
@@ -48,11 +52,14 @@
     return id_;
   }
 
+  uint32_t GetDeviceId() { return device_id_; }
+
   virtual ~PhyLayer() = default;
 
  private:
   Phy::Type phy_type_;
   uint32_t id_;
+  uint32_t device_id_;
 
  protected:
   const std::function<void(model::packets::LinkLayerPacketView)>
diff --git a/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.cc b/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.cc
index e1da9f3..b963ce0 100644
--- a/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.cc
+++ b/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.cc
@@ -15,6 +15,7 @@
  */
 
 #include "phy_layer_factory.h"
+#include <sstream>
 
 namespace test_vendor_lib {
 
@@ -31,10 +32,11 @@
 
 std::shared_ptr<PhyLayer> PhyLayerFactory::GetPhyLayer(
     const std::function<void(model::packets::LinkLayerPacketView)>&
-        device_receive) {
-  std::shared_ptr<PhyLayer> new_phy =
-      std::make_shared<PhyLayerImpl>(phy_type_, next_id_++, device_receive,
-                                     std::shared_ptr<PhyLayerFactory>(this));
+        device_receive,
+    uint32_t device_id) {
+  std::shared_ptr<PhyLayer> new_phy = std::make_shared<PhyLayerImpl>(
+      phy_type_, next_id_++, device_receive, device_id,
+      std::shared_ptr<PhyLayerFactory>(this));
   phy_layers_.push_back(new_phy);
   return new_phy;
 }
@@ -63,16 +65,12 @@
       model::packets::LinkLayerPacketView::Create(packet_view);
   ASSERT(link_layer_packet_view.IsValid());
 
-  for (const auto phy : phy_layers_) {
-    if (id != phy->GetId()) {
-      phy->Receive(link_layer_packet_view);
-    }
-  }
+  Send(link_layer_packet_view, id);
 }
 
 void PhyLayerFactory::Send(model::packets::LinkLayerPacketView packet,
                            uint32_t id) {
-  for (const auto phy : phy_layers_) {
+  for (const auto& phy : phy_layers_) {
     if (id != phy->GetId()) {
       phy->Receive(packet);
     }
@@ -80,30 +78,37 @@
 }
 
 void PhyLayerFactory::TimerTick() {
-  for (auto phy : phy_layers_) {
+  for (auto& phy : phy_layers_) {
     phy->TimerTick();
   }
 }
 
 std::string PhyLayerFactory::ToString() const {
+  std::stringstream factory;
   switch (phy_type_) {
     case Phy::Type::LOW_ENERGY:
-      return "LOW_ENERGY";
+      factory << "LOW_ENERGY: ";
       break;
     case Phy::Type::BR_EDR:
-      return "BR_EDR";
+      factory << "BR_EDR: ";
       break;
     default:
-      return "Unknown";
+      factory << "Unknown: ";
   }
+  for (auto& phy : phy_layers_) {
+    factory << phy->GetDeviceId();
+    factory << ",";
+  }
+
+  return factory.str();
 }
 
 PhyLayerImpl::PhyLayerImpl(
     Phy::Type phy_type, uint32_t id,
     const std::function<void(model::packets::LinkLayerPacketView)>&
         device_receive,
-    const std::shared_ptr<PhyLayerFactory>& factory)
-    : PhyLayer(phy_type, id, device_receive), factory_(factory) {}
+    uint32_t device_id, const std::shared_ptr<PhyLayerFactory> factory)
+    : PhyLayer(phy_type, id, device_receive, device_id), factory_(factory) {}
 
 PhyLayerImpl::~PhyLayerImpl() {
   Unregister();
diff --git a/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.h b/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.h
index 003236a..7d46733 100644
--- a/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.h
+++ b/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.h
@@ -39,7 +39,8 @@
 
   std::shared_ptr<PhyLayer> GetPhyLayer(
       const std::function<void(model::packets::LinkLayerPacketView)>&
-          device_receive);
+          device_receive,
+      uint32_t device_id);
 
   void UnregisterPhyLayer(uint32_t id);
 
@@ -65,17 +66,20 @@
   PhyLayerImpl(Phy::Type phy_type, uint32_t id,
                const std::function<void(model::packets::LinkLayerPacketView)>&
                    device_receive,
-               const std::shared_ptr<PhyLayerFactory>& factory);
+               uint32_t device_id,
+               const std::shared_ptr<PhyLayerFactory> factory);
   virtual ~PhyLayerImpl() override;
 
   virtual void Send(
       const std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet)
       override;
-  virtual void Send(model::packets::LinkLayerPacketView packet) override;
-  virtual void Receive(model::packets::LinkLayerPacketView packet) override;
-  virtual void Unregister() override;
-  virtual bool IsFactoryId(uint32_t factory_id) override;
-  virtual void TimerTick() override;
+  void Send(model::packets::LinkLayerPacketView packet) override;
+  void Receive(model::packets::LinkLayerPacketView packet) override;
+  void Unregister() override;
+  bool IsFactoryId(uint32_t factory_id) override;
+  void TimerTick() override;
+
+  uint32_t device_id_;
 
  private:
   std::shared_ptr<PhyLayerFactory> factory_;
diff --git a/vendor_libs/test_vendor_lib/model/setup/test_model.cc b/vendor_libs/test_vendor_lib/model/setup/test_model.cc
index 2a7e5a5..688b7dc 100644
--- a/vendor_libs/test_vendor_lib/model/setup/test_model.cc
+++ b/vendor_libs/test_vendor_lib/model/setup/test_model.cc
@@ -134,7 +134,8 @@
   dev->RegisterPhyLayer(phy->second->GetPhyLayer(
       [dev](model::packets::LinkLayerPacketView packet) {
         dev->IncomingPacket(packet);
-      }));
+      },
+      device->first));
 }
 
 void TestModel::DelDeviceFromPhy(size_t dev_index, size_t phy_index) {
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl b/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
index a7e6614..0c27d97 100644
--- a/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
+++ b/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
@@ -6,26 +6,38 @@
 enum PacketType : 8 {
     UNKNOWN = 0x00,
     ACL = 0x01,
-    COMMAND = 0x02,
-    DISCONNECT = 0x03,
-    ENCRYPT_CONNECTION = 0x04,
-    ENCRYPT_CONNECTION_RESPONSE = 0x05,
-    EVENT = 0x06,
-    INQUIRY = 0x07,
-    INQUIRY_RESPONSE = 0x08,
-    IO_CAPABILITY_REQUEST = 0x09,
-    IO_CAPABILITY_RESPONSE = 0x0A,
-    IO_CAPABILITY_NEGATIVE_RESPONSE = 0x0B,
-    LE_ADVERTISEMENT = 0x0C,
-    LE_CONNECT = 0x0D,
-    LE_CONNECT_COMPLETE = 0x0E,
-    LE_SCAN = 0x0F,
-    LE_SCAN_RESPONSE = 0x10,
-    PAGE = 0x11,
-    PAGE_RESPONSE = 0x12,
-    PAGE_REJECT = 0x13,
-    RESPONSE = 0x14,
-    SCO = 0x15,
+    DISCONNECT = 0x02,
+    ENCRYPT_CONNECTION = 0x03,
+    ENCRYPT_CONNECTION_RESPONSE = 0x04,
+    EVENT = 0x05,
+    INQUIRY = 0x06,
+    INQUIRY_RESPONSE = 0x07,
+    IO_CAPABILITY_REQUEST = 0x08,
+    IO_CAPABILITY_RESPONSE = 0x09,
+    IO_CAPABILITY_NEGATIVE_RESPONSE = 0x0A,
+    LE_ADVERTISEMENT = 0x0B,
+    LE_CONNECT = 0x0C,
+    LE_CONNECT_COMPLETE = 0x0D,
+    LE_SCAN = 0x0E,
+    LE_SCAN_RESPONSE = 0x0F,
+    PAGE = 0x10,
+    PAGE_RESPONSE = 0x11,
+    PAGE_REJECT = 0x12,
+    READ_CLOCK_OFFSET = 0x13,
+    READ_CLOCK_OFFSET_RESPONSE = 0x14,
+    READ_REMOTE_SUPPORTED_FEATURES = 0x15,
+    READ_REMOTE_SUPPORTED_FEATURES_RESPONSE = 0x16,
+    READ_REMOTE_LMP_FEATURES = 0x17,
+    READ_REMOTE_LMP_FEATURES_RESPONSE = 0x18,
+    READ_REMOTE_EXTENDED_FEATURES = 0x19,
+    READ_REMOTE_EXTENDED_FEATURES_RESPONSE = 0x1A,
+    READ_REMOTE_VERSION_INFORMATION = 0x1B,
+    READ_REMOTE_VERSION_INFORMATION_RESPONSE = 0x1C,
+    REMOTE_NAME_REQUEST = 0x1D,
+    REMOTE_NAME_REQUEST_RESPONSE = 0x1E,
+    SCO = 0x1F,
+    COMMAND = 0x23, // Remove
+    RESPONSE = 0x24, // Remove
 }
 
 packet LinkLayerPacket {
@@ -35,11 +47,16 @@
   _body_,
 }
 
-packet AclPacket : LinkLayerPacket (type = ACL) {
+packet Command : LinkLayerPacket (type = COMMAND) {
   _payload_,
 }
 
-packet Command : LinkLayerPacket (type = COMMAND) {
+packet Response : LinkLayerPacket (type = RESPONSE) {
+  opcode : 16,
+  _payload_,
+}
+
+packet AclPacket : LinkLayerPacket (type = ACL) {
   _payload_,
 }
 
@@ -165,7 +182,56 @@
   reason : 8,
 }
 
-packet Response : LinkLayerPacket (type = RESPONSE) {
-  opcode : 16,
+packet ReadClockOffset : LinkLayerPacket (type = READ_CLOCK_OFFSET) {
+}
+
+packet ReadClockOffsetResponse : LinkLayerPacket (type = READ_CLOCK_OFFSET_RESPONSE) {
+  offset : 16,
+}
+
+packet ReadRemoteSupportedFeatures : LinkLayerPacket (type = READ_REMOTE_SUPPORTED_FEATURES) {
+}
+
+packet ReadRemoteSupportedFeaturesResponse : LinkLayerPacket (type = READ_REMOTE_SUPPORTED_FEATURES_RESPONSE) {
+  features : 64,
+}
+
+packet ReadRemoteLmpFeatures : LinkLayerPacket (type = READ_REMOTE_LMP_FEATURES) {
+}
+
+packet ReadRemoteLmpFeaturesResponse : LinkLayerPacket (type = READ_REMOTE_LMP_FEATURES_RESPONSE) {
+  features : 64,
+}
+
+packet ReadRemoteExtendedFeatures : LinkLayerPacket (type = READ_REMOTE_EXTENDED_FEATURES) {
+  page_number : 8,
+}
+
+packet ReadRemoteExtendedFeaturesResponse : LinkLayerPacket (type = READ_REMOTE_EXTENDED_FEATURES_RESPONSE) {
+  status : 8,
+  page_number : 8,
+  max_page_number : 8,
+  features : 64,
+}
+
+packet ReadRemoteVersionInformation : LinkLayerPacket (type = READ_REMOTE_VERSION_INFORMATION) {
+}
+
+packet ReadRemoteVersionInformationResponse : LinkLayerPacket (type = READ_REMOTE_VERSION_INFORMATION_RESPONSE) {
+  lmp_version : 8,
+  lmp_subversion : 8,
+  manufacturer_name : 16,
+}
+
+packet RemoteNameRequest : LinkLayerPacket (type = REMOTE_NAME_REQUEST) {
+}
+
+packet RemoteNameRequestResponse : LinkLayerPacket (type = REMOTE_NAME_REQUEST_RESPONSE) {
+  _size_(name) : 8,
+  name : 8[],
+}
+
+packet ScoPacket : LinkLayerPacket (type = SCO) {
   _payload_,
 }
+