Root-Canal: Handle eSCO disconnection requests

Bug: 209800014
Tag: #feature
Test: cert/run
Change-Id: I16891dd50a40a36544149fc731cdb84947c80871
diff --git a/system/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.cc b/system/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.cc
index cd5af7a..d8bc019 100644
--- a/system/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.cc
+++ b/system/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.cc
@@ -31,8 +31,12 @@
   return acl_connections_.count(handle) != 0;
 }
 
+bool AclConnectionHandler::HasScoHandle(uint16_t handle) const {
+  return sco_connections_.count(handle) != 0;
+}
+
 uint16_t AclConnectionHandler::GetUnusedHandle() {
-  while (HasHandle(last_handle_) ||
+  while (HasHandle(last_handle_) || HasScoHandle(last_handle_) ||
          isochronous_connection_handler_.HasHandle(last_handle_)) {
     last_handle_ = (last_handle_ + 1) % kReservedHandle;
   }
@@ -131,7 +135,18 @@
 }
 
 bool AclConnectionHandler::Disconnect(uint16_t handle) {
-  return acl_connections_.erase(handle) > 0;
+  if (HasScoHandle(handle)) {
+    sco_connections_.erase(handle);
+    return true;
+  }
+  if (HasHandle(handle)) {
+    // It is the responsibility of the caller to remove SCO connections
+    // with connected peer first.
+    ASSERT(GetScoHandle(GetAddress(handle).GetAddress()) == 0);
+    acl_connections_.erase(handle);
+    return true;
+  }
+  return false;
 }
 
 uint16_t AclConnectionHandler::GetHandle(AddressWithType addr) const {
@@ -154,12 +169,17 @@
 }
 
 AddressWithType AclConnectionHandler::GetAddress(uint16_t handle) const {
-  ASSERT_LOG(HasHandle(handle), "Handle unknown %hd", handle);
+  ASSERT_LOG(HasHandle(handle), "Unknown handle %hd", handle);
   return acl_connections_.at(handle).GetAddress();
 }
 
+Address AclConnectionHandler::GetScoAddress(uint16_t handle) const {
+  ASSERT_LOG(HasScoHandle(handle), "Unknown SCO handle %hd", handle);
+  return sco_connections_.at(handle).GetAddress();
+}
+
 AddressWithType AclConnectionHandler::GetOwnAddress(uint16_t handle) const {
-  ASSERT_LOG(HasHandle(handle), "Handle unknown %hd", handle);
+  ASSERT_LOG(HasHandle(handle), "Unknown handle %hd", handle);
   return acl_connections_.at(handle).GetOwnAddress();
 }
 
diff --git a/system/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.h b/system/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.h
index 83b51bf..9928137 100644
--- a/system/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.h
+++ b/system/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.h
@@ -63,10 +63,12 @@
                               bluetooth::hci::AddressWithType own_addr);
   bool Disconnect(uint16_t handle);
   bool HasHandle(uint16_t handle) const;
+  bool HasScoHandle(uint16_t handle) const;
 
   uint16_t GetHandle(bluetooth::hci::AddressWithType addr) const;
   uint16_t GetHandleOnlyAddress(bluetooth::hci::Address addr) const;
   bluetooth::hci::AddressWithType GetAddress(uint16_t handle) const;
+  bluetooth::hci::Address GetScoAddress(uint16_t handle) const;
   bluetooth::hci::AddressWithType GetOwnAddress(uint16_t handle) const;
 
   void Encrypt(uint16_t handle);
