L2CAP: Save remote supported features in link

Save extended_features_supported. Don't send ERTM in initial config
request if remote doesn't support.

Test: run_cert.sh
Bug: 144770885
Change-Id: I17ee89855b0a05bd3265295f909b28787da355e9
diff --git a/gd/l2cap/classic/cert/cert.cc b/gd/l2cap/classic/cert/cert.cc
index 31c4760..63c8593 100644
--- a/gd/l2cap/classic/cert/cert.cc
+++ b/gd/l2cap/classic/cert/cert.cc
@@ -209,7 +209,7 @@
     switch (request->type()) {
       case InformationRequestType::CONNECTIONLESS_MTU: {
         auto builder = InformationResponseConnectionlessMtuBuilder::Create(request->signal_id(),
-                                                                           InformationRequestResult::NOT_SUPPORTED, 0);
+                                                                           InformationRequestResult::SUCCESS, 100);
         auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
         outgoing_packet_queue_.push(std::move(l2cap_builder));
         send_packet_from_queue();
@@ -217,7 +217,7 @@
       }
       case InformationRequestType::EXTENDED_FEATURES: {
         auto builder = InformationResponseExtendedFeaturesBuilder::Create(
-            request->signal_id(), InformationRequestResult::NOT_SUPPORTED, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+            request->signal_id(), InformationRequestResult::SUCCESS, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0);
         auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
         outgoing_packet_queue_.push(std::move(l2cap_builder));
         send_packet_from_queue();
diff --git a/gd/l2cap/classic/internal/link.cc b/gd/l2cap/classic/internal/link.cc
index bc31996..7b5178f 100644
--- a/gd/l2cap/classic/internal/link.cc
+++ b/gd/l2cap/classic/internal/link.cc
@@ -157,6 +157,30 @@
   local_cid_to_pending_dynamic_channel_connection_map_.erase(cid);
 }
 
+void Link::SetRemoteConnectionlessMtu(Mtu mtu) {
+  remote_mtu_ = mtu;
+}
+
+Mtu Link::GetRemoteConnectionlessMtu() const {
+  return remote_mtu_;
+}
+
+void Link::SetRemoteSupportsErtm(bool supported) {
+  remote_supports_ertm_ = supported;
+}
+
+bool Link::GetRemoteSupportsErtm() const {
+  return remote_supports_ertm_;
+}
+
+void Link::SetRemoteSupportsFcs(bool supported) {
+  remote_supports_fcs_ = supported;
+}
+
+bool Link::GetRemoteSupportsFcs() const {
+  return remote_supports_fcs_;
+}
+
 }  // namespace internal
 }  // namespace classic
 }  // namespace l2cap
diff --git a/gd/l2cap/classic/internal/link.h b/gd/l2cap/classic/internal/link.h
index 7ed82e9..2c6c1b8 100644
--- a/gd/l2cap/classic/internal/link.h
+++ b/gd/l2cap/classic/internal/link.h
@@ -99,6 +99,14 @@
   virtual void NotifyChannelCreation(Cid cid, std::unique_ptr<DynamicChannel> channel);
   virtual void NotifyChannelFail(Cid cid);
 
+  // Information received from signalling channel
+  virtual void SetRemoteConnectionlessMtu(Mtu mtu);
+  virtual Mtu GetRemoteConnectionlessMtu() const;
+  virtual void SetRemoteSupportsErtm(bool supported);
+  virtual bool GetRemoteSupportsErtm() const;
+  virtual void SetRemoteSupportsFcs(bool supported);
+  virtual bool GetRemoteSupportsFcs() const;
+
  private:
   os::Handler* l2cap_handler_;
   l2cap::internal::FixedChannelAllocator<FixedChannelImpl, Link> fixed_channel_allocator_{this, l2cap_handler_};
@@ -112,6 +120,9 @@
   ClassicSignallingManager signalling_manager_;
   std::unordered_map<Cid, PendingDynamicChannelConnection> local_cid_to_pending_dynamic_channel_connection_map_;
   os::Alarm link_idle_disconnect_alarm_{l2cap_handler_};
