[bt][rfcomm] Open RFCOMM channels

RFCOMM opens both incoming and outgoing channels.

NET-1013 #done
TEST=bt-host-unittests --gtest_filter='RFCOMM_ChannelManagerTest.Open*'

Change-Id: I6ef1babb03e6cf9b9a08b2fef4cf2ad62c85deb7
diff --git a/drivers/bluetooth/lib/rfcomm/channel.cc b/drivers/bluetooth/lib/rfcomm/channel.cc
index 3a4904f..84ef13b 100644
--- a/drivers/bluetooth/lib/rfcomm/channel.cc
+++ b/drivers/bluetooth/lib/rfcomm/channel.cc
@@ -15,11 +15,8 @@
 
 namespace internal {
 
-void ChannelImpl::CallRxCallback(common::ByteBufferPtr data) {
-  async::PostTask(dispatcher_, [this, data_ = std::move(data)]() mutable {
-    rx_callback_(std::move(data_));
-  });
-}
+ChannelImpl::ChannelImpl(DLCI dlci, Session* session)
+    : Channel(dlci, session) {}
 
 void ChannelImpl::Activate(RxCallback rx_callback,
                            ClosedCallback closed_callback,
@@ -30,19 +27,24 @@
 
   while (!pending_rxed_frames_.empty()) {
     auto& data = pending_rxed_frames_.front();
-    CallRxCallback(std::move(data));
+    async::PostTask(dispatcher_, [this, data_ = std::move(data)]() mutable {
+      rx_callback_(std::move(data_));
+    });
     pending_rxed_frames_.pop();
   }
 }
 
 void ChannelImpl::Send(common::ByteBufferPtr data) {
   FXL_DCHECK(session_);
-  session_->Send(dlci_, std::move(data));
+  FXL_DCHECK(rx_callback_) << "Must call Activate() first";
+  session_->SendUserData(dlci_, std::move(data));
 }
 
 void ChannelImpl::Receive(std::unique_ptr<common::ByteBuffer> data) {
   if (rx_callback_) {
-    CallRxCallback(std::move(data));
+    async::PostTask(dispatcher_, [this, data_ = std::move(data)]() mutable {
+      rx_callback_(std::move(data_));
+    });
   } else {
     pending_rxed_frames_.push(std::move(data));
   }
diff --git a/drivers/bluetooth/lib/rfcomm/channel.h b/drivers/bluetooth/lib/rfcomm/channel.h
index dbfd57e..e5c421f 100644
--- a/drivers/bluetooth/lib/rfcomm/channel.h
+++ b/drivers/bluetooth/lib/rfcomm/channel.h
@@ -30,10 +30,13 @@
 
   // Send a buffer of user data. Takes ownership of |data|. This method is
   // asynchronous, and there is no notification of delivery. We operate under
-  // the assumption that the underlying transport is reliable.
+  // the assumption that the underlying transport is reliable. The channel must
+  // be activated prior to sending.
   virtual void Send(common::ByteBufferPtr data) = 0;
 
  protected:
+  friend class rfcomm::Session;
+
   Channel(DLCI dlci, Session* session);
 
   RxCallback rx_callback_;
@@ -63,17 +66,14 @@
   void Send(common::ByteBufferPtr data) override;
 
  private:
-  friend class Session;
+  friend class rfcomm::Session;
 
   // This should only be called from Session.
-  ChannelImpl();
+  ChannelImpl(DLCI dlci, Session* session);
 
   // This should only be called from Session.
   void Receive(std::unique_ptr<common::ByteBuffer> data) override;
 
-  // Post a call to |rx_callback_| on |dispatcher_|, passing in |data|.
-  void CallRxCallback(std::unique_ptr<common::ByteBuffer> data);
-
   std::queue<std::unique_ptr<common::ByteBuffer>> pending_rxed_frames_;
 };
 
diff --git a/drivers/bluetooth/lib/rfcomm/rfcomm_unittest.cc b/drivers/bluetooth/lib/rfcomm/rfcomm_unittest.cc
index 63070c3..d01ee37 100644
--- a/drivers/bluetooth/lib/rfcomm/rfcomm_unittest.cc
+++ b/drivers/bluetooth/lib/rfcomm/rfcomm_unittest.cc
@@ -77,6 +77,24 @@
     handle_to_incoming_frames_.clear();
   }
 
