// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include "service-evtmgr/BatteryMessages.hpp" #include "service-evtmgr/EVMessages.hpp" #include "service-evtmgr/KbdMessage.hpp" #include "service-evtmgr/ScreenLightControlMessage.hpp" #include "service-evtmgr/Constants.hpp" #include "service-evtmgr/EventManager.hpp" #include "service-evtmgr/WorkerEvent.hpp" #include "battery-level-check/BatteryLevelCheck.hpp" #include "screen-light-control/ScreenLightControl.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { constexpr auto loggerDelayMs = 1000 * 60 * 5; constexpr auto loggerTimerName = "Logger"; } // namespace EventManager::EventManager(const std::string &name) : sys::Service(name, "", stackDepth), settings(std::make_shared()), loggerTimer{sys::TimerFactory::createPeriodicTimer( this, loggerTimerName, std::chrono::milliseconds{loggerDelayMs}, [this](sys::Timer & /*timer*/) { dumpLogsToFile(); })}, Vibra(std::make_unique(this)), backlightHandler(settings, this) { LOG_INFO("[%s] Initializing", name.c_str()); alarmTimestamp = 0; alarmID = 0; bus.channels.push_back(sys::BusChannel::ServiceDBNotifications); loggerTimer.start(); } EventManager::~EventManager() { if (EventWorker != nullptr) { EventWorker->close(); } } // Invoked upon receiving data message sys::MessagePointer EventManager::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp) { bool handled = false; if (msgl->messageType == MessageType::DBServiceNotification) { auto *msg = dynamic_cast(msgl); if (msg != nullptr) { if (msg->interface == db::Interface::Name::Alarms) { alarmDBEmpty = false; alarmIsValid = false; handled = true; } } } else if (msgl->messageType == MessageType::EVM_GPIO) { LOG_DEBUG("EVM_GPIO msg"); } else if (msgl->messageType == MessageType::KBDKeyEvent && (msgl->sender == this->GetName() || msgl->sender == service::name::service_desktop)) { auto *msg = dynamic_cast(msgl); assert(msg); auto message = std::make_shared(); message->key = msg->key; if (message->key.state == RawKey::State::Pressed) { const auto code = message->key.key_code; if (code == bsp::KeyCodes::FnRight) { bus.sendUnicast(message, service::name::system_manager); } else if (code == bsp::KeyCodes::SSwitchUp || code == bsp::KeyCodes::SSwitchMid || code == bsp::KeyCodes::SSwitchDown) { const auto mode = sys::SystemManager::translateSliderState(message->key); bus.sendUnicast(std::make_shared(mode), service::name::system_manager); } backlightHandler.handleKeyPressed(); } // send key to focused application if (!targetApplication.empty()) { bus.sendUnicast(message, targetApplication); } // notify application manager to prevent screen locking app::manager::Controller::preventBlockingDevice(this); handled = true; } else if (msgl->messageType == MessageType::EVMFocusApplication) { auto *msg = static_cast(msgl); if (msg->sender == "ApplicationManager") { targetApplication = msg->getApplication(); handled = true; LOG_INFO("Switching focus to %s", targetApplication.c_str()); } } else if (msgl->messageType == MessageType::EVMMinuteUpdated && msgl->sender == this->GetName()) { auto msg = static_cast(msgl); handleMinuteUpdate(msg->timestamp); handled = true; } else if (auto msg = dynamic_cast(msgl); msg) { AudioServiceAPI::SendEvent(this, msg->getEvent()); handled = true; } else if (msgl->messageType == MessageType::EVMGetBoard) { using namespace bsp; auto msg = std::make_shared(true); auto board = magnetometer::GetBoard(); msg->board = board; LOG_INFO("Board discovered: %s", c_str(board)); return msg; } else if (msgl->messageType == MessageType::EVMModemStatus) { if (auto msg = dynamic_cast(msgl)) { auto message = std::make_shared(MessageType::EVMModemStatus); message->state = msg->state; bus.sendUnicast(message, ServiceCellular::serviceName); } handled = true; } else if (msgl->messageType == MessageType::EVMRingIndicator) { auto msg = std::make_shared(); bus.sendUnicast(std::move(msg), ServiceCellular::serviceName); } if (handled) { return std::make_shared(); } else { return std::make_shared(sys::ReturnCodes::Unresolved); } } // Invoked during initialization sys::ReturnCodes EventManager::InitHandler() { settings->init(service::ServiceProxy(shared_from_this())); backlightHandler.init(); connect(sdesktop::developerMode::DeveloperModeRequest(), [&](sys::Message *msg) { using namespace sdesktop::developerMode; auto req = static_cast(msg); if (typeid(*req->event.get()) == typeid(AppFocusChangeEvent)) { auto event = std::make_unique(targetApplication); auto message = std::make_shared(std::move(event)); bus.sendUnicast(message, service::name::service_desktop); } return std::make_shared(); }); connect(app::AppInputEventMessage(gui::InputEvent(RawKey())), [&](sys::Message *msgl) { auto msg = static_cast(msgl); assert(msg); auto message = std::make_shared(msg->getEvent()); if (!targetApplication.empty()) { bus.sendUnicast(std::move(message), targetApplication); } return std::make_shared(); }); connect(sevm::KeypadBacklightMessage(bsp::keypad_backlight::Action::turnOff), [&](sys::Message *msgl) { auto request = static_cast(msgl); auto response = std::make_shared(); response->success = backlightHandler.processKeypadRequest(request->action); return response; }); connect(sevm::BatterySetCriticalLevel(0), [&](sys::Message *msgl) { auto request = static_cast(msgl); battery_level_check::setBatteryCriticalLevel(request->criticalLevel); return sys::msgHandled(); }); connect(typeid(sevm::ScreenLightControlMessage), [&](sys::Message *msgl) { auto *m = dynamic_cast(msgl); backlightHandler.processScreenRequest(m->getAction(), screen_light_control::Parameters()); return sys::msgHandled(); }); connect(typeid(sevm::ScreenLightSetAutoModeParams), [&](sys::Message *msgl) { auto *m = static_cast(msgl); backlightHandler.processScreenRequest(m->getAction(), screen_light_control::Parameters(m->getParams())); return sys::msgHandled(); }); connect(typeid(sevm::ScreenLightSetManualModeParams), [&](sys::Message *msgl) { auto *m = static_cast(msgl); backlightHandler.processScreenRequest(m->getAction(), screen_light_control::Parameters(m->getParams())); return sys::msgHandled(); }); connect(sevm::ScreenLightControlRequestParameters(), [&](sys::Message *msgl) { screen_light_control::ManualModeParameters params = {backlightHandler.getScreenBrightnessValue()}; auto msg = std::make_shared( backlightHandler.getScreenLightState(), backlightHandler.getScreenAutoModeState(), params); return msg; }); connect(sevm::BatteryStatusChangeMessage(), [&](sys::Message *msgl) { if (msgl->sender == this->GetName()) { LOG_INFO("Battery level: %d , charging: %d", Store::Battery::get().level, Store::Battery::get().state == Store::Battery::State::Charging); if (Store::Battery::get().state == Store::Battery::State::Discharging) { bus.sendUnicast(std::make_shared(), service::name::system_manager); } if (!targetApplication.empty()) { bus.sendUnicast(std::make_shared(), targetApplication); } } return sys::msgHandled(); }); connect(sevm::VibraMessage(bsp::vibrator::Action::stop), [&](sys::Message *msgl) { auto request = static_cast(msgl); processVibraRequest(request->action, request->repetitionTime); return sys::msgHandled(); }); connect(sevm::ToggleTorchOnOffMessage(), [&]([[maybe_unused]] sys::Message *msg) { toggleTorchOnOff(); return sys::MessageNone{}; }); connect(sevm::ToggleTorchColorMessage(), [&]([[maybe_unused]] sys::Message *msg) { toggleTorchColor(); return sys::MessageNone{}; }); connect(sevm::RequestPhoneModeForceUpdate(), [&]([[maybe_unused]] sys::Message *msg) { EventWorker->requestSliderPositionRead(); return sys::MessageNone{}; }); connect(typeid(sevm::SIMTrayMessage), [&](sys::Message *) { bus.sendUnicast(std::make_shared(), ServiceCellular::serviceName); return sys::MessageNone{}; }); connect(typeid(stm::message::UpdateRTCValueFromTmMessage), [&](sys::Message *msg) { auto message = static_cast(msg); processRTCFromTmRequest(message->getTime()); return sys::MessageNone{}; }); connect(typeid(stm::message::UpdateRTCValueFromTimestampMessage), [&](sys::Message *msg) { auto message = static_cast(msg); processRTCFromTimestampRequest(message->getTime()); return sys::MessageNone{}; }); connect(typeid(stm::message::UpdateTimeZoneMessage), [&](sys::Message *msg) { auto message = static_cast(msg); processTimezoneRequest(message->getTimezone()); return sys::MessageNone{}; }); // initialize keyboard worker EventWorker = std::make_unique(this); using namespace std::string_literals; std::list list; list.emplace_back("qIrq"s, sizeof(uint8_t), 10); list.emplace_back("qHeadset"s, sizeof(uint8_t), 10); list.emplace_back("qBattery"s, sizeof(uint8_t), 10); list.emplace_back("qRTC"s, sizeof(uint8_t), 20); list.emplace_back("qSIM"s, sizeof(uint8_t), 5); list.emplace_back("qMagnetometer"s, sizeof(uint8_t), 5); list.emplace_back(WorkerEvent::MagnetometerNotifyQueue, sizeof(uint8_t), 1); list.emplace_back("qTorch"s, sizeof(uint8_t), 5); list.emplace_back("qLightSensor"s, sizeof(uint8_t), 5); list.emplace_back("qChargerDetect"s, sizeof(uint8_t), 5); EventWorker->init(list, settings); EventWorker->run(); return sys::ReturnCodes::Success; } sys::ReturnCodes EventManager::DeinitHandler() { settings->deinit(); EventWorker->close(); EventWorker.reset(); EventWorker = nullptr; return sys::ReturnCodes::Success; } void EventManager::ProcessCloseReason(sys::CloseReason closeReason) { bsp::torch::turn(bsp::torch::State::off); sendCloseReadyMessage(this); } sys::ReturnCodes EventManager::SwitchPowerModeHandler(const sys::ServicePowerMode mode) { LOG_FATAL("[ServiceEvtMgr] PowerModeHandler: %s", c_str(mode)); switch (mode) { case sys::ServicePowerMode ::Active: break; case sys::ServicePowerMode ::SuspendToRAM: case sys::ServicePowerMode ::SuspendToNVM: break; } return sys::ReturnCodes::Success; } bool EventManager::messageSetApplication(sys::Service *sender, const std::string &applicationName) { auto msg = std::make_shared(applicationName); return sender->bus.sendUnicast(msg, service::name::evt_manager); } void EventManager::dumpLogsToFile() { const auto logPath = purefs::dir::getUserDiskPath() / LOG_FILE_NAME; const bool dumpLog = !(std::filesystem::exists(logPath) && std::filesystem::file_size(logPath) > MAX_LOG_FILE_SIZE); if (dumpLog) { const auto &logs = Log::Logger::get().getLogs(); std::fstream logFile(logPath, std::fstream::out | std::fstream::app); logFile.write(logs.data(), logs.size()); } } void EventManager::handleMinuteUpdate(time_t timestamp) { if (!targetApplication.empty()) { auto message = std::make_shared(MessageType::EVMMinuteUpdated); message->timestamp = timestamp; bus.sendUnicast(message, targetApplication); } } bool EventManager::processVibraRequest(bsp::vibrator::Action act, std::chrono::milliseconds RepetitionTime) { switch (act) { case bsp::vibrator::Action::pulse: Vibra->Pulse(); break; case bsp::vibrator::Action::pulseRepeat: Vibra->PulseRepeat(RepetitionTime); break; case bsp::vibrator::Action::pulseRepeatInfinite: Vibra->PulseRepeat(); break; case bsp::vibrator::Action::stop: Vibra->PulseRepeatStop(); break; } return true; } void EventManager::toggleTorchOnOff() { auto state = bsp::torch::getState(); auto newState = (state.second == bsp::torch::State::off) ? bsp::torch::State::on : bsp::torch::State::off; bsp::torch::turn(newState, bsp::torch::ColourTemperature::coldest); } void EventManager::toggleTorchColor() { auto state = bsp::torch::getState(); if (state.second == bsp::torch::State::on) { auto color = bsp::torch::getColorTemp(); auto newColor = (color == bsp::torch::ColourTemperature::coldest) ? bsp::torch::ColourTemperature::warmest : bsp::torch::ColourTemperature::coldest; bsp::torch::turn(bsp::torch::State::on, newColor); } } void EventManager::processRTCFromTmRequest(struct tm &newTime) { if (bsp::rtc::setDateTime(&newTime) != bsp::rtc::ErrorCode::OK) { LOG_ERROR("Setting RTC failed."); return; } auto timestamp = time(nullptr); bsp::rtc::setMinuteAlarm(timestamp); auto notification = std::make_shared(MessageType::EVMTimeUpdated); bus.sendMulticast(std::move(notification), sys::BusChannel::ServiceEvtmgrNotifications); } void EventManager::processRTCFromTimestampRequest(time_t &newTime) { if (bsp::rtc::setDateTimeFromTimestamp(newTime) != bsp::rtc::ErrorCode::OK) { LOG_ERROR("Setting RTC failed."); return; } bsp::rtc::setMinuteAlarm(newTime); auto notification = std::make_shared(MessageType::EVMTimeUpdated); bus.sendMulticast(std::move(notification), sys::BusChannel::ServiceEvtmgrNotifications); } void EventManager::processTimezoneRequest(const std::string &timezone) { if (setenv("TZ", timezone.c_str(), 1) != 0) { LOG_ERROR("Setting timezone failed."); return; } auto notification = std::make_shared(MessageType::EVMTimeUpdated); bus.sendMulticast(std::move(notification), sys::BusChannel::ServiceEvtmgrNotifications); }