// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include "service-bluetooth/ServiceBluetooth.hpp" #include "service-bluetooth/BluetoothMessage.hpp" #include #include #include #include #include #include #include "service-bluetooth/messages/AudioVolume.hpp" #include "service-bluetooth/messages/AudioRouting.hpp" #include "service-bluetooth/messages/Connect.hpp" #include #include "service-bluetooth/messages/Disconnect.hpp" #include "service-bluetooth/messages/Status.hpp" #include "service-bluetooth/messages/SetStatus.hpp" #include "service-bluetooth/messages/BondedDevices.hpp" #include "service-bluetooth/messages/Unpair.hpp" #include "service-bluetooth/messages/SetDeviceName.hpp" #include "service-bluetooth/messages/Ring.hpp" #include "service-bluetooth/BluetoothDevicesModel.hpp" #include "service-bluetooth/messages/BluetoothModeChanged.hpp" #include "system/messages/SentinelRegistrationMessage.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { constexpr auto BluetoothServiceStackDepth = 2560U; inline constexpr auto nameSettings = "ApplicationSettings"; inline constexpr auto connectionTimeout = std::chrono::minutes{30}; inline constexpr auto btRestartDelay = std::chrono::milliseconds{500}; } // namespace ServiceBluetooth::ServiceBluetooth() : sys::Service(service::name::bluetooth, "", BluetoothServiceStackDepth) { LOG_INFO("[ServiceBluetooth] Initializing"); bus.channels.push_back(sys::BusChannel::ServiceCellularNotifications); } ServiceBluetooth::~ServiceBluetooth() { LOG_INFO("[ServiceBluetooth] Cleaning resources"); } sys::ReturnCodes ServiceBluetooth::InitHandler() { auto settings = std::make_unique(); settings->init(service::ServiceProxy(shared_from_this())); settingsHolder = std::make_shared(std::move(settings)); bluetoothDevicesModel = std::make_shared(this); bluetooth::KeyStorage::settings = settingsHolder; bus.channels.push_back(sys::BusChannel::BluetoothNotifications); worker = std::make_unique(this); worker->run(); cpuSentinel = std::make_shared(service::name::bluetooth, this); auto sentinelRegistrationMsg = std::make_shared(cpuSentinel); bus.sendUnicast(sentinelRegistrationMsg, service::name::system_manager); connectionTimeoutTimer = sys::TimerFactory::createSingleShotTimer(this, "btTimeoutTimer", connectionTimeout, [this](sys::Timer &_) { sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::DisconnectAudio)); LOG_ERROR("Disconnecting from timeout timer!"); }); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); settingsHolder->onStateChange = [this]() { auto initialState = std::visit(bluetooth::IntVisitor(), settingsHolder->getValue(bluetooth::Settings::State)); if (static_cast(initialState) == BluetoothStatus::State::On) { settingsHolder->setValue(bluetooth::Settings::State, static_cast(BluetoothStatus::State::Off)); } }; return sys::ReturnCodes::Success; } sys::ReturnCodes ServiceBluetooth::DeinitHandler() { settingsHolder->deinit(); worker->deinit(); return sys::ReturnCodes::Success; } void ServiceBluetooth::ProcessCloseReason(sys::CloseReason closeReason) { sendCloseReadyMessage(this); } sys::MessagePointer ServiceBluetooth::DataReceivedHandler([[maybe_unused]] sys::DataMessage *msg, [[maybe_unused]] sys::ResponseMessage *resp) { return std::make_shared(); } sys::ReturnCodes ServiceBluetooth::SwitchPowerModeHandler(const sys::ServicePowerMode mode) { LOG_ERROR("TODO"); return sys::ReturnCodes::Success; } void ServiceBluetooth::sendWorkerCommand(bluetooth::Command command) { xQueueSend(workerQueue, &command, portMAX_DELAY); } auto ServiceBluetooth::handle(BluetoothAudioStartMessage *msg) -> std::shared_ptr { worker->setAudioDevice(msg->getAudioDevice()); return std::make_shared(); } auto ServiceBluetooth::handle([[maybe_unused]] message::bluetooth::RequestBondedDevices *msg) -> std::shared_ptr { auto bondedDevicesStr = std::visit(bluetooth::StringVisitor(), this->settingsHolder->getValue(bluetooth::Settings::BondedDevices)); bluetoothDevicesModel->mergeDevicesList(SettingsSerializer::fromString(bondedDevicesStr)); bus.sendMulticast( std::make_shared(bluetoothDevicesModel->getDevices(), ""), sys::BusChannel::BluetoothNotifications); return sys::MessageNone{}; } auto ServiceBluetooth::handle([[maybe_unused]] message::bluetooth::RequestStatus *msg) -> std::shared_ptr { auto state = std::visit(bluetooth::IntVisitor(), settingsHolder->getValue(bluetooth::Settings::State)); auto visibility = std::visit(bluetooth::BoolVisitor(), settingsHolder->getValue(bluetooth::Settings::Visibility)); BluetoothStatus status{static_cast(state), status.visibility = visibility}; bus.sendMulticast(std::make_shared(status), sys::BusChannel::BluetoothNotifications); return sys::MessageNone{}; } auto ServiceBluetooth::handle(message::bluetooth::SetStatus *msg) -> std::shared_ptr { auto newBtStatus = msg->getStatus(); switch (newBtStatus.state) { case BluetoothStatus::State::On: cpuSentinel->HoldMinimumFrequency(bsp::CpuFrequencyHz::Level_3); sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::PowerOn)); bus.sendMulticast( std::make_shared(sys::bluetooth::BluetoothMode::Enabled), sys::BusChannel::BluetoothModeChanges); { auto bondedDevicesStr = std::visit(bluetooth::StringVisitor(), this->settingsHolder->getValue(bluetooth::Settings::BondedDevices)); bluetoothDevicesModel->mergeDevicesList(SettingsSerializer::fromString(bondedDevicesStr)); bluetoothDevicesModel->syncDevicesWithApp(); } break; case BluetoothStatus::State::Off: sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::PowerOff)); cpuSentinel->ReleaseMinimumFrequency(); bus.sendMulticast( std::make_shared(sys::bluetooth::BluetoothMode::Disabled), sys::BusChannel::BluetoothModeChanges); break; default: break; } bluetooth::Command command(newBtStatus.visibility ? bluetooth::Command::Type::VisibilityOn : bluetooth::Command::Type::VisibilityOff); sendWorkerCommand(command); return sys::MessageNone{}; } auto ServiceBluetooth::handle(BluetoothPairMessage *msg) -> std::shared_ptr { auto device = msg->getDevice(); bluetoothDevicesModel->removeDevice(device); sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::Pair, device)); device.deviceState = DeviceState::Pairing; bluetoothDevicesModel->insertDevice(device); bluetoothDevicesModel->syncDevicesWithApp(); return sys::MessageNone{}; } auto ServiceBluetooth::handle(BluetoothPairResultMessage *msg) -> std::shared_ptr { auto device = msg->getDevice(); if (msg->isSucceed()) { bluetoothDevicesModel->setInternalDeviceState(device, DeviceState::Paired); } else { bluetoothDevicesModel->removeDevice(device); } bluetoothDevicesModel->syncDevicesWithApp(); bus.sendMulticast(std::make_shared(msg->getDevice(), msg->isSucceed()), sys::BusChannel::BluetoothNotifications); return sys::MessageNone{}; } auto ServiceBluetooth::handle(message::bluetooth::Unpair *msg) -> std::shared_ptr { sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::Unpair, msg->getDevice())); bluetoothDevicesModel->removeDevice(msg->getDevice()); return sys::MessageNone{}; } auto ServiceBluetooth::handle([[maybe_unused]] message::bluetooth::RequestDeviceName *msg) -> std::shared_ptr { auto deviceNameString = std::visit(bluetooth::StringVisitor(), this->settingsHolder->getValue(bluetooth::Settings::DeviceName)); bus.sendMulticast(std::make_shared(std::move(deviceNameString)), sys::BusChannel::BluetoothNotifications); return sys::MessageNone{}; } auto ServiceBluetooth::handle(message::bluetooth::SetDeviceName *msg) -> std::shared_ptr { auto newName = msg->getName(); bluetooth::set_name(newName); settingsHolder->setValue(bluetooth::Settings::DeviceName, newName); sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::PowerOff)); btRestartTimer = sys::TimerFactory::createSingleShotTimer(this, "btRestartTimer", btRestartDelay, [this](sys::Timer &_) { sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::PowerOn)); }); btRestartTimer.start(); return sys::MessageNone{}; } auto ServiceBluetooth::handle(message::bluetooth::Connect *msg) -> std::shared_ptr { auto device = msg->getDevice(); sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::ConnectAudio, device)); bluetoothDevicesModel->setInternalDeviceState(device, DeviceState::Connecting); bluetoothDevicesModel->syncDevicesWithApp(); return sys::MessageNone{}; } auto ServiceBluetooth::handle(message::bluetooth::ConnectResult *msg) -> std::shared_ptr { if (msg->isSucceed()) { auto device = msg->getDevice(); bluetoothDevicesModel->mergeInternalDeviceState(device); settingsHolder->setValue(bluetooth::Settings::ConnectedDevice, bd_addr_to_str(device.address)); startTimeoutTimer(); bus.sendMulticast( std::make_shared(sys::bluetooth::BluetoothMode::Connected), sys::BusChannel::BluetoothModeChanges); } for (auto &device : bluetoothDevicesModel->getDevices()) { if (device.deviceState == DeviceState::Connecting) { device.deviceState = DeviceState::Paired; } } bluetoothDevicesModel->syncDevicesWithApp(); bus.sendMulticast(std::make_shared(*msg), sys::BusChannel::BluetoothNotifications); return sys::MessageNone{}; } auto ServiceBluetooth::handle([[maybe_unused]] message::bluetooth::Disconnect *msg) -> std::shared_ptr { sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::DisconnectAudio)); return sys::MessageNone{}; } auto ServiceBluetooth::handle(message::bluetooth::DisconnectResult *msg) -> std::shared_ptr { auto deviceAddr = std::visit(bluetooth::StringVisitor(), this->settingsHolder->getValue(bluetooth::Settings::ConnectedDevice)); auto device = bluetoothDevicesModel->getDeviceByAddress(deviceAddr); if (device.has_value()) { device.value().get().deviceState = DeviceState::Paired; } bluetoothDevicesModel->syncDevicesWithApp(); settingsHolder->setValue(bluetooth::Settings::ConnectedDevice, std::string()); if (auto btOn = std::visit(bluetooth::BoolVisitor(), settingsHolder->getValue(bluetooth::Settings::State)); btOn) { bus.sendMulticast( std::make_shared(sys::bluetooth::BluetoothMode::Enabled), sys::BusChannel::BluetoothModeChanges); } bus.sendMulticast(std::make_shared(msg->getDevice()), sys::BusChannel::BluetoothNotifications); stopTimeoutTimer(); return sys::MessageNone{}; } auto ServiceBluetooth::handle(message::bluetooth::ResponsePasskey *msg) -> std::shared_ptr { auto passKey = msg->getPasskey(); bluetooth::GAP::respondPinCode(passKey); return sys::MessageNone{}; } auto ServiceBluetooth::handle(BluetoothMessage *msg) -> std::shared_ptr { LOG_INFO("Bluetooth request!"); switch (msg->req) { case BluetoothMessage::Start: break; case BluetoothMessage::Scan: sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StartScan)); break; case BluetoothMessage::StopScan: sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StopScan)); break; case BluetoothMessage::getDevicesAvailable: sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::getDevicesAvailable)); break; case BluetoothMessage::Visible: { auto visibility = not std::visit(bluetooth::BoolVisitor(), settingsHolder->getValue(bluetooth::Settings::Visibility)); bluetooth::Command command(visibility ? bluetooth::Command::Type::VisibilityOn : bluetooth::Command::Type::VisibilityOff); sendWorkerCommand(command); } break; case BluetoothMessage::Play: sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StartStream)); stopTimeoutTimer(); break; case BluetoothMessage::SwitchProfile: sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::SwitchProfile)); break; case BluetoothMessage::Disconnect: sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::DisconnectAudio)); break; case BluetoothMessage::Stop: sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StopStream)); startTimeoutTimer(); break; default: break; } return std::make_shared(); } auto ServiceBluetooth::handle(BluetoothAddrMessage *msg) -> std::shared_ptr { sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::ConnectAudio, msg->device)); return std::make_shared(); } auto ServiceBluetooth::handle(sdesktop::developerMode::DeveloperModeRequest *msg) -> std::shared_ptr { if (typeid(*msg->event) == typeid(sdesktop::bluetooth::GetAvailableDevicesEvent)) { sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::getDevicesAvailable)); } return sys::MessageNone{}; } auto ServiceBluetooth::handle(message::bluetooth::A2DPVolume *msg) -> std::shared_ptr { using namespace message::bluetooth; AudioServiceAPI::BluetoothA2DPVolumeChanged(this, msg->getVolume()); return sys::MessageNone{}; } auto ServiceBluetooth::handle(message::bluetooth::HSPVolume *msg) -> std::shared_ptr { using namespace message::bluetooth; AudioServiceAPI::BluetoothHSPVolumeChanged(this, msg->getVolume()); return sys::MessageNone{}; } auto ServiceBluetooth::handle(message::bluetooth::HFPVolume *msg) -> std::shared_ptr { using namespace message::bluetooth; AudioServiceAPI::BluetoothHFPVolumeChanged(this, msg->getVolume()); return sys::MessageNone{}; } auto ServiceBluetooth::handle(message::bluetooth::Ring *msg) -> std::shared_ptr { const auto enableRing = msg->enabled(); sendWorkerCommand(bluetooth::Command(enableRing ? bluetooth::Command::Type::StartRinging : bluetooth::Command::Type::StopRinging)); return std::make_shared(); } auto ServiceBluetooth::handle(message::bluetooth::StartAudioRouting *msg) -> std::shared_ptr { sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StartRouting)); return std::make_shared(); } auto ServiceBluetooth::handle(CellularCallerIdMessage *msg) -> std::shared_ptr { auto number = msg->number; auto btOn = std::visit(bluetooth::BoolVisitor(), settingsHolder->getValue(bluetooth::Settings::State)); LOG_ERROR("Received caller ID msg! "); if (btOn) { LOG_DEBUG("Sending to profile!"); sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::IncomingCallNumber, number)); } return sys::MessageNone{}; } auto ServiceBluetooth::handle(CellularCallActiveNotification *msg) -> std::shared_ptr { sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::CallAnswered)); return std::make_shared(); } void ServiceBluetooth::startTimeoutTimer() { if (connectionTimeoutTimer.isValid()) { connectionTimeoutTimer.start(); } } void ServiceBluetooth::stopTimeoutTimer() { if (connectionTimeoutTimer.isValid()) { connectionTimeoutTimer.stop(); } }