+  // Emplace a new PeerState for a new fake peer. Should be called for each fake
+  // peer which a test is emulating. The returned PeerState should then be
+  // updated throughout the test (e.g. the multiplexer state should change when
+  // the multiplexer starts up).
+  PeerState& AddFakePeerState(hci::ConnectionHandle handle, PeerState state) {
+    FXL_DCHECK(handle_to_peer_state_.find(handle) ==
+               handle_to_peer_state_.end());
+    handle_to_peer_state_.emplace(handle, std::move(state));
+    return handle_to_peer_state_[handle];
+  }
+
+  fbl::RefPtr<l2cap::testing::FakeChannel> GetFakeChannel(
+      hci::ConnectionHandle handle) {
+    FXL_DCHECK(handle_to_fake_channel_.find(handle) !=
+               handle_to_fake_channel_.end());
+    return handle_to_fake_channel_[handle];
+  }
+
   void ExpectFrame(hci::ConnectionHandle handle, FrameType type, DLCI dlci) {
     auto queue_it = handle_to_incoming_frames_.find(handle);
     EXPECT_FALSE(handle_to_incoming_frames_.end() == queue_it);
@@ -95,31 +113,31 @@
 
   void ReceiveFrame(hci::ConnectionHandle handle,
                     std::unique_ptr<Frame> frame) {
-    auto channel_it = handle_to_fake_channel_.find(handle);
-    EXPECT_FALSE(channel_it == handle_to_fake_channel_.end());
+    auto channel = GetFakeChannel(handle);
 
     auto buffer = common::NewSlabBuffer(frame->written_size());
     frame->Write(buffer->mutable_view());
-    channel_it->second->Receive(buffer->view());
+    channel->Receive(buffer->view());
   }
 
-  // Emplace a new PeerState for a new fake peer. Should be called for each fake
-  // peer which a test is emulating. The returned PeerState should then be
-  // updated throughout the test (e.g. the multiplexer state should change when
-  // the multiplexer starts up).
-  PeerState& AddFakePeerState(hci::ConnectionHandle handle, PeerState state) {
-    FXL_DCHECK(handle_to_peer_state_.find(handle) ==
-               handle_to_peer_state_.end());
-    handle_to_peer_state_.emplace(handle, std::move(state));
-    return handle_to_peer_state_[handle];
-  }
+  // Makes the asynchronous channel-getting process synchronous, for the
+  // purposes of writing clean tests. This function will imitiate (to
+  // |channel_manager_|) an RFCOMM peer, and will send frames to handle
+  // multiplexer startup, optional parameter negotiation, and finally, channel
+  // opening.
+  //
+  // If this is the first channel which will be opened on this handle,
+  // the state of the peer should first be set in |handle_to_peer_state_|.
+  // These state variables will then be used during the session startup
+  // procedure which will ensue.
+  fbl::RefPtr<Channel> OpenOutgoingChannel(hci::ConnectionHandle handle,
+                                           ServerChannel server_channel);
 
-  fbl::RefPtr<l2cap::testing::FakeChannel> GetFakeChannel(
-      hci::ConnectionHandle handle) {
-    FXL_DCHECK(handle_to_fake_channel_.find(handle) !=
-               handle_to_fake_channel_.end());
-    return handle_to_fake_channel_[handle];
-  }
+  // The fake remote peer represented by |handle| attempts to open
+  // |server_channel|. If no session exists, this function will handle session
+  // startup, multiplexer startup, and parameter negotiation.
+  void OpenIncomingChannel(hci::ConnectionHandle handle,
+                           ServerChannel server_channel);
 
   std::unique_ptr<ChannelManager> channel_manager_;
 
@@ -143,6 +161,181 @@
   std::unordered_map<hci::ConnectionHandle, PeerState> handle_to_peer_state_;
 };
 
