// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include "service-appmgr/service-appmgr/messages/DOMRequest.hpp" #include "service-desktop/DesktopMessages.hpp" #include "service-desktop/ServiceDesktop.hpp" #include "service-desktop/WorkerDesktop.hpp" #include "service-cellular/CellularMessage.hpp" #include "endpoints/factoryReset/FactoryReset.hpp" #include "endpoints/backup/BackupRestore.hpp" #include "endpoints/update/UpdateMuditaOS.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { bool RemountFS(bool readOnly = false, std::string path = std::string(purefs::dir::getRootDiskPath())) { struct statvfs stat; if (statvfs(path.c_str(), &stat)) { LOG_ERROR("Failed reading statvfs!"); return false; } auto flags = stat.f_flag; if ((readOnly && (flags & MS_RDONLY)) || (!readOnly && !(flags & MS_RDONLY))) { LOG_WARN("Filesystem is already mounted in requested mode!"); return false; } if (readOnly) { LOG_INFO("Remounting filesystem RO"); flags |= MS_RDONLY; } else { LOG_INFO("Remounting filesystem R/W"); flags &= ~MS_RDONLY; } return !mount(NULL, path.c_str(), NULL, flags | MS_REMOUNT, NULL); } } // namespace ServiceDesktop::ServiceDesktop() : sys::Service(service::name::service_desktop, "", sdesktop::service_stack), btMsgHandler(std::make_unique(this)) { LOG_INFO("[ServiceDesktop] Initializing"); bus.channels.push_back(sys::BusChannel::PhoneLockChanges); updateOS = std::make_unique(this); } ServiceDesktop::~ServiceDesktop() { LOG_INFO("[ServiceDesktop] Cleaning resources"); } sys::ReturnCodes ServiceDesktop::InitHandler() { settings = std::make_unique(); settings->init(service::ServiceProxy(shared_from_this())); usbSecurityModel = std::make_unique(this, settings.get()); desktopWorker = std::make_unique(this, *usbSecurityModel.get()); const bool ret = desktopWorker->init({{sdesktop::RECEIVE_QUEUE_BUFFER_NAME, sizeof(std::string *), sdesktop::cdc_queue_len}, {sdesktop::SEND_QUEUE_BUFFER_NAME, sizeof(std::string *), sdesktop::cdc_queue_object_size}, {sdesktop::IRQ_QUEUE_BUFFER_NAME, 1, sdesktop::irq_queue_object_size}}); if (ret == false) { LOG_ERROR("!!! service-desktop InitHandler failed to initialize worker, service-desktop won't work"); return sys::ReturnCodes::Failure; } else { desktopWorker->run(); } transferTimer = sys::TimerFactory::createPeriodicTimer(this, "WorkerDesktop file upload", std::chrono::milliseconds{sdesktop::file_transfer_timeout}, [this](sys::Timer &) { desktopWorker->cancelTransferOnTimeout(); }); connect(sdesktop::developerMode::DeveloperModeRequest(), [&](sys::Message *msg) { auto request = static_cast(msg); if (request->event != nullptr) { request->event->send(); } return sys::MessageNone{}; }); connect(sdesktop::BackupMessage(), [&](sys::Message *msg) { sdesktop::BackupMessage *backupMessage = dynamic_cast(msg); if (backupMessage != nullptr) { RemountFS(); backupRestoreStatus.state = OperationState::Running; backupRestoreStatus.lastOperationResult = BackupRestore::BackupUserFiles(this, backupRestoreStatus.backupTempDir); backupRestoreStatus.location = (purefs::dir::getBackupOSPath() / backupRestoreStatus.task).replace_extension(purefs::extension::tar); backupRestoreStatus.state = OperationState::Stopped; } return sys::MessageNone{}; }); connect(sdesktop::RestoreMessage(), [&](sys::Message *msg) { sdesktop::RestoreMessage *restoreMessage = dynamic_cast(msg); if (restoreMessage != nullptr) { RemountFS(); backupRestoreStatus.state = OperationState::Running; backupRestoreStatus.lastOperationResult = BackupRestore::RestoreUserFiles(this, backupRestoreStatus.location); backupRestoreStatus.state = OperationState::Stopped; if (backupRestoreStatus.lastOperationResult == true) { sys::SystemManager::Reboot(this); } else { LOG_ERROR("Restore failed"); } } return sys::MessageNone{}; }); connect(sdesktop::FactoryMessage(), [&](sys::Message *msg) { auto *factoryMessage = dynamic_cast(msg); if (factoryMessage != nullptr) { LOG_DEBUG("ServiceDesktop: FactoryMessage received"); RemountFS(); // Factory reset calls SystemManager::Reboot(), but // there is no umount() in SystemManager::CloseSystemHandler() - // this might theoretically cause filesystem corruption FactoryReset::Run(this); } return sys::MessageNone{}; }); connect(sdesktop::UpdateOsMessage(), [&](sys::Message *msg) { sdesktop::UpdateOsMessage *updateOsMsg = dynamic_cast(msg); if (updateOsMsg != nullptr && updateOsMsg->messageType == updateos::UpdateMessageType::UpdateCheckForUpdateOnce) { fs::path file = UpdateMuditaOS::checkForUpdate(); if (file.has_filename()) { /* send info to applicationDesktop that there is an update waiting */ auto msgToSend = std::make_shared(updateos::UpdateMessageType::UpdateFoundOnBoot, file); msgToSend->updateStats.versionInformation = UpdateMuditaOS::getVersionInfoFromFile(file); bus.sendUnicast(msgToSend, app::name_desktop); } } if (updateOsMsg != nullptr && updateOsMsg->messageType == updateos::UpdateMessageType::UpdateNow) { LOG_DEBUG("ServiceDesktop::DataReceivedHandler file:%s uuid:%" PRIu32 "", updateOsMsg->updateStats.updateFile.c_str(), updateOsMsg->updateStats.uuid); if (updateOS->setUpdateFile(purefs::dir::getUpdatesOSPath(), updateOsMsg->updateStats.updateFile) == updateos::UpdateError::NoError) { RemountFS(); // Same possible issue as with FactoryReset::Run() updateOS->runUpdate(); } } return sys::MessageNone{}; }); connect(sdesktop::transfer::TransferTimerState(), [&](sys::Message *msg) { sdesktop::transfer::TransferTimerState *timerStateMsg = dynamic_cast(msg); if (timerStateMsg != nullptr && timerStateMsg->messageType == MessageType::TransferTimer) { switch (timerStateMsg->req) { case sdesktop::transfer::TransferTimerState::Start: [[fallthrough]]; case sdesktop::transfer::TransferTimerState::Reload: transferTimer.start(); break; case sdesktop::transfer::TransferTimerState::Stop: transferTimer.stop(); break; case sdesktop::transfer::TransferTimerState::None: [[fallthrough]]; default: LOG_DEBUG("ServiceDesktop::SetTransferTimerState unhandled req:%u", timerStateMsg->req); break; } } return sys::MessageNone{}; }); connect(sdesktop::usb::USBConfigured(), [&](sys::Message *msg) { auto message = static_cast(msg); if (message->getConfigurationType() == sdesktop::usb::USBConfigurationType::firstConfiguration) { bus.sendUnicast(std::make_shared(sys::phone_modes::Tethering::On), service::name::system_manager); } if (usbSecurityModel->isSecurityEnabled()) { LOG_INFO("Endpoint security enabled, requesting passcode"); bus.sendUnicast(std::make_shared(), app::manager::ApplicationManager::ServiceName); } return sys::MessageNone{}; }); connect(sdesktop::usb::USBDisconnected(), [&](sys::Message *msg) { LOG_INFO("USB disconnected"); if (usbSecurityModel->isSecurityEnabled()) { bus.sendUnicast(std::make_shared(), app::manager::ApplicationManager::ServiceName); } bus.sendUnicast(std::make_shared(sys::phone_modes::Tethering::Off), service::name::system_manager); return sys::MessageNone{}; }); connect(typeid(locks::UnlockedPhone), [&](sys::Message *msg) { LOG_INFO("Phone unlocked."); usbSecurityModel->setPhoneUnlocked(); return sys::MessageNone{}; }); connect(typeid(locks::LockedPhone), [&](sys::Message *msg) { LOG_INFO("Phone locked."); usbSecurityModel->setPhoneLocked(); return sys::MessageNone{}; }); connect(typeid(message::bluetooth::ResponseStatus), [&](sys::Message *msg) { auto msgl = static_cast(msg); return btMsgHandler->handle(msgl); }); connect(typeid(message::bluetooth::ResponseBondedDevices), [&](sys::Message *msg) { auto msgl = static_cast(msg); return btMsgHandler->handle(msgl); }); connect(typeid(message::bluetooth::ResponseVisibleDevices), [&](sys::Message *msg) { auto msgl = static_cast(msg); return btMsgHandler->handle(msgl); }); settings->registerValueChange(updateos::settings::history, [this](const std::string &value) { updateOS->setInitialHistory(value); }); return (sys::ReturnCodes::Success); } sys::ReturnCodes ServiceDesktop::DeinitHandler() { settings->deinit(); desktopWorker->deinit(); return sys::ReturnCodes::Success; } void ServiceDesktop::ProcessCloseReason(sys::CloseReason closeReason) { sendCloseReadyMessage(this); } sys::ReturnCodes ServiceDesktop::SwitchPowerModeHandler(const sys::ServicePowerMode mode) { return sys::ReturnCodes::Success; } sys::MessagePointer ServiceDesktop::DataReceivedHandler(sys::DataMessage *msg, sys::ResponseMessage *resp) { if (resp != nullptr) { if (resp->responseTo == MessageType::DBQuery) { if (auto queryResponse = dynamic_cast(resp)) { auto result = queryResponse->getResult(); if (result != nullptr) { LOG_DEBUG("Result: %s", result->debugInfo().c_str()); if (result->hasListener()) { LOG_DEBUG("Handling result..."); result->handle(); } } else { LOG_ERROR("Wrong result - nullptr!"); } } } } return std::make_shared(); } void ServiceDesktop::storeHistory(const std::string &historyValue) { settings->setValue(updateos::settings::history, historyValue); } void ServiceDesktop::prepareBackupData() { backupRestoreStatus.operation = ServiceDesktop::Operation::Backup; backupRestoreStatus.task = std::to_string(static_cast(std::time(nullptr))); backupRestoreStatus.state = OperationState::Stopped; backupRestoreStatus.backupTempDir = purefs::dir::getTemporaryPath() / backupRestoreStatus.task; } void ServiceDesktop::prepareRestoreData(const std::filesystem::path &restoreLocation) { backupRestoreStatus.operation = ServiceDesktop::Operation::Restore; backupRestoreStatus.location = purefs::dir::getBackupOSPath() / restoreLocation; backupRestoreStatus.state = OperationState::Stopped; backupRestoreStatus.task = restoreLocation.filename(); }