OSDN Git Service

L2CAP: Classic fixed channel reference counting
authorJack He <siyuanh@google.com>
Wed, 21 Aug 2019 05:07:26 +0000 (22:07 -0700)
committerJack He <siyuanh@google.com>
Tue, 10 Sep 2019 05:33:04 +0000 (05:33 +0000)
* Add a mechanism to disconnect ACL after a link is idle for kClassicLinkIdleDisconnectTimeout
  seconds. The current setting is 20.
* Add Acquire() and Release() method to ClassicFixedChannel for user to
  indicate if a fixed channel should hold the current ACL link or should
  it be oportunistic
* Implemented methods to register for disconnection callbacks
* Add tests to verify fixed channel acquire/release and register
  disconnction callback methods
* Add tests to verify classic link idle timeout disconnect methods

Bug: 138261142
Test: bluetooth_test_gd
Change-Id: I78129afb1b988311c56019f87cbb3f78e9a08cc5

20 files changed:
gd/l2cap/Android.bp
gd/l2cap/classic_fixed_channel.cc
gd/l2cap/classic_fixed_channel.h
gd/l2cap/constant.h [deleted file]
gd/l2cap/internal/classic_fixed_channel_allocator.cc
gd/l2cap/internal/classic_fixed_channel_allocator.h
gd/l2cap/internal/classic_fixed_channel_allocator_test.cc
gd/l2cap/internal/classic_fixed_channel_impl.cc [new file with mode: 0644]
gd/l2cap/internal/classic_fixed_channel_impl.h
gd/l2cap/internal/classic_fixed_channel_impl_mock.h [new file with mode: 0644]
gd/l2cap/internal/classic_fixed_channel_impl_test.cc [new file with mode: 0644]
gd/l2cap/internal/classic_link.h
gd/l2cap/internal/classic_link_manager.cc
gd/l2cap/internal/classic_link_manager.h
gd/l2cap/internal/classic_link_manager_test.cc
gd/l2cap/internal/classic_link_mock.h [new file with mode: 0644]
gd/l2cap/internal/parameter_provider.h [new file with mode: 0644]
gd/l2cap/internal/parameter_provider_mock.h [new file with mode: 0644]
gd/l2cap/internal/scheduler_mock.h [new file with mode: 0644]
gd/l2cap/l2cap_layer.cc

index fa32e94..ac5a16b 100644 (file)
@@ -6,8 +6,9 @@ filegroup {
         "classic_fixed_channel.cc",
         "classic_fixed_channel_manager.cc",
         "classic_fixed_channel_service.cc",
-        "internal/classic_fixed_channel_service_manager_impl.cc",
         "internal/classic_fixed_channel_allocator.cc",
+        "internal/classic_fixed_channel_impl.cc",
+        "internal/classic_fixed_channel_service_manager_impl.cc",
         "internal/classic_link_manager.cc",
         "internal/scheduler_fifo.cc",
     ],