+fbl::RefPtr<Channel> RFCOMM_ChannelManagerTest::OpenOutgoingChannel(
+    hci::ConnectionHandle handle, ServerChannel server_channel) {
+  FXL_DCHECK(handle_to_peer_state_.find(handle) != handle_to_peer_state_.end())
+      << "No peer state for handle " << handle;
+  PeerState& state = handle_to_peer_state_[handle];
+
+  fbl::RefPtr<Channel> return_channel = nullptr;
+  channel_manager_->OpenRemoteChannel(
+      handle, server_channel,
+      [&return_channel](auto channel, auto server_channel) {
+        return_channel = channel;
+      },
+      dispatcher());
+  RunLoopUntilIdle();
+
+  // If the fake L2CAP channel doesn't exist yet, we need to trigger its
+  // creation with TriggerOutboundChannel.
+  if (handle_to_fake_channel_.find(handle) == handle_to_fake_channel_.end()) {
+    l2cap_->TriggerOutboundChannel(handle, l2cap::kRFCOMM, kL2CAPChannelId1,
+                                   kL2CAPChannelId2);
+    RunLoopUntilIdle();
+  }
+
+  EXPECT_FALSE(handle_to_incoming_frames_.find(handle) ==
+               handle_to_incoming_frames_.end());
+  auto& queue = handle_to_incoming_frames_[handle];
+
+  EXPECT_FALSE(queue.empty());
+  auto frame =
+      Frame::Parse(state.credit_based_flow, state.role, queue.front()->view());
+
+  queue.pop();
+  FXL_DCHECK(frame);
+
+  // If we received a mux startup request, respond to it.
+  if (static_cast<FrameType>(frame->control()) ==
+          FrameType::kSetAsynchronousBalancedMode &&
+      frame->dlci() == kMuxControlDLCI) {
+    ReceiveFrame(handle, std::make_unique<UnnumberedAcknowledgementResponse>(
+                             state.role, kMuxControlDLCI));
+
+    state.role = Role::kResponder;
+
+    RunLoopUntilIdle();
+
+    // Expect that another frame has arrived.
+    EXPECT_FALSE(queue.empty());
+    frame = Frame::Parse(state.credit_based_flow, state.role,
+                         queue.front()->view());
+    queue.pop();
+  }
+
+  // If we received a parameter negotiation request, respond to it.
+  if (static_cast<FrameType>(frame->control()) ==
+          FrameType::kUnnumberedInfoHeaderCheck &&
+      frame->dlci() == kMuxControlDLCI) {
+    auto pn_command_mux_command =
+        static_cast<MuxCommandFrame*>(frame.get())->TakeMuxCommand();
+    EXPECT_EQ(MuxCommandType::kDLCParameterNegotiation,
+              pn_command_mux_command->command_type());
+    EXPECT_EQ(CommandResponse::kCommand,
+              pn_command_mux_command->command_response());
+
+    auto pn_command = std::unique_ptr<DLCParameterNegotiationCommand>(
+        static_cast<DLCParameterNegotiationCommand*>(
+            pn_command_mux_command.release()));
+
+    // For now, just send back the same parameters (making sure to send the
+    // correct credit-based flow response based on our credit-based flow
+    // setting).
+    ParameterNegotiationParams params = pn_command->params();
+    params.credit_based_flow_handshake =
+        state.credit_based_flow ? CreditBasedFlowHandshake::kSupportedResponse
+                                : CreditBasedFlowHandshake::kUnsupported;
+    ReceiveFrame(handle, std::make_unique<MuxCommandFrame>(
+                             state.role, state.credit_based_flow,
+                             std::make_unique<DLCParameterNegotiationCommand>(
+                                 CommandResponse::kResponse, params)));
+
+    RunLoopUntilIdle();
+
+    // Expect that another frame has arrived.
+    EXPECT_FALSE(queue.empty());
+    frame = Frame::Parse(state.credit_based_flow, state.role,
+                         queue.front()->view());
+    queue.pop();
+  }
+
+  EXPECT_EQ(FrameType::kSetAsynchronousBalancedMode,
+            FrameType(frame->control()));
+  DLCI dlci = ServerChannelToDLCI(server_channel, state.role);
+  EXPECT_EQ(dlci, frame->dlci());
+
+  ReceiveFrame(handle, std::make_unique<UnnumberedAcknowledgementResponse>(
+                           state.role, dlci));
+
+  RunLoopUntilIdle();
+
+  EXPECT_TRUE(return_channel);
+
+  return return_channel;
+}
+
+void RFCOMM_ChannelManagerTest::OpenIncomingChannel(
+    hci::ConnectionHandle handle, ServerChannel server_channel) {
+  FXL_DCHECK(handle_to_peer_state_.find(handle) != handle_to_peer_state_.end())
+      << "Please set peer state for handle " << handle
+      << " before attempting to open a channel on this handle";
+
+  PeerState& state = handle_to_peer_state_[handle];
+
+  if (handle_to_fake_channel_.find(handle) == handle_to_fake_channel_.end()) {
+    l2cap_->TriggerInboundChannel(handle, l2cap::kRFCOMM, kL2CAPChannelId1,
+                                  kL2CAPChannelId2);
+    RunLoopUntilIdle();
+
+    // If channel didn't exist, then we need to do mux startup and parameter
+    // negotiation.
+    auto l2cap_channel = GetFakeChannel(handle);
+
+    ReceiveFrame(handle, std::make_unique<SetAsynchronousBalancedModeCommand>(
+                             Role::kUnassigned, kMuxControlDLCI));
+    RunLoopUntilIdle();
+    ExpectFrame(handle, FrameType::kUnnumberedAcknowledgement, kMuxControlDLCI);
+    state.role = Role::kInitiator;
+
+    DLCI dlci = ServerChannelToDLCI(server_channel, OppositeRole(state.role));
+
+    // Send parameter negotiation
+    ParameterNegotiationParams params;
+    params.dlci = dlci;
+    params.credit_based_flow_handshake =
+        CreditBasedFlowHandshake::kSupportedRequest;
+    params.priority = 61;
+    params.maximum_frame_size =
+        l2cap_channel->rx_mtu() < l2cap_channel->tx_mtu()
+            ? l2cap_channel->rx_mtu()
+            : l2cap_channel->tx_mtu();
+    params.initial_credits = kMaxInitialCredits;
+    ReceiveFrame(handle, std::make_unique<MuxCommandFrame>(
+                             state.role, state.credit_based_flow,
+                             std::make_unique<DLCParameterNegotiationCommand>(
+                                 CommandResponse::kCommand, params)));
+    RunLoopUntilIdle();
+
+    // Expect parameter negotiation response
+    EXPECT_TRUE(handle_to_incoming_frames_[handle].size());
+    auto frame =
+        Frame::Parse(state.credit_based_flow, state.role,
+                     handle_to_incoming_frames_[handle].front()->view());
+    handle_to_incoming_frames_[handle].pop();
+    EXPECT_EQ(FrameType::kUnnumberedInfoHeaderCheck,
+              static_cast<FrameType>(frame->control()));
+    EXPECT_EQ(kMuxControlDLCI, frame->dlci());
+    auto mux_command =
+        static_cast<MuxCommandFrame*>(frame.get())->TakeMuxCommand();
+    EXPECT_EQ(MuxCommandType::kDLCParameterNegotiation,
+              mux_command->command_type());
+    EXPECT_EQ(CommandResponse::kResponse, mux_command->command_response());
+  }
+
+  // Otherwise, a session must already exist with this remote peer. We can
+  // furthermore assume that a channel must be open, and thus that the
+  // multiplexer has also been started, and parameter negotiation is complete.
+
+  DLCI dlci = ServerChannelToDLCI(server_channel, OppositeRole(state.role));
+
+  // Send SABM.
+  ReceiveFrame(handle, std::make_unique<SetAsynchronousBalancedModeCommand>(
+                           state.role, dlci));
+  RunLoopUntilIdle();
+
+  // Expect UA response.
+  ExpectFrame(handle, FrameType::kUnnumberedAcknowledgement, dlci);
+}
 // Expect that registration of an L2CAP channel with the Channel Manager results
 // in the L2CAP channel's eventual activation.
 TEST_F(RFCOMM_ChannelManagerTest, RegisterL2CAPChannel) {
@@ -218,6 +411,7 @@
   RunLoopUntilIdle();
 
   state.role = Role::kResponder;
+  DLCI dlci = ServerChannelToDLCI(kMinServerChannel, state.role);
 
   {
     // Expect a PN command from the session
@@ -229,8 +423,6 @@
     queue_it->second.pop();
     EXPECT_EQ(FrameType::kUnnumberedInfoHeaderCheck,
               static_cast<FrameType>(frame->control()));
-    DLCI dlci =
-        ServerChannelToDLCI(kMinServerChannel, OppositeRole(state.role));
     auto mux_command =
         static_cast<MuxCommandFrame*>(frame.get())->TakeMuxCommand();
     EXPECT_EQ(MuxCommandType::kDLCParameterNegotiation,
@@ -251,8 +443,13 @@
     RunLoopUntilIdle();
   }
 
+  ExpectFrame(kHandle1, FrameType::kSetAsynchronousBalancedMode, dlci);
+  ReceiveFrame(kHandle1, std::make_unique<UnnumberedAcknowledgementResponse>(
+                             state.role, dlci));
+  RunLoopUntilIdle();
+
   EXPECT_TRUE(channel_received);
-  EXPECT_FALSE(channel);
+  EXPECT_TRUE(channel);
 }
 
 // Test multiplexer startup conflict procedure (resulting role: initiator).
@@ -295,6 +492,7 @@
   RunLoopUntilIdle();
 
   state.role = Role::kResponder;
+  DLCI dlci = ServerChannelToDLCI(kMinServerChannel, state.role);
 
   {
     // Expect a PN command from the session
@@ -306,8 +504,6 @@
     queue_it->second.pop();
     EXPECT_EQ(FrameType::kUnnumberedInfoHeaderCheck,
               static_cast<FrameType>(frame->control()));
-    DLCI dlci =
-        ServerChannelToDLCI(kMinServerChannel, OppositeRole(state.role));
     auto mux_command =
         static_cast<MuxCommandFrame*>(frame.get())->TakeMuxCommand();
     EXPECT_EQ(MuxCommandType::kDLCParameterNegotiation,
@@ -328,8 +524,13 @@
     RunLoopUntilIdle();
   }
 
+  ExpectFrame(kHandle1, FrameType::kSetAsynchronousBalancedMode, dlci);
+  ReceiveFrame(kHandle1, std::make_unique<UnnumberedAcknowledgementResponse>(
+                             state.role, dlci));
+  RunLoopUntilIdle();
+
   EXPECT_TRUE(channel_received);
-  EXPECT_FALSE(channel);
+  EXPECT_TRUE(channel);
 }
 
 // Test multiplexer startup conflict procedure (resulting role: responder).
@@ -382,8 +583,7 @@
     queue.pop();
     EXPECT_EQ(FrameType::kUnnumberedInfoHeaderCheck,
               static_cast<FrameType>(frame->control()));
-    DLCI dlci =
-        ServerChannelToDLCI(kMinServerChannel, OppositeRole(state.role));
+    DLCI dlci = ServerChannelToDLCI(kMinServerChannel, state.role);
     auto mux_command =
         static_cast<MuxCommandFrame*>(frame.get())->TakeMuxCommand();
     EXPECT_EQ(MuxCommandType::kDLCParameterNegotiation,
@@ -434,7 +634,7 @@
   RunLoopUntilIdle();
 
   state.role = Role::kResponder;
-  DLCI dlci = ServerChannelToDLCI(kMinServerChannel, OppositeRole(state.role));
+  DLCI dlci = ServerChannelToDLCI(kMinServerChannel, state.role);
 
   {
     // Expect a PN command from the session
@@ -447,8 +647,7 @@
     queue.pop();
     EXPECT_EQ(FrameType::kUnnumberedInfoHeaderCheck,
               static_cast<FrameType>(frame->control()));
-    DLCI dlci =
-        ServerChannelToDLCI(kMinServerChannel, OppositeRole(state.role));
+    DLCI dlci = ServerChannelToDLCI(kMinServerChannel, state.role);
     auto mux_command =
         static_cast<MuxCommandFrame*>(frame.get())->TakeMuxCommand();
     EXPECT_EQ(MuxCommandType::kDLCParameterNegotiation,
@@ -500,6 +699,90 @@
   RunLoopUntilIdle();
 }
 
+TEST_F(RFCOMM_ChannelManagerTest, OpenOutgoingChannel) {
+  handle_to_peer_state_.emplace(kHandle1, PeerState{true, Role::kUnassigned});
+  PeerState& state = handle_to_peer_state_[kHandle1];
+
+  auto channel = OpenOutgoingChannel(kHandle1, kMinServerChannel);
+  EXPECT_TRUE(channel);
+
+  DLCI dlci = ServerChannelToDLCI(kMinServerChannel, state.role);
+
+  common::ByteBufferPtr received_data;
+  channel->Activate(
+      [&received_data](auto data) { received_data = std::move(data); }, []() {},
+      dispatcher());
+
+  auto pattern = common::CreateStaticByteBuffer(1, 2, 3, 4);
+  auto buffer = std::make_unique<common::DynamicByteBuffer>(pattern);
+  channel->Send(std::move(buffer));
+  RunLoopUntilIdle();
+
+  auto frame =
+      Frame::Parse(state.credit_based_flow, OppositeRole(state.role),
+                   handle_to_incoming_frames_[kHandle1].front()->view());
+  EXPECT_TRUE(frame);
+  EXPECT_EQ(FrameType::kUnnumberedInfoHeaderCheck,
+            static_cast<FrameType>(frame->control()));
+  EXPECT_EQ(dlci, frame->dlci());
+  EXPECT_EQ(pattern,
+            *static_cast<UserDataFrame*>(frame.get())->TakeInformation());
+
+  buffer = std::make_unique<common::DynamicByteBuffer>(pattern);
+  ReceiveFrame(kHandle1, std::make_unique<UserDataFrame>(
+                             state.role, state.credit_based_flow, dlci,
+                             std::move(buffer)));
+  RunLoopUntilIdle();
+
+  EXPECT_TRUE(received_data);
+  EXPECT_EQ(pattern, *received_data);
+}
+
+TEST_F(RFCOMM_ChannelManagerTest, OpenIncomingChannel) {
+  auto& state = AddFakePeerState(
+      kHandle1, PeerState{true /* credit-based flow */, Role::kUnassigned});
+
+  fbl::RefPtr<Channel> channel;
+  auto server_channel = channel_manager_->AllocateLocalChannel(
+      [&channel](auto received_channel, auto) { channel = received_channel; },
+      dispatcher());
+
+  OpenIncomingChannel(kHandle1, server_channel);
+  RunLoopUntilIdle();
+  EXPECT_TRUE(channel);
+
+  DLCI dlci = ServerChannelToDLCI(server_channel, OppositeRole(state.role));
+
+  common::ByteBufferPtr received_data;
+  channel->Activate(
+      [&received_data](auto data) { received_data = std::move(data); }, []() {},
+      dispatcher());
+
+  auto pattern = common::CreateStaticByteBuffer(1, 2, 3, 4);
+  auto buffer = std::make_unique<common::DynamicByteBuffer>(pattern);
+  channel->Send(std::move(buffer));
+  RunLoopUntilIdle();
+
+  auto frame =
+      Frame::Parse(state.credit_based_flow, OppositeRole(state.role),
+                   handle_to_incoming_frames_[kHandle1].front()->view());
+  EXPECT_TRUE(frame);
+  EXPECT_EQ(FrameType::kUnnumberedInfoHeaderCheck,
+            static_cast<FrameType>(frame->control()));
+  EXPECT_EQ(dlci, frame->dlci());
+  EXPECT_EQ(pattern,
+            *static_cast<UserDataFrame*>(frame.get())->TakeInformation());
+
+  buffer = std::make_unique<common::DynamicByteBuffer>(pattern);
+  ReceiveFrame(kHandle1, std::make_unique<UserDataFrame>(
+                             state.role, state.credit_based_flow, dlci,
+                             std::move(buffer)));
+  RunLoopUntilIdle();
+
+  EXPECT_TRUE(received_data);
+  EXPECT_EQ(pattern, *received_data);
+}
+
 }  // namespace
 }  // namespace rfcomm
 }  // namespace btlib
