~aleteoryx/muditaos

f9b80ac26c46c6b37d9c28d791647273c321ab86 — SP2FET 4 years ago 81e49a9
[EGD-5764] Implement handling multiple BT profiles

To be able to connect to the BT device with more than one audio
profile, we need something to manage those profiles. Created here
ProfileManager made it convenient
To be able to work properly with BT, CPU freq has to be at least
at Level_5 - to be fixed!
26 files changed, 337 insertions(+), 114 deletions(-)

M module-apps/application-settings-new/models/BluetoothSettingsModel.cpp
M module-apps/application-settings-new/models/BluetoothSettingsModel.hpp
M module-apps/application-settings-new/windows/AddDeviceWindow.cpp
M module-apps/application-settings-new/windows/AllDevicesWindow.cpp
M module-apps/application-settings/windows/BtWindow.cpp
M module-bluetooth/Bluetooth/BluetoothWorker.cpp
M module-bluetooth/Bluetooth/BluetoothWorker.hpp
M module-bluetooth/Bluetooth/BtKeysStorage.cpp
M module-bluetooth/Bluetooth/CommandHandler.cpp
M module-bluetooth/Bluetooth/CommandHandler.hpp
M module-bluetooth/Bluetooth/WorkerController.cpp
M module-bluetooth/Bluetooth/btstack_config.h
M module-bluetooth/Bluetooth/glucode/BluetoothRunLoop.hpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp
M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp
M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp
M module-bluetooth/Bluetooth/interface/profiles/HSP/HSPImpl.hpp
M module-bluetooth/Bluetooth/interface/profiles/Profile.hpp
A module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp
A module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp
M module-bluetooth/CMakeLists.txt
M module-services/service-bluetooth/ServiceBluetooth.cpp
M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp
M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp
M module-apps/application-settings-new/models/BluetoothSettingsModel.cpp => module-apps/application-settings-new/models/BluetoothSettingsModel.cpp +4 -5
@@ 57,11 57,6 @@ void BluetoothSettingsModel::stopScan()
                                 service::name::bluetooth);
}

void BluetoothSettingsModel::setAddrForAudioProfiles(std::string addr)
{
    application->bus.sendUnicast(std::make_shared<BluetoothAddrMessage>(std::move(addr)), service::name::bluetooth);
}

void BluetoothSettingsModel::requestDevicePairing(std::string addr)
{
    application->bus.sendUnicast(std::make_shared<BluetoothPairMessage>(std::move(addr)), service::name::bluetooth);


@@ 72,3 67,7 @@ void BluetoothSettingsModel::responsePasskey(std::string passkey)
    application->bus.sendUnicast(std::make_shared<message::bluetooth::ResponsePasskey>(std::move(passkey)),
                                 service::name::bluetooth);
}
void BluetoothSettingsModel::requestAudioConnection(std::string addr)
{
    application->bus.sendUnicast(std::make_shared<BluetoothAddrMessage>(std::move(addr)), service::name::bluetooth);
}

M module-apps/application-settings-new/models/BluetoothSettingsModel.hpp => module-apps/application-settings-new/models/BluetoothSettingsModel.hpp +1 -1
@@ 21,10 21,10 @@ class BluetoothSettingsModel
    void setDeviceName(const UTF8 &deviceName);
    void requestBondedDevices();
    void requestScan();
    void requestAudioConnection(std::string addr);
    void stopScan();
    void requestDevicePairing(std::string addr);
    void responsePasskey(std::string passkey);
    void setAddrForAudioProfiles(std::string addr);

  private:
    app::Application *application = nullptr;

M module-apps/application-settings-new/windows/AddDeviceWindow.cpp => module-apps/application-settings-new/windows/AddDeviceWindow.cpp +0 -1
@@ 43,7 43,6 @@ namespace gui
                device.name,
                [=](gui::Item & /*unused*/) {
                    LOG_DEBUG("Device: %s", device.name.c_str());
                    bluetoothSettingsModel->setAddrForAudioProfiles(bd_addr_to_str(device.address));
                    bluetoothSettingsModel->requestDevicePairing(bd_addr_to_str(device.address));
                    return true;
                },

M module-apps/application-settings-new/windows/AllDevicesWindow.cpp => module-apps/application-settings-new/windows/AllDevicesWindow.cpp +2 -0
@@ 70,6 70,8 @@ namespace gui
                device.name,
                [=](gui::Item & /*item*/) {
                    LOG_DEBUG("Device: %s", device.name.c_str());
                    std::string addr{bd_addr_to_str(device.address)};
                    bluetoothSettingsModel->requestAudioConnection(std::move(addr));
                    return true;
                },
                nullptr,