diff --git a/system/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc b/system/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
index 60caf9f..c86269b 100644
--- a/system/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
+++ b/system/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
@@ -568,6 +568,7 @@
 void DualModeController::ReadLocalSupportedFeatures(CommandView command) {
   auto command_view = gd_hci::ReadLocalSupportedFeaturesView::Create(command);
   ASSERT(command_view.IsValid());
+
   auto packet =
       bluetooth::hci::ReadLocalSupportedFeaturesCompleteBuilder::Create(
           kNumCommandPackets, ErrorCode::SUCCESS,
diff --git a/system/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc b/system/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc
index f2f9e64..27db9ee 100644
--- a/system/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc
+++ b/system/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc
@@ -16,6 +16,7 @@
 
 #include "link_layer_controller.h"
 
+#include <cinttypes>
 #include <hci/hci_packets.h>
 
 #include "crypto_toolbox/crypto_toolbox.h"
@@ -390,6 +391,9 @@
     case model::packets::PacketType::ESCO_CONNECTION_RESPONSE:
       IncomingEScoConnectionResponse(incoming);
       break;
+    case model::packets::PacketType::ESCO_DISCONNECT:
+      IncomingEScoDisconnect(incoming);
+      break;
 
     default:
       LOG_WARN("Dropping unhandled packet of type %s",
@@ -627,8 +631,7 @@
              "GetHandle() returned invalid handle %hx", handle);
 
   uint8_t reason = disconnect.GetReason();
-  ScheduleTask(kShortDelayMs,
-               [this, handle, reason]() { DisconnectCleanup(handle, reason); });
+  SendDisconnectionCompleteEvent(handle, reason);
 }
 
 void LinkLayerController::IncomingEncryptConnection(
@@ -1370,23 +1373,37 @@
 
   Address address = incoming.GetSourceAddress();
   auto request = model::packets::EScoConnectionRequestView::Create(incoming);
+  ASSERT(request.IsValid());
+
+  LOG_INFO("Received eSCO connection request from %s",
+           address.ToString().c_str());
 
   // Automatically reject if connection request was already sent
   // from the current device.
   if (connections_.HasPendingScoConnection(address)) {
-    auto packet = model::packets::EScoConnectionResponseBuilder::Create(
-      properties_.GetLeAddress(), address,
-      (uint8_t)ErrorCode::SYNCHRONOUS_CONNECTION_LIMIT_EXCEEDED, 0, 0, 0, 0, 0);
-    SendLinkLayerPacket(std::move(packet));
+    LOG_INFO("Rejecting eSCO connection request from %s, "
+             "an eSCO connection already exist with this device",
+             address.ToString().c_str());
+
+    SendLinkLayerPacket(model::packets::EScoConnectionResponseBuilder::Create(
+        properties_.GetLeAddress(), address,
+        (uint8_t)ErrorCode::SYNCHRONOUS_CONNECTION_LIMIT_EXCEEDED,
+        0, 0, 0, 0, 0));
     return;
   }
 
-  // Send connection request event to use and wait for Accept or Reject
-  // command.
-  auto packet = bluetooth::hci::ConnectionRequestBuilder::Create(
+  // Create local connection context.
+  ScoConnectionParameters connection_parameters = {
+    request.GetTransmitBandwidth(), request.GetReceiveBandwidth(),
+    request.GetMaxLatency(), request.GetVoiceSetting(),
+    request.GetRetransmissionEffort(), request.GetPacketType()
+  };
+  connections_.CreatePendingScoConnection(address, connection_parameters);
+
+  // Send connection request event and wait for Accept or Reject command.
+  send_event_(bluetooth::hci::ConnectionRequestBuilder::Create(
       address, ClassOfDevice(),
-      bluetooth::hci::ConnectionRequestLinkType::ESCO);
-  send_event_(std::move(packet));
+      bluetooth::hci::ConnectionRequestLinkType::ESCO));
 }
 
 void LinkLayerController::IncomingEScoConnectionResponse(
@@ -1394,8 +1411,12 @@
 
   Address address = incoming.GetSourceAddress();
   auto response = model::packets::EScoConnectionResponseView::Create(incoming);
+  ASSERT(response.IsValid());
   auto status = response.GetStatus();
 
+  LOG_INFO("Received eSCO connection response with status %" PRIx8 " from %s",
+           status, incoming.GetSourceAddress().ToString().c_str());
+
   if (status == (uint8_t)ErrorCode::SUCCESS) {
     ScoLinkParameters link_parameters = {
       response.GetTransmissionInterval(),
@@ -1405,23 +1426,38 @@
       response.GetAirMode(),
     };
     connections_.AcceptPendingScoConnection(address, link_parameters);
-    auto packet = bluetooth::hci::SynchronousConnectionCompleteBuilder::Create(
-      ErrorCode(status), connections_.GetScoHandle(address), address,
-      bluetooth::hci::ScoLinkType::ESCO,
-      response.GetTransmissionInterval(),
-      response.GetRetransmissionWindow(),
-      response.GetRxPacketLength(),
-      response.GetTxPacketLength(),
-      bluetooth::hci::ScoAirMode(response.GetAirMode()));
-
-    send_event_(std::move(packet));
+    send_event_(bluetooth::hci::SynchronousConnectionCompleteBuilder::Create(
+        ErrorCode(status), connections_.GetScoHandle(address), address,
+        bluetooth::hci::ScoLinkType::ESCO,
+        response.GetTransmissionInterval(),
+        response.GetRetransmissionWindow(),
+        response.GetRxPacketLength(),
+        response.GetTxPacketLength(),
+        bluetooth::hci::ScoAirMode(response.GetAirMode())));
   } else {
     connections_.CancelPendingScoConnection(address);
-    auto packet = bluetooth::hci::SynchronousConnectionCompleteBuilder::Create(
-      ErrorCode(status), 0, address, bluetooth::hci::ScoLinkType::ESCO,
-      0, 0, 0, 0, bluetooth::hci::ScoAirMode::TRANSPARENT);
+    send_event_(bluetooth::hci::SynchronousConnectionCompleteBuilder::Create(
+        ErrorCode(status), 0, address, bluetooth::hci::ScoLinkType::ESCO,
+        0, 0, 0, 0, bluetooth::hci::ScoAirMode::TRANSPARENT));
+  }
+}
 
-    send_event_(std::move(packet));
+void LinkLayerController::IncomingEScoDisconnect(
+    model::packets::LinkLayerPacketView incoming) {
+
+  Address address = incoming.GetSourceAddress();
+  auto request = model::packets::EScoDisconnectView::Create(incoming);
+  ASSERT(request.IsValid());
+  auto reason = request.GetReason();
+  uint16_t handle = connections_.GetScoHandle(address);
+
+  LOG_INFO("Received eSCO disconnection request with"
+           " reason 0x%" PRIx8 " from %s",
+           reason, incoming.GetSourceAddress().ToString().c_str());
+
+  if (handle != 0) {
+    connections_.Disconnect(handle);
+    SendDisconnectionCompleteEvent(handle, reason);
   }
 }
 
@@ -2546,39 +2582,67 @@
   return ErrorCode::SUCCESS;
 }
 