diff --git a/drivers/bluetooth/lib/rfcomm/session.cc b/drivers/bluetooth/lib/rfcomm/session.cc
index c907fcf..23e3dd0 100644
--- a/drivers/bluetooth/lib/rfcomm/session.cc
+++ b/drivers/bluetooth/lib/rfcomm/session.cc
@@ -4,9 +4,9 @@
 
 #include <lib/async/default.h>
 
-#include "garnet/drivers/bluetooth/lib/rfcomm/session.h"
 #include "garnet/drivers/bluetooth/lib/common/slab_allocator.h"
 #include "garnet/drivers/bluetooth/lib/rfcomm/rfcomm.h"
+#include "garnet/drivers/bluetooth/lib/rfcomm/session.h"
 
 namespace btlib {
 namespace rfcomm {
@@ -66,8 +66,25 @@
   }
 }
 
+// Returns true if this user DLCI "belongs to" the side of the session with the
+// given |role|. See RFCOMM 5.2: "...this partitions the DLCI value space such
+// that server applications on the non-initiating device are reachable on DLCIs
+// 2,4,6,...,60, and server applications on the initiating device are reachable
+// on 3,5,7,...,61."
+constexpr bool IsValidLocalChannel(Role role, DLCI dlci) {
+  FXL_DCHECK(IsMultiplexerStarted(role));
+  FXL_DCHECK(IsUserDLCI(dlci));
+  return (role == Role::kInitiator ? 1 : 0) == dlci % 2;
+}
+
 }  // namespace
 