M module-apps/application-settings/windows/BtWindow.cpp => module-apps/application-settings/windows/BtWindow.cpp +15 -12
@@ 63,14 63,12 @@ namespace gui
        setTitle(utils::localize.get("app_settings_bt"));

        LOG_INFO("Create box layout");
        box = new gui::VBox(this, 0, title->offset_h(), style::window_width, 7 * style::window::label::default_h);
        box = new gui::VBox(this, 0, title->offset_h(), style::window_width, 8 * style::window::label::default_h);
        box->setEdges(RectangleEdge::None);

        // TODO WIP: it's just for usability now
        // TODO scan should return async - handle that... (if in scan -> add to list and refresh if new on window)
        add_box_label(box, "Bluetooth on off", [=](Item &) {
            LOG_DEBUG("Callback Bluetooth on");
            message_bt(application, BluetoothMessage::Request::Start);

            for (auto &el : box->children) {
                el->visible = true;
            }


@@ 92,21 90,26 @@ namespace gui
            return true;
        });

        add_box_label(box, "  -> Set visible", [=](Item &) {
            LOG_DEBUG("Callback set visibility");
            message_bt(application, BluetoothMessage::Request::Visible);
        add_box_label(box, "  -> Switch A2DP <-> HSP", [=](Item &) {
            LOG_DEBUG("Callback switch profile");
            message_bt(application, BluetoothMessage::Request::SwitchProfile);
            return true;
        });

        add_box_label(box, "  -> Play", [=](Item &) {
            LOG_DEBUG("Start playback");
        add_box_label(box, "  -> Start stream", [=](Item &) {
            LOG_DEBUG("Start stream");
            message_bt(application, BluetoothMessage::Request::Play);
            return true;
        });

        add_box_label(box, "  -> Stop", [=](Item &) {
            LOG_DEBUG("Stop playback");
            message_bt(application, BluetoothMessage::Request::StopPlayback);
        add_box_label(box, "  -> Stop stream", [=](Item &) {
            LOG_DEBUG("Stop stream");
            message_bt(application, BluetoothMessage::Request::Stop);
            return true;
        });
        add_box_label(box, "  -> Disconnect audio", [=](Item &) {
            LOG_DEBUG("Disconnect");
            message_bt(application, BluetoothMessage::Request::Disconnect);
            return true;
        });


M module-bluetooth/Bluetooth/BluetoothWorker.cpp => module-bluetooth/Bluetooth/BluetoothWorker.cpp +4 -9
@@ 71,12 71,12 @@ namespace
    auto createStatefulController(sys::Service *service,
                                  bluetooth::RunLoop *loop,
                                  std::shared_ptr<bluetooth::SettingsHolder> settings,
                                  std::shared_ptr<bluetooth::Profile> currentProfile,
                                  std::shared_ptr<bluetooth::ProfileManager> profileManager,
                                  DeviceRegistration::OnLinkKeyAddedCallback &&onLinkKeyAddedCallback)
    {
        auto driver = std::make_shared<bluetooth::Driver>(loop->getRunLoopInstance(), service);
        auto commandHandler =
            std::make_unique<bluetooth::CommandHandler>(service, settings, std::move(currentProfile), driver);
            std::make_unique<bluetooth::CommandHandler>(service, settings, std::move(profileManager), driver);
        return std::make_unique<bluetooth::StatefulController>(
            std::move(driver),
            std::move(commandHandler),


@@ 85,11 85,11 @@ namespace
} // namespace

BluetoothWorker::BluetoothWorker(sys::Service *service)
    : Worker(service), service(service), currentProfile(std::make_shared<bluetooth::HSP>()),
    : Worker(service), service(service), profileManager(std::make_shared<bluetooth::ProfileManager>(service)),
      settings(static_cast<ServiceBluetooth *>(service)->settingsHolder),
      runLoop(std::make_unique<bluetooth::RunLoop>()),
      controller{createStatefulController(
          service, runLoop.get(), settings, currentProfile, [this](const std::string &addr) { onLinkKeyAdded(addr); })}
          service, runLoop.get(), settings, profileManager, [this](const std::string &addr) { onLinkKeyAdded(addr); })}
{
    init({
        {queues::io, sizeof(bluetooth::Message), queues::queueLength},


@@ 247,11 247,6 @@ auto BluetoothWorker::handleMessage(uint32_t queueID) -> bool
    return true;
}

void BluetoothWorker::setDeviceAddress(bd_addr_t addr)
{
    currentProfile->setDeviceAddress(addr);
}

auto BluetoothWorker::deinit() -> bool
{
    controller->turnOff();

M module-bluetooth/Bluetooth/BluetoothWorker.hpp => module-bluetooth/Bluetooth/BluetoothWorker.hpp +1 -2
@@ 98,12 98,11 @@ class BluetoothWorker : private sys::Worker
    auto handleBtStackTrigger(QueueHandle_t queue) -> bool;

    bool run() override;
    void setDeviceAddress(bd_addr_t addr);
    auto deinit() -> bool override;

    /// bluetooth stack id in use
    unsigned long active_features;
    std::shared_ptr<bluetooth::Profile> currentProfile;
    std::shared_ptr<bluetooth::ProfileManager> profileManager;
    std::shared_ptr<bluetooth::SettingsHolder> settings;
    std::vector<Devicei> pairedDevices;
    std::unique_ptr<bluetooth::RunLoop> runLoop;

M module-bluetooth/Bluetooth/BtKeysStorage.cpp => module-bluetooth/Bluetooth/BtKeysStorage.cpp +5 -2
@@ 72,7 72,8 @@ namespace bluetooth
    {
        if (type != nullptr && bd_addr != nullptr) {
            LOG_INFO("getting key for address %s from API", bd_addr_to_str(bd_addr));
            if (keys.size() == 0) {
            if (keys.empty()) {
                LOG_ERROR("Keys empty!");
                return 0;
            }
            for (auto key : keys) {


@@ 81,11 82,13 @@ namespace bluetooth

                if (bd_addr_cmp(addr, bd_addr) == 0) {
                    auto foundLinkKey = key[strings::link_key].string_value().c_str();
                    memcpy(link_key, foundLinkKey, sizeof(link_key_t));
                    memcpy(link_key, reinterpret_cast<const uint8_t *>(foundLinkKey), sizeof(link_key_t));
                    LOG_INFO("Getting key: %s", foundLinkKey);
                    *type = static_cast<link_key_type_t>(key[strings::type].int_value());

                    return 1;
                }
                LOG_ERROR("Can't find key for this address!");
            }
        }
        return 0;

M module-bluetooth/Bluetooth/CommandHandler.cpp => module-bluetooth/Bluetooth/CommandHandler.cpp +33 -24
@@ 29,9 29,9 @@ namespace bluetooth

    CommandHandler::CommandHandler(sys::Service *service,
                                   std::shared_ptr<bluetooth::SettingsHolder> settings,
                                   std::shared_ptr<bluetooth::Profile> currentProfile,
                                   std::shared_ptr<bluetooth::ProfileManager> profileManager,
                                   std::shared_ptr<bluetooth::AbstractDriver> driver)
        : service{service}, settings{std::move(settings)}, currentProfile{std::move(currentProfile)}, driver{std::move(
        : service{service}, settings{std::move(settings)}, profileManager{std::move(profileManager)}, driver{std::move(
                                                                                                          driver)}
    {}



@@ 47,19 47,27 @@ namespace bluetooth
        case bluetooth::Command::StartPan:
            return startPan();
        case bluetooth::Command::Pair:
            return pair(command.getArgument().value());
            return pair(command.getAddress());
        case bluetooth::Command::VisibilityOn:
            return setVisibility(true);
        case bluetooth::Command::VisibilityOff:
            return setVisibility(false);
        case bluetooth::Command::ConnectAudio:
            return establishAudioConnection();
            return establishAudioConnection(command.getAddress());
        case bluetooth::Command::DisconnectAudio:
            return disconnectAudioConnection();
        case bluetooth::Command::PowerOff:
            return Error::Success;
        case bluetooth::Command::SwitchProfile:
            return switchAudioProfile();
        case bluetooth::Command::None:
            return Error::Success;
        case Command::StartStream:
            profileManager->start();
            return Error::Success;
        case Command::StopStream:
            profileManager->stop();
            return Error::Success;
        }
        return Error::LibraryError;
    }


@@ 102,38 110,39 @@ namespace bluetooth
        return Error::Success;
    }

    Error::Code CommandHandler::establishAudioConnection()
    Error::Code CommandHandler::establishAudioConnection(uint8_t *addr)
    {
        currentProfile->setOwnerService(service);
        if (const auto status = currentProfile->init(); status != bluetooth::Error::Success) {
            return status;
        }
        profileManager->init();
        LOG_INFO("Connecting audio with %s", bd_addr_to_str(addr));
        profileManager->connect(addr);

        currentProfile->connect();
        return Error::Success;
    }

    Error::Code CommandHandler::disconnectAudioConnection()
    {
        currentProfile->disconnect();
        profileManager->disconnect();
        return Error::Success;
    }
    Error::Code CommandHandler::pair(CommandArgument arg)
    Error::Code CommandHandler::pair(bd_addr_t addr)
    {
        try {
            auto addrString = std::get<std::string>(arg);
            bd_addr_t addr;
            if (sscanf_bd_addr(addrString.c_str(), addr) != 0) {
                LOG_INFO("Pairing with %s", addrString.c_str());
                driver->pair(addr);
            }
            else {
                return Error::SystemError;
            }
        LOG_INFO("Pairing with %s", bd_addr_to_str(addr));

        return driver->pair(addr) ? Error::Success : Error::LibraryError;
    }
    Error::Code CommandHandler::switchAudioProfile()
    {
        static auto profile = AudioProfile::A2DP;
        if (profile == AudioProfile::A2DP) {
            profile = AudioProfile::HSP;
            LOG_INFO("New profile: HSP");
        }
        catch (const std::bad_variant_access &) {
            return Error::SystemError;
        else {
            profile = AudioProfile::A2DP;
            LOG_INFO("New profile: A2DP");
        }
        profileManager->switchProfile(profile);
        return Error::Success;
    }

} // namespace bluetooth

M module-bluetooth/Bluetooth/CommandHandler.hpp => module-bluetooth/Bluetooth/CommandHandler.hpp +22 -13
@@ 4,7 4,7 @@
#pragma once

#include "Error.hpp"
#include "interface/profiles/Profile.hpp"
#include "interface/profiles/ProfileManager.hpp"
#include "interface/BluetoothDriver.hpp"

#include <cstdint>


@@ 17,11 17,10 @@ namespace sys
namespace bluetooth
{
    class SettingsHolder;
    using CommandArgument = std::variant<std::string, bool, int>;
    class Command
    {
      public:
        enum Type : std::uint8_t
        enum Type
        {
            StartScan,
            StopScan,


@@ 33,25 32,34 @@ namespace bluetooth
            PowerOn,
            PowerOff,
            Pair,
            StartStream,
            StopStream,
            SwitchProfile,
            None,
        };

        Command(Command::Type type, std::optional<CommandArgument> arg = std::nullopt)
            : argument(std::move(arg)), type(type)
        {}
        explicit Command(Command::Type type, std::optional<uint8_t *> addr = std::nullopt) : type(type)
        {
            if (addr) {
                bd_addr_copy(address, addr.value());
            }
            else {
                memset(address, 0, sizeof(bd_addr_t));
            }
        }

        auto getType() const noexcept -> Command::Type
        {
            return type;
        }

        auto getArgument() const -> std::optional<CommandArgument>
        auto getAddress() -> uint8_t *
        {
            return argument;
            return address;
        }

      private:
        std::optional<CommandArgument> argument;
        bd_addr_t address{};
        Type type;
    };



@@ 68,7 76,7 @@ namespace bluetooth
      public:
        explicit CommandHandler(sys::Service *service,
                                std::shared_ptr<bluetooth::SettingsHolder> settings,
                                std::shared_ptr<bluetooth::Profile> currentProfile,
                                std::shared_ptr<bluetooth::ProfileManager> profileManager,
                                std::shared_ptr<bluetooth::AbstractDriver> driver);

        auto handle(Command command) -> Error::Code override;


@@ 78,13 86,14 @@ namespace bluetooth
        Error::Code stopScan();
        Error::Code startPan();
        Error::Code setVisibility(bool visibility);
        Error::Code establishAudioConnection();
        Error::Code establishAudioConnection(bd_addr_t addr);
        Error::Code disconnectAudioConnection();
        Error::Code pair(CommandArgument arg);
        Error::Code pair(bd_addr_t addr);

        Error::Code switchAudioProfile();
        sys::Service *service;
        std::shared_ptr<bluetooth::SettingsHolder> settings;
        std::shared_ptr<bluetooth::Profile> currentProfile;
        std::shared_ptr<bluetooth::ProfileManager> profileManager;
        std::shared_ptr<AbstractDriver> driver;
    };
} // namespace bluetooth

M module-bluetooth/Bluetooth/WorkerController.cpp => module-bluetooth/Bluetooth/WorkerController.cpp +1 -1
@@ 4,7 4,7 @@
#include "WorkerController.hpp"

#include "Device.hpp"
#include "interface/profiles/Profile.hpp"
#include "interface/profiles/ProfileManager.hpp"

#include <module-utils/log/log.hpp>
#define BOOST_SML_CFG_DISABLE_MIN_SIZE // GCC10 fix

M module-bluetooth/Bluetooth/btstack_config.h => module-bluetooth/Bluetooth/btstack_config.h +9 -8
@@ 36,20 36,21 @@

// BTstack configuration. buffers, sizes, ...
#define HCI_INCOMING_PRE_BUFFER_SIZE 14 // sizeof benep heade, avoid memcpy
#define HCI_ACL_PAYLOAD_SIZE         (1691 + 4)
//#define HCI_ACL_PAYLOAD_SIZE         (1691 + 4)
#define HCI_ACL_PAYLOAD_SIZE         (1021+4)

#define ENABLE_GATT_CLIENT_PAIRING
#define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE
#define ENABLE_CC256X_BAUDRATE_CHANGE_FLOWCONTROL_BUG_WORKAROUND
//#define HAVE_EMBEDDED_TIME_MS

#define MAX_NR_HCI_CONNECTIONS                    3
#define MAX_NR_L2CAP_SERVICES                     4
#define MAX_NR_L2CAP_CHANNELS                     10
#define MAX_NR_RFCOMM_MULTIPLEXERS                2
#define MAX_NR_RFCOMM_SERVICES                    4
#define MAX_NR_RFCOMM_CHANNELS                    4
#define MAX_NR_BTSTACK_LINK_KEY_DB_MEMORY_ENTRIES 10
//#define MAX_NR_HCI_CONNECTIONS                    4
//#define MAX_NR_L2CAP_SERVICES                     5
//#define MAX_NR_L2CAP_CHANNELS                     6
//#define MAX_NR_RFCOMM_MULTIPLEXERS                2
//#define MAX_NR_RFCOMM_SERVICES                    3
//#define MAX_NR_RFCOMM_CHANNELS                    3
//#define MAX_NR_BTSTACK_LINK_KEY_DB_MEMORY_ENTRIES 10

// As an option - much slower (according to docs)
// HCI Controller to Host Flow Control

M module-bluetooth/Bluetooth/glucode/BluetoothRunLoop.hpp => module-bluetooth/Bluetooth/glucode/BluetoothRunLoop.hpp +1 -1
@@ 30,7 30,6 @@ namespace bluetooth
        static auto removeDataSource(btstack_data_source_t *ds) -> bool;
        static void triggerExit();
        static auto getTimeMs() -> TickType_t;
        static void trigger();
        static void executeCodeOnMainThread(void (*fn)(void *arg), void *arg);
        static void addTimer(btstack_timer_source_t *ts);
        static void setTimer(btstack_timer_source_t *ts, uint32_t timeout_in_ms);


@@ 46,6 45,7 @@ namespace bluetooth
        btstack_run_loop runLoop;

      public:
        static void trigger();
        auto process() -> bool;
        static void deinit();
        void setTriggerQueue(QueueHandle_t queue);

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp +34 -11
@@ 77,11 77,21 @@ namespace bluetooth

    void A2DP::connect()
    {
        pimpl->start();
        pimpl->connect();
    }

    void A2DP::disconnect()
    {
        pimpl->disconnect();
    }

    void A2DP::start()
    {
        pimpl->start();
    }

    void A2DP::stop()
    {
        pimpl->stop();
    }



@@ 98,6 108,7 @@ namespace bluetooth
        2,
        53};

    bool A2DP::A2DPImpl::isConnected = false;
    /* LISTING_START(MainConfiguration): Setup Audio Source and AVRCP Target services */

    auto A2DP::A2DPImpl::init() -> Error::Code


@@ 155,8 166,6 @@ namespace bluetooth
        // Set local name with a template Bluetooth address, that will be automatically
        // replaced with a actual address once it is available, i.e. when BTstack boots
        // up and starts talking to a Bluetooth module.
        gap_set_local_name("PurePhone");
        gap_discoverable_control(1);
        gap_set_class_of_device(0x200408);

        // Register for HCI events.


@@ 330,6 339,7 @@ namespace bluetooth
                     bd_addr_to_str(address),
                     AVRCP::mediaTracker.a2dp_cid,
                     AVRCP::mediaTracker.local_seid);
            isConnected = true;
            break;

        case A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION: {


@@ 458,8 468,6 @@ namespace bluetooth
                LOG_ERROR("failed to create queue!");
            }

            AVRCP::mediaTracker.stream_opened = 1;
            a2dp_source_start_stream(AVRCP::mediaTracker.a2dp_cid, AVRCP::mediaTracker.local_seid);
            break;

        case A2DP_SUBEVENT_STREAM_RECONFIGURED:


@@ 553,24 561,26 @@ namespace bluetooth
                AVRCP::mediaTracker.a2dp_cid  = 0;
                LOG_INFO("A2DP Source: Signaling released.\n\n");
            }
            isConnected = false;
            break;
        default:
            break;
        }
    }

    void A2DP::A2DPImpl::start()
    void A2DP::A2DPImpl::connect()
    {
        LOG_INFO("Starting playback to %s", bd_addr_to_str(deviceAddr));
        a2dp_source_establish_stream(deviceAddr, &AVRCP::mediaTracker.a2dp_cid);
        if (!isConnected) {
            LOG_INFO("Starting playback to %s", bd_addr_to_str(deviceAddr));
            a2dp_source_establish_stream(deviceAddr, &AVRCP::mediaTracker.a2dp_cid);
        }
    }

    void A2DP::A2DPImpl::stop()
    void A2DP::A2DPImpl::disconnect()
    {
        LOG_INFO("Stopping playback");
        a2dp_source_disconnect(AVRCP::mediaTracker.a2dp_cid);
        l2cap_unregister_service(1);
    };
    }

    void A2DP::A2DPImpl::setDeviceAddress(bd_addr_t addr)
    {


@@ 595,5 605,18 @@ namespace bluetooth
        auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
        busProxy.sendUnicast(std::move(msg), service::name::evt_manager);
    }
    void A2DP::A2DPImpl::start()
    {
        if (!isConnected) {
            connect();
        }
        AVRCP::mediaTracker.stream_opened = 1;
        a2dp_source_start_stream(AVRCP::mediaTracker.a2dp_cid, AVRCP::mediaTracker.local_seid);
    }
    void A2DP::A2DPImpl::stop()
    {
        AVRCP::mediaTracker.stream_opened = 1;
        a2dp_source_pause_stream(AVRCP::mediaTracker.a2dp_cid, AVRCP::mediaTracker.local_seid);
    }

} // namespace Bt

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp +2 -0
@@ 29,6 29,8 @@ namespace bluetooth

        void connect() override;
        void disconnect() override;
        void start() override;
        void stop() override;

      private:
        class A2DPImpl;

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp +3 -0
@@ 68,9 68,12 @@ namespace bluetooth
        static void sendMediaPacket();
        static auto fillSbcAudioBuffer(MediaContext *context) -> int;
        static void sendAudioEvent(audio::EventType event, audio::Event::DeviceState state);
        static bool isConnected;

      public:
        auto init() -> Error::Code;
        void connect();
        void disconnect();
        void start();
        void stop();
        void setDeviceAddress(bd_addr_t addr);

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp +33 -9
@@ 8,6 8,7 @@
#include <log/log.hpp>
#include <service-evtmgr/Constants.hpp>
#include <service-audio/AudioMessage.hpp>
#include <BluetoothWorker.hpp>

extern "C"
{


@@ 57,11 58,21 @@ namespace bluetooth

    void HSP::connect()
    {
        pimpl->start();
        pimpl->connect();
    }

    void HSP::disconnect()
    {
        pimpl->disconnect();
    }

    void HSP::start()
    {
        pimpl->start();
    }

    void HSP::stop()
    {
        pimpl->stop();
    }



@@ 74,6 85,7 @@ namespace bluetooth
    std::unique_ptr<SCO> HSP::HSPImpl::sco;
    const sys::Service *HSP::HSPImpl::ownerService;
    std::string HSP::HSPImpl::agServiceName = "PurePhone HSP";
    bool HSP::HSPImpl::isConnected          = false;

    void HSP::HSPImpl::sendAudioEvent(audio::EventType event, audio::Event::DeviceState state)
    {


@@ 126,8 138,7 @@ namespace bluetooth
                break;
            }
            LOG_DEBUG("RFCOMM connection established.\n");
            LOG_DEBUG("Establish Audio connection to %s...\n", bd_addr_to_str(deviceAddr));
            hsp_ag_establish_audio_connection();
            isConnected = true;
            break;
        case HSP_SUBEVENT_RFCOMM_DISCONNECTION_COMPLETE:
            if (hsp_subevent_rfcomm_disconnection_complete_get_status(event) != 0u) {


@@ 148,13 159,14 @@ namespace bluetooth
                scoHandle = hsp_subevent_audio_connection_complete_get_handle(event);
                LOG_DEBUG("Audio connection established with SCO handle 0x%04x.\n", scoHandle);
                hci_request_sco_can_send_now_event();
                btstack_run_loop_freertos_trigger();
                RunLoop::trigger();
            }
            break;
        case HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE:
            LOG_DEBUG("Audio connection released.\n\n");
            sendAudioEvent(audio::EventType::BlutoothHSPDeviceState, audio::Event::DeviceState::Disconnected);
            scoHandle = HCI_CON_HANDLE_INVALID;
            isConnected = false;
            break;
        case HSP_SUBEVENT_MICROPHONE_GAIN_CHANGED:
            LOG_DEBUG("Received microphone gain change %d\n", hsp_subevent_microphone_gain_changed_get_gain(event));


@@ 199,7 211,6 @@ namespace bluetooth
        // register for SCO packets
        hci_register_sco_packet_handler(&packetHandler);

        gap_set_local_name("PurePhone");
        gap_discoverable_control(1);
        gap_set_class_of_device(CLASS_OF_DEVICE);



@@ 208,14 219,15 @@ namespace bluetooth
        return bluetooth::Error::Success;
    }

    void HSP::HSPImpl::start()
    void HSP::HSPImpl::connect()
    {
        hsp_ag_connect(deviceAddr);
        if (!isConnected) {
            hsp_ag_connect(deviceAddr);
        }
    }

    void HSP::HSPImpl::stop()
    void HSP::HSPImpl::disconnect()
    {
        hsp_ag_release_audio_connection();
        hsp_ag_disconnect();
    }



@@ 234,5 246,17 @@ namespace bluetooth
    {
        return sco->getStreamData();
    }
    void HSP::HSPImpl::start()
    {
        if (!isConnected) {
            connect();
        }
        LOG_DEBUG("Establish Audio connection to %s...\n", bd_addr_to_str(deviceAddr));
        hsp_ag_establish_audio_connection();
    }
    void HSP::HSPImpl::stop()
    {
        hsp_ag_release_audio_connection();
    }

} // namespace Bt

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp +5 -1
@@ 5,6 5,7 @@

#include "Profile.hpp"
#include <service-bluetooth/BluetoothMessage.hpp>
#include <btstack_run_loop.h>

namespace bluetooth
{


@@ 29,11 30,14 @@ namespace bluetooth

        void connect() override;
        void disconnect() override;
        void start() override;
        void stop() override;

      private:
        class HSPImpl;
        std::unique_ptr<HSPImpl> pimpl;
        const sys::Service *ownerService;
        const sys::Service *ownerService{};
        btstack_run_loop *runLoopInstance{};
    };

} // namespace Bt

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSPImpl.hpp +3 -0
@@ 19,6 19,8 @@ namespace bluetooth
        auto init() -> Error::Code;
        void start();
        void stop();
        void connect();
        void disconnect();
        void setDeviceAddress(bd_addr_t addr);
        void setOwnerService(const sys::Service *service);
        auto getStreamData() -> std::shared_ptr<BluetoothStreamData>;


