Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d539cc98 authored by Hansong Zhang's avatar Hansong Zhang
Browse files

L2CAP Classic dynamic channel and allocator impl

Test: bluetooth_test_gd and cert/run_cert.sh
Bug: 138260719
Change-Id: I15f40c6b55d180003afaa14252c576b1b2a5e25e
parent f43ff6bf
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -9,6 +9,8 @@ filegroup {
        "classic_fixed_channel.cc",
        "classic_fixed_channel_manager.cc",
        "classic_fixed_channel_service.cc",
        "internal/classic_dynamic_channel_allocator.cc",
        "internal/classic_dynamic_channel_impl.cc",
        "internal/classic_fixed_channel_allocator.cc",
        "internal/classic_fixed_channel_impl.cc",
        "internal/classic_fixed_channel_service_manager_impl.cc",
@@ -21,6 +23,8 @@ filegroup {
    name: "BluetoothL2capTestSources",
    srcs: [
        "l2cap_packet_test.cc",
        "internal/classic_dynamic_channel_allocator_test.cc",
        "internal/classic_dynamic_channel_impl_test.cc",
        "internal/classic_fixed_channel_allocator_test.cc",
        "internal/classic_fixed_channel_impl_test.cc",
        "internal/classic_fixed_channel_service_manager_test.cc",
+92 −0
Original line number Diff line number Diff line
/*
 * 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 "classic_dynamic_channel_allocator.h"
#include "l2cap/cid.h"
#include "l2cap/internal/classic_dynamic_channel_allocator.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 {

std::shared_ptr<ClassicDynamicChannelImpl> ClassicDynamicChannelAllocator::AllocateChannel(
    Psm psm, Cid remote_cid, SecurityPolicy security_policy) {
  if (IsChannelAllocated((psm))) {
    LOG_INFO("Psm 0x%x for device %s is already in use", psm, link_->GetDevice().ToString().c_str());
    return nullptr;
  }
  if (!IsPsmValid(psm)) {
    LOG_INFO("Psm 0x%x is invalid", psm);
    return nullptr;
  }
  if (used_remote_cid_.find(remote_cid) != used_remote_cid_.end()) {
    LOG_INFO("Remote cid 0x%x is used", remote_cid);
    return nullptr;
  }
  Cid cid = kFirstDynamicChannel;
  for (; cid <= kLastDynamicChannel; cid++) {
    LOG_INFO();
    if (used_cid_.count(cid) == 0) break;
  }
  if (cid > kLastDynamicChannel) {
    LOG_WARN("All cid are used");
    return nullptr;
  }
  auto elem = channels_.try_emplace(
      psm, std::make_shared<ClassicDynamicChannelImpl>(psm, cid, remote_cid, link_, l2cap_handler_));
  ASSERT_LOG(elem.second, "Failed to create channel for psm 0x%x device %s", psm,
             link_->GetDevice().ToString().c_str());
  ASSERT(elem.first->second != nullptr);
  used_cid_.insert(cid);
  used_remote_cid_.insert(remote_cid);
  return elem.first->second;
}

void ClassicDynamicChannelAllocator::FreeChannel(Psm psm) {
  ASSERT_LOG(IsChannelAllocated(psm), "Channel is not in use: psm %d, device %s", psm,
             link_->GetDevice().ToString().c_str());
  channels_.erase(psm);
}

bool ClassicDynamicChannelAllocator::IsChannelAllocated(Psm psm) const {
  return channels_.find(psm) != channels_.end();
}

std::shared_ptr<ClassicDynamicChannelImpl> ClassicDynamicChannelAllocator::FindChannel(Psm psm) {
  ASSERT_LOG(IsChannelAllocated(psm), "Channel is not in use: psm %d, device %s", psm,
             link_->GetDevice().ToString().c_str());
  return channels_.find(psm)->second;
}

size_t ClassicDynamicChannelAllocator::NumberOfChannels() const {
  return channels_.size();
}

void ClassicDynamicChannelAllocator::OnAclDisconnected(hci::ErrorCode reason) {
  for (auto& elem : channels_) {
    elem.second->OnClosed(reason);
  }
}

}  // namespace internal
}  // namespace l2cap
}  // namespace bluetooth
+70 −0
Original line number Diff line number Diff line
/*
 * 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 <set>
#include <unordered_map>

#include "l2cap/cid.h"
#include "l2cap/internal/classic_dynamic_channel_impl.h"
#include "l2cap/psm.h"
#include "l2cap/security_policy.h"
#include "os/handler.h"
#include "os/log.h"

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 ClassicDynamicChannelAllocator {
 public:
  ClassicDynamicChannelAllocator(ClassicLink* link, os::Handler* l2cap_handler)
      : link_(link), l2cap_handler_(l2cap_handler) {
    ASSERT(link_ != nullptr);
    ASSERT(l2cap_handler_ != nullptr);
  }

  // Allocates a channel. If psm is used, OR the remote cid already exists, return nullptr.
  // NOTE: The returned ClassicDynamicChannelImpl object is still owned by the channel allocator, NOT the client.
  std::shared_ptr<ClassicDynamicChannelImpl> AllocateChannel(Psm psm, Cid remote_cid, SecurityPolicy security_policy);

  // Frees a channel. If psm doesn't exist, it will crash
  void FreeChannel(Psm psm);

  bool IsChannelAllocated(Psm psm) const;

  std::shared_ptr<ClassicDynamicChannelImpl> FindChannel(Psm psm);

  size_t NumberOfChannels() const;

  void OnAclDisconnected(hci::ErrorCode hci_status);

 private:
  ClassicLink* link_;
  os::Handler* l2cap_handler_;
  std::unordered_map<Psm, std::shared_ptr<ClassicDynamicChannelImpl>> channels_;
  std::set<Cid> used_cid_;
  std::set<Cid> used_remote_cid_;
};

}  // namespace internal
}  // namespace l2cap
}  // namespace bluetooth
+78 −0
Original line number Diff line number Diff line
/*
 * 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_dynamic_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 L2capClassicDynamicChannelAllocatorTest : public ::testing::Test {
 protected:
  void SetUp() override {
    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
    handler_ = new os::Handler(thread_);
    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<ClassicDynamicChannelAllocator>(mock_classic_link_, handler_);
  }

  void TearDown() override {
    channel_allocator_.reset();
    delete mock_classic_link_;
    delete mock_parameter_provider_;
    handler_->Clear();
    delete handler_;
    delete thread_;
  }

  os::Thread* thread_{nullptr};
  os::Handler* handler_{nullptr};
  MockParameterProvider* mock_parameter_provider_{nullptr};
  MockClassicLink* mock_classic_link_{nullptr};
  std::unique_ptr<ClassicDynamicChannelAllocator> channel_allocator_;
};

TEST_F(L2capClassicDynamicChannelAllocatorTest, precondition) {
  Psm psm = 0x03;
  EXPECT_FALSE(channel_allocator_->IsChannelAllocated(psm));
}

TEST_F(L2capClassicDynamicChannelAllocatorTest, allocate_and_free_channel) {
  Psm psm = 0x03;
  Cid remote_cid = kFirstDynamicChannel;
  auto channel = channel_allocator_->AllocateChannel(psm, remote_cid, {});
  EXPECT_TRUE(channel_allocator_->IsChannelAllocated(psm));
  EXPECT_EQ(channel, channel_allocator_->FindChannel(psm));
  ASSERT_NO_FATAL_FAILURE(channel_allocator_->FreeChannel(psm));
  EXPECT_FALSE(channel_allocator_->IsChannelAllocated(psm));
}

}  // namespace internal
}  // namespace l2cap
}  // namespace bluetooth
+83 −0
Original line number Diff line number Diff line
/*
 * 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_dynamic_channel_impl.h"
#include "l2cap/internal/classic_link.h"
#include "l2cap/psm.h"
#include "l2cap/security_policy.h"
#include "os/handler.h"
#include "os/log.h"

namespace bluetooth {
namespace l2cap {
namespace internal {

ClassicDynamicChannelImpl::ClassicDynamicChannelImpl(Psm psm, Cid cid, Cid remote_cid, ClassicLink* link,
                                                     os::Handler* l2cap_handler)
    : psm_(psm), cid_(cid), remote_cid_(remote_cid), link_(link), l2cap_handler_(l2cap_handler) {
  ASSERT(IsPsmValid(psm_));
  ASSERT(cid_ > 0);
  ASSERT(remote_cid_ > 0);
  ASSERT(link_ != nullptr);
  ASSERT(l2cap_handler_ != nullptr);
}

hci::Address ClassicDynamicChannelImpl::GetDevice() const {
  return link_->GetDevice();
}

void ClassicDynamicChannelImpl::RegisterOnCloseCallback(os::Handler* user_handler,
                                                        ClassicDynamicChannel::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 ClassicDynamicChannelImpl::Close() {}

void ClassicDynamicChannelImpl::OnClosed(hci::ErrorCode status) {
  ASSERT_LOG(!closed_, "Device %s Cid 0x%x closed twice, old status 0x%x, new status 0x%x",
             link_->GetDevice().ToString().c_str(), cid_, static_cast<int>(close_reason_), static_cast<int>(status));
  closed_ = true;
  close_reason_ = status;
  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();
}

std::string ClassicDynamicChannelImpl::ToString() {
  std::ostringstream ss;
  ss << "Device " << link_->GetDevice().ToString() << "Psm 0x" << std::hex << psm_ << " Cid 0x" << std::hex << cid_;
  return ss.str();
}

}  // namespace internal
}  // namespace l2cap
}  // namespace bluetooth
Loading