+void Session::SendUserData(DLCI dlci, common::ByteBufferPtr data) {
+  bool sent = SendFrame(std::make_unique<UserDataFrame>(
+      role_, credit_based_flow_, dlci, std::move(data)));
+  FXL_DCHECK(sent);
+}
+
 std::unique_ptr<Session> Session::Create(
     fbl::RefPtr<l2cap::Channel> l2cap_channel,
     ChannelOpenedCallback channel_opened_cb) {
@@ -115,7 +132,10 @@
     return;
   }
 
-  DLCI dlci = ServerChannelToDLCI(server_channel, role_);
+  // RFCOMM 5.4: An RFCOMM entity making a new DLC on an existing session forms
+  // the DLCI by combining the Server Channel for the application on the other
+  // device, and the inverse of its own direction bit for the session."
+  DLCI dlci = ServerChannelToDLCI(server_channel, OppositeRole(role_));
 
   if (initial_param_negotiation_state_ !=
       ParameterNegotiationState::kNegotiated) {
@@ -132,15 +152,40 @@
     return;
   }
 
-  // TODO(NET-1013): implement channel open
+  SendCommand(
+      FrameType::kSetAsynchronousBalancedMode, dlci,
+      [this, dlci, server_channel, cb = std::move(channel_opened_cb)](
+          std::unique_ptr<Frame> response) mutable {
+        FXL_DCHECK(response);
+        FrameType type = FrameType(response->control());
+        fbl::RefPtr<Channel> new_channel;
+        switch (type) {
+          case FrameType::kUnnumberedAcknowledgement: {
+            FXL_LOG(INFO) << "rfcomm: Channel " << static_cast<unsigned>(dlci)
+                          << " started successfully";
+            new_channel = fbl::AdoptRef(new internal::ChannelImpl(dlci, this));
+            FXL_DCHECK(channels_.find(dlci) == channels_.end());
+            channels_.emplace(dlci, new_channel);
+            break;
+          }
+          case FrameType::kDisconnectedMode:
+            FXL_LOG(WARNING)
+                << "rfcomm: Channel " << static_cast<unsigned>(dlci)
+                << " failed to start";
+            break;
+          default:
+            FXL_LOG(WARNING) << "rfcomm: Unexpected response to SABM: "
+                             << static_cast<unsigned>(type);
+            break;
+        }
 
-  // Return a nullptr channel if we fail to open
-  FXL_LOG(WARNING) << "Not implemented";
-  async::PostTask(dispatcher_, [cb = std::move(channel_opened_cb)] {
-    cb(nullptr, kInvalidServerChannel);
-  });
+        // Send the result.
+        async::PostTask(dispatcher_,
+                        [server_channel, new_channel, cb_ = std::move(cb)] {
+                          cb_(new_channel, server_channel);
+                        });
+      });
 }