@@ 35,5 37,6 @@ namespace bluetooth
        static std::array<char, commandBufferLength> ATcommandBuffer;
        static bd_addr_t deviceAddr;
        static const sys::Service *ownerService;
        static bool isConnected;
    };
} // namespace Bt

M module-bluetooth/Bluetooth/interface/profiles/Profile.hpp => module-bluetooth/Bluetooth/interface/profiles/Profile.hpp +2 -0
@@ 18,6 18,8 @@ namespace bluetooth
        virtual void setOwnerService(const sys::Service *service)            = 0;
        virtual auto getStreamData() -> std::shared_ptr<BluetoothStreamData> = 0;
        virtual void connect()                                               = 0;
        virtual void start()                                                 = 0;
        virtual void stop()                                                  = 0;
        virtual void disconnect()                                            = 0;
    };


A module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp +81 -0
@@ 0,0 1,81 @@
#include <service-bluetooth/ServiceBluetooth.hpp>
#include "ProfileManager.hpp"

namespace bluetooth
{

    ProfileManager::ProfileManager(sys::Service *ownerService) : ownerService(ownerService)
    {}

    auto ProfileManager::init() -> Error::Code
    {
        if (!initialized) {
            profilesList = {{AudioProfile::A2DP, std::make_shared<bluetooth::A2DP>()},
                            {AudioProfile::HSP, std::make_shared<bluetooth::HSP>()},
                            {AudioProfile::HFP, nullptr},
                            {AudioProfile::None, nullptr}};

            for (auto &[profileName, ptr] : profilesList) {
                if (ptr != nullptr) {
                    ptr->setOwnerService(ownerService);
                    ptr->init();
                }
            }
            currentProfilePtr = profilesList[AudioProfile::A2DP].get();

            if (auto serviceBt = dynamic_cast<ServiceBluetooth *>(ownerService); serviceBt != nullptr) {
                serviceBt->profileManagerPtr = this;
            }
            initialized = true;
        }
        return Error::Success;
    }