+  Mtu remote_mtu_ = kMinimumClassicMtu;
+  bool remote_supports_ertm_ = false;
+  bool remote_supports_fcs_ = false;
   DISALLOW_COPY_AND_ASSIGN(Link);
 };
 
diff --git a/gd/l2cap/classic/internal/signalling_manager.cc b/gd/l2cap/classic/internal/signalling_manager.cc
index d86621c..66a3f51 100644
--- a/gd/l2cap/classic/internal/signalling_manager.cc
+++ b/gd/l2cap/classic/internal/signalling_manager.cc
@@ -149,6 +149,9 @@
                            ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
   auto* service = dynamic_service_manager_->GetService(psm);
   auto initial_config = service->GetConfigOption();
+  if (!link_->GetRemoteSupportsErtm()) {
+    initial_config.channel_mode = DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC;
+  }
   auto mtu_configuration = std::make_unique<MtuConfigurationOption>();
   mtu_configuration->mtu_ = initial_config.incoming_mtu;
   auto fcs_option = std::make_unique<FrameCheckSequenceOption>();
@@ -209,6 +212,9 @@
   }
   alarm_.Cancel();
   auto initial_config = link_->GetConfigurationForInitialConfiguration(new_channel->GetCid());
+  if (!link_->GetRemoteSupportsErtm()) {
+    initial_config.channel_mode = DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC;
+  }
   auto mtu_configuration = std::make_unique<MtuConfigurationOption>();
   mtu_configuration->mtu_ = initial_config.incoming_mtu;
   auto retransmission_flow_control_configuration = std::make_unique<RetransmissionAndFlowControlConfigurationOption>();
@@ -407,7 +413,7 @@
   }
 }
 
-void ClassicSignallingManager::OnInformationResponse(SignalId signal_id, const InformationResponseView& view) {
+void ClassicSignallingManager::OnInformationResponse(SignalId signal_id, const InformationResponseView& response) {
   if (pending_commands_.empty()) {
     LOG_WARN("Unexpected response: no pending request");
     return;
@@ -419,7 +425,40 @@
       last_sent_command.command_code_ != CommandCode::INFORMATION_REQUEST) {
     return;
   }
-  // TODO (hsz): Store the information response
+
+  auto type = response.GetInfoType();
+  switch (type) {
+    case InformationRequestInfoType::CONNECTIONLESS_MTU: {
+      auto view = InformationResponseConnectionlessMtuView::Create(response);
+      if (!view.IsValid()) {
+        LOG_WARN("Invalid InformationResponseConnectionlessMtu received");
+        return;
+      }
+      link_->SetRemoteConnectionlessMtu(view.GetConnectionlessMtu());
+      break;
+    }
+    case InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED: {
+      auto view = InformationResponseExtendedFeaturesView::Create(response);
+      if (!view.IsValid()) {
+        LOG_WARN("Invalid InformationResponseExtendedFeatures received");
+        return;
+      }
+      link_->SetRemoteSupportsErtm((view.GetEnhancedRetransmissionMode()));
+      link_->SetRemoteSupportsFcs(view.GetFcsOption());
+      // We don't care about other parameters
+      break;
+    }
+    case InformationRequestInfoType::FIXED_CHANNELS_SUPPORTED: {
+      auto view = InformationResponseFixedChannelsView::Create(response);
+      if (!view.IsValid()) {
+        LOG_WARN("Invalid InformationResponseFixedChannel received");
+        return;
+      }
+      // We don't use fixed channels (connectionless or BR/EDR security) for now so we don't care
+      break;
+    }
+  }
+
   alarm_.Cancel();
   handle_send_next_command();
 }
diff --git a/gd/l2cap/classic/internal/signalling_manager.h b/gd/l2cap/classic/internal/signalling_manager.h
index 0ec6a28..0e17ec7 100644
--- a/gd/l2cap/classic/internal/signalling_manager.h
+++ b/gd/l2cap/classic/internal/signalling_manager.h
@@ -92,7 +92,7 @@
 
   void OnInformationRequest(SignalId signal_id, InformationRequestInfoType type);
 
-  void OnInformationResponse(SignalId signal_id, const InformationResponseView& view);
+  void OnInformationResponse(SignalId signal_id, const InformationResponseView& response);
 
  private:
   void on_incoming_packet();