-
 void Session::RxCallback(const l2cap::SDU& sdu) {
   l2cap::PDU::Reader reader(&sdu);
   reader.ReadNext(sdu.length(), [&](const common::ByteBuffer& buffer) {
@@ -182,8 +227,19 @@
           HandleMuxCommand(
               static_cast<MuxCommandFrame*>(frame.get())->TakeMuxCommand());
           return;
+        } else if (IsUserDLCI(dlci)) {
+          auto channel_it = channels_.find(dlci);
+          if (channel_it == channels_.end()) {
+            FXL_LOG(WARNING) << "rfcomm: User data received for unopened DLCI "
+                             << static_cast<unsigned>(dlci);
+            return;
+          }
+          channel_it->second->Receive(
+              static_cast<UserDataFrame*>(frame.get())->TakeInformation());
+          return;
         } else {
-          FXL_NOTIMPLEMENTED();
+          FXL_LOG(WARNING) << "rfcomm: UIH frame on invalid DLCI "
+                           << static_cast<unsigned>(dlci);
         }
       }
 
@@ -421,17 +477,53 @@
       case Role::kInitiator:
       case Role::kResponder:
         // TODO(gusss): should we send a DM in this case?
-        FXL_LOG(WARNING) << "rfcomm: Request to start alreadys started"
+        FXL_LOG(WARNING) << "rfcomm: Request to start already started"
                          << " multiplexer";
         return;
       default:
         FXL_NOTREACHED();
         return;
     }