    auto ProfileManager::connect(bd_addr_t address) -> Error::Code
    {
        bd_addr_copy(remoteAddr, address);
        for (auto &[profileName, ptr] : profilesList) {
            if (ptr != nullptr) {
                ptr->setDeviceAddress(remoteAddr);
                ptr->connect();
            }
        }
        return Error::Success;
    }

    auto ProfileManager::disconnect() -> Error::Code
    {
        for (auto &[profileName, ptr] : profilesList) {
            if (ptr != nullptr) {
                ptr->disconnect();
            }
        }
        return Error::Success;
    }

    auto ProfileManager::switchProfile(AudioProfile profile) -> Error::Code
    {
        if (profilesList[profile] == nullptr) {
            LOG_ERROR("Invalid profile!");
            return Error::SystemError;
        }
        if (currentProfilePtr == profilesList[profile].get()) {
            return Error::Success;
        }
        stop();
        currentProfilePtr = profilesList[profile].get();
        start();
        return Error::Success;
    }
    auto ProfileManager::start() -> Error::Code
    {
        currentProfilePtr->start();
        return Error::Success;
    }
    auto ProfileManager::stop() -> Error::Code
    {
        currentProfilePtr->stop();
        return Error::Success;
    }

} // namespace bluetooth

A module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp +51 -0
@@ 0,0 1,51 @@
#pragma once

