// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md #include #include #include #include #include #include #include #include #include #include ServiceDesktop::ServiceDesktop(const std::filesystem::path &mtpRootPath) : sys::Service(service::name::service_desktop, "", sdesktop::serviceStackSize), btMsgHandler(std::make_unique(this)), connectionActiveTimer{sys::TimerFactory::createSingleShotTimer( this, sdesktop::connectionActiveTimerName, sdesktop::connectionActiveTimerDelayMs, [this](sys::Timer & /*timer*/) { outboxNotifications.clearNotifications(); })}, mtpRootPath{mtpRootPath} { bus.channels.push_back(sys::BusChannel::PhoneLockChanges); bus.channels.push_back(sys::BusChannel::ServiceDBNotifications); bus.channels.push_back(sys::BusChannel::USBNotifications); } ServiceDesktop::~ServiceDesktop() = default; auto ServiceDesktop::InitHandler() -> sys::ReturnCodes { settings = std::make_unique(); settings->init(service::ServiceProxy(shared_from_this())); usbSecurityModel = std::make_unique(this, settings.get()); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); connectHandler(); checkChargingCondition(); LOG_INFO("Initialized"); return sys::ReturnCodes::Success; } auto ServiceDesktop::DeinitHandler() -> sys::ReturnCodes { LOG_INFO("Deinitialized"); return usbWorkerDeinit(); } auto ServiceDesktop::SwitchPowerModeHandler([[maybe_unused]] const sys::ServicePowerMode mode) -> sys::ReturnCodes { return sys::ReturnCodes::Success; } auto ServiceDesktop::DataReceivedHandler([[maybe_unused]] sys::DataMessage *msg, sys::ResponseMessage *resp) -> sys::MessagePointer { auto response = std::make_shared(); if (resp == nullptr) { return response; } if (resp->responseTo != MessageType::DBQuery) { return response; } if (auto queryResponse = dynamic_cast(resp)) { auto result = queryResponse->getResult(); if (result == nullptr) { LOG_ERROR("Wrong result: nullptr!"); return response; } if (result->hasListener()) { LOG_DEBUG("Handling result"); result->handle(); } } return response; } auto ServiceDesktop::prepareSyncData() -> void { syncStatus.state = Sync::OperationState::Stopped; syncStatus.tempDir = purefs::dir::getTemporaryPath() / "sync"; } auto ServiceDesktop::requestLogsFlush() -> void { int response = 0; auto ret = bus.sendUnicastSync( std::make_shared(), service::name::evt_manager, defaultLogFlushTimeoutMs); if (ret.first == sys::ReturnCodes::Success) { auto responseMsg = std::dynamic_pointer_cast(ret.second); if ((responseMsg != nullptr) && responseMsg->retCode) { response = responseMsg->data; LOG_DEBUG("Response data: %d", response); } } if (ret.first == sys::ReturnCodes::Failure || response < 0) { throw std::runtime_error("Logs flush failed"); } } auto ServiceDesktop::getSerialNumber() const -> std::string { return settings->getValue(sdesktop::pathFactoryDataSerial, settings::SettingsScope::Global); } auto ServiceDesktop::getCaseColour() const -> std::string { return settings->getValue(sdesktop::pathFactoryDataCaseColour, settings::SettingsScope::Global); } auto ServiceDesktop::getDeviceToken() -> std::string { auto tokenSeed = getDeviceUniqueId(); if (tokenSeed.empty()) { LOG_DEBUG("Device unique id is empty, generating one..."); generateDeviceUniqueId(); tokenSeed = getDeviceUniqueId(); } return tokenSeed; } auto ServiceDesktop::getNotificationEntries() const -> std::vector { return outboxNotifications.getNotificationEntries(); } auto ServiceDesktop::removeNotificationEntries(const std::vector &uidsOfNotificationsToBeRemoved) -> void { outboxNotifications.removeNotificationEntries(uidsOfNotificationsToBeRemoved); } auto ServiceDesktop::generateDeviceUniqueId() -> void { const auto deviceUniqueId = utils::generateRandomId(sdesktop::deviceUniqueIdLength); LOG_SENSITIVE(LOGINFO, "Device unique id: %s", deviceUniqueId.c_str()); setDeviceUniqueId(deviceUniqueId); } auto ServiceDesktop::getDeviceUniqueId() const -> std::string { return settings->getValue(sdesktop::deviceUniqueIdName); } auto ServiceDesktop::setDeviceUniqueId(const std::string &token) -> void { return settings->setValue(sdesktop::deviceUniqueIdName, token); } auto ServiceDesktop::usbWorkerInit() -> sys::ReturnCodes { if (initialized) { return sys::ReturnCodes::Success; } auto serialNumber = getSerialNumber(); auto caseColour = getCaseColour(); LOG_DEBUG("Initializing USB worker, serial number: %s, case colour: %s", serialNumber.c_str(), caseColour.c_str()); desktopWorker = std::make_unique(this, std::bind(&ServiceDesktop::restartConnectionActiveTimer, this), *usbSecurityModel, serialNumber, caseColour, mtpRootPath); initialized = desktopWorker->init( {{sdesktop::cdcReceiveQueueName, sdesktop::cdcReceiveQueueItemSize, sdesktop::cdcReceiveQueueLength}, {sdesktop::cdcSendQueueName, sdesktop::cdcSendQueueItemSize, sdesktop::cdcSendQueueLength}, {sdesktop::irqQueueName, sdesktop::irqQueueItemSize, sdesktop::irqQueueLength}, {sdesktop::signallingQueueName, sdesktop::signallingQueueItemSize, sdesktop::signallingQueueLength}}); if (!initialized) { LOG_FATAL("Failed to initialize USB worker, ServiceDesktop won't work!"); return sys::ReturnCodes::Failure; } desktopWorker->run(); return sys::ReturnCodes::Success; } auto ServiceDesktop::usbWorkerDeinit() -> sys::ReturnCodes { if (!initialized) { return sys::ReturnCodes::Success; } LOG_DEBUG("Deinitializing USB worker"); settings->deinit(); desktopWorker->closeWorker(); desktopWorker.reset(); initialized = false; isUsbConfigured = false; LOG_DEBUG("USB worker deinitialized"); // It must be run after the worker is closed. cleanFileSystemEndpointUndeliveredTransfers(); return sys::ReturnCodes::Success; } auto ServiceDesktop::restartConnectionActiveTimer() -> void { connectionActiveTimer.restart(sdesktop::connectionActiveTimerDelayMs); } auto ServiceDesktop::checkChargingCondition() -> void { if (Store::Battery::get().state == Store::Battery::State::Discharging) { usbWorkerDeinit(); } else { usbWorkerInit(); isPlugEventUnhandled = true; } } auto ServiceDesktop::handle(db::NotificationMessage *msg) -> std::shared_ptr { if (!connectionActiveTimer.isActive()) { return sys::MessageNone{}; } auto notificationMessage = static_cast(msg); outboxNotifications.newNotificationHandler(notificationMessage); return sys::MessageNone{}; } auto ServiceDesktop::handle([[maybe_unused]] locks::UnlockedPhone *msg) -> std::shared_ptr { LOG_INFO("Phone unlocked."); usbSecurityModel->setPhoneUnlocked(); if (isUsbConfigured && isPlugEventUnhandled) { bus.sendUnicast(std::make_shared(sys::phone_modes::Tethering::On), service::name::system_manager); isPlugEventUnhandled = false; if (desktopWorker != nullptr) { desktopWorker->notify(WorkerDesktop::Signal::unlockMTP); } } return sys::MessageNone{}; } auto ServiceDesktop::handle([[maybe_unused]] locks::LockedPhone *msg) -> std::shared_ptr { LOG_INFO("Phone locked."); usbSecurityModel->setPhoneLocked(); return sys::MessageNone{}; } auto ServiceDesktop::handle(locks::NextPhoneUnlockAttemptLockTime *msg) -> std::shared_ptr { auto message = static_cast(msg); usbSecurityModel->updatePhoneLockTime(message->getTime()); return sys::MessageNone{}; } auto ServiceDesktop::handle(message::bluetooth::ResponseStatus *msg) -> std::shared_ptr { auto msgl = static_cast(msg); return btMsgHandler->handle(msgl); } auto ServiceDesktop::handle(message::bluetooth::ResponseBondedDevices *msg) -> std::shared_ptr { auto msgl = static_cast(msg); return btMsgHandler->handle(msgl); } auto ServiceDesktop::handle(message::bluetooth::ResponseVisibleDevices *msg) -> std::shared_ptr { auto msgl = static_cast(msg); return btMsgHandler->handle(msgl); } auto ServiceDesktop::handle(sdesktop::developerMode::DeveloperModeRequest *msg) -> std::shared_ptr { auto request = static_cast(msg); if (request->event != nullptr) { request->event->send(); } return sys::MessageNone{}; } auto ServiceDesktop::handle([[maybe_unused]] sdesktop::SyncMessage *msg) -> std::shared_ptr { syncStatus.state = Sync::OperationState::Running; syncStatus.completionCode = Sync::PrepareSyncPackage(this, syncStatus.tempDir); if (syncStatus.completionCode == Sync::CompletionCode::Success) { LOG_INFO("Sync package preparation finished"); syncStatus.state = Sync::OperationState::Finished; } else { LOG_ERROR("Sync package preparation failed"); syncStatus.state = Sync::OperationState::Error; } return sys::MessageNone{}; } auto ServiceDesktop::handle([[maybe_unused]] sdesktop::FactoryMessage *msg) -> std::shared_ptr { LOG_DEBUG("ServiceDesktop: FactoryMessage received"); sys::SystemManagerCommon::FactoryReset(this); return sys::MessageNone{}; } auto ServiceDesktop::handle([[maybe_unused]] sdesktop::usb::USBConfigured *msg) -> std::shared_ptr { isUsbConfigured = true; if (usbSecurityModel->isSecurityEnabled()) { LOG_INFO("Endpoint security enabled, requesting passcode"); bus.sendUnicast(std::make_shared(), service::name::appmgr); } else if (isPlugEventUnhandled) { bus.sendUnicast(std::make_shared(sys::phone_modes::Tethering::On), service::name::system_manager); isPlugEventUnhandled = false; if (desktopWorker != nullptr) { desktopWorker->notify(WorkerDesktop::Signal::unlockMTP); } } return sys::MessageNone{}; } auto ServiceDesktop::handle([[maybe_unused]] sdesktop::usb::USBDisconnected *msg) -> std::shared_ptr { LOG_INFO("USB disconnected"); if (usbSecurityModel->isSecurityEnabled()) { bus.sendUnicast(std::make_shared(), service::name::appmgr); } bus.sendUnicast(std::make_shared(sys::phone_modes::Tethering::Off), service::name::system_manager); return sys::MessageNone{}; } auto ServiceDesktop::handle(sevm::USBPlugEvent *msg) -> std::shared_ptr { const auto message = static_cast(msg); if (message->event == sevm::USBPlugEvent::Event::CablePlugged) { usbWorkerInit(); isPlugEventUnhandled = true; } else { usbWorkerDeinit(); } return sys::MessageNone{}; } auto ServiceDesktop::getMtpPath() const noexcept -> std::filesystem::path { return mtpRootPath; } auto ServiceDesktop::getOnboardingState() const -> sdesktop::endpoints::OnboardingState { return static_cast(utils::getNumericValue( settings->getValue(settings::SystemProperties::onboardingDone, settings::SettingsScope::Global))); } auto ServiceDesktop::cleanFileSystemEndpointUndeliveredTransfers() -> void { FileOperations::instance().cleanUpUndeliveredTransfers(); }