-  } else {
-    // TODO(NET-1014): open channel when requested by remote peer
-    FXL_NOTIMPLEMENTED();
   }
+
+  // If it isn't a multiplexer startup request, it must be a request for a user
+  // channel.
+
+  // TODO(NET-1301): unit test this case.
+  if (!IsUserDLCI(dlci) || !IsValidLocalChannel(role_, dlci)) {
+    FXL_LOG(WARNING) << "rfcomm: Remote requested invalid DLCI "
+                     << static_cast<unsigned>(dlci);
+    SendResponse(FrameType::kDisconnectedMode, dlci);
+    return;
+  }
+
+  // TODO(NET-1301): unit test this case.
+  if (channels_.find(dlci) != channels_.end()) {
+    // If the channel is already open, the remote is confused about the state of
+    // the Session. Send a DM and a DISC for that channel.
+    // TODO(NET-1274): do we want to just shut down the whole session here?
+    // Things would be in a nasty state at this point.
+    FXL_LOG(WARNING) << "rfcomm: Remote requested already open channel";
+    SendResponse(FrameType::kDisconnectedMode, dlci);
+    SendCommand(FrameType::kDisconnect, dlci, [](auto response) {
+      // TODO(NET-1273): implement clean channel close + state reset
+    });
+
+    return;
+  }
+
+  // Start the channel by first responding positively.
+  SendResponse(FrameType::kUnnumberedAcknowledgement, dlci);
+
+  // Now form the channel and pass it off.
+  auto channel = fbl::AdoptRef(new internal::ChannelImpl(dlci, this));
+  channels_.emplace(dlci, channel);
+  async::PostTask(dispatcher_, [this, dlci, channel] {
+    channel_opened_cb_(channel, DLCIToServerChannel(dlci));
+  });
+
+  FXL_LOG(INFO) << "rfcomm: Remote peer opened channel with DLCI "
+                << static_cast<unsigned>(dlci);
 }
 
 void Session::HandleMuxCommand(std::unique_ptr<MuxCommand> mux_command) {
diff --git a/drivers/bluetooth/lib/rfcomm/session.h b/drivers/bluetooth/lib/rfcomm/session.h
index 8220877..e520a16 100644
--- a/drivers/bluetooth/lib/rfcomm/session.h
+++ b/drivers/bluetooth/lib/rfcomm/session.h
@@ -25,7 +25,7 @@
 
 class Session {
  public:
-  void Send(DLCI dlci, common::ByteBufferPtr data);
+  void SendUserData(DLCI dlci, common::ByteBufferPtr data);
 
  private:
   friend class ChannelManager;
@@ -138,7 +138,7 @@
   bool credit_based_flow_;
 
   // Keeps track of opened channels.
-  std::unordered_map<DLCI, fxl::WeakPtr<l2cap::Channel>> channels_;
+  std::unordered_map<DLCI, fbl::RefPtr<Channel>> channels_;
 
   // Called when the remote peer opens a new incoming channel. The session
   // object constructs a new channel and then passes ownership of the channel