#include <Service/Service.hpp>
#include <Error.hpp>
#include "Profile.hpp"
#include "interface/profiles/A2DP/A2DP.hpp"
#include "interface/profiles/HSP/HSP.hpp"

extern "C"
{
#include <bluetooth.h>
#include "btstack_util.h"
}

namespace bluetooth
{

    enum class AudioProfile
    {
        A2DP,
        HSP,
        HFP,
        None
    };

    using ProfileList = std::map<AudioProfile, std::shared_ptr<bluetooth::Profile>>;

    class ProfileManager
    {
      public:
        explicit ProfileManager(sys::Service *ownerService);

        auto init() -> Error::Code;

        auto connect(bd_addr_t address) -> Error::Code;

        auto disconnect() -> Error::Code;

        auto switchProfile(AudioProfile profile) -> Error::Code;

        auto start() -> Error::Code;
        auto stop() -> Error::Code;

      private:
        sys::Service *ownerService;
        ProfileList profilesList;
        bluetooth::Profile *currentProfilePtr = nullptr;
        bd_addr_t remoteAddr{};
        bool initialized = false;
    };
} // namespace bluetooth

M module-bluetooth/CMakeLists.txt => module-bluetooth/CMakeLists.txt +3 -1
@@ 15,7 15,9 @@ set(SOURCES
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/A2DP/AVDTP.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/HSP/HSP.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/HSP/SCO.cpp
)
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/ProfileManager.cpp

        )