+void LinkLayerController::SendDisconnectionCompleteEvent(
+    uint16_t handle, uint8_t reason)
+{
+  if (properties_.IsUnmasked(EventCode::DISCONNECTION_COMPLETE)) {
+    ScheduleTask(kShortDelayMs, [this, handle, reason]() {
+      send_event_(bluetooth::hci::DisconnectionCompleteBuilder::Create(
+          ErrorCode::SUCCESS, handle, ErrorCode(reason)));
+    });
+  }
+}
+
 ErrorCode LinkLayerController::Disconnect(uint16_t handle, uint8_t reason) {
+  if (connections_.HasScoHandle(handle)) {
+    const Address remote = connections_.GetScoAddress(handle);
+    LOG_INFO("Disconnecting eSCO connection with %s",
+             remote.ToString().c_str());
+
+    SendLinkLayerPacket(model::packets::EScoDisconnectBuilder::Create(
+        properties_.GetAddress(), remote, reason));
+
+    connections_.Disconnect(handle);
+    SendDisconnectionCompleteEvent(handle, reason);
+    return ErrorCode::SUCCESS;
+  }
+
   if (!connections_.HasHandle(handle)) {
     return ErrorCode::UNKNOWN_CONNECTION;
   }
 
   const AddressWithType remote = connections_.GetAddress(handle);
+
   if (connections_.GetPhyType(handle) == Phy::Type::BR_EDR) {
+    LOG_INFO("Disconnecting ACL connection with %s",
+             remote.ToString().c_str());
+
+    uint16_t sco_handle = connections_.GetScoHandle(remote.GetAddress());
+    if (sco_handle != 0) {
+      SendLinkLayerPacket(model::packets::EScoDisconnectBuilder::Create(
+          properties_.GetAddress(), remote.GetAddress(), reason));
+
+      connections_.Disconnect(sco_handle);
+      SendDisconnectionCompleteEvent(sco_handle, reason);
+    }
+
     SendLinkLayerPacket(model::packets::DisconnectBuilder::Create(
         properties_.GetAddress(), remote.GetAddress(), reason));
+
   } else {
+    LOG_INFO("Disconnecting LE connection with %s",
+             remote.ToString().c_str());
+
     SendLeLinkLayerPacket(model::packets::DisconnectBuilder::Create(
         connections_.GetOwnAddress(handle).GetAddress(), remote.GetAddress(),
         reason));
   }
-  ASSERT_LOG(connections_.Disconnect(handle), "Disconnecting %hx", handle);
 
-  ScheduleTask(kShortDelayMs, [this, handle]() {
-    DisconnectCleanup(
-        handle,
-        static_cast<uint8_t>(ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST));
-  });
-
+  connections_.Disconnect(handle);
+  SendDisconnectionCompleteEvent(handle, reason);
   return ErrorCode::SUCCESS;
 }
 