@@ -17,8 +18,9 @@ filegroup {
     name: "BluetoothL2capTestSources",
     srcs: [
         "l2cap_packet_test.cc",
-        "internal/classic_fixed_channel_service_manager_test.cc",
         "internal/classic_fixed_channel_allocator_test.cc",
+        "internal/classic_fixed_channel_impl_test.cc",
+        "internal/classic_fixed_channel_service_manager_test.cc",
         "internal/classic_link_manager_test.cc",
         "internal/scheduler_fifo_test.cc",
         "signal_id_test.cc",
index 6c08eec..2887c45 100644 (file)
  */
 
 #include "l2cap/classic_fixed_channel.h"
+#include "common/bind.h"
+#include "l2cap/internal/classic_fixed_channel_impl.h"
 
 namespace bluetooth {
 namespace l2cap {
-void ClassicFixedChannel::RegisterOnCloseCallback(os::Handler* handler, OnCloseCallback callback) {}
-void ClassicFixedChannel::Acquire() {}
-void ClassicFixedChannel::Release() {}
+
+hci::Address ClassicFixedChannel::GetDevice() const {
+  return impl_->GetDevice();
+}
+
+void ClassicFixedChannel::RegisterOnCloseCallback(os::Handler* user_handler,
+                                                  ClassicFixedChannel::OnCloseCallback on_close_callback) {
+  l2cap_handler_->Post(common::BindOnce(&internal::ClassicFixedChannelImpl::RegisterOnCloseCallback, impl_,
+                                        user_handler, std::move(on_close_callback)));
+}
+
+void ClassicFixedChannel::Acquire() {
+  l2cap_handler_->Post(common::BindOnce(&internal::ClassicFixedChannelImpl::Acquire, impl_));
+}
+
+void ClassicFixedChannel::Release() {
+  l2cap_handler_->Post(common::BindOnce(&internal::ClassicFixedChannelImpl::Release, impl_));
+}
+
 common::BidiQueueEnd<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder>*
 ClassicFixedChannel::GetQueueUpEnd() const {
   return nullptr;
index 16ce1ad..4039cf5 100644 (file)
@@ -17,6 +17,8 @@
 
 #include "common/bidi_queue.h"
 #include "common/callback.h"
+#include "hci/acl_manager.h"
+#include "l2cap/cid.h"
 #include "os/handler.h"
 #include "packet/base_packet_builder.h"
 #include "packet/packet_view.h"
@@ -31,33 +33,40 @@ class ClassicFixedChannelImpl;
 /**
  * L2CAP fixed channel object. When a new object is created, it must be
  * acquired through calling {@link FixedChannel#Acquire()} within X seconds.
- * Otherwise, {@link FixeChannel#Release()} will be called automatically.
+ * Otherwise, {@link FixedChannel#Release()} will be called automatically.
  *
  */
 class ClassicFixedChannel {
  public:
-  using OnCloseCallback = common::Callback<void()>;
+  // Should only be constructed by modules that have access to ClassicLinkManager
+  ClassicFixedChannel(std::shared_ptr<internal::ClassicFixedChannelImpl> impl, os::Handler* l2cap_handler)
+      : impl_(std::move(impl)), l2cap_handler_(l2cap_handler) {
+    ASSERT(impl_ != nullptr);
+    ASSERT(l2cap_handler_ != nullptr);
+  }
+
+  hci::Address GetDevice() const;
 
   /**
    * Register close callback. If close callback is registered, when a channel is closed, the channel's resource will
    * only be freed after on_close callback is invoked. Otherwise, if no on_close callback is registered, the channel's
    * resource will be freed immediately after closing.
    *
-   * @param on_close The callback invoked upon channel closing.
+   * @param user_handler The handler used to invoke the callback on
+   * @param on_close_callback The callback invoked upon channel closing.
    */
-  void RegisterOnCloseCallback(os::Handler* handler, OnCloseCallback on_close);
+  using OnCloseCallback = common::OnceCallback<void(hci::ErrorCode)>;
+  void RegisterOnCloseCallback(os::Handler* user_handler, OnCloseCallback on_close_callback);
 
   /**
-   * Indicate that this Fixed Channel is being used. This will prevent ACL
-   * connection from being disconnected.
+   * Indicate that this Fixed Channel is being used. This will prevent ACL connection from being disconnected.
    */
   void Acquire();
 
   /**
-   * Indicate that this Fixed Channel is no longer being used. ACL connection
-   * will be disconnected after X seconds if no other DynamicChannel is connected
-   * or no other Fixed Channel is using this ACL connection. However a module can
-   * still receive data on this channel as long as it remains open.
+   * Indicate that this Fixed Channel is no longer being used. ACL connection will be disconnected after
+   * kClassicLinkIdleDisconnectTimeout if no other DynamicChannel is connected or no other Fixed Channel is  using this
+   * ACL connection. However a module can still receive data on this channel as long as it remains open.
    */
   void Release();
 
@@ -70,14 +79,9 @@ class ClassicFixedChannel {
    */
   common::BidiQueueEnd<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder>* GetQueueUpEnd() const;
 
-  friend class internal::ClassicFixedChannelImpl;
-
  private:
-  ClassicFixedChannel(os::Handler* l2cap_handler, internal::ClassicFixedChannelImpl* classic_fixed_channel_impl)
-      : l2cap_handler_(l2cap_handler), classic_fixed_channel_impl_(classic_fixed_channel_impl) {}
+  std::shared_ptr<internal::ClassicFixedChannelImpl> impl_;
   os::Handler* l2cap_handler_;
-  internal::ClassicFixedChannelImpl* classic_fixed_channel_impl_;
-  DISALLOW_COPY_AND_ASSIGN(ClassicFixedChannel);
 };
 
 }  // namespace l2cap
diff --git a/gd/l2cap/constant.h b/gd/l2cap/constant.h
deleted file mode 100644 (file)
index 51b07c5..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <chrono>
-#include <cstddef>
-
-#include "l2cap/cid.h"
-#include "l2cap/signal_id.h"
-
-namespace bluetooth {
-namespace l2cap {
-
-constexpr size_t kChannelQueueCapacity = 10;
-constexpr size_t kLinkQueueCapacity = 10;
-constexpr std::chrono::milliseconds bogus_link_wakeup_time = std::chrono::milliseconds(15000);
-
-constexpr SignalId kInitialSignalId = SignalId(0x80);
-
-// Time after last channels closes before link is torn down
-constexpr auto kLinkDisconnectTimeout = std::chrono::seconds(30);
-
-// TODO(cmanton) Random numbers for now
-constexpr auto kChannelConnectionTimeout = std::chrono::seconds(30);
-constexpr auto kChannelConnectionPendingTimeout = std::chrono::seconds(30);
-constexpr auto kChannelConfigurationTimeout = std::chrono::seconds(30);
-constexpr auto kChannelDisconnectionTimeout = std::chrono::seconds(30);
-
-// The depth of buffering that the signalling channel can handle.
-constexpr auto kSignallingChannelSize = 20;
-
-}  // namespace l2cap
-}  // namespace bluetooth
index 6c87cc5..b8b552e 100644 (file)
@@ -19,6 +19,7 @@
 #include "classic_fixed_channel_allocator.h"
 #include "l2cap/cid.h"
 #include "l2cap/internal/classic_fixed_channel_allocator.h"
+#include "l2cap/internal/classic_link.h"
 #include "l2cap/security_policy.h"
 #include "os/handler.h"
 #include "os/log.h"
@@ -27,30 +28,54 @@ namespace bluetooth {
 namespace l2cap {
 namespace internal {
 
-ClassicFixedChannelImpl* ClassicFixedChannelAllocator::AllocateChannel(Cid cid, SecurityPolicy security_policy) {
-  ASSERT_LOG(!IsChannelInUse((cid)), "Cid %d is already in use", cid);
+std::shared_ptr<ClassicFixedChannelImpl> ClassicFixedChannelAllocator::AllocateChannel(Cid cid,
+                                                                                       SecurityPolicy security_policy) {
+  ASSERT_LOG(!IsChannelAllocated((cid)), "Cid 0x%x for device %s is already in use", cid,
+             link_->GetDevice().ToString().c_str());
   ASSERT_LOG(cid >= kFirstFixedChannel && cid <= kLastFixedChannel, "Cid %d out of bound", cid);
-  channels_.try_emplace(cid, cid, handler_);
-  return &channels_.find(cid)->second;
+  auto elem = channels_.try_emplace(cid, std::make_shared<ClassicFixedChannelImpl>(cid, link_, l2cap_handler_));
+  ASSERT_LOG(elem.second, "Failed to create channel for cid 0x%x device %s", cid,
+             link_->GetDevice().ToString().c_str());
+  ASSERT(elem.first->second != nullptr);
+  return elem.first->second;
 }
 
-bool ClassicFixedChannelAllocator::FreeChannel(Cid cid) {
-  ASSERT_LOG(IsChannelInUse(cid), "Channel is not in use: cid %d", cid);
+void ClassicFixedChannelAllocator::FreeChannel(Cid cid) {
+  ASSERT_LOG(IsChannelAllocated(cid), "Channel is not in use: cid %d, device %s", cid,
+             link_->GetDevice().ToString().c_str());
   channels_.erase(cid);
-  return true;
 }
 
-bool ClassicFixedChannelAllocator::IsChannelInUse(Cid cid) const {
+bool ClassicFixedChannelAllocator::IsChannelAllocated(Cid cid) const {
   return channels_.find(cid) != channels_.end();
 }
 
-ClassicFixedChannelImpl* ClassicFixedChannelAllocator::FindChannel(Cid cid) {
-  ASSERT_LOG(IsChannelInUse(cid), "Channel is not in use: cid %d", cid);
-  return &channels_.find(cid)->second;
+std::shared_ptr<ClassicFixedChannelImpl> ClassicFixedChannelAllocator::FindChannel(Cid cid) {
+  ASSERT_LOG(IsChannelAllocated(cid), "Channel is not in use: cid %d, device %s", cid,
+             link_->GetDevice().ToString().c_str());
+  return channels_.find(cid)->second;
 }
+
 size_t ClassicFixedChannelAllocator::NumberOfChannels() const {
   return channels_.size();
 }
+
+void ClassicFixedChannelAllocator::OnAclDisconnected(hci::ErrorCode reason) {
+  for (auto& elem : channels_) {
+    elem.second->OnClosed(reason);
+  }
+}
+
+int ClassicFixedChannelAllocator::GetRefCount() {
+  int ref_count = 0;
+  for (auto& elem : channels_) {
+    if (elem.second->IsAcquired()) {
+      ref_count++;
+    }
+  }
+  return ref_count;
+}
+
 }  // namespace internal
 }  // namespace l2cap
 }  // namespace bluetooth
index 917dec2..527e750 100644 (file)
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <hci/hci_packets.h>
 #include <unordered_map>
 
 #include "l2cap/cid.h"
@@ -28,30 +29,39 @@ namespace bluetooth {
 namespace l2cap {
 namespace internal {
 
+class ClassicLink;
+
 // Helper class for keeping channels in a Link. It allocates and frees Channel object, and supports querying whether a
 // channel is in use
 class ClassicFixedChannelAllocator {
  public:
-  explicit ClassicFixedChannelAllocator(os::Handler* handler) : handler_(handler) {
-    ASSERT(handler_ != nullptr);
+  ClassicFixedChannelAllocator(ClassicLink* link, os::Handler* l2cap_handler)
+      : link_(link), l2cap_handler_(l2cap_handler) {
+    ASSERT(link_ != nullptr);
+    ASSERT(l2cap_handler_ != nullptr);
   }
 
   // Allocates a channel. If cid is used, return nullptr. NOTE: The returned ClassicFixedChannelImpl object is still
-  // owned by the channel cllocator, NOT the client.
-  ClassicFixedChannelImpl* AllocateChannel(Cid cid, SecurityPolicy security_policy);
+  // owned by the channel allocator, NOT the client.
+  std::shared_ptr<ClassicFixedChannelImpl> AllocateChannel(Cid cid, SecurityPolicy security_policy);
 
-  // Frees a channel. If cid doesn't exist, return false
-  bool FreeChannel(Cid cid);
+  // Frees a channel. If cid doesn't exist, it will crash
+  void FreeChannel(Cid cid);
 
-  bool IsChannelInUse(Cid cid) const;
+  bool IsChannelAllocated(Cid cid) const;
 
-  ClassicFixedChannelImpl* FindChannel(Cid cid);
+  std::shared_ptr<ClassicFixedChannelImpl> FindChannel(Cid cid);
 
   size_t NumberOfChannels() const;
 
+  void OnAclDisconnected(hci::ErrorCode hci_status);
+
+  int GetRefCount();
+
  private:
-  os::Handler* handler_ = nullptr;
-  std::unordered_map<Cid, ClassicFixedChannelImpl> channels_;
+  ClassicLink* link_;
+  os::Handler* l2cap_handler_;
+  std::unordered_map<Cid, std::shared_ptr<ClassicFixedChannelImpl>> channels_;
 };
 
 }  // namespace internal
index 8249a4c..e362a6b 100644 (file)
  */
 
 #include "l2cap/internal/classic_fixed_channel_allocator.h"
+#include "l2cap/internal/classic_link_mock.h"
+#include "l2cap/internal/parameter_provider_mock.h"
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 namespace bluetooth {
 namespace l2cap {
 namespace internal {
 
+using testing::MockClassicLink;
+using testing::MockParameterProvider;
+using ::testing::Return;
+
+const hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+
 class L2capClassicFixedChannelAllocatorTest : public ::testing::Test {
  protected:
   void SetUp() override {
     thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
     handler_ = new os::Handler(thread_);
-    channel_allocator_ = std::make_unique<ClassicFixedChannelAllocator>(handler_);
+    mock_parameter_provider_ = new MockParameterProvider();
+    mock_classic_link_ = new MockClassicLink(handler_, mock_parameter_provider_);
+    EXPECT_CALL(*mock_classic_link_, GetDevice()).WillRepeatedly(Return(device));
+    channel_allocator_ = std::make_unique<ClassicFixedChannelAllocator>(mock_classic_link_, handler_);
   }
 
   void TearDown() override {
     channel_allocator_.reset();
+    delete mock_classic_link_;
+    delete mock_parameter_provider_;
     handler_->Clear();
     delete handler_;
     delete thread_;
@@ -39,21 +53,23 @@ class L2capClassicFixedChannelAllocatorTest : public ::testing::Test {
 
   os::Thread* thread_{nullptr};
   os::Handler* handler_{nullptr};
+  MockParameterProvider* mock_parameter_provider_{nullptr};
+  MockClassicLink* mock_classic_link_{nullptr};
   std::unique_ptr<ClassicFixedChannelAllocator> channel_allocator_;
 };
 
 TEST_F(L2capClassicFixedChannelAllocatorTest, precondition) {
   Cid cid = kFirstFixedChannel;
-  EXPECT_FALSE(channel_allocator_->IsChannelInUse(cid));
+  EXPECT_FALSE(channel_allocator_->IsChannelAllocated(cid));
 }
 
 TEST_F(L2capClassicFixedChannelAllocatorTest, allocate_and_free_channel) {
   Cid cid = kFirstFixedChannel;
-  auto* channel = channel_allocator_->AllocateChannel(cid, {});
-  EXPECT_TRUE(channel_allocator_->IsChannelInUse(cid));
+  auto channel = channel_allocator_->AllocateChannel(cid, {});
+  EXPECT_TRUE(channel_allocator_->IsChannelAllocated(cid));
   EXPECT_EQ(channel, channel_allocator_->FindChannel(cid));
-  EXPECT_TRUE(channel_allocator_->FreeChannel(cid));
-  EXPECT_FALSE(channel_allocator_->IsChannelInUse(cid));
+  ASSERT_NO_FATAL_FAILURE(channel_allocator_->FreeChannel(cid));
+  EXPECT_FALSE(channel_allocator_->IsChannelAllocated(cid));
 }
 
 }  // namespace internal
diff --git a/gd/l2cap/internal/classic_fixed_channel_impl.cc b/gd/l2cap/internal/classic_fixed_channel_impl.cc
new file mode 100644 (file)
index 0000000..c90aab3
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unordered_map>
+
+#include "l2cap/cid.h"
+#include "l2cap/internal/classic_fixed_channel_allocator.h"
+#include "l2cap/internal/classic_fixed_channel_impl.h"
+#include "l2cap/internal/classic_link.h"
+#include "l2cap/security_policy.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+ClassicFixedChannelImpl::ClassicFixedChannelImpl(Cid cid, ClassicLink* link, os::Handler* l2cap_handler)
+    : cid_(cid), device_(link->GetDevice()), link_(link), l2cap_handler_(l2cap_handler) {
+  ASSERT_LOG(cid_ >= kFirstFixedChannel && cid_ <= kLastFixedChannel, "Invalid cid: %d", cid_);
+  ASSERT(!device_.IsEmpty());
+  ASSERT(link_ != nullptr);
+  ASSERT(l2cap_handler_ != nullptr);
+}
+
+void ClassicFixedChannelImpl::RegisterOnCloseCallback(os::Handler* user_handler,
+                                                      ClassicFixedChannel::OnCloseCallback on_close_callback) {
+  ASSERT_LOG(user_handler_ == nullptr, "OnCloseCallback can only be registered once");
+  // If channel is already closed, call the callback immediately without saving it
+  if (closed_) {
+    user_handler->Post(common::BindOnce(std::move(on_close_callback), close_reason_));
+    return;
+  }
+  user_handler_ = user_handler;
+  on_close_callback_ = std::move(on_close_callback);
+}
+
+void ClassicFixedChannelImpl::OnClosed(hci::ErrorCode status) {
+  ASSERT_LOG(!closed_, "Device %s Cid 0x%x closed twice, old status 0x%x, new status 0x%x", device_.ToString().c_str(),
+             cid_, static_cast<int>(close_reason_), static_cast<int>(status));
+  closed_ = true;
+  close_reason_ = status;
+  acquired_ = false;
+  link_ = nullptr;
+  l2cap_handler_ = nullptr;
+  if (user_handler_ == nullptr) {
+    return;
+  }
+  // On close callback can only be called once
+  user_handler_->Post(common::BindOnce(std::move(on_close_callback_), status));
+  user_handler_ = nullptr;
+  on_close_callback_.Reset();
+}
+
+void ClassicFixedChannelImpl::Acquire() {
+  ASSERT_LOG(user_handler_ != nullptr, "Must register OnCloseCallback before calling any methods");
+  if (closed_) {
+    LOG_WARN("%s is already closed", ToString().c_str());
+    ASSERT(!acquired_);
+    return;
+  }
+  if (acquired_) {
+    LOG_DEBUG("%s was already acquired", ToString().c_str());
+    return;
+  }
+  acquired_ = true;
+  link_->RefreshRefCount();
+}
+
+void ClassicFixedChannelImpl::Release() {
+  ASSERT_LOG(user_handler_ != nullptr, "Must register OnCloseCallback before calling any methods");
+  if (closed_) {
+    LOG_WARN("%s is already closed", ToString().c_str());
+    ASSERT(!acquired_);
+    return;
+  }
+  if (!acquired_) {
+    LOG_DEBUG("%s was already released", ToString().c_str());
+    return;
+  }
+  acquired_ = false;
+  link_->RefreshRefCount();
+}
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
index 5cfd5a2..ae19578 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "l2cap/cid.h"
 #include "l2cap/classic_fixed_channel.h"
+#include "l2cap/internal/classic_fixed_channel_allocator.h"
 #include "os/handler.h"
 #include "os/log.h"
 
@@ -25,20 +26,57 @@ namespace bluetooth {
 namespace l2cap {
 namespace internal {
 
+class ClassicLink;
+
 class ClassicFixedChannelImpl {
  public:
-  ClassicFixedChannelImpl(Cid cid, os::Handler* handler) : cid_(cid), handler_(handler) {
-    ASSERT_LOG(cid_ >= kFirstFixedChannel && cid_ <= kLastFixedChannel, "Invalid cid: %d", cid_);
-    ASSERT(handler_ != nullptr);
+  ClassicFixedChannelImpl(Cid cid, ClassicLink* link, os::Handler* l2cap_handler);
+
+  virtual ~ClassicFixedChannelImpl() = default;
+
+  hci::Address GetDevice() const {
+    return device_;
+  }
+
+  virtual void RegisterOnCloseCallback(os::Handler* user_handler,
+                                       ClassicFixedChannel::OnCloseCallback on_close_callback);
+
+  virtual void Acquire();
+
+  virtual void Release();
+
+  virtual bool IsAcquired() const {
+    return acquired_;
   }
 
-  std::unique_ptr<ClassicFixedChannel> GetChannelInterface() {
-    return std::unique_ptr<ClassicFixedChannel>(new ClassicFixedChannel(handler_, this));
+  virtual void OnClosed(hci::ErrorCode status);
+
+  virtual std::string ToString() {
+    std::ostringstream ss;
+    ss << "Device " << device_ << " Cid 0x" << std::hex << cid_;
+    return ss.str();
   }
 
  private:
-  Cid cid_;
-  os::Handler* handler_;
+  // Constructor states
+  // For logging purpose only
+  const Cid cid_;
+  // For logging purpose only
+  const hci::Address device_;
+  // Needed to handle Acquire() and Release()
+  ClassicLink* link_;
+  os::Handler* l2cap_handler_;
+
+  // User supported states
+  os::Handler* user_handler_ = nullptr;
+  ClassicFixedChannel::OnCloseCallback on_close_callback_{};
+
+  // Internal states
+  bool acquired_ = false;
+  bool closed_ = false;
+  hci::ErrorCode close_reason_ = hci::ErrorCode::SUCCESS;
+
+  DISALLOW_COPY_AND_ASSIGN(ClassicFixedChannelImpl);
 };
 
 }  // namespace internal
diff --git a/gd/l2cap/internal/classic_fixed_channel_impl_mock.h b/gd/l2cap/internal/classic_fixed_channel_impl_mock.h
new file mode 100644 (file)
index 0000000..1c28474
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/internal/classic_fixed_channel_impl.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace testing {
+
+class MockClassicFixedChannelImpl : public ClassicFixedChannelImpl {
+ public:
+  MOCK_METHOD(void, RegisterOnCloseCallback,
+              (os::Handler * user_handler, ClassicFixedChannel::OnCloseCallback on_close_callback), (override));
+  MOCK_METHOD(void, Acquire, (), (override));
+  MOCK_METHOD(void, Release, (), (override));
+  MOCK_METHOD(bool, IsAcquired, (), (override, const));
+  MOCK_METHOD(void, OnClosed, (hci::ErrorCode status), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/internal/classic_fixed_channel_impl_test.cc b/gd/l2cap/internal/classic_fixed_channel_impl_test.cc
new file mode 100644 (file)
index 0000000..e03531f
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "l2cap/internal/classic_fixed_channel_impl.h"
+
+#include "common/testing/bind_test_util.h"
+#include "l2cap/cid.h"
+#include "l2cap/internal/classic_link_mock.h"
+#include "l2cap/internal/parameter_provider_mock.h"
+#include "os/handler.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+using testing::MockClassicLink;
+using testing::MockParameterProvider;
+using ::testing::Return;
+
+class L2capClassicFixedChannelImplTest : public ::testing::Test {
+ public:
+  static void SyncHandler(os::Handler* handler) {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    future.wait_for(std::chrono::milliseconds(3));
+  }
+
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    l2cap_handler_ = new os::Handler(thread_);
+  }
+
+  void TearDown() override {
+    l2cap_handler_->Clear();
+    delete l2cap_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* l2cap_handler_ = nullptr;
+};
+
+TEST_F(L2capClassicFixedChannelImplTest, get_device) {
+  MockParameterProvider mock_parameter_provider;
+  MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+  EXPECT_EQ(device, fixed_channel_impl.GetDevice());
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, close_triggers_callback) {
+  MockParameterProvider mock_parameter_provider;
+  MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+  // Channel closure should trigger such callback
+  fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, register_callback_after_close_should_call_immediately) {
+  MockParameterProvider mock_parameter_provider;
+  MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+  // Channel closure should do nothing
+  fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+
+  // Register on close callback should trigger callback immediately
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, close_twice_should_fail) {
+  MockParameterProvider mock_parameter_provider;
+  MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+  // Channel closure should trigger such callback
+  fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+  // 2nd OnClose() callback should fail
+  EXPECT_DEATH(fixed_channel_impl.OnClosed(hci::ErrorCode::PAGE_TIMEOUT), ".*OnClosed.*");
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, multiple_registeration_should_fail) {
+  MockParameterProvider mock_parameter_provider;
+  MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+  EXPECT_DEATH(fixed_channel_impl.RegisterOnCloseCallback(user_handler.get(),
+                                                          common::BindOnce([](hci::ErrorCode status) { FAIL(); })),
+               ".*RegisterOnCloseCallback.*");
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, call_acquire_before_registeration_should_fail) {
+  MockParameterProvider mock_parameter_provider;
+  MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+  EXPECT_DEATH(fixed_channel_impl.Acquire(), ".*Acquire.*");
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, call_release_before_registeration_should_fail) {
+  MockParameterProvider mock_parameter_provider;
+  MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+  EXPECT_DEATH(fixed_channel_impl.Release(), ".*Release.*");
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, test_acquire_release_channel) {
+  MockParameterProvider mock_parameter_provider;
+  MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+  // Default should be false
+  EXPECT_FALSE(fixed_channel_impl.IsAcquired());
+
+  // Should be called 2 times after Acquire() and Release()
+  EXPECT_CALL(mock_classic_link, RefreshRefCount()).Times(2);
+
+  fixed_channel_impl.Acquire();
+  EXPECT_TRUE(fixed_channel_impl.IsAcquired());
+
+  fixed_channel_impl.Release();
+  EXPECT_FALSE(fixed_channel_impl.IsAcquired());
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, test_acquire_after_close) {
+  MockParameterProvider mock_parameter_provider;
+  MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+  // Channel closure should trigger such callback
+  fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+  // Release or Acquire after closing should crash
+  EXPECT_CALL(mock_classic_link, RefreshRefCount()).Times(0);
+  EXPECT_FALSE(fixed_channel_impl.IsAcquired());
+  EXPECT_DEATH(fixed_channel_impl.Acquire(), ".*Acquire.*");
+
+  user_handler->Clear();
+}
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
index 51f6303..a2d58ce 100644 (file)
 
 #pragma once
 
+#include <chrono>
 #include <memory>
 
 #include "hci/acl_manager.h"
 #include "l2cap/internal/classic_fixed_channel_allocator.h"
+#include "l2cap/internal/classic_fixed_channel_impl.h"
+#include "l2cap/internal/parameter_provider.h"
 #include "l2cap/internal/scheduler.h"
+#include "os/alarm.h"
 
 namespace bluetooth {
 namespace l2cap {
@@ -28,17 +32,66 @@ namespace internal {
 
 class ClassicLink {
  public:
-  ClassicLink(os::Handler* l2cap_layer_handler, std::unique_ptr<hci::AclConnection> acl_connection,
-              std::unique_ptr<Scheduler> scheduler)
-      : handler_(l2cap_layer_handler), acl_connection_(std::move(acl_connection)), scheduler_(std::move(scheduler)) {}
+  ClassicLink(os::Handler* l2cap_handler, std::unique_ptr<hci::AclConnection> acl_connection,
+              std::unique_ptr<Scheduler> scheduler, ParameterProvider* parameter_provider)
+      : l2cap_handler_(l2cap_handler), acl_connection_(std::move(acl_connection)), scheduler_(std::move(scheduler)),
+        parameter_provider_(parameter_provider) {
+    ASSERT(l2cap_handler_ != nullptr);
+    ASSERT(acl_connection_ != nullptr);
+    ASSERT(scheduler_ != nullptr);
+    ASSERT(parameter_provider_ != nullptr);
+    acl_connection_->RegisterDisconnectCallback(
+        common::BindOnce(&ClassicLink::OnAclDisconnected, common::Unretained(this)), l2cap_handler_);
+    link_idle_disconnect_alarm_.Schedule(common::BindOnce(&ClassicLink::Disconnect, common::Unretained(this)),
+                                         parameter_provider_->GetClassicLinkIdleDisconnectTimeout());
+  }
 
-  friend class ClassicLinkManager;
+  virtual ~ClassicLink() = default;
+
+  inline virtual hci::Address GetDevice() {
+    return acl_connection_->GetAddress();
+  }
+
+  // ACL methods
+
+  virtual void OnAclDisconnected(hci::ErrorCode status) {
+    fixed_channel_allocator_.OnAclDisconnected(status);
+  }
+
+  virtual void Disconnect() {
+    acl_connection_->Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION);
+  }
+
+  // ClassicFixedChannel methods
+
+  virtual std::shared_ptr<ClassicFixedChannelImpl> AllocateFixedChannel(Cid cid, SecurityPolicy security_policy) {
+    return fixed_channel_allocator_.AllocateChannel(cid, security_policy);
+  }
+
+  virtual bool IsFixedChannelAllocated(Cid cid) {
+    return fixed_channel_allocator_.IsChannelAllocated(cid);
+  }
+
+  // Check how many channels are acquired or in use, if zero, start tear down timer, if non-zero, cancel tear down timer
+  virtual void RefreshRefCount() {
+    int ref_count = 0;
+    ref_count += fixed_channel_allocator_.GetRefCount();
+    ASSERT_LOG(ref_count > 0, "ref_count %d is less than 0", ref_count);
+    if (ref_count > 0) {
+      link_idle_disconnect_alarm_.Cancel();
+    } else {
+      link_idle_disconnect_alarm_.Schedule(common::BindOnce(&ClassicLink::Disconnect, common::Unretained(this)),
+                                           parameter_provider_->GetClassicLinkIdleDisconnectTimeout());
+    }
+  }
 
  private:
-  os::Handler* handler_;
-  ClassicFixedChannelAllocator fixed_channel_allocator_{handler_};
+  os::Handler* l2cap_handler_;
+  ClassicFixedChannelAllocator fixed_channel_allocator_{this, l2cap_handler_};
   std::unique_ptr<hci::AclConnection> acl_connection_;
   std::unique_ptr<Scheduler> scheduler_;
+  ParameterProvider* parameter_provider_;
+  os::Alarm link_idle_disconnect_alarm_{l2cap_handler_};
   DISALLOW_COPY_AND_ASSIGN(ClassicLink);
 };
 
index bda953b..a14fece 100644 (file)
@@ -49,14 +49,14 @@ void ClassicLinkManager::ConnectFixedChannelServices(hci::Address device,
     // Check if all registered services have an allocated channel and allocate one if not already allocated
     int num_new_channels = 0;
     for (auto& fixed_channel_service : fixed_channel_services) {
-      if (link->fixed_channel_allocator_.IsChannelInUse(fixed_channel_service.first)) {
+      if (link->IsFixedChannelAllocated(fixed_channel_service.first)) {
         // This channel is already allocated for this link, do not allocated twice
         continue;
       }
       // Allocate channel for newly registered fixed channels
-      auto* classic_fixed_channel_impl =
-          link->fixed_channel_allocator_.AllocateChannel(fixed_channel_service.first, SecurityPolicy());
-      fixed_channel_service.second->NotifyChannelCreation(classic_fixed_channel_impl->GetChannelInterface());
+      auto fixed_channel_impl = link->AllocateFixedChannel(fixed_channel_service.first, SecurityPolicy());
+      fixed_channel_service.second->NotifyChannelCreation(
+          std::make_unique<ClassicFixedChannel>(fixed_channel_impl, l2cap_handler_));
       num_new_channels++;
     }
     // Declare connection failure if no new channels are created
@@ -92,21 +92,22 @@ ClassicLink* ClassicLinkManager::GetLink(const hci::Address device) {
 
 void ClassicLinkManager::OnConnectSuccess(std::unique_ptr<hci::AclConnection> acl_connection) {
   // Same link should not be connected twice
-  hci::Address address = acl_connection->GetAddress();
-  ASSERT_LOG(GetLink(address) == nullptr, "%s is connected twice without disconnection",
+  hci::Address device = acl_connection->GetAddress();
+  ASSERT_LOG(GetLink(device) == nullptr, "%s is connected twice without disconnection",
              acl_connection->GetAddress().ToString().c_str());
   auto* link_queue_up_end = acl_connection->GetAclQueueEnd();
-  links_.try_emplace(address, handler_, std::move(acl_connection), std::make_unique<Fifo>(link_queue_up_end, handler_));
-  auto* link = GetLink(address);
+  links_.try_emplace(device, l2cap_handler_, std::move(acl_connection),
+                     std::make_unique<Fifo>(link_queue_up_end, l2cap_handler_), parameter_provider_);
+  auto* link = GetLink(device);
   // Allocate and distribute channels for all registered fixed channel services
   auto fixed_channel_services = service_manager_->GetRegisteredServices();
   for (auto& fixed_channel_service : fixed_channel_services) {
-    ClassicFixedChannelImpl* classic_fixed_channel_impl =
-        link->fixed_channel_allocator_.AllocateChannel(fixed_channel_service.first, SecurityPolicy());
-    fixed_channel_service.second->NotifyChannelCreation(classic_fixed_channel_impl->GetChannelInterface());
+    auto fixed_channel_impl = link->AllocateFixedChannel(fixed_channel_service.first, SecurityPolicy());
+    fixed_channel_service.second->NotifyChannelCreation(
+        std::make_unique<ClassicFixedChannel>(fixed_channel_impl, l2cap_handler_));
   }
   // Remove device from pending links list, if any
-  auto pending_link = pending_links_.find(address);
+  auto pending_link = pending_links_.find(device);
   if (pending_link == pending_links_.end()) {
     // This an incoming connection, exit
     return;
@@ -120,6 +121,7 @@ void ClassicLinkManager::OnConnectFail(hci::Address device, hci::ErrorCode reaso
   auto pending_link = pending_links_.find(device);
   if (pending_link == pending_links_.end()) {
     // There is no pending link, exit
+    LOG_DEBUG("Connection to %s failed without a pending link", device.ToString().c_str());
     return;
   }
   for (auto& pending_fixed_channel_connection : pending_link->second.pending_fixed_channel_connections_) {
@@ -134,6 +136,10 @@ void ClassicLinkManager::OnConnectFail(hci::Address device, hci::ErrorCode reaso
 }
 
 void ClassicLinkManager::OnDisconnect(hci::Address device, hci::ErrorCode status) {
+  auto* link = GetLink(device);
+  ASSERT_LOG(link != nullptr, "Device %s is disconnected with reason 0x%x, but not in local database",
+             device.ToString().c_str(), static_cast<uint8_t>(status));
+  link->OnAclDisconnected(status);
   links_.erase(device);
 }
 
index c07c852..80438eb 100644 (file)
@@ -22,9 +22,9 @@
 #include "os/handler.h"
 
 #include "l2cap/classic_fixed_channel_manager.h"
-#include "l2cap/internal/classic_fixed_channel_service_impl.h"
 #include "l2cap/internal/classic_fixed_channel_service_manager_impl.h"
 #include "l2cap/internal/classic_link.h"
+#include "l2cap/internal/parameter_provider.h"
 #include "l2cap/internal/scheduler.h"
 
 #include "hci/acl_manager.h"
@@ -36,10 +36,11 @@ namespace internal {
 
 class ClassicLinkManager : public hci::ConnectionCallbacks {
  public:
-  ClassicLinkManager(os::Handler* l2cap_layer_handler, hci::AclManager* acl_manager,
-                     ClassicFixedChannelServiceManagerImpl* service_manager)
-      : handler_(l2cap_layer_handler), acl_manager_(acl_manager), service_manager_(service_manager) {
-    acl_manager_->RegisterCallbacks(this, handler_);
+  ClassicLinkManager(os::Handler* l2cap_handler, hci::AclManager* acl_manager,
+                     ClassicFixedChannelServiceManagerImpl* service_manager, ParameterProvider* parameter_provider)
+      : l2cap_handler_(l2cap_handler), acl_manager_(acl_manager), service_manager_(service_manager),
+        parameter_provider_(parameter_provider) {
+    acl_manager_->RegisterCallbacks(this, l2cap_handler_);
   }
 
   struct PendingFixedChannelConnection {
@@ -51,18 +52,23 @@ class ClassicLinkManager : public hci::ConnectionCallbacks {
     std::vector<PendingFixedChannelConnection> pending_fixed_channel_connections_;
   };
 
-  void ConnectFixedChannelServices(hci::Address device, PendingFixedChannelConnection pending_fixed_channel_connection);
+  // ACL methods
 
   ClassicLink* GetLink(hci::Address device);
   void OnConnectSuccess(std::unique_ptr<hci::AclConnection> acl_connection) override;
   void OnConnectFail(hci::Address device, hci::ErrorCode reason) override;
   void OnDisconnect(hci::Address device, hci::ErrorCode status);
 
+  // ClassicFixedChannelManager methods
+
+  void ConnectFixedChannelServices(hci::Address device, PendingFixedChannelConnection pending_fixed_channel_connection);
+
  private:
   // Dependencies
-  os::Handler* handler_;
+  os::Handler* l2cap_handler_;
   hci::AclManager* acl_manager_;
   ClassicFixedChannelServiceManagerImpl* service_manager_;
+  ParameterProvider* parameter_provider_;
 
   // Internal states
   std::unordered_map<hci::Address, PendingLink> pending_links_;
index 7e6e229..a7663cf 100644 (file)
@@ -17,6 +17,7 @@
 #include "l2cap/internal/classic_link_manager.h"
 
 #include <future>
+#include <thread>
 
 #include "common/bind.h"
 #include "common/testing/bind_test_util.h"
@@ -26,6 +27,7 @@
 #include "l2cap/classic_fixed_channel_manager.h"
 #include "l2cap/internal/classic_fixed_channel_service_impl_mock.h"
 #include "l2cap/internal/classic_fixed_channel_service_manager_impl_mock.h"
+#include "l2cap/internal/parameter_provider_mock.h"
 #include "os/handler.h"
 #include "os/thread.h"
 
@@ -43,13 +45,15 @@ using ::testing::ByMove;
 using ::testing::DoAll;
 using testing::MockClassicFixedChannelServiceImpl;
 using testing::MockClassicFixedChannelServiceManagerImpl;
+using testing::MockParameterProvider;
 using ::testing::Return;
 using ::testing::SaveArg;
 
+constexpr static auto kTestIdleDisconnectTimeoutLong = std::chrono::milliseconds(1000);
+constexpr static auto kTestIdleDisconnectTimeoutShort = std::chrono::milliseconds(30);
+
 class L2capClassicLinkManagerTest : public ::testing::Test {
  public:
-  void OnFailCallback(ClassicFixedChannelManager::ConnectionResult result) {}
-
   static void SyncHandler(os::Handler* handler) {
     std::promise<void> promise;
     auto future = promise.get_future();
@@ -61,9 +65,13 @@ class L2capClassicLinkManagerTest : public ::testing::Test {
   void SetUp() override {
     thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
     l2cap_handler_ = new os::Handler(thread_);
+    mock_parameter_provider_ = new MockParameterProvider;
+    EXPECT_CALL(*mock_parameter_provider_, GetClassicLinkIdleDisconnectTimeout)
+        .WillRepeatedly(Return(kTestIdleDisconnectTimeoutLong));
   }
 
   void TearDown() override {
+    delete mock_parameter_provider_;
     l2cap_handler_->Clear();
     delete l2cap_handler_;
     delete thread_;
@@ -71,6 +79,7 @@ class L2capClassicLinkManagerTest : public ::testing::Test {
 
   os::Thread* thread_ = nullptr;
   os::Handler* l2cap_handler_ = nullptr;
+  MockParameterProvider* mock_parameter_provider_ = nullptr;
 };
 
 TEST_F(L2capClassicLinkManagerTest, connect_fixed_channel_service_without_acl) {
@@ -85,7 +94,7 @@ TEST_F(L2capClassicLinkManagerTest, connect_fixed_channel_service_without_acl) {
   EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
       .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
   ClassicLinkManager classic_link_manager(l2cap_handler_, &mock_acl_manager,
-                                          &mock_classic_fixed_channel_service_manager);
+                                          &mock_classic_fixed_channel_service_manager, mock_parameter_provider_);
   EXPECT_EQ(hci_connection_callbacks, &classic_link_manager);
   EXPECT_EQ(hci_callback_handler, l2cap_handler_);
 
@@ -161,7 +170,7 @@ TEST_F(L2capClassicLinkManagerTest, connect_fixed_channel_service_without_acl_wi
   EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
       .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
   ClassicLinkManager classic_link_manager(l2cap_handler_, &mock_acl_manager,
-                                          &mock_classic_fixed_channel_service_manager);
+                                          &mock_classic_fixed_channel_service_manager, mock_parameter_provider_);
   EXPECT_EQ(hci_connection_callbacks, &classic_link_manager);
   EXPECT_EQ(hci_callback_handler, l2cap_handler_);
 
@@ -196,7 +205,7 @@ TEST_F(L2capClassicLinkManagerTest, connect_fixed_channel_service_without_acl_wi
   EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
       .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
   ClassicLinkManager classic_link_manager(l2cap_handler_, &mock_acl_manager,
-                                          &mock_classic_fixed_channel_service_manager);
+                                          &mock_classic_fixed_channel_service_manager, mock_parameter_provider_);
   EXPECT_EQ(hci_connection_callbacks, &classic_link_manager);
   EXPECT_EQ(hci_callback_handler, l2cap_handler_);
 
@@ -228,6 +237,74 @@ TEST_F(L2capClassicLinkManagerTest, connect_fixed_channel_service_without_acl_wi
   user_handler->Clear();
 }
 
+TEST_F(L2capClassicLinkManagerTest, not_acquiring_channels_should_disconnect_acl_after_timeout) {
+  EXPECT_CALL(*mock_parameter_provider_, GetClassicLinkIdleDisconnectTimeout)
+      .WillRepeatedly(Return(kTestIdleDisconnectTimeoutShort));
+  MockClassicFixedChannelServiceManagerImpl mock_classic_fixed_channel_service_manager;
+  MockAclManager mock_acl_manager;
+  hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+
+  // Step 1: Verify callback registration with HCI
+  hci::ConnectionCallbacks* hci_connection_callbacks = nullptr;
+  os::Handler* hci_callback_handler = nullptr;
+  EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
+  ClassicLinkManager classic_link_manager(l2cap_handler_, &mock_acl_manager,
+                                          &mock_classic_fixed_channel_service_manager, mock_parameter_provider_);
+  EXPECT_EQ(hci_connection_callbacks, &classic_link_manager);
+  EXPECT_EQ(hci_callback_handler, l2cap_handler_);
+
+  // Register fake services
+  MockClassicFixedChannelServiceImpl mock_service_1, mock_service_2;
+  std::vector<std::pair<Cid, ClassicFixedChannelServiceImpl*>> results;
+  results.emplace_back(kSmpBrCid, &mock_service_1);
+  results.emplace_back(kConnectionlessCid, &mock_service_2);
+  EXPECT_CALL(mock_classic_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
+
+  // Step 2: Connect to fixed channel without ACL connection should trigger ACL connection process
+  EXPECT_CALL(mock_acl_manager, CreateConnection(device)).Times(1);
+  ClassicLinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
+      .handler_ = user_handler.get(),
+      .on_fail_callback_ = common::BindOnce([](ClassicFixedChannelManager::ConnectionResult result) { FAIL(); })};
+  classic_link_manager.ConnectFixedChannelServices(device, std::move(pending_fixed_channel_connection));
+
+  // Step 3: ACL connection success event should trigger channel creation for all registered services
+  auto* raw_acl_connection = new MockAclConnection();
+  std::unique_ptr<MockAclConnection> acl_connection(raw_acl_connection);
+  hci::AclConnection::Queue link_queue{10};
+  EXPECT_CALL(*acl_connection, GetAclQueueEnd()).WillRepeatedly((Return(link_queue.GetUpEnd())));
+  EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(device));
+  std::unique_ptr<ClassicFixedChannel> channel_1, channel_2;
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1](std::unique_ptr<ClassicFixedChannel> channel) { channel_1 = std::move(channel); });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2](std::unique_ptr<ClassicFixedChannel> channel) { channel_2 = std::move(channel); });
+  hci_callback_handler->Post(common::BindOnce(&hci::ConnectionCallbacks::OnConnectSuccess,
+                                              common::Unretained(hci_connection_callbacks), std::move(acl_connection)));
+  SyncHandler(hci_callback_handler);
+  EXPECT_NE(channel_1, nullptr);
+  EXPECT_NE(channel_2, nullptr);
+  hci::ErrorCode status_1 = hci::ErrorCode::SUCCESS;
+  channel_1->RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_1 = status; }));
+  hci::ErrorCode status_2 = hci::ErrorCode::SUCCESS;
+  channel_2->RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_2 = status; }));
+
+  // Step 4: Leave channel IDLE long enough, they will disconnect
+  EXPECT_CALL(*raw_acl_connection, Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION)).Times(1);
+  std::this_thread::sleep_for(kTestIdleDisconnectTimeoutShort * 1.2);
+
+  // Step 5: Link disconnect will trigger all callbacks
+  classic_link_manager.OnDisconnect(device, hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_1);
+  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_2);
+
+  user_handler->Clear();
+}
+
 }  // namespace internal
 }  // namespace l2cap
 }  // namespace bluetooth
diff --git a/gd/l2cap/internal/classic_link_mock.h b/gd/l2cap/internal/classic_link_mock.h
new file mode 100644 (file)
index 0000000..0b03f55
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "hci/acl_manager_mock.h"
+#include "hci/address.h"
+#include "l2cap/internal/classic_link.h"
+#include "l2cap/internal/scheduler_mock.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace testing {
+
+using hci::testing::MockAclConnection;
+
+class MockClassicLink : public ClassicLink {
+ public:
+  explicit MockClassicLink(os::Handler* handler, ParameterProvider* parameter_provider)
+      : ClassicLink(handler, std::make_unique<MockAclConnection>(), std::make_unique<MockScheduler>(),
+                    parameter_provider){};
+  MOCK_METHOD(hci::Address, GetDevice, (), (override));
+  MOCK_METHOD(void, OnAclDisconnected, (hci::ErrorCode status), (override));
+  MOCK_METHOD(void, Disconnect, (), (override));
+  MOCK_METHOD(std::shared_ptr<ClassicFixedChannelImpl>, AllocateFixedChannel, (Cid cid, SecurityPolicy security_policy),
+              (override));
+  MOCK_METHOD(bool, IsFixedChannelAllocated, (Cid cid), (override));
+  MOCK_METHOD(void, RefreshRefCount, (), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/internal/parameter_provider.h b/gd/l2cap/internal/parameter_provider.h
new file mode 100644 (file)
index 0000000..2890f43
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+/**
+ * A class that provide constant parameters to the L2CAP stack
+ *
+ * All methods are virtual so that they can be override in unit tests
+ */
+class ParameterProvider {
+ public:
+  virtual ~ParameterProvider() = default;
+  virtual std::chrono::milliseconds GetClassicLinkIdleDisconnectTimeout() {
+    return std::chrono::seconds(20);
+  }
+};
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/parameter_provider_mock.h b/gd/l2cap/internal/parameter_provider_mock.h
new file mode 100644 (file)
index 0000000..6fd2907
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/internal/parameter_provider.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace testing {
+
+class MockParameterProvider : public ParameterProvider {
+ public:
+  MOCK_METHOD(std::chrono::milliseconds, GetClassicLinkIdleDisconnectTimeout, (), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/internal/scheduler_mock.h b/gd/l2cap/internal/scheduler_mock.h
new file mode 100644 (file)
index 0000000..b49e138
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/internal/scheduler.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace testing {
+
+using hci::testing::MockAclConnection;
+
+class MockScheduler : public Scheduler {
+ public:
+  MOCK_METHOD(void, AttachChannel, (Cid cid, UpperQueueDownEnd* channel_down_end), (override));
+  MOCK_METHOD(void, DetachChannel, (Cid cid), (override));
+  MOCK_METHOD(LowerQueueUpEnd*, GetLowerQueueUpEnd, (), (override, const));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
index 087e58c..71c0635 100644 (file)
@@ -23,6 +23,7 @@
 #include "hci/hci_packets.h"
 #include "l2cap/internal/classic_fixed_channel_service_manager_impl.h"
 #include "l2cap/internal/classic_link_manager.h"
+#include "l2cap/internal/parameter_provider.h"
 #include "module.h"
 #include "os/handler.h"
 #include "os/log.h"
@@ -35,12 +36,14 @@ namespace l2cap {
 const ModuleFactory L2capLayer::Factory = ModuleFactory([]() { return new L2capLayer(); });
 
 struct L2capLayer::impl {
-  impl(os::Handler* handler, hci::AclManager* acl_manager) : handler_(handler), acl_manager_(acl_manager) {}
-  os::Handler* handler_;
+  impl(os::Handler* l2cap_handler, hci::AclManager* acl_manager)
+      : l2cap_handler_(l2cap_handler), acl_manager_(acl_manager) {}
+  os::Handler* l2cap_handler_;
   hci::AclManager* acl_manager_;
-  internal::ClassicFixedChannelServiceManagerImpl classic_fixed_channel_service_manager_impl_{handler_};
-  internal::ClassicLinkManager classic_link_manager_{handler_, acl_manager_,
-                                                     &classic_fixed_channel_service_manager_impl_};
+  internal::ParameterProvider parameter_provider_;
+  internal::ClassicFixedChannelServiceManagerImpl classic_fixed_channel_service_manager_impl_{l2cap_handler_};
+  internal::ClassicLinkManager classic_link_manager_{
+      l2cap_handler_, acl_manager_, &classic_fixed_channel_service_manager_impl_, &parameter_provider_};
 };
 
 void L2capLayer::ListDependencies(ModuleList* list) {
@@ -57,7 +60,7 @@ void L2capLayer::Stop() {
 
 std::unique_ptr<ClassicFixedChannelManager> L2capLayer::GetClassicFixedChannelManager() {
   return std::unique_ptr<ClassicFixedChannelManager>(new ClassicFixedChannelManager(
-      &pimpl_->classic_fixed_channel_service_manager_impl_, &pimpl_->classic_link_manager_, pimpl_->handler_));
+      &pimpl_->classic_fixed_channel_service_manager_impl_, &pimpl_->classic_link_manager_, pimpl_->l2cap_handler_));
 }
 
 }  // namespace l2cap