message("Build with BlueKitchen")
include(lib/btstack.cmake)

M module-services/service-bluetooth/ServiceBluetooth.cpp => module-services/service-bluetooth/ServiceBluetooth.cpp +17 -12
@@ 25,7 25,7 @@
#include <BtCommand.hpp>
#include <BtKeysStorage.hpp>

ServiceBluetooth::ServiceBluetooth() : sys::Service(service::name::bluetooth)
ServiceBluetooth::ServiceBluetooth() : sys::Service(service::name::bluetooth, "", 4096)
{
    auto settings  = std::make_unique<settings::Settings>(this);
    settingsHolder = std::make_shared<bluetooth::SettingsHolder>(std::move(settings));


@@ 93,8 93,9 @@ sys::ReturnCodes ServiceBluetooth::InitHandler()

    connect(typeid(BluetoothPairMessage), [&](sys::Message *msg) {
        auto pairMsg = static_cast<BluetoothPairMessage *>(msg);
        auto addr    = pairMsg->addr;

        const auto addrString = pairMsg->addr;
        bd_addr_t addr;
        sscanf_bd_addr(addrString.c_str(), addr);
        sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::Pair, addr));
        return sys::MessageNone{};
    });


@@ 175,11 176,18 @@ sys::MessagePointer ServiceBluetooth::DataReceivedHandler(sys::DataMessage *msg,
            } break;

            case BluetoothMessage::Play:
                sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::ConnectAudio));
                sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StartStream));
                break;
            case BluetoothMessage::SwitchProfile:
                sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::SwitchProfile));

                break;
            case BluetoothMessage::StopPlayback:
            case BluetoothMessage::Disconnect:
                sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::DisconnectAudio));
                break;
            case BluetoothMessage::Stop:
                sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StopStream));
                break;

            default:
                break;


