~aleteoryx/muditaos

82030a32a87c68f29eada26a146a93fd62a688f5 — Alek Rudnik 4 years ago 2149069 + 017e3d8
Merge branch 'master' into stable
245 files changed, 3171 insertions(+), 1426 deletions(-)

M .github/CODEOWNERS
M .gitignore
M .gitmodules
M CMakeLists.txt
M Jenkinsfile
M Target_RT1051.cmake
M board/linux/libiosyscalls/CMakeLists.txt
M board/rt1051/crashdump/crashdumpwriter_vfs.hpp
M board/rt1051/memwrap.c
M cmake/modules/PureCoverage.cmake
M doc/Doxyfile.in
M enabled_unittests
M image/assets/lang/English.json
M image/user/db/settings_v2_002.sql
M image/user/db/sms_001.sql
M image/user/db/sms_003.sql
M module-apps/Application.cpp
M module-apps/CMakeLists.txt
M module-apps/application-calculator/CMakeLists.txt
M module-apps/application-calculator/data/CalculatorUtility.cpp
M module-apps/application-calculator/tests/CMakeLists.txt
M module-apps/application-calculator/windows/CalculatorMainWindow.hpp
M module-apps/application-calendar/ApplicationCalendar.cpp
M module-apps/application-calendar/data/dateCommon.hpp
M module-apps/application-call/ApplicationCall.cpp
M module-apps/application-desktop/ApplicationDesktop.cpp
M module-apps/application-desktop/models/ActiveNotificationsModel.cpp
M module-apps/application-desktop/windows/DesktopMainWindow.cpp
M module-apps/application-desktop/windows/DesktopMainWindow.hpp
M module-apps/application-desktop/windows/UpdateProgress.cpp
M module-apps/application-messages/ApplicationMessages.cpp
M module-apps/application-notes/windows/NoteEditWindow.cpp
M module-apps/application-onboarding/ApplicationOnBoarding.cpp
M module-apps/application-onboarding/ApplicationOnBoarding.hpp
M module-apps/application-onboarding/CMakeLists.txt
A module-apps/application-onboarding/data/OnBoardingMessages.hpp
A module-apps/application-onboarding/presenter/OnBoardingFinalizeWindowPresenter.cpp
A module-apps/application-onboarding/presenter/OnBoardingFinalizeWindowPresenter.hpp
M module-apps/application-onboarding/windows/ConfigurationSuccessfulDialogWindow.cpp
M module-apps/application-onboarding/windows/NoConfigurationDialogWindow.cpp
M module-apps/application-onboarding/windows/OnBoardingChangeDateAndTimeWindow.cpp
M module-apps/application-onboarding/windows/OnBoardingChangeDateAndTimeWindow.hpp
M module-apps/application-onboarding/windows/OnBoardingDateAndTimeWindow.cpp
M module-apps/application-onboarding/windows/OnBoardingLanguagesWindow.cpp
D module-apps/application-onboarding/windows/OnBoardingMainWindow.cpp
M module-apps/application-onboarding/windows/OnBoardingSimSelectWindow.cpp
M module-apps/application-onboarding/windows/OnBoardingSimSelectWindow.hpp
M module-apps/application-onboarding/windows/StartConfigurationWindow.cpp
M module-apps/application-onboarding/windows/UpdateDialogWindow.cpp
M module-apps/application-onboarding/windows/UpdateDialogWindow.hpp
M module-apps/application-settings-new/ApplicationSettings.cpp
M module-apps/application-settings-new/ApplicationSettings.hpp
M module-apps/application-settings-new/windows/ChangeDateAndTimeWindow.cpp
M module-apps/application-settings-new/windows/DateAndTimeMainWindow.cpp
M module-apps/application-settings-new/windows/DateAndTimeMainWindow.hpp
M module-apps/application-settings-new/windows/DisplayLightWindow.cpp
M module-apps/application-settings-new/windows/NetworkWindow.cpp
M module-apps/application-settings-new/windows/NetworkWindow.hpp
M module-apps/application-settings/windows/DateTimeWindow.cpp
M module-apps/locks/data/LockStyle.hpp
M module-apps/locks/data/PhoneLockMessages.hpp
M module-apps/locks/handlers/PhoneLockHandler.cpp
M module-apps/locks/handlers/PhoneLockHandler.hpp
M module-apps/locks/handlers/SimLockHandler.cpp
M module-apps/locks/handlers/SimLockHandler.hpp
M module-apps/locks/widgets/PhoneLockBox.cpp
M module-apps/locks/windows/LockInputWindow.cpp
M module-apps/locks/windows/LockInputWindow.hpp
M module-apps/notifications/NotificationsModel.cpp
M module-apps/notifications/NotificationsModel.hpp
M module-apps/popups/CMakeLists.txt
M module-apps/popups/Popups.cpp
M module-apps/popups/Popups.hpp
A module-apps/popups/TetheringOffPopup.cpp
A module-apps/popups/TetheringOffPopup.hpp
M module-apps/popups/lock-popups/PhoneLockInputWindow.cpp
M module-apps/popups/lock-popups/PhoneLockedInfoWindow.cpp
M module-apps/popups/lock-popups/PhoneLockedWindow.cpp
M module-apps/popups/lock-popups/SimInfoWindow.cpp
M module-apps/popups/lock-popups/SimLockInputWindow.cpp
A module-apps/popups/lock-popups/SimNotReadyWindow.cpp
R module-apps/{application-onboarding/windows/OnBoardingMainWindow => popups/lock-popups/SimNotReadyWindow}.hpp
A module-apps/widgets/IceBox.cpp
A module-apps/widgets/IceBox.hpp
M module-apps/widgets/TimeWidget.cpp
M module-audio/Audio/AudioDevice.hpp
M module-audio/Audio/Operation/RecorderOperation.cpp
M module-audio/Audio/Profiles/ProfilePlaybackBluetoothA2DP.hpp
M module-audio/Audio/Profiles/ProfileRecordingBluetoothHSP.hpp
M module-audio/Audio/Profiles/ProfileRoutingBluetoothHSP.hpp
M module-audio/Audio/decoder/decoderMP3.hpp
D module-audio/Audio/decoder/minimp3
M module-audio/Audio/transcode/InputTranscodeProxy.cpp
M module-audio/Audio/transcode/InputTranscodeProxy.hpp
M module-audio/CMakeLists.txt
M module-audio/board/rt1051/RT1051DeviceFactory.cpp
M module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.cpp
M module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.hpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp
A module-bluetooth/Bluetooth/interface/profiles/AudioProfile.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/HSP/SCO.cpp
M module-bluetooth/Bluetooth/interface/profiles/HSP/SCO.hpp
M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp
M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp
M module-bsp/board/linux/battery-charger/battery_charger.cpp
M module-bsp/board/linux/magnetometer/magnetometer.cpp
M module-bsp/board/rt1051/bsp/battery-charger/battery_charger.cpp
M module-bsp/board/rt1051/bsp/magnetometer/ALS31300.hpp
D module-bsp/board/rt1051/bsp/magnetometer/doc/magnetometer_values.png
D module-bsp/board/rt1051/bsp/magnetometer/doc/readme.md
M module-bsp/board/rt1051/bsp/magnetometer/magnetometer.cpp
M module-bsp/board/rt1051/common/fsl_drivers/fsl_snvs_hp.c
M module-bsp/board/rt1051/drivers/RT1051DriverI2C.cpp
M module-bsp/bsp/BoardDefinitions.hpp
M module-bsp/bsp/battery-charger/battery_charger.hpp
M module-bsp/bsp/magnetometer/magnetometer.hpp
M module-cellular/at/Commands.hpp
M module-cellular/at/src/ATFactory.cpp
M module-cellular/at/src/Commands.cpp
M module-cellular/modem/mux/CellularMux.cpp
M module-db/CMakeLists.txt
M module-db/Interface/CalllogRecord.cpp
M module-db/Interface/CalllogRecord.hpp
M module-db/Interface/NotificationsRecord.cpp
M module-db/Interface/NotificationsRecord.hpp
M module-db/Interface/SMSRecord.cpp
M module-db/Interface/SMSRecord.hpp
M module-db/Tables/SMSTable.cpp
M module-db/Tables/SMSTable.hpp
A module-db/queries/notifications/QueryNotificationsMultipleIncrement.cpp
A module-db/queries/notifications/QueryNotificationsMultipleIncrement.hpp
M module-db/tests/CMakeLists.txt
M module-db/tests/SMSRecord_tests.cpp
M module-db/tests/SMSTable_tests.cpp
M module-db/tests/ThreadRecord_tests.cpp
M module-db/tests/test-initializer/CMakeLists.txt
M module-gui/gui/widgets/TopBar/Time.cpp
M module-gui/test/test-catch-text/CMakeLists.txt
M module-gui/test/test-catch/CMakeLists.txt
M module-services/service-appmgr/model/ApplicationManager.cpp
M module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp
M module-services/service-audio/ServiceAudio.cpp
M module-services/service-audio/service-audio/ServiceAudio.hpp
M module-services/service-cellular/CellularCall.cpp
M module-services/service-cellular/CellularUrcHandler.cpp
M module-services/service-cellular/ServiceCellular.cpp
A module-services/service-cellular/include/service-cellular/api/message.hpp
M module-services/service-cellular/include/service-cellular/api/notification/notification.hpp
D module-services/service-cellular/include/service-cellular/api/request/request.hpp
M module-services/service-cellular/include/service-cellular/api/request/sim.hpp
M module-services/service-cellular/service-cellular/ServiceCellular.hpp
M module-services/service-cellular/src/ServiceCellularPriv.cpp
M module-services/service-cellular/src/SimCard.cpp
M module-services/service-cellular/src/SimCard.hpp
M module-services/service-cellular/src/messages.hpp
M module-services/service-db/agents/settings/SystemSettings.hpp
M module-services/service-desktop/CMakeLists.txt
M module-services/service-desktop/ServiceDesktop.cpp
M module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.cpp
M module-services/service-desktop/endpoints/deviceInfo/DeviceInfoEndpoint.cpp
M module-services/service-desktop/endpoints/messages/MessageHelper.cpp
M module-services/service-desktop/endpoints/update/UpdateMuditaOS.cpp
M module-services/service-desktop/parser/ParserUtils.hpp
M module-services/service-desktop/tests/unittest.cpp
M module-services/service-evtmgr/CMakeLists.txt
M module-services/service-evtmgr/EventManager.cpp
M module-services/service-evtmgr/WorkerEvent.cpp
M module-services/service-evtmgr/backlight-handler/BacklightHandler.cpp
M module-services/service-evtmgr/backlight-handler/BacklightHandler.hpp
A module-services/service-evtmgr/battery-brownout-detector/BatteryBrownoutDetector.cpp
A module-services/service-evtmgr/battery-brownout-detector/BatteryBrownoutDetector.hpp
M module-services/service-evtmgr/doc/battery_charging.md
M module-services/service-evtmgr/screen-light-control/ScreenLightControl.cpp
M module-services/service-evtmgr/screen-light-control/ScreenLightControl.hpp
A module-services/service-evtmgr/screen-light-control/ScreenLightControlParameters.hpp
M module-services/service-evtmgr/service-evtmgr/EVMessages.hpp
M module-services/service-evtmgr/service-evtmgr/EventManager.hpp
M module-services/service-evtmgr/service-evtmgr/ScreenLightControlMessage.hpp
M module-services/service-evtmgr/service-evtmgr/WorkerEvent.hpp
M module-services/service-fileindexer/CMakeLists.txt
M module-services/service-fileindexer/ServiceFileIndexer.cpp
M module-services/service-fileindexer/ServiceFileIndexer.hpp
M module-services/service-fileindexer/StartupIndexer.cpp
D module-services/service-fileindexer/messages/FileChangeMessage.cpp
D module-services/service-fileindexer/messages/FileChangeMessage.hpp
M module-services/service-time/RTCcommand.cpp
M module-services/service-time/ServiceTime.cpp
M module-services/service-time/TimeManager.cpp
M module-services/service-time/service-time/RTCCommand.hpp
M module-services/service-time/service-time/RTCCommandInterface.hpp
M module-services/service-time/service-time/TimeManager.hpp
M module-services/service-time/service-time/TimeMessage.hpp
M module-sys/Service/BusProxy.hpp
M module-sys/Service/Message.hpp
M module-utils/CMakeLists.txt
M module-utils/Utils.cpp
M module-utils/Utils.hpp
M module-utils/bootconfig/src/bootconfig.cpp
D module-utils/parallel-hashmap
D module-utils/protobuf
D module-utils/taglib
M module-utils/test/test_i18n.cpp
M module-utils/third-party/libphonenumber.cmake
D module-utils/third-party/protobuf-lite.cmake
D module-utils/third-party/tinyexpr.cmake
M module-utils/time/time_conversion.cpp
M module-utils/time/time_conversion.hpp
D module-utils/tinyexpr
M module-vfs/CMakeLists.txt
A module-vfs/include/internal/purefs/fs/notifier.hpp
D module-vfs/include/user/deprecated/vfsNotifier.hpp
R module-vfs/include/{internal => user}/purefs/fs/directory_handle.hpp
R module-vfs/include/{internal => user}/purefs/fs/file_handle.hpp
M module-vfs/include/user/purefs/fs/filesystem.hpp
A module-vfs/include/user/purefs/fs/fsnotify.hpp
R module-vfs/include/{internal => user}/purefs/fs/handle_mapper.hpp
A module-vfs/include/user/purefs/fs/inotify.hpp
A module-vfs/include/user/purefs/fs/inotify_flags.hpp
A module-vfs/include/user/purefs/fs/inotify_message.hpp
R module-vfs/include/{internal => user}/purefs/fs/mount_flags.hpp
R module-vfs/include/{internal => user}/purefs/fs/mount_point.hpp
D module-vfs/src/deprecated/vfsNotifier.cpp
M module-vfs/src/purefs/fs/filesystem.cpp
M module-vfs/src/purefs/fs/filesystem_syscalls.cpp
A module-vfs/src/purefs/fs/fsnotify.cpp
A module-vfs/src/purefs/fs/notifier.cpp
M module-vfs/tests/CMakeLists.txt
A module-vfs/tests/unittest_filesystem_inotify.cpp
M test/pytest/service-desktop/test_messages.py
M test/pytest/service-desktop/test_templates.py
M test/pytest/service-desktop/test_threads.py
M third-party/CMakeLists.txt
A third-party/minimp3/CMakeLists.txt
A third-party/minimp3/minimp3
A third-party/parallel-hashmap/CMakeLists.txt
A third-party/parallel-hashmap/src
A third-party/protobuf/CMakeLists.txt
A third-party/protobuf/src
A third-party/taglib
A third-party/tinyexpr/CMakeLists.txt
A third-party/tinyexpr/src
M .github/CODEOWNERS => .github/CODEOWNERS +1 -0
@@ 15,6 15,7 @@
# build system
/CMakeLists.txt                             @mudita/chapter-bsp
/third-party/CMakeLists.txt                 @mudita/chapter-bsp
/module-utils/CMakeLists.txt                @mudita/chapter-bsp
/cmake                                      @mudita/chapter-bsp
/test/CMakeLists.txt                        @mudita/chapter-bsp


M .gitignore => .gitignore +2 -0
@@ 60,3 60,5 @@ update/

# often created by the visual studio code
/null.d

test/test_env
\ No newline at end of file

M .gitmodules => .gitmodules +10 -10
@@ 17,7 17,7 @@
	branch = rt1051
	shallow = true
[submodule "module-utils/protobuf"]
	path = module-utils/protobuf
	path = third-party/protobuf/src
	url = ../protobuf.git
	branch = rt1051
	shallow = true


@@ 36,12 36,8 @@
[submodule "module-utils/date"]
	path = third-party/date
	url = ../date.git
[submodule "module-audio/Audio/decoder/minimp3"]
	path = module-audio/Audio/decoder/minimp3
	url = ../minimp3.git
    branch = RT1051
[submodule "module-utils/taglib"]
	path = module-utils/taglib
	path = third-party/taglib
	url = ../taglib.git
	branch = rt1051
[submodule "module-bluetooth/lib/btstack"]


@@ 54,7 50,7 @@
	path = third-party/magic_enum
	url = https://github.com/Neargye/magic_enum.git
[submodule "module-utils/tinyexpr"]
	path = module-utils/tinyexpr
	path = third-party/tinyexpr/src
	url = https://github.com/codeplea/tinyexpr.git
[submodule "module-bsp/board/rt1051/bsp/usb"]
	path = module-bsp/board/rt1051/bsp/usb


@@ 68,9 64,6 @@
[submodule "module-vfs/thirdparty/lfsfs/littlefs"]
	path = module-vfs/thirdparty/lfsfs/littlefs
	url = https://github.com/littlefs-project/littlefs.git
[submodule "module-utils/parallel-hashmap"]
	path = module-utils/parallel-hashmap
	url = https://github.com/greg7mdp/parallel-hashmap.git
[submodule "module-utils/sml"]
	path = third-party/sml/src
	url = https://github.com/boost-ext/sml


@@ 84,3 77,10 @@
[submodule "module-utils/CrashDebug"]
	path = module-utils/CrashDebug
	url = https://github.com/adamgreen/CrashDebug.git
[submodule "third-party/minimp3/minimp3"]
	path = third-party/minimp3/minimp3
	url = ../minimp3.git
	branch = RT1051
[submodule "third-party/parallel-hashmap/src"]
	path = third-party/parallel-hashmap/src
	url = https://github.com/greg7mdp/parallel-hashmap.git

M CMakeLists.txt => CMakeLists.txt +2 -2
@@ 150,6 150,8 @@ target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_INCLUDES})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_BINARY_DIR})

add_subdirectory(third-party)

if (${PROJECT_TARGET} STREQUAL "TARGET_Linux")
     add_subdirectory(board/linux/libiosyscalls)
endif()


@@ 192,8 194,6 @@ add_subdirectory(module-bluetooth)
message("${PROJECT_NAME}: add_subdirectory module-lwip")
add_subdirectory(module-lwip)

add_subdirectory(third-party)

add_subdirectory(image)

set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES SUFFIX ".elf")

M Jenkinsfile => Jenkinsfile +2 -13
@@ 81,13 81,8 @@ popd'''
        when {
            changeRequest()
        }
        parallel {
        stages{
        stage('Build RT1051') {
            agent {
                node {
                    label 'jenkins-slave-ccache-ram'
                }
            }
            steps {
                sh '''#!/bin/bash -e
PATH="/usr/local/cmake-3.19.5-Linux-x86_64/bin:/usr/local/gcc-arm-none-eabi-10-2020-q4-major/bin:$PATH"


@@ 114,12 109,6 @@ ccache --show-stats'''
        }

        stage('Build Linux') {
            agent {
                node {
                    label 'jenkins-slave-ccache-ram'
                }
            }

            environment {
                PATH="/usr/local/cmake-3.19.5-Linux-x86_64/bin:/usr/local/gcc-arm-none-eabi-10-2020-q4-major/bin:$PATH"
                CCACHE_DIR="/ccache/Linux"


@@ 166,7 155,7 @@ popd
popd'''
            }
        }
        }
    }
    }
    stage('master-jobs') {
        when {

M Target_RT1051.cmake => Target_RT1051.cmake +1 -0
@@ 95,6 95,7 @@ set(BOARD_DIR_INCLUDES
        ${CMAKE_CURRENT_LIST_DIR}/board/rt1051/newlib/include
        ${CMAKE_SOURCE_DIR}/module-utils/CrashDebug/CrashCatcher/include
        ${CMAKE_SOURCE_DIR}/module-utils/CrashDebug/CrashCatcher/Core/src
        ${CMAKE_SOURCE_DIR}/module-vfs/include/internal
)

set(TARGET_LIBRARIES

M board/linux/libiosyscalls/CMakeLists.txt => board/linux/libiosyscalls/CMakeLists.txt +21 -13
@@ 5,22 5,21 @@ project(iosyscalls VERSION 1.0


set(SOURCES
        ${CMAKE_CURRENT_SOURCE_DIR}/src/syscalls_stdio.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/syscalls_scan_family.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/syscalls_posix.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/syscalls_posix_dirent.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/iosyscalls.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/src/syscalls_stdio.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/src/syscalls_scan_family.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/src/syscalls_posix.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/src/syscalls_posix_dirent.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/src/iosyscalls.cpp
)


set(INCLUDES
	"${CMAKE_CURRENT_SOURCE_DIR}/include"
	"${CMAKE_SOURCE_DIR}/module-os/board/linux"
        "${CMAKE_SOURCE_DIR}/module-os/RTOSWrapper/include"
	"${CMAKE_SOURCE_DIR}/module-utils/parallel-hashmap"
	"${CMAKE_SOURCE_DIR}/module-utils"
	"${CMAKE_SOURCE_DIR}/module-vfs/include/user"
	"${CMAKE_SOURCE_DIR}/module-vfs/include/internal"
    "${CMAKE_CURRENT_SOURCE_DIR}/include"
    "${CMAKE_SOURCE_DIR}/module-os/board/linux"
    "${CMAKE_SOURCE_DIR}/module-os/RTOSWrapper/include"
    "${CMAKE_SOURCE_DIR}/module-utils"
    "${CMAKE_SOURCE_DIR}/module-vfs/include/user"
    "${CMAKE_SOURCE_DIR}/module-vfs/include/internal"
)




@@ 45,7 44,16 @@ target_link_options(${PROJECT_NAME} PRIVATE
)

target_include_directories(${PROJECT_NAME} PUBLIC ${INCLUDES} )
target_link_libraries( ${PROJECT_NAME} dl )

target_link_libraries(
    ${PROJECT_NAME} 
    PUBLIC
        dl
    PRIVATE
        phmap
)


target_compile_definitions( ${PROJECT_NAME} PUBLIC TARGET_Linux )

if (${ENABLE_TESTS})

M board/rt1051/crashdump/crashdumpwriter_vfs.hpp => board/rt1051/crashdump/crashdumpwriter_vfs.hpp +1 -1
@@ 33,7 33,7 @@ namespace crashdump
        {
            std::time_t now;
            std::time(&now);
            std::strftime(name.data(), name.size(), CrashDumpFileNameFormat, gmtime(&now));
            std::strftime(name.data(), name.size(), CrashDumpFileNameFormat, std::localtime(&now));
        }

        int dumpFd{-1};

M board/rt1051/memwrap.c => board/rt1051/memwrap.c +0 -9
@@ 58,13 58,6 @@ void* calloc (size_t num, size_t size)
    return memset(p, 0, total);
}

/* struct tm * localtime(const time_t *t)
{
  static struct tm y = {0};
  //RtcServiceGetCurrentDateTime(&tm);
  return &y;
} */

void *realloc(void *aptr, size_t nbytes)
{
    return userrealloc(aptr, nbytes);


@@ 104,5 97,3 @@ void _putchar(char character)
{
    // Use of printf is banned
}



M cmake/modules/PureCoverage.cmake => cmake/modules/PureCoverage.cmake +1 -8
@@ 19,10 19,10 @@ if(COVERAGE_ENABLE)
        .*/test/.*
        .*/tests/.*
        .*/thirdparty/.*
        .*/third-party/.*
        board/linux/libiosyscalls/.*
        host-tools/.*
        module-audio/Audio/decoder/dr_flac.h
        module-audio/Audio/decoder/minimp3/minimp3.h
        module-bluetooth/Bluetooth/glucode/.*
        module-bluetooth/lib/.*
        module-db/Database/sqlite3.c


@@ 31,15 31,8 @@ if(COVERAGE_ENABLE)
        module-utils/gsl/.*
        module-utils/json/.*
        module-utils/libphonenumber/.*
        module-utils/microtar/.*
        module-utils/parallel-hashmap/.*
        module-utils/protobuf/.*
        third-party/pugixml/.*
        module-utils/re2/.*
        module-utils/segger/.*
        module-utils/sml/.*
        module-utils/taglib/.*
        module-utils/tinyexpr/.*
        source/main.cpp
        test/.*
    )

M doc/Doxyfile.in => doc/Doxyfile.in +2 -4
@@ 887,12 887,9 @@ RECURSIVE              = YES

EXCLUDE                = @PROJECT_SOURCE_DIR@/module-bluetooth/lib \
                         @PROJECT_SOURCE_DIR@/module-utils/tinyfsm \
                         @PROJECT_SOURCE_DIR@/module-utils/microtar \
                         @PROJECT_SOURCE_DIR@/module-utils/segger \
                         @PROJECT_SOURCE_DIR@/module-utils/crc32 \
                         @PROJECT_SOURCE_DIR@/module-utils/sbini \
                         @PROJECT_SOURCE_DIR@/module-utils/re2 \
                         @PROJECT_SOURCE_DIR@/module-utils/protobuf \
                         @PROJECT_SOURCE_DIR@/module-utils/libphonenumber \
                         @PROJECT_SOURCE_DIR@/module-utils/test \
                         @PROJECT_SOURCE_DIR@/module-db/tests \


@@ 906,7 903,8 @@ EXCLUDE                = @PROJECT_SOURCE_DIR@/module-bluetooth/lib \
                         @PROJECT_SOURCE_DIR@/module-vfs/tests \
                         @PROJECT_SOURCE_DIR@/module-vfs/board \
                         @PROJECT_SOURCE_DIR@/module-bsp/board/rt1051/common \
                         @PROJECT_SOURCE_DIR@/module-audio/Audio/decoder/taglib
                         @PROJECT_SOURCE_DIR@/module-audio/Audio/decoder/taglib \
                         @PROJECT_SOURCE_DIR@/third-party

# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded

M enabled_unittests => enabled_unittests +5 -0
@@ 460,6 460,11 @@ TESTS_LIST["catch2-iosyscalls"]="
    VFS linux support;
"
#---------
TESTS_LIST["catch2-vfs-inotify"]="
   Notifier base class test;
   Filtering test;
"
#---------
TESTS_LIST["catch2-cellular-cmux"]="
    TS0170 frame;
"

M image/assets/lang/English.json => image/assets/lang/English.json +7 -3
@@ 246,6 246,8 @@
  "sim_setup_wrong_puk_last_attempt_warning": "<text>If the code is wrong this time, the<br></br>SIM card will be blocked and you'll<br></br>have to contact the operator.</text>",
  "sim_card_pin_disabled": "SIM card pin disabled",
  "sim_card_pin_enabled": "SIM card pin enabled",
  "sim_card_cant_connect": "<text>Cannot connect to <token>$SIM</token> card.<br></br>Please insert card.</text>",
  "sim_card_not_ready": "<text>Waiting for Modem to start.<br></br>This may take a moment.</text>",
  "app_desktop_press_to_unlock": "<text font='gt_pressura' size='27'>Press <b>Unlock</b> and then <b>#</b></text>",
  "app_desktop_unread_messages": "<text>Unread <b>messages</b></text>",
  "app_desktop_missed_calls": "<text>Missed <b>calls</b></text>",


@@ 318,8 320,8 @@
  "app_onboarding_title_configuration": "Configuration",
  "app_onboarding_title_update_info": "MuditaOS update",
  "app_onboarding_skip_confirm": "<text>Sim setup is required for network connection. Skip the setup anyway? </text>",
  "app_onboarding_configuration_successful": "<text>Your Pure was configured</text><br></br><text>successfully.</text>",
  "app_onboarding_no_configuration": "<text>Your Pure is not configured.</text><br></br><text>You can go to Settings to.</text><br></br><text>configure it.</text>",
  "app_onboarding_configuration_successful": "<text>Your Mudita Pure<br></br>is ready to use.</text>",
  "app_onboarding_no_configuration": "<text>Your Mudita Pure has not been<br></br>configured. You can go to<br></br>Settings to set it up.</text>",
  "app_onboarding_update_info": "<text>New Mudita OS version is available.</text><br></br><text>To update your Pure Phone, please</text><br></br><text>visit: </text><text font='gt_pressura' weight='bold' size='27'>www.mudita.com/updateos</text><br></br><text>and follow the instructions.</text>",
  "app_settings_title_main": "Advanced",
  "app_settings_title_main_new": "Settings",


@@ 566,6 568,8 @@
  "app_desktop_update_success": "MuditaOS has been updated to ver. $VERSION succesfully.",
  "app_call_private_number": "Private number",
  "tethering": "Tethering",
  "tethering_turn_off_question": "Turn tethering off?",
  "tethering_enable_question": "<text>You're connected to the computer.<br />Turn tethering on?<br /><text color='9'>(some functions may be disabled)</text></text>",
  "tethering_phone_mode_change_prohibited": "<text>Tethering is on.<br /><br />Other modes (Connected, DND,<br />Offline) are overriden by this mode<br />and are not working.</text>"
  "tethering_phone_mode_change_prohibited": "<text>Tethering is on.<br /><br />Other modes (Connected, DND,<br />Offline) are overriden by this mode<br />and are not working.</text>",
  "tethering_menu_access_decline": "<text>Tethering is on.<br /><br />To access menu,<br />turn tethering off.</text>"
}

M image/user/db/settings_v2_002.sql => image/user/db/settings_v2_002.sql +4 -4
@@ 19,7 19,7 @@ INSERT OR IGNORE INTO settings_tab (path, value) VALUES
    ('gs_display_language', 'English'),
    ('gs_input_language', 'English'),
    ('gs_eula_accepted', '0'),
    ('gs_onboarding_done', '0'),
    ('gs_onboarding_done', '1'),
    ('gs_usb_security', '1'),
    ('gs_usb_devices', ''),
    ('gs_os_update_version', '0.00.0'),


@@ 33,7 33,7 @@ INSERT OR IGNORE INTO settings_tab (path, value) VALUES
    ('off_connection_frequency', '0'),
    ('off_notifications_when_locked', '0'),
    ('off_calls_from_favorites', '0'),
    ('br_state', '0'),
    ('br_auto_mode', '0'),
    ('br_level', '50.0f'),
    ('\EventManager\\br_state', '0'),
    ('\EventManager\\br_auto_mode', '0'),
    ('\EventManager\\br_level', '50.0f'),
    ('keypad_light_state', '0');

M image/user/db/sms_001.sql => image/user/db/sms_001.sql +0 -1
@@ 7,7 7,6 @@ CREATE TABLE IF NOT EXISTS sms
    thread_id  INTEGER,
    contact_id INTEGER,
    date       INTEGER,
    date_send  INTEGER,
    error_code INTEGER,
    body       TEXT NOT_NULL,
    type       INTEGER,

M image/user/db/sms_003.sql => image/user/db/sms_003.sql +6 -6
@@ 1,12 1,12 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
BEGIN TRANSACTION;
INSERT OR REPLACE INTO "sms" ("_id","thread_id","contact_id","date","date_send","error_code","body","type") VALUES (1,2,2,1547492320,3,0,'Thank you for today!' || CHAR(10) || 'You chose a fantastic place :)',8);
INSERT OR REPLACE INTO "sms" ("_id","thread_id","contact_id","date","date_send","error_code","body","type") VALUES (2,2,2,1547492321,4,0,'It was great seeing you too :*',4);
INSERT OR REPLACE INTO "sms" ("_id","thread_id","contact_id","date","date_send","error_code","body","type") VALUES (3,1,1,1618907731,3,2,'Hi! How are you today?',8);
INSERT OR REPLACE INTO "sms" ("_id","thread_id","contact_id","date","date_send","error_code","body","type") VALUES (4,1,1,1618907732,3,2,'I hope you''re feeling better now...',8);
INSERT OR REPLACE INTO "sms" ("_id","thread_id","contact_id","date","date_send","error_code","body","type") VALUES (5,1,1,1618907733,3,2,'Thanks :) Today is better than yesterday',4);
INSERT OR REPLACE INTO "sms" ("_id","thread_id","contact_id","date","date_send","error_code","body","type") VALUES (6,1,1,1618907734,3,2,'I''m happy to hear that :)',8);
INSERT OR REPLACE INTO "sms" ("_id","thread_id","contact_id","date","error_code","body","type") VALUES (1,2,2,1547492320,0,'Thank you for today!' || CHAR(10) || 'You chose a fantastic place :)',8);
INSERT OR REPLACE INTO "sms" ("_id","thread_id","contact_id","date","error_code","body","type") VALUES (2,2,2,1547492321,0,'It was great seeing you too :*',4);
INSERT OR REPLACE INTO "sms" ("_id","thread_id","contact_id","date","error_code","body","type") VALUES (3,1,1,1618907731,2,'Hi! How are you today?',8);
INSERT OR REPLACE INTO "sms" ("_id","thread_id","contact_id","date","error_code","body","type") VALUES (4,1,1,1618907732,2,'I hope you''re feeling better now...',8);
INSERT OR REPLACE INTO "sms" ("_id","thread_id","contact_id","date","error_code","body","type") VALUES (5,1,1,1618907733,2,'Thanks :) Today is better than yesterday',4);
INSERT OR REPLACE INTO "sms" ("_id","thread_id","contact_id","date","error_code","body","type") VALUES (6,1,1,1618907734,2,'I''m happy to hear that :)',8);
INSERT OR REPLACE INTO "templates" ("_id","text","lastUsageTimestamp") VALUES (1,'Thanks for reaching out. I can''t talk right now, I''ll call you later',4);
INSERT OR REPLACE INTO "templates" ("_id","text","lastUsageTimestamp") VALUES (2,'I''ll call you later',3);
INSERT OR REPLACE INTO "templates" ("_id","text","lastUsageTimestamp") VALUES (3,'I''ll be there in 15 minutes',2);

M module-apps/Application.cpp => module-apps/Application.cpp +6 -0
@@ 51,6 51,7 @@
#include <popups/lock-popups/PhoneLockChangeInfoWindow.hpp>
#include <popups/lock-popups/SimLockInputWindow.hpp>
#include <popups/lock-popups/SimInfoWindow.hpp>
#include <popups/lock-popups/SimNotReadyWindow.hpp>
#include <popups/data/PopupData.hpp>
#include <popups/data/PopupRequestParams.hpp>
#include <popups/data/PhoneModeParams.hpp>


@@ 774,6 775,7 @@ namespace app
                break;
            case ID::PhoneLock:
            case ID::PhoneLockInput:
            case ID::PhoneLockInfo:
            case ID::PhoneLockChangeInfo:
                windowsFactory.attach(window::phone_lock_window, [](Application *app, const std::string &name) {
                    return std::make_unique<gui::PhoneLockedWindow>(app, window::phone_lock_window);


@@ 796,12 798,16 @@ namespace app
                break;
            case ID::SimLock:
            case ID::SimInfo:
            case ID::SimNotReady:
                windowsFactory.attach(window::sim_unlock_window, [](Application *app, const std::string &name) {
                    return std::make_unique<gui::SimLockInputWindow>(app, window::sim_unlock_window);
                });
                windowsFactory.attach(window::sim_info_window, [](Application *app, const std::string &name) {
                    return std::make_unique<gui::SimInfoWindow>(app, window::sim_info_window);
                });
                windowsFactory.attach(window::sim_not_ready_window, [](Application *app, const std::string &name) {
                    return std::make_unique<gui::SimNotReadyWindow>(app, window::sim_not_ready_window);
                });
                break;
            }
        }

M module-apps/CMakeLists.txt => module-apps/CMakeLists.txt +1 -0
@@ 37,6 37,7 @@ set( SOURCES
    "widgets/ActiveIconFactory.cpp"
    "widgets/TextWithIconsWidget.cpp"
    "widgets/DateWidget.cpp"
    "widgets/IceBox.cpp"
    "widgets/TimeWidget.cpp"
    "widgets/WidgetsUtils.cpp"
    "notifications/NotificationListItem.cpp"

M module-apps/application-calculator/CMakeLists.txt => module-apps/application-calculator/CMakeLists.txt +3 -1
@@ 18,4 18,6 @@ endif()

target_link_libraries(${PROJECT_NAME}
    PRIVATE
        service-evtmgr)
        service-evtmgr
        tinyexpr::tinyexpr
)

M module-apps/application-calculator/data/CalculatorUtility.cpp => module-apps/application-calculator/data/CalculatorUtility.cpp +1 -1
@@ 4,7 4,7 @@
#include "CalculatorUtility.hpp"
#include "application-calculator/data/CalculatorInputProcessor.hpp"
#include "application-calculator/widgets/CalculatorStyle.hpp"
#include <module-utils/tinyexpr/tinyexpr.h>
#include <tinyexpr.h>
#include <i18n/i18n.hpp>
#include <Utils.hpp>
#include <cmath>

M module-apps/application-calculator/tests/CMakeLists.txt => module-apps/application-calculator/tests/CMakeLists.txt +4 -2
@@ 10,8 10,10 @@ add_catch2_executable(
        "${CMAKE_CURRENT_SOURCE_DIR}/CalculatorInput_tests.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/CalculatorUtility_tests.cpp"
    LIBS
        module-sys
        iosyscalls
        module-apps
        module-utils
        module-vfs
        iosyscalls
    DEPS
        assets
)

M module-apps/application-calculator/windows/CalculatorMainWindow.hpp => module-apps/application-calculator/windows/CalculatorMainWindow.hpp +0 -1
@@ 8,7 8,6 @@
#include "application-calculator/data/CalculatorInputProcessor.hpp"
#include "application-calculator/widgets/MathOperationsBox.hpp"
#include <module-gui/gui/widgets/Text.hpp>
#include <module-utils/tinyexpr/tinyexpr.h>

namespace gui
{

M module-apps/application-calendar/ApplicationCalendar.cpp => module-apps/application-calendar/ApplicationCalendar.cpp +2 -3
@@ 20,7 20,7 @@
#include <service-db/QueryMessage.hpp>
#include <service-db/DBNotificationMessage.hpp>

#include <time/time_conversion.hpp>
#include <ctime>

namespace app
{


@@ 98,8 98,7 @@ namespace app

    sys::ReturnCodes ApplicationCalendar::InitHandler()
    {
        utils::time::Timestamp timestamp;
        applicationStartTime = timestamp.getTime();
        applicationStartTime = std::time(nullptr);
        auto ret             = Application::InitHandler();
        createUserInterface();
        return ret;

M module-apps/application-calendar/data/dateCommon.hpp => module-apps/application-calendar/data/dateCommon.hpp +3 -4
@@ 103,8 103,7 @@ inline time_t TimePointToTimeT(const TimePoint &tp)

inline TimePoint TimePointNow()
{
    utils::time::Timestamp timestamp;
    return TimePointFromTimeT(timestamp.getTime());
    return TimePointFromTimeT(std::time(nullptr));
}

inline std::string TimePointToString(const TimePoint &tp)


@@ 130,7 129,7 @@ inline auto LocalizedHoursToUtcHours(int hour = 0)
{
    std::tm tm           = CreateTmStruct(unix_epoch_year, 1, 1, hour, 0, 0);
    std::time_t basetime = std::mktime(&tm);
    basetime -= utils::time::Time::getTimeZoneOffset();
    basetime -= GetDiffLocalWithUTCTime();
    return TimePointToHour24H(TimePointFromTimeT(basetime));
}



@@ 304,7 303,7 @@ inline std::string createUID()
{
    constexpr uint32_t bufferLimit = 16;
    char Buffer[bufferLimit];
    utils::time::Timestamp timestamp;
    utils::time::Timestamp timestamp = utils::time::getCurrentTimestamp();
    std::string UID{timestamp.str("%Y%m%dT%H%M%S")};
    UID += '-';
    std::random_device rd;

M module-apps/application-call/ApplicationCall.cpp => module-apps/application-call/ApplicationCall.cpp +0 -3
@@ 91,9 91,6 @@ namespace app
        return getState() == call::State::IDLE;
    }

    //  number of seconds after end call to switch back to previous application
    const inline utils::time::Duration delayToSwitchToPreviousApp = 3;

    void ApplicationCall::CallAbortHandler()
    {
        manager::Controller::sendAction(this, manager::actions::Call, std::make_unique<app::CallAbortData>());

M module-apps/application-desktop/ApplicationDesktop.cpp => module-apps/application-desktop/ApplicationDesktop.cpp +5 -0
@@ 17,6 17,7 @@
#include "windows/MmiPushWindow.hpp"
#include "windows/MmiInternalMsgWindow.hpp"
#include <popups/presenter/PowerOffPresenter.hpp>
#include <popups/TetheringOffPopup.hpp>
#include <windows/Dialog.hpp>
#include <windows/DialogMetadata.hpp>
#include <messages/DialogMetadataMessage.hpp>


@@ 262,6 263,9 @@ namespace app
        windowsFactory.attach(gui::window::name::dialog_confirm, [](Application *app, const std::string &name) {
            return std::make_unique<gui::DialogConfirm>(app, name);
        });
        windowsFactory.attach(gui::popup::window::tethering_off_window, [](Application *app, const std::string &name) {
            return std::make_unique<gui::TetheringOffPopup>(app, gui::popup::window::tethering_off_window);
        });

        attachPopups({gui::popup::ID::Volume,
                      gui::popup::ID::Tethering,


@@ 329,4 333,5 @@ namespace app
        osUpdateVersion = value;
        settings->setValue(settings::SystemProperties::osUpdateVersion, value, settings::SettingsScope::Global);
    }

} // namespace app

M module-apps/application-desktop/models/ActiveNotificationsModel.cpp => module-apps/application-desktop/models/ActiveNotificationsModel.cpp +2 -3
@@ 6,7 6,6 @@
#include <module-db/queries/notifications/QueryNotificationsClear.hpp>
#include <service-appmgr/Controller.hpp>
#include <application-call/data/CallSwitchData.hpp>
#include <SystemManager/messages/TetheringStateRequest.hpp>
#include <queries/messages/threads/QueryThreadGetByNumber.hpp>
#include <application-messages/data/SMSdata.hpp>
#include <application-messages/Constants.hpp>


@@ 148,8 147,8 @@ namespace
    void setTetheringActivatedCallback(NotificationListItem *item, app::Application *app)
    {
        item->activatedCallback = [app]([[maybe_unused]] gui::Item &_item) {
            return app->bus.sendUnicast(std::make_shared<sys::TetheringStateRequest>(sys::phone_modes::Tethering::Off),
                                        service::name::system_manager);
            app->switchWindow(gui::popup::window::tethering_off_window);
            return true;
        };
    }


M module-apps/application-desktop/windows/DesktopMainWindow.cpp => module-apps/application-desktop/windows/DesktopMainWindow.cpp +32 -6
@@ 13,6 13,9 @@
#include <service-time/ServiceTime.hpp>
#include <service-time/TimeMessage.hpp>
#include <notifications/NotificationsModel.hpp>
#include <windows/Dialog.hpp>
#include <windows/DialogMetadata.hpp>
#include <messages/DialogMetadataMessage.hpp>

#include <log/log.hpp>



@@ 40,7 43,17 @@ namespace gui
        dayText->setAlignment(Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top));

        activatedCallback = [this]([[maybe_unused]] Item &item) {
            application->switchWindow(app::window::name::desktop_menu);
            if (notificationsModel->isTetheringOn()) {
                showInformationPopup(
                    [this]() {
                        app::manager::Controller::sendAction(application, app::manager::actions::Home);
                        return true;
                    },
                    utils::translate("tethering_menu_access_decline"));
            }
            else {
                application->switchWindow(app::window::name::desktop_menu);
            }
            return true;
        };



@@ 196,13 209,14 @@ namespace gui
            return;
        }
        bottomBar->setText(BottomBar::Side::CENTER, utils::translate("app_desktop_menu"));
        bottomBar->setText(BottomBar::Side::LEFT, utils::translate("app_desktop_calls"));
        const auto tetheringNotActive = !notificationsModel->isTetheringOn();
        bottomBar->setText(BottomBar::Side::LEFT, utils::translate("app_desktop_calls"), tetheringNotActive);
        const auto hasDismissibleNotification = notificationsModel->hasDismissibleNotification();
        bottomBar->setText(
            BottomBar::Side::RIGHT, utils::translate("app_desktop_clear_all"), hasDismissibleNotification);

        inputCallback = [this, hasDismissibleNotification]([[maybe_unused]] Item &item,
                                                           const InputEvent &inputEvent) -> bool {
        inputCallback = [this, hasDismissibleNotification, tetheringNotActive]([[maybe_unused]] Item &item,
                                                                               const InputEvent &inputEvent) -> bool {
            if (!inputEvent.isShortRelease() || notificationsList->focus) {
                return false;
            }


@@ 211,7 225,7 @@ namespace gui
                notificationsModel->dismissAll(inputEvent);
                return true;
            }
            if (inputEvent.is(gui::KeyCode::KEY_LF)) {
            if (inputEvent.is(gui::KeyCode::KEY_LF) && tetheringNotActive) {
                LOG_DEBUG("KEY_LF pressed to navigate to calls");
                return app::manager::Controller::sendAction(application, app::manager::actions::ShowCallLog);
            }


@@ 230,7 244,7 @@ namespace gui
    {
        using namespace utils::time;
        auto ret       = AppWindow::updateTime();
        auto timestamp = utils::time::Timestamp();
        auto timestamp = utils::time::getCurrentTimestamp();
        if (time != nullptr) {
            auto fmt = utils::dateAndTimeSettings.isTimeFormat12()
                           ? Locale::format(Locale::TimeFormat::FormatTime12HShort)


@@ 242,4 256,16 @@ namespace gui
        }
        return ret;
    }

    bool DesktopMainWindow::showInformationPopup(std::function<bool()> action, const std::string &notification)
    {
        DialogMetadata meta;
        meta.icon       = "info_big_circle_W_G";
        meta.text       = notification;
        meta.action     = std::move(action);
        auto switchData = std::make_unique<DialogMetadataMessage>(std::move(meta));
        application->switchWindow(window::name::dialog_confirm, std::move(switchData));
        return true;
    }

} /* namespace gui */

M module-apps/application-desktop/windows/DesktopMainWindow.hpp => module-apps/application-desktop/windows/DesktopMainWindow.hpp +1 -0
@@ 50,6 50,7 @@ namespace gui
        bool updateTime() override;

      private:
        bool showInformationPopup(std::function<bool()> action, const std::string &notification);
        void invalidate() noexcept;
        std::string osUpdateVer;
        std::string osCurrentVer;

M module-apps/application-desktop/windows/UpdateProgress.cpp => module-apps/application-desktop/windows/UpdateProgress.cpp +2 -1
@@ 27,6 27,7 @@ namespace gui
        : AppWindow(app, app::window::name::desktop_update_progress)
    {
        buildInterface();
        preventsAutoLock = true;
    }

    void UpdateProgressWindow::onBeforeShow(ShowMode mode, SwitchData *data)


@@ 37,7 38,7 @@ namespace gui
        else {
            auto *item = dynamic_cast<gui::UpdateSwitchData *>(data);
            if (item != nullptr) {
                auto msg   = item->getUpdateOsMessage();
                const auto &msg = item->getUpdateOsMessage();
                updateFile = msg.updateStats.updateFile;
                auto updateVersion =
                    msg.updateStats.versionInformation[boot::json::os_version][boot::json::version_string]

M module-apps/application-messages/ApplicationMessages.cpp => module-apps/application-messages/ApplicationMessages.cpp +7 -8
@@ 32,10 32,10 @@
#include <module-db/queries/phonebook/QueryContactGetByID.hpp>

#include <service-cellular/CellularMessage.hpp>
#include <messages/OptionsWindow.hpp>

#include <cassert>
#include <time/time_conversion.hpp>
#include <messages/OptionsWindow.hpp>
#include <ctime>

namespace app
{


@@ 329,7 329,7 @@ namespace app
        assert(!body.empty()); // precondition check.

        record.body = body;
        record.date = utils::time::getCurrentTimestamp().getTime();
        record.date = std::time(nullptr);

        using db::query::SMSUpdate;
        const auto [succeed, _] =


@@ 346,7 346,7 @@ namespace app
        record.number = number;
        record.body   = body;
        record.type   = SMSType::DRAFT;
        record.date   = utils::time::getCurrentTimestamp().getTime();
        record.date   = std::time(nullptr);

        using db::query::SMSAdd;
        const auto [success, _] =


@@ 372,7 372,7 @@ namespace app
        record.number = number;
        record.body   = body;
        record.type   = SMSType::QUEUED;
        record.date   = utils::time::getCurrentTimestamp().getTime();
        record.date   = std::time(nullptr);

        using db::query::SMSAdd;
        const auto [succeed, _] =


@@ 384,9 384,8 @@ namespace app
    {
        auto resendRecord = record;
        resendRecord.type = SMSType::QUEUED;
        resendRecord.date =
            utils::time::getCurrentTimestamp().getTime(); // update date sent - it will display an old, failed sms at
                                                          // the the bottom, but this is correct
        resendRecord.date = std::time(nullptr); // update date sent - it will display an old, failed sms at
                                                // the the bottom, but this is correct

        using db::query::SMSUpdate;
        const auto [succeed, _] =

M module-apps/application-notes/windows/NoteEditWindow.cpp => module-apps/application-notes/windows/NoteEditWindow.cpp +3 -2
@@ 14,11 14,12 @@
#include <module-apps/messages/OptionsWindow.hpp>

#include <i18n/i18n.hpp>
#include <module-utils/time/time_conversion.hpp>

#include <module-gui/gui/widgets/BottomBar.hpp>
#include <module-gui/gui/widgets/TopBar.hpp>

#include <ctime>

namespace app::notes
{
    namespace


@@ 138,7 139,7 @@ namespace app::notes

    void NoteEditWindow::saveNote()
    {
        notesRecord->date    = utils::time::getCurrentTimestamp().getTime();
        notesRecord->date    = std::time(nullptr);
        notesRecord->snippet = edit->getText();
        presenter->save(*notesRecord);
    }

M module-apps/application-onboarding/ApplicationOnBoarding.cpp => module-apps/application-onboarding/ApplicationOnBoarding.cpp +32 -11
@@ 5,7 5,7 @@

#include "ApplicationOnBoarding.hpp"

#include "windows/OnBoardingMainWindow.hpp"
#include "data/OnBoardingMessages.hpp"
#include "windows/StartConfigurationWindow.hpp"
#include "windows/OnBoardingLanguagesWindow.hpp"
#include "windows/EULALicenseWindow.hpp"


@@ 23,6 23,8 @@
#include <module-services/service-db/agents/settings/SystemSettings.hpp>
#include <module-apps/application-settings-new/windows/ChangeTimeZone.hpp>
#include <module-apps/locks/data/PhoneLockMessages.hpp>
#include <module-apps/locks/data/SimLockMessages.hpp>
#include <service-appmgr/service-appmgr/model/ApplicationManager.hpp>

namespace app
{


@@ 42,6 44,7 @@ namespace app

        bus.channels.push_back(sys::BusChannel::ServiceDBNotifications);
        bus.channels.push_back(sys::BusChannel::PhoneLockChanges);
        bus.channels.push_back(sys::BusChannel::ServiceCellularNotifications);
    }

    // Invoked upon receiving data message


@@ 85,12 88,27 @@ namespace app
            return sys::msgHandled();
        });

        connect(typeid(cellular::msg::notification::SimReady), [&](sys::Message *msg) {
            if (getCurrentWindow()->getName() == gui::window::name::onBoarding_sim_select) {
                phoneLockSubject.setPhoneLock();
                return sys::msgHandled();
            }
            return sys::msgNotHandled();
        });

        return ret;
    }

    void ApplicationOnBoarding::acceptEULA()
    {
        settings->setValue(settings::SystemProperties::eulaAccepted, "1", settings::SettingsScope::Global);
        settings->setValue(
            settings::SystemProperties::eulaAccepted, utils::to_string(true), settings::SettingsScope::Global);
    }

    void ApplicationOnBoarding::finalizeOnBoarding()
    {
        bus.sendUnicast(std::make_shared<onBoarding::FinalizeOnBoarding>(),
                        app::manager::ApplicationManager::ServiceName);
    }

    sys::ReturnCodes ApplicationOnBoarding::DeinitHandler()


@@ 106,9 124,6 @@ namespace app
    void ApplicationOnBoarding::createUserInterface()
    {
        windowsFactory.attach(gui::name::window::main_window, [](Application *app, const std::string &name) {
            return std::make_unique<app::onBoarding::OnBoardingMainWindow>(app);
        });
        windowsFactory.attach(gui::window::name::onBoarding_languages, [](Application *app, const std::string &name) {
            return std::make_unique<app::onBoarding::OnBoardingLanguagesWindow>(app);
        });
        windowsFactory.attach(gui::window::name::onBoarding_start_configuration,


@@ 122,7 137,8 @@ namespace app
            return std::make_unique<app::onBoarding::EULALicenseWindow>(app, std::move(presenter));
        });
        windowsFactory.attach(gui::window::name::onBoarding_sim_select, [](Application *app, const std::string &name) {
            return std::make_unique<gui::OnBoardingSimSelectWindow>(app, gui::window::name::onBoarding_sim_select);
            return std::make_unique<app::onBoarding::OnBoardingSimSelectWindow>(
                app, gui::window::name::onBoarding_sim_select);
        });
        windowsFactory.attach(gui::window::name::onBoarding_no_sim_selected,
                              [](Application *app, const std::string &name) {


@@ 136,8 152,10 @@ namespace app
                              [](Application *app, const std::string &name) {
                                  return std::make_unique<app::onBoarding::NoConfigurationDialogWindow>(app);
                              });
        windowsFactory.attach(gui::window::name::onBoarding_update, [](Application *app, const std::string &name) {
            return std::make_unique<app::onBoarding::UpdateDialogWindow>(app);
        windowsFactory.attach(gui::window::name::onBoarding_update, [&](Application *app, const std::string &name) {
            auto presenter =
                std::make_unique<app::onBoarding::OnBoardingFinalizeWindowPresenter>([&]() { finalizeOnBoarding(); });
            return std::make_unique<app::onBoarding::UpdateDialogWindow>(app, std::move(presenter));
        });
        windowsFactory.attach(gui::window::name::onBoarding_skip, [](Application *app, const std::string &name) {
            return std::make_unique<app::onBoarding::SkipDialogWindow>(app);


@@ 148,7 166,7 @@ namespace app
                              });
        windowsFactory.attach(gui::window::name::onBoarding_change_date_and_time,
                              [](Application *app, const std::string &name) {
                                  return std::make_unique<gui::OnBoardingChangeDateAndTimeWindow>(app);
                                  return std::make_unique<app::onBoarding::OnBoardingChangeDateAndTimeWindow>(app);
                              });
        windowsFactory.attach(gui::window::name::change_time_zone, [](Application *app, const std::string &name) {
            return std::make_unique<gui::ChangeTimeZone>(app);


@@ 157,8 175,11 @@ namespace app
            return std::make_unique<gui::DialogConfirm>(app, gui::window::name::dialog_confirm);
        });

        attachPopups(
            {gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes, gui::popup::ID::PhoneLock});
        attachPopups({gui::popup::ID::Volume,
                      gui::popup::ID::Tethering,
                      gui::popup::ID::PhoneModes,
                      gui::popup::ID::PhoneLock,
                      gui::popup::ID::SimLock});
    }

    void ApplicationOnBoarding::destroyUserInterface()

M module-apps/application-onboarding/ApplicationOnBoarding.hpp => module-apps/application-onboarding/ApplicationOnBoarding.hpp +1 -0
@@ 38,6 38,7 @@ namespace app
        sys::ReturnCodes SwitchPowerModeHandler(const sys::ServicePowerMode mode) override;

        void acceptEULA();
        void finalizeOnBoarding();

        void createUserInterface() override;
        void destroyUserInterface() override;

M module-apps/application-onboarding/CMakeLists.txt => module-apps/application-onboarding/CMakeLists.txt +3 -2
@@ 12,11 12,11 @@ target_sources(${PROJECT_NAME}

	PRIVATE
		"${CMAKE_CURRENT_LIST_DIR}/ApplicationOnBoarding.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/OnBoardingMainWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/StartConfigurationWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/OnBoardingLanguagesWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/EULALicenseWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/presenter/EULALicenseWindowPresenter.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/presenter/OnBoardingFinalizeWindowPresenter.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/model/EULARepository.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/ConfigurationSuccessfulDialogWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/NoConfigurationDialogWindow.cpp"


@@ 28,12 28,13 @@ target_sources(${PROJECT_NAME}
		"${CMAKE_CURRENT_LIST_DIR}/windows/NoSimSelectedDialogWindow.cpp"
	PUBLIC
		"${CMAKE_CURRENT_LIST_DIR}/ApplicationOnBoarding.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/OnBoardingMainWindow.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/StartConfigurationWindow.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/OnBoardingLanguagesWindow.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/data/OnBoardingSwitchData.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/data/OnBoardingMessages.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/EULALicenseWindow.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/presenter/EULALicenseWindowPresenter.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/presenter/OnBoardingFinalizeWindowPresenter.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/model/EULARepository.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/ConfigurationSuccessfulDialogWindow.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/NoConfigurationDialogWindow.hpp"

A module-apps/application-onboarding/data/OnBoardingMessages.hpp => module-apps/application-onboarding/data/OnBoardingMessages.hpp +12 -0
@@ 0,0 1,12 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <Service/Message.hpp>

namespace app::onBoarding
{
    class FinalizeOnBoarding : public sys::DataMessage
    {};
} // namespace app::onBoarding

A module-apps/application-onboarding/presenter/OnBoardingFinalizeWindowPresenter.cpp => module-apps/application-onboarding/presenter/OnBoardingFinalizeWindowPresenter.cpp +17 -0
@@ 0,0 1,17 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "OnBoardingFinalizeWindowPresenter.hpp"

namespace app::onBoarding
{
    OnBoardingFinalizeWindowPresenter::OnBoardingFinalizeWindowPresenter(Function finalizeOnBoarding)
        : finalizeOnBoardingApp(std::move(finalizeOnBoarding))
    {}

    void OnBoardingFinalizeWindowPresenter::finalizeOnBoarding()
    {
        finalizeOnBoardingApp();
    }

} // namespace app::onBoarding

A module-apps/application-onboarding/presenter/OnBoardingFinalizeWindowPresenter.hpp => module-apps/application-onboarding/presenter/OnBoardingFinalizeWindowPresenter.hpp +41 -0
@@ 0,0 1,41 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "functional"
#include "string"
#include "BasePresenter.hpp"

using Function = std::function<void()>;

namespace app::onBoarding
{
    class OnBoardingFinalizeContract
    {
      public:
        class View
        {
          public:
            virtual ~View() noexcept = default;
        };
        class Presenter : public BasePresenter<OnBoardingFinalizeContract::View>
        {
          public:
            ~Presenter() noexcept override = default;

            virtual void finalizeOnBoarding() = 0;
        };
    };

    class OnBoardingFinalizeWindowPresenter : public OnBoardingFinalizeContract::Presenter
    {
      public:
        explicit OnBoardingFinalizeWindowPresenter(Function finalizeOnBoarding);

        void finalizeOnBoarding() override;

      private:
        std::function<void()> finalizeOnBoardingApp;
    };
} // namespace app::onBoarding

M module-apps/application-onboarding/windows/ConfigurationSuccessfulDialogWindow.cpp => module-apps/application-onboarding/windows/ConfigurationSuccessfulDialogWindow.cpp +14 -3
@@ 3,12 3,14 @@

#include "ConfigurationSuccessfulDialogWindow.hpp"

#include <i18n/i18n.hpp>
#include <Style.hpp>

#include <module-apps/application-onboarding/ApplicationOnBoarding.hpp>
#include <module-apps/application-onboarding/data/OnBoardingSwitchData.hpp>
#include <module-apps/messages/DialogMetadataMessage.hpp>
#include <widgets/IceBox.hpp>
#include <service-appmgr/Controller.hpp>

#include <i18n/i18n.hpp>
#include <Style.hpp>

namespace app::onBoarding
{


@@ 17,6 19,8 @@ namespace app::onBoarding
    {
        bottomBar->setText(gui::BottomBar::Side::CENTER, utils::translate(style::strings::common::start));
        bottomBar->setActive(gui::BottomBar::Side::RIGHT, false);

        new gui::IceBox(this);
    }

    bool ConfigurationSuccessfulDialogWindow::onInput(const gui::InputEvent &inputEvent)


@@ 37,6 41,13 @@ namespace app::onBoarding
            if (inputEvent.is(gui::KeyCode::KEY_RF)) {
                return true;
            }

            if (inputEvent.is(gui::KeyCode::KEY_LEFT)) {
                app::manager::Controller::sendAction(application,
                                                     app::manager::actions::EmergencyDial,
                                                     std::make_unique<gui::SwitchData>(),
                                                     app::manager::OnSwitchBehaviour::RunInBackground);
            }
        }
        return AppWindow::onInput(inputEvent);
    }

M module-apps/application-onboarding/windows/NoConfigurationDialogWindow.cpp => module-apps/application-onboarding/windows/NoConfigurationDialogWindow.cpp +15 -3
@@ 3,12 3,14 @@

#include "NoConfigurationDialogWindow.hpp"

#include <i18n/i18n.hpp>
#include <Style.hpp>

#include <module-apps/application-onboarding/ApplicationOnBoarding.hpp>
#include <module-apps/application-onboarding/data/OnBoardingSwitchData.hpp>
#include <module-apps/messages/DialogMetadataMessage.hpp>
#include <widgets/IceBox.hpp>
#include <service-appmgr/Controller.hpp>

#include <i18n/i18n.hpp>
#include <Style.hpp>

namespace app::onBoarding
{


@@ 17,6 19,8 @@ namespace app::onBoarding
    {
        bottomBar->setText(gui::BottomBar::Side::CENTER, utils::translate(style::strings::common::start));
        bottomBar->setActive(gui::BottomBar::Side::RIGHT, false);

        new gui::IceBox(this);
    }

    bool NoConfigurationDialogWindow::onInput(const gui::InputEvent &inputEvent)


@@ 37,6 41,14 @@ namespace app::onBoarding
            if (inputEvent.is(gui::KeyCode::KEY_RF)) {
                return true;
            }

            if (inputEvent.is(gui::KeyCode::KEY_LEFT)) {
                app::manager::Controller::sendAction(application,
                                                     app::manager::actions::EmergencyDial,
                                                     std::make_unique<gui::SwitchData>(),
                                                     app::manager::OnSwitchBehaviour::RunInBackground);
                return true;
            }
        }
        return AppWindow::onInput(inputEvent);
    }

M module-apps/application-onboarding/windows/OnBoardingChangeDateAndTimeWindow.cpp => module-apps/application-onboarding/windows/OnBoardingChangeDateAndTimeWindow.cpp +2 -2
@@ 5,7 5,7 @@
#include <module-apps/application-onboarding/ApplicationOnBoarding.hpp>
#include <InputEvent.hpp>

namespace gui
namespace app::onBoarding
{
    OnBoardingChangeDateAndTimeWindow::OnBoardingChangeDateAndTimeWindow(app::Application *app)
        : ChangeDateAndTimeWindow(app, gui::window::name::onBoarding_change_date_and_time)


@@ 22,4 22,4 @@ namespace gui
        return ChangeDateAndTimeWindow::onInput(inputEvent);
    }

} /* namespace gui */
} // namespace app::onBoarding

M module-apps/application-onboarding/windows/OnBoardingChangeDateAndTimeWindow.hpp => module-apps/application-onboarding/windows/OnBoardingChangeDateAndTimeWindow.hpp +4 -4
@@ 6,13 6,13 @@
#include <application-settings-new/windows/ChangeDateAndTimeWindow.hpp>
#include <application-settings-new/models/DateAndTimeModel.hpp>

namespace gui
namespace app::onBoarding
{
    class OnBoardingChangeDateAndTimeWindow : public ChangeDateAndTimeWindow
    class OnBoardingChangeDateAndTimeWindow : public gui::ChangeDateAndTimeWindow
    {
      public:
        explicit OnBoardingChangeDateAndTimeWindow(app::Application *app);

        bool onInput(const InputEvent &inputEvent) override;
        bool onInput(const gui::InputEvent &inputEvent) override;
    };
} /* namespace gui */
} // namespace app::onBoarding

M module-apps/application-onboarding/windows/OnBoardingDateAndTimeWindow.cpp => module-apps/application-onboarding/windows/OnBoardingDateAndTimeWindow.cpp +12 -2
@@ 8,11 8,13 @@
#include <module-gui/gui/input/InputEvent.hpp>
#include <module-apps/application-onboarding/data/OnBoardingSwitchData.hpp>
#include <module-apps/messages/DialogMetadataMessage.hpp>
#include <module-apps/application-settings-new/data/ChangePasscodeData.hpp>
#include <widgets/IceBox.hpp>
#include <service-appmgr/Controller.hpp>

namespace app::onBoarding
{
    OnBoardingDateAndTimeWindow::OnBoardingDateAndTimeWindow(app::Application *app) : DateAndTimeMainWindow(app)
    OnBoardingDateAndTimeWindow::OnBoardingDateAndTimeWindow(app::Application *app)
        : DateAndTimeMainWindow(app, gui::window::name::onBoarding_date_and_time)
    {
        changeDateAndTimeWindow = gui::window::name::onBoarding_change_date_and_time;
    }


@@ 25,6 27,7 @@ namespace app::onBoarding
    void OnBoardingDateAndTimeWindow::onBeforeShow(gui::ShowMode mode, gui::SwitchData *data)
    {
        DateAndTimeMainWindow::onBeforeShow(mode, data);
        new gui::IceBox(this);

        bottomBar->setText(gui::BottomBar::Side::CENTER, utils::translate(style::strings::common::save));
        bottomBar->setText(gui::BottomBar::Side::LEFT, utils::translate(style::strings::common::Switch));


@@ 48,6 51,13 @@ namespace app::onBoarding
                                      std::move(metaData));
            return true;
        }
        else if (inputEvent.isShortRelease(gui::KeyCode::KEY_LEFT)) {
            app::manager::Controller::sendAction(application,
                                                 app::manager::actions::EmergencyDial,
                                                 std::make_unique<gui::SwitchData>(),
                                                 app::manager::OnSwitchBehaviour::RunInBackground);
            return true;
        }
        else {
            return DateAndTimeMainWindow::onInput(inputEvent);
        }

M module-apps/application-onboarding/windows/OnBoardingLanguagesWindow.cpp => module-apps/application-onboarding/windows/OnBoardingLanguagesWindow.cpp +13 -2
@@ 1,9 1,12 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "application-onboarding/ApplicationOnBoarding.hpp"
#include "OnBoardingLanguagesWindow.hpp"
#include <application-onboarding/ApplicationOnBoarding.hpp>

#include <widgets/IceBox.hpp>
#include <module-gui/gui/input/InputEvent.hpp>
#include <service-appmgr/Controller.hpp>

namespace app::onBoarding
{


@@ 14,6 17,7 @@ namespace app::onBoarding
    void OnBoardingLanguagesWindow::onBeforeShow(gui::ShowMode mode, gui::SwitchData *data)
    {
        bottomBar->setActive(gui::BottomBar::Side::RIGHT, false);
        new gui::IceBox(this);

        LanguagesWindow::onBeforeShow(mode, data);
    }


@@ 23,8 27,15 @@ namespace app::onBoarding
        if (inputEvent.isShortRelease(gui::KeyCode::KEY_RF)) {
            return true;
        }
        else if (inputEvent.isShortRelease(gui::KeyCode::KEY_LEFT)) {
            app::manager::Controller::sendAction(application,
                                                 app::manager::actions::EmergencyDial,
                                                 std::make_unique<gui::SwitchData>(),
                                                 app::manager::OnSwitchBehaviour::RunInBackground);
            return true;
        }
        else {
            return LanguagesWindow::onInput(inputEvent);
        }
    }
} /* namespace gui */
} // namespace app::onBoarding

D module-apps/application-onboarding/windows/OnBoardingMainWindow.cpp => module-apps/application-onboarding/windows/OnBoardingMainWindow.cpp +0 -52
@@ 1,52 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <i18n/i18n.hpp>
#include <Style.hpp>

#include <InputEvent.hpp>

#include <module-apps/application-onboarding/ApplicationOnBoarding.hpp>

#include "module-apps/application-onboarding/data/OnBoardingSwitchData.hpp"
#include "OnBoardingMainWindow.hpp"

namespace app::onBoarding
{
    OnBoardingMainWindow::OnBoardingMainWindow(app::Application *app) : AppWindow(app, gui::name::window::main_window)
    {
        buildInterface();
    }

    void OnBoardingMainWindow::buildInterface()
    {
        AppWindow::buildInterface();

        bottomBar->setActive(gui::BottomBar::Side::CENTER, true);
        bottomBar->setText(gui::BottomBar::Side::CENTER, utils::translate(::style::strings::common::start));

        new gui::Image(this, 0, 0, 0, 0, "logo");
    }

    gui::top_bar::Configuration OnBoardingMainWindow::configureTopBar(gui::top_bar::Configuration appConfiguration)
    {
        appConfiguration.setIndicator(gui::top_bar::Indicator::Time, false);
        appConfiguration.setIndicator(gui::top_bar::Indicator::Battery, false);
        appConfiguration.setIndicator(gui::top_bar::Indicator::SimCard, false);
        appConfiguration.setIndicator(gui::top_bar::Indicator::Signal, false);
        return appConfiguration;
    }

    bool OnBoardingMainWindow::onInput(const gui::InputEvent &inputEvent)
    {
        if (inputEvent.isShortRelease(gui::KeyCode::KEY_ENTER)) {
            application->switchWindow(gui::window::name::onBoarding_languages,
                                      gui::ShowMode::GUI_SHOW_INIT,
                                      std::make_unique<OnBoardingSwitchData>());

            return true;
        }
        return AppWindow::onInput(inputEvent);
    }

} // namespace gui

M module-apps/application-onboarding/windows/OnBoardingSimSelectWindow.cpp => module-apps/application-onboarding/windows/OnBoardingSimSelectWindow.cpp +15 -9
@@ 7,9 7,11 @@
#include <application-onboarding/style/OnBoardingStyle.hpp>
#include <OptionSetting.hpp>

#include <widgets/IceBox.hpp>
#include <service-appmgr/Controller.hpp>
#include <module-apps/messages/DialogMetadataMessage.hpp>

namespace gui
namespace app::onBoarding
{
    OnBoardingSimSelectWindow::OnBoardingSimSelectWindow(app::Application *app, std::string name)
        : BaseSettingsWindow(app, std::move(name))


@@ 25,6 27,8 @@ namespace gui
        bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::translate(::style::strings::common::back));
        bottomBar->setText(gui::BottomBar::Side::LEFT, utils::translate(::style::strings::common::skip));

        new gui::IceBox(this);

        descriptionText = new gui::Text(this,
                                        style::window::default_left_margin,
                                        style::onboarding::sim_select::description_y,


@@ 33,17 37,12 @@ namespace gui
        descriptionText->setFont(style::window::font::medium);
        descriptionText->setAlignment(
            gui::Alignment{gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top});
        descriptionText->setEdges(RectangleEdge::Top);
        descriptionText->setEdges(gui::RectangleEdge::Top);
        descriptionText->setPenWidth(style::window::default_border_rect_no_focus);
        descriptionText->setPadding(Padding(0, style::onboarding::sim_select::description_top_padding, 0, 0));
        descriptionText->setPadding(gui::Padding(0, style::onboarding::sim_select::description_top_padding, 0, 0));
        descriptionText->setRichText(utils::translate("app_onboarding_select_sim_description"));
    }

    void OnBoardingSimSelectWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        refreshOptionsList();
    }

    auto OnBoardingSimSelectWindow::buildOptionsList() -> std::list<gui::Option>
    {
        std::list<gui::Option> options;


@@ 97,6 96,13 @@ namespace gui
                gui::window::name::onBoarding_skip, gui::ShowMode::GUI_SHOW_INIT, std::move(metaData));
            return true;
        }
        else if (inputEvent.isShortRelease(gui::KeyCode::KEY_LEFT)) {
            app::manager::Controller::sendAction(application,
                                                 app::manager::actions::EmergencyDial,
                                                 std::make_unique<gui::SwitchData>(),
                                                 app::manager::OnSwitchBehaviour::RunInBackground);
            return true;
        }
        return AppWindow::onInput(inputEvent);
    }
} /* namespace gui */
} // namespace app::onBoarding

M module-apps/application-onboarding/windows/OnBoardingSimSelectWindow.hpp => module-apps/application-onboarding/windows/OnBoardingSimSelectWindow.hpp +4 -5
@@ 6,9 6,9 @@
#include <application-settings-new/windows/BaseSettingsWindow.hpp>
#include <Text.hpp>

namespace gui
namespace app::onBoarding
{
    class OnBoardingSimSelectWindow : public BaseSettingsWindow
    class OnBoardingSimSelectWindow : public gui::BaseSettingsWindow
    {
      public:
        explicit OnBoardingSimSelectWindow(app::Application *app, std::string name);


@@ 17,8 17,7 @@ namespace gui
      private:
        gui::Text *descriptionText = nullptr;

        auto buildOptionsList() -> std::list<Option> override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        auto buildOptionsList() -> std::list<gui::Option> override;
        bool onInput(const gui::InputEvent &inputEvent) override;
    };
} /* namespace gui */
} // namespace app::onBoarding

M module-apps/application-onboarding/windows/StartConfigurationWindow.cpp => module-apps/application-onboarding/windows/StartConfigurationWindow.cpp +9 -0
@@ 8,6 8,8 @@

#include <module-apps/windows/DialogMetadata.hpp>
#include <module-apps/messages/DialogMetadataMessage.hpp>
#include <widgets/IceBox.hpp>
#include <service-appmgr/Controller.hpp>

#include <i18n/i18n.hpp>
#include <Style.hpp>


@@ 29,6 31,7 @@ namespace app::onBoarding
        bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::translate(::style::strings::common::back));
        bottomBar->setText(gui::BottomBar::Side::LEFT, utils::translate(::style::strings::common::skip));

        new gui::IceBox(this);
        new gui::Icon(this,
                      0,
                      0,


@@ 51,6 54,12 @@ namespace app::onBoarding
                                          gui::ShowMode::GUI_SHOW_INIT,
                                          std::make_unique<OnBoardingSwitchData>());
            }
            if (inputEvent.is(gui::KeyCode::KEY_LEFT)) {
                app::manager::Controller::sendAction(application,
                                                     app::manager::actions::EmergencyDial,
                                                     std::make_unique<gui::SwitchData>(),
                                                     app::manager::OnSwitchBehaviour::RunInBackground);
            }
            if (inputEvent.is(gui::KeyCode::KEY_LF)) {

                auto metaData = std::make_unique<gui::DialogMetadataMessage>(gui::DialogMetadata{

M module-apps/application-onboarding/windows/UpdateDialogWindow.cpp => module-apps/application-onboarding/windows/UpdateDialogWindow.cpp +7 -8
@@ 6,26 6,25 @@
#include <i18n/i18n.hpp>
#include <Style.hpp>

#include <module-apps/application-onboarding/ApplicationOnBoarding.hpp>
#include <module-apps/application-onboarding/data/OnBoardingSwitchData.hpp>
#include <module-apps/messages/DialogMetadataMessage.hpp>
#include <application-onboarding/ApplicationOnBoarding.hpp>
#include <service-appmgr/service-appmgr/Controller.hpp>

namespace app::onBoarding
{
    UpdateDialogWindow::UpdateDialogWindow(app::Application *app)
        : gui::Dialog(app, gui::window::name::onBoarding_update)
    UpdateDialogWindow::UpdateDialogWindow(app::Application *app,
                                           std::unique_ptr<OnBoardingFinalizeContract::Presenter> &&windowPresenter)
        : gui::Dialog(app, gui::window::name::onBoarding_update), presenter(std::move(windowPresenter))
    {
        bottomBar->setText(gui::BottomBar::Side::CENTER, utils::translate(style::strings::common::ok));
        bottomBar->setActive(gui::BottomBar::Side::RIGHT, false);
        presenter->attach(this);
    }

    bool UpdateDialogWindow::onInput(const gui::InputEvent &inputEvent)
    {
        if (inputEvent.isShortRelease()) {
            if (inputEvent.is(gui::KeyCode::KEY_ENTER)) {
                application->switchWindow(gui::window::name::onBoarding_start_configuration,
                                          gui::ShowMode::GUI_SHOW_INIT,
                                          std::make_unique<OnBoardingSwitchData>());
                presenter->finalizeOnBoarding();
                return true;
            }
            if (inputEvent.is(gui::KeyCode::KEY_RF)) {

M module-apps/application-onboarding/windows/UpdateDialogWindow.hpp => module-apps/application-onboarding/windows/UpdateDialogWindow.hpp +8 -7
@@ 5,18 5,19 @@

#include "Dialog.hpp"

#include <module-apps/application-onboarding/presenter/EULALicenseWindowPresenter.hpp>

#include <module-gui/gui/widgets/Label.hpp>
#include <module-gui/gui/widgets/Text.hpp>
#include <module-gui/gui/input/InputEvent.hpp>
#include <application-onboarding/presenter/EULALicenseWindowPresenter.hpp>
#include <application-onboarding/presenter/OnBoardingFinalizeWindowPresenter.hpp>

namespace app::onBoarding
{
    class UpdateDialogWindow : public gui::Dialog
    class UpdateDialogWindow : public gui::Dialog, public OnBoardingFinalizeContract::View
    {
      private:
        std::unique_ptr<OnBoardingFinalizeContract::Presenter> presenter;

      public:
        explicit UpdateDialogWindow(app::Application *app);
        UpdateDialogWindow(app::Application *app,
                           std::unique_ptr<OnBoardingFinalizeContract::Presenter> &&windowPresenter);

        bool onInput(const gui::InputEvent &inputEvent) override;
    };

M module-apps/application-settings-new/ApplicationSettings.cpp => module-apps/application-settings-new/ApplicationSettings.cpp +12 -24
@@ 146,15 146,9 @@ namespace app
        if (ret != sys::ReturnCodes::Success) {
            return ret;
        }
        connect(typeid(cellular::msg::notification::SimReady), [&](sys::Message *msg) {
            auto simReadyMsg = static_cast<cellular::msg::notification::SimReady *>(msg);
            selectedSim      = Store::GSM::get()->selected;
            if (simReadyMsg->ready) {
                CellularServiceAPI::RequestForOwnNumber(this);
            }
            else {
                selectedSimNumber = {};
            }
        connect(typeid(cellular::msg::notification::SimReady), [&](sys::Message *) {
            selectedSim = Store::GSM::get()->selected;
            CellularServiceAPI::RequestForOwnNumber(this);
            auto currentWindow = getCurrentWindow();
            if (gui::window::name::network == currentWindow->getName()) {
                updateWindow(gui::window::name::network, nullptr);


@@ 446,7 440,7 @@ namespace app
            return std::make_unique<gui::LanguagesWindow>(app);
        });
        windowsFactory.attach(gui::window::name::date_and_time, [](Application *app, const std::string &name) {
            return std::make_unique<gui::DateAndTimeMainWindow>(app);
            return std::make_unique<gui::DateAndTimeMainWindow>(app, gui::window::name::date_and_time);
        });
        windowsFactory.attach(gui::window::name::about_your_pure, [](Application *app, const std::string &name) {
            return std::make_unique<gui::AboutYourPureWindow>(app);


@@ 530,6 524,11 @@ namespace app
        getSimLockSubject().setSim(arg);
    }

    void ApplicationSettingsNew::updateSim()
    {
        selectedSim = Store::GSM::get()->selected;
    }

    Store::GSM::SIM ApplicationSettingsNew::getSim()
    {
        return selectedSim;


@@ 591,7 590,7 @@ namespace app
                return {};
            }

            return {msgState->lightOn, msgState->mode, msgState->parameters};
            return {msgState->isLightOn(), msgState->getMode(), msgState->getParams()};
        }

        return {};


@@ 599,10 598,8 @@ namespace app

    void ApplicationSettingsNew::setBrightness(bsp::eink_frontlight::BrightnessPercentage value)
    {
        screen_light_control::Parameters parameters{value};
        bus.sendUnicast(std::make_shared<sevm::ScreenLightSetParameters>(
                            screen_light_control::ParameterizedAction::setManualModeBrightness, parameters),
                        service::name::evt_manager);
        screen_light_control::ManualModeParameters parameters{value};
        bus.sendUnicast(std::make_shared<sevm::ScreenLightSetManualModeParams>(parameters), service::name::evt_manager);
    }

    void ApplicationSettingsNew::setMode(bool isAutoLightSwitchOn)


@@ 620,15 617,6 @@ namespace app
                                                                              : screen_light_control::Action::turnOff),
                        service::name::evt_manager);
    }
    void ApplicationSettingsNew::setBrightnessFunction()
    {
        screen_light_control::Parameters parameters;
        parameters.functionPoints = screen_light_control::functions::BrightnessFunction(
            {{0.0f, 70.0f}, {250.0f, 70.0f}, {450.0f, 40.0f}, {500.0f, 0.0f}});
        bus.sendUnicast(std::make_shared<sevm::ScreenLightSetParameters>(
                            screen_light_control::ParameterizedAction::setAutomaticModeParameters, parameters),
                        service::name::evt_manager);
    }

    auto ApplicationSettingsNew::getKeypadBacklightState() -> bsp::keypad_backlight::State
    {

M module-apps/application-settings-new/ApplicationSettings.hpp => module-apps/application-settings-new/ApplicationSettings.hpp +5 -5
@@ 90,6 90,7 @@ namespace app
          public:
            virtual ~SimParams()                     = default;
            virtual void setSim(Store::GSM::SIM sim) = 0;
            virtual void updateSim()                 = 0;
            virtual Store::GSM::SIM getSim()         = 0;
            virtual std::string getNumber()          = 0;
        };


@@ 109,15 110,14 @@ namespace app
            {
                bool lightOn;
                screen_light_control::ScreenLightMode mode;
                screen_light_control::Parameters parameters;
                screen_light_control::ManualModeParameters parameters;
            };

            virtual ~ScreenLightSettings()                      = default;
            virtual auto getCurrentValues() -> Values           = 0;
            virtual void setBrightness(float brigtnessValue)    = 0;
            virtual void setBrightness(float brightnessValue)   = 0;
            virtual void setMode(bool isAutoLightSwitchOn)      = 0;
            virtual void setStatus(bool isDisplayLightSwitchOn) = 0;
            virtual void setBrightnessFunction()                = 0;
        };

        class KeypdBacklightSettings


@@ 202,6 202,7 @@ namespace app
        void createUserInterface() override;
        void destroyUserInterface() override;
        void setSim(Store::GSM::SIM sim) override;
        void updateSim() override;
        Store::GSM::SIM getSim() override;
        std::string getNumber() override;



@@ 214,10 215,9 @@ namespace app
        void setOsUpdateVersion(const std::string &value);

        ScreenLightSettings::Values getCurrentValues() override;
        void setBrightness(float brigtnessValue) override;
        void setBrightness(float brightnessValue) override;
        void setMode(bool isAutoLightSwitchOn) override;
        void setStatus(bool isDisplayLightSwitchOn) override;
        void setBrightnessFunction() override;

        auto getKeypadBacklightState() -> bsp::keypad_backlight::State override;
        void setKeypadBacklightState(bsp::keypad_backlight::State keypadLightState) override;

M module-apps/application-settings-new/windows/ChangeDateAndTimeWindow.cpp => module-apps/application-settings-new/windows/ChangeDateAndTimeWindow.cpp +4 -4
@@ 5,8 5,8 @@
#include <application-settings-new/ApplicationSettings.hpp>
#include <gui/input/InputEvent.hpp>
#include <ListView.hpp>
#include <service-evtmgr/service-evtmgr/Constants.hpp>
#include <service-evtmgr/service-evtmgr/EVMessages.hpp>
#include <service-time/Constants.hpp>
#include <service-time/service-time/TimeMessage.hpp>
#include <widgets/DateAndTimeStyle.hpp>

namespace gui


@@ 64,7 64,7 @@ namespace gui

    void ChangeDateAndTimeWindow::sendRtcUpdateTimeMessage()
    {
        auto msg = std::make_shared<sevm::RtcUpdateTimeMessage>(TimePointToTimeT(fromTillDate->from));
        application->bus.sendUnicast(std::move(msg), service::name::evt_manager);
        auto msg = std::make_shared<stm::message::TimeChangeRequestMessage>(TimePointToTimeT(fromTillDate->from));
        application->bus.sendUnicast(std::move(msg), service::name::service_time);
    }
} /* namespace gui */

M module-apps/application-settings-new/windows/DateAndTimeMainWindow.cpp => module-apps/application-settings-new/windows/DateAndTimeMainWindow.cpp +2 -2
@@ 10,8 10,8 @@

namespace gui
{
    DateAndTimeMainWindow::DateAndTimeMainWindow(app::Application *app)
        : BaseSettingsWindow(app, window::name::date_and_time)
    DateAndTimeMainWindow::DateAndTimeMainWindow(app::Application *app, std::string name)
        : BaseSettingsWindow(app, std::move(name))
    {
        setTitle(utils::translate("app_settings_date_and_time"));
        automaticDateAndTimeIsOn = utils::dateAndTimeSettings.isAutomaticDateAndTimeOn();

M module-apps/application-settings-new/windows/DateAndTimeMainWindow.hpp => module-apps/application-settings-new/windows/DateAndTimeMainWindow.hpp +1 -1
@@ 11,7 11,7 @@ namespace gui
    class DateAndTimeMainWindow : public BaseSettingsWindow
    {
      public:
        explicit DateAndTimeMainWindow(app::Application *app);
        DateAndTimeMainWindow(app::Application *app, std::string name);

      protected:
        auto buildOptionsList() -> std::list<Option> override;

M module-apps/application-settings-new/windows/DisplayLightWindow.cpp => module-apps/application-settings-new/windows/DisplayLightWindow.cpp +1 -3
@@ 23,8 23,6 @@ namespace gui

        setTitle(utils::translate("app_settings_display_display_light"));

        screenLightSettings->setBrightnessFunction();

        timerCallback = [this](Item &it, sys::Timer &task) { return onTimerTimeout(it, task); };
        timerTask     = app::GuiTimerFactory::createPeriodicTimer(
            application, this, "AmbientLightTimer", std::chrono::milliseconds{gui::lighting::AMBIENT_LIGHT_TIMER_MS});


@@ 122,7 120,7 @@ namespace gui
        auto spinner = std::make_unique<gui::SpinBoxOptionSettings>(
            utils::translate("app_settings_display_light_brightness"),
            std::ceil(brightnessValue / brightnessStep),
            std::ceil(screen_light_control::Parameters::MAX_BRIGHTNESS / brightnessStep),
            std::ceil(screen_light_control::ManualModeParameters::MAX_BRIGHTNESS / brightnessStep),
            setBrightness,
            setBottomBarOnSpinnerFocus);


M module-apps/application-settings-new/windows/NetworkWindow.cpp => module-apps/application-settings-new/windows/NetworkWindow.cpp +6 -0
@@ 18,6 18,12 @@ namespace gui
          operatorsSettings(operatorsSettings)
    {}

    void NetworkWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        simParams->updateSim();
        BaseSettingsWindow::onBeforeShow(mode, data);
    }

    auto NetworkWindow::buildOptionsList() -> std::list<gui::Option>
    {
        std::list<gui::Option> optList;

M module-apps/application-settings-new/windows/NetworkWindow.hpp => module-apps/application-settings-new/windows/NetworkWindow.hpp +3 -1
@@ 23,7 23,7 @@ namespace gui
    class NetworkWindow : public BaseSettingsWindow
    {
      private:
        auto buildOptionsList() -> std::list<Option>;
        auto buildOptionsList() -> std::list<Option> override;
        app::settingsInterface::SimParams *simParams;
        app::settingsInterface::OperatorsSettings *operatorsSettings;



@@ 31,5 31,7 @@ namespace gui
        NetworkWindow(app::Application *app,
                      app::settingsInterface::SimParams *simParams,
                      app::settingsInterface::OperatorsSettings *operatorsSettings);

        void onBeforeShow(ShowMode mode, SwitchData *data) override;
    };
} // namespace gui

M module-apps/application-settings/windows/DateTimeWindow.cpp => module-apps/application-settings/windows/DateTimeWindow.cpp +1 -1
@@ 58,7 58,7 @@ namespace gui
        // create date widgets
        uint32_t w = this->getWidth();

        utils::time::Timestamp time;
        utils::time::Timestamp time = utils::time::getCurrentTimestamp();

        // create date widgets
        dateBody = new gui::HBox(this,

M module-apps/locks/data/LockStyle.hpp => module-apps/locks/data/LockStyle.hpp +0 -16
@@ 22,22 22,6 @@ namespace style::window::lock_input
        constexpr inline auto top_margin   = 30;
    } // namespace pin_label

    namespace ice
    {
        constexpr inline auto x = style::window::default_left_margin;
        constexpr inline auto y = 60;
        constexpr inline auto w = 60;
        constexpr inline auto h = 50;

        constexpr inline auto margin = 3;

        namespace text
        {
            constexpr inline auto w = 40;
        }

    } // namespace ice

    namespace primary_text
    {
        constexpr inline auto h = 60;

M module-apps/locks/data/PhoneLockMessages.hpp => module-apps/locks/data/PhoneLockMessages.hpp +37 -0
@@ 57,4 57,41 @@ namespace locks
            return inputData;
        }
    };

    class ExternalUnLockPhone : public UnLockPhoneInput
    {
      public:
        explicit ExternalUnLockPhone(std::vector<unsigned int> inputData) : UnLockPhoneInput(std::move(inputData))
        {}
    };

    class ExternalPhoneLockAvailabilityChange : public sys::DataMessage
    {
      private:
        bool value = true;

      public:
        explicit ExternalPhoneLockAvailabilityChange(bool value) : value(value)
        {}

        [[nodiscard]] auto getAvailability() const noexcept
        {
            return value;
        }
    };

    class ExternalUnLockPhoneInfo : public sys::DataMessage
    {
      private:
        unsigned int attemptsLeft;

      public:
        explicit ExternalUnLockPhoneInfo(unsigned int attemptsLeft) : DataMessage{}, attemptsLeft(attemptsLeft)
        {}

        [[nodiscard]] auto getAttemptsLeft() const noexcept
        {
            return attemptsLeft;
        }
    };
} // namespace locks

M module-apps/locks/handlers/PhoneLockHandler.cpp => module-apps/locks/handlers/PhoneLockHandler.cpp +54 -6
@@ 66,6 66,10 @@ namespace locks
            app::manager::Controller::sendAction(owner,
                                                 app::manager::actions::AbortPopup,
                                                 std::make_unique<gui::PopupRequestParams>(gui::popup::ID::PhoneLock));
            app::manager::Controller::sendAction(
                owner,
                app::manager::actions::AbortPopup,
                std::make_unique<gui::PopupRequestParams>(gui::popup::ID::PhoneLockInfo));
        }
    }



@@ 121,6 125,12 @@ namespace locks
                                                 gui::popup::ID::PhoneLockChangeInfo, lock, phoneLockInputTypeAction));
    }

    void PhoneLockHandler::phoneExternalUnlockInfoAction()
    {
        owner->bus.sendMulticast(std::make_shared<locks::ExternalUnLockPhoneInfo>(lock.getAttemptsLeft()),
                                 sys::BusChannel::PhoneLockChanges);
    }

    void PhoneLockHandler::setPhoneLockInSettings()
    {
        phoneLockHash = getHash(storedInputData);


@@ 234,7 244,7 @@ namespace locks
        phoneInputRequiredAction();
    }

    sys::MessagePointer PhoneLockHandler::verifyPhoneLockInput(LockInput inputData)
    sys::MessagePointer PhoneLockHandler::handlePhoneLockInput(LockInput inputData)
    {
        if (checkPhoneLockInputTypeAction(PhoneLockInputTypeAction::Enable) ||
            checkPhoneLockInputTypeAction(PhoneLockInputTypeAction::Change) ||


@@ 246,7 256,7 @@ namespace locks
        }
    }

    sys::MessagePointer PhoneLockHandler::verifyPhoneUnlockInput(LockInput inputData)
    void PhoneLockHandler::comparePhoneLockHashCode(LockInput inputData)
    {
        const uint32_t hash = getHash(inputData);
        lock.attemptsLeft--;


@@ 254,8 264,6 @@ namespace locks
        if (phoneLockHash == hash) {
            lock.lockState    = Lock::LockState::Unlocked;
            lock.attemptsLeft = default_attempts;
            resolvePhoneLockAction();
            return sys::msgHandled();
        }
        else if (lock.attemptsLeft > 0) {
            lock.lockState = Lock::LockState::InputInvalid;


@@ 263,8 271,18 @@ namespace locks
        else {
            lock.lockState = Lock::LockState::Blocked;
        }
    }

        phoneInputRequiredAction();
    sys::MessagePointer PhoneLockHandler::verifyPhoneUnlockInput(LockInput inputData)
    {
        comparePhoneLockHashCode(inputData);

        if (lock.isState(Lock::LockState::Unlocked)) {
            resolvePhoneLockAction();
        }
        else {
            phoneInputRequiredAction();
        }

        return sys::msgHandled();
    }


@@ 287,7 305,37 @@ namespace locks

        phoneInputRequiredAction();

        return sys::MessagePointer();
        return sys::msgHandled();
    }

    sys::MessagePointer PhoneLockHandler::handleExternalUnlockRequest(LockInput inputData)
    {
        if (!phoneLockEnabled) {
            phoneUnlockAction();
            return sys::msgHandled();
        }

        if (lock.isState(Lock::LockState::Blocked)) {
            phoneExternalUnlockInfoAction();
            return sys::msgHandled();
        }

        comparePhoneLockHashCode(inputData);

        if (lock.isState(Lock::LockState::Unlocked)) {
            phoneUnlockAction();
        }
        else {
            phoneExternalUnlockInfoAction();
        }

        return sys::msgHandled();
    }

    sys::MessagePointer PhoneLockHandler::handleExternalAvailabilityChange(bool value)
    {
        setPhoneLockAvailabilityInSettings(value);
        return sys::msgHandled();
    }

    void PhoneLockHandler::resolvePhoneLockAction()

M module-apps/locks/handlers/PhoneLockHandler.hpp => module-apps/locks/handlers/PhoneLockHandler.hpp +5 -1
@@ 45,10 45,12 @@ namespace locks
        void phoneInputRequiredAction();
        void phoneUnlockPopupsCloseAction();
        void phoneLockChangeInfoAction();
        void phoneExternalUnlockInfoAction();

        void checkNewPhoneLock();
        void resolvePhoneLockAction();

        void comparePhoneLockHashCode(LockInput inputData);
        sys::MessagePointer verifyPhoneUnlockInput(LockInput inputData);
        sys::MessagePointer verifyPhoneLockChangeInput(LockInput inputData);



@@ 63,7 65,9 @@ namespace locks
        sys::MessagePointer handleChangePhoneLock();
        sys::MessagePointer handleSetPhoneLock();
        sys::MessagePointer handleSkipSetPhoneLock();
        sys::MessagePointer verifyPhoneLockInput(LockInput inputData);
        sys::MessagePointer handlePhoneLockInput(LockInput inputData);
        sys::MessagePointer handleExternalUnlockRequest(LockInput inputData);
        sys::MessagePointer handleExternalAvailabilityChange(bool value);

        void enablePhoneLock(bool _phoneLockEnabled);
        void setPhoneLockHash(const std::string &value);

M module-apps/locks/handlers/SimLockHandler.cpp => module-apps/locks/handlers/SimLockHandler.cpp +58 -7
@@ 4,13 4,13 @@
#include "SimLockHandler.hpp"

#include <service-appmgr/service-appmgr/Controller.hpp>
#include <locks/widgets/LockHash.hpp>
#include <Utils.hpp>
#include <memory>

#include <module-apps/popups/data/PopupRequestParams.hpp>
#include <service-cellular-api>
#include <module-utils/common_data/EventStore.hpp>
#include <module-sys/Timers/TimerFactory.hpp>
#include <service-cellular-api>

namespace locks
{


@@ 22,6 22,11 @@ namespace locks
        : owner(owner), lock(Lock::LockState::Unlocked, default_attempts)
    {
        lock.setInputSizeBounds(min_input_size, max_input_size);

        simResponseTimer = sys::TimerFactory::createSingleShotTimer(
            owner, simResponseTimerName, std::chrono::seconds{1}, [this](sys::Timer &) {
                handleSimNotRespondingMessage();
            });
    }

    void SimLockHandler::clearStoredInputs()


@@ 32,6 37,8 @@ namespace locks

    void SimLockHandler::setSimInputTypeAction(SimInputTypeAction _simInputTypeAction)
    {
        simResponseTimer.stop();

        if (simInputTypeAction != _simInputTypeAction) {
            simInputTypeAction = _simInputTypeAction;
            lock.lockState     = Lock::LockState::Unlocked;


@@ 61,6 68,20 @@ namespace locks
                                             std::make_unique<gui::PopupRequestParams>(gui::popup::ID::SimLock));
    }

    void SimLockHandler::simNotReadyAction()
    {
        app::manager::Controller::sendAction(owner,
                                             app::manager::actions::ShowPopup,
                                             std::make_unique<gui::PopupRequestParams>(gui::popup::ID::SimNotReady));
    }

    void SimLockHandler::simReadyAction()
    {
        app::manager::Controller::sendAction(owner,
                                             app::manager::actions::AbortPopup,
                                             std::make_unique<gui::PopupRequestParams>(gui::popup::ID::SimNotReady));
    }

    void SimLockHandler::simInfoAction()
    {
        app::manager::Controller::sendAction(


@@ 71,10 92,10 @@ namespace locks

    void SimLockHandler::getSettingsSimSelect(const std::string &settingsSim)
    {
        auto selectedSim            = magic_enum::enum_cast<Store::GSM::SIM>(settingsSim);
        Store::GSM::get()->selected = selectedSim.value();
        auto selectedSim = magic_enum::enum_cast<Store::GSM::SIM>(settingsSim);

        if ((selectedSim.value() == Store::GSM::SIM::SIM1 || selectedSim.value() == Store::GSM::SIM::SIM2)) {
        if (selectedSim.has_value() &&
            (selectedSim.value() == Store::GSM::SIM::SIM1 || selectedSim.value() == Store::GSM::SIM::SIM2)) {
            setSim(static_cast<cellular::api::SimSlot>(selectedSim.value()));
        }
        else {


@@ 84,7 105,14 @@ namespace locks

    void SimLockHandler::setSim(cellular::api::SimSlot simSlot)
    {
        owner->bus.sendUnicast<cellular::msg::request::sim::SetActiveSim>(simSlot);
        if (simReady) {
            simResponseTimer.start();
            Store::GSM::get()->selected = static_cast<Store::GSM::SIM>(simSlot);
            owner->bus.sendUnicast<cellular::msg::request::sim::SetActiveSim>(simSlot);
        }
        else {
            simNotReadyAction();
        }
    }

    sys::MessagePointer SimLockHandler::handleSimPinRequest(unsigned int attempts)


@@ 147,7 175,7 @@ namespace locks
        return sys::msgHandled();
    }

    sys::MessagePointer SimLockHandler::handleSimChangedMessage()
    sys::MessagePointer SimLockHandler::handleSimPinChangedMessage()
    {
        lock.lockState = Lock::LockState::Unlocked;
        simInfoAction();


@@ 240,6 268,22 @@ namespace locks
        return sys::msgHandled();
    }

    sys::MessagePointer SimLockHandler::handleSimReadyMessage()
    {
        simResponseTimer.stop();
        return sys::msgHandled();
    }

    sys::MessagePointer SimLockHandler::handleSimNotRespondingMessage()
    {
        setSimInputTypeAction(SimInputTypeAction::Error);

        lock.lockName = utils::enumToString(Store::GSM::get()->selected);
        simInfoAction();

        return sys::msgHandled();
    }

    sys::MessagePointer SimLockHandler::processLockWithNewInput(LockInput inputData)
    {
        if (lock.isState(Lock::LockState::InputRequired) || (lock.isState(Lock::LockState::InputInvalid))) {


@@ 289,6 333,12 @@ namespace locks
        simUnlockBlockOnLockedPhone = true;
    }

    void SimLockHandler::setSimReady()
    {
        simReady = true;
        simReadyAction();
    }

    sys::MessagePointer SimLockHandler::releaseSimUnlockBlockOnLockedPhone()
    {
        if (simUnlockBlockOnLockedPhone) {


@@ 351,4 401,5 @@ namespace locks
                                                                        pinInputData);
        return sys::msgHandled();
    }

} // namespace locks

M module-apps/locks/handlers/SimLockHandler.hpp => module-apps/locks/handlers/SimLockHandler.hpp +13 -2
@@ 8,10 8,12 @@
#include <locks/data/LockData.hpp>

#include <module-sys/Service/Service.hpp>
#include <Timers/TimerHandle.hpp>

namespace locks
{
    using StoredLockInput = std::vector<unsigned int>;
    using StoredLockInput               = std::vector<unsigned int>;
    constexpr auto simResponseTimerName = "SimResponseTimer";

    class SimLockHandler
    {


@@ 22,9 24,12 @@ namespace locks
        SimInputTypeAction simInputTypeAction = SimInputTypeAction::UnlockWithPin;
        unsigned int storedErrorCode          = 0;
        bool simUnlockBlockOnLockedPhone      = false;
        bool simReady                         = false;
        StoredLockInput storedFirstInput;
        StoredLockInput storedSecondInput;

        sys::TimerHandle simResponseTimer;

        void clearStoredInputs();
        void setSimInputTypeAction(SimInputTypeAction _simInputTypeAction);



@@ 32,6 37,8 @@ namespace locks
        void simErrorAction(unsigned int errorCode);
        void simUnlockAction();
        void simInfoAction();
        void simNotReadyAction();
        void simReadyAction();

        sys::MessagePointer unlockSimWithPin(LockInput pinInputData);
        sys::MessagePointer processLockWithNewInput(LockInput inputData);


@@ 45,6 52,8 @@ namespace locks
        explicit SimLockHandler(sys::Service *owner);

        void setSimUnlockBlockOnLockedPhone();
        void setSimReady();

        sys::MessagePointer releaseSimUnlockBlockOnLockedPhone();

        sys::MessagePointer verifySimLockInput(LockInput inputData);


@@ 58,8 67,10 @@ namespace locks
        sys::MessagePointer handleSimBlockedRequest();
        sys::MessagePointer handleCMEErrorRequest(unsigned int errorCode);
        sys::MessagePointer handleSimUnlockedMessage();
        sys::MessagePointer handleSimChangedMessage();
        sys::MessagePointer handleSimPinChangedMessage();
        sys::MessagePointer handleSimAvailabilityMessage();
        sys::MessagePointer handleSimReadyMessage();
        sys::MessagePointer handleSimNotRespondingMessage();

        void getSettingsSimSelect(const std::string &settingsSim);
        void setSim(cellular::api::SimSlot simSlot);

M module-apps/locks/widgets/PhoneLockBox.cpp => module-apps/locks/widgets/PhoneLockBox.cpp +1 -1
@@ 36,7 36,7 @@ namespace gui
            leftBottomBarState     = false;
            break;
        case locks::PhoneLockInputTypeAction::Set:
            LockWindow->setTitleBar(true, false);
            LockWindow->setTitleBar(true, true);
            LockWindow->setText("phone_lock_configure", LockInputWindow::TextType::Title);

            textForInputRequired   = "phone_lock_current";

M module-apps/locks/windows/LockInputWindow.cpp => module-apps/locks/windows/LockInputWindow.cpp +7 -19
@@ 72,25 72,7 @@ namespace gui

    void LockInputWindow::buildIceBox()
    {
        using namespace style::window::lock_input;

        iceBox = new gui::HBox(this, ice::x, ice::y, ice::w, ice::h);
        iceBox->setAlignment(Alignment(Alignment::Horizontal::Left, Alignment::Vertical::Center));
        iceBox->setEdges(RectangleEdge::None);
        iceBox->setVisible(false);

        auto arrow        = new gui::Image("left_label_arrow");
        arrow->activeItem = false;
        arrow->setAlignment(Alignment(Alignment::Horizontal::Left, Alignment::Vertical::Center));
        arrow->setMargins(Margins(0, 0, ice::margin, 0));
        iceBox->addWidget(arrow);

        auto iceText        = new gui::Text(nullptr, 0, 0, ice::text::w, ice::h);
        iceText->activeItem = false;
        iceText->setAlignment(Alignment(Alignment::Horizontal::Left, Alignment::Vertical::Center));
        iceText->setFont(style::window::font::verysmall);
        iceText->setText(utils::translate("app_desktop_emergency"));
        iceBox->addWidget(iceText);
        iceBox = new gui::IceBox(this);
    }

    void LockInputWindow::buildPinBody()


@@ 232,6 214,11 @@ namespace gui
        }
    }

    auto LockInputWindow::isIceVisible() const noexcept -> bool
    {
        return iceBox->visible;
    }

    auto LockInputWindow::isInInputState() const noexcept -> bool
    {
        return lock && (lock->isState(locks::Lock::LockState::InputRequired) ||


@@ 244,4 231,5 @@ namespace gui
        return lock && (lock->isState(locks::Lock::LockState::InputInvalid) ||
                        lock->isState(locks::Lock::LockState::NewInputInvalid));
    }

} // namespace gui

M module-apps/locks/windows/LockInputWindow.hpp => module-apps/locks/windows/LockInputWindow.hpp +3 -1
@@ 9,6 9,7 @@
#include <RichTextParser.hpp>
#include <Text.hpp>
#include <ImageBox.hpp>
#include <widgets/IceBox.hpp>

namespace locks
{


@@ 62,10 63,11 @@ namespace gui

        [[nodiscard]] auto isInInputState() const noexcept -> bool;
        [[nodiscard]] auto isInInvalidInputState() const noexcept -> bool;
        [[nodiscard]] auto isIceVisible() const noexcept -> bool;

      private:
        gui::VBox *body          = nullptr;
        gui::HBox *iceBox        = nullptr;
        gui::IceBox *iceBox      = nullptr;
        gui::ImageBox *infoImage = nullptr;
        gui::Text *primaryText   = nullptr;
        gui::Text *secondaryText = nullptr;

M module-apps/notifications/NotificationsModel.cpp => module-apps/notifications/NotificationsModel.cpp +17 -2
@@ 20,6 20,15 @@ namespace
            item->setName(utils::translate(text), true);
        }
    }

    bool hasTetheringNotification(app::manager::actions::NotificationsChangedParams *params)
    {
        const auto &notifications = params->getNotifications();
        const auto it = std::find_if(std::begin(notifications), std::end(notifications), [](const auto &notification) {
            return notification->getType() == notifications::NotificationType::Tethering;
        });
        return it != std::end(notifications);
    }
} // namespace

unsigned int NotificationsModel::requestRecordsCount()


@@ 104,12 113,13 @@ void NotificationsModel::updateData(app::manager::actions::NotificationsChangedP
            delete item;
        }
    };
    tetheringOn = hasTetheringNotification(params);
    for (const auto &notification : params->getNotifications()) {
        if (typeid(*notification) == typeid(notifications::NotSeenSMSNotification)) {
        if (not tetheringOn && typeid(*notification) == typeid(notifications::NotSeenSMSNotification)) {
            auto sms = static_cast<const notifications::NotSeenSMSNotification *>(notification.get());
            internalData.push_back(create(sms));
        }
        else if (typeid(*notification) == typeid(notifications::NotSeenCallNotification)) {
        else if (not tetheringOn && typeid(*notification) == typeid(notifications::NotSeenCallNotification)) {
            auto call = static_cast<const notifications::NotSeenCallNotification *>(notification.get());
            internalData.push_back(create(call));
        }


@@ 128,3 138,8 @@ void NotificationsModel::clearAll()
    list->reset();
    eraseInternalData();
}

bool NotificationsModel::isTetheringOn() const noexcept
{
    return tetheringOn;
}

M module-apps/notifications/NotificationsModel.hpp => module-apps/notifications/NotificationsModel.hpp +2 -0
@@ 22,6 22,7 @@ namespace gui
        void requestRecords(uint32_t offset, uint32_t limit) final;

      protected:
        bool tetheringOn = false;
        [[nodiscard]] virtual auto create(const notifications::NotSeenSMSNotification *notification)
            -> NotificationListItem *;
        [[nodiscard]] virtual auto create(const notifications::NotSeenCallNotification *notification)


@@ 32,6 33,7 @@ namespace gui
      public:
        [[nodiscard]] bool isEmpty() const noexcept;
        [[nodiscard]] bool hasDismissibleNotification() const noexcept;
        [[nodiscard]] bool isTetheringOn() const noexcept;

        void updateData(app::manager::actions::NotificationsChangedParams *params);
        void dismissAll(const InputEvent &event);

M module-apps/popups/CMakeLists.txt => module-apps/popups/CMakeLists.txt +3 -0
@@ 21,6 21,7 @@ target_sources( ${PROJECT_NAME}
		"${CMAKE_CURRENT_LIST_DIR}/TetheringConfirmationPopup.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/TetheringNotificationPopup.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/TetheringPhoneModePopup.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/TetheringOffPopup.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/PowerOffWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/presenter/PowerOffPresenter.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/lock-popups/PhoneLockedInfoWindow.cpp"


@@ 29,6 30,7 @@ target_sources( ${PROJECT_NAME}
		"${CMAKE_CURRENT_LIST_DIR}/lock-popups/PhoneLockChangeInfoWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/lock-popups/SimLockInputWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/lock-popups/SimInfoWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/lock-popups/SimNotReadyWindow.cpp"

	PRIVATE
		"${CMAKE_CURRENT_LIST_DIR}/Popups.hpp"


@@ 47,4 49,5 @@ target_sources( ${PROJECT_NAME}
		"${CMAKE_CURRENT_LIST_DIR}/lock-popups/PhoneLockChangeInfoWindow.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/lock-popups/SimLockInputWindow.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/lock-popups/SimInfoWindow.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/lock-popups/SimNotReadyWindow.hpp"
)

M module-apps/popups/Popups.cpp => module-apps/popups/Popups.cpp +4 -0
@@ 22,12 22,16 @@ namespace gui::popup
            return gui::popup::window::phone_lock_window;
        case ID::PhoneLockInput:
            return gui::popup::window::phone_lock_input_window;
        case ID::PhoneLockInfo:
            return gui::popup::window::phone_lock_info_window;
        case ID::PhoneLockChangeInfo:
            return gui::popup::window::phone_lock_change_info_window;
        case ID::SimLock:
            return gui::popup::window::sim_unlock_window;
        case ID::SimInfo:
            return gui::popup::window::sim_info_window;
        case ID::SimNotReady:
            return gui::popup::window::sim_not_ready_window;
        }

        return {};

M module-apps/popups/Popups.hpp => module-apps/popups/Popups.hpp +4 -1
@@ 18,9 18,11 @@ namespace gui
            TetheringPhoneModeChangeProhibited,
            PhoneLock,
            PhoneLockInput,
            PhoneLockInfo,
            PhoneLockChangeInfo,
            SimLock,
            SimInfo,
            SimNotReady,
        };

        namespace window


@@ 30,6 32,7 @@ namespace gui
            inline constexpr auto brightness_window                 = "BrightnessPopup";
            inline constexpr auto tethering_confirmation_window     = "TetheringConfirmationPopup";
            inline constexpr auto tethering_phonemode_change_window = "TetheringPhoneModeChangeProhibitedPopup";
            inline constexpr auto tethering_off_window              = "TetheringOffPopup";
            inline constexpr auto phone_lock_window                 = "PhoneLockPopup";
            inline constexpr auto phone_lock_info_window            = "PhoneLockInfoPopup";
            inline constexpr auto phone_lock_input_window           = "PhoneLockInputPopup";


@@ 37,7 40,7 @@ namespace gui
            inline constexpr auto power_off_window                  = "PowerOffPopup";
            inline constexpr auto sim_unlock_window                 = "SimUnlockPopup";
            inline constexpr auto sim_info_window                   = "SimInfoPopup";

            inline constexpr auto sim_not_ready_window              = "SimNotReadyPopup";
        } // namespace window

        std::string resolveWindowName(ID id);

A module-apps/popups/TetheringOffPopup.cpp => module-apps/popups/TetheringOffPopup.cpp +45 -0
@@ 0,0 1,45 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "TetheringOffPopup.hpp"

#include <log/log.hpp>
#include <messages/DialogMetadataMessage.hpp>
#include <module-apps/Application.hpp>
#include <SystemManager/messages/TetheringStateRequest.hpp>
#include <service-appmgr/Controller.hpp>

namespace gui
{
    TetheringOffPopup::TetheringOffPopup(app::Application *app, const std::string &name) : DialogYesNo{app, name}
    {
        topBar->configure(configureTopBar(application->getTopBarConfiguration()));
    }

    top_bar::Configuration TetheringOffPopup::configureTopBar(top_bar::Configuration appConfiguration)
    {
        appConfiguration.enable(top_bar::Indicator::Time);
        appConfiguration.disable(top_bar::Indicator::Lock);
        appConfiguration.disable(top_bar::Indicator::Battery);
        appConfiguration.disable(top_bar::Indicator::Signal);
        appConfiguration.disable(top_bar::Indicator::SimCard);
        return appConfiguration;
    }

    void TetheringOffPopup::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        DialogMetadata metadata;
        metadata.action = [this]() {
            application->bus.sendUnicast(std::make_shared<sys::TetheringStateRequest>(sys::phone_modes::Tethering::Off),
                                         service::name::system_manager);
            app::manager::Controller::sendAction(application, app::manager::actions::Home);
            return true;
        };
        metadata.title = utils::translate("tethering");
        metadata.text  = utils::translate("tethering_turn_off_question");
        metadata.icon  = "tethering_icon";
        auto msg       = std::make_unique<DialogMetadataMessage>(std::move(metadata));
        DialogYesNo::onBeforeShow(mode, msg.get());
    }

} /* namespace gui */

A module-apps/popups/TetheringOffPopup.hpp => module-apps/popups/TetheringOffPopup.hpp +18 -0
@@ 0,0 1,18 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <windows/Dialog.hpp>

namespace gui
{
    class TetheringOffPopup : public DialogYesNo
    {
      public:
        TetheringOffPopup(app::Application *app, const std::string &name);
        top_bar::Configuration configureTopBar(top_bar::Configuration appConfiguration) override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
    };

} // namespace gui

M module-apps/popups/lock-popups/PhoneLockInputWindow.cpp => module-apps/popups/lock-popups/PhoneLockInputWindow.cpp +7 -1
@@ 101,7 101,6 @@ namespace gui
                return true;
            }
        }

        else if (inputEvent.is(KeyCode::KEY_ENTER) && bottomBar->isActive(BottomBar::Side::CENTER)) {
            if (lock->isState(locks::Lock::LockState::Blocked)) {
                application->returnToPreviousWindow();


@@ 119,6 118,13 @@ namespace gui
            lock->clearAttempt();
            return true;
        }
        else if (inputEvent.is(KeyCode::KEY_LEFT) && isIceVisible()) {
            app::manager::Controller::sendAction(application,
                                                 app::manager::actions::EmergencyDial,
                                                 std::make_unique<gui::SwitchData>(),
                                                 app::manager::OnSwitchBehaviour::RunInBackground);
            return true;
        }

        // check if any of the lower inheritance onInput methods catch the event
        return AppWindow::onInput(inputEvent);

M module-apps/popups/lock-popups/PhoneLockedInfoWindow.cpp => module-apps/popups/lock-popups/PhoneLockedInfoWindow.cpp +0 -2
@@ 5,7 5,6 @@

#include <service-appmgr/Controller.hpp>
#include <application-desktop/data/DesktopStyle.hpp>
#include <locks/data/LockStyle.hpp>

#include <i18n/i18n.hpp>



@@ 49,7 48,6 @@ top_bar::Configuration PhoneLockedInfoWindow::configureTopBar(top_bar::Configura

void PhoneLockedInfoWindow::buildInterface()
{
    namespace lock_style = style::window::lock_input;
    AppWindow::buildInterface();

    bottomBar->setText(BottomBar::Side::LEFT, utils::translate("app_desktop_emergency"));

M module-apps/popups/lock-popups/PhoneLockedWindow.cpp => module-apps/popups/lock-popups/PhoneLockedWindow.cpp +1 -1
@@ 147,7 147,7 @@ namespace gui
    {
        using namespace utils::time;
        auto ret       = AppWindow::updateTime();
        auto timestamp = utils::time::Timestamp();
        auto timestamp = utils::time::getCurrentTimestamp();
        if (time != nullptr) {
            auto fmt = utils::dateAndTimeSettings.isTimeFormat12()
                           ? Locale::format(Locale::TimeFormat::FormatTime12HShort)

M module-apps/popups/lock-popups/SimInfoWindow.cpp => module-apps/popups/lock-popups/SimInfoWindow.cpp +6 -0
@@ 44,6 44,12 @@ void SimInfoWindow::onBeforeShow(ShowMode mode, SwitchData *data)
            setTitle("");
            infoIcon->text->setRichText(utils::translate("sim_card_pin_disabled"));
            break;
        case locks::SimInputTypeAction::Error:
            setTitle(utils::translate("app_settings_net"));
            infoIcon->text->setRichText(utils::translate("sim_card_cant_connect"),
                                        {{"$SIM", infoData->getLock().getLockName()}});
            infoIcon->image->set("sim_card_W_G");
            break;
        default:
            break;
        }

M module-apps/popups/lock-popups/SimLockInputWindow.cpp => module-apps/popups/lock-popups/SimLockInputWindow.cpp +7 -0
@@ 115,6 115,13 @@ namespace gui
            }
            return true;
        }
        else if (inputEvent.is(KeyCode::KEY_LEFT) && isIceVisible()) {
            app::manager::Controller::sendAction(application,
                                                 app::manager::actions::EmergencyDial,
                                                 std::make_unique<gui::SwitchData>(),
                                                 app::manager::OnSwitchBehaviour::RunInBackground);
            return true;
        }

        // check if any of the lower inheritance onInput methods catch the event
        return AppWindow::onInput(inputEvent);

A module-apps/popups/lock-popups/SimNotReadyWindow.cpp => module-apps/popups/lock-popups/SimNotReadyWindow.cpp +46 -0
@@ 0,0 1,46 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "SimNotReadyWindow.hpp"

#include <service-appmgr/Controller.hpp>
#include <application-desktop/data/DesktopStyle.hpp>

#include <i18n/i18n.hpp>

using namespace gui;

SimNotReadyWindow::SimNotReadyWindow(app::Application *app, const std::string &name) : AppWindow(app, name)
{
    buildInterface();
}

top_bar::Configuration SimNotReadyWindow::configureTopBar(top_bar::Configuration appConfiguration)
{
    appConfiguration.disable(top_bar::Indicator::NetworkAccessTechnology);
    appConfiguration.disable(top_bar::Indicator::Lock);
    appConfiguration.enable(top_bar::Indicator::PhoneMode);
    appConfiguration.enable(top_bar::Indicator::Time);
    appConfiguration.enable(top_bar::Indicator::Battery);
    appConfiguration.enable(top_bar::Indicator::Signal);
    appConfiguration.enable(top_bar::Indicator::SimCard);
    return appConfiguration;
}

void SimNotReadyWindow::buildInterface()
{
    AppWindow::buildInterface();

    setTitle(utils::translate("app_settings_net"));

    bottomBar->setText(BottomBar::Side::RIGHT, utils::translate("common_back"));

    infoIcon = new gui::Icon(this,
                             style::window::default_left_margin,
                             style::header::height,
                             style::window::default_body_width,
                             style::window::default_body_height,
                             "sim_card_W_G",
                             utils::translate("sim_card_not_ready"));
    infoIcon->setAlignment(Alignment::Horizontal::Center);
}

R module-apps/application-onboarding/windows/OnBoardingMainWindow.hpp => module-apps/popups/lock-popups/SimNotReadyWindow.hpp +9 -10
@@ 3,21 3,20 @@

#pragma once

#include <memory>

#include <AppWindow.hpp>
#include <module-gui/gui/widgets/Image.hpp>
#include <Text.hpp>
#include <gui/widgets/Icon.hpp>

namespace app::onBoarding
namespace gui
{
    class OnBoardingMainWindow : public gui::AppWindow
    class SimNotReadyWindow : public AppWindow
    {
      public:
        explicit OnBoardingMainWindow(app::Application *app);
        Icon *infoIcon = nullptr;

        bool onInput(const gui::InputEvent &inputEvent) override;
      public:
        SimNotReadyWindow(app::Application *app, const std::string &name);

        void buildInterface() override;
        gui::top_bar::Configuration configureTopBar(gui::top_bar::Configuration appConfiguration) override;
        top_bar::Configuration configureTopBar(top_bar::Configuration appConfiguration) override;
    };
} // namespace gui
} /* namespace gui */

A module-apps/widgets/IceBox.cpp => module-apps/widgets/IceBox.cpp +30 -0
@@ 0,0 1,30 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "IceBox.hpp"

#include <Text.hpp>
#include <i18n/i18n.hpp>

namespace gui
{
    IceBox::IceBox(Item *parent)
        : HBox(parent, style::window::ice::x, style::window::ice::y, style::window::ice::w, style::window::ice::h)
    {
        setAlignment(Alignment(Alignment::Horizontal::Left, Alignment::Vertical::Center));
        setEdges(RectangleEdge::None);

        auto arrow        = new gui::Image("left_label_arrow");
        arrow->activeItem = false;
        arrow->setAlignment(Alignment(Alignment::Horizontal::Left, Alignment::Vertical::Center));
        arrow->setMargins(Margins(0, 0, style::window::ice::margin, 0));
        addWidget(arrow);

        auto iceText        = new gui::Text(nullptr, 0, 0, style::window::ice::text::w, style::window::ice::h);
        iceText->activeItem = false;
        iceText->setAlignment(Alignment(Alignment::Horizontal::Left, Alignment::Vertical::Center));
        iceText->setFont(style::window::font::verysmall);
        iceText->setText(utils::translate("app_desktop_emergency"));
        addWidget(iceText);
    }
} // namespace gui

A module-apps/widgets/IceBox.hpp => module-apps/widgets/IceBox.hpp +32 -0
@@ 0,0 1,32 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <BoxLayout.hpp>
#include <Label.hpp>
#include <Image.hpp>

namespace style::window::ice
{
    constexpr inline auto x = 25;
    constexpr inline auto y = 53;
    constexpr inline auto w = 60;
    constexpr inline auto h = 38;

    constexpr inline auto margin = 3;

    namespace text
    {
        constexpr inline auto w = 40;
    }
} // namespace style::window::ice

namespace gui
{
    class IceBox : public HBox
    {
      public:
        explicit IceBox(Item *parent = nullptr);
    };
} // namespace gui

M module-apps/widgets/TimeWidget.cpp => module-apps/widgets/TimeWidget.cpp +8 -8
@@ 384,8 384,8 @@ namespace gui
        if (!mode24H) {
            if (type == Type::Start) {
                const auto hours12H = date::make12(hoursFrom);
                hourInput->setText(std::to_string(hours12H.count()));
                minuteInput->setText(std::to_string(minutesFrom.count()));
                hourInput->setText(utils::to_string(hours12H.count()));
                minuteInput->setText(utils::to_string(minutesFrom.count()));
                if (date::is_am(hoursFrom)) {
                    mode12hInput->setText(timeConstants::before_noon);
                }


@@ 395,8 395,8 @@ namespace gui
            }
            else if (type == Type::End) {
                const auto hours12H = date::make12(hoursTill);
                hourInput->setText(std::to_string(hours12H.count()));
                minuteInput->setText(std::to_string(minutesTill.count()));
                hourInput->setText(utils::to_string(hours12H.count()));
                minuteInput->setText(utils::to_string(minutesTill.count()));
                if (date::is_am(hoursTill)) {
                    mode12hInput->setText(timeConstants::before_noon);
                }


@@ 407,12 407,12 @@ namespace gui
        }
        else {
            if (type == Type::Start) {
                hourInput->setText(std::to_string(hoursFrom.count()));
                minuteInput->setText(std::to_string(minutesFrom.count()));
                hourInput->setText(utils::to_string(hoursFrom.count()));
                minuteInput->setText(utils::to_string(minutesFrom.count()));
            }
            else if (type == Type::End) {
                hourInput->setText(std::to_string(hoursTill.count()));
                minuteInput->setText(std::to_string(minutesTill.count()));
                hourInput->setText(utils::to_string(hoursTill.count()));
                minuteInput->setText(utils::to_string(minutesTill.count()));
            }
        }
    }

M module-audio/Audio/AudioDevice.hpp => module-audio/Audio/AudioDevice.hpp +2 -1
@@ 27,7 27,8 @@ namespace audio
            None,
            Audiocodec,
            Cellular,
            Bluetooth
            BluetoothA2DP,
            BluetoothHSP
        };

        virtual ~AudioDevice() = default;

M module-audio/Audio/Operation/RecorderOperation.cpp => module-audio/Audio/Operation/RecorderOperation.cpp +0 -2
@@ 21,8 21,6 @@ namespace audio

#define PERF_STATS_ON 0

    using namespace bsp;

    RecorderOperation::RecorderOperation(const char *file, AudioServiceMessage::Callback callback) : Operation(callback)
    {


M module-audio/Audio/Profiles/ProfilePlaybackBluetoothA2DP.hpp => module-audio/Audio/Profiles/ProfilePlaybackBluetoothA2DP.hpp +1 -1
@@ 21,7 21,7 @@ namespace audio
                                                  .inputGain     = 0,
                                                  .inputPath     = audio::codec::InputPath::None,
                                                  .outputPath    = audio::codec::OutputPath::None},
                      AudioDevice::Type::Bluetooth)
                      AudioDevice::Type::BluetoothA2DP)
        {}
    };


M module-audio/Audio/Profiles/ProfileRecordingBluetoothHSP.hpp => module-audio/Audio/Profiles/ProfileRecordingBluetoothHSP.hpp +1 -1
@@ 23,7 23,7 @@ namespace audio
                                              .inputGain    = static_cast<float>(gain),
                                              .inputPath    = audio::codec::InputPath::None,
                                              .outputPath   = audio::codec::OutputPath::None},
                  AudioDevice::Type::Bluetooth)
                  AudioDevice::Type::BluetoothHSP)
        {}
    };


M module-audio/Audio/Profiles/ProfileRoutingBluetoothHSP.hpp => module-audio/Audio/Profiles/ProfileRoutingBluetoothHSP.hpp +1 -1
@@ 23,7 23,7 @@ namespace audio
                          .inputGain    = static_cast<float>(gain),
                          .inputPath    = audio::codec::InputPath::None,
                          .outputPath   = audio::codec::OutputPath::None},
                      AudioDevice::Type::Bluetooth)
                      AudioDevice::Type::BluetoothHSP)
        {}
    };


M module-audio/Audio/decoder/decoderMP3.hpp => module-audio/Audio/decoder/decoderMP3.hpp +3 -4
@@ 1,12 1,11 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "minimp3/minimp3.h"

#include <cstring>
#include "Decoder.hpp"
#include <minimp3.h>
#include <cstring>

extern "C"
{

D module-audio/Audio/decoder/minimp3 => module-audio/Audio/decoder/minimp3 +0 -1
@@ 1,1 0,0 @@
Subproject commit 446d3a32d281eb1dcae7726ba0ff594695182908

M module-audio/Audio/transcode/InputTranscodeProxy.cpp => module-audio/Audio/transcode/InputTranscodeProxy.cpp +5 -0
@@ 23,6 23,11 @@ bool InputTranscodeProxy::push(const Span &span)
    return getWrappedStream().push(transform->transform(span, transcodingSpaceSpan));
}

bool InputTranscodeProxy::push(void *data, std::size_t dataSize)
{
    return push(Span{.data = reinterpret_cast<std::uint8_t *>(data), .dataSize = dataSize});
}

void InputTranscodeProxy::commit()
{
    if (isReserved) {

M module-audio/Audio/transcode/InputTranscodeProxy.hpp => module-audio/Audio/transcode/InputTranscodeProxy.hpp +1 -0
@@ 27,6 27,7 @@ namespace audio::transcode
                                     std::shared_ptr<Transform> transform) noexcept;

        bool push(const Span &span) override;
        bool push(void *data, std::size_t dataSize);
        void commit() override;
        bool reserve(Span &span) override;
        [[nodiscard]] auto getInputTraits() const noexcept -> Traits override;

M module-audio/CMakeLists.txt => module-audio/CMakeLists.txt +9 -7
@@ 49,7 49,7 @@ target_sources(
                ${CMAKE_CURRENT_SOURCE_DIR}/Audio/transcode/TransformFactory.cpp
                ${CMAKE_CURRENT_SOURCE_DIR}/Audio/VolumeScaler.cpp
)
target_include_directories(${PROJECT_NAME} PRIVATE ${TAGLIB_INCLUDE_DIRS})

target_compile_definitions(${PROJECT_NAME} PUBLIC ${PROJECT_CONFIG_DEFINITIONS})
target_compile_definitions(${PROJECT_NAME} PUBLIC ${PROJECT_TARGET})
target_compile_definitions(${PROJECT_NAME} PUBLIC ${TARGET_COMPILE_DEFINITIONS})


@@ 66,12 66,14 @@ set_source_files_properties(
)

target_link_libraries(${PROJECT_NAME}
    ${AUDIO_BOARD_LIBRARY}
    module-bsp
    module-os
    module-utils
    module-sys
    tag
        PUBLIC
                ${AUDIO_BOARD_LIBRARY}
                module-bsp
                module-os
                module-utils
                module-sys
                tag
                minimp3::minimp3
)

if (${ENABLE_TESTS})

M module-audio/board/rt1051/RT1051DeviceFactory.cpp => module-audio/board/rt1051/RT1051DeviceFactory.cpp +6 -2
@@ 21,8 21,12 @@ std::shared_ptr<AudioDevice> RT1051DeviceFactory::getDevice(const audio::Profile
        device = std::make_shared<RT1051AudioCodec>(profile.GetAudioConfiguration());
    } break;

    case AudioDevice::Type::Bluetooth: {
        device = std::make_shared<bluetooth::BluetoothAudioDevice>();
    case AudioDevice::Type::BluetoothA2DP: {
        device = std::make_shared<bluetooth::A2DPAudioDevice>();
    } break;

    case AudioDevice::Type::BluetoothHSP: {
        device = std::make_shared<bluetooth::HSPAudioDevice>();
    } break;

    case AudioDevice::Type::Cellular: {

M module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.cpp => module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.cpp +158 -21
@@ 3,42 3,46 @@

#include "BluetoothAudioDevice.hpp"

#include <Audio/AudioCommon.hpp>
#include <interface/profiles/A2DP/AVDTP.hpp>
#include <module-audio/Audio/VolumeScaler.hpp>
#include <interface/profiles/A2DP/AVRCP.hpp>
#include <interface/profiles/HSP/SCO.hpp>

#include <Audio/AudioCommon.hpp>
#include <Audio/VolumeScaler.hpp>
#include <Audio/Stream.hpp>

#include <chrono>
#include <cassert>

using audio::AudioFormat;
using namespace bluetooth;
using bluetooth::A2DPAudioDevice;
using bluetooth::BluetoothAudioDevice;
using bluetooth::HSPAudioDevice;

using namespace std::chrono_literals;

BluetoothAudioDevice::BluetoothAudioDevice(MediaContext *mediaContext) : ctx(mediaContext)
{
    LOG_DEBUG("Bluetooth audio device created");
}
BluetoothAudioDevice::BluetoothAudioDevice(AudioProfile audioProfile) : profile(audioProfile)
{}

BluetoothAudioDevice::~BluetoothAudioDevice()
{
    LOG_DEBUG("Destroying bluetooth audio device");
}

void BluetoothAudioDevice::setMediaContext(MediaContext *mediaContext)
auto BluetoothAudioDevice::getProfileType() const -> AudioProfile
{
    ctx = mediaContext;
    return profile;
}

auto BluetoothAudioDevice::setOutputVolume(float vol) -> audio::AudioDevice::RetCode
{
    const auto volumeToSet = audio::volume::scaler::toAvrcpVolume(vol);
    const auto status      = avrcp_controller_set_absolute_volume(ctx->avrcp_cid, volumeToSet);
    const auto status      = avrcp_controller_set_absolute_volume(AVRCP::mediaTracker.avrcp_cid, volumeToSet);
    if (status != ERROR_CODE_SUCCESS) {
        LOG_ERROR("Can't set volume level. Status %x", status);
        return audio::AudioDevice::RetCode::Failure;
    }

    outputVolume = vol;
    return audio::AudioDevice::RetCode::Success;
}


@@ 48,19 52,125 @@ auto BluetoothAudioDevice::setInputGain(float gain) -> audio::AudioDevice::RetCo
    return audio::AudioDevice::RetCode::Success;
}

void BluetoothAudioDevice::onDataSend()
auto BluetoothAudioDevice::isInputEnabled() const -> bool
{
    return inputEnabled;
}

auto BluetoothAudioDevice::isOutputEnabled() const -> bool
{
    if (outputEnabled) {
        fillSbcAudioBuffer(ctx);
    return outputEnabled;
}

void A2DPAudioDevice::onDataSend()
{
    if (isOutputEnabled()) {
        fillSbcAudioBuffer();
    }
}

void BluetoothAudioDevice::onDataReceive()
void A2DPAudioDevice::onDataReceive()
{}

void BluetoothAudioDevice::enableInput()
void HSPAudioDevice::onDataSend()
{}

void HSPAudioDevice::onDataSend(std::uint16_t scoHandle)
{
    if (!isOutputEnabled()) {
        return;
    }

    hci_reserve_packet_buffer();
    auto scoPacket = hci_get_outgoing_packet_buffer();

    // get data to send
    audio::AbstractStream::Span dataSpan;
    Sink::_stream->peek(dataSpan);

    // prepare packet to send
    std::copy(dataSpan.data, dataSpan.dataEnd(), &scoPacket[packetDataOffset]);
    Sink::_stream->consume();
    little_endian_store_16(scoPacket, packetHandleOffset, scoHandle);
    scoPacket[packetLengthOffset] = dataSpan.dataSize;

    // send packet
    hci_send_sco_packet_buffer(dataSpan.dataSize + packetDataOffset);
    hci_request_sco_can_send_now_event();
}

void HSPAudioDevice::receiveCVSD(audio::AbstractStream::Span receivedData)
{
    if (!isInputEnabled()) {
        return;
    }

    auto blockSize          = Source::_stream->getInputTraits().blockSize;
    auto decodedData        = decodeCVSD(receivedData);
    auto processedDataIndex = 0;

    // try to complete leftovers to the full block size
    if (leftoversSize != 0) {
        auto maxFillSize = blockSize - leftoversSize;
        auto fillSize    = std::min(maxFillSize, decodedData.dataSize);

        std::copy_n(decodedData.data, fillSize, &rxLeftovers[leftoversSize]);

        if (fillSize + leftoversSize < blockSize) {
            leftoversSize += fillSize;
            return;
        }

        Source::_stream->push(&rxLeftovers[0], blockSize);
        leftoversSize      = 0;
        processedDataIndex = fillSize;
    }

    // push as many blocks as possible
    while (decodedData.dataSize - processedDataIndex >= blockSize) {
        Source::_stream->push(&decodedData.data[processedDataIndex], blockSize);
        processedDataIndex += blockSize;
    }

    // save leftovers
    leftoversSize = decodedData.dataSize - processedDataIndex;
    if (leftoversSize > 0) {
        std::copy_n(&decodedData.data[processedDataIndex], leftoversSize, &rxLeftovers[0]);
    }
}

auto HSPAudioDevice::decodeCVSD(audio::AbstractStream::Span dataToDecode) -> audio::AbstractStream::Span
{
    auto decodedData = dataToDecode;
    std::array<std::int16_t, scratchBufferSize> scratchBuffer;

    const auto audioBytesRead = dataToDecode.dataSize - packetDataOffset;
    const auto samplesCount   = audioBytesRead / sizeof(std::int16_t);
    auto dataStart            = &dataToDecode.data[packetDataOffset];

    for (auto i = 0; i < samplesCount; ++i) {
        scratchBuffer[i] = little_endian_read_16(dataStart, i * sizeof(std::uint16_t));
    }

    auto packetStatusByte = dataToDecode.data[packetStatusOffset];
    auto isBadFrame       = (packetStatusByte & allGoodMask) != 0;

    btstack_cvsd_plc_process_data(&cvsdPlcState, isBadFrame, &scratchBuffer[0], samplesCount, &decoderBuffer[0]);

    decodedData.data     = reinterpret_cast<std::uint8_t *>(decoderBuffer.get());
    decodedData.dataSize = audioBytesRead;

    return decodedData;
}

void HSPAudioDevice::onDataReceive()
{}

void BluetoothAudioDevice::enableInput()
{
    inputEnabled = true;
}

void BluetoothAudioDevice::enableOutput()
{
    LOG_DEBUG("Enabling bluetooth audio output.");


@@ 68,7 178,9 @@ void BluetoothAudioDevice::enableOutput()
}

void BluetoothAudioDevice::disableInput()
{}
{
    inputEnabled = false;
}

void BluetoothAudioDevice::disableOutput()
{


@@ 76,11 188,21 @@ void BluetoothAudioDevice::disableOutput()
    outputEnabled = false;
}

auto BluetoothAudioDevice::fillSbcAudioBuffer(MediaContext *context) -> int
void HSPAudioDevice::enableInput()
{
    auto blockSize = Source::_stream->getInputTraits().blockSize;
    rxLeftovers    = std::make_unique<std::uint8_t[]>(blockSize);
    decoderBuffer  = std::make_unique<std::int16_t[]>(scratchBufferSize);
    btstack_cvsd_plc_init(&cvsdPlcState);
    BluetoothAudioDevice::enableInput();
}

auto BluetoothAudioDevice::fillSbcAudioBuffer() -> int
{
    // perform sbc encodin
    int totalNumBytesRead                    = 0;
    unsigned int numAudioSamplesPerSbcBuffer = btstack_sbc_encoder_num_audio_frames();
    auto context                             = &AVRCP::mediaTracker;

    assert(context != nullptr);



@@ 104,7 226,7 @@ auto BluetoothAudioDevice::fillSbcAudioBuffer(MediaContext *context) -> int
    return totalNumBytesRead;
}

auto BluetoothAudioDevice::getSupportedFormats() -> std::vector<audio::AudioFormat>
auto A2DPAudioDevice::getSupportedFormats() -> std::vector<audio::AudioFormat>
{
    constexpr static auto supportedBitWidth = 16U;
    return std::vector<AudioFormat>{AudioFormat{static_cast<unsigned>(AVDTP::sbcConfig.samplingFrequency),


@@ 112,12 234,27 @@ auto BluetoothAudioDevice::getSupportedFormats() -> std::vector<audio::AudioForm
                                                static_cast<unsigned>(AVDTP::sbcConfig.numChannels)}};
}

auto BluetoothAudioDevice::getSourceFormat() -> audio::AudioFormat
auto HSPAudioDevice::getSupportedFormats() -> std::vector<audio::AudioFormat>
{
    return audio::nullFormat;
    return std::vector<AudioFormat>{getSourceFormat()};
}

auto BluetoothAudioDevice::getTraits() const -> Traits
auto A2DPAudioDevice::getTraits() const -> ::audio::Endpoint::Traits
{
    return Traits{.usesDMA = false, .blockSizeConstraint = 512U, .timeConstraint = 10ms};
}

auto HSPAudioDevice::getTraits() const -> ::audio::Endpoint::Traits
{
    return Traits{.usesDMA = false, .blockSizeConstraint = 32U, .timeConstraint = 16ms};
}

auto A2DPAudioDevice::getSourceFormat() -> ::audio::AudioFormat
{
    return audio::nullFormat;
}

auto HSPAudioDevice::getSourceFormat() -> ::audio::AudioFormat
{
    return AudioFormat{bluetooth::SCO::CVSD_SAMPLE_RATE, supportedBitWidth, supportedChannels};
}

M module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.hpp => module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.hpp +70 -14
@@ 3,9 3,16 @@

#pragma once

#include <Audio/Endpoint.hpp>
#include <Audio/AudioDevice.hpp>
#include <Audio/AudioFormat.hpp>
#include <interface/profiles/A2DP/MediaContext.hpp>
#include <interface/profiles/AudioProfile.hpp>

extern "C"
{
#include "classic/btstack_cvsd_plc.h"
}

namespace bluetooth
{


@@ 13,31 20,80 @@ namespace bluetooth
    class BluetoothAudioDevice : public audio::AudioDevice
    {
      public:
        BluetoothAudioDevice() = default;
        explicit BluetoothAudioDevice(MediaContext *mediaContext);
        explicit BluetoothAudioDevice(AudioProfile audioProfile);
        virtual ~BluetoothAudioDevice();

        RetCode setOutputVolume(float vol) override;
        RetCode setInputGain(float gain) override;
        void setMediaContext(MediaContext *MediaContext);
        auto getTraits() const -> Traits override;
        auto getSupportedFormats() -> std::vector<audio::AudioFormat> override;
        auto getSourceFormat() -> audio::AudioFormat override;
        virtual auto getProfileType() const -> AudioProfile;

        auto setOutputVolume(float vol) -> audio::AudioDevice::RetCode override;
        auto setInputGain(float gain) -> audio::AudioDevice::RetCode override;

        // Endpoint control methods
        void onDataSend() override;
        void onDataReceive() override;
        void enableInput() override;
        void enableOutput() override;
        void disableInput() override;
        void disableOutput() override;

      protected:
        auto isInputEnabled() const -> bool;
        auto isOutputEnabled() const -> bool;
        auto fillSbcAudioBuffer() -> int;

      private:
        bool outputEnabled   = false;
        bool inputEnabled    = false;
        AudioProfile profile = AudioProfile::None;
        float outputVolume;
    };

    class A2DPAudioDevice : public BluetoothAudioDevice
    {
      public:
        explicit A2DPAudioDevice() : BluetoothAudioDevice(AudioProfile::A2DP)
        {}

        void onDataSend() override;
        void onDataReceive() override;
        auto getSupportedFormats() -> std::vector<audio::AudioFormat> override;
        auto getTraits() const -> Traits override;
        auto getSourceFormat() -> ::audio::AudioFormat override;
    };

    class HSPAudioDevice : public BluetoothAudioDevice
    {
      public:
        explicit HSPAudioDevice() : BluetoothAudioDevice(AudioProfile::HSP)
        {}

        void onDataSend() override;
        void onDataSend(std::uint16_t scoHandle);
        void onDataReceive() override;
        auto getSupportedFormats() -> std::vector<audio::AudioFormat> override;
        auto getTraits() const -> Traits override;
        auto getSourceFormat() -> ::audio::AudioFormat override;
        void enableInput() override;

        void receiveCVSD(audio::AbstractStream::Span receivedData);

      private:
        auto fillSbcAudioBuffer(MediaContext *context) -> int;
        static constexpr std::size_t scratchBufferSize = 128;

        static constexpr auto packetHandleOffset = 0;
        static constexpr auto packetStatusOffset = 1;
        static constexpr auto packetLengthOffset = 2;
        static constexpr auto packetDataOffset   = 3;

        constexpr static auto supportedBitWidth = 16U;
        constexpr static auto supportedChannels = 1;

        constexpr static auto allGoodMask = 0x30;

        auto decodeCVSD(audio::AbstractStream::Span dataToDecode) -> audio::AbstractStream::Span;

        MediaContext *ctx  = nullptr;
        bool outputEnabled = false;
        float outputVolume = 0.0;
        std::unique_ptr<std::uint8_t[]> rxLeftovers;
        std::unique_ptr<std::int16_t[]> decoderBuffer;
        std::size_t leftoversSize = 0;
        btstack_cvsd_plc_state_t cvsdPlcState;
    };

} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp +1 -1
@@ 97,6 97,7 @@ namespace bluetooth

    auto A2DP::startRinging() const noexcept -> Error::Code
    {
        LOG_ERROR("Can't ring in A2DP profile");
        return Error::SystemError;
    }



@@ 622,7 623,6 @@ namespace bluetooth

    void A2DP::A2DPImpl::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> newAudioDevice)
    {
        newAudioDevice->setMediaContext(&AVRCP::mediaTracker);
        A2DP::A2DPImpl::audioDevice = std::move(newAudioDevice);
    }


M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp +0 -1
@@ 50,7 50,6 @@ namespace bluetooth
        static void sendMediaPacket();
        static void sendAudioEvent(audio::EventType event, audio::Event::DeviceState state);
        static bool isConnected;

        static std::shared_ptr<BluetoothAudioDevice> audioDevice;

      public:

A module-bluetooth/Bluetooth/interface/profiles/AudioProfile.hpp => module-bluetooth/Bluetooth/interface/profiles/AudioProfile.hpp +15 -0
@@ 0,0 1,15 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

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

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp +18 -2
@@ 111,6 111,7 @@ namespace bluetooth
    const sys::Service *HSP::HSPImpl::ownerService;
    std::string HSP::HSPImpl::agServiceName = "PurePhone HSP";
    bool HSP::HSPImpl::isConnected          = false;
    std::shared_ptr<HSPAudioDevice> HSP::HSPImpl::audioDevice;

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


@@ 127,7 128,9 @@ namespace bluetooth
            if (READ_SCO_CONNECTION_HANDLE(event) != scoHandle) {
                break;
            }
            sco->receive(event, eventSize);
            if (audioDevice != nullptr) {
                audioDevice->receiveCVSD(audio::AbstractStream::Span{.data = event, .dataSize = eventSize});
            }
            break;

        case HCI_EVENT_PACKET:


@@ 142,7 145,9 @@ namespace bluetooth
    {
        switch (hci_event_packet_get_type(event)) {
        case HCI_EVENT_SCO_CAN_SEND_NOW:
            sco->send(scoHandle);
            if (audioDevice != nullptr) {
                audioDevice->onDataSend(scoHandle);
            }
            break;
        case HCI_EVENT_HSP_META:
            processHSPEvent(event);


@@ 163,6 168,7 @@ namespace bluetooth
                break;
            }
            LOG_DEBUG("RFCOMM connection established.\n");
            sendAudioEvent(audio::EventType::BlutoothHSPDeviceState, audio::Event::DeviceState::Connected);
            isConnected = true;
            break;
        case HSP_SUBEVENT_RFCOMM_DISCONNECTION_COMPLETE:


@@ 314,4 320,14 @@ namespace bluetooth
        stopRinging();
        establishAudioConnection();
    }

    void HSP::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice)
    {
        pimpl->setAudioDevice(audioDevice);
    }

    void HSP::HSPImpl::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice)
    {
        HSP::HSPImpl::audioDevice = std::static_pointer_cast<HSPAudioDevice>(audioDevice);
    }
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp +1 -2
@@ 55,8 55,7 @@ namespace bluetooth
        /// @return Success
        [[nodiscard]] auto initializeCall() const noexcept -> Error::Code override;

        void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice) override
        {}
        void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice) override;

      private:
        class HSPImpl;

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSPImpl.hpp +3 -1
@@ 27,6 27,7 @@ namespace bluetooth
        void setDeviceAddress(bd_addr_t addr);
        void setOwnerService(const sys::Service *service);
        auto getStreamData() -> std::shared_ptr<BluetoothStreamData>;
        void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice);

      private:
        static void sendAudioEvent(audio::EventType event, audio::Event::DeviceState state);


@@ 43,5 44,6 @@ namespace bluetooth
        static bd_addr_t deviceAddr;
        static const sys::Service *ownerService;
        static bool isConnected;
        static std::shared_ptr<HSPAudioDevice> audioDevice;
    };
} // namespace Bt
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/HSP/SCO.cpp => module-bluetooth/Bluetooth/interface/profiles/HSP/SCO.cpp +0 -1
@@ 32,7 32,6 @@ namespace bluetooth
        auto getStreamData() -> std::shared_ptr<BluetoothStreamData>;

      private:
        static constexpr auto CVSD_SAMPLE_RATE    = 8000;
        static constexpr auto BYTES_PER_FRAME     = 2;
        static constexpr auto ALL_GOOD_MASK       = 0x30;
        static constexpr auto AUDIO_BUFFER_LENGTH = 128;

M module-bluetooth/Bluetooth/interface/profiles/HSP/SCO.hpp => module-bluetooth/Bluetooth/interface/profiles/HSP/SCO.hpp +3 -1
@@ 30,8 30,10 @@ namespace bluetooth
        [[nodiscard]] auto getStreamData() const -> std::shared_ptr<BluetoothStreamData>;
        void setOwnerService(const sys::Service *service);

        static constexpr auto CVSD_SAMPLE_RATE = 8000;

      private:
        class SCOImpl;
        std::unique_ptr<SCOImpl> pimpl;
    };
} // namespace Bt
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp +7 -3
@@ 97,6 97,7 @@ namespace bluetooth

    auto ProfileManager::startRinging() -> Error::Code
    {
        switchProfile(AudioProfile::HSP);
        return currentProfilePtr->startRinging();
    }



@@ 107,16 108,19 @@ namespace bluetooth

    auto ProfileManager::initializeCall() -> Error::Code
    {
        switchProfile(AudioProfile::HSP);
        return currentProfilePtr->initializeCall();
    }

    auto ProfileManager::setAudioDevice(std::shared_ptr<BluetoothAudioDevice> device) -> Error::Code
    {
        if (currentProfilePtr == nullptr) {
        auto profileType = device->getProfileType();

        if (currentProfilePtr == nullptr || profilesList[profileType] == nullptr) {
            return Error::NotReady;
        }

        currentProfilePtr->setAudioDevice(std::move(device));
        return Error::Success;
        profilesList[profileType]->setAudioDevice(device);
        return switchProfile(profileType);
    }
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp +1 -8
@@ 6,6 6,7 @@
#include <Service/Service.hpp>
#include <Error.hpp>
#include "Profile.hpp"
#include "AudioProfile.hpp"
#include "interface/profiles/A2DP/A2DP.hpp"
#include "interface/profiles/HSP/HSP.hpp"
#include "audio/BluetoothAudioDevice.hpp"


@@ 21,14 22,6 @@ extern "C"
namespace bluetooth
{

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

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

    class ProfileManager

M module-bsp/board/linux/battery-charger/battery_charger.cpp => module-bsp/board/linux/battery-charger/battery_charger.cpp +11 -0
@@ 185,4 185,15 @@ namespace bsp::battery_charger
        }
    }

    int getVoltageFilteredMeasurement()
    {
        constexpr auto nominalVoltage = 4350;
        return nominalVoltage;
    }

    BaseType_t INTB_IRQHandler()
    {
        return 1;
    }

} // namespace bsp::battery_charger

M module-bsp/board/linux/magnetometer/magnetometer.cpp => module-bsp/board/linux/magnetometer/magnetometer.cpp +1 -5
@@ 35,11 35,7 @@ namespace bsp

        void enableIRQ()
        {}
        void initFirstReadout()
        void resetCurrentParsedValue()
        {}
        bsp::KeyCodes getCurrentSliderPosition()
        {
            return bsp::KeyCodes::Undefined;
        }
    } // namespace magnetometer
} // namespace bsp

M module-bsp/board/rt1051/bsp/battery-charger/battery_charger.cpp => module-bsp/board/rt1051/bsp/battery-charger/battery_charger.cpp +10 -0
@@ 592,6 592,9 @@ namespace bsp::battery_charger
        enableChargerIRQs();
        IRQPinsInit();

        // Check IRQ status register
        INTB_IRQHandler();

        return 0;
    }



@@ 740,6 743,13 @@ namespace bsp::battery_charger
        }
    }

    int getVoltageFilteredMeasurement()
    {
        auto [retCode, value] = fuelGaugeRead(Registers::AvgVCELL_REG);
        int voltage           = value * voltageSenseGain;
        return voltage;
    }

    BaseType_t INTB_IRQHandler()
    {
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;

M module-bsp/board/rt1051/bsp/magnetometer/ALS31300.hpp => module-bsp/board/rt1051/bsp/magnetometer/ALS31300.hpp +2 -22
@@ 17,7 17,7 @@ namespace drivers::als31300
    using whole_reg_t = uint32_t; // ALS31300 always talks 4 bytes

    constexpr auto I2C_ADDRESS = 0x64;
    constexpr auto DUMMY_BYTE  = 1;

    // base ALS31300 register struct.
    struct base_reg
    {


@@ 109,33 109,13 @@ namespace drivers::als31300
    constexpr auto PWR_REG_LOOP_MODE_fast_loop = 0b01;
    constexpr auto PWR_REG_LOOP_MODE_full_loop = 0b10;

    enum class PWR_REG_SLEEP_MODE
    enum PWR_REG_SLEEP_MODE
    {
        active          = PWR_REG_SLEEP_MODE_active,
        sleep           = PWR_REG_SLEEP_MODE_sleep,
        periodic_active = PWR_REG_SLEEP_MODE_LPDCM,
    };
    enum class LPDCM_INACTIVE_TIME
    {
        inactive_500us,
        inactive_1ms,
        inactive_5ms,
        inactive_10ms,
        inactive_50ms,
        inactive_100ms,
        inactive_500ms,
        inactive_1s
    };

    enum class BANDWIDTH_SELECT
    {
        bandwidth_3500Hz = 0,
        bandwidth_7kHz   = 1,
        bandwidth_14kHz  = 2,
        bandwidth_10kHz  = 4,
        bandwidth_20kHz  = 5,
        bandwidth_40kHz  = 6
    };
    constexpr auto PWR_ON_DELAY_MS = 1; //  spec'd as 600µs at most

    // --------

D module-bsp/board/rt1051/bsp/magnetometer/doc/magnetometer_values.png => module-bsp/board/rt1051/bsp/magnetometer/doc/magnetometer_values.png +0 -0
D module-bsp/board/rt1051/bsp/magnetometer/doc/readme.md => module-bsp/board/rt1051/bsp/magnetometer/doc/readme.md +0 -27
@@ 1,27 0,0 @@
# ALS31300 Hall effect sensor
This magnetometer is used as a detector of the slider position, which has a magnet attached to itself. When slider position changes,
the magnetic field intensity measured by the sensor is changing and is properly interpreted to determine the position of the slider.

## Values per position
![Magnetometer values](./magnetometer_values.png)
<center><b>Positions: down, middle, up, middle</b></center>

As shown on the graph, using only X and Y axes can clearly determine the slider position so this has been implemented.

## Magnetometer configuration
Looking at the graph it's easy to find out, that each slider position has some mean value of the magnetic field intensity. This particular
sensor has the possibility to generate an interrupt on its pin when the absolute threshold has been exceeded or the increment of
the intensity related to the last taken measurement was higher than a certain, set value.

In this case, increment (delta) mode has been used, combined with Low Power Delta Compare Mode (LPDCM) - 
magnetometer cycles between active and inactive state and takes measurements. When the change in the magnetic field has been higher than
set value, it generates an interrupt for the main processor, which then retrieves the measurements and does proper action.

## Caveats

- once the magnetometer has been put to the LPDCM mode it cannot be read nor written until the interrupt happens - despite the datasheet which claims,
that the device should respond to the I2C commands, the only answer to every one command was NAK,
- setting a too small period for LPDCM mode (too small inactivity time) caused some instability during tests, 
- to get proper measurements after receiving the interrupt, there's a need to put the magnetometer to the active state,
  then take measurements and put it again to the LPDCM mode - doing anything else means the measurements data was inaccurate and way off.


M module-bsp/board/rt1051/bsp/magnetometer/magnetometer.cpp => module-bsp/board/rt1051/bsp/magnetometer/magnetometer.cpp +84 -50
@@ 34,6 34,27 @@ namespace bsp
{
    namespace magnetometer
    {
        enum class LPDCM_INACTIVE_TIME
        {
            inactive_500us,
            inactive_1ms,
            inactive_5ms,
            inactive_10ms,
            inactive_50ms,
            inactive_100ms,
            inactive_500ms,
            inactive_1s
        };

        enum class BANDWIDTH_SELECT
        {
            bandwidth_3500Hz = 0,
            bandwidth_7kHz   = 1,
            bandwidth_14kHz  = 2,
            bandwidth_10kHz  = 4,
            bandwidth_20kHz  = 5,
            bandwidth_40kHz  = 6
        };
        namespace
        {
            bool isTimeToCompleteWriteDefinedForRegistry(std::uint8_t address)


@@ 41,28 62,29 @@ namespace bsp
                const auto it = std::find(als31300::EEPROM_REGS.begin(), als31300::EEPROM_REGS.end(), address);
                return it != als31300::EEPROM_REGS.end();
            }
            auto getThresholdLevel(std::uint16_t gaussThreshold) -> std::uint8_t
            {
                return floor(((gaussThreshold + 1) / 32.0) - 1); // calculation formula taken from ALS datasheet
            }
        } // namespace

        std::shared_ptr<DriverGPIO> gpio;

        bsp::KeyCodes current_parsed = bsp::KeyCodes::Undefined;

        bool setActive(als31300::PWR_REG_SLEEP_MODE sleep_mode);
        static TimerHandle_t timerHandle;
        static constexpr uint16_t MAGNETOMETER_POLL_INTERVAL_MS = 500;

        bsp::KeyCodes getCurrentSliderPosition()
        static void TimerHandler(TimerHandle_t xTimer)
        {
            return current_parsed;
            if (qHandleIrq != nullptr) {
                uint8_t val = 0x01;
                xQueueSend(qHandleIrq, &val, 0);
            }
        }

        bool setActive(als31300::PWR_REG_SLEEP_MODE sleep_mode);

        bool i2cRead(const uint8_t reg_addr, als31300::whole_reg_t &whole_reg)
        {
            addr.subAddress = reg_addr;
            auto readBytes  = i2c->Read(addr, i2c_buf.buf, sizeof(als31300::whole_reg_t));
            if (readBytes != sizeof(als31300::whole_reg_t)) {
            if (i2c->Read(addr, i2c_buf.buf, sizeof(als31300::whole_reg_t)) != sizeof(als31300::whole_reg_t)) {
                return false;
            }
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__


@@ 99,9 121,11 @@ namespace bsp

            // any configuration must be proceeded in active state
            setActive(als31300::PWR_REG_SLEEP_MODE::active);

            // GET WRITE ACCESS
            if (!i2cWrite(als31300::CUSTOMER_ACCESS_REG, als31300::CUSTOMER_ACCESS_REG_code)) {
                LOG_WARN("magneto: Customer access already unlocked or write error");
                LOG_ERROR("magneto: CANNOT INIT SLIDER SENSOR");
                return kStatus_Fail;
            }

            // CONFIGURATION register read


@@ 115,12 139,10 @@ namespace bsp
            reg_conf.channel_X_en       = als31300::CONF_REG_CHANNEL_enabled;
            reg_conf.channel_Y_en       = als31300::CONF_REG_CHANNEL_enabled;
            reg_conf.channel_Z_en       = als31300::CONF_REG_CHANNEL_disabled;
            reg_conf.bandwidth          = static_cast<std::uint8_t>(als31300::BANDWIDTH_SELECT::bandwidth_7kHz);
            reg_conf.bandwidth          = static_cast<uint8_t>(BANDWIDTH_SELECT::bandwidth_7kHz);
            if (current_reg_conf != reg_conf) {
                if (!i2cWrite(als31300::CONF_REG, reg_conf)) {
                    LOG_ERROR("magneto: CANNOT INIT SLIDER SENSOR");
                    return kStatus_Fail;
                }
                [[maybe_unused]] auto ret = i2cWrite(als31300::CONF_REG, reg_conf);
                assert(ret);
                LOG_DEBUG("CONF wrote:\t%" PRIu32, static_cast<uint32_t>(reg_conf));

                i2cRead(als31300::CONF_REG, read_reg);


@@ 136,16 158,17 @@ namespace bsp
            LOG_DEBUG("INT read:\t%" PRIu32, static_cast<uint32_t>(current_reg_int));
            als31300::int_reg reg_int    = current_reg_int;
            reg_int.int_eeprom_en        = als31300::INT_REG_INT_EEPROM_disable;
            reg_int.int_mode             = als31300::INT_REG_INT_MODE_delta;
            reg_int.int_mode             = als31300::INT_REG_INT_MODE_threshold;
            reg_int.int_threshold_signed = als31300::INT_REG_THRESHOLD_absolute;
            reg_int.int_X_en             = als31300::INT_REG_INT_CHANNEL_enabled;
            reg_int.int_Y_en             = als31300::INT_REG_INT_CHANNEL_enabled;
            reg_int.int_X_en             = als31300::INT_REG_INT_CHANNEL_disabled;
            reg_int.int_Y_en             = als31300::INT_REG_INT_CHANNEL_disabled;
            reg_int.int_Z_en             = als31300::INT_REG_INT_CHANNEL_disabled;
            reg_int.int_X_threshold      = getThresholdLevel(32);
            reg_int.int_Y_threshold      = getThresholdLevel(128);
            reg_int.int_X_threshold      = 1;
            reg_int.int_Y_threshold      = 4;
            reg_int.int_Z_threshold      = 0;
            if (current_reg_int != reg_int) {
                i2cWrite(als31300::INT_REG, reg_int);
                [[maybe_unused]] auto ret = i2cWrite(als31300::INT_REG, reg_int);
                assert(ret);
                LOG_DEBUG("INT wrote:\t%" PRIu32, static_cast<uint32_t>(reg_int));

                i2cRead(als31300::INT_REG, read_reg);


@@ 165,8 188,17 @@ namespace bsp
                                              .irqMode  = DriverGPIOPinParams::InterruptMode::IntFallingEdge,
                                              .defLogic = 0,
                                              .pin      = static_cast<uint32_t>(BoardDefinitions::MAGNETOMETER_IRQ)});

            enableIRQ();
            // NOTE: irq not yet enabled
            // this version uses timer to poll the sensor
            // the timer requests to read the magnetometer periodically
            if (timerHandle == nullptr) {
                timerHandle = xTimerCreate(
                    "SliderTimer", pdMS_TO_TICKS(MAGNETOMETER_POLL_INTERVAL_MS), true, nullptr, TimerHandler);
                if (timerHandle == nullptr) {
                    LOG_FATAL("Could not create the timer for Headset insertion/removal detection");
                    return kStatus_Fail;
                }
            }

            // POWER register
            i2cRead(als31300::PWR_REG, read_reg);


@@ 175,12 207,13 @@ namespace bsp
            als31300::pwr_reg reg_pwr = current_reg_pwr;
            reg_pwr.I2C_loop_mode     = als31300::PWR_REG_LOOP_MODE_single; // we don't want constant data flow
            reg_pwr.sleep             = als31300::PWR_REG_SLEEP_MODE_active;
            reg_pwr.count_max_LP_mode = static_cast<std::uint8_t>(als31300::LPDCM_INACTIVE_TIME::inactive_100ms);
            reg_pwr.count_max_LP_mode = static_cast<uint8_t>(LPDCM_INACTIVE_TIME::inactive_10ms);

            i2cWrite(als31300::PWR_REG, reg_pwr);
            LOG_DEBUG("POWER wrote:\t%" PRIu32, static_cast<uint32_t>(reg_pwr));

            initFirstReadout();
            xTimerStart(timerHandle, 1000);

            return kStatus_Success;
        }



@@ 197,21 230,30 @@ namespace bsp

            if (reg_msb.int_flag == true) {
                LOG_DEBUG("magneto: INT flag in register");
                if (!i2cWrite(als31300::MEASUREMENTS_MSB_REG, reg_msb)) {
                    return std::make_pair(false, Measurements()); // todo: null opt
                }
            }
            Measurements meas;

            i2cRead(als31300::MEASUREMENTS_LSB_REG, read_reg);
            if (reg_msb.new_data_flag != als31300::MEAS_REG_NEW_DATA_available) {
                return std::make_pair(false, Measurements());
            }
            else {
                if (reg_msb.int_flag == true) {
                    // clear INT flag
                    if (!i2cWrite(als31300::MEASUREMENTS_MSB_REG, reg_msb)) {
                        return std::make_pair(false, Measurements()); // todo: null opt
                    }
                }
                Measurements meas;

                i2cRead(als31300::MEASUREMENTS_LSB_REG, read_reg);

            als31300::measurements_LSB_reg reg_lsb = read_reg;
                als31300::measurements_LSB_reg reg_lsb = read_reg;

            meas.X = als31300::measurement_sign_convert(reg_msb.X_MSB << 4 | reg_lsb.X_LSB);
            meas.Y = als31300::measurement_sign_convert(reg_msb.Y_MSB << 4 | reg_lsb.Z_LSB);
            meas.Z = als31300::measurement_sign_convert(reg_msb.Z_MSB << 4 | reg_lsb.Z_LSB);
                meas.X = als31300::measurement_sign_convert(reg_msb.X_MSB << 4 | reg_lsb.X_LSB);
                meas.Y = als31300::measurement_sign_convert(reg_msb.Y_MSB << 4 | reg_lsb.Z_LSB);
                meas.Z = als31300::measurement_sign_convert(reg_msb.Z_MSB << 4 | reg_lsb.Z_LSB);

            return std::pair(true, meas);
                return std::pair(true, meas);
            }
        }

        bool setActive(als31300::PWR_REG_SLEEP_MODE sleep_mode)


@@ 220,14 262,12 @@ namespace bsp
            als31300::whole_reg_t read_reg;

            if (!i2cRead(als31300::PWR_REG, read_reg)) {
                LOG_ERROR("setActive: cannot read PWR REG!");
                return false;
            }
            als31300::pwr_reg reg_pwr = read_reg;
            reg_pwr.sleep             = static_cast<std::uint8_t>(sleep_mode);
            reg_pwr.sleep             = sleep_mode;

            if (!i2cWrite(als31300::PWR_REG, reg_pwr)) {
                LOG_ERROR("setActive: cannot write PWR REG!");
                return false;
            }
            if (sleep_mode == als31300::PWR_REG_SLEEP_MODE::active ||


@@ 243,10 283,7 @@ namespace bsp
            addr.subAddress = 0x00;
            auto read       = i2c->Read(addr, &buf, 1);

            if (read != 1) {
                return false;
            }
            return true;
            return read == 1;
        }

        bsp::KeyCodes parse(const Measurements &measurements)


@@ 280,21 317,17 @@ namespace bsp
            }
            return bsp::KeyCodes::Undefined;
        }
        void initFirstReadout()
        void resetCurrentParsedValue()
        {
            current_parsed = bsp::KeyCodes::Undefined;
            if (qHandleIrq != NULL) {
                std::uint8_t val           = als31300::DUMMY_BYTE;
                constexpr auto ticksToWait = 1000;
                xQueueSend(qHandleIrq, &val, pdMS_TO_TICKS(ticksToWait));
            }
        }

        std::optional<bsp::KeyCodes> WorkerEventHandler()
        {
            // try to get new data from active magneto
            setActive(als31300::PWR_REG_SLEEP_MODE::active);
            auto [new_data, measurement] = getMeasurement();
            setActive(als31300::PWR_REG_SLEEP_MODE::periodic_active);
            setActive(als31300::PWR_REG_SLEEP_MODE::sleep);
            if (new_data) {
                auto incoming_parsed = parse(measurement);
                if (incoming_parsed != bsp::KeyCodes::Undefined and incoming_parsed != current_parsed) {


@@ 307,9 340,10 @@ namespace bsp

        BaseType_t IRQHandler()
        {
            gpio->DisableInterrupt(1 << static_cast<uint32_t>(BoardDefinitions::MAGNETOMETER_IRQ));
            BaseType_t xHigherPriorityTaskWoken = pdFALSE;
            if (qHandleIrq != NULL) {
                std::uint8_t val = als31300::DUMMY_BYTE;
                uint8_t val = 0x01;
                xQueueSendFromISR(qHandleIrq, &val, &xHigherPriorityTaskWoken);
            }
            return xHigherPriorityTaskWoken;

M module-bsp/board/rt1051/common/fsl_drivers/fsl_snvs_hp.c => module-bsp/board/rt1051/common/fsl_drivers/fsl_snvs_hp.c +0 -2
@@ 417,8 417,6 @@ void SNVS_HP_RTC_GetDatetime(SNVS_Type *base, snvs_hp_rtc_datetime_t *datetime)
 */
status_t SNVS_HP_RTC_SetAlarmSeconds(SNVS_Type *base, uint32_t alarmSeconds)
{
    assert(alarmTime != NULL);

    uint32_t currSeconds  = 0U;
    uint32_t tmp          = base->HPCR;


M module-bsp/board/rt1051/drivers/RT1051DriverI2C.cpp => module-bsp/board/rt1051/drivers/RT1051DriverI2C.cpp +0 -2
@@ 50,7 50,6 @@ namespace drivers
        auto ret = BOARD_LPI2C_Send(
            base, addr.deviceAddress, addr.subAddress, addr.subAddressSize, const_cast<uint8_t *>(txBuff), size);
        if (ret != kStatus_Success) {
            LOG_ERROR("I2C Write: Error %ld", ret);
            return -1; // TODO:M.P: fix me
        }
        else {


@@ 63,7 62,6 @@ namespace drivers
        cpp_freertos::LockGuard lock(mutex);
        auto ret = BOARD_LPI2C_Receive(base, addr.deviceAddress, addr.subAddress, addr.subAddressSize, rxBuff, size);
        if (ret != kStatus_Success) {
            LOG_ERROR("I2C Read: Error %ld", ret);
            return -1; // TODO:M.P: fix me
        }
        else {

M module-bsp/bsp/BoardDefinitions.hpp => module-bsp/bsp/BoardDefinitions.hpp +2 -3
@@ 18,9 18,8 @@ enum class BoardDefinitions

    USB_FUNCTION_MUX_SELECT = 25, // GPIO_AD_B1_09, USB_MUX_SEL0
    USB_POWER_ACK           = 3,  // GPIO_B0_03  Note: pull-up in order to read
    I2C_STD_BAUDRATE   = 100000,

    I2C_STD_BAUDRATE = 100000,
    I2C_FAST_BAUDRATE = 400000,
    AUDIOCODEC_I2C_BAUDRATE   = I2C_STD_BAUDRATE,
    AUDIOCODEC_I2C            = static_cast<int>(drivers::I2CInstances ::I2C2),
    AUDIOCODEC_DMAMUX         = static_cast<int>(drivers::DMAMuxInstances ::DMAMUX0),


@@ 107,7 106,7 @@ enum class BoardDefinitions
    VIBRATOR_EN = 0, // GPIO_AD_B0_00

	MAGNETOMETER_I2C = AUDIOCODEC_I2C,
	MAGNETOMETER_I2C_BAUDRATE = I2C_FAST_BAUDRATE,
	MAGNETOMETER_I2C_BAUDRATE = AUDIOCODEC_I2C_BAUDRATE,
    MAGNETOMETER_GPIO = static_cast<int>(drivers::GPIOInstances::GPIO_1),
    MAGNETOMETER_IRQ = 20, // GPIO_AD_B1_04


M module-bsp/bsp/battery-charger/battery_charger.hpp => module-bsp/bsp/battery-charger/battery_charger.hpp +2 -0
@@ 75,6 75,8 @@ namespace bsp::battery_charger

	void actionIfChargerUnplugged();

	int getVoltageFilteredMeasurement();

	BaseType_t INTB_IRQHandler();

	extern "C"

M module-bsp/bsp/magnetometer/magnetometer.hpp => module-bsp/bsp/magnetometer/magnetometer.hpp +1 -2
@@ 39,8 39,7 @@ namespace bsp

        bsp::KeyCodes parse(const Measurements &measurements);
        std::optional<bsp::KeyCodes> WorkerEventHandler();
        void initFirstReadout();
        bsp::KeyCodes getCurrentSliderPosition();
        void resetCurrentParsedValue();

        BaseType_t IRQHandler();
        void enableIRQ();

M module-cellular/at/Commands.hpp => module-cellular/at/Commands.hpp +16 -8
@@ 31,7 31,7 @@ namespace at
        URC_DELAY_ON,               /// Enable delay the output of URC indication until ring indicator pulse ends
        URC_UART1,                  /// Route URCs to UART1
        AT_PIN_READY_LOGIC,         /// Configure AP_Ready pin logic ( enable, logic level 1, 200ms )
        URC_NOTIF_SIGNAL,           /// Turn on signal strength change URC
        CSQ_URC_ON,                 /// Turn on signal strength change URC
        CRC_ON,                     /// Change incoming call notification from "RING" to "+CRING:type"
        CALLER_NUMBER_PRESENTATION, /// Turn on caller's number presentation
        SMS_TEXT_FORMAT,            /// Set Message format to Text


@@ 54,13 54,14 @@ namespace at
        CNUM, /// doc: the command can get the subscribers own number(s) from the (U)SI
        CIMI, /// Its getting IMSI from selected SIM card
        QCMGR,
        ATH,  /// hangup
        ATA,  /// (doc): timeout should be possibly set up to 90s
        ATD,  /// setup call
        IPR,  /// set baudrate
        CMUX, /// setup cmux params
        CFUN, /// set phone functionality
        CMGS, /// sms
        ATH,       /// hangup
        QHUP_BUSY, /// hangup all calls with busy reason
        ATA,       /// (doc): timeout should be possibly set up to 90s
        ATD,       /// setup call
        IPR,       /// set baudrate
        CMUX,      /// setup cmux params
        CFUN,      /// set phone functionality
        CMGS,      /// sms
        QCMGS,
        CREG,       /// network registration status
        QNWINFO,    /// network informations (band etc)


@@ 126,6 127,13 @@ namespace at
        QMBNCFG,  /// Quectel command for MBN files management
        QCFG_IMS, /// Set/Get IP Multimedia Services, get state of VoLTE
        QEEC,     /// Echo cancellation parameters
        RING_URC_OFF,
        RING_URC_ON,
        CSQ_URC_OFF,
        SMS_URC_OFF,
        SMS_URC_ON,
        ACT_URC_OFF,
        ACT_URC_ON,
    };

    enum class commadsSet

M module-cellular/at/src/ATFactory.cpp => module-cellular/at/src/ATFactory.cpp +12 -2
@@ 24,7 24,8 @@ namespace at
        {AT::URC_DELAY_ON, {"AT+QCFG=\"urc/delay\",1"}},
        {AT::URC_UART1, {"AT+QURCCFG=\"urcport\",\"uart1\""}},
        {AT::AT_PIN_READY_LOGIC, {"AT+QCFG=\"apready\",1,1,200"}},
        {AT::URC_NOTIF_SIGNAL, {"AT+QINDCFG=\"csq\",1"}},
        {AT::CSQ_URC_ON, {"AT+QINDCFG=\"csq\",1"}},
        {AT::CSQ_URC_OFF, {"AT+QINDCFG=\"csq\",0"}},
        {AT::CRC_ON, {"AT+CRC=1"}},
        {AT::CALLER_NUMBER_PRESENTATION, {"AT+CLIP=1", default_long_timeout}},
        {AT::SMS_TEXT_FORMAT, {"AT+CMGF=1"}},


@@ 41,6 42,7 @@ namespace at
        {AT::CIMI, {"AT+CIMI"}},
        {AT::QCMGR, {"AT+QCMGR=", 180s}},
        {AT::ATH, {"ATH", 100s}},
        {AT::QHUP_BUSY, {"AT+QHUP=17", 100s}},
        {AT::ATA, {"ATA", 100s}},
        {AT::ATD, {"ATD", 6s}},
        {AT::IPR, {"AT+IPR="}},


@@ 110,7 112,15 @@ namespace at
        {AT::QNVFR, {"AT+QNVFR=", default_long_timeout}},
        {AT::QNVFW, {"AT+QNVFW=", default_long_timeout}},
        {AT::QMBNCFG, {"AT+QMBNCFG=", default_long_timeout}},
        {AT::QCFG_IMS, {"AT+QCFG=\"ims\""}}};
        {AT::QCFG_IMS, {"AT+QCFG=\"ims\""}},
        {AT::RING_URC_ON, {"AT+QINDCFG=\"ring\",1"}},
        {AT::RING_URC_OFF, {"AT+QINDCFG=\"ring\",0"}},
        {AT::ACT_URC_OFF, {"AT+QINDCFG=\"act\",0"}},
        {AT::ACT_URC_ON, {"AT+QINDCFG=\"act\",1"}},
        {AT::SMS_URC_ON, {"AT+QINDCFG=\"smsincoming\",1"}},
        {AT::SMS_URC_OFF, {"AT+QINDCFG=\"smsincoming\",0"}},

    };

    auto factory(AT at) -> const Cmd &
    {

M module-cellular/at/src/Commands.cpp => module-cellular/at/src/Commands.cpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <Commands.hpp>


@@ 19,7 19,7 @@ namespace at
            ret.push_back(AT::URC_DELAY_ON);
            ret.push_back(AT::URC_UART1);
            ret.push_back(AT::AT_PIN_READY_LOGIC);
            ret.push_back(AT::URC_NOTIF_SIGNAL);
            ret.push_back(AT::CSQ_URC_ON);
            break;
        case commadsSet::simInit:
            ret.push_back(AT::CALLER_NUMBER_PRESENTATION);

M module-cellular/modem/mux/CellularMux.cpp => module-cellular/modem/mux/CellularMux.cpp +3 -1
@@ 14,11 14,13 @@
#include <service-cellular/CellularMessage.hpp>

#include <RTOSWrapper/include/ticks.hpp>
#include <SystemManager/messages/DeviceRegistrationMessage.hpp>
#include <time/time_conversion.hpp>

#include <gsl/gsl_util>

#include <memory>
#include <sstream>
#include <SystemManager/messages/DeviceRegistrationMessage.hpp>

std::map<TypeOfFrame_e, std::string> TypeOfFrame_text = {{TypeOfFrame_e::SABM, "SABM"},
                                                         {TypeOfFrame_e::UA, "UA"},

M module-db/CMakeLists.txt => module-db/CMakeLists.txt +1 -0
@@ 98,6 98,7 @@ set(SOURCES
        queries/calllog/QueryCalllogGetByContactID.cpp
        queries/notifications/QueryNotificationsGet.cpp
        queries/notifications/QueryNotificationsIncrement.cpp
        queries/notifications/QueryNotificationsMultipleIncrement.cpp
        queries/notifications/QueryNotificationsClear.cpp
        queries/notifications/QueryNotificationsGetAll.cpp
        queries/phonebook/QueryContactGet.cpp

M module-db/Interface/CalllogRecord.cpp => module-db/Interface/CalllogRecord.cpp +5 -1
@@ 25,6 25,10 @@ CalllogRecord::CalllogRecord(const CalllogTableRow &tableRow)
      phoneNumber(utils::PhoneNumber(tableRow.number, tableRow.e164number).getView()), isRead(tableRow.isRead)
{}

CalllogRecord::CalllogRecord(const CallType type, const utils::PhoneNumber::View &number)
    : presentation(PresentationType::PR_UNKNOWN), date(std::time(nullptr)), type(type), phoneNumber(number)
{}

uint32_t CalllogRecord::getContactId() const
{
    return contactId;


@@ 54,7 58,7 @@ CalllogRecordInterface::CalllogRecordInterface(CalllogDB *calllogDb, ContactsDB 

bool CalllogRecordInterface::Add(const CalllogRecord &rec)
{
    auto localRec      = rec;
    auto localRec = rec;
    if (!rec.phoneNumber.getFormatted().empty()) {
        ContactRecordInterface contactInterface(contactsDB);
        auto contactMatch =

M module-db/Interface/CalllogRecord.hpp => module-db/Interface/CalllogRecord.hpp +2 -1
@@ 29,7 29,8 @@ struct CalllogRecord : public Record
    friend std::ostream &operator<<(std::ostream &out, const CalllogRecord &point);
    [[nodiscard]] std::string str() const;

    CalllogRecord()  = default;
    CalllogRecord() = default;
    CalllogRecord(const CallType type, const utils::PhoneNumber::View &number);
    CalllogRecord(const CalllogTableRow &tableRow);

    uint32_t getContactId() const;

M module-db/Interface/NotificationsRecord.cpp => module-db/Interface/NotificationsRecord.cpp +54 -17
@@ 4,6 4,7 @@
#include "NotificationsRecord.hpp"
#include "module-db/queries/notifications/QueryNotificationsGet.hpp"
#include "module-db/queries/notifications/QueryNotificationsIncrement.hpp"
#include "module-db/queries/notifications/QueryNotificationsMultipleIncrement.hpp"
#include "module-db/queries/notifications/QueryNotificationsClear.hpp"
#include "module-db/queries/notifications/QueryNotificationsGetAll.hpp"
#include "Databases/NotificationsDB.hpp"


@@ 147,6 148,9 @@ std::unique_ptr<db::QueryResult> NotificationsRecordInterface::runQuery(std::sha
    if (const auto local_query = dynamic_cast<const db::query::notifications::Increment *>(query.get())) {
        return runQueryImpl(local_query);
    }
    if (const auto local_query = dynamic_cast<const db::query::notifications::MultipleIncrement *>(query.get())) {
        return runQueryImpl(local_query);
    }
    if (const auto local_query = dynamic_cast<const db::query::notifications::Clear *>(query.get())) {
        return runQueryImpl(local_query);
    }


@@ 166,25 170,26 @@ std::unique_ptr<db::query::notifications::GetResult> NotificationsRecordInterfac
std::unique_ptr<db::query::notifications::IncrementResult> NotificationsRecordInterface::runQueryImpl(
    const db::query::notifications::Increment *query)
{
    auto ret = false;
    if (auto record = GetByKey(query->getKey()); record.isValid()) {
        auto &currentContactRecord = record.contactRecord;
        if (auto numberMatch = contactsDb->MatchByNumber(query->getNumber()); numberMatch.has_value()) {
            if (record.value == 0) {
                currentContactRecord = std::move(numberMatch.value().contact);
            }
            else if (currentContactRecord.has_value() &&
                     numberMatch.value().contactId != currentContactRecord.value().ID) {
                currentContactRecord.reset();
            }
        }
        else {
            currentContactRecord.reset();
    auto ret = processIncrement(query->getKey(), query->getNumber(), 1);

    return std::make_unique<db::query::notifications::IncrementResult>(ret);
}

std::unique_ptr<db::query::notifications::MultipleIncrementResult> NotificationsRecordInterface::runQueryImpl(
    const db::query::notifications::MultipleIncrement *query)
{
    auto ret            = false;
    const auto &numbers = query->getNumbers();
    std::optional<utils::PhoneNumber::View> number;

    if (!numbers.empty()) {
        if (all_of(numbers.begin(), numbers.end(), [&](const auto &number) { return number == numbers.at(0); })) {
            number = std::make_optional(numbers.at(0));
        }
        record.value++;
        ret = Update(record);

        ret = processIncrement(query->getKey(), std::move(number), numbers.size());
    }
    return std::make_unique<db::query::notifications::IncrementResult>(ret);
    return std::make_unique<db::query::notifications::MultipleIncrementResult>(ret);
}

std::unique_ptr<db::query::notifications::ClearResult> NotificationsRecordInterface::runQueryImpl(


@@ 206,3 211,35 @@ std::unique_ptr<db::query::notifications::GetAllResult> NotificationsRecordInter
    auto records               = GetLimitOffset(0, numberOfNotifications);
    return std::make_unique<db::query::notifications::GetAllResult>(std::move(records));
}

bool NotificationsRecordInterface::processIncrement(NotificationsRecord::Key key,
                                                    std::optional<utils::PhoneNumber::View> &&number,
                                                    size_t size)
{
    auto ret = false;

    if (auto record = GetByKey(key); record.isValid() && number.has_value()) {
        auto &currentContactRecord = record.contactRecord;
        if (size == 1) {
            if (auto numberMatch = contactsDb->MatchByNumber(number.value()); numberMatch.has_value()) {
                if (record.value == 0) {
                    currentContactRecord = std::move(numberMatch.value().contact);
                }
                else if (currentContactRecord.has_value() &&
                         numberMatch.value().contactId != currentContactRecord.value().ID) {
                    currentContactRecord.reset();
                }
            }
            else {
                currentContactRecord.reset();
            }
        }
        else if (size > 1) {
            currentContactRecord.reset();
        }
        record.value += size;
        ret = Update(record);
    }

    return ret;
}

M module-db/Interface/NotificationsRecord.hpp => module-db/Interface/NotificationsRecord.hpp +9 -2
@@ 21,6 21,8 @@ namespace db::query::notifications
    class Get;
    class GetResult;
    class Increment;
    class MultipleIncrement;
    class MultipleIncrementResult;
    class IncrementResult;
    class Clear;
    class ClearResult;


@@ 44,7 46,7 @@ struct NotificationsRecord : public Record

    friend std::ostream &operator<<(std::ostream &out, const NotificationsRecord &point);

    NotificationsRecord()  = default;
    NotificationsRecord() = default;
    explicit NotificationsRecord(const NotificationsTableRow &tableRow,
                                 std::optional<ContactRecord> record = std::nullopt);



@@ 78,13 80,18 @@ class NotificationsRecordInterface : public RecordInterface<NotificationsRecord,
    std::unique_ptr<db::QueryResult> runQuery(std::shared_ptr<db::Query> query) override;

  private:
    NotificationsDB *notificationsDb = nullptr;
    NotificationsDB *notificationsDb   = nullptr;
    ContactRecordInterface *contactsDb = nullptr;

    std::optional<ContactRecord> getContactRecord(uint32_t id) const;
    std::unique_ptr<db::query::notifications::GetResult> runQueryImpl(const db::query::notifications::Get *query);
    std::unique_ptr<db::query::notifications::IncrementResult> runQueryImpl(
        const db::query::notifications::Increment *query);
    std::unique_ptr<db::query::notifications::MultipleIncrementResult> runQueryImpl(
        const db::query::notifications::MultipleIncrement *query);
    std::unique_ptr<db::query::notifications::ClearResult> runQueryImpl(const db::query::notifications::Clear *query);
    std::unique_ptr<db::query::notifications::GetAllResult> runQueryImpl(const db::query::notifications::GetAll *query);
    [[nodiscard]] bool processIncrement(NotificationsRecord::Key key,
                                        std::optional<utils::PhoneNumber::View> &&number,
                                        size_t size);
};

M module-db/Interface/SMSRecord.cpp => module-db/Interface/SMSRecord.cpp +0 -2
@@ 68,7 68,6 @@ bool SMSRecordInterface::Add(const SMSRecord &rec)
                                    .threadID  = thread.ID,
                                    .contactID = contactID,
                                    .date      = rec.date,
                                    .dateSent  = rec.dateSent,
                                    .errorCode = rec.errorCode,
                                    .body      = rec.body,
                                    .type      = rec.type


@@ 175,7 174,6 @@ bool SMSRecordInterface::Update(const SMSRecord &recUpdated)
                                  .threadID  = recCurrent.threadID,
                                  .contactID = recCurrent.contactID,
                                  .date      = recUpdated.date,
                                  .dateSent  = recUpdated.dateSent,
                                  .errorCode = recUpdated.errorCode,
                                  .body      = recUpdated.body,
                                  .type      = recUpdated.type});

M module-db/Interface/SMSRecord.hpp => module-db/Interface/SMSRecord.hpp +5 -6
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 23,7 23,6 @@ namespace db::query
struct SMSRecord : public Record
{
    uint32_t date      = 0;
    uint32_t dateSent  = 0;
    uint32_t errorCode = 0;
    UTF8 body;
    SMSType type       = SMSType::UNKNOWN;


@@ 33,12 32,12 @@ struct SMSRecord : public Record

    SMSRecord() = default;
    SMSRecord(const SMSTableRow &w)
        : Record(w.ID), date(w.date), dateSent(w.dateSent), errorCode(w.errorCode), body(w.body), type(w.type),
          threadID(w.threadID), contactID(w.contactID)
        : Record(w.ID), date(w.date), errorCode(w.errorCode), body(w.body), type(w.type), threadID(w.threadID),
          contactID(w.contactID)
    {}
    SMSRecord(const SMSTableRow &w, const utils::PhoneNumber::View &num)
        : Record(w.ID), date(w.date), dateSent(w.dateSent), errorCode(w.errorCode), body(w.body), type(w.type),
          threadID(w.threadID), contactID(w.contactID), number(num)
        : Record(w.ID), date(w.date), errorCode(w.errorCode), body(w.body), type(w.type), threadID(w.threadID),
          contactID(w.contactID), number(num)
    {}
};


M module-db/Tables/SMSTable.cpp => module-db/Tables/SMSTable.cpp +35 -47
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "SMSTable.hpp"


@@ 14,12 14,11 @@ bool SMSTable::create()

bool SMSTable::add(SMSTableRow entry)
{
    return db->execute("INSERT or ignore INTO sms ( thread_id,contact_id, date, date_send, error_code, body, "
                       "type ) VALUES (%lu,%lu,%lu,%lu,0,'%q',%d);",
    return db->execute("INSERT or ignore INTO sms ( thread_id,contact_id, date, error_code, body, "
                       "type ) VALUES (%lu,%lu,%lu,0,'%q',%d);",
                       entry.threadID,
                       entry.contactID,
                       entry.date,
                       entry.dateSent,
                       entry.body.c_str(),
                       entry.type);
}


@@ 54,12 53,11 @@ bool SMSTable::removeByField(SMSTableFields field, const char *str)

bool SMSTable::update(SMSTableRow entry)
{
    return db->execute("UPDATE sms SET thread_id = %lu, contact_id = %lu ,date = %lu, date_send = %lu, error_code = 0, "
    return db->execute("UPDATE sms SET thread_id = %lu, contact_id = %lu ,date = %lu, error_code = 0, "
                       "body = '%q', type =%d WHERE _id=%lu;",
                       entry.threadID,
                       entry.contactID,
                       entry.date,
                       entry.dateSent,
                       entry.body.c_str(),
                       entry.type,
                       entry.ID);


@@ 78,10 76,9 @@ SMSTableRow SMSTable::getById(uint32_t id)
        (*retQuery)[1].getUInt32(),                       // threadID
        (*retQuery)[2].getUInt32(),                       // contactID
        (*retQuery)[3].getUInt32(),                       // date
        (*retQuery)[4].getUInt32(),                       // dateSent
        (*retQuery)[5].getUInt32(),                       // errorCode
        (*retQuery)[6].getString(),                       // body
        static_cast<SMSType>((*retQuery)[7].getUInt32()), // type
        (*retQuery)[4].getUInt32(),                       // errorCode
        (*retQuery)[5].getString(),                       // body
        static_cast<SMSType>((*retQuery)[6].getUInt32()), // type
    };
}



@@ 101,10 98,9 @@ std::vector<SMSTableRow> SMSTable::getByContactId(uint32_t contactId)
            (*retQuery)[1].getUInt32(),                       // threadID
            (*retQuery)[2].getUInt32(),                       // contactID
            (*retQuery)[3].getUInt32(),                       // date
            (*retQuery)[4].getUInt32(),                       // dateSent
            (*retQuery)[5].getUInt32(),                       // errorCode
            (*retQuery)[6].getString(),                       // body
            static_cast<SMSType>((*retQuery)[7].getUInt32()), // type
            (*retQuery)[4].getUInt32(),                       // errorCode
            (*retQuery)[5].getString(),                       // body
            static_cast<SMSType>((*retQuery)[6].getUInt32()), // type
        });
    } while (retQuery->nextRow());



@@ 130,10 126,9 @@ std::vector<SMSTableRow> SMSTable::getByThreadId(uint32_t threadId, uint32_t off
            (*retQuery)[1].getUInt32(),                       // threadID
            (*retQuery)[2].getUInt32(),                       // contactID
            (*retQuery)[3].getUInt32(),                       // date
            (*retQuery)[4].getUInt32(),                       // dateSent
            (*retQuery)[5].getUInt32(),                       // errorCode
            (*retQuery)[6].getString(),                       // body
            static_cast<SMSType>((*retQuery)[7].getUInt32()), // type
            (*retQuery)[4].getUInt32(),                       // errorCode
            (*retQuery)[5].getString(),                       // body
            static_cast<SMSType>((*retQuery)[6].getUInt32()), // type
        });
    } while (retQuery->nextRow());



@@ 146,7 141,7 @@ std::vector<SMSTableRow> SMSTable::getByThreadIdWithoutDraftWithEmptyInput(uint3
{
    auto retQuery = db->query("SELECT * FROM sms WHERE thread_id= %u AND type != %u UNION ALL SELECT 0 as _id, 0 as "
                              "thread_id, 0 as contact_id, 0 as "
                              "date, 0 as date_send, 0 as error_code, 0 as body, %u as type LIMIT %u OFFSET %u",
                              "date, 0 as error_code, 0 as body, %u as type LIMIT %u OFFSET %u",
                              threadId,
                              SMSType::DRAFT,
                              SMSType::INPUT,


@@ 165,10 160,9 @@ std::vector<SMSTableRow> SMSTable::getByThreadIdWithoutDraftWithEmptyInput(uint3
            (*retQuery)[1].getUInt32(),                       // threadID
            (*retQuery)[2].getUInt32(),                       // contactID
            (*retQuery)[3].getUInt32(),                       // date
            (*retQuery)[4].getUInt32(),                       // dateSent
            (*retQuery)[5].getUInt32(),                       // errorCode
            (*retQuery)[6].getString(),                       // body
            static_cast<SMSType>((*retQuery)[7].getUInt32()), // type
            (*retQuery)[4].getUInt32(),                       // errorCode
            (*retQuery)[5].getString(),                       // body
            static_cast<SMSType>((*retQuery)[6].getUInt32()), // type
        });
    } while (retQuery->nextRow());



@@ 200,10 194,9 @@ SMSTableRow SMSTable::getDraftByThreadId(uint32_t threadId)
        (*retQuery)[1].getUInt32(),                       // threadID
        (*retQuery)[2].getUInt32(),                       // contactID
        (*retQuery)[3].getUInt32(),                       // date
        (*retQuery)[4].getUInt32(),                       // dateSent
        (*retQuery)[5].getUInt32(),                       // errorCode
        (*retQuery)[6].getString(),                       // body
        static_cast<SMSType>((*retQuery)[7].getUInt32()), // type
        (*retQuery)[4].getUInt32(),                       // errorCode
        (*retQuery)[5].getString(),                       // body
        static_cast<SMSType>((*retQuery)[6].getUInt32()), // type
    };
}



@@ 224,10 217,9 @@ std::vector<SMSTableRow> SMSTable::getByText(std::string text)
            (*retQuery)[1].getUInt32(),                       // threadID
            (*retQuery)[2].getUInt32(),                       // contactID
            (*retQuery)[3].getUInt32(),                       // date
            (*retQuery)[4].getUInt32(),                       // dateSent
            (*retQuery)[5].getUInt32(),                       // errorCode
            (*retQuery)[6].getString(),                       // body
            static_cast<SMSType>((*retQuery)[7].getUInt32()), // type
            (*retQuery)[4].getUInt32(),                       // errorCode
            (*retQuery)[5].getString(),                       // body
            static_cast<SMSType>((*retQuery)[6].getUInt32()), // type
        });
    } while (retQuery->nextRow());



@@ 249,10 241,9 @@ std::vector<SMSTableRow> SMSTable::getByText(std::string text, uint32_t threadId
            (*retQuery)[1].getUInt32(),                       // threadID
            (*retQuery)[2].getUInt32(),                       // contactID
            (*retQuery)[3].getUInt32(),                       // date
            (*retQuery)[4].getUInt32(),                       // dateSent
            (*retQuery)[5].getUInt32(),                       // errorCode
            (*retQuery)[6].getString(),                       // body
            static_cast<SMSType>((*retQuery)[7].getUInt32()), // type
            (*retQuery)[4].getUInt32(),                       // errorCode
            (*retQuery)[5].getString(),                       // body
            static_cast<SMSType>((*retQuery)[6].getUInt32()), // type
        });
    } while (retQuery->nextRow());
    return ret;


@@ 274,10 265,9 @@ std::vector<SMSTableRow> SMSTable::getLimitOffset(uint32_t offset, uint32_t limi
            (*retQuery)[1].getUInt32(),                       // threadID
            (*retQuery)[2].getUInt32(),                       // contactID
            (*retQuery)[3].getUInt32(),                       // date
            (*retQuery)[4].getUInt32(),                       // dateSent
            (*retQuery)[5].getUInt32(),                       // errorCode
            (*retQuery)[6].getString(),                       // body
            static_cast<SMSType>((*retQuery)[7].getUInt32()), // type
            (*retQuery)[4].getUInt32(),                       // errorCode
            (*retQuery)[5].getString(),                       // body
            static_cast<SMSType>((*retQuery)[6].getUInt32()), // type
        });
    } while (retQuery->nextRow());



@@ 323,10 313,9 @@ std::vector<SMSTableRow> SMSTable::getLimitOffsetByField(uint32_t offset,
            (*retQuery)[1].getUInt32(),                       // threadID
            (*retQuery)[2].getUInt32(),                       // contactID
            (*retQuery)[3].getUInt32(),                       // date
            (*retQuery)[4].getUInt32(),                       // dateSent
            (*retQuery)[5].getUInt32(),                       // errorCode
            (*retQuery)[6].getString(),                       // body
            static_cast<SMSType>((*retQuery)[7].getUInt32()), // type
            (*retQuery)[4].getUInt32(),                       // errorCode
            (*retQuery)[5].getString(),                       // body
            static_cast<SMSType>((*retQuery)[6].getUInt32()), // type
        });
    } while (retQuery->nextRow());



@@ 375,10 364,9 @@ std::pair<uint32_t, std::vector<SMSTableRow>> SMSTable::getManyByType(SMSType ty
                    (*retQuery)[1].getUInt32(),                       // threadID
                    (*retQuery)[2].getUInt32(),                       // contactID
                    (*retQuery)[3].getUInt32(),                       // date
                    (*retQuery)[4].getUInt32(),                       // dateSent
                    (*retQuery)[5].getUInt32(),                       // errorCode
                    (*retQuery)[6].getString(),                       // body
                    static_cast<SMSType>((*retQuery)[7].getUInt32()), // type
                    (*retQuery)[4].getUInt32(),                       // errorCode
                    (*retQuery)[5].getString(),                       // body
                    static_cast<SMSType>((*retQuery)[6].getUInt32()), // type
                });
            } while (retQuery->nextRow());
        }

M module-db/Tables/SMSTable.hpp => module-db/Tables/SMSTable.hpp +1 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 14,7 14,6 @@ struct SMSTableRow : public Record
    uint32_t threadID;
    uint32_t contactID;
    uint32_t date;
    uint32_t dateSent;
    uint32_t errorCode;
    UTF8 body;
    SMSType type;

A module-db/queries/notifications/QueryNotificationsMultipleIncrement.cpp => module-db/queries/notifications/QueryNotificationsMultipleIncrement.cpp +39 -0
@@ 0,0 1,39 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "QueryNotificationsMultipleIncrement.hpp"

namespace db::query::notifications
{
    MultipleIncrement::MultipleIncrement(NotificationsRecord::Key key,
                                         const std::vector<utils::PhoneNumber::View> &numbers)
        : Query(Query::Type::Update), key(key), numbers(numbers)
    {}

    auto MultipleIncrement::getKey() const noexcept -> NotificationsRecord::Key
    {
        return key;
    }
    auto MultipleIncrement::getNumbers() const noexcept -> const std::vector<utils::PhoneNumber::View> &
    {
        return numbers;
    }

    auto MultipleIncrement::debugInfo() const -> std::string
    {
        return "MultipleIncrement";
    }

    MultipleIncrementResult::MultipleIncrementResult(bool ret) : ret(ret)
    {}

    auto MultipleIncrementResult::getResult() const noexcept -> bool
    {
        return ret;
    }

    auto MultipleIncrementResult::debugInfo() const -> std::string
    {
        return "MultipleIncrementResult";
    }
} // namespace db::query::notifications

A module-db/queries/notifications/QueryNotificationsMultipleIncrement.hpp => module-db/queries/notifications/QueryNotificationsMultipleIncrement.hpp +38 -0
@@ 0,0 1,38 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <Interface/NotificationsRecord.hpp>
#include <Common/Query.hpp>
#include <string>
#include <PhoneNumber.hpp>

namespace db::query::notifications
{
    class MultipleIncrement : public Query
    {
        const NotificationsRecord::Key key;
        const std::vector<utils::PhoneNumber::View> numbers;

      public:
        MultipleIncrement(NotificationsRecord::Key key, const std::vector<utils::PhoneNumber::View> &numbers);

        [[nodiscard]] auto getKey() const noexcept -> NotificationsRecord::Key;
        [[nodiscard]] auto getNumbers() const noexcept -> const std::vector<utils::PhoneNumber::View> &;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class MultipleIncrementResult : public QueryResult
    {
        bool ret;

      public:
        explicit MultipleIncrementResult(bool ret);
        [[nodiscard]] auto getResult() const noexcept -> bool;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

} // namespace db::query::notifications

M module-db/tests/CMakeLists.txt => module-db/tests/CMakeLists.txt +3 -3
@@ 35,10 35,10 @@ add_catch2_executable(
        common.cpp
        
    LIBS
        module-db
        module-vfs
        module-sys
        iosyscalls
        module-db
    DEPS
        disk_image
        assets
)
add_subdirectory(test-initializer)

M module-db/tests/SMSRecord_tests.cpp => module-db/tests/SMSRecord_tests.cpp +0 -2
@@ 40,7 40,6 @@ TEST_CASE("SMS Record tests")
    SmsDB smsDB(smsPath.c_str());

    const uint32_t dateTest      = 123456789;
    const uint32_t dateSentTest  = 987654321;
    const uint32_t errorCodeTest = 555;
    auto numberTest              = utils::PhoneNumber("+48600123456", utils::country::Id::UNKNOWN).getView();
    auto numberTest2             = utils::PhoneNumber("222333444", utils::country::Id::UNKNOWN).getView();


@@ 52,7 51,6 @@ TEST_CASE("SMS Record tests")

    SMSRecord recordIN;
    recordIN.date      = dateTest;
    recordIN.dateSent  = dateSentTest;
    recordIN.errorCode = errorCodeTest;
    recordIN.number    = numberTest;
    recordIN.body      = bodyTest;

M module-db/tests/SMSTable_tests.cpp => module-db/tests/SMSTable_tests.cpp +0 -2
@@ 28,7 28,6 @@ TEST_CASE("SMS Table tests")
                            .threadID  = 0,
                            .contactID = 0,
                            .date      = 0,
                            .dateSent  = 0,
                            .errorCode = 0,
                            .body      = "Test SMS message 1",
                            .type      = SMSType ::INBOX


@@ 39,7 38,6 @@ TEST_CASE("SMS Table tests")
                            .threadID  = 0,
                            .contactID = 0,
                            .date      = 0,
                            .dateSent  = 0,
                            .errorCode = 0,
                            .body      = "Test Draft SMS",
                            .type      = SMSType ::DRAFT

M module-db/tests/ThreadRecord_tests.cpp => module-db/tests/ThreadRecord_tests.cpp +0 -2
@@ 218,7 218,6 @@ TEST_CASE("Thread Record tests")
        SMSRecordInterface smsRecInterface(&smsDB, &contactsDB);
        SMSRecord recordIN;
        recordIN.date      = 123456789;
        recordIN.dateSent  = 987654321;
        recordIN.errorCode = 0;
        recordIN.number    = phoneNumber.getView();
        recordIN.body      = "Ala";


@@ 274,7 273,6 @@ TEST_CASE("Thread Record tests")
        SMSRecordInterface smsRecInterface(&smsDB, &contactsDB);
        SMSRecord recordIN;
        recordIN.date      = 123456789;
        recordIN.dateSent  = 987654321;
        recordIN.errorCode = 0;
        recordIN.number    = phoneNumber.getView();
        recordIN.type      = SMSType ::DRAFT;

M module-db/tests/test-initializer/CMakeLists.txt => module-db/tests/test-initializer/CMakeLists.txt +3 -3
@@ 10,9 10,9 @@ add_catch2_executable(
        "${CMAKE_CURRENT_SOURCE_DIR}/tests-main.cpp"
        
    LIBS
        module-db
        module-vfs
        module-sys
        iosyscalls
        module-db
    DEPS
        disk_image
        assets
)

M module-gui/gui/widgets/TopBar/Time.cpp => module-gui/gui/widgets/TopBar/Time.cpp +2 -2
@@ 2,9 2,9 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "Time.hpp"
#include "time/time_conversion.hpp"
#include "Style.hpp"

#include <ctime>
namespace gui::top_bar
{
    Time::Time(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h)


@@ 21,7 21,7 @@ namespace gui::top_bar

    void Time::update()
    {
        _time.set_time(utils::time::getCurrentTimestamp().getTime());
        _time.set_time(std::time(nullptr));
        setText(_time.str());
    }


M module-gui/test/test-catch-text/CMakeLists.txt => module-gui/test/test-catch-text/CMakeLists.txt +2 -3
@@ 20,10 20,9 @@ add_catch2_executable(
                ..
                ../mock/
        LIBS
                module-vfs
                module-gui
                module-sys
                iosyscalls
                module-gui
        DEPS
                assets
                disk_image
)

M module-gui/test/test-catch/CMakeLists.txt => module-gui/test/test-catch/CMakeLists.txt +2 -3
@@ 14,10 14,9 @@ add_catch2_executable(
        INCLUDE
                ..
        LIBS
                module-gui
                module-vfs
                module-sys
                iosyscalls
                module-gui
        DEPS
                assets
                disk_image
)

M module-services/service-appmgr/model/ApplicationManager.cpp => module-services/service-appmgr/model/ApplicationManager.cpp +41 -4
@@ 17,6 17,8 @@
#include <application-call/ApplicationCall.hpp>
#include <application-special-input/ApplicationSpecialInput.hpp>
#include <application-desktop/ApplicationDesktop.hpp>
#include <application-onboarding/ApplicationOnBoarding.hpp>
#include <application-onboarding/data/OnBoardingMessages.hpp>
#include <i18n/i18n.hpp>
#include <log/log.hpp>
#include <service-appmgr/messages/Message.hpp>


@@ 437,7 439,7 @@ namespace app::manager
        });
        connect(typeid(locks::UnLockPhoneInput), [&](sys::Message *request) -> sys::MessagePointer {
            auto data = static_cast<locks::UnLockPhoneInput *>(request);
            return phoneLockHandler.verifyPhoneLockInput(data->getInputData());
            return phoneLockHandler.handlePhoneLockInput(data->getInputData());
        });
        connect(typeid(locks::EnablePhoneLock),
                [&](sys::Message *request) -> sys::MessagePointer { return phoneLockHandler.handleEnablePhoneLock(); });


@@ 463,6 465,14 @@ namespace app::manager
            auto req = static_cast<SetAutoLockTimeoutRequest *>(request);
            return handleAutoLockSetRequest(req);
        });
        connect(typeid(locks::ExternalUnLockPhone), [&](sys::Message *request) -> sys::MessagePointer {
            auto data = static_cast<locks::ExternalUnLockPhone *>(request);
            return phoneLockHandler.handleExternalUnlockRequest(data->getInputData());
        });
        connect(typeid(locks::ExternalPhoneLockAvailabilityChange), [&](sys::Message *request) -> sys::MessagePointer {
            auto data = static_cast<locks::ExternalPhoneLockAvailabilityChange *>(request);
            return phoneLockHandler.handleExternalAvailabilityChange(data->getAvailability());
        });

        // SimLock connects
        connect(typeid(cellular::msg::notification::SimNeedPin), [&](sys::Message *request) -> sys::MessagePointer {


@@ 506,7 516,7 @@ namespace app::manager
                [&](sys::Message *request) -> sys::MessagePointer {
                    auto data = static_cast<cellular::msg::request::sim::ChangePin::Response *>(request);
                    if (data->retCode) {
                        return simLockHandler.handleSimChangedMessage();
                        return simLockHandler.handleSimPinChangedMessage();
                    }
                    else {
                        return simLockHandler.handleSimPinChangeFailedRequest();


@@ 560,9 570,12 @@ namespace app::manager
                    }
                    return sys::msgNotHandled();
                });
        connect(typeid(cellular::msg::notification::SimReady),
                [&](sys::Message *request) -> sys::MessagePointer { return simLockHandler.handleSimReadyMessage(); });
        connect(typeid(cellular::StateChange), [&](sys::Message *request) -> sys::MessagePointer {
            auto data = static_cast<cellular::StateChange *>(request);
            if (data->request == cellular::service::State::ST::URCReady) {
                simLockHandler.setSimReady();
                simLockHandler.getSettingsSimSelect(
                    settings->getValue(settings::SystemProperties::activeSim, settings::SettingsScope::Global));
                return sys::msgHandled();


@@ 570,6 583,9 @@ namespace app::manager
            return sys::msgNotHandled();
        });

        connect(typeid(onBoarding::FinalizeOnBoarding),
                [&](sys::Message *request) -> sys::MessagePointer { return handleOnBoardingFinalize(); });

        connect(typeid(sdesktop::developerMode::DeveloperModeRequest),
                [&](sys::Message *request) -> sys::MessagePointer { return handleDeveloperModeRequest(request); });



@@ 777,9 793,9 @@ namespace app::manager

    auto ApplicationManager::handleHomeAction(ActionEntry &action) -> ActionProcessStatus
    {
        action.setTargetApplication(rootApplicationName);
        action.setTargetApplication(resolveHomeApplication());

        SwitchRequest switchRequest(ServiceName, rootApplicationName, resolveHomeWindow(), nullptr);
        SwitchRequest switchRequest(ServiceName, resolveHomeApplication(), resolveHomeWindow(), nullptr);
        return handleSwitchApplication(&switchRequest) ? ActionProcessStatus::Accepted : ActionProcessStatus::Dropped;
    }



@@ 789,6 805,27 @@ namespace app::manager
                                                : gui::name::window::main_window;
    }

    auto ApplicationManager::handleOnBoardingFinalize() -> sys::MessagePointer
    {
        settings->setValue(settings::SystemProperties::onboardingDone, utils::to_string(true));
        app::manager::Controller::sendAction(this, app::manager::actions::Home);
        return sys::msgHandled();
    }

    auto ApplicationManager::checkOnBoarding() -> bool
    {
        return not utils::getNumericValue<bool>(settings->getValue(settings::SystemProperties::onboardingDone));
    }

    auto ApplicationManager::resolveHomeApplication() -> std::string
    {
        if (checkOnBoarding()) {
            phoneLockHandler.handleUnlockRequest();
            return app::name_onboarding;
        }
        return rootApplicationName;
    }

    auto ApplicationManager::handleLaunchAction(ActionEntry &action) -> ActionProcessStatus
    {
        auto launchParams = static_cast<ApplicationLaunchData *>(action.params.get());

M module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp => module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp +3 -0
@@ 129,6 129,9 @@ namespace app::manager
        void handleActionRequest(ActionRequest *actionMsg);
        auto handleHomeAction(ActionEntry &action) -> ActionProcessStatus;
        auto resolveHomeWindow() -> std::string;
        auto handleOnBoardingFinalize() -> sys::MessagePointer;
        auto checkOnBoarding() -> bool;
        auto resolveHomeApplication() -> std::string;
        auto handleLaunchAction(ActionEntry &action) -> ActionProcessStatus;
        auto handleActionOnFocusedApp(ActionEntry &action) -> ActionProcessStatus;
        auto handlePhoneModeChangedAction(ActionEntry &action) -> ActionProcessStatus;

M module-services/service-audio/ServiceAudio.cpp => module-services/service-audio/ServiceAudio.cpp +31 -13
@@ 26,11 26,11 @@ using namespace audio;

inline constexpr auto audioServiceStackSize = 1024 * 4;

static constexpr auto defaultVolumeHigh  = "10";
static constexpr auto defaultVolumeLow   = "2";
static constexpr auto defaultVolumeMuted = "0";
static constexpr auto defaultTrue        = "1";
static constexpr auto defaultFalse       = "0";
static constexpr auto defaultVolumeHigh              = "10";
static constexpr auto defaultVolumeLow               = "2";
static constexpr auto defaultVolumeMuted             = "0";
static constexpr auto defaultTrue                    = "1";
static constexpr auto defaultFalse                   = "0";
static constexpr auto defaultCallRingtonePath        = "assets/audio/ringtone/ringtone_drum_2.mp3";
static constexpr auto defaultTextMessageRingtonePath = "assets/audio/sms/sms_drum_2.mp3";
static constexpr auto defaultNotificationsPath       = "assets/audio/alarm/alarm_hang_drum.mp3";


@@ 153,9 153,14 @@ std::optional<std::string> ServiceAudio::AudioServicesCallback(const sys::Messag
        return settings_it->second;
    }
    else if (const auto *deviceMsg = dynamic_cast<const AudioServiceMessage::AudioDeviceCreated *>(msg); deviceMsg) {
        if (deviceMsg->getDeviceType() == AudioDevice::Type::Bluetooth) {
        if (deviceMsg->getDeviceType() == AudioDevice::Type::BluetoothA2DP) {
            auto startBluetoothAudioMsg = std::make_shared<BluetoothAudioStartMessage>(
                std::static_pointer_cast<bluetooth::BluetoothAudioDevice>(deviceMsg->getDevice()));
                std::static_pointer_cast<bluetooth::A2DPAudioDevice>(deviceMsg->getDevice()));
            bus.sendUnicast(std::move(startBluetoothAudioMsg), service::name::bluetooth);
        }
        else if (deviceMsg->getDeviceType() == AudioDevice::Type::BluetoothHSP) {
            auto startBluetoothAudioMsg = std::make_shared<BluetoothAudioStartMessage>(
                std::static_pointer_cast<bluetooth::HSPAudioDevice>(deviceMsg->getDevice()));
            bus.sendUnicast(std::move(startBluetoothAudioMsg), service::name::bluetooth);
        }
    }


@@ 325,11 330,14 @@ std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleStart(const Operation:
    if (opType == Operation::Type::Playback) {
        auto input = audioMux.GetPlaybackInput(playbackType);
        // stop bluetooth stream if available
        if (bluetoothConnected) {
        if (bluetoothA2DPConnected || bluetoothHSPConnected) {
            if (playbackType == audio::PlaybackType::CallRingtone) {
                HandleSendEvent(std::make_shared<audio::Event>(EventType::BlutoothHSPDeviceState));

                LOG_DEBUG("Sending Bluetooth start ringing");
                bus.sendUnicast(std::make_shared<message::bluetooth::Ring>(message::bluetooth::Ring::State::Enable),
                                service::name::bluetooth);
                return std::make_unique<AudioStartPlaybackResponse>(audio::RetCode::Success, retToken);
            }
            else {
                LOG_DEBUG("Sending Bluetooth start stream request");


@@ 348,7 356,10 @@ std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleStart(const Operation:
    }
    else if (opType == Operation::Type::Router) {
        auto input = audioMux.GetRoutingInput(true);
        if (bluetoothConnected) {
        if (bluetoothA2DPConnected) {
            HandleSendEvent(std::make_shared<audio::Event>(EventType::BlutoothHSPDeviceState));
        }
        if (bluetoothHSPConnected) {
            LOG_DEBUG("Sending Bluetooth start routing");
            bus.sendUnicast(std::make_shared<message::bluetooth::StartAudioRouting>(), service::name::bluetooth);
        }


@@ 363,11 374,10 @@ std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleSendEvent(std::shared_
    // update bluetooth state
    if (evt->getType() == EventType::BlutoothA2DPDeviceState) {
        auto newState = evt->getDeviceState() == Event::DeviceState::Connected;
        if (newState != bluetoothConnected) {
        if (newState != bluetoothA2DPConnected) {
            LOG_DEBUG("Bluetooth connection status changed: %s", newState ? "connected" : "disconnected");
            bluetoothConnected = newState;
            bluetoothA2DPConnected = newState;
            HandleStop({audio::PlaybackType::Alarm,
                        audio::PlaybackType::CallRingtone,
                        audio::PlaybackType::Meditation,
                        audio::PlaybackType::Notifications,
                        audio::PlaybackType::TextMessageRingtone},


@@ 375,6 385,14 @@ std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleSendEvent(std::shared_
        }
    }

    if (evt->getType() == EventType::BlutoothHSPDeviceState) {
        auto newState = evt->getDeviceState() == Event::DeviceState::Connected;
        if (newState != bluetoothHSPConnected) {
            LOG_DEBUG("Bluetooth connection status changed: %s", newState ? "connected" : "disconnected");
            bluetoothHSPConnected = newState;
        }
    }

    // update information about endpoints availability
    for (auto &input : audioMux.GetAllInputs()) {
        input.audio->SendEvent(evt);


@@ 426,7 444,7 @@ std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleStop(const std::vector
    }

    // stop bluetooth stream if available
    if (bluetoothConnected) {
    if (bluetoothA2DPConnected) {
        LOG_DEBUG("Sending Bluetooth stop request");
        bus.sendUnicast(std::make_shared<BluetoothMessage>(BluetoothMessage::Request::Stop), service::name::bluetooth);
    }

M module-services/service-audio/service-audio/ServiceAudio.hpp => module-services/service-audio/service-audio/ServiceAudio.hpp +2 -1
@@ 57,7 57,8 @@ class ServiceAudio : public sys::Service
    audio::AudioMux::VibrationStatus vibrationMotorStatus = audio::AudioMux::VibrationStatus::Off;
    std::unique_ptr<settings::Settings> settingsProvider;
    std::map<std::string, std::string> settingsCache;
    bool bluetoothConnected = false;
    bool bluetoothA2DPConnected = false;
    bool bluetoothHSPConnected  = false;

    auto IsVibrationMotorOn()
    {

M module-services/service-cellular/CellularCall.cpp => module-services/service-cellular/CellularCall.cpp +4 -8
@@ 7,9 7,9 @@
#include <PhoneNumber.hpp>
#include <Utils.hpp>
#include <log/log.hpp>
#include <time/time_conversion.hpp>

#include <cinttypes>
#include <ctime>
#include <optional>
#include <sstream>
#include <stdexcept>


@@ 30,11 30,7 @@ namespace CellularCall
        }

        clear();
        CalllogRecord callRec;
        callRec.type        = type;
        callRec.date        = utils::time::Timestamp().getTime();
        callRec.presentation = PresentationType::PR_UNKNOWN;
        callRec.phoneNumber  = number;
        CalllogRecord callRec{type, number};
        call                = startCallAction ? startCallAction(callRec) : CalllogRecord();
        if (!call.isValid()) {
            LOG_ERROR("startCallAction failed");


@@ 51,7 47,7 @@ namespace CellularCall
    bool CellularCall::setActive()
    {
        if (isValid()) {
            startActiveTime = utils::time::Timestamp();
            startActiveTime = utils::time::getCurrentTimestamp();
            isActiveCall    = true;
            return true;
        }


@@ 70,7 66,7 @@ namespace CellularCall
        }

        if (isActiveCall) {
            auto endTime  = utils::time::Timestamp();
            auto endTime  = utils::time::getCurrentTimestamp();
            call.duration = (endTime - startActiveTime).get();
        }
        else {

M module-services/service-cellular/CellularUrcHandler.cpp => module-services/service-cellular/CellularUrcHandler.cpp +1 -1
@@ 154,7 154,7 @@ void CellularUrcHandler::Handle(Cpin &urc)
            LOG_INFO("Invalid cpin - ignore");
        }
        else {
            response = std::make_unique<cellular::internal::msg::SimStateChanged>(*state);
            response = std::make_unique<cellular::internal::msg::HandleATSimStateChange>(*state);
            urc.setHandled(true);
        }
    }

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +116 -8
@@ 58,6 58,7 @@
#include <at/UrcFactory.hpp>
#include <queries/messages/sms/QuerySMSSearchByType.hpp>
#include <queries/notifications/QueryNotificationsIncrement.hpp>
#include <queries/notifications/QueryNotificationsMultipleIncrement.hpp>
#include <projdefs.h>
#include <service-antenna/AntennaMessage.hpp>
#include <service-antenna/AntennaServiceAPI.hpp>


@@ 74,7 75,6 @@
#include <service-desktop/DeveloperModeMessage.hpp>
#include <service-appmgr/model/ApplicationManager.hpp>
#include <task.h>
#include <time/time_conversion.hpp>
#include <ucs2/UCS2.hpp>
#include <utf8/UTF8.hpp>



@@ 99,6 99,8 @@
#include <service-cellular/api/request/sim.hpp>
#include <service-cellular/api/notification/notification.hpp>

#include <ctime>

const char *ServiceCellular::serviceName = cellular::service::name;

inline constexpr auto cellularStack = 8000;


@@ 279,9 281,23 @@ void ServiceCellular::registerMessageHandlers()
    phoneModeObserver->connect(this);
    phoneModeObserver->subscribe(
        [this](sys::phone_modes::PhoneMode mode) { connectionManager->onPhoneModeChange(mode); });
    phoneModeObserver->subscribe([](sys::phone_modes::Tethering tethering) {
    phoneModeObserver->subscribe([&](sys::phone_modes::Tethering tethering) {
        using bsp::cellular::USB::setPassthrough;
        using bsp::cellular::USB::PassthroughState;
        if (tethering == sys::phone_modes::Tethering::On) {
            if (!tetheringTurnOffURC()) {
                LOG_ERROR("Failed to disable URC on tethering enable");
            }
        }
        else {
            if (!tetheringTurnOnURC()) {
                LOG_ERROR("Failed to enable URC on tethering disable");
            }
            if (!receiveAllMessages()) {
                LOG_ERROR("Failed to receive all sms after tethering disabling");
            }
            logTetheringCalls();
        }
        setPassthrough(tethering == sys::phone_modes::Tethering::On ? PassthroughState::ENABLED
                                                                    : PassthroughState::DISABLED);
    });


@@ 1116,8 1132,7 @@ auto ServiceCellular::receiveSMS(std::string messageNumber) -> bool
                // parse date
                tokens[3].erase(std::remove(tokens[3].begin(), tokens[3].end(), '\"'), tokens[3].end());

                utils::time::Timestamp time;
                auto messageDate = time.getTime();
                auto messageDate = std::time(nullptr);

                if (tokens.size() == 5) {
                    LOG_DEBUG("Single message");


@@ 1413,6 1428,7 @@ bool ServiceCellular::handle_modem_on()
    channel->cmd("AT+CCLK?");
    // inform host ap ready
    cmux->informModemHostWakeup();
    tetheringTurnOnURC();
    priv->state->set(State::ST::URCReady);
    LOG_DEBUG("AP ready");
    return true;


@@ 2048,6 2064,7 @@ auto ServiceCellular::handleCellularIncominCallMessage(sys::Message *msg) -> std

auto ServiceCellular::handleCellularCallerIdMessage(sys::Message *msg) -> std::shared_ptr<sys::ResponseMessage>
{

    auto message = static_cast<CellularCallerIdMessage *>(msg);
    ongoingCall.setNumber(message->number);
    return sys::MessageNone{};


@@ 2319,13 2336,19 @@ auto ServiceCellular::handleCellularRingNotification(sys::Message *msg) -> std::

auto ServiceCellular::handleCellularCallerIdNotification(sys::Message *msg) -> std::shared_ptr<sys::ResponseMessage>
{
    auto message = static_cast<CellularCallerIdNotification *>(msg);
    if (isIncommingCallAllowed()) {
        auto message = static_cast<CellularCallerIdNotification *>(msg);
        bus.sendMulticast(std::make_shared<CellularCallerIdMessage>(message->getNubmer()),
                          sys::BusChannel::ServiceCellularNotifications);
        return std::make_shared<CellularResponseMessage>(true);
    }
    return std::make_shared<CellularResponseMessage>(this->hangUpCall());
    else {
        if (phoneModeObserver->isTetheringOn()) {
            tetheringCalllog.push_back(CalllogRecord{CallType::CT_MISSED, message->getNubmer()});
            return std::make_shared<CellularResponseMessage>(hangUpCallBusy());
        }
    }
    return std::make_shared<CellularResponseMessage>(hangUpCall());
}

auto ServiceCellular::handleCellularSetConnectionFrequencyMessage(sys::Message *msg)


@@ 2343,13 2366,13 @@ auto ServiceCellular::handleCellularSetConnectionFrequencyMessage(sys::Message *

auto ServiceCellular::isIncommingCallAllowed() -> bool
{
    return phoneModeObserver->isInMode(sys::phone_modes::PhoneMode::Connected);
    return phoneModeObserver->isInMode(sys::phone_modes::PhoneMode::Connected) && !phoneModeObserver->isTetheringOn();
}

auto ServiceCellular::hangUpCall() -> bool
{
    auto channel = cmux->get(CellularMux::Channel::Commands);
    if (channel) {
    if (channel != nullptr) {
        if (channel->cmd(at::factory(at::AT::ATH))) {
            return true;
        }


@@ 2357,3 2380,88 @@ auto ServiceCellular::hangUpCall() -> bool
    LOG_ERROR("Failed to hang up call");
    return false;
}

auto ServiceCellular::hangUpCallBusy() -> bool
{
    auto channel = cmux->get(CellularMux::Channel::Commands);
    if (channel != nullptr) {
        if (channel->cmd(at::factory(at::AT::QHUP_BUSY))) {
            return true;
        }
    }
    LOG_ERROR("Failed to hang up call");
    return false;
}

auto ServiceCellular::tetheringTurnOffURC() -> bool
{
    auto channel = cmux->get(CellularMux::Channel::Commands);
    if (channel != nullptr) {
        if (!channel->cmd(at::factory(at::AT::CSQ_URC_OFF))) {
            LOG_ERROR("Failed to stop CSQ URC");
            return false;
        }
        if (!channel->cmd(at::factory(at::AT::ACT_URC_OFF))) {
            LOG_ERROR("Failed to stop ACT URC");
            return false;
        }
        if (!channel->cmd(at::factory(at::AT::SMS_URC_OFF))) {
            LOG_ERROR("Failed to stop SMS URC");
            return false;
        }
        if (!channel->cmd(at::factory(at::AT::RING_URC_OFF))) {
            LOG_ERROR("Failed to stop RING URC");
            return false;
        }
    }
    return true;
}

auto ServiceCellular::tetheringTurnOnURC() -> bool
{
    auto channel = cmux->get(CellularMux::Channel::Commands);
    if (channel != nullptr) {
        if (!channel->cmd(at::factory(at::AT::CSQ_URC_ON))) {
            LOG_ERROR("Failed to stop CSQ URC");
            return false;
        }
        if (!channel->cmd(at::factory(at::AT::ACT_URC_ON))) {
            LOG_ERROR("Failed to stop ACT URC");
            return false;
        }
        if (!channel->cmd(at::factory(at::AT::SMS_URC_ON))) {
            LOG_ERROR("Failed to stop SMS URC");
            return false;
        }

        if (!channel->cmd(at::factory(at::AT::RING_URC_ON))) {
            LOG_ERROR("Failed to stop RING URC");
            return false;
        }
    }
    return true;
}

auto ServiceCellular::logTetheringCalls() -> void
{
    if (!tetheringCalllog.empty()) {
        for (auto callRecord : tetheringCalllog) {
            auto call = DBServiceAPI::CalllogAdd(this, callRecord);
            if (call.ID == DB_ID_NONE) {
                LOG_ERROR("CalllogAdd failed");
            }
        }

        std::vector<utils::PhoneNumber::View> numbers;
        for (auto calllogRecord : tetheringCalllog) {
            numbers.push_back(calllogRecord.phoneNumber);
        }

        DBServiceAPI::GetQuery(
            this,
            db::Interface::Name::Notifications,
            std::make_unique<db::query::notifications::MultipleIncrement>(NotificationsRecord::Key::Calls, numbers));

        tetheringCalllog.clear();
    }
}

A module-services/service-cellular/include/service-cellular/api/message.hpp => module-services/service-cellular/include/service-cellular/api/message.hpp +38 -0
@@ 0,0 1,38 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <Service/Message.hpp>

namespace cellular::msg
{
    struct Request : public sys::msg::Request
    {
        const char *target() const final
        {
            return m_target;
        }

      private:
        static constexpr const char *m_target = cellular::service::name;
    };

    struct Response : public sys::msg::Response
    {
        explicit Response(bool retCode = true) : sys::msg::Response(retCode)
        {}
    };

    struct Notification : public sys::msg::Notification
    {
        sys::BusChannel channel() const final
        {
            return m_channel;
        }

      private:
        static constexpr sys::BusChannel m_channel = sys::BusChannel::ServiceCellularNotifications;
    };

} // namespace cellular::msg

M module-services/service-cellular/include/service-cellular/api/notification/notification.hpp => module-services/service-cellular/include/service-cellular/api/notification/notification.hpp +25 -38
@@ 3,49 3,36 @@

#pragma once

#include <Service/Message.hpp>

#include <service-cellular/api/common.hpp>
#include <service-cellular/api/message.hpp>

namespace cellular::msg
namespace cellular::msg::notification
{
    struct Notification : public sys::Message
    struct SimReady : public msg::Notification
    {};

    struct SimNeedPin : public msg::Notification
    {
        explicit SimNeedPin(unsigned int attempts) : attempts(attempts)
        {}
        const unsigned int attempts;
    };

    struct SimNeedPuk : public msg::Notification
    {
        Notification() : Message(Type::Data)
        explicit SimNeedPuk(unsigned int attempts) : attempts(attempts)
        {}
        const unsigned int attempts;
    };

    namespace notification
    struct SimBlocked : public msg::Notification
    {};

    struct UnhandledCME : public msg::Notification
    {
        struct SimReady : public msg::Notification
        {
            explicit SimReady(bool ready) : ready(ready)
            {}
            const bool ready;
        };

        struct SimNeedPin : public msg::Notification
        {
            explicit SimNeedPin(unsigned int attempts) : attempts(attempts)
            {}
            const unsigned int attempts;
        };

        struct SimNeedPuk : public msg::Notification
        {
            explicit SimNeedPuk(unsigned int attempts) : attempts(attempts)
            {}
            const unsigned int attempts;
        };

        struct SimBlocked : public msg::Notification
        {};

        struct UnhandledCME : public msg::Notification
        {
            explicit UnhandledCME(unsigned int code) : code(code)
            {}
            const unsigned int code;
        };
    } // namespace notification
} // namespace cellular::msg
        explicit UnhandledCME(unsigned int code) : code(code)
        {}
        const unsigned int code;
    };

} // namespace cellular::msg::notification

D module-services/service-cellular/include/service-cellular/api/request/request.hpp => module-services/service-cellular/include/service-cellular/api/request/request.hpp +0 -25
@@ 1,25 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <Service/Message.hpp>

#include <service-cellular/api/common.hpp>

namespace cellular::msg
{
    struct Request : public sys::Message
    {
        Request() : Message(Type::Data)
        {}
        static constexpr const char *target = cellular::service::name;
    };
    struct Response : public sys::Message
    {
        explicit Response(bool retCode = true) : Message(Type::Data), retCode(retCode)
        {}
        const bool retCode;
    };

} // namespace cellular::msg

M module-services/service-cellular/include/service-cellular/api/request/sim.hpp => module-services/service-cellular/include/service-cellular/api/request/sim.hpp +2 -1
@@ 3,7 3,8 @@

#pragma once

#include "request.hpp"
#include <service-cellular/api/common.hpp>
#include <service-cellular/api/message.hpp>

namespace cellular::msg::request::sim
{

M module-services/service-cellular/service-cellular/ServiceCellular.hpp => module-services/service-cellular/service-cellular/ServiceCellular.hpp +6 -0
@@ 138,6 138,7 @@ class ServiceCellular : public sys::Service
    std::vector<std::string> messageParts;

    CellularCall::CellularCall ongoingCall;
    std::vector<CalllogRecord> tetheringCalllog;

    ussd::State ussdState = ussd::State::none;



@@ 316,6 317,11 @@ class ServiceCellular : public sys::Service
    auto isIncommingCallAllowed() -> bool;

    auto hangUpCall() -> bool;
    auto hangUpCallBusy() -> bool;

    auto tetheringTurnOffURC() -> bool;
    auto tetheringTurnOnURC() -> bool;
    auto logTetheringCalls() -> void;

  private:
    std::unique_ptr<cellular::internal::ServiceCellularPriv> priv;

M module-services/service-cellular/src/ServiceCellularPriv.cpp => module-services/service-cellular/src/ServiceCellularPriv.cpp +13 -22
@@ 20,32 20,22 @@ namespace cellular::internal
    void ServiceCellularPriv::initSimCard()
    {
        using namespace cellular::msg;
        simCard->onSimReady = [this](bool ready) {
            // SIM causes SIM INIT, only on ready
            if (ready) {
                state->set(State::ST::SimInit);
            }
            owner->bus.sendMulticast(std::make_shared<notification::SimReady>(ready),
                                     sys::BusChannel::ServiceCellularNotifications);
        simCard->onSimReady = [this]() {
            state->set(State::ST::SimInit);
            owner->bus.sendMulticast<notification::SimReady>();
        };
        simCard->onNeedPin = [this](unsigned int attempts) {
            owner->bus.sendMulticast(std::make_shared<notification::SimNeedPin>(attempts),
                                     sys::BusChannel::ServiceCellularNotifications);
            owner->bus.sendMulticast<notification::SimNeedPin>(attempts);
        };
        simCard->onNeedPuk = [this](unsigned int attempts) {
            owner->bus.sendMulticast(std::make_shared<notification::SimNeedPuk>(attempts),
                                     sys::BusChannel::ServiceCellularNotifications);
        };
        simCard->onSimBlocked = [this]() {
            owner->bus.sendMulticast(std::make_shared<notification::SimBlocked>(),
                                     sys::BusChannel::ServiceCellularNotifications);
            owner->bus.sendMulticast<notification::SimNeedPuk>(attempts);
        };
        simCard->onSimBlocked = [this]() { owner->bus.sendMulticast<notification::SimBlocked>(); };
        simCard->onSimEvent = [this]() {
            owner->bus.sendUnicast(std::make_shared<sevm::SIMMessage>(), ::service::name::evt_manager);
        };
        simCard->onUnhandledCME = [this](unsigned int code) {
            owner->bus.sendMulticast(std::make_shared<notification::UnhandledCME>(code),
                                     sys::BusChannel::ServiceCellularNotifications);
            owner->bus.sendMulticast<notification::UnhandledCME>(code);
        };
    }



@@ 77,11 67,12 @@ namespace cellular::internal
            auto msg = static_cast<request::sim::PinUnlock *>(request);
            return std::make_shared<request::sim::PinUnlock::Response>(simCard->handlePinUnlock(msg->pin));
        });
        owner->connect(typeid(internal::msg::SimStateChanged), [&](sys::Message *request) -> sys::MessagePointer {
            auto msg = static_cast<internal::msg::SimStateChanged *>(request);
            simCard->handleSimStateChanged(msg->state);
            return sys::MessageNone{};
        });
        owner->connect(typeid(internal::msg::HandleATSimStateChange),
                       [&](sys::Message *request) -> sys::MessagePointer {
                           auto msg = static_cast<internal::msg::HandleATSimStateChange *>(request);
                           simCard->handleATSimStateChange(msg->state);
                           return sys::MessageNone{};
                       });
    }

} // namespace cellular::internal

M module-services/service-cellular/src/SimCard.cpp => module-services/service-cellular/src/SimCard.cpp +2 -4
@@ 89,7 89,7 @@ namespace cellular
            return processPinResult(supplyPin(_pin));
        }

        void SimCard::handleSimStateChanged(at::SimState state)
        void SimCard::handleATSimStateChange(at::SimState state)
        {
            handleSimState(state);
        }


@@ 100,12 100,10 @@ namespace cellular
            case at::SimState::Ready:
                Store::GSM::get()->sim = Store::GSM::get()->selected;
                if (onSimReady)
                    onSimReady(true);
                    onSimReady();
                break;
            case at::SimState::NotReady:
                Store::GSM::get()->sim = Store::GSM::SIM::SIM_FAIL;
                if (onSimReady)
                    onSimReady(false);
                break;
            case at::SimState::SimPin:
                [[fallthrough]];

M module-services/service-cellular/src/SimCard.hpp => module-services/service-cellular/src/SimCard.hpp +2 -2
@@ 86,12 86,12 @@ namespace cellular::service
        /**
         * Internal message handlers
         */
        void handleSimStateChanged(at::SimState state);
        void handleATSimStateChange(at::SimState state);

        /**
         * Notification events
         */
        std::function<void(bool ready)> onSimReady;
        std::function<void()> onSimReady;
        std::function<void(unsigned int attempts)> onNeedPin;
        std::function<void(unsigned int attempts)> onNeedPuk;
        std::function<void()> onSimBlocked;

M module-services/service-cellular/src/messages.hpp => module-services/service-cellular/src/messages.hpp +3 -3
@@ 3,14 3,14 @@

#pragma once

#include <service-cellular/api/request/request.hpp>
#include <service-cellular/api/message.hpp>
#include <at/SimState.hpp>

namespace cellular::internal::msg
{
    struct SimStateChanged : public cellular::msg::Request
    struct HandleATSimStateChange : public cellular::msg::Request
    {
        SimStateChanged(at::SimState state) : state(state)
        HandleATSimStateChange(at::SimState state) : state(state)
        {}
        at::SimState state;
    };

M module-services/service-db/agents/settings/SystemSettings.hpp => module-services/service-db/agents/settings/SystemSettings.hpp +1 -0
@@ 19,6 19,7 @@ namespace settings
        constexpr inline auto automaticTimeZoneIsOn    = "gs_automatic_time_zone_is_on";
        constexpr inline auto timeFormat               = "gs_time_format";
        constexpr inline auto dateFormat               = "gs_date_format";
        constexpr inline auto onboardingDone           = "gs_onboarding_done";
        constexpr inline auto eulaAccepted             = "gs_eula_accepted";
        constexpr inline auto osCurrentVersion         = "gs_os_current_version";
        constexpr inline auto osUpdateVersion          = "gs_os_update_version";

M module-services/service-desktop/CMakeLists.txt => module-services/service-desktop/CMakeLists.txt +1 -0
@@ 60,6 60,7 @@ target_link_libraries(${PROJECT_NAME}
        module-cellular
        crc32
        microtar
        utils-bootconfig
)

if (${ENABLE_TESTS})

M module-services/service-desktop/ServiceDesktop.cpp => module-services/service-desktop/ServiceDesktop.cpp +5 -3
@@ 22,8 22,6 @@
#include <module-sys/SystemManager/SystemManager.hpp>
#include <module-sys/Timers/TimerFactory.hpp>

#include <cinttypes>
#include <filesystem>
#include <module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp>
#include <module-services/service-db/agents/settings/SystemSettings.hpp>
#include <module-sys/SystemManager/Constants.hpp>


@@ 34,6 32,10 @@
#include <sys/mount.h>
#include <sys/statvfs.h>

#include <ctime>
#include <cinttypes>
#include <filesystem>

namespace
{
    bool RemountFS(bool readOnly = false, std::string path = std::string(purefs::dir::getRootDiskPath()))


@@ 348,7 350,7 @@ void ServiceDesktop::storeHistory(const std::string &historyValue)
void ServiceDesktop::prepareBackupData()
{
    backupRestoreStatus.operation = ServiceDesktop::Operation::Backup;
    backupRestoreStatus.task      = std::to_string(static_cast<uint32_t>(utils::time::getCurrentTimestamp().getTime()));
    backupRestoreStatus.task          = std::to_string(static_cast<uint32_t>(std::time(nullptr)));
    backupRestoreStatus.state     = OperationState::Stopped;
    backupRestoreStatus.backupTempDir = purefs::dir::getTemporaryPath() / backupRestoreStatus.task;
}

M module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.cpp => module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.cpp +3 -2
@@ 21,11 21,12 @@
#include <module-sys/SystemManager/Constants.hpp>

#include <service-db/DBServiceAPI.hpp>
#include <time/time_conversion.hpp>
#include <service-desktop/parser/MessageHandler.hpp>
#include <service-desktop/endpoints/developerMode/event/ATRequest.hpp>
#include <service-appmgr/service-appmgr/Controller.hpp>

#include <ctime>

namespace parserFSM
{
    class Context;


@@ 283,7 284,7 @@ auto DeveloperModeHelper::smsRecordFromJson(json11::Json msgJson) -> SMSRecord
    record.type = static_cast<SMSType>(msgJson[json::messages::messageType].int_value());
    utils::PhoneNumber phoneNumber(msgJson[json::messages::phoneNumber].string_value());
    record.number = phoneNumber.getView();
    record.date   = utils::time::getCurrentTimestamp().getTime();
    record.date   = std::time(nullptr);
    record.body   = UTF8(msgJson[json::messages::messageBody].string_value());
    return record;
}

M module-services/service-desktop/endpoints/deviceInfo/DeviceInfoEndpoint.cpp => module-services/service-desktop/endpoints/deviceInfo/DeviceInfoEndpoint.cpp +3 -2
@@ 9,7 9,6 @@
#include <common_data/EventStore.hpp>
#include <json/json11.hpp>
#include <source/version.hpp>
#include <time/time_conversion.hpp>
#include <service-desktop/service-desktop/ServiceDesktop.hpp>
#include <version.hpp>



@@ 18,6 17,8 @@
#include <sys/statvfs.h>
#include <purefs/filesystem_paths.hpp>

#include <ctime>

using namespace parserFSM;

auto DeviceInfoEndpoint::handle(Context &context) -> void


@@ 61,7 62,7 @@ auto DeviceInfoEndpoint::getDeviceInfo(Context &context) -> bool
         {json::gitTag, (std::string)GIT_TAG},
         {json::gitBranch, (std::string)GIT_BRANCH},
         {json::updateHistory, updateHistory},
         {json::currentRTCTime, std::to_string(static_cast<uint32_t>(utils::time::getCurrentTimestamp().getTime()))},
         {json::currentRTCTime, std::to_string(static_cast<uint32_t>(std::time(nullptr)))},
         {json::version, std::string(VERSION)}}));

    MessageHandler::putToSendQueue(context.createSimpleResponse());

M module-services/service-desktop/endpoints/messages/MessageHelper.cpp => module-services/service-desktop/endpoints/messages/MessageHelper.cpp +1 -2
@@ 41,8 41,7 @@ namespace parserFSM
    {

        auto recordEntry = json11::Json::object{{json::messages::contactID, static_cast<int>(record.contactID)},
                                                {json::messages::receivedAt, static_cast<int>(record.date)},
                                                {json::messages::sentAt, static_cast<int>(record.dateSent)},
                                                {json::messages::createdAt, static_cast<int>(record.date)},
                                                {json::messages::messageID, static_cast<int>(record.ID)},
                                                {json::messages::messageBody, record.body.c_str()},
                                                {json::messages::messageType, static_cast<int>(record.type)},

M module-services/service-desktop/endpoints/update/UpdateMuditaOS.cpp => module-services/service-desktop/endpoints/update/UpdateMuditaOS.cpp +4 -4
@@ 11,7 11,6 @@
#include <application-desktop/Constants.hpp>
#include <service-db/service-db/Settings.hpp>
#include <purefs/filesystem_paths.hpp>
#include <time/time_conversion.hpp>
#include <filesystem>
#include <Utils.hpp>
#include <boot/bootconfig.hpp>


@@ 26,8 25,9 @@
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <memory>
#include <ctime>
#include <fstream>
#include <memory>

FileInfo::FileInfo(mtar_header_t &h, unsigned long crc32) : fileSize(h.size), fileCRC32(crc32)
{


@@ 93,7 93,7 @@ updateos::UpdateError UpdateMuditaOS::runUpdate()
{
    informDebug("Preparing temp dir");

    updateRunStatus.startTime   = utils::time::getCurrentTimestamp().getTime();
    updateRunStatus.startTime   = static_cast<uint32_t>(std::time(nullptr));
    updateRunStatus.fromVersion = bootConfig.to_json()[boot::json::git_info];
    versionInformation          = UpdateMuditaOS::getVersionInfoFromFile(updateFile);



@@ 166,7 166,7 @@ updateos::UpdateError UpdateMuditaOS::runUpdate()
        informError(err, "runUpdate cleanupAfterUpdate failed, resetting anyway");
    }

    updateRunStatus.endTime = utils::time::Time().getTime();
    updateRunStatus.endTime = static_cast<uint32_t>(std::time(nullptr));
    storeRunStatusInDB();

    // reboot always

M module-services/service-desktop/parser/ParserUtils.hpp => module-services/service-desktop/parser/ParserUtils.hpp +1 -2
@@ 155,8 155,7 @@ namespace parserFSM
            inline constexpr auto messageID          = "messageID";
            inline constexpr auto messageType        = "messageType";
            inline constexpr auto phoneNumber        = "phoneNumber";
            inline constexpr auto receivedAt         = "receivedAt";
            inline constexpr auto sentAt             = "sentAt";
            inline constexpr auto createdAt          = "createdAt";
            inline constexpr auto lastUsedAt         = "lastUsedAt";
            inline constexpr auto lastUpdatedAt      = "lastUpdatedAt";
            inline constexpr auto isUnread           = "isUnread";

M module-services/service-desktop/tests/unittest.cpp => module-services/service-desktop/tests/unittest.cpp +1 -3
@@ 187,7 187,6 @@ TEST_CASE("DB Helpers test - json encoding (messages)")
    message->body      = "test message";
    message->contactID = 1;
    message->date      = 12345;
    message->dateSent  = 54321;
    message->errorCode = 0;
    message->number    = contactNum.number;
    message->threadID  = 1;


@@ 198,8 197,7 @@ TEST_CASE("DB Helpers test - json encoding (messages)")

    REQUIRE(messageJson[json::messages::messageBody] == "test message");
    REQUIRE(messageJson[json::messages::contactID] == 1);
    REQUIRE(messageJson[json::messages::receivedAt] == 12345);
    REQUIRE(messageJson[json::messages::sentAt] == 54321);
    REQUIRE(messageJson[json::messages::createdAt] == 12345);
    REQUIRE(messageJson[json::messages::threadID] == 1);
    REQUIRE(messageJson[json::messages::messageID] == 10);


M module-services/service-evtmgr/CMakeLists.txt => module-services/service-evtmgr/CMakeLists.txt +1 -0
@@ 7,6 7,7 @@ set(SOURCES
        api/EventManagerServiceAPI.cpp
        battery-level-check/BatteryLevelCheck.cpp
        backlight-handler/BacklightHandler.cpp
        battery-brownout-detector/BatteryBrownoutDetector.cpp
        screen-light-control/ControlFunctions.cpp
        screen-light-control/ScreenLightControl.cpp
        vibra/Vibra.cpp

M module-services/service-evtmgr/EventManager.cpp => module-services/service-evtmgr/EventManager.cpp +35 -19
@@ 222,30 222,28 @@ sys::ReturnCodes EventManager::InitHandler()

    connect(typeid(sevm::ScreenLightControlMessage), [&](sys::Message *msgl) {
        auto *m = dynamic_cast<sevm::ScreenLightControlMessage *>(msgl);
        backlightHandler.processScreenRequest(m->action);
        backlightHandler.processScreenRequest(m->getAction(), screen_light_control::Parameters());
        return sys::msgHandled();
    });

    connect(typeid(sevm::ScreenLightSetParameters), [&](sys::Message *msgl) {
        auto *m = dynamic_cast<sevm::ScreenLightSetParameters *>(msgl);
        backlightHandler.processScreenRequest(m->action, std::move(m->parameters));
    connect(typeid(sevm::ScreenLightSetAutoModeParams), [&](sys::Message *msgl) {
        auto *m = static_cast<sevm::ScreenLightSetAutoModeParams *>(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<sevm::ScreenLightSetManualModeParams *>(msgl);
        backlightHandler.processScreenRequest(m->getAction(), screen_light_control::Parameters(m->getParams()));
        return sys::msgHandled();
    });

    connect(sevm::ScreenLightControlRequestParameters(), [&](sys::Message *msgl) {
        screen_light_control::Parameters params = {backlightHandler.getScreenBrightnessValue()};
        auto msg                                = std::make_shared<sevm::ScreenLightControlParametersResponse>(
        screen_light_control::ManualModeParameters params = {backlightHandler.getScreenBrightnessValue()};
        auto msg = std::make_shared<sevm::ScreenLightControlParametersResponse>(
            backlightHandler.getScreenLightState(), backlightHandler.getScreenAutoModeState(), params);
        return msg;
    });
    connect(sevm::RtcUpdateTimeMessage(0), [&](sys::Message *msgl) {
        auto msg = static_cast<sevm::RtcUpdateTimeMessage *>(msgl);
        bsp::rtc::setDateTimeFromTimestamp(msg->getTime());
        bsp::rtc::setMinuteAlarm(msg->getTime());
        handleMinuteUpdate(msg->getTime());
        return sys::msgHandled();
    });

    connect(sevm::BatteryStatusChangeMessage(), [&](sys::Message *msgl) {
        if (msgl->sender == this->GetName()) {
            LOG_INFO("Battery level: %d , charging: %d",


@@ 284,9 282,15 @@ sys::ReturnCodes EventManager::InitHandler()
        return sys::MessageNone{};
    });

    connect(typeid(stm::message::UpdateRTCValueMessage), [&](sys::Message *msg) {
        auto message = static_cast<stm::message::UpdateRTCValueMessage *>(msg);
        processRTCRequest(message->getTime());
    connect(typeid(stm::message::UpdateRTCValueFromTmMessage), [&](sys::Message *msg) {
        auto message = static_cast<stm::message::UpdateRTCValueFromTmMessage *>(msg);
        processRTCFromTmRequest(message->getTime());
        return sys::MessageNone{};
    });

    connect(typeid(stm::message::UpdateRTCValueFromTimestampMessage), [&](sys::Message *msg) {
        auto message = static_cast<stm::message::UpdateRTCValueFromTimestampMessage *>(msg);
        processRTCFromTimestampRequest(message->getTime());
        return sys::MessageNone{};
    });



@@ 412,7 416,7 @@ void EventManager::toggleTorchColor()
    }
}

void EventManager::processRTCRequest(struct tm &newTime)
void EventManager::processRTCFromTmRequest(struct tm &newTime)
{
    if (bsp::rtc::setDateTime(&newTime) != bsp::rtc::ErrorCode::OK) {
        LOG_ERROR("Setting RTC failed.");


@@ 423,6 427,18 @@ void EventManager::processRTCRequest(struct tm &newTime)
    auto notification = std::make_shared<sys::DataMessage>(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<sys::DataMessage>(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) {


@@ 431,4 447,4 @@ void EventManager::processTimezoneRequest(const std::string &timezone)
    }
    auto notification = std::make_shared<sys::DataMessage>(MessageType::EVMTimeUpdated);
    bus.sendMulticast(std::move(notification), sys::BusChannel::ServiceEvtmgrNotifications);
}
\ No newline at end of file
}

M module-services/service-evtmgr/WorkerEvent.cpp => module-services/service-evtmgr/WorkerEvent.cpp +61 -36
@@ 45,6 45,18 @@ extern "C"
#include <string>      // for string
#include <vector>      // for vector

WorkerEvent::WorkerEvent(sys::Service *service)
    : sys::Worker(service, stackDepthBytes), service(service),
      batteryBrownoutDetector(
          service,
          []() { return bsp::battery_charger::getVoltageFilteredMeasurement(); },
          [service]() {
              auto messageBrownout = std::make_shared<sevm::BatteryBrownoutMessage>();
              service->bus.sendUnicast(std::move(messageBrownout), service::name::system_manager);
          },
          [this]() { checkBatteryChargerInterrupts(); })
{}

bool WorkerEvent::handleMessage(uint32_t queueID)
{



@@ 98,41 110,7 @@ bool WorkerEvent::handleMessage(uint32_t queueID)
            return false;
        }
        if (notification == static_cast<std::uint8_t>(bsp::battery_charger::batteryIRQSource::INTB)) {
            auto topINT = bsp::battery_charger::getTopControllerINTSource();
            if (topINT & static_cast<std::uint8_t>(bsp::battery_charger::topControllerIRQsource::CHGR_INT)) {
                bsp::battery_charger::getChargeStatus();
                bsp::battery_charger::actionIfChargerUnplugged();
                auto message = std::make_shared<sevm::BatteryStatusChangeMessage>();
                service->bus.sendUnicast(std::move(message), service::name::evt_manager);
                battery_level_check::checkBatteryLevel();
                bsp::battery_charger::clearAllChargerIRQs();
            }
            if (topINT & static_cast<std::uint8_t>(bsp::battery_charger::topControllerIRQsource::FG_INT)) {
                const auto status = bsp::battery_charger::getStatusRegister();
                if (status & static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::minVAlert)) {
                    auto messageBrownout = std::make_shared<sevm::BatteryBrownoutMessage>();
                    service->bus.sendUnicast(std::move(messageBrownout), service::name::system_manager);
                }
                if (status & static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::SOCOnePercentChange)) {
                    bsp::battery_charger::clearFuelGuageIRQ(
                        static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::SOCOnePercentChange));
                    bsp::battery_charger::getBatteryLevel();
                    auto message = std::make_shared<sevm::BatteryStatusChangeMessage>();
                    service->bus.sendUnicast(std::move(message), service::name::evt_manager);
                    battery_level_check::checkBatteryLevel();
                }
                if (status & static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::maxTemp) ||
                    status & static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::minTemp)) {
                    bsp::battery_charger::clearFuelGuageIRQ(
                        static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::maxTemp) |
                        static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::minTemp));
                    bsp::battery_charger::checkTemperatureRange();
                    bsp::battery_charger::getChargeStatus();
                    auto message = std::make_shared<sevm::BatteryStatusChangeMessage>();
                    service->bus.sendUnicast(std::move(message), service::name::evt_manager);
                    battery_level_check::checkBatteryLevel();
                }
            }
            checkBatteryChargerInterrupts();
        }
    }



@@ 184,7 162,10 @@ bool WorkerEvent::handleMessage(uint32_t queueID)
        if (!queue->Dequeue(&notification, 0)) {
            return false;
        }
        processKeyEvent(bsp::KeyEvents::Pressed, bsp::magnetometer::getCurrentSliderPosition());

        bsp::magnetometer::resetCurrentParsedValue();
        LOG_WARN("Received notify, current value reset!");
        handleMagnetometerEvent();
    }

    if (queueID == static_cast<uint32_t>(WorkerEventQueues::queueMagnetometerIRQ)) {


@@ 244,6 225,7 @@ bool WorkerEvent::init(std::list<sys::WorkerQueueInfo> queuesList)

    auto sentinelRegistrationMsg = std::make_shared<sys::SentinelRegistrationMessage>(cpuSentinel);
    service->bus.sendUnicast(std::move(sentinelRegistrationMsg), service::name::system_manager);

    return true;
}



@@ 327,6 309,49 @@ void WorkerEvent::handleMagnetometerEvent()
    }
}

void WorkerEvent::checkBatteryChargerInterrupts()
{
    auto topINT = bsp::battery_charger::getTopControllerINTSource();
    if (topINT & static_cast<std::uint8_t>(bsp::battery_charger::topControllerIRQsource::CHGR_INT)) {
        bsp::battery_charger::getChargeStatus();
        bsp::battery_charger::actionIfChargerUnplugged();
        auto message = std::make_shared<sevm::BatteryStatusChangeMessage>();
        service->bus.sendUnicast(std::move(message), service::name::evt_manager);
        battery_level_check::checkBatteryLevel();
        bsp::battery_charger::clearAllChargerIRQs();
    }
    if (topINT & static_cast<std::uint8_t>(bsp::battery_charger::topControllerIRQsource::FG_INT)) {
        const auto status = bsp::battery_charger::getStatusRegister();
        if (status & static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::minVAlert)) {
            batteryBrownoutDetector.startDetection();
            bsp::battery_charger::clearFuelGuageIRQ(
                static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::minVAlert));
        }
        if (status & static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::SOCOnePercentChange)) {
            bsp::battery_charger::clearFuelGuageIRQ(
                static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::SOCOnePercentChange));
            bsp::battery_charger::getBatteryLevel();
            auto message = std::make_shared<sevm::BatteryStatusChangeMessage>();
            service->bus.sendUnicast(std::move(message), service::name::evt_manager);
            battery_level_check::checkBatteryLevel();
        }
        if (status & static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::maxTemp) ||
            status & static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::minTemp)) {
            bsp::battery_charger::clearFuelGuageIRQ(
                static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::maxTemp) |
                static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::minTemp));
            bsp::battery_charger::checkTemperatureRange();
            bsp::battery_charger::getChargeStatus();
            auto message = std::make_shared<sevm::BatteryStatusChangeMessage>();
            service->bus.sendUnicast(std::move(message), service::name::evt_manager);
            battery_level_check::checkBatteryLevel();
        }
        // Clear other unsupported IRQ sources just in case
        bsp::battery_charger::clearFuelGuageIRQ(
            static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::all));
    }
}

bsp::KeyCodes WorkerEvent::headsetKeyToKeyboardKey(uint8_t headsetKeyCode)
{
    switch (headsetKeyCode) {

M module-services/service-evtmgr/backlight-handler/BacklightHandler.cpp => module-services/service-evtmgr/backlight-handler/BacklightHandler.cpp +53 -36
@@ 18,18 18,18 @@ namespace backlight
    } // namespace timers

    Handler::Handler(std::shared_ptr<settings::Settings> settings, sys::Service *parent)
        : settings{settings}, screenLightControl{std::make_unique<screen_light_control::ScreenLightControl>(settings,
                                                                                                            parent)},
        : settings{std::move(settings)}, screenLightControl{std::make_unique<screen_light_control::ScreenLightControl>(
                                             parent)},
          keypadLightTimer{
              sys::TimerFactory::createSingleShotTimer(parent,
                                                       timers::keypadLightTimerName,
                                                       timers::keypadLightTimerTimeout,
                                                       [this](sys::Timer &) { bsp::keypad_backlight::shutdown(); })},
          screenLightTimer{sys::TimerFactory::createSingleShotTimer(
              parent, timers::screenLightTimerName, timers::keypadLightTimerTimeout, [this](sys::Timer &) {
                  if (utils::getNumericValue<bool>(getValue(settings::Brightness::state)) &&
                      utils::getNumericValue<bool>(getValue(settings::Brightness::autoMode)) &&
                      screenLightControl->getLightState()) {
              parent, timers::screenLightTimerName, timers::keypadLightTimerTimeout, [this](sys::Timer &t) {
                  if (getScreenLightState() &&
                      getScreenAutoModeState() == screen_light_control::ScreenLightMode::Manual &&
                      screenLightControl->isLightOn()) {
                      screenLightControl->processRequest(screen_light_control::Action::turnOff);
                  }
              })}


@@ 38,21 38,33 @@ namespace backlight

    void Handler::init()
    {
        screenLightControl->initFromSettings();
        using namespace screen_light_control;
        settings->registerValueChange(settings::Brightness::brightnessLevel, [&](const std::string &value) {
            ManualModeParameters params{utils::getNumericValue<float>(value)};
            screenLightControl->processRequest(Action::setManualModeBrightness, Parameters(params));
        });

        settings->registerValueChange(settings::Brightness::autoMode, [&]([[maybe_unused]] const std::string &value) {
            const auto action = getScreenAutoModeState() == ScreenLightMode::Automatic ? Action::enableAutomaticMode
                                                                                       : Action::disableAutomaticMode;
            screenLightControl->processRequest(action);
        });

        settings->registerValueChange(settings::Brightness::state, [&]([[maybe_unused]] const std::string &value) {
            const auto action = getScreenLightState() ? Action::turnOn : Action::turnOff;
            if (action == Action::turnOn) {
                screenLightTimer.start();
            }
            screenLightControl->processRequest(action);
        });
    }

    void Handler::processScreenRequest(screen_light_control::Action action)
    void Handler::processScreenRequest(screen_light_control::Action action,
                                       const screen_light_control::Parameters &params)
    {
        if (action == screen_light_control::Action::enableAutomaticMode) {
            startScreenLightTimer();
        }
        handleScreenLightSettings(action);
        screenLightControl->processRequest(action);
    }

    void Handler::processScreenRequest(screen_light_control::ParameterizedAction action,
                                       screen_light_control::Parameters params)
    {
        handleScreenLightSettings(action, params);
        screenLightControl->processRequest(action, params);
    }


@@ 132,12 144,12 @@ namespace backlight
        }
    }

    auto Handler::getValue(std::string path) const -> std::string
    auto Handler::getValue(const std::string &path) const -> std::string
    {
        return settings->getValue(path);
    }

    void Handler::setValue(std::string path, std::string value)
    void Handler::setValue(const std::string &path, const std::string &value)
    {
        settings->setValue(path, value);
    }


@@ 154,17 166,18 @@ namespace backlight

    auto Handler::getScreenLightState() const noexcept -> bool
    {
        return screenLightControl->getLightState();
        return utils::getNumericValue<bool>(getValue(settings::Brightness::state));
    }

    auto Handler::getScreenAutoModeState() const noexcept -> screen_light_control::ScreenLightMode
    {
        return screenLightControl->getAutoModeState();
        auto mode = utils::getNumericValue<int>(getValue(settings::Brightness::autoMode));
        return static_cast<screen_light_control::ScreenLightMode>(mode);
    }

    auto Handler::getScreenBrightnessValue() const noexcept -> bsp::eink_frontlight::BrightnessPercentage
    {
        return screenLightControl->getBrightnessValue();
        return utils::getNumericValue<float>(getValue(settings::Brightness::brightnessLevel));
    }

    void Handler::handleKeypadLightRefresh()


@@ 177,40 190,44 @@ namespace backlight

    void Handler::handleScreenLightRefresh()
    {
        if (utils::getNumericValue<bool>(getValue(settings::Brightness::state)) &&
            utils::getNumericValue<bool>(getValue(settings::Brightness::autoMode))) {

            if (!screenLightControl->getLightState()) {
        if (getScreenLightState() && getScreenAutoModeState() == screen_light_control::ScreenLightMode::Manual) {
            if (!screenLightControl->isLightOn()) {
                screenLightControl->processRequest(screen_light_control::Action::turnOn);
            }
            startScreenLightTimer();
        }
    }

    void Handler::handleScreenLightSettings(screen_light_control::Action action)
    void Handler::handleScreenLightSettings(screen_light_control::Action action,
                                            const screen_light_control::Parameters &params)
    {
        switch (action) {
        case screen_light_control::Action::enableAutomaticMode:
            setValue(settings::Brightness::autoMode, utils::to_string(true));
            setValue(settings::Brightness::autoMode,
                     utils::to_string(static_cast<int>(screen_light_control::ScreenLightMode::Automatic)));
            break;
        case screen_light_control::Action::disableAutomaticMode:
            setValue(settings::Brightness::autoMode, utils::to_string(false));
            screenLightControl->processRequest(screen_light_control::ParameterizedAction::setManualModeBrightness,
                                               screen_light_control::Parameters{utils::getNumericValue<float>(
                                                   getValue(settings::Brightness::brightnessLevel))});
            setValue(settings::Brightness::autoMode,
                     utils::to_string(static_cast<int>(screen_light_control::ScreenLightMode::Manual)));
            break;
        case screen_light_control::Action::turnOff:
            setValue(settings::Brightness::state, utils::to_string(false));
            break;
        case screen_light_control::Action::turnOn:
            setValue(settings::Brightness::state, utils::to_string(true));
            screenLightTimer.start();
            break;
        case screen_light_control::Action::setManualModeBrightness:
            if (params.hasManualModeParams()) {
                setValue(settings::Brightness::brightnessLevel,
                         utils::to_string(params.getManualModeParams().manualModeBrightness));
            }
            else {
                LOG_ERROR("Missing ManualModeBrightness value, change request ignored");
            }
            break;
        default:
            break;
        }
    }

    void Handler::handleScreenLightSettings(screen_light_control::ParameterizedAction action,
                                            screen_light_control::Parameters params)
    {
        setValue(settings::Brightness::brightnessLevel, utils::to_string(params.manualModeBrightness));
    }
} // namespace backlight

M module-services/service-evtmgr/backlight-handler/BacklightHandler.hpp => module-services/service-evtmgr/backlight-handler/BacklightHandler.hpp +8 -24
@@ 15,38 15,23 @@ namespace settings

namespace backlight
{
    class SettingsInterface
    {
      public:
        virtual ~SettingsInterface() = default;

      protected:
        virtual auto getValue(std::string path) const -> std::string = 0;
        virtual void setValue(std::string path, std::string value)   = 0;
    };

    /// @brief Backlight events handler
    class Handler : public SettingsInterface
    class Handler
    {
      public:
        Handler(std::shared_ptr<settings::Settings> settings, sys::Service *parent);

        /// initiaise in InitHandler when Service is ready
        /// initialise in InitHandler when Service is ready
        void init();

        /// Process request of the screen light control
        /// @screen_light_control::Action an action to perform
        void processScreenRequest(screen_light_control::Action action);

        /// Process request of the screen light control with specified parameters
        /// @screen_light_control::ParameterizedAction an action to perform
        /// @screen_light_control::Parameters parameters being set
        void processScreenRequest(screen_light_control::ParameterizedAction action,
                                  screen_light_control::Parameters params);
        void processScreenRequest(screen_light_control::Action action, const screen_light_control::Parameters &params);

        void handleKeyPressed();
        /// Process request of the keypad light control
        /// @screen_light_control::ParameterizedAction an action to perform
        /// @keypad_backlight::action an action to perform
        /// @return True if request was processed successfully, false otherwise
        auto processKeypadRequest(bsp::keypad_backlight::Action action) -> bool;



@@ 55,8 40,8 @@ namespace backlight
        [[nodiscard]] auto getScreenBrightnessValue() const noexcept -> bsp::eink_frontlight::BrightnessPercentage;

      protected:
        [[nodiscard]] auto getValue(std::string path) const -> std::string override;
        void setValue(std::string path, std::string value) override;
        [[nodiscard]] auto getValue(const std::string &path) const -> std::string;
        void setValue(const std::string &path, const std::string &value);

      private:
        std::shared_ptr<settings::Settings> settings;


@@ 76,8 61,7 @@ namespace backlight
        void restoreKeypadLightState();
        void handleKeypadLightRefresh();
        void handleScreenLightRefresh();
        void handleScreenLightSettings(screen_light_control::Action action);
        void handleScreenLightSettings(screen_light_control::ParameterizedAction action,
                                       screen_light_control::Parameters params);
        void handleScreenLightSettings(screen_light_control::Action action,
                                       const screen_light_control::Parameters &params);
    };
} // namespace backlight

A module-services/service-evtmgr/battery-brownout-detector/BatteryBrownoutDetector.cpp => module-services/service-evtmgr/battery-brownout-detector/BatteryBrownoutDetector.cpp +54 -0
@@ 0,0 1,54 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "BatteryBrownoutDetector.hpp"
#include <Timers/TimerFactory.hpp>

namespace
{
    constexpr std::chrono::milliseconds measurementTickTime{1000};
    constexpr auto measurementTickName     = "BrtownoutDetectorTick";
    constexpr unsigned measurementMaxCount = 5;
    constexpr auto brownoutLevelVoltage    = 3600; // mV
} // namespace

BatteryBrownoutDetector::BatteryBrownoutDetector(sys::Service *service,
                                                 std::function<int()> getMeasurementCallback,
                                                 std::function<void()> positiveResultCallback,
                                                 std::function<void()> negativeResultCallback)
    : parentService(service), getMeasurementCallback(std::move(getMeasurementCallback)),
      positiveResultCallback(std::move(positiveResultCallback)),
      negativeResultCallback(std::move(negativeResultCallback)), detectionOngoing{false}, measurementCount{0},
      measurementTick{sys::TimerFactory::createSingleShotTimer(
          service, measurementTickName, measurementTickTime, [this](sys::Timer &) { checkBrownout(); })}
{}

void BatteryBrownoutDetector::startDetection()
{
    if (detectionOngoing) {
        return;
    }
    LOG_DEBUG("Battery Brownout detection window start");
    detectionOngoing = true;
    measurementCount = 0;
    checkBrownout();
}

void BatteryBrownoutDetector::checkBrownout()
{
    if (getMeasurementCallback() < brownoutLevelVoltage) {
        LOG_DEBUG("Battery Brownout detected");
        positiveResultCallback();
        return;
    }

    measurementCount++;
    if (measurementCount <= measurementMaxCount) {
        measurementTick.start();
    }
    else {
        LOG_DEBUG("Battery Brownout detection window finish with negative result");
        detectionOngoing = false;
        negativeResultCallback();
    }
}

A module-services/service-evtmgr/battery-brownout-detector/BatteryBrownoutDetector.hpp => module-services/service-evtmgr/battery-brownout-detector/BatteryBrownoutDetector.hpp +33 -0
@@ 0,0 1,33 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <Service/Service.hpp>
#include <Timers/TimerHandle.hpp>

namespace sys
{
    class Service;
} // namespace sys

class BatteryBrownoutDetector
{
  public:
    BatteryBrownoutDetector(sys::Service *service,
                            std::function<int()> getMeasurementCallback,
                            std::function<void()> positiveResultCallback,
                            std::function<void()> negativeResultCallback);
    void startDetection();

  private:
    void checkBrownout();

    sys::Service *parentService;
    const std::function<int()> getMeasurementCallback;
    const std::function<void()> positiveResultCallback;
    const std::function<void()> negativeResultCallback;
    bool detectionOngoing;
    unsigned measurementCount;
    sys::TimerHandle measurementTick;
};

M module-services/service-evtmgr/doc/battery_charging.md => module-services/service-evtmgr/doc/battery_charging.md +5 -1
@@ 48,4 48,8 @@ Implementation is interrupt-driven. In interrupt handler temperature measurement

![](charger_temperature_algorithm.svg "Charging control due to temperature")

Additional 2 Cdeg hysteresis was introduced to prevent rapid changes in charging states.
\ No newline at end of file
Additional 2 Cdeg hysteresis was introduced to prevent rapid changes in charging states.

## Battery Brownout Detection

Battery charger chip is capable of providing interrupt when low voltage threshold is violated. This voltage value is instantaneous measurement. To prevent spurious brownout events, additional algorithm was introduced. This kind of false detection could occur when phone call is stared and GSM module consumes high current what results in narrow voltage dip. Interrupt from battery charger creates a time window of 5 seconds and every second filtered value of voltage is sampled. If any of those samples drop below given 3.6V threshold, the brownout event is fired.

M module-services/service-evtmgr/screen-light-control/ScreenLightControl.cpp => module-services/service-evtmgr/screen-light-control/ScreenLightControl.cpp +26 -48
@@ 2,19 2,13 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ScreenLightControl.hpp"

#include <agents/settings/SystemSettings.hpp>
#include <module-sys/Timers/TimerFactory.hpp>
#include <Service/Message.hpp>
#include <Service/Service.hpp>
#include <service-db/service-db/Settings.hpp>
#include <Utils.hpp>

namespace screen_light_control
{

    ScreenLightControl::ScreenLightControl(std::shared_ptr<settings::Settings> settings, sys::Service *parent)
        : settings(settings)
    ScreenLightControl::ScreenLightControl(sys::Service *parent)
    {
        controlTimer = sys::TimerFactory::createPeriodicTimer(parent,
                                                              "LightControlTimer",


@@ 24,6 18,8 @@ namespace screen_light_control
                                                              "LightSensorReadoutTimer",
                                                              std::chrono::milliseconds{READOUT_TIMER_MS},
                                                              [this](sys::Timer &) { readoutTimerCallback(); });

        setParameters(screen_light_control::AutomaticModeParameters());
    }

    ScreenLightControl::~ScreenLightControl()


@@ 31,32 27,11 @@ namespace screen_light_control
        disableTimers();
    }

    void ScreenLightControl::initFromSettings()
    void ScreenLightControl::processRequest(Action action)
    {
        settings->registerValueChange(settings::Brightness::brightnessLevel, [&](const std::string &value) {
            setBrightnessLevel(utils::getNumericValue<float>(value));
        });

        settings->registerValueChange(settings::Brightness::autoMode, [&](const std::string &value) {
            if (utils::getNumericValue<bool>(value)) {
                enableAutomaticMode();
            }
            else {
                disableAutomaticMode();
            }
        });

        settings->registerValueChange(settings::Brightness::state, [&](const std::string &value) {
            if (utils::getNumericValue<bool>(value)) {
                turnOn();
            }
            else {
                turnOff();
            }
        });
        processRequest(action, Parameters());
    }

    void ScreenLightControl::processRequest(Action action)
    void ScreenLightControl::processRequest(Action action, const Parameters &params)
    {
        switch (action) {
        case Action::turnOff:


@@ 71,17 46,15 @@ namespace screen_light_control
        case Action::disableAutomaticMode:
            disableAutomaticMode();
            break;
        }
    }

    void ScreenLightControl::processRequest(ParameterizedAction action, Parameters params)
    {
        switch (action) {
        case ParameterizedAction::setManualModeBrightness:
            setBrightnessLevel(params.manualModeBrightness);
        case Action::setManualModeBrightness:
            if (params.hasManualModeParams()) {
                setParameters(params.getManualModeParams());
            }
            break;
        case ParameterizedAction::setAutomaticModeParameters:
            setAutomaticModeParameters(params);
        case Action::setAutomaticModeParameters:
            if (params.hasAutoModeParams()) {
                setParameters(params.getAutoModeParams());
            }
            break;
        }
    }


@@ 101,7 74,7 @@ namespace screen_light_control
        return automaticMode;
    }

    auto ScreenLightControl::getLightState() const noexcept -> bool
    auto ScreenLightControl::isLightOn() const noexcept -> bool
    {
        return lightOn;
    }


@@ 123,7 96,7 @@ namespace screen_light_control
        readoutTimer.stop();
    }

    void ScreenLightControl::setAutomaticModeParameters(const Parameters &params)
    void ScreenLightControl::setParameters(const AutomaticModeParameters &params)
    {
        if (lightOn && automaticMode == ScreenLightMode::Automatic) {
            disableTimers();


@@ 138,6 111,12 @@ namespace screen_light_control
        }
    }

    void ScreenLightControl::setParameters(ManualModeParameters params)
    {
        brightnessValue = params.manualModeBrightness;
        setManualBrightnessLevel();
    }

    void ScreenLightControl::enableAutomaticMode()
    {
        if (lightOn) {


@@ 150,6 129,7 @@ namespace screen_light_control
    {
        disableTimers();
        automaticMode = ScreenLightMode::Manual;
        setManualBrightnessLevel();
    }

    void ScreenLightControl::turnOn()


@@ 162,18 142,16 @@ namespace screen_light_control
        lightOn = true;
    }

    void ScreenLightControl::setBrightnessLevel(bsp::eink_frontlight::BrightnessPercentage brightnessPercentage)
    void ScreenLightControl::setManualBrightnessLevel()
    {
        bsp::eink_frontlight::setBrightness(brightnessPercentage);
        brightnessValue = brightnessPercentage;
        bsp::eink_frontlight::setBrightness(brightnessValue);
    }

    void ScreenLightControl::turnOff()
    {
        bsp::eink_frontlight::turnOff();
        bsp::light_sensor::standby();
        controlTimer.stop();
        readoutTimer.stop();
        disableTimers();
        lightOn = false;
    }
} // namespace screen_light_control

M module-services/service-evtmgr/screen-light-control/ScreenLightControl.hpp => module-services/service-evtmgr/screen-light-control/ScreenLightControl.hpp +7 -51
@@ 3,10 3,9 @@

#pragma once

#include "ControlFunctions.hpp"
#include "ScreenLightControlParameters.hpp"
#include <memory>
#include <module-sys/Timers/TimerHandle.hpp>

namespace settings
{
    class Settings;


@@ 21,62 20,20 @@ namespace sys
/// Processing of ambient light sensor input to screen brightness output.
namespace screen_light_control
{
    /// Modes in which front light can operate
    enum ScreenLightMode
    {
        Automatic, /// Automally sets screen brightness based on sensor data
        Manual     /// Manually set brightness level
    };

    /// Set of actions to control the screen light
    enum class Action
    {
        turnOff,             ///< Turn off screen frontlight
        turnOn,              ///< Turn on screen frontlight
        enableAutomaticMode, ///< Enable automatic mode of screen frontlight
        disableAutomaticMode ///< Disable automatic mode of screen frontlight
    };

    /// Set of actions to control the screen light using specified parameters
    enum class ParameterizedAction
    {
        setManualModeBrightness,   ///< Set screen brightness in manual mode control
        setAutomaticModeParameters ///< Set parameters for automatic mode of screen frontlight
    };

    struct Parameters
    {
        static constexpr auto MAX_BRIGHTNESS = 100.0;

        /// Screen brightness 0-100% in manual mode
        bsp::eink_frontlight::BrightnessPercentage manualModeBrightness = 50.0f;
        /// Vector of points for screen brightness [%] in relation to ambient light [Lux] function. Points have to be in
        /// ascending order of ambient light values.
        functions::BrightnessFunction functionPoints = functions::BrightnessFunction({{0.0f, 50.0f}});
        /// Ramp time of screen brightness in miliseconds per 0-100% change
        unsigned int rampTimeMS = 1500;
        /// Hysteresis value of screen brightness control
        float brightnessHysteresis = 10.0f;
        /// Gamma factor for screen brightness correction
        float gammaFactor = 2.5f;
    };

    /// Control screen light and keeps it's current state
    class ScreenLightControl
    {
      public:
        explicit ScreenLightControl(std::shared_ptr<settings::Settings> settings, sys::Service *parent);
        explicit ScreenLightControl(sys::Service *parent);
        ~ScreenLightControl();

        void processRequest(Action action);
        void processRequest(ParameterizedAction action, Parameters params);
        void processRequest(Action action, const Parameters &params);

        [[nodiscard]] auto getLightState() const noexcept -> bool;
        [[nodiscard]] auto isLightOn() const noexcept -> bool;
        [[nodiscard]] auto getAutoModeState() const noexcept -> ScreenLightMode;
        [[nodiscard]] auto getBrightnessValue() const noexcept -> bsp::eink_frontlight::BrightnessPercentage;

        void initFromSettings();

      private:
        void controlTimerCallback();
        void readoutTimerCallback();


@@ 84,8 41,9 @@ namespace screen_light_control
        void enableTimers();
        void disableTimers();

        void setAutomaticModeParameters(const Parameters &params);
        void setBrightnessLevel(bsp::eink_frontlight::BrightnessPercentage brightnessPercentage);
        void setParameters(const AutomaticModeParameters &params);
        void setParameters(ManualModeParameters params);
        void setManualBrightnessLevel();

        void turnOff();
        void turnOn();


@@ 102,8 60,6 @@ namespace screen_light_control
        bool lightOn                                               = false;
        screen_light_control::ScreenLightMode automaticMode        = ScreenLightMode::Manual;
        bsp::eink_frontlight::BrightnessPercentage brightnessValue = 0.0;

        std::shared_ptr<settings::Settings> settings;
    };

} // namespace screen_light_control

A module-services/service-evtmgr/screen-light-control/ScreenLightControlParameters.hpp => module-services/service-evtmgr/screen-light-control/ScreenLightControlParameters.hpp +87 -0
@@ 0,0 1,87 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "ControlFunctions.hpp"
#include <gsl_assert>
#include <optional>

namespace screen_light_control
{
    /// Modes in which front light can operate
    enum class ScreenLightMode
    {
        Automatic, /// Automatically sets screen brightness based on sensor data
        Manual     /// Manually set brightness level
    };

    /// Set of actions to control the screen light
    enum class Action
    {
        turnOff,                   ///< Turn off screen frontlight
        turnOn,                    ///< Turn on screen frontlight
        enableAutomaticMode,       ///< Enable automatic mode of screen frontlight
        disableAutomaticMode,      ///< Disable automatic mode of screen frontlight
        setManualModeBrightness,   ///< Set screen brightness in manual mode control
        setAutomaticModeParameters ///< Set parameters for automatic mode of screen frontlight
    };

    struct ManualModeParameters
    {
        static constexpr auto MAX_BRIGHTNESS = 100.0;
        /// Screen brightness 0-100% in manual mode
        bsp::eink_frontlight::BrightnessPercentage manualModeBrightness = 50.0f;
    };

    struct AutomaticModeParameters
    {
        /// Vector of points for screen brightness [%] in relation to ambient light [Lux] function. Points have to be in
        /// ascending order of ambient light values.
        functions::BrightnessFunction functionPoints =
            functions::BrightnessFunction({{0.0f, 70.0f}, {250.0f, 70.0f}, {450.0f, 40.0f}, {500.0f, 0.0f}});
        /// Ramp time of screen brightness in milliseconds per 0-100% change
        unsigned int rampTimeMS = 1500;
        /// Hysteresis value of screen brightness control
        float brightnessHysteresis = 10.0f;
        /// Gamma factor for screen brightness correction
        float gammaFactor = 2.5f;
    };

    class Parameters
    {
        std::optional<ManualModeParameters> manualModeParams;
        std::optional<AutomaticModeParameters> autoModeParams;

      public:
        Parameters() = default;
        explicit Parameters(ManualModeParameters manualModeParams)
            : manualModeParams{std::make_optional(manualModeParams)}
        {}
        explicit Parameters(const AutomaticModeParameters &autoModeParams)
            : autoModeParams{std::make_optional(autoModeParams)}
        {}

        [[nodiscard]] bool hasManualModeParams() const noexcept
        {
            return manualModeParams.has_value();
        }

        [[nodiscard]] auto getManualModeParams() const noexcept -> ManualModeParameters
        {
            Expects(hasManualModeParams());
            return manualModeParams.value();
        }

        [[nodiscard]] bool hasAutoModeParams() const noexcept
        {
            return autoModeParams.has_value();
        }

        [[nodiscard]] auto getAutoModeParams() const noexcept -> const AutomaticModeParameters &
        {
            Expects(hasAutoModeParams());
            return autoModeParams.value();
        }
    };
} // namespace screen_light_control

M module-services/service-evtmgr/service-evtmgr/EVMessages.hpp => module-services/service-evtmgr/service-evtmgr/EVMessages.hpp +0 -15
@@ 40,27 40,12 @@ namespace sevm
        uint32_t timestamp = 0;
    };

    class RtcUpdateTimeMessage : public sys::DataMessage
    {
      public:
        explicit RtcUpdateTimeMessage(time_t time) : time(time)
        {}
        [[nodiscard]] time_t getTime() const noexcept
        {
            return time;
        }

      private:
        const time_t time = 0;
    };

    class SIMMessage : public sys::DataMessage
    {
      public:
        SIMMessage() : DataMessage(MessageType::SIMTrayEvent)
        {}
    };

    /*
     * @brief Template for all messages that go to application manager
     */

M module-services/service-evtmgr/service-evtmgr/EventManager.hpp => module-services/service-evtmgr/service-evtmgr/EventManager.hpp +2 -1
@@ 37,7 37,8 @@ class EventManager : public sys::Service
    void toggleTorchOnOff();
    void toggleTorchColor();

    void processRTCRequest(struct tm &newTime);
    void processRTCFromTmRequest(struct tm &newTime);
    void processRTCFromTimestampRequest(time_t &newTime);
    void processTimezoneRequest(const std::string &timezone);

    std::shared_ptr<settings::Settings> settings;

M module-services/service-evtmgr/service-evtmgr/ScreenLightControlMessage.hpp => module-services/service-evtmgr/service-evtmgr/ScreenLightControlMessage.hpp +50 -15
@@ 3,7 3,7 @@

#pragma once

#include "module-services/service-evtmgr/screen-light-control/ScreenLightControl.hpp"
#include <service-evtmgr/screen-light-control/ScreenLightControlParameters.hpp>

#include <Service/Message.hpp>
#include <Service/Service.hpp>


@@ 13,23 13,48 @@ namespace sevm
{
    class ScreenLightControlMessage : public sys::DataMessage
    {
        const screen_light_control::Action action;

      public:
        explicit ScreenLightControlMessage(screen_light_control::Action act)
            : sys::DataMessage(MessageType::ScreenLightControlAction), action(act)
        {}

        const screen_light_control::Action action;
        [[nodiscard]] auto getAction() const noexcept -> screen_light_control::Action
        {
            return action;
        }
    };

    class ScreenLightSetAutoModeParams : public ScreenLightControlMessage
    {
        screen_light_control::AutomaticModeParameters params;

      public:
        explicit ScreenLightSetAutoModeParams(screen_light_control::AutomaticModeParameters params)
            : ScreenLightControlMessage(screen_light_control::Action::setAutomaticModeParameters), params{std::move(
                                                                                                       params)}
        {}

        [[nodiscard]] auto getParams() const noexcept -> const screen_light_control::AutomaticModeParameters &
        {
            return params;
        }
    };

    class ScreenLightSetParameters : public sys::DataMessage
    class ScreenLightSetManualModeParams : public ScreenLightControlMessage
    {
        screen_light_control::ManualModeParameters params;

      public:
        ScreenLightSetParameters(screen_light_control::ParameterizedAction act, screen_light_control::Parameters params)
            : sys::DataMessage(MessageType::ScreenLightControlAction), action(act), parameters(std::move(params))
        explicit ScreenLightSetManualModeParams(screen_light_control::ManualModeParameters params)
            : ScreenLightControlMessage(screen_light_control::Action::setManualModeBrightness), params{params}
        {}

        const screen_light_control::ParameterizedAction action;
        const screen_light_control::Parameters parameters;
        [[nodiscard]] auto getParams() const noexcept -> screen_light_control::ManualModeParameters
        {
            return params;
        }
    };

    class ScreenLightControlRequestParameters : public sys::DataMessage


@@ 41,19 66,29 @@ namespace sevm

    class ScreenLightControlParametersResponse : public sys::DataMessage
    {
      public:
        ScreenLightControlParametersResponse() : sys::DataMessage(MessageType::ScreenLightControlParametersResponse)
        {}
        bool lightOn;
        screen_light_control::ScreenLightMode mode;
        screen_light_control::ManualModeParameters parameters;

      public:
        ScreenLightControlParametersResponse(bool lightOn,
                                             screen_light_control::ScreenLightMode mode,
                                             screen_light_control::Parameters params)
                                             screen_light_control::ManualModeParameters params)
            : sys::DataMessage(MessageType::ScreenLightControlParametersResponse), lightOn(lightOn), mode(mode),
              parameters(std::move(params))
              parameters(params)
        {}

        bool lightOn;
        screen_light_control::ScreenLightMode mode;
        screen_light_control::Parameters parameters;
        [[nodiscard]] bool isLightOn() const noexcept
        {
            return lightOn;
        }
        [[nodiscard]] auto getMode() const noexcept -> screen_light_control::ScreenLightMode
        {
            return mode;
        }
        [[nodiscard]] auto getParams() const noexcept -> screen_light_control::ManualModeParameters
        {
            return parameters;
        }
    };
} // namespace sevm

M module-services/service-evtmgr/service-evtmgr/WorkerEvent.hpp => module-services/service-evtmgr/service-evtmgr/WorkerEvent.hpp +4 -1
@@ 12,6 12,7 @@
#include <bsp/keyboard/key_codes.hpp>
#include <bsp/keyboard/key_codes.hpp>
#include <Service/CpuSentinel.hpp>
#include <module-services/service-evtmgr/battery-brownout-detector/BatteryBrownoutDetector.hpp>

#include <cstdint>
#include <list>


@@ 73,9 74,10 @@ class WorkerEvent : public sys::Worker
    bsp::KeyCodes lastPressed = static_cast<bsp::KeyCodes>(0);
    sys::Service *service     = nullptr;
    std::shared_ptr<sys::CpuSentinel> cpuSentinel;
    BatteryBrownoutDetector batteryBrownoutDetector;

  public:
    WorkerEvent(sys::Service *service) : sys::Worker(service, stackDepthBytes), service(service){};
    WorkerEvent(sys::Service *service);
    /**
     * This function is responsible for creating all queues provided in the constructor.
     * When all queues are created this method creates set of queues.


@@ 92,4 94,5 @@ class WorkerEvent : public sys::Worker
    bool handleMessage(uint32_t queueID) override final;
    void requestSliderPositionRead();
    void handleMagnetometerEvent();
    void checkBatteryChargerInterrupts();
};

M module-services/service-fileindexer/CMakeLists.txt => module-services/service-fileindexer/CMakeLists.txt +0 -3
@@ 5,8 5,6 @@ project(service-fileindexer VERSION 1.0

add_library(${PROJECT_NAME} STATIC ${SOURCES} ${BOARD_SOURCES})

target_include_directories( ${PROJECT_NAME} PRIVATE ${TAGLIB_INCLUDE_DIRS} )

# Board specific compilation definitions,options,include directories and features
target_compile_definitions(${PROJECT_NAME} PUBLIC ${PROJECT_CONFIG_DEFINITIONS})
target_compile_definitions(${PROJECT_NAME} PUBLIC ${PROJECT_TARGET})


@@ 20,7 18,6 @@ target_link_options(${PROJECT_NAME} PUBLIC ${TARGET_LINK_OPTIONS})
target_sources( ${PROJECT_NAME}
	PRIVATE
	"ServiceFileIndexer.cpp"
	"messages/FileChangeMessage.cpp"
	"StartupIndexer.cpp"
	"notesIndexer.cpp"
)

M module-services/service-fileindexer/ServiceFileIndexer.cpp => module-services/service-fileindexer/ServiceFileIndexer.cpp +40 -14
@@ 1,13 1,23 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <log/log.hpp>
#include <purefs/filesystem_paths.hpp>
#include <purefs/fs/inotify.hpp>
#include "ServiceFileIndexer.hpp"
#include "notesIndexer.hpp"
#include "messages/FileChangeMessage.hpp"
#include "Constants.hpp"
#include <fileref.h>
#include <tag.h>
#include "notesIndexer.hpp"
#include "Constants.hpp"
#include <purefs/fs/inotify_message.hpp>

namespace
{
    inline auto getMusicPath()
    {
        return purefs::createPath(purefs::dir::getUserDiskPath(), "music").string();
    }
} // namespace

namespace service
{


@@ 20,6 30,15 @@ namespace service
    // When receive notification handler
    sys::MessagePointer ServiceFileIndexer::DataReceivedHandler(sys::DataMessage *msg, sys::ResponseMessage *resp)
    {
        auto inotify = dynamic_cast<purefs::fs::message::inotify *>(msg);
        if (inotify) {
            LOG_ERROR("Inotify event %s %08x", inotify->name.c_str(), int(inotify->flags));
        }
        else {
            LOG_ERROR("Not a inotify message");
        }

#if 0
        auto fcm = dynamic_cast<msg::FileChangeMessage *>(msg);
        if (fcm) {
            switch (fcm->event()) {


@@ 47,6 66,7 @@ namespace service
            }
            return std::make_shared<sys::ResponseMessage>();
        }
#endif
        return std::make_shared<sys::ResponseMessage>(sys::ReturnCodes::Unresolved);
    }



@@ 54,23 74,29 @@ namespace service
    sys::ReturnCodes ServiceFileIndexer::InitHandler()
    {
        /*
        vfs.registerNotificationHandler(
            [_this = shared_from_this()](std::string_view new_path, vfs::FsEvent event, std::string_view old_path) {
                namespace fs       = std::filesystem;
                const auto new_ext = fs::path(new_path).extension().string();
                auto msg           = std::make_shared<msg::FileChangeMessage>(new_path, event, old_path);
                _this->bus.sendUnicast(msg, std::string(service::name::file_indexer));
            });
        mStartupIndexer.start(shared_from_this(), service::name::file_indexer);
        */

        mfsNotifier = purefs::fs::inotify_create(shared_from_this());
        if (!mfsNotifier) {
            LOG_ERROR("Unable to create inotify object");
            return sys::ReturnCodes::Failure;
        }
        const int err = mfsNotifier->add_watch(getMusicPath(), purefs::fs::inotify_flags::close_write);
        if (err) {
            LOG_ERROR("Unable to create inotify watch errno: %i", err);
            return sys::ReturnCodes::Failure;
        }
        return sys::ReturnCodes::Success;
    }

    sys::ReturnCodes ServiceFileIndexer::DeinitHandler()
    {
        /*
        vfs.registerNotificationHandler(nullptr);
        */
        const int err = mfsNotifier->rm_watch(getMusicPath());
        if (err) {
            LOG_ERROR("Unable to remove watch errno: %i", err);
            return sys::ReturnCodes::Failure;
        }
        return sys::ReturnCodes::Success;
    }


M module-services/service-fileindexer/ServiceFileIndexer.hpp => module-services/service-fileindexer/ServiceFileIndexer.hpp +8 -0
@@ 8,6 8,11 @@
#include "Constants.hpp"
#include "StartupIndexer.hpp"

namespace purefs::fs
{
    class inotify;
}

namespace service
{



@@ 28,6 33,9 @@ namespace service
        auto onRenameFile(std::string_view oldPath, std::string_view newPath) -> void;
        auto onAudioContentChanged(std::string_view path) -> void;
        auto onTextContentChanged(std::string_view path) -> void;

      private:
        std::shared_ptr<purefs::fs::inotify> mfsNotifier;
        detail::StartupIndexer mStartupIndexer;
    };


M module-services/service-fileindexer/StartupIndexer.cpp => module-services/service-fileindexer/StartupIndexer.cpp +1 -3
@@ 2,10 2,8 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "StartupIndexer.hpp"
#include "messages/FileChangeMessage.hpp"
#include <Timers/TimerFactory.hpp>
#include <filesystem>
//#include <ff_stdio_listdir_recursive.h>
#include <purefs/filesystem_paths.hpp>
#include "Constants.hpp"



@@ 63,7 61,7 @@ namespace service::detail
            mIdxTimer = sys::TimerFactory::createPeriodicTimer(
                svc.get(), "file_indexing", std::chrono::milliseconds{timer_indexing_time}, [this, svc](sys::Timer &) {
                    if (!mMsgs.empty()) {
                        svc->bus.sendUnicast(mMsgs.front(), std::string(service::name::file_indexer));
                        // svc->bus.sendUnicast(mMsgs.front(), std::string(service::name::file_indexer));
                        mMsgs.pop_front();
                    }
                    else {

D module-services/service-fileindexer/messages/FileChangeMessage.cpp => module-services/service-fileindexer/messages/FileChangeMessage.cpp +0 -11
@@ 1,11 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "FileChangeMessage.hpp"

namespace service::msg
{
    FileChangeMessage::FileChangeMessage(std::string_view new_path, evt_t ev, std::string_view old_path)
        : DataMessage(MessageType::FileContentModified), mNewPath(new_path), mEvent(ev), mOldPath(old_path)
    {}
} // namespace service::msg

D module-services/service-fileindexer/messages/FileChangeMessage.hpp => module-services/service-fileindexer/messages/FileChangeMessage.hpp +0 -36
@@ 1,36 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <Service/Message.hpp>
#include <vfsNotifier.hpp>
namespace service::msg
{
    class FileChangeMessage final : public sys::DataMessage
    {
      public:
        using evt_t = vfsn::utility::vfsNotifier::FsEvent;
        FileChangeMessage(std::string_view new_path, evt_t ev, std::string_view old_path);
        virtual ~FileChangeMessage()           = default;
        FileChangeMessage(FileChangeMessage &) = delete;
        FileChangeMessage &operator=(FileChangeMessage &) = delete;
        [[nodiscard]] auto newPath() const noexcept
        {
            return mNewPath;
        }
        [[nodiscard]] auto oldPath() const noexcept
        {
            return mOldPath;
        }
        [[nodiscard]] auto event() const noexcept
        {
            return mEvent;
        }

      private:
        const std::string mNewPath;
        const evt_t mEvent;
        const std::string mOldPath;
    };
} // namespace service::msg

M module-services/service-time/RTCcommand.cpp => module-services/service-time/RTCcommand.cpp +7 -1
@@ 8,7 8,13 @@

void RTCCommand::setTime(const struct tm &time)
{
    auto message = std::make_shared<stm::message::UpdateRTCValueMessage>(time);
    auto message = std::make_shared<stm::message::UpdateRTCValueFromTmMessage>(time);
    owner->bus.sendUnicast(message, service::name::evt_manager);
}

void RTCCommand::setTime(const time_t &time)
{
    auto message = std::make_shared<stm::message::UpdateRTCValueFromTimestampMessage>(time);
    owner->bus.sendUnicast(message, service::name::evt_manager);
}


M module-services/service-time/ServiceTime.cpp => module-services/service-time/ServiceTime.cpp +6 -0
@@ 128,5 128,11 @@ namespace stm
                std::chrono::minutes{message->getTimeZoneOffset().value() / utils::time::secondsInMinute});
            return std::make_shared<sys::ResponseMessage>();
        });

        connect(typeid(stm::message::TimeChangeRequestMessage), [&](sys::Message *request) -> sys::MessagePointer {
            auto message = static_cast<stm::message::TimeChangeRequestMessage *>(request);
            timeManager->handleTimeChangeRequest(message->getTime());
            return std::make_shared<sys::ResponseMessage>();
        });
    }
} /* namespace stm */

M module-services/service-time/TimeManager.cpp => module-services/service-time/TimeManager.cpp +5 -0
@@ 13,3 13,8 @@ void TimeManager::handleCellularTimeUpdate(struct tm time, std::chrono::minutes 
    rtcCommand->setTime(time);
    rtcCommand->setTimezone(timezone);
}

void TimeManager::handleTimeChangeRequest(const time_t &time)
{
    rtcCommand->setTime(time);
}

M module-services/service-time/service-time/RTCCommand.hpp => module-services/service-time/service-time/RTCCommand.hpp +1 -0
@@ 12,6 12,7 @@ class RTCCommand : public RTCCommandInterface
    {}
    virtual ~RTCCommand() = default;
    virtual void setTime(const struct tm &time) final;
    virtual void setTime(const time_t &time) final;
    virtual void setTimezone(const std::string &timezone) final;

  private:

M module-services/service-time/service-time/RTCCommandInterface.hpp => module-services/service-time/service-time/RTCCommandInterface.hpp +7 -2
@@ 5,7 5,7 @@

#include <module-sys/Service/Service.hpp>

#include <time.h>
#include <ctime>

/**
 * @brief Basic interface for bsp rtc related commands


@@ 19,10 19,15 @@ class RTCCommandInterface
    virtual ~RTCCommandInterface() = default;
    /**
     * Sets RTC counter
     * @param time UTC time value to sed
     * @param time UTC time value to set
     */
    virtual void setTime(const struct tm &time) = 0;
    /**
     * Sets RTC counter
     * @param time UTC time value to set
     */
    virtual void setTime(const time_t &time) = 0;
    /**
     * Sets timezone variable
     * @param timezone new timezone variable
     */

M module-services/service-time/service-time/TimeManager.hpp => module-services/service-time/service-time/TimeManager.hpp +5 -0
@@ 25,6 25,11 @@ class TimeManager
     * @param timezoneOffset timezone offset related to UTC time
     */
    void handleCellularTimeUpdate(const struct tm time, std::chrono::minutes timezoneOffset);
    /**
     * Handles time change request.
     * @param time UTC time to set
     */
    void handleTimeChangeRequest(const time_t &time);

  private:
    std::unique_ptr<RTCCommandInterface> rtcCommand;

M module-services/service-time/service-time/TimeMessage.hpp => module-services/service-time/service-time/TimeMessage.hpp +32 -2
@@ 52,10 52,10 @@ class TimeResponseMessage : public sys::ResponseMessage

namespace stm::message
{
    class UpdateRTCValueMessage : public sys::DataMessage
    class UpdateRTCValueFromTmMessage : public sys::DataMessage
    {
      public:
        explicit UpdateRTCValueMessage(const struct tm &time)
        explicit UpdateRTCValueFromTmMessage(const struct tm &time)
            : sys::DataMessage(MessageType::MessageTypeUninitialized), time(time){};

        auto getTime() -> struct tm &


@@ 67,6 67,21 @@ namespace stm::message
        struct tm time;
    };

    class UpdateRTCValueFromTimestampMessage : public sys::DataMessage
    {
      public:
        explicit UpdateRTCValueFromTimestampMessage(const time_t &time)
            : sys::DataMessage(MessageType::MessageTypeUninitialized), time(time){};

        auto getTime() -> time_t &
        {
            return time;
        }

      private:
        time_t time;
    };

    class UpdateTimeZoneMessage : public sys::DataMessage
    {
      public:


@@ 81,4 96,19 @@ namespace stm::message
      private:
        std::string timezone;
    };

    class TimeChangeRequestMessage : public sys::DataMessage
    {
      public:
        explicit TimeChangeRequestMessage(const time_t &time)
            : sys::DataMessage(MessageType::MessageTypeUninitialized), time(time){};

        auto getTime() -> time_t
        {
            return time;
        }

      private:
        time_t time;
    };
} // namespace stm::message

M module-sys/Service/BusProxy.hpp => module-sys/Service/BusProxy.hpp +11 -1
@@ 32,8 32,18 @@ namespace sys

        template <typename Msg, typename... Params> bool sendUnicast(Params &&... params)
        {
            static_assert(std::is_base_of<sys::msg::Request, Msg>::value,
                          "Only sys::msg::Request can be sent via Unicast<>");
            auto msg = std::make_shared<Msg>(std::forward<Params>(params)...);
            return sendUnicast(msg, Msg::target);
            return sendUnicast(msg, msg->target());
        }

        template <typename Msg, typename... Params> void sendMulticast(Params &&... params)
        {
            static_assert(std::is_base_of<sys::msg::Notification, Msg>::value,
                          "Only sys::msg::Notification can be sent via Multicast<>");
            auto msg = std::make_shared<Msg>(std::forward<Params>(params)...);
            sendMulticast(msg, msg->channel());
        }

        std::vector<BusChannel> channels;

M module-sys/Service/Message.hpp => module-sys/Service/Message.hpp +24 -0
@@ 106,6 106,30 @@ namespace sys
        void ValidateMulticastMessage() const;
    };

    namespace msg
    {
        struct Request : public sys::Message
        {
            Request() : Message(Type::Data)
            {}
            virtual const char *target() const = 0;
        };

        struct Response : public sys::Message
        {
            explicit Response(bool retCode = true) : Message(Type::Data), retCode(retCode)
            {}
            const bool retCode;
        };

        struct Notification : public sys::Message
        {
            Notification() : Message(Type::Data)
            {}
            virtual sys::BusChannel channel() const = 0;
        };
    } // namespace msg

    enum class SystemMessageType
    {
        Ping,

M module-utils/CMakeLists.txt => module-utils/CMakeLists.txt +0 -10
@@ 50,21 50,11 @@ include(segger/rtt/CMakeLists.txt)

# add third party libs
include(third-party/re2.cmake)
include(third-party/protobuf-lite.cmake)
include(third-party/libphonenumber.cmake)
include(third-party/tinyexpr.cmake)

# link against libphonenumber
target_link_libraries (${PROJECT_NAME} PUBLIC ${LIBPHONENUMBER_TARGET})

add_subdirectory(taglib EXCLUDE_FROM_ALL)
set( TAGLIB_INCLUDE_DIRS
	 "${CMAKE_CURRENT_SOURCE_DIR}/taglib/taglib/toolkit"
	 "${CMAKE_CURRENT_SOURCE_DIR}/taglib/taglib"
	 "${CMAKE_CURRENT_BINARY_DIR}/taglib"
	 PARENT_SCOPE
)

target_link_libraries(${PROJECT_NAME} PRIVATE crc32 )

# link against tinyexpr

M module-utils/Utils.cpp => module-utils/Utils.cpp +5 -2
@@ 2,9 2,12 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "Utils.hpp"
#include <filesystem>

#include <crc32.h>

#include <ctime>
#include <filesystem>

namespace utils::filesystem
{
    namespace


@@ 35,7 38,7 @@ namespace utils::filesystem

        std::random_device random_device;
        std::mt19937 generator(random_device());
        generator.seed(utils::time::Timestamp().getTime());
        generator.seed(std::time(nullptr));
        std::uniform_int_distribution<> distribution(0, CHARACTERS.size() - 1);

        std::string random_string;

M module-utils/Utils.hpp => module-utils/Utils.hpp +5 -4
@@ 3,16 3,17 @@

#pragma once
#include "Split.hpp"
#include "i18n/i18n.hpp"
#include <i18n/i18n.hpp>
#include <log/log.hpp>

#include <magic_enum.hpp>

#include <algorithm> // std::find_if_not
#include <sstream>
#include <iomanip>
#include <cmath>
#include <chrono>
#include <random>
#include "time/time_conversion.hpp"

#include <magic_enum.hpp>

namespace utils
{

M module-utils/bootconfig/src/bootconfig.cpp => module-utils/bootconfig/src/bootconfig.cpp +2 -2
@@ 149,7 149,7 @@ namespace boot
            m_os_root_path       = purefs::createPath(purefs::dir::getRootDiskPath(), m_os_type);
            m_boot_json          = bootJsonPath;
            m_bootloader_version = m_boot_json_parsed[boot::json::bootloader][boot::json::os_version].string_value();
            m_timestamp          = utils::time::Timestamp().str("%c");
            m_timestamp          = utils::time::getCurrentTimestamp().str("%c");
            m_os_version         = std::string(VERSION);

            LOG_INFO("boot_config: %s", to_json().dump().c_str());


@@ 160,7 160,7 @@ namespace boot
            m_os_image     = purefs::file::boot_bin;
            m_os_root_path = purefs::createPath(purefs::dir::getRootDiskPath(), m_os_type);
            m_boot_json    = bootJsonPath;
            m_timestamp    = utils::time::Timestamp().str("%c");
            m_timestamp    = utils::time::getCurrentTimestamp().str("%c");
            m_os_version   = std::string(VERSION);
            LOG_WARN("%s failed to parse %s: \"%s\"", __FUNCTION__, bootJsonPath.c_str(), parseErrors.c_str());
            return false;

D module-utils/parallel-hashmap => module-utils/parallel-hashmap +0 -1
@@ 1,1 0,0 @@
Subproject commit e77a448e95643a4cf9a0efdaa8fe91d44d97869c

D module-utils/protobuf => module-utils/protobuf +0 -1
@@ 1,1 0,0 @@
Subproject commit 1733c4f163746471c720d99100110f078b5dcd44

D module-utils/taglib => module-utils/taglib +0 -1
@@ 1,1 0,0 @@
Subproject commit 351dd5f5c8ad2fa95b483bee2505b8e926a21873

M module-utils/test/test_i18n.cpp => module-utils/test/test_i18n.cpp +4 -4
@@ 16,7 16,7 @@ TEST_CASE("Test set display language - empty display language string")
    REQUIRE(utils::getDisplayLanguage().empty());

    // Invalid string provided - language should be set to default one.
    CHECK(utils::setDisplayLanguage(""));
    CHECK_FALSE(utils::setDisplayLanguage(""));
    REQUIRE(utils::getDisplayLanguage() == utils::getDefaultLanguage());
}



@@ 27,7 27,7 @@ TEST_CASE("Test set display language - invalid display language string")
    REQUIRE(utils::getDisplayLanguage().empty());

    // Invalid string provided - language should be set to default one.
    CHECK(utils::setDisplayLanguage("NonExistingLanguage"));
    CHECK_FALSE(utils::setDisplayLanguage("NonExistingLanguage"));
    REQUIRE(utils::getDisplayLanguage() == utils::getDefaultLanguage());
}



@@ 38,7 38,7 @@ TEST_CASE("Test set display language - double invalid display language string")
    REQUIRE(utils::getDisplayLanguage().empty());

    // Invalid string provided - language should be set to default one.
    CHECK(utils::setDisplayLanguage(""));
    CHECK_FALSE(utils::setDisplayLanguage(""));
    REQUIRE(utils::getDisplayLanguage() == utils::getDefaultLanguage());

    // Invalid string provided - language already set to default one so method returns false.


@@ 93,7 93,7 @@ TEST_CASE("Test get string method - invalid display language set")
    REQUIRE(utils::getDisplayLanguage().empty());

    // Invalid string provided - language should be set to default one.
    CHECK(utils::setDisplayLanguage(""));
    CHECK_FALSE(utils::setDisplayLanguage(""));
    REQUIRE(utils::getDisplayLanguage() == utils::getDefaultLanguage());

    // Default fallback language set - English

M module-utils/third-party/libphonenumber.cmake => module-utils/third-party/libphonenumber.cmake +1 -5
@@ 67,11 67,7 @@ if (NOT RE2_TARGET)
endif ()
target_link_libraries (${LIBPHONENUMBER_TARGET} PUBLIC ${RE2_TARGET})

# protobuf dependency
if (NOT PROTOBUF_TARGET)
        message (FATAL_ERROR "Protobuf is required for libphonenumber")
endif()
target_link_libraries (${LIBPHONENUMBER_TARGET} PUBLIC ${PROTOBUF_TARGET})
target_link_libraries (${LIBPHONENUMBER_TARGET} PUBLIC protobuf::libprotobuf-lite)

# turn on optimization in debug
third_party_source_optimization (${LIBPHONENUMBER_SOURCES})

D module-utils/third-party/protobuf-lite.cmake => module-utils/third-party/protobuf-lite.cmake +0 -64
@@ 1,64 0,0 @@
include (thirdparty)

# add sources
set (PROTOBUF_SRCDIR ${CMAKE_CURRENT_SOURCE_DIR}/protobuf/src)
set (PROTOBUF ${PROTOBUF_SRCDIR}/google/protobuf)
set (PROTOBUF_SOURCES
        ${PROTOBUF}/any_lite.cc
        ${PROTOBUF}/arena.cc
        ${PROTOBUF}/extension_set.cc
        ${PROTOBUF}/generated_enum_util.cc
        ${PROTOBUF}/generated_message_table_driven_lite.cc
        ${PROTOBUF}/generated_message_util.cc
        ${PROTOBUF}/implicit_weak_message.cc
        ${PROTOBUF}/io/coded_stream.cc
        ${PROTOBUF}/io/io_win32.cc
        ${PROTOBUF}/io/strtod.cc
        ${PROTOBUF}/io/zero_copy_stream.cc
        ${PROTOBUF}/io/zero_copy_stream_impl.cc
        ${PROTOBUF}/io/zero_copy_stream_impl_lite.cc
        ${PROTOBUF}/message_lite.cc
        ${PROTOBUF}/parse_context.cc
        ${PROTOBUF}/repeated_field.cc
        ${PROTOBUF}/stubs/bytestream.cc
        ${PROTOBUF}/stubs/common.cc
        ${PROTOBUF}/stubs/int128.cc
        ${PROTOBUF}/stubs/status.cc
        ${PROTOBUF}/stubs/statusor.cc
        ${PROTOBUF}/stubs/stringpiece.cc
        ${PROTOBUF}/stubs/stringprintf.cc
        ${PROTOBUF}/stubs/structurally_valid.cc
        ${PROTOBUF}/stubs/strutil.cc
        ${PROTOBUF}/stubs/time.cc
        ${PROTOBUF}/wire_format_lite.cc
)

# create static library for the third party
set (PROTOBUF_TARGET protobuf)
add_library (${PROTOBUF_TARGET} STATIC ${PROTOBUF_SOURCES})

# setup flags for the third party
third_party_target_setup (${PROTOBUF_TARGET})

# set compile definitions for third party libraries
target_compile_definitions (${PROTOBUF_TARGET} PUBLIC GOOGLE_PROTOBUF_NO_THREADS)

# supress warnings for protobuf
set_source_files_properties (${PROTOBUF_SOURCES}
        PROPERTIES COMPILE_FLAGS
	"-Wno-stringop-truncation \
        -Wno-stringop-overflow \
        -Wno-sign-compare \
        -Wno-type-limits  \
        -Wno-redundant-move \
        -Wno-maybe-uninitialized"
)

# add include dir
target_include_directories (${PROTOBUF_TARGET} PUBLIC ${PROTOBUF_SRCDIR})

# module-os dependency (locking support)
target_link_libraries (${RE2_TARGET} PUBLIC module-os)

# turn on optimization in debug
third_party_source_optimization (${PROTOBUF_SOURCES})

D module-utils/third-party/tinyexpr.cmake => module-utils/third-party/tinyexpr.cmake +0 -20
@@ 1,20 0,0 @@
include (thirdparty)

# add tinyexpr library sources
set (TINYEXPR_SRCDIR ${CMAKE_CURRENT_SOURCE_DIR}/tinyexpr)
set (TINYEXPR_SOURCES
        ${TINYEXPR_SRCDIR}/tinyexpr.c
)

# create static library for the third party
set (TINYEXPR_TARGET tinyexpr)
add_library (${TINYEXPR_TARGET} STATIC ${TINYEXPR_SOURCES})

# setup flags for the third party
third_party_target_setup (${TINYEXPR_TARGET})

# add include directory path
target_include_directories (${TINYEXPR_TARGET} PUBLIC ${TINYEXPR_SRCDIR})

# optimize thirdy party sources in dbeug
third_party_source_optimization (${TINYEXPR_SOURCES})

M module-utils/time/time_conversion.cpp => module-utils/time/time_conversion.cpp +3 -25
@@ 56,7 56,6 @@ namespace utils::time
    } // namespace

    Locale tlocale;
    static int msTimeGmtOff = 4 * utils::time::minutesInQuarterOfHour * utils::time::secondsInMinute;

    UTF8 Localer::get_replacement(Replacements val, const struct tm &timeinfo)
    {


@@ 82,12 81,8 @@ namespace utils::time

    Timestamp::Timestamp()
    {
        auto err = bsp::rtc::getCurrentTimestamp(&time);
        time += utils::time::Time::getTimeZoneOffset();
        if (err != bsp::rtc::ErrorCode::OK) {
            LOG_ERROR("rtc::getCurrentTimestamp failure!");
        }
        timeinfo = *localtime(&time);
        time     = 0;
        timeinfo = *std::localtime(&time);
    }

    void Timestamp::set_time(time_t newtime)


@@ 284,16 279,6 @@ namespace utils::time
        }
    }

    void Time::setTimeZoneOffset(int tzOffset)
    {
        msTimeGmtOff = tzOffset;
    }

    int Time::getTimeZoneOffset()
    {
        return msTimeGmtOff;
    };

    Duration::Duration(time_t duration) : duration(duration)
    {
        calculate();


@@ 345,13 330,6 @@ namespace utils::time

    Timestamp getCurrentTimestamp()
    {
        return Timestamp{};
    }

    std::string getHoursMinInCurrentTimeFormat()
    {
        auto timestamp = getCurrentTimestamp();
        return utils::dateAndTimeSettings.isTimeFormat12() ? timestamp.str(hoursMinFormat12H)
                                                           : timestamp.str(hoursMinFormat24H);
        return Timestamp{std::time(nullptr)};
    }
}; // namespace utils::time

M module-utils/time/time_conversion.hpp => module-utils/time/time_conversion.hpp +2 -7
@@ 153,7 153,8 @@ namespace utils
            std::string long_ago_format = "%d.%m.%y";

            /// shows time in past: time_now - val in seconds
            DateTime(time_t val = 0, bool date_format_long = true) : date_format_long(date_format_long)
            DateTime(time_t val = 0, bool date_format_long = true)
                : Timestamp(std::time(nullptr)), date_format_long(date_format_long)
            {
                before_n_sec(val);
            }


@@ 185,11 186,6 @@ namespace utils
          public:
            Time(time_t val = 0, bool date_format_long = true) : DateTime(val, date_format_long){};
            virtual UTF8 str(std::string format = "") final;

            /// set time zone offset including adjustment for daylight saving
            static void setTimeZoneOffset(int tzOffset);
            // get time zone offset including adjustment for daylight saving
            static int getTimeZoneOffset();
        };

        class Duration


@@ 288,6 284,5 @@ namespace utils
        };

        Timestamp getCurrentTimestamp();
        std::string getHoursMinInCurrentTimeFormat();
    } // namespace time
} // namespace utils

D module-utils/tinyexpr => module-utils/tinyexpr +0 -1
@@ 1,1 0,0 @@
Subproject commit 2ef22d228f8760561b8f485f0997ca07fafd0f12

M module-vfs/CMakeLists.txt => module-vfs/CMakeLists.txt +22 -20
@@ 16,23 16,28 @@ set(FF_FAT_SOURCES
    ${FF_FAT_SOURCES_THIRDPARTY}
    ${CMAKE_CURRENT_SOURCE_DIR}/drivers/src/thirdparty/fatfs/ffsystem.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/drivers/src/thirdparty/fatfs/ff_glue.cpp
)
set(FF_LFS_SOURCES
    ${CMAKE_CURRENT_SOURCE_DIR}/drivers/src/thirdparty/littlefs/lfs_glue.cpp
)

set(SOURCES
        ${FF_FAT_SOURCES}
        src/purefs/filesystem_paths.cpp
        src/purefs/blkdev/disk_manager.cpp
        src/purefs/blkdev/disk.cpp
        src/purefs/blkdev/partition_parser.cpp
        src/purefs/blkdev/disk_handle.cpp
        src/purefs/fs/filesystem.cpp
        src/purefs/fs/filesystem_operations.cpp
        src/purefs/fs/filesystem_syscalls.cpp
        src/purefs/fs/filesystem_cwd.cpp
        src/purefs/vfs_subsystem.cpp
        drivers/src/purefs/fs/filesystem_vfat.cpp
        drivers/src/purefs/fs/filesystem_littlefs.cpp
    ${FF_FAT_SOURCES}
    ${FF_LFS_SOURCES}
    src/purefs/filesystem_paths.cpp
    src/purefs/blkdev/disk_manager.cpp
    src/purefs/blkdev/disk.cpp
    src/purefs/blkdev/partition_parser.cpp
    src/purefs/blkdev/disk_handle.cpp
    src/purefs/fs/filesystem.cpp
    src/purefs/fs/filesystem_operations.cpp
    src/purefs/fs/filesystem_syscalls.cpp
    src/purefs/fs/filesystem_cwd.cpp
    src/purefs/vfs_subsystem.cpp
    src/purefs/fs/notifier.cpp
    src/purefs/fs/fsnotify.cpp
    drivers/src/purefs/fs/filesystem_vfat.cpp
    drivers/src/purefs/fs/filesystem_littlefs.cpp
)




@@ 64,23 69,20 @@ target_compile_definitions(${PROJECT_NAME}

target_include_directories(${PROJECT_NAME}
        PUBLIC
        ${CMAKE_CURRENT_SOURCE_DIR}/include/user/deprecated
        ${CMAKE_CURRENT_SOURCE_DIR}/include/user/
        ${CMAKE_CURRENT_SOURCE_DIR}/include/internal/
        ${CMAKE_CURRENT_SOURCE_DIR}/include/user
        ${CMAKE_CURRENT_SOURCE_DIR}/drivers/include
)

target_include_directories(${PROJECT_NAME}
        PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/include/internal
        ${CMAKE_CURRENT_SOURCE_DIR}/drivers/include/thirdparty/fatfs
        ${CMAKE_CURRENT_SOURCE_DIR}/drivers/include/thirdparty
        ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/fatfs/source
)

target_link_libraries(${PROJECT_NAME} PUBLIC ${TARGET_LIBRARIES} module-bsp module-utils)
target_link_libraries(${PROJECT_NAME} PRIVATE littlefs )
# TODO: Temporary only remove when removing old vfs
target_link_libraries(${PROJECT_NAME} PUBLIC utils-bootconfig )
target_link_libraries(${PROJECT_NAME} PUBLIC ${TARGET_LIBRARIES} module-os module-sys module-bsp module-utils)
target_link_libraries(${PROJECT_NAME} PRIVATE littlefs)

if (${ENABLE_TESTS})
    add_subdirectory(tests)

A module-vfs/include/internal/purefs/fs/notifier.hpp => module-vfs/include/internal/purefs/fs/notifier.hpp +130 -0
@@ 0,0 1,130 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once

#include <map>
#include <string>
#include <memory>
#include <optional>
#include <purefs/fs/fsnotify.hpp>
#include <purefs/fs/inotify_flags.hpp>

namespace sys
{
    class Service;
}
namespace cpp_freertos
{
    class MutexRecursive;
}
namespace purefs::fs::internal
{
    //! Internal class related to the notify VFS events
    class notifier
    {
        //! Container for path item
        struct path_item
        {
            path_item(std::string_view _path, bool _read_only) : path(_path), read_only(_read_only)
            {}
            const std::string path;
            const bool read_only;
        };
        //! Container for service and subscribed events
        struct service_item
        {
            service_item(std::weak_ptr<sys::Service> _service, inotify_flags _subscribed_events)
                : service(_service), subscribed_events(_subscribed_events)
            {}
            const std::weak_ptr<sys::Service> service;
            const inotify_flags subscribed_events;
        };
        //! Container for the the event
        using container_t = std::multimap<std::string, service_item>;

      public:
        notifier();
        notifier(notifier &) = delete;
        notifier &operator=(notifier &) = delete;
        virtual ~notifier();
        //! Iterator for the registered event
        using item_it = container_t::iterator;
        /**
         * @brief Register selected path for the monitoring
         *
         * @param path Path for monitor
         * @param owner Service which should be notified
         * @param flags Event mask which should be monitored
         * @return std::optional<item_it>  Registered event iterator or nothing if failed
         */
        auto register_path(std::string_view path, std::shared_ptr<sys::Service> owner, inotify_flags flags)
            -> std::optional<item_it>;
        /**
         * @brief Unregister selected path from monitoring
         *
         * @param item Iterator returned by the @see register_path method
         */
        auto unregister_path(item_it item) -> void;
        /**
         * @brief Internal method called on file open
         *
         * @param path Open file path
         * @param fd File descriptor assigned by open method
         * @param ro File is opened in read only mode
         */
        auto notify_open(std::string_view path, int fd, bool ro) const -> void;
        /**
         * @brief Internal method called on closing gile
         *
         * @param fd File descriptor
         */
        auto notify_close(int fd) const -> void;
        /**
         * @brief Notify for event on the file system
         *
         * @param fd File descriptor for file
         * @param mask Filesystem event type
         */
        auto notify(int fd, inotify_flags mask) const -> void;
        /**
         * @brief Notify for event on filesystem on path change
         *
         * @param path Path before event occured
         * @param path_prv Path after event occured
         * @param mask Filesystem event type
         */
        auto notify(std::string_view path, std::string_view path_prv, inotify_flags mask) const -> void;
        /**
         * @brief Notify for event on the filesystem
         *
         * @param path Path related to th event
         * @param mask Filesystem event type
         */
        auto notify(std::string_view path, inotify_flags mask) const -> void
        {
            notify(path, "", mask);
        }

      private:
        /**
         * @brief Private method called for send file monitor event
         *
         * @param svc Target service
         * @param flags Filesystem event type
         * @param name Path related to the event before change
         * @param name_dst Path realated to the event after change
         */
        virtual auto send_notification(std::shared_ptr<sys::Service> svc,
                                       inotify_flags flags,
                                       std::string_view name,
                                       std::string_view name_dst) const -> void;

      private:
        //! Events container
        container_t m_events;
        //! Map file descriptors with path assiociated with it
        mutable std::map<int, path_item> m_fd_map;
        //! Internal mutex for lock the object
        std::unique_ptr<cpp_freertos::MutexRecursive> m_lock;
    };
} // namespace purefs::fs::internal

D module-vfs/include/user/deprecated/vfsNotifier.hpp => module-vfs/include/user/deprecated/vfsNotifier.hpp +0 -98
@@ 1,98 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
//
#pragma once
#include <unordered_map>
#include <mutex.hpp>
#include <functional>
#include <string>
#include <thread.hpp>

namespace vfsn::utility
{
    class vfsNotifier
    {
        struct FF_FILE;
        using FILE = FF_FILE;

      public:
        //! Default constructor and destructor
        vfsNotifier()                    = default;
        vfsNotifier(const vfsNotifier &) = delete;
        vfsNotifier &operator=(const vfsNotifier &) = delete;
        //! Event type notified for indexer
        enum class FsEvent
        {
            initialized, //! Filesystem is intialized
            modified,    //! File is modified
            deleted,     //! File is deleted
            renamed
        };
        /** Notification handler type
         * Notify function callback defiinition
         */
        using NotifyHandler = std::function<void(std::string_view, FsEvent, std::string_view)>;
        /** This method is called by the vfs layer when vfs open file
         * @param[in] filename Opened file path
         * @param[in] mode Open mode
         * @param[in] Handle to opened file
         * @return None
         */
        auto onFileOpen(std::string_view filename, const char *mode, const FILE *file) noexcept -> void;
        /** This method is called by the vfs layer when vfs close file
         * @param[in] filee Closed file handle
         * @return None
         */
        auto onFileClose(const FILE *file) noexcept -> void;
        /** This method is called by the vfs layer when file is removed
         * @param[in] filename Removed file path
         * @return None
         */
        auto onFileRemove(std::string_view filename) noexcept -> void;
        /** This method is called by the vfs layer when the vfs rename file
         * @param[in] new_file New path name for the file
         * @param[in] old_file Old path for the file
         * @return None
         */
        auto onFileRename(std::string_view new_file, std::string_view old_file) noexcept -> void;
        /** This method is called by the vfs layer when the disk is intiialized
         * @return None
         */
        auto onFileSystemInitialized() noexcept -> void
        {
            notify("/", FsEvent::initialized);
        }
        /** Method for register notification handler
         * @note This function is called from the thread contest which use vfs call
         * @param[in] hwnd Notification handler funtion
         * @return None
         */
        auto registerNotificationHandler(NotifyHandler hwnd) -> void
        {
            notificationCallback = hwnd;
            threadHandle         = hwnd ? cpp_freertos::Thread::GetCurrentThreadHandle() : nullptr;
        }

      private:
        /** Private notification helper internal method
         * @param[in] file Modified file path
         * @param[in] event Notification event type
         * @param[in] old_file Old file path
         * @return None
         */
        auto notify(std::string_view file, FsEvent event, std::string_view old_file = "") -> void
        {
            if (threadHandle != cpp_freertos::Thread::GetCurrentThreadHandle() && notificationCallback)
                notificationCallback(file, event, old_file);
        }

      private:
        //! Map for opened handles and paths
        std::unordered_map<const FILE *, std::string> mOpenedMap;
        //! Mutex for unordered map
        cpp_freertos::MutexStandard mMutex;
        //! Notification handler callback
        NotifyHandler notificationCallback;
        TaskHandle_t threadHandle;
    };
} // namespace vfsn::utility

R module-vfs/include/internal/purefs/fs/directory_handle.hpp => module-vfs/include/user/purefs/fs/directory_handle.hpp +0 -0
R module-vfs/include/internal/purefs/fs/file_handle.hpp => module-vfs/include/user/purefs/fs/file_handle.hpp +1 -1
@@ 14,7 14,7 @@ namespace purefs::fs::internal
      public:
        file_handle(const file_handle &) = delete;
        auto operator=(const file_handle &) = delete;
        virtual ~file_handle()           = default;
        virtual ~file_handle()              = default;
        file_handle(std::shared_ptr<mount_point> mp, unsigned flags) : m_mount_point(mp), m_flags(flags)
        {}
        [[nodiscard]] auto error() const noexcept

M module-vfs/include/user/purefs/fs/filesystem.hpp => module-vfs/include/user/purefs/fs/filesystem.hpp +11 -1
@@ 15,6 15,7 @@
#include <purefs/fs/directory_handle.hpp>
#include <purefs/fs/mount_point.hpp>
#include <purefs/fs/mount_flags.hpp>
#include <purefs/fs/fsnotify.hpp>
#include <type_traits>

struct statvfs;


@@ 30,6 31,11 @@ namespace cpp_freertos
    class MutexRecursive;
}

namespace sys
{
    class Service;
}

namespace purefs::fs
{
    /** This is the filesystem class layer


@@ 41,6 47,7 @@ namespace purefs::fs
    namespace internal
    {
        class directory_handle;
        class notifier;
    }
    class filesystem
    {


@@ 142,6 149,9 @@ namespace purefs::fs
        auto getcwd() noexcept -> std::string_view;
        auto chdir(std::string_view name) noexcept -> int;

        /** Inotify API */
        [[nodiscard]] auto inotify_create(std::shared_ptr<sys::Service> svc) -> std::shared_ptr<inotify>;

      private:
        /** Unregister filesystem driver
         * @param[in] fsname Unique filesystem name for example fat


@@ 271,7 281,6 @@ namespace purefs::fs
                }
            }
        }

      private:
        std::weak_ptr<blkdev::disk_manager> m_diskmm;
        std::unordered_map<std::string, std::shared_ptr<filesystem_operations>> m_fstypes;


@@ 279,5 288,6 @@ namespace purefs::fs
        std::unordered_set<std::string> m_partitions;
        internal::handle_mapper<fsfile> m_fds;
        std::unique_ptr<cpp_freertos::MutexRecursive> m_lock;
        std::shared_ptr<internal::notifier> m_notifier;
    };
} // namespace purefs::fs

A module-vfs/include/user/purefs/fs/fsnotify.hpp => module-vfs/include/user/purefs/fs/fsnotify.hpp +58 -0
@@ 0,0 1,58 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include <string>
#include <memory>
#include <purefs/fs/inotify_flags.hpp>

namespace sys
{
    class Service;
}
namespace cpp_freertos
{
    class MutexRecursive;
}
namespace purefs::fs
{
    namespace internal
    {
        class notifier;
        struct inotify_container;
    } // namespace internal

    //! Inotifier object returned by the monitoring API
    class inotify
    {
      public:
        ~inotify();
        /** Constructor for the inotify user object
         * @param[in] svc Service owner
         * @param[in] notifier Master notifier object
         */
        inotify(std::shared_ptr<sys::Service> svc, std::shared_ptr<purefs::fs::internal::notifier> notifier);
        /**  Add path for monitoring for monitoring
         * @param[in] monitored_path Path or file which should be monitored
         * @param[in] event_mask Event mask for file monitor
         * @return Error code
         */
        int add_watch(std::string_view monitored_path, inotify_flags event_mask);
        /**
         * @param[in] monitored_path Monitored path for removal
         * @return Error code
         */
        int rm_watch(std::string_view monitored_path);

      private:
        //! Owning service
        std::weak_ptr<sys::Service> m_svc;
        //! Owner notifier
        std::weak_ptr<internal::notifier> m_notify;
        //! Map for the devices
        std::unique_ptr<internal::inotify_container> m_evlist;
        //! Lock the object
        std::unique_ptr<cpp_freertos::MutexRecursive> m_lock;
    };

} // namespace purefs::fs

R module-vfs/include/internal/purefs/fs/handle_mapper.hpp => module-vfs/include/user/purefs/fs/handle_mapper.hpp +0 -0
A module-vfs/include/user/purefs/fs/inotify.hpp => module-vfs/include/user/purefs/fs/inotify.hpp +22 -0
@@ 0,0 1,22 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once
#include <memory>
#include <purefs/vfs_subsystem.hpp>
#include <purefs/fs/fsnotify.hpp>

namespace purefs::fs
{
    /** Create inotify class which owns the notifier interface
     * @param[in] svc Owner of the service
     * @return Inotify object shared ptr or errno if nullptr
     */
    inline auto inotify_create(std::shared_ptr<sys::Service> svc) -> std::shared_ptr<inotify>
    {
        const auto vfs = subsystem::vfs_core();
        if (!vfs) {
            return nullptr;
        }
        return vfs->inotify_create(svc);
    }
} // namespace purefs::fs

A module-vfs/include/user/purefs/fs/inotify_flags.hpp => module-vfs/include/user/purefs/fs/inotify_flags.hpp +36 -0
@@ 0,0 1,36 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
namespace purefs::fs
{
    //! Event monitor flag
    enum class inotify_flags : unsigned
    {
        attrib        = 0x01, //! Attribute changed
        close_write   = 0x02, //! File closed after write
        close_nowrite = 0x04, //! File closed without write
        del           = 0x08, //! File was deleted
        move_src      = 0x10, //! File moved
        move_dst      = 0x20, //! File moved
        open          = 0x40, //! File was opended
        dmodify       = 0x80, //! Directory entry modified
    };
    inline auto operator|(inotify_flags fl1, inotify_flags fl2)
    {
        return static_cast<inotify_flags>(static_cast<unsigned>(fl1) | static_cast<unsigned>(fl2));
    }
    inline auto operator&(inotify_flags fl1, inotify_flags fl2)
    {
        return static_cast<inotify_flags>(static_cast<unsigned>(fl1) & static_cast<unsigned>(fl2));
    }
    inline auto operator&&(inotify_flags fl1, inotify_flags fl2) -> bool
    {
        return static_cast<bool>(static_cast<unsigned>(fl1) & static_cast<unsigned>(fl2));
    }
    inline auto operator||(inotify_flags fl1, inotify_flags fl2) -> bool
    {
        return static_cast<bool>(static_cast<unsigned>(fl1) | static_cast<unsigned>(fl2));
    }

} // namespace purefs::fs

A module-vfs/include/user/purefs/fs/inotify_message.hpp => module-vfs/include/user/purefs/fs/inotify_message.hpp +28 -0
@@ 0,0 1,28 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include <Service/Message.hpp>
#include <purefs/fs/inotify_flags.hpp>

namespace purefs::fs::message
{
    //! Class message received when on new file event
    struct inotify final : public ::sys::DataMessage
    {
        /**
         * @brief Construct a new inotify object
         *
         * @param _flags Event type
         * @param _name  Filename path
         * @param _name_prev Old path (when move)
         */
        inotify(inotify_flags _flags, std::string_view _name, std::string_view _name_prev)
            : flags(_flags), name(_name), name_prev(_name_prev)
        {}
        virtual ~inotify() = default;
        const inotify_flags flags;
        const std::string name;
        const std::string name_prev;
    };
} // namespace purefs::fs::message

R module-vfs/include/internal/purefs/fs/mount_flags.hpp => module-vfs/include/user/purefs/fs/mount_flags.hpp +0 -0
R module-vfs/include/internal/purefs/fs/mount_point.hpp => module-vfs/include/user/purefs/fs/mount_point.hpp +1 -1
@@ 31,7 31,7 @@ namespace purefs::fs::internal
        {}
        mount_point(const mount_point &) = delete;
        auto operator=(const mount_point &) = delete;
        virtual ~mount_point() = default;
        virtual ~mount_point()              = default;
        auto disk() const noexcept
        {
            return m_diskh.lock();

D module-vfs/src/deprecated/vfsNotifier.cpp => module-vfs/src/deprecated/vfsNotifier.cpp +0 -38
@@ 1,38 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "vfsNotifier.hpp"
#include <vfs.hpp>
#include <cstring>

namespace vfsn::utility
{

    auto vfsNotifier::onFileOpen(std::string_view filename, const char *mode, const FILE *file) noexcept -> void
    {
        if (file && std::strpbrk(mode, "+wa")) {
            cpp_freertos::LockGuard _lock(mMutex);
            mOpenedMap.emplace(file, vfs.getAbsolutePath(filename));
        }
    }
    auto vfsNotifier::onFileClose(const FILE *file) noexcept -> void
    {
        cpp_freertos::LockGuard _lock(mMutex);
        const auto item = mOpenedMap.find(file);
        if (item != mOpenedMap.end()) {
            const auto path = item->second;
            mOpenedMap.erase(item);
            notify(path, FsEvent::modified);
        }
    }

    auto vfsNotifier::onFileRemove(std::string_view filename) noexcept -> void
    {
        notify(vfs.getAbsolutePath(filename), FsEvent::deleted);
    }

    auto vfsNotifier::onFileRename(std::string_view new_file, std::string_view old_file) noexcept -> void
    {
        notify(vfs.getAbsolutePath(new_file), FsEvent::renamed, vfs.getAbsolutePath(old_file));
    }
} // namespace vfsn::utility

M module-vfs/src/purefs/fs/filesystem.cpp => module-vfs/src/purefs/fs/filesystem.cpp +11 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <purefs/fs/filesystem.hpp>
#include <purefs/fs/filesystem_operations.hpp>


@@ 6,6 6,8 @@
#include <purefs/blkdev/disk_manager.hpp>
#include <purefs/fs/thread_local_cwd.hpp>
#include <purefs/blkdev/disk_handle.hpp>
#include <purefs/fs/notifier.hpp>
#include <purefs/fs/fsnotify.hpp>
#include <log/log.hpp>
#include <split_sv.hpp>
#include <errno.h>


@@ 21,7 23,8 @@ namespace purefs::fs
        };
    }
    filesystem::filesystem(std::shared_ptr<blkdev::disk_manager> diskmm)
        : m_diskmm(diskmm), m_lock(new cpp_freertos::MutexRecursive)
        : m_diskmm(diskmm), m_lock(std::make_unique<cpp_freertos::MutexRecursive>()),
          m_notifier(std::make_unique<internal::notifier>())
    {}

    filesystem::~filesystem()


@@ 308,4 311,10 @@ namespace purefs::fs
            return {};
        }
    }

    auto filesystem::inotify_create(std::shared_ptr<sys::Service> svc) -> std::shared_ptr<inotify>
    {
        return std::make_shared<inotify>(svc, m_notifier);
    }

} // namespace purefs::fs

M module-vfs/src/purefs/fs/filesystem_syscalls.cpp => module-vfs/src/purefs/fs/filesystem_syscalls.cpp +47 -10
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <purefs/fs/filesystem.hpp>
#include <errno.h>


@@ 7,6 7,7 @@
#include <purefs/fs/file_handle.hpp>
#include <purefs/fs/directory_handle.hpp>
#include <purefs/fs/thread_local_cwd.hpp>
#include <purefs/fs/notifier.hpp>
#include <fcntl.h>

namespace purefs::fs


@@ 23,12 24,20 @@ namespace purefs::fs

    auto filesystem::unlink(std::string_view name) noexcept -> int
    {
        return invoke_fops(iaccess::rw, &filesystem_operations::unlink, name);
        const auto err = invoke_fops(iaccess::rw, &filesystem_operations::unlink, name);
        if (!err) {
            m_notifier->notify(name, inotify_flags::del);
        }
        return err;
    }

    auto filesystem::mkdir(std::string_view path, int mode) noexcept -> int
    {
        return invoke_fops(iaccess::rw, &filesystem_operations::mkdir, path, mode);
        const auto err = invoke_fops(iaccess::rw, &filesystem_operations::mkdir, path, mode);
        if (!err) {
            m_notifier->notify(path, inotify_flags::dmodify);
        }
        return err;
    }

    auto filesystem::ioctl(std::string_view path, int cmd, void *arg) noexcept -> int


@@ 38,7 47,11 @@ namespace purefs::fs

    auto filesystem::utimens(std::string_view path, std::array<timespec, 2> &tv) noexcept -> int
    {
        return invoke_fops(iaccess::ro, &filesystem_operations::utimens, path, tv);
        const auto err = invoke_fops(iaccess::ro, &filesystem_operations::utimens, path, tv);
        if (!err) {
            m_notifier->notify(path, inotify_flags::attrib);
        }
        return err;
    }

    auto filesystem::flock(int fd, int cmd) noexcept -> int


@@ 53,7 66,11 @@ namespace purefs::fs

    auto filesystem::chmod(std::string_view path, mode_t mode) noexcept -> int
    {
        return invoke_fops(iaccess::rw, &filesystem_operations::chmod, path, mode);
        const auto err = invoke_fops(iaccess::rw, &filesystem_operations::chmod, path, mode);
        if (!err) {
            m_notifier->notify(path, inotify_flags::attrib);
        }
        return err;
    }

    auto filesystem::write(int fd, const char *ptr, size_t len) noexcept -> ssize_t


@@ 88,22 105,39 @@ namespace purefs::fs

    auto filesystem::fchmod(int fd, mode_t mode) noexcept -> int
    {
        return invoke_fops(&filesystem_operations::fchmod, fd, mode);
        const auto err = invoke_fops(&filesystem_operations::fchmod, fd, mode);
        if (!err) {
            m_notifier->notify(fd, inotify_flags::attrib);
        }
        return err;
    }

    auto filesystem::symlink(std::string_view existing, std::string_view newlink) noexcept -> int
    {
        return invoke_fops_same_mp(&filesystem_operations::symlink, existing, newlink);
        const auto err = invoke_fops_same_mp(&filesystem_operations::symlink, existing, newlink);
        if (!err) {
            m_notifier->notify(newlink, inotify_flags::dmodify);
        }
        return err;
    }

    auto filesystem::link(std::string_view existing, std::string_view newlink) noexcept -> int
    {
        return invoke_fops_same_mp(&filesystem_operations::link, existing, newlink);
        const auto err = invoke_fops_same_mp(&filesystem_operations::link, existing, newlink);
        if (!err) {
            m_notifier->notify(newlink, inotify_flags::dmodify);
        }
        return err;
    }

    auto filesystem::rename(std::string_view oldname, std::string_view newname) noexcept -> int
    {
        return invoke_fops_same_mp(&filesystem_operations::rename, oldname, newname);
        const auto err = invoke_fops_same_mp(&filesystem_operations::rename, oldname, newname);
        if (!err) {
            m_notifier->notify(oldname, newname, inotify_flags::move_src);
            m_notifier->notify(newname, oldname, inotify_flags::move_dst);
        }
        return err;
    }

    auto filesystem::open(std::string_view path, int flags, int mode) noexcept -> int


@@ 129,7 163,9 @@ namespace purefs::fs
            if (err) {
                return err;
            }
            return add_filehandle(fh);
            const auto fd = add_filehandle(fh);
            m_notifier->notify_open(path, fd, (flags & O_ACCMODE) == O_RDONLY);
            return fd;
        }
        else {
            LOG_ERROR("VFS: Unable to lock fops");


@@ 142,6 178,7 @@ namespace purefs::fs
        auto ret = invoke_fops(&filesystem_operations::close, fd);
        if (!ret) {
            ret = (remove_filehandle(fd)) ? (0) : (-EBADF);
            m_notifier->notify_close(fd);
        }
        return ret;
    }

A module-vfs/src/purefs/fs/fsnotify.cpp => module-vfs/src/purefs/fs/fsnotify.cpp +83 -0
@@ 0,0 1,83 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <purefs/fs/fsnotify.hpp>
#include <purefs/fs/notifier.hpp>
#include <log/log.hpp>
#include <mutex.hpp>
#include <cerrno>

namespace purefs::fs
{
    namespace internal
    {
        struct inotify_container
        {
            std::map<std::string, internal::notifier::item_it> items;
        };
    } // namespace internal
    inotify::inotify(std::shared_ptr<sys::Service> svc, std::shared_ptr<internal::notifier> notifier)
        : m_svc(svc), m_notify(notifier), m_evlist(std::make_unique<internal::inotify_container>()),
          m_lock(std::make_unique<cpp_freertos::MutexRecursive>())
    {}

    inotify::~inotify()
    {
        const auto notifier = m_notify.lock();
        if (!notifier) {
            LOG_ERROR("Unable lock notifier");
        }
        const auto svc = m_svc.lock();
        if (!svc) {
            LOG_ERROR("Unable lock service");
        }
        for (const auto &[_, val] : m_evlist->items) {
            notifier->unregister_path(val);
        }
    }

    int inotify::add_watch(std::string_view monitored_path, inotify_flags event_mask)
    {
        const auto notifier = m_notify.lock();
        if (!notifier) {
            LOG_ERROR("Unable lock notifier");
            return -ENXIO;
        }
        const auto svc = m_svc.lock();
        if (!svc) {
            LOG_ERROR("Unable lock service");
            return -ENXIO;
        }
        auto it = notifier->register_path(monitored_path, svc, event_mask);
        if (!it) {
            LOG_ERROR("Unable to register path");
            return -EIO;
        }
        cpp_freertos::LockGuard _lck(*m_lock);
        m_evlist->items.emplace(std::make_pair(std::string(monitored_path), *it));
        return {};
    }

    int inotify::rm_watch(std::string_view monitored_path)
    {
        const auto notifier = m_notify.lock();
        if (!notifier) {
            LOG_ERROR("Unable lock notifier");
            return -ENXIO;
        }
        const auto svc = m_svc.lock();
        if (!svc) {
            LOG_ERROR("Unable lock service");
            return -ENXIO;
        }
        cpp_freertos::LockGuard _lck(*m_lock);
        auto it = m_evlist->items.find(std::string(monitored_path));
        if (it == std::end(m_evlist->items)) {
            LOG_ERROR("Unable to find registered path %s", std::string(monitored_path).c_str());
            return -ENOENT;
        }
        notifier->unregister_path(it->second);
        m_evlist->items.erase(it);
        return {};
    }
} // namespace purefs::fs

A module-vfs/src/purefs/fs/notifier.cpp => module-vfs/src/purefs/fs/notifier.cpp +115 -0
@@ 0,0 1,115 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <purefs/fs/notifier.hpp>
#include <purefs/fs/inotify_message.hpp>
#include <functional>
#include <Service/Service.hpp>
#include <purefs/fs/inotify_message.hpp>
#include <purefs/fs/thread_local_cwd.hpp>
#include <log/log.hpp>

namespace purefs::fs::internal
{
    namespace
    {
        void for_path(std::string_view path, std::function<void(std::string_view)> fun)
        {
            constexpr auto sep = '/';
            for (auto it = (path.back() == sep) ? (path.size() - 1) : (path.size()); (it && it != std::string::npos);
                 it      = path.rfind(sep, it - 1))
                fun(path.substr(0, it));
        }
        std::string absolute_path(std::string_view path)
        {
            using namespace std::string_literals;
            std::string ret;
            if (!path.empty() && path[0] != '/') {
                ret = std::string(internal::get_thread_local_cwd_path()) + "/"s + std::string(path);
            }
            else {
                ret = std::string(path);
            }
            if (!ret.empty() && ret.back() == '/') {
                ret.pop_back();
            }
            return ret;
        }
    } // namespace
    notifier::notifier() : m_lock(std::make_unique<cpp_freertos::MutexRecursive>())
    {}
    notifier::~notifier()
    {}
    auto notifier::register_path(std::string_view path, std::shared_ptr<sys::Service> owner, inotify_flags flags)
        -> std::optional<item_it>
    {
        cpp_freertos::LockGuard _lck(*m_lock);
        const auto abspath = absolute_path(path);
        // # Check if it is already registered for same path
        const auto range = m_events.equal_range(abspath);
        for (auto i = range.first; i != range.second; ++i) {
            if (i->second.service.lock() == owner) {
                return std::nullopt;
            }
        }
        return m_events.emplace(std::make_pair(abspath, service_item(owner, flags)));
    }
    auto notifier::unregister_path(item_it item) -> void
    {
        cpp_freertos::LockGuard _lck(*m_lock);
        m_events.erase(item);
    }
    auto notifier::notify(int fd, inotify_flags mask) const -> void
    {
        cpp_freertos::LockGuard _lck(*m_lock);
        const auto fname_it = m_fd_map.find(fd);
        if (fname_it != std::end(m_fd_map)) {
            notify(fname_it->second.path, mask);
        }
    }
    void notifier::notify(std::string_view path, std::string_view path_prv, inotify_flags mask) const
    {
        cpp_freertos::LockGuard _lck(*m_lock);
        const auto abs_path     = absolute_path(path);
        const auto abs_path_prv = absolute_path(path_prv);
        for_path(abs_path, [this, abs_path, abs_path_prv, mask](std::string_view path) {
            const auto range = m_events.equal_range(std::string(path));
            for (auto i = range.first; i != range.second; ++i) {
                if (i->second.subscribed_events && mask) {
                    auto svc = i->second.service.lock();
                    if (svc) {
                        send_notification(svc, mask, abs_path, abs_path_prv);
                    }
                }
            }
        });
    }
    auto notifier::notify_open(std::string_view path, int fd, bool ro) const -> void
    {
        cpp_freertos::LockGuard _lck(*m_lock);
        m_fd_map.emplace(std::make_pair(fd, path_item(path, ro)));
        notify(path, inotify_flags::open);
    }
    auto notifier::notify_close(int fd) const -> void
    {
        cpp_freertos::LockGuard _lck(*m_lock);
        const auto fname_it = m_fd_map.find(fd);
        if (fname_it != std::end(m_fd_map)) {
            notify(fname_it->first,
                   fname_it->second.read_only ? inotify_flags::close_nowrite : inotify_flags::close_write);
            m_fd_map.erase(fname_it);
        }
    }
    auto notifier::send_notification(std::shared_ptr<sys::Service> svc,
                                     inotify_flags flags,
                                     std::string_view name,
                                     std::string_view name_dst) const -> void
    {
        if (svc->GetHandle() != cpp_freertos::Thread::GetCurrentThreadHandle()) {
            auto msg = std::make_shared<message::inotify>(flags, name, name_dst);
            svc->bus.sendUnicast(std::move(msg), svc->GetName());
        }
        else {
            LOG_WARN("Sent notification to the same thread is forbidded");
        }
    }
} // namespace purefs::fs::internal

M module-vfs/tests/CMakeLists.txt => module-vfs/tests/CMakeLists.txt +17 -0
@@ 22,6 22,8 @@ add_catch2_executable(
    NAME vfs-core-fs
    SRCS
        ${CMAKE_CURRENT_LIST_DIR}/unittest_filesystem_core.cpp
    INCLUDE 
        ${CMAKE_CURRENT_SOURCE_DIR}/../include/internal
    LIBS
        module-vfs
)


@@ 38,6 40,8 @@ add_catch2_executable(
    NAME vfs-littlefs
    SRCS
        ${CMAKE_CURRENT_LIST_DIR}/unittest_filesystem_littlefs.cpp
    INCLUDE 
        ${CMAKE_CURRENT_SOURCE_DIR}/../include/internal
    LIBS
        module-vfs
    DEPS


@@ 48,12 52,25 @@ add_catch2_executable(
    NAME vfs-dualmount
    SRCS
        ${CMAKE_CURRENT_LIST_DIR}/unittest_filesystem_dualmount.cpp
    INCLUDE 
        ${CMAKE_CURRENT_SOURCE_DIR}/../include/internal

    LIBS
        module-vfs
    DEPS
        ${LITTLEFS_IMAGE}
)

add_catch2_executable(
    NAME vfs-inotify
    SRCS
        ${CMAKE_CURRENT_LIST_DIR}/unittest_filesystem_inotify.cpp
    LIBS
        module-vfs
    INCLUDE 
        ${CMAKE_CURRENT_SOURCE_DIR}/../include/internal
)

# prepare test assets
set(ASSETS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/test_dir")
set(ASSETS_TARGET_DIR "${CMAKE_BINARY_DIR}/module-vfs/test_dir")

A module-vfs/tests/unittest_filesystem_inotify.cpp => module-vfs/tests/unittest_filesystem_inotify.cpp +168 -0
@@ 0,0 1,168 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
#include <purefs/fs/notifier.hpp>
#include <purefs/fs/inotify_message.hpp>

namespace purefs::fs
{

    struct notifier_mock final : public internal::notifier
    {
        virtual ~notifier_mock() = default;
        auto send_notification(std::shared_ptr<sys::Service> svc,
                               inotify_flags flags,
                               std::string_view name,
                               std::string_view name_dst) const -> void override
        {
            messages.emplace_back(flags, name, name_dst);
            xsvc.push_back(svc.get());
        }
        mutable std::vector<message::inotify> messages;
        mutable std::vector<sys::Service *> xsvc;
    };
} // namespace purefs::fs

namespace sys
{
    class Service
    {};
} // namespace sys

TEST_CASE("Notifier base class test")
{
    using namespace purefs::fs;
    auto svc = std::make_shared<sys::Service>();
    notifier_mock notify;
    notify.register_path("/sys/ala",
                         svc,
                         inotify_flags::open | inotify_flags::close_nowrite | inotify_flags::close_write |
                             inotify_flags::attrib | inotify_flags::move_src | inotify_flags::move_dst);
    SECTION("open and close simple")
    {
        notify.notify_open("/sys/ala", 77, true);
        notify.notify_close(77);
        REQUIRE(notify.messages.size() == 2);
        REQUIRE(notify.messages[0].name == "/sys/ala");
        REQUIRE(notify.messages[0].name_prev.empty());
        REQUIRE((notify.messages[0].flags && inotify_flags::open));
        REQUIRE(notify.messages[1].name == "/sys/ala");
        REQUIRE(notify.messages[1].name_prev.empty());
        REQUIRE((notify.messages[1].flags && inotify_flags::close_nowrite));
        notify.messages.clear();
    }
    SECTION("open and close pairing")
    {
        notify.notify_open("/sys/ala/1", 100, true);
        notify.notify_open("/sys/ala/2", 200, false);
        notify.notify_open("/sys/ala/3", 300, true);
        REQUIRE(notify.messages.size() == 3);
        notify.notify(200, inotify_flags::attrib);
        REQUIRE(notify.messages.size() == 4);
        notify.notify_close(200);
        REQUIRE(notify.messages.size() == 5);
        notify.notify_close(200);
        REQUIRE(notify.messages.size() == 5);
        REQUIRE(notify.messages[0].name == "/sys/ala/1");
        REQUIRE((notify.messages[0].flags && inotify_flags::open));
        REQUIRE(notify.messages[1].name == "/sys/ala/2");
        REQUIRE((notify.messages[1].flags && inotify_flags::open));
        REQUIRE(notify.messages[2].name == "/sys/ala/3");
        REQUIRE((notify.messages[2].flags && inotify_flags::open));
        REQUIRE(notify.messages[3].name == "/sys/ala/2");
        REQUIRE((notify.messages[3].flags && inotify_flags::attrib));
        REQUIRE(notify.messages[4].name == "/sys/ala/2");
        REQUIRE((notify.messages[4].flags && inotify_flags::close_write));
        notify.messages.clear();
        notify.notify_close(100);
        notify.notify_close(300);
        REQUIRE(notify.messages.size() == 2);
        REQUIRE(notify.messages[0].name == "/sys/ala/1");
        REQUIRE((notify.messages[0].flags && inotify_flags::close_nowrite));
        REQUIRE(notify.messages[1].name == "/sys/ala/3");
        REQUIRE((notify.messages[1].flags && inotify_flags::close_nowrite));
        notify.notify_close(300);
        notify.notify_close(300);
        REQUIRE(notify.messages.size() == 2);
        notify.messages.clear();
    }
    SECTION("Other notifies tests")
    {
        notify.notify("/sys/ala/100", "/sys/ala/555", inotify_flags::move_src);
        notify.notify("/sys/ala/555", "/sys/ala/100", inotify_flags::move_dst);
        notify.notify("/sys/ala/999/989", inotify_flags::attrib);
        REQUIRE(notify.messages.size() == 3);
        REQUIRE(notify.messages[0].name == "/sys/ala/100");
        REQUIRE(notify.messages[0].name_prev == "/sys/ala/555");
        REQUIRE((notify.messages[0].flags && inotify_flags::move_src));
        REQUIRE(notify.messages[1].name == "/sys/ala/555");
        REQUIRE(notify.messages[1].name_prev == "/sys/ala/100");
        REQUIRE((notify.messages[1].flags && inotify_flags::move_dst));
    }
}

TEST_CASE("Filtering test")
{
    using namespace purefs::fs;
    SECTION("Different mask for services")
    {
        notifier_mock notify;
        auto svc1 = std::make_shared<sys::Service>();
        auto svc2 = std::make_shared<sys::Service>();
        notify.register_path(
            "/sys/path1", svc1, inotify_flags::open | inotify_flags::close_nowrite | inotify_flags::close_write);
        notify.register_path("/sys/path1/", svc2, inotify_flags::open | inotify_flags::close_write);
        notify.notify_open("/sys/path1/file.txt", 100, false);
        notify.notify_open("/sys/path1/file1.txt", 200, true);
        REQUIRE(notify.messages.size() == 4);
        notify.notify_close(100);
        REQUIRE(notify.messages.size() == 6);
        notify.notify_close(200);
        REQUIRE(notify.messages.size() == 7);
        // Checking values in the queue
        REQUIRE(notify.messages[0].name == "/sys/path1/file.txt");
        REQUIRE((notify.messages[0].flags && inotify_flags::open));
        REQUIRE(notify.xsvc[0] == svc1.get());
        REQUIRE(notify.messages[1].name == "/sys/path1/file.txt");
        REQUIRE((notify.messages[1].flags && inotify_flags::open));
        REQUIRE(notify.xsvc[1] == svc2.get());
        REQUIRE(notify.messages[2].name == "/sys/path1/file1.txt");
        REQUIRE((notify.messages[2].flags && inotify_flags::open));
        REQUIRE(notify.xsvc[2] == svc1.get());
        REQUIRE(notify.messages[3].name == "/sys/path1/file1.txt");
        REQUIRE((notify.messages[3].flags && inotify_flags::open));
        REQUIRE(notify.xsvc[3] == svc2.get());
        REQUIRE(notify.messages[4].name == "/sys/path1/file.txt");
        REQUIRE((notify.messages[4].flags && inotify_flags::close_write));
        REQUIRE(notify.xsvc[4] == svc1.get());
        REQUIRE(notify.messages[5].name == "/sys/path1/file.txt");
        REQUIRE((notify.messages[5].flags && inotify_flags::close_write));
        REQUIRE(notify.xsvc[5] == svc2.get());
        REQUIRE(notify.messages[6].name == "/sys/path1/file1.txt");
        REQUIRE((notify.messages[6].flags && inotify_flags::close_nowrite));
        REQUIRE(notify.xsvc[6] == svc1.get());
    }
    SECTION("Different paths for services")
    {
        notifier_mock notify;
        auto svc1 = std::make_shared<sys::Service>();
        auto svc2 = std::make_shared<sys::Service>();
        notify.register_path("/sys/path1", svc1, inotify_flags::attrib);
        notify.register_path("/sys/path2", svc2, inotify_flags::attrib);
        notify.notify("/sys/path1/file.txt", inotify_flags::attrib);
        REQUIRE(notify.messages.size() == 1);
        notify.notify("/sys/path1/file2.txt", inotify_flags::attrib);
        REQUIRE(notify.messages.size() == 2);
        notify.notify("/sys/path3", inotify_flags::attrib);
        REQUIRE(notify.messages.size() == 2);
        notify.notify("/sys/path2/", inotify_flags::attrib);
        REQUIRE(notify.messages.size() == 3);
        REQUIRE(notify.messages[0].name == "/sys/path1/file.txt");
        REQUIRE(notify.xsvc[0] == svc1.get());
        REQUIRE(notify.messages[1].name == "/sys/path1/file2.txt");
        REQUIRE(notify.xsvc[1] == svc1.get());
        REQUIRE(notify.messages[2].name == "/sys/path2");
        REQUIRE(notify.xsvc[2] == svc2.get());
    }
}

M test/pytest/service-desktop/test_messages.py => test/pytest/service-desktop/test_messages.py +11 -3
@@ 4,7 4,6 @@
import pytest
from harness.interface.defs import status


MESSAGES_PAGE_SIZE = 4




@@ 19,6 18,15 @@ def test_get_messages_without_pagination(harness):
    messages = ret["body"]["entries"]
    messages_count = len(messages)
    assert messages_count == limit
    message_types = [1, 2, 4, 8, 16, 18, 255]
    for message in messages:
        assert type(message["contactID"]) == int
        assert type(message["messageBody"]) == str
        assert type(message["messageID"]) == int
        assert type(message["messageType"]) == int
        assert type(message["createdAt"]) == int
        assert type(message["threadID"]) == int
        assert message["messageType"] in message_types


@pytest.mark.service_desktop_test


@@ 61,7 69,7 @@ def test_get_all_messages(harness):
            if "nextPage" in ret["body"]:

                offset = ret["body"]["nextPage"]["offset"]
                messages_left_count =  count - len(all_messages)
                messages_left_count = count - len(all_messages)

                body = {"category": "message", "limit": messages_left_count, "offset": offset}
                ret = harness.endpoint_request("messages", "get", body)


@@ 136,7 144,7 @@ def test_get_all_messages_by_thread_id(harness):
            if "nextPage" in ret["body"]:

                offset = ret["body"]["nextPage"]["offset"]
                messages_left_count =  messages_by_thread_id_count - len(all_messages)
                messages_left_count = messages_by_thread_id_count - len(all_messages)

                body = {"category": "message", "threadID": thread_id, "limit": messages_left_count, "offset": offset}
                ret = harness.endpoint_request("messages", "get", body)

M test/pytest/service-desktop/test_templates.py => test/pytest/service-desktop/test_templates.py +4 -0
@@ 83,6 83,10 @@ class TemplatesTester:
        assert ret["status"] == status["OK"]
        assert ret["body"]["totalCount"] == count
        assert len(ret["body"]["entries"]) == number_of_requested_templates
        for template in ret["body"]["entries"]:
            assert type(template["lastUsedAt"]) == int
            assert type(template["templateBody"]) == str
            assert type(template["templateID"]) == int

    def test_changing_template_body(self):
        test_passed = False

M test/pytest/service-desktop/test_threads.py => test/pytest/service-desktop/test_threads.py +13 -0
@@ 12,6 12,19 @@ def test_threads(harness):
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    # Check if all fields are present and have proper type
    message_types = [1, 2, 4, 8, 16, 18, 255]
    for thread in ret["body"]["entries"]:
        assert type(thread["contactID"]) == int
        assert type(thread["isUnread"]) == bool
        assert type(thread["lastUpdatedAt"]) == int
        assert type(thread["messageCount"]) == int
        assert type(thread["messageSnippet"]) == str
        assert type(thread["messageType"]) == int
        assert type(thread["numberID"]) == int
        assert type(thread["threadID"]) == int
        assert thread["messageType"] in message_types

    # getting a number of threads
    number_of_requested_threads = 3
    if ret["body"]["totalCount"] < number_of_requested_threads:

M third-party/CMakeLists.txt => third-party/CMakeLists.txt +6 -0
@@ 5,3 5,9 @@ add_subdirectory(microtar)
add_subdirectory(date)
add_subdirectory(pugixml)
add_subdirectory(sml)
add_subdirectory(taglib)
add_subdirectory(minimp3)
add_subdirectory(tinyexpr)
add_subdirectory(parallel-hashmap)
add_subdirectory(protobuf)


A third-party/minimp3/CMakeLists.txt => third-party/minimp3/CMakeLists.txt +7 -0
@@ 0,0 1,7 @@
add_library(minimp3 INTERFACE)
add_library(minimp3::minimp3 ALIAS minimp3)

target_include_directories(minimp3
    INTERFACE 
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/minimp3>
)

A third-party/minimp3/minimp3 => third-party/minimp3/minimp3 +1 -0
@@ 0,0 1,1 @@
Subproject commit 446d3a32d281eb1dcae7726ba0ff594695182908

A third-party/parallel-hashmap/CMakeLists.txt => third-party/parallel-hashmap/CMakeLists.txt +3 -0
@@ 0,0 1,3 @@
set(CMAKE_POLICY_DEFAULT_CMP0076 NEW)

add_subdirectory(src)

A third-party/parallel-hashmap/src => third-party/parallel-hashmap/src +1 -0
@@ 0,0 1,1 @@
Subproject commit 1f2671ff0a12710f19c92080693c857f918bda88

A third-party/protobuf/CMakeLists.txt => third-party/protobuf/CMakeLists.txt +3 -0
@@ 0,0 1,3 @@
add_subdirectory(src/cmake)
set_target_properties(libprotobuf PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1)
target_compile_options(libprotobuf-lite PRIVATE -w)

A third-party/protobuf/src => third-party/protobuf/src +1 -0
@@ 0,0 1,1 @@
Subproject commit 2555e35fc68441e76618621be0815fe343201fdf

A third-party/taglib => third-party/taglib +1 -0
@@ 0,0 1,1 @@
Subproject commit 88ec9107c210eb19aaef4e1153460a754578197b

A third-party/tinyexpr/CMakeLists.txt => third-party/tinyexpr/CMakeLists.txt +14 -0
@@ 0,0 1,14 @@

add_library(tinyexpr)
target_sources(tinyexpr
   PRIVATE
      ${CMAKE_CURRENT_SOURCE_DIR}/src/tinyexpr.c
      ${CMAKE_CURRENT_SOURCE_DIR}/src/tinyexpr.h
)

target_include_directories(tinyexpr
   PUBLIC
      $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
)
add_library(tinyexpr::tinyexpr ALIAS tinyexpr)


A third-party/tinyexpr/src => third-party/tinyexpr/src +1 -0
@@ 0,0 1,1 @@
Subproject commit 2ef22d228f8760561b8f485f0997ca07fafd0f12