-void LinkLayerController::DisconnectCleanup(uint16_t handle, uint8_t reason) {
-  // TODO: Clean up other connection state.
-  if (properties_.IsUnmasked(EventCode::DISCONNECTION_COMPLETE)) {
-    send_event_(bluetooth::hci::DisconnectionCompleteBuilder::Create(
-        ErrorCode::SUCCESS, handle, static_cast<ErrorCode>(reason)));
-  }
-}
-
 ErrorCode LinkLayerController::ChangeConnectionPacketType(uint16_t handle,
                                                           uint16_t types) {
   if (!connections_.HasHandle(handle)) {
@@ -3515,6 +3579,7 @@
   if (!connections_.HasHandle(connection_handle)) {
     return ErrorCode::UNKNOWN_CONNECTION;
   }
+
   Address bd_addr = connections_.GetAddress(connection_handle).GetAddress();
   if (connections_.HasPendingScoConnection(bd_addr)) {
     // This command may be used to modify an exising eSCO link.
@@ -3523,6 +3588,8 @@
     return ErrorCode::COMMAND_DISALLOWED;
   }
 
+  LOG_INFO("Creating eSCO connection with %s", bd_addr.ToString().c_str());
+
   // Save connection parameters.
   ScoConnectionParameters connection_parameters = {
     transmit_bandwidth, receive_bandwidth, max_latency,
@@ -3533,11 +3600,10 @@
     connection_parameters);
 
   // Send eSCO connection request to peer.
-  auto packet = model::packets::EScoConnectionRequestBuilder::Create(
-    properties_.GetAddress(), bd_addr,
-    transmit_bandwidth, receive_bandwidth, max_latency,
-    voice_setting, retransmission_effort, packet_types);
-  SendLinkLayerPacket(std::move(packet));
+  SendLinkLayerPacket(model::packets::EScoConnectionRequestBuilder::Create(
+      properties_.GetAddress(), bd_addr,
+      transmit_bandwidth, receive_bandwidth, max_latency,
+      voice_setting, retransmission_effort, packet_types));
   return ErrorCode::SUCCESS;
 }
 
@@ -3550,7 +3616,12 @@
     uint8_t retransmission_effort,
     uint16_t packet_types) {
 
+  LOG_INFO("Accepting eSCO connection request from %s",
+           bd_addr.ToString().c_str());
+
   if (!connections_.HasPendingScoConnection(bd_addr)) {
+    LOG_INFO("No pending eSCO connection for %s",
+             bd_addr.ToString().c_str());
     return ErrorCode::COMMAND_DISALLOWED;
   }
 
@@ -3571,27 +3642,26 @@
   }
 
   // Send eSCO connection response to peer.