@@ 188,13 196,10 @@ sys::MessagePointer ServiceBluetooth::DataReceivedHandler(sys::DataMessage *msg,
        }
        case MessageType::BluetoothAddrResult: {
            auto addrMsg = static_cast<BluetoothAddrMessage *>(msg);
            worker->setDeviceAddress(addrMsg->addr);
        } break;
        case MessageType::BluetoothRequestStream: {
            auto result =
                std::make_shared<BluetoothRequestStreamResultMessage>(worker->currentProfile->getStreamData());
            bus.sendUnicast(std::move(result), "ServiceAudio");
            LOG_INFO("Queues sent after a request!");
            std::string addrString{bd_addr_to_str(addrMsg->addr)};
            LOG_INFO("Connecting with %s", addrString.c_str());
            sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::ConnectAudio, addrMsg->addr));

        } break;
        default:
            break;

M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp => module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp +3 -1
@@ 37,7 37,9 @@ class BluetoothMessage : public sys::DataMessage
        PAN,
        Visible,
        Play,
        StopPlayback
        SwitchProfile,
        Stop,
        Disconnect
    };
    enum Request req = Request::None;
    BluetoothMessage(enum Request req = None) : sys::DataMessage(MessageType::BluetoothRequest), req(req){};

M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp => module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp +2 -0
@@ 12,6 12,7 @@
#include <service-audio/ServiceAudio.hpp>
#include <module-bluetooth/Bluetooth/CommandHandler.hpp>
#include "BluetoothMessage.hpp"
#include "ProfileManager.hpp"

#include <memory> // for unique_ptr



@@ 35,6 36,7 @@ class ServiceBluetooth : public sys::Service
    void sendWorkerCommand(bluetooth::Command command);
    QueueHandle_t workerQueue = nullptr;
    std::shared_ptr<bluetooth::SettingsHolder> settingsHolder;
    bluetooth::ProfileManager *profileManagerPtr = nullptr;
    void scanStartedCallback();
    void scanStoppedCallback();