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

Commit 49ba8dc8 authored by Hansong Zhang's avatar Hansong Zhang
Browse files

L2CAP Classic dynamic channel and allocator impl

Reland I15f40c6b55d180003afaa14252c576b1b2a5e25e L2CAP Classic dynamic
channel and allocator impl, with the fix of NPE

Test: bluetooth_test_gd and cert/run_cert.sh
Bug: 138260719
Change-Id: I5c42dcb5871086903ac0f87441235ff75bfc8fdf
parent d8ac6c1f
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
+86 −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),
      device_(link->GetDevice()) {
  ASSERT(IsPsmValid(psm_));
  ASSERT(cid_ > 0);
  ASSERT(remote_cid_ > 0);
  ASSERT(link_ != nullptr);
  ASSERT(l2cap_handler_ != nullptr);
}

hci::Address ClassicDynamicChannelImpl::GetDevice() const {
  return device_;
}

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() {
  // TODO: Implement it by sending signalling packet
}

void ClassicDynamicChannelImpl::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;
  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 " << device_.ToString() << "Psm 0x" << std::hex << psm_ << " Cid 0x" << std::hex << cid_;
  return ss.str();
}

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