-  auto packet = model::packets::EScoConnectionResponseBuilder::Create(
-    properties_.GetAddress(), bd_addr, (uint8_t)status,
-    link_parameters.transmission_interval,
-    link_parameters.retransmission_window,
-    link_parameters.rx_packet_length,
-    link_parameters.tx_packet_length,
-    link_parameters.air_mode);
-  SendLinkLayerPacket(std::move(packet));
+  SendLinkLayerPacket(model::packets::EScoConnectionResponseBuilder::Create(
+      properties_.GetAddress(), bd_addr, (uint8_t)status,
+      link_parameters.transmission_interval,
+      link_parameters.retransmission_window,
+      link_parameters.rx_packet_length,
+      link_parameters.tx_packet_length,
+      link_parameters.air_mode));
 
   // Schedule HCI Synchronous Connection Complete event.
   ScheduleTask(kShortDelayMs,
     [this, status, sco_handle, bd_addr, link_parameters]() {
-      auto packet = bluetooth::hci::SynchronousConnectionCompleteBuilder::Create(
-        ErrorCode(status), sco_handle, bd_addr,
-        bluetooth::hci::ScoLinkType::ESCO,
-        link_parameters.transmission_interval,
-        link_parameters.retransmission_window,
-        link_parameters.rx_packet_length,
-        link_parameters.tx_packet_length,
-        bluetooth::hci::ScoAirMode(link_parameters.air_mode));
-    send_event_(std::move(packet));
+      send_event_(
+          bluetooth::hci::SynchronousConnectionCompleteBuilder::Create(
+            ErrorCode(status), sco_handle, bd_addr,
+            bluetooth::hci::ScoLinkType::ESCO,
+            link_parameters.transmission_interval,
+            link_parameters.retransmission_window,
+            link_parameters.rx_packet_length,
+            link_parameters.tx_packet_length,
+            bluetooth::hci::ScoAirMode(link_parameters.air_mode)));
   });
 
   return ErrorCode::SUCCESS;
@@ -3601,6 +3671,9 @@
     Address bd_addr,
     uint16_t reason) {
 
+  LOG_INFO("Rejecting eSCO connection request from %s",
+           bd_addr.ToString().c_str());
+
   if (reason == (uint8_t)ErrorCode::SUCCESS) {
     reason = (uint8_t)ErrorCode::REMOTE_USER_TERMINATED_CONNECTION;
   }
@@ -3611,16 +3684,14 @@
   connections_.CancelPendingScoConnection(bd_addr);
 
   // Send eSCO connection response to peer.
-  auto packet = model::packets::EScoConnectionResponseBuilder::Create(
-    properties_.GetAddress(), bd_addr, reason, 0, 0, 0, 0, 0);
-  SendLinkLayerPacket(std::move(packet));
+  SendLinkLayerPacket(model::packets::EScoConnectionResponseBuilder::Create(
+      properties_.GetAddress(), bd_addr, reason, 0, 0, 0, 0, 0));
 
   // Schedule HCI Synchronous Connection Complete event.
   ScheduleTask(kShortDelayMs, [this, reason, bd_addr]() {
-    auto packet = bluetooth::hci::SynchronousConnectionCompleteBuilder::Create(
-      ErrorCode(reason), 0, bd_addr, bluetooth::hci::ScoLinkType::ESCO,
-      0, 0, 0, 0, bluetooth::hci::ScoAirMode::TRANSPARENT);
-    send_event_(std::move(packet));
+    send_event_(bluetooth::hci::SynchronousConnectionCompleteBuilder::Create(
+        ErrorCode(reason), 0, bd_addr, bluetooth::hci::ScoLinkType::ESCO,
+        0, 0, 0, 0, bluetooth::hci::ScoAirMode::TRANSPARENT));
   });
 
   return ErrorCode::SUCCESS;
diff --git a/system/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h b/system/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h
index 23c36b4..71bfd5e 100644
--- a/system/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h
+++ b/system/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h
@@ -95,7 +95,7 @@
   ErrorCode Disconnect(uint16_t handle, uint8_t reason);
 
  private:
-  void DisconnectCleanup(uint16_t handle, uint8_t reason);
+  void SendDisconnectionCompleteEvent(uint16_t handle, uint8_t reason);
 
  public:
   void IncomingPacket(model::packets::LinkLayerPacketView incoming);
@@ -446,6 +446,7 @@
       model::packets::LinkLayerPacketView packet);
   void IncomingEScoConnectionResponse(
       model::packets::LinkLayerPacketView packet);
+  void IncomingEScoDisconnect(model::packets::LinkLayerPacketView packet);
 
  private:
   const DeviceProperties& properties_;
diff --git a/system/vendor_libs/test_vendor_lib/model/controller/sco_connection.cc b/system/vendor_libs/test_vendor_lib/model/controller/sco_connection.cc
index 2d080c9..ab5e0cb 100644
--- a/system/vendor_libs/test_vendor_lib/model/controller/sco_connection.cc
+++ b/system/vendor_libs/test_vendor_lib/model/controller/sco_connection.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <cinttypes>
 #include <vector>
 
 #include <hci/hci_packets.h>
@@ -68,21 +69,24 @@
     if (tx.length == 0)
       continue;
 
-    unsigned tx_count = (transmit_bandwidth + tx.length - 1) / tx.length;
-    unsigned tx_max_interval = 1600 / tx_count;
+    unsigned tx_max_interval = (1600 * tx.length) / transmit_bandwidth;
 
     for (auto rx : accepted_packets) {
       if (rx.length == 0)
         continue;
 
-      unsigned rx_count = (receive_bandwidth + rx.length - 1) / rx.length;
-      unsigned rx_max_interval = 1600 / rx_count;
+      LOG_INFO("Testing combination %u/%u : %u/%u",
+               tx.length, tx.slots, rx.length, rx.slots);
+
+      unsigned rx_max_interval = (1600 * rx.length) / receive_bandwidth;
 
       // Choose the best interval satisfying both.
       unsigned transmission_interval = std::min(tx_max_interval, rx_max_interval);
       transmission_interval -= transmission_interval % 2;
       transmission_interval = std::min(transmission_interval, 254u);
 
+      LOG_INFO("Transmission interval: %u slots", transmission_interval);
+
       // Compute retransmission window.
       unsigned retransmission_window =
         retransmission_effort == (uint8_t)RetransmissionEffort::NO_RETRANSMISSION ? 0 :
@@ -91,6 +95,8 @@
         retransmission_effort == (uint8_t)RetransmissionEffort::OPTIMIZED_FOR_LINK_QUALITY ?
             rx.slots + tx.slots : 0;
 
+      LOG_INFO("Retransmission window: %u slots", retransmission_window);
+
       // Compute transmission window and validate latency.
       unsigned transmission_window = tx.slots + rx.slots +
         retransmission_window;
@@ -102,7 +108,10 @@
 
       // Compute and validate latency.
       unsigned latency = (transmission_window * 1250) / 2;
-      if (latency > max_latency)
+
+      LOG_INFO("Latency: %u us (max %u us)", latency, max_latency * 1000u);
+
+      if (latency > (1000 * max_latency))
         // Oops
         continue;
 
@@ -112,17 +121,28 @@
         (double)transmission_window / (double)transmission_interval;
 
       if (bandwidth_usage < best_bandwidth_usage) {
+        LOG_INFO("Valid combination!");
+
         uint16_t tx_packet_length =
             (transmit_bandwidth * transmission_interval + 1600 - 1) / 1600;
         uint16_t rx_packet_length =
             (receive_bandwidth * transmission_interval + 1600 - 1) / 1600;
-        uint8_t air_mode = voice_setting & 0x3;
+
+        uint8_t air_coding = voice_setting & 0x3;
+        uint8_t air_coding_to_air_mode[] = {
+          0x02, // CVSD
+          0x00, // u-law
+          0x01, // A-law
+          0x03, // transparent data
+        };
 
         best_bandwidth_usage = bandwidth_usage;
         best_parameters = {
             (uint8_t)transmission_interval,
             (uint8_t)retransmission_window,
-            rx_packet_length, tx_packet_length, air_mode };
+            rx_packet_length, tx_packet_length,
+            air_coding_to_air_mode[air_coding]
+        };
       }
     }
   }
@@ -134,26 +154,28 @@
 
   if (peer.transmit_bandwidth != 0xffff &&
       peer.transmit_bandwidth != parameters_.receive_bandwidth) {
-    LOG_WARN("transmit bandwidth requirements cannot be met");
+    LOG_WARN("Transmit bandwidth requirements cannot be met");
     return false;
   }
 
   if (peer.receive_bandwidth != 0xffff &&
       peer.receive_bandwidth != parameters_.transmit_bandwidth) {
-    LOG_WARN("receive bandwidth requirements cannot be met");
+    LOG_WARN("Receive bandwidth requirements cannot be met");
     return false;
   }
 
   if (peer.voice_setting != parameters_.voice_setting) {
-    LOG_WARN("voice setting requirements cannot be met");
+    LOG_WARN("Voice setting requirements cannot be met");
     return false;
   }
 
-  uint16_t packet_type = peer.packet_type & parameters_.packet_type & 0x3f;
-  packet_type |= ~peer.packet_type & ~parameters_.packet_type & 0x3c0;
+  uint16_t packet_type = (peer.packet_type & parameters_.packet_type) & 0x3f;
+  packet_type |= (peer.packet_type | parameters_.packet_type) & 0x3c0;
 
   if (packet_type == 0) {
-    LOG_WARN("packet type requirements cannot be met");
+    LOG_WARN("Packet type requirements cannot be met");
+    LOG_WARN("Remote packet type: %" PRIx16, parameters_.packet_type);
+    LOG_WARN("Local packet type: %" PRIx16, peer.packet_type);
     return false;
   }
 
@@ -170,7 +192,7 @@
     retransmission_effort = peer.retransmission_effort;
   else if (peer.retransmission_effort == (uint8_t)RetransmissionEffort::NO_RETRANSMISSION ||
            parameters_.retransmission_effort == (uint8_t)RetransmissionEffort::NO_RETRANSMISSION) {
-    LOG_WARN("retransmission effort requirements cannot be met");
+    LOG_WARN("Retransmission effort requirements cannot be met");
     return false;
   } else {
     retransmission_effort = (uint8_t)RetransmissionEffort::OPTIMIZED_FOR_POWER;
@@ -182,7 +204,20 @@
   };
 
   auto link_parameters = negotiated_parameters.GetLinkParameters();
-  if (link_parameters.has_value())
+  if (link_parameters.has_value()) {
     link_parameters_ = link_parameters.value();
+    LOG_INFO("Negotiated link parameters for eSCO connection:");
+    LOG_INFO("  Transmission interval: %" PRIu8 " slots",
+             link_parameters_.transmission_interval);
+    LOG_INFO("  Retransmission window: %" PRIu8 " slots",
+             link_parameters_.retransmission_window);
+    LOG_INFO("  RX packet length: %" PRIu16 " bytes",
+             link_parameters_.rx_packet_length);
+    LOG_INFO("  TX packet length: %" PRIu16 " bytes",
+             link_parameters_.tx_packet_length);
+    LOG_INFO("  Air mode: %" PRIu8, link_parameters_.air_mode);
+  } else {
+    LOG_WARN("Failed to derive link parameters");
+  }
   return link_parameters.has_value();
 }
diff --git a/system/vendor_libs/test_vendor_lib/model/devices/device_properties.h b/system/vendor_libs/test_vendor_lib/model/devices/device_properties.h
index fcac380..b52cef8 100644
--- a/system/vendor_libs/test_vendor_lib/model/devices/device_properties.h
+++ b/system/vendor_libs/test_vendor_lib/model/devices/device_properties.h
@@ -405,13 +405,12 @@
   uint8_t lmp_pal_version_;
   uint16_t manufacturer_name_;
   uint16_t lmp_pal_subversion_;
-  uint64_t supported_features_{};
   uint64_t event_mask_{0x00001fffffffffff};
   uint8_t authentication_enable_{};
   std::vector<uint8_t> supported_codecs_;
   std::vector<uint32_t> vendor_specific_codecs_;
   std::array<uint8_t, 64> supported_commands_;
-  std::vector<uint64_t> extended_features_{{0x875b3fd8fe8ffeff, 0x04}};
+  std::vector<uint64_t> extended_features_{{0x875bffdbfe8ffeff, 0x04}};
   ClassOfDevice class_of_device_{{0, 0, 0}};
   std::vector<uint8_t> extended_inquiry_data_;
   std::array<uint8_t, 248> name_{};
diff --git a/system/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl b/system/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
index 882b23e..74b2b05 100644
--- a/system/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
+++ b/system/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
@@ -55,6 +55,7 @@
 
     ESCO_CONNECTION_REQUEST = 0x30,
     ESCO_CONNECTION_RESPONSE = 0x31,
+    ESCO_DISCONNECT = 0x32,
 }
 
 packet LinkLayerPacket {
@@ -399,3 +400,7 @@
   tx_packet_length : 16,
   air_mode : 8,
 }
+
+packet EScoDisconnect : LinkLayerPacket (type = ESCO_DISCONNECT) {
+  reason : 8,
+}