~aleteoryx/muditaos

4651eb3cf98a27d50149f3e629a26776b1c7bebf — Lefucjusz 2 years ago 20e0437
[MOS-1033] Add 'beta' label to VoLTE switch for selected countries

Added mechanism enabling to individually show
or hide "beta" label on VoLTE switch for each
country.
31 files changed, 255 insertions(+), 125 deletions(-)

M image/system_a/data/lang/Deutsch.json
M image/system_a/data/lang/English.json
M image/system_a/data/lang/Espanol.json
M image/system_a/data/lang/Francais.json
M image/system_a/data/lang/Polski.json
M image/system_a/data/lang/Svenska.json
M module-apps/application-settings/ApplicationSettings.cpp
M module-apps/application-settings/include/application-settings/ApplicationSettings.hpp
M module-apps/application-settings/windows/display-keypad/DisplayAndKeypadWindow.cpp
M module-apps/application-settings/windows/network/NetworkWindow.cpp
M module-apps/application-settings/windows/network/NetworkWindow.hpp
M module-services/service-cellular/service-cellular/VolteState.hpp
M module-services/service-cellular/src/ServiceCellularPriv.cpp
M module-services/service-cellular/src/VolteHandler.hpp
M module-services/service-cellular/src/volte/ImsiParser.cpp
M module-services/service-cellular/src/volte/ImsiParser.hpp
M module-services/service-cellular/src/volte/ImsiParser_Austria.hpp
M module-services/service-cellular/src/volte/ImsiParser_Canada.hpp
M module-services/service-cellular/src/volte/ImsiParser_Denmark.hpp
M module-services/service-cellular/src/volte/ImsiParser_Germany.hpp
M module-services/service-cellular/src/volte/ImsiParser_GreatBritain.hpp
M module-services/service-cellular/src/volte/ImsiParser_Netherlands.hpp
M module-services/service-cellular/src/volte/ImsiParser_Poland.hpp
M module-services/service-cellular/src/volte/ImsiParser_UnitedStates.hpp
M module-services/service-cellular/src/volte/VolteAllowedList.cpp
M module-services/service-cellular/src/volte/VolteAllowedList.hpp
M module-services/service-cellular/src/volte/VolteAllowedListInterface.hpp
M module-services/service-cellular/src/volte/VolteCapabilityHandler.cpp
M module-services/service-cellular/src/volte/VolteCapabilityHandler.hpp
M module-services/service-cellular/tests/unittest_volteCapabilityHandler.cpp
M pure_changelog.md
M image/system_a/data/lang/Deutsch.json => image/system_a/data/lang/Deutsch.json +2 -1
@@ 442,7 442,7 @@
    "app_settings_date_and_time_time_zone": "Zeitzone",
    "app_settings_disp_key": "Display und Tastenfeld",
    "app_settings_display": "Display",
    "app_settings_display_dark_mode": "Dunkelmodus (Beta)",
    "app_settings_display_dark_mode": "Dunkelmodus",
    "app_settings_display_display_light": "Displaylicht",
    "app_settings_display_factory_reset_confirmation": "Dadurch wird dein Pure ausgeschaltet. Schalte es wieder ein, um mit dem Prozess fortzufahren.\nTelefon jetzt ausschalten?",
    "app_settings_display_font_size": "Schriftgr\u00f6\u00dfe",


@@ 562,6 562,7 @@
    "common_april": "April",
    "common_august": "August",
    "common_back": "ZUR\u00dcCK",
    "common_beta": "(beta)",
    "common_call": "ANRUFEN",
    "common_check": "AUSW\u00c4HLEN",
    "common_confirm": "BEST\u00c4TIGEN",

M image/system_a/data/lang/English.json => image/system_a/data/lang/English.json +2 -1
@@ 446,7 446,7 @@
    "app_settings_date_and_time_time_zone": "Time zone",
    "app_settings_disp_key": "Display and keypad",
    "app_settings_display": "Display",
    "app_settings_display_dark_mode": "Dark mode (Beta)",
    "app_settings_display_dark_mode": "Dark mode",
    "app_settings_display_display_light": "Display light",
    "app_settings_display_factory_reset_confirmation": "Your Pure will turn off. Turn it on manually to continue the process.\nTurn off the phone now?",
    "app_settings_display_font_size": "Font size",


@@ 564,6 564,7 @@
    "common_april": "April",
    "common_august": "August",
    "common_back": "BACK",
    "common_beta": "(beta)",
    "common_call": "CALL",
    "common_check": "CHECK",
    "common_confirm": "CONFIRM",

M image/system_a/data/lang/Espanol.json => image/system_a/data/lang/Espanol.json +2 -1
@@ 441,7 441,7 @@
    "app_settings_date_and_time_time_zone": "Zona horaria",
    "app_settings_disp_key": "Pantalla y teclado num\u00e9rico",
    "app_settings_display": "Pantalla",
    "app_settings_display_dark_mode": "Modo oscuro (Beta)",
    "app_settings_display_dark_mode": "Modo oscuro",
    "app_settings_display_display_light": "Luz de pantalla",
    "app_settings_display_factory_reset_confirmation": "Tu Pure se va a apagar. Enci\u00e9ndelo manualmente para continuar.\n\u00bfQuieres apagar el tel\u00e9fono ahora?",
    "app_settings_display_font_size": "Tama\u00f1o de la fuente",


@@ 562,6 562,7 @@
    "common_april": "Abril",
    "common_august": "Agosto",
    "common_back": "ATR\u00c1S",
    "common_beta": "(beta)",
    "common_call": "LLAMAR",
    "common_check": "MARCAR",
    "common_confirm": "CONFIRMAR",

M image/system_a/data/lang/Francais.json => image/system_a/data/lang/Francais.json +2 -1
@@ 408,7 408,7 @@
    "app_settings_date_and_time_time_zone": "Fuseau horaire",
    "app_settings_disp_key": "Affichage et clavier",
    "app_settings_display": "Affichage",
    "app_settings_display_dark_mode": "Mode sombre (Beta)",
    "app_settings_display_dark_mode": "Mode sombre",
    "app_settings_display_display_light": "Lumi\u00e8re d'affichage",
    "app_settings_display_factory_reset_confirmation": "Votre Pure va s'\u00e9teindre. Allumez-la manuellement pour continuer.\n\u00c9teindre le t\u00e9l\u00e9phone?",
    "app_settings_display_font_size": "Taille de police",


@@ 529,6 529,7 @@
    "common_april": "Avril",
    "common_august": "Ao\u00fbt",
    "common_back": "RETOUR",
    "common_beta": "(beta)",
    "common_call": "APPELER",
    "common_check": "COCHER",
    "common_confirm": "CONFIRMER",

M image/system_a/data/lang/Polski.json => image/system_a/data/lang/Polski.json +2 -1
@@ 435,7 435,7 @@
    "app_settings_date_and_time_time_zone": "Strefa czasowa",
    "app_settings_disp_key": "Ekran i klawiatura",
    "app_settings_display": "Wy\u015bwietlacz",
    "app_settings_display_dark_mode": "Tryb nocny (Beta)",
    "app_settings_display_dark_mode": "Tryb nocny",
    "app_settings_display_display_light": "Pod\u015bwietlenie ekranu",
    "app_settings_display_factory_reset_confirmation": "Pure zostanie teraz wy\u0142\u0105czony. Aby rozpocz\u0105\u0107 proces, w\u0142\u0105cz go ponownie.\nWy\u0142\u0105czy\u0107 telefon?",
    "app_settings_display_font_size": "Rozmiar fontu",


@@ 556,6 556,7 @@
    "common_april": "Kwiecie\u0144",
    "common_august": "Sierpie\u0144",
    "common_back": "WR\u00d3\u0106",
    "common_beta": "(beta)",
    "common_call": "ZADZWO\u0143",
    "common_check": "ZAZNACZ",
    "common_confirm": "POTWIERD\u0179",

M image/system_a/data/lang/Svenska.json => image/system_a/data/lang/Svenska.json +2 -1
@@ 287,7 287,7 @@
    "app_settings_date_and_time_time_zone": "Tidszon",
    "app_settings_disp_key": "Sk\u00e4rm och knappar",
    "app_settings_display": "Sk\u00e4rm",
    "app_settings_display_dark_mode": "M\u00f6rkt l\u00e4ge (Beta)",
    "app_settings_display_dark_mode": "M\u00f6rkt l\u00e4ge",
    "app_settings_display_display_light": "Sk\u00e4rmbelysning",
    "app_settings_display_factory_reset_confirmation": "Din Pure st\u00e4ngs av. S\u00e4tt p\u00e5 den manuellt f\u00f6r att forts\u00e4tta.\nSt\u00e4ng av telefonen nu?",
    "app_settings_display_font_size": "Teckenstorlek",


@@ 390,6 390,7 @@
    "common_april": "April",
    "common_august": "Augusti",
    "common_back": "TILLBAKA",
    "common_beta": "(beta)",
    "common_call": "RING",
    "common_check": "MARKERA",
    "common_confirm": "BEKR\u00c4FTA",

M module-apps/application-settings/ApplicationSettings.cpp => module-apps/application-settings/ApplicationSettings.cpp +7 -3
@@ 93,6 93,11 @@
#include <i18n/i18n.hpp>
#include <service-bluetooth/messages/SyncDevices.hpp>

namespace
{
    constexpr auto applicationSettingsStackSize = 1024 * 6;
}

namespace app
{
    namespace settings


@@ 100,13 105,12 @@ namespace app
        constexpr inline auto operators_on = "operators_on";
    } // namespace settings

    static constexpr auto settingStackDepth = 1024 * 6; // 6Kb stack size

    ApplicationSettings::ApplicationSettings(std::string name,
                                             std::string parent,
                                             StatusIndicators statusIndicators,
                                             StartInBackground startInBackground)
        : Application(std::move(name), std::move(parent), statusIndicators, startInBackground, settingStackDepth),
        : Application(
              std::move(name), std::move(parent), statusIndicators, startInBackground, applicationSettingsStackSize),
          AsyncCallbackReceiver{this}, soundsPlayer{std::make_shared<SoundsPlayer>(this)}
    {
        bus.channels.push_back(sys::BusChannel::ServiceAudioNotifications);

M module-apps/application-settings/include/application-settings/ApplicationSettings.hpp => module-apps/application-settings/include/application-settings/ApplicationSettings.hpp +1 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 188,7 188,6 @@ namespace app
        void sendVolteChangeRequest(bool enable);

      private:
        void attachQuotesWindows();
        void switchToAllDevicesViaBtErrorPrompt(std::shared_ptr<sys::DataMessage> msg, const std::string &errorMsg);

        auto handleSimNotification() -> sys::MessagePointer;

M module-apps/application-settings/windows/display-keypad/DisplayAndKeypadWindow.cpp => module-apps/application-settings/windows/display-keypad/DisplayAndKeypadWindow.cpp +9 -6
@@ 1,10 1,9 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "DisplayAndKeypadWindow.hpp"

#include <application-settings/windows/WindowNames.hpp>

#include <OptionSetting.hpp>

namespace gui


@@ 21,10 20,10 @@ namespace gui
    {
        std::list<gui::Option> optionList;

        auto addMenu = [&](UTF8 name, std::string window) {
        auto addMenu = [&](const UTF8 &name, const std::string &window) {
            optionList.emplace_back(std::make_unique<gui::option::OptionSettings>(
                name,
                [=](gui::Item &item) {
                [=]([[maybe_unused]] gui::Item &item) {
                    if (window.empty()) {
                        return false;
                    }


@@ 42,7 41,7 @@ namespace gui
                gui::option::SettingRightItem::ArrowWhite));
        };

        auto addOnOffOption = [&](const UTF8 &text, std::function<bool(gui::Item &)> onActivated) {
        auto addOnOffOption = [&](const UTF8 &text, const std::function<bool(gui::Item &)> &onActivated) {
            optionList.emplace_back(std::make_unique<gui::option::OptionSettings>(
                text,
                [=](gui::Item &item) mutable { return onActivated(item); },


@@ 63,10 62,14 @@ namespace gui
        addMenu(utils::translate("app_settings_display_wallpaper"), gui::window::name::wallpaper);
        addMenu(utils::translate("app_settings_display_keypad_light"), gui::window::name::keypad_light);
        addMenu(utils::translate("app_settings_display_input_language"), gui::window::name::input_language);
        addOnOffOption(utils::translate("app_settings_display_dark_mode"), [this](gui::Item & /*item*/) {

        const auto &darkModeLabelText = "<text>" + utils::translate("app_settings_display_dark_mode") + " <b>" +
                                        utils::translate("common_beta") + "</b></text>";
        addOnOffOption(darkModeLabelText, [this](gui::Item & /*item*/) {
            switchDisplayMode();
            return true;
        });

        return optionList;
    }


M module-apps/application-settings/windows/network/NetworkWindow.cpp => module-apps/application-settings/windows/network/NetworkWindow.cpp +67 -52
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NetworkWindow.hpp"


@@ 12,8 12,7 @@ namespace
{
    cellular::VolteState getVolteStateFromSettingsApp(app::ApplicationCommon *application)
    {
        auto const *settingsApp = static_cast<app::ApplicationSettings *>(application);
        return settingsApp->getVolteState();
        return static_cast<app::ApplicationSettings *>(application)->getVolteState();
    }
} // namespace



@@ 33,14 32,12 @@ namespace gui

    auto NetworkWindow::buildOptionsList() -> std::list<gui::Option>
    {
        using namespace cellular;
        std::list<gui::Option> optionsList;

        std::list<gui::Option> optList;

        optList.emplace_back(std::make_unique<gui::option::OptionSettings>(
        optionsList.emplace_back(std::make_unique<gui::option::OptionSettings>(
            utils::translate("app_settings_network_sim_cards"),
            [=](gui::Item &item) {
                this->application->switchWindow(gui::window::name::sim_cards, nullptr);
            [=]([[maybe_unused]] gui::Item &item) {
                application->switchWindow(gui::window::name::sim_cards, nullptr);
                return true;
            },
            nullptr,


@@ 48,66 45,46 @@ namespace gui
            gui::option::SettingRightItem::ArrowWhite,
            false));

        optList.emplace_back(std::make_unique<gui::option::OptionSettings>(
            [this]() {
                auto ret              = utils::translate("app_settings_network_voice_over_lte");
                const auto volteState = getVolteStateFromSettingsApp(application);
                if (!volteState.permitted) {
                    if (volteState.enablement == VolteState::Enablement::On) {
                        LOG_ERROR("[VoLTE] still enabled in modem despite not permitted by operator");
                    }
                    ret += ": ";
                    ret += utils::translate("app_settings_network_volte_not_available");
                }
                return ret;
            }(),
            [this](gui::Item &item) {
        optionsList.emplace_back(std::make_unique<gui::option::OptionSettings>(
            getVolteLabel(),
            [this]([[maybe_unused]] gui::Item &item) {
                const auto volteState = getVolteStateFromSettingsApp(application);
                if (!volteState.permitted) {
                    return true;
                }
                auto *settingsApp = static_cast<app::ApplicationSettings *>(application);

                const auto settingsApp = static_cast<app::ApplicationSettings *>(application);
                switch (volteState.enablement) {
                case VolteState::Enablement::Off:
                case cellular::VolteState::Enablement::Off:
                    settingsApp->sendVolteChangeRequest(true);
                    break;
                case VolteState::Enablement::On:
                case cellular::VolteState::Enablement::On:
                    settingsApp->sendVolteChangeRequest(false);
                    break;
                default:
                    LOG_INFO("[VoLTE] skip request due to unsettled VoLTE state");
                    break;
                }

                return true;
            },
            [&](Item &item) {
                navBar->setText(nav_bar::Side::Center, [&]() {
                    if (!item.focus) {
                        return utils::translate("common_select");
                    }
                    return getVolteStateFromSettingsApp(application).permitted ? utils::translate("common_switch") : "";
                }());
                std::string labelText{};
                if (!item.focus) {
                    labelText = utils::translate("common_select");
                }
                else if (getVolteStateFromSettingsApp(application).permitted) {
                    labelText = utils::translate("common_switch");
                }
                navBar->setText(nav_bar::Side::Center, labelText);
                return true;
            },
            nullptr,
            [&]() {
                const auto volteState = getVolteStateFromSettingsApp(application);
                if (!volteState.permitted) {
                    return option::SettingRightItem::Disabled;
                }
                switch (volteState.enablement) {
                case VolteState::Enablement::Off:
                    return option::SettingRightItem::Off;
                case VolteState::Enablement::On:
                    return option::SettingRightItem::On;
                default:
                    return option::SettingRightItem::Transiting;
                }
            }()));
            getRightItemSetting()));

#if DISABLED_SETTINGS_OPTIONS == 1
        auto operatorsOn = operatorsSettings->getOperatorsOn();
        optList.emplace_back(std::make_unique<gui::option::OptionSettings>(
        optionsList.emplace_back(std::make_unique<gui::option::OptionSettings>(
            utils::translate("app_settings_network_operator_auto_select"),
            [=](gui::Item &item) {
                operatorsSettings->setOperatorsOn(!operatorsOn);


@@ 118,7 95,7 @@ namespace gui
            nullptr,
            operatorsOn ? gui::option::SettingRightItem::On : gui::option::SettingRightItem::Off));
        if (!operatorsOn) {
            optList.emplace_back(std::make_unique<gui::option::OptionSettings>(
            optionsList.emplace_back(std::make_unique<gui::option::OptionSettings>(
                utils::translate("app_settings_network_all_operators"),
                [=](gui::Item &item) {
                    this->application->switchWindow(gui::window::name::all_operators, nullptr);


@@ 131,10 108,10 @@ namespace gui
        }
#endif // DISABLED_SETTINGS_OPTIONS

        optList.emplace_back(std::make_unique<gui::option::OptionSettings>(
        optionsList.emplace_back(std::make_unique<gui::option::OptionSettings>(
            utils::translate("app_settings_network_apn_settings"),
            [=](gui::Item &item) {
                this->application->switchWindow(gui::window::name::apn_settings, nullptr);
            [=]([[maybe_unused]] gui::Item &item) {
                application->switchWindow(gui::window::name::apn_settings, nullptr);
                return true;
            },
            nullptr,


@@ 144,6 121,44 @@ namespace gui

        navBar->setText(nav_bar::Side::Center, utils::translate(style::strings::common::select));

        return optList;
        return optionsList;
    }

    auto NetworkWindow::getVolteLabel() -> std::string
    {
        const auto &volteState = getVolteStateFromSettingsApp(application);

        auto labelText = "<text>" + utils::translate("app_settings_network_voice_over_lte");
        if (!volteState.permitted) {
            if (volteState.enablement == cellular::VolteState::Enablement::On) {
                LOG_ERROR("[VoLTE] still enabled in modem despite not permitted by operator");
            }
            labelText += ": ";
            labelText += utils::translate("app_settings_network_volte_not_available");
        }
        else if (volteState.beta) {
            labelText += " <b>" + utils::translate("common_beta") + "</b>";
        }
        labelText += "</text>";

        return labelText;
    }

    auto NetworkWindow::getRightItemSetting() -> option::SettingRightItem
    {
        const auto &volteState = getVolteStateFromSettingsApp(application);

        if (!volteState.permitted) {
            return option::SettingRightItem::Disabled;
        }

        switch (volteState.enablement) {
        case cellular::VolteState::Enablement::Off:
            return option::SettingRightItem::Off;
        case cellular::VolteState::Enablement::On:
            return option::SettingRightItem::On;
        default:
            return option::SettingRightItem::Transiting;
        }
    }
} // namespace gui

M module-apps/application-settings/windows/network/NetworkWindow.hpp => module-apps/application-settings/windows/network/NetworkWindow.hpp +9 -8
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 14,15 14,16 @@ namespace gui
{
    class NetworkWindow : public BaseSettingsWindow
    {
      private:
        auto buildOptionsList() -> std::list<Option> override;
        app::settingsInterface::OperatorsSettings *operatorsSettings;

        OptionWindowDestroyer rai_destroyer = OptionWindowDestroyer(*this);

      public:
        NetworkWindow(app::ApplicationCommon *app, app::settingsInterface::OperatorsSettings *operatorsSettings);

        void onBeforeShow(ShowMode mode, SwitchData *data) override;

      private:
        auto buildOptionsList() -> std::list<Option> override;
        auto getVolteLabel() -> std::string;
        auto getRightItemSetting() -> option::SettingRightItem;

        app::settingsInterface::OperatorsSettings *operatorsSettings = nullptr;
        OptionWindowDestroyer rai_destroyer                          = OptionWindowDestroyer(*this);
    };
} // namespace gui

M module-services/service-cellular/service-cellular/VolteState.hpp => module-services/service-cellular/service-cellular/VolteState.hpp +2 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 16,5 16,6 @@ namespace cellular
            Undefined
        } enablement   = Enablement::Undefined;
        bool permitted = false;
        bool beta      = true;
    };
}

M module-services/service-cellular/src/ServiceCellularPriv.cpp => module-services/service-cellular/src/ServiceCellularPriv.cpp +11 -9
@@ 63,13 63,17 @@ namespace cellular::internal
            state->set(State::ST::Ready);

            auto channel     = owner->cmux->get(CellularMux::Channel::Commands);
            auto permitVolte = volteCapability->isVolteAllowed(*channel);
            auto enableVolte =
                owner->settings->getValue(settings::Cellular::volteEnabled, settings::SettingsScope::Global) == "1";

            const auto permitVolte = volteCapability->isVolteAllowed(*channel);
            const auto enableVolte =
                (owner->settings->getValue(settings::Cellular::volteEnabled, settings::SettingsScope::Global) == "1");
            const auto isVolteBeta =
                (volteCapability->getSupportStatus(*channel) != service::ImsiParser::SupportStatus::Normal);

            auto volteNeedReset = false;

            try {
                volteNeedReset    = !volteHandler->switchVolte(*channel, permitVolte, enableVolte);
                volteNeedReset    = !volteHandler->switchVolte(*channel, permitVolte, enableVolte, isVolteBeta);
                auto notification = std::make_shared<cellular::VolteStateNotification>(volteHandler->getVolteState());
                owner->bus.sendMulticast(std::move(notification), sys::BusChannel::ServiceCellularNotifications);
            }


@@ 83,17 87,15 @@ namespace cellular::internal
            }
            owner->bus.sendMulticast<notification::SimReady>();
        };
        simCard->onNeedPin = [this](unsigned int attempts) {
        simCard->onNeedPin = [this](unsigned attempts) {
            owner->bus.sendMulticast<notification::SimNeedPin>(attempts);
        };
        simCard->onNeedPuk = [this](unsigned int attempts) {
        simCard->onNeedPuk = [this](unsigned attempts) {
            owner->bus.sendMulticast<notification::SimNeedPuk>(attempts);
        };
        simCard->onSimBlocked   = [this]() { owner->bus.sendMulticast<notification::SimBlocked>(); };
        simCard->onSimEvent     = [this]() { owner->bus.sendMulticast<notification::SimStateUpdate>(); };
        simCard->onUnhandledCME = [this](unsigned int code) {
            owner->bus.sendMulticast<notification::UnhandledCME>(code);
        };
        simCard->onUnhandledCME = [this](unsigned code) { owner->bus.sendMulticast<notification::UnhandledCME>(code); };
        simCard->onSimNotPresent = [this]() { owner->bus.sendMulticast<notification::SimNotInserted>(); };
        simCard->onSimSelected   = [this]() {
            owner->connectionManager->onPhoneModeChange(owner->phoneModeObserver->getCurrentPhoneMode());

M module-services/service-cellular/src/VolteHandler.hpp => module-services/service-cellular/src/VolteHandler.hpp +9 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 29,14 29,14 @@ namespace cellular::service

    struct QcfgImsResult : Result
    {
        QcfgImsResult(Result &rhs) : Result{std::move(rhs)}
        explicit QcfgImsResult(Result &rhs) : Result{std::move(rhs)}
        {}
    };

    template <typename CmuxChannel, typename ModemResponseParser>
    struct VolteHandler : private NonCopyable
    {
        bool switchVolte(CmuxChannel &channel, bool permit, bool enable)
        auto switchVolte(CmuxChannel &channel, bool permit, bool enable) -> bool
        {
            if (!permit) {
                if (enable) {


@@ 113,6 113,12 @@ namespace cellular::service
            return alreadyConfigured;
        }

        auto switchVolte(CmuxChannel &channel, bool permit, bool enable, bool beta) -> bool
        {
            volteState.beta = beta;
            return switchVolte(channel, permit, enable);
        }

        auto getVolteState() -> cellular::VolteState
        {
            return volteState;

M module-services/service-cellular/src/volte/ImsiParser.cpp => module-services/service-cellular/src/volte/ImsiParser.cpp +8 -3
@@ 3,9 3,9 @@

#include "ImsiParser.hpp"

namespace cellular
namespace cellular::service
{
    auto service::ImsiParser::isAllowed(const std::string &imsi) const -> bool
    auto ImsiParser::isAllowed(const std::string &imsi) const -> bool
    {
        if (operatorCodes.empty()) {
            return false;


@@ 20,7 20,12 @@ namespace cellular
        return false;
    }

    auto service::ImsiParser::textStartsWith(std::string_view text, std::string_view prefix) const -> bool
    auto ImsiParser::getSupportStatus() const -> SupportStatus
    {
        return supportStatus;
    }

    auto ImsiParser::textStartsWith(std::string_view text, std::string_view prefix) const -> bool
    {
        return !text.empty() && !prefix.empty() && std::equal(prefix.begin(), prefix.end(), text.begin());
    }

M module-services/service-cellular/src/volte/ImsiParser.hpp => module-services/service-cellular/src/volte/ImsiParser.hpp +12 -1
@@ 12,12 12,23 @@ namespace cellular::service
    class ImsiParser
    {
      public:
        explicit ImsiParser(std::vector<std::string> &&operatorList) : operatorCodes{operatorList}
        enum class SupportStatus
        {
            Normal,
            Beta,
            Unsupported
        };

        explicit ImsiParser(std::vector<std::string> &&operatorList, SupportStatus supportStatus)
            : operatorCodes{operatorList}, supportStatus{supportStatus}
        {}
        auto isAllowed(const std::string &imsi) const -> bool;
        auto getSupportStatus() const -> SupportStatus;

      private:
        std::vector<std::string> operatorCodes;
        SupportStatus supportStatus;

        inline auto textStartsWith(std::string_view text, std::string_view prefix) const -> bool;
    };
} // namespace cellular::service

M module-services/service-cellular/src/volte/ImsiParser_Austria.hpp => module-services/service-cellular/src/volte/ImsiParser_Austria.hpp +3 -4
@@ 7,12 7,11 @@

namespace cellular::service
{
    ;

    struct ImsiParserAT : ImsiParser
    {
        explicit ImsiParserAT()
            : ImsiParser(std::vector<std::string>{"23201", "23203", "23213", "23205", "23210", "23207", "23208"})
        ImsiParserAT()
            : ImsiParser(std::vector<std::string>{"23201", "23203", "23213", "23205", "23210", "23207", "23208"},
                         SupportStatus::Beta)
        {}
    };
} // namespace cellular::service

M module-services/service-cellular/src/volte/ImsiParser_Canada.hpp => module-services/service-cellular/src/volte/ImsiParser_Canada.hpp +12 -3
@@ 9,9 9,18 @@ namespace cellular::service
{
    struct ImsiParserCA : ImsiParser
    {
        explicit ImsiParserCA()
            : ImsiParser(std::vector<std::string>{
                  "302220", "302270", "302300", "302310", "302490", "302510", "302500", "302610", "302720", "302780"})
        ImsiParserCA()
            : ImsiParser(std::vector<std::string>{"302220",
                                                  "302270",
                                                  "302300",
                                                  "302310",
                                                  "302490",
                                                  "302510",
                                                  "302500",
                                                  "302610",
                                                  "302720",
                                                  "302780"},
                         SupportStatus::Beta)
        {}
    };
} // namespace cellular::service

M module-services/service-cellular/src/volte/ImsiParser_Denmark.hpp => module-services/service-cellular/src/volte/ImsiParser_Denmark.hpp +3 -2
@@ 9,8 9,9 @@ namespace cellular::service
{
    struct ImsiParserDK : ImsiParser
    {
        explicit ImsiParserDK()
            : ImsiParser(std::vector<std::string>{"23801", "23802", "23806", "23820", "23866", "28801", "28802"})
        ImsiParserDK()
            : ImsiParser(std::vector<std::string>{"23801", "23802", "23806", "23820", "23866", "28801", "28802"},
                         SupportStatus::Beta)
        {}
    };
} // namespace cellular::service

M module-services/service-cellular/src/volte/ImsiParser_Germany.hpp => module-services/service-cellular/src/volte/ImsiParser_Germany.hpp +5 -3
@@ 9,9 9,11 @@ namespace cellular::service
{
    struct ImsiParserDE : ImsiParser
    {
        explicit ImsiParserDE()
            : ImsiParser(std::vector<std::string>{
                  "26201", "26206", "26202", "26204", "26209", "26203", "26205", "26208", "26223"})
        ImsiParserDE()
            : ImsiParser(
                  std::vector<std::string>{
                      "26201", "26206", "26202", "26204", "26209", "26203", "26205", "26208", "26223"},
                  SupportStatus::Beta)
        {}
    };
} // namespace cellular::service

M module-services/service-cellular/src/volte/ImsiParser_GreatBritain.hpp => module-services/service-cellular/src/volte/ImsiParser_GreatBritain.hpp +3 -2
@@ 9,7 9,7 @@ namespace cellular::service
{
    struct ImsiParserGB : ImsiParser
    {
        explicit ImsiParserGB()
        ImsiParserGB()
            : ImsiParser(std::vector<std::string>{"23401",
                                                  "23410",
                                                  "23411",


@@ 28,7 28,8 @@ namespace cellular::service
                                                  "346001",
                                                  "346140",
                                                  "750001",
                                                  "26601"})
                                                  "26601"},
                         SupportStatus::Beta)
        {}
    };
} // namespace cellular::service

M module-services/service-cellular/src/volte/ImsiParser_Netherlands.hpp => module-services/service-cellular/src/volte/ImsiParser_Netherlands.hpp +5 -3
@@ 9,9 9,11 @@ namespace cellular::service
{
    struct ImsiParserNL : ImsiParser
    {
        explicit ImsiParserNL()
            : ImsiParser(std::vector<std::string>{
                  "20402", "20404", "20406", "20407", "20408", "20410", "20416", "20420", "20433", "20418"})
        ImsiParserNL()
            : ImsiParser(
                  std::vector<std::string>{
                      "20402", "20404", "20406", "20407", "20408", "20410", "20416", "20420", "20433", "20418"},
                  SupportStatus::Beta)
        {}
    };
} // namespace cellular::service

M module-services/service-cellular/src/volte/ImsiParser_Poland.hpp => module-services/service-cellular/src/volte/ImsiParser_Poland.hpp +3 -3
@@ 4,13 4,12 @@
#pragma once

#include "ImsiParser.hpp"
#include <vector>

namespace cellular::service
{
    struct ImsiParserPL : ImsiParser
    {
        explicit ImsiParserPL()
        ImsiParserPL()
            : ImsiParser(std::vector<std::string>{"26001",
                                                  "26011",
                                                  "26002",


@@ 26,7 25,8 @@ namespace cellular::service
                                                  "26008",
                                                  "26009",
                                                  "26012",
                                                  "26013"})
                                                  "26013"},
                         SupportStatus::Beta)
        {}
    };
} // namespace cellular::service

M module-services/service-cellular/src/volte/ImsiParser_UnitedStates.hpp => module-services/service-cellular/src/volte/ImsiParser_UnitedStates.hpp +3 -3
@@ 4,13 4,12 @@
#pragma once

#include "ImsiParser.hpp"
#include <vector>

namespace cellular::service
{
    struct ImsiParserUS : ImsiParser
    {
        explicit ImsiParserUS()
        ImsiParserUS()
            : ImsiParser(std::vector<std::string>{"310053",
                                                  "310120",
                                                  "310260",


@@ 22,7 21,8 @@ namespace cellular::service
                                                  "311882",
                                                  "312190",
                                                  "312250",
                                                  "312530"})
                                                  "312530"},
                         SupportStatus::Normal)
        {}
    };
} // namespace cellular::service

M module-services/service-cellular/src/volte/VolteAllowedList.cpp => module-services/service-cellular/src/volte/VolteAllowedList.cpp +16 -1
@@ 11,7 11,8 @@
#include "ImsiParser_Canada.hpp"
#include "ImsiParser_Austria.hpp"

#include "module-utils/log/Logger.hpp"
#include <log/log.hpp>
#include <magic_enum.hpp>

namespace
{


@@ 37,6 38,20 @@ namespace cellular::service
        return false;
    }

    auto VolteAllowedList::getSupportStatus(const std::string &imsi) const -> ImsiParser::SupportStatus
    {
        for (const auto &country : allowedList) {
            if (country.isAllowed(imsi)) {
                const auto supportStatus = country.getSupportStatus();
                LOG_INFO("MCC supported with status %s.", magic_enum::enum_name(supportStatus).data());
                return supportStatus;
            }
        }

        LOG_ERROR("MCC not supported.");
        return ImsiParser::SupportStatus::Unsupported;
    }

    void VolteAllowedList::buildList()
    {
        pushBack(allowedList,

M module-services/service-cellular/src/volte/VolteAllowedList.hpp => module-services/service-cellular/src/volte/VolteAllowedList.hpp +1 -0
@@ 16,6 16,7 @@ namespace cellular::service
        }

        auto isVolteAllowed(const std::string &imsi) const -> bool final;
        auto getSupportStatus(const std::string &imsi) const -> ImsiParser::SupportStatus final;

      private:
        void buildList();

M module-services/service-cellular/src/volte/VolteAllowedListInterface.hpp => module-services/service-cellular/src/volte/VolteAllowedListInterface.hpp +2 -0
@@ 4,6 4,7 @@
#pragma once

#include <string>
#include "ImsiParser.hpp"

namespace cellular::service
{


@@ 13,5 14,6 @@ namespace cellular::service
        virtual ~VolteAllowedListInterface() = default;

        virtual auto isVolteAllowed(const std::string &imsi) const -> bool = 0;
        virtual auto getSupportStatus(const std::string &imsi) const -> ImsiParser::SupportStatus = 0;
    };
} // namespace cellular::service

M module-services/service-cellular/src/volte/VolteCapabilityHandler.cpp => module-services/service-cellular/src/volte/VolteCapabilityHandler.cpp +14 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "VolteCapabilityHandler.hpp"


@@ 14,12 14,23 @@ namespace cellular::service

    auto VolteCapabilityHandler::isVolteAllowed(at::BaseChannel &channel) -> bool
    {
        const auto imsi = cellularInterface->getImsi(channel);
        if (not imsi.has_value()) {
        const auto &imsi = cellularInterface->getImsi(channel);
        if (!imsi.has_value()) {
            LOG_ERROR("[VoLTE] failed to read IMSI - VoLTE not permitted");
            return false;
        }

        return allowedList->isVolteAllowed(imsi.value());
    }

    auto VolteCapabilityHandler::getSupportStatus(at::BaseChannel &channel) -> ImsiParser::SupportStatus
    {
        const auto &imsi = cellularInterface->getImsi(channel);
        if (!imsi.has_value()) {
            LOG_ERROR("[VoLTE] failed to read IMSI - VoLTE not permitted");
            return ImsiParser::SupportStatus::Unsupported;
        }

        return allowedList->getSupportStatus(imsi.value());
    }
} // namespace cellular::service

M module-services/service-cellular/src/volte/VolteCapabilityHandler.hpp => module-services/service-cellular/src/volte/VolteCapabilityHandler.hpp +2 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 19,6 19,7 @@ namespace cellular::service
         * @return true when VoLTE is allowed, false when not
         */
        auto isVolteAllowed(at::BaseChannel &channel) -> bool;
        auto getSupportStatus(at::BaseChannel &channel) -> ImsiParser::SupportStatus;

      private:
        std::unique_ptr<VolteAllowedListInterface> allowedList;

M module-services/service-cellular/tests/unittest_volteCapabilityHandler.cpp => module-services/service-cellular/tests/unittest_volteCapabilityHandler.cpp +35 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <catch2/catch.hpp>


@@ 50,7 50,7 @@ TEST_CASE("VoLTE Capability handler")
        }
    };

    SECTION("VolteCapabilityHandler succes = TMobileUS")
    SECTION("VolteCapabilityHandler success = TMobileUS")
    {
        using namespace cellular::service;
        VolteCapabilityHandler handler{std::make_unique<VolteAllowedList>(), std::make_unique<MockTMobileUS>()};


@@ 58,6 58,14 @@ TEST_CASE("VoLTE Capability handler")
        REQUIRE(result == true);
    }

    SECTION("VolteCapabilityHandler support normal = TMobileUS")
    {
        using namespace cellular::service;
        VolteCapabilityHandler handler{std::make_unique<VolteAllowedList>(), std::make_unique<MockTMobileUS>()};
        auto result = handler.getSupportStatus(baseChannelStub);
        REQUIRE(result == ImsiParser::SupportStatus::Normal);
    }

    class MockNotAllowedIMSI : public cellular::service::VolteCapabilityCellularInterface
    {
        auto getImsi(at::BaseChannel &) -> std::optional<std::string>


@@ 74,6 82,22 @@ TEST_CASE("VoLTE Capability handler")
        REQUIRE(result == false);
    }

    class MockAllowedBetaIMSI : public cellular::service::VolteCapabilityCellularInterface
    {
        auto getImsi(at::BaseChannel &) -> std::optional<std::string>
        {
            return "260032137213769"; // Orange PL HNI
        }
    };

    SECTION("VolteCapabilityHandler support beta = Orange PL")
    {
        using namespace cellular::service;
        VolteCapabilityHandler handler{std::make_unique<VolteAllowedList>(), std::make_unique<MockAllowedBetaIMSI>()};
        auto result = handler.getSupportStatus(baseChannelStub);
        REQUIRE(result == ImsiParser::SupportStatus::Beta);
    }

    class MockFailedToGetImsi : public cellular::service::VolteCapabilityCellularInterface
    {
        auto getImsi(at::BaseChannel &) -> std::optional<std::string>


@@ 82,11 106,19 @@ TEST_CASE("VoLTE Capability handler")
        }
    };

    SECTION("VolteCapabilityHandler failure = failed to get imsi")
    SECTION("VolteCapabilityHandler failure = failed to get IMSI")
    {
        using namespace cellular::service;
        VolteCapabilityHandler handler{std::make_unique<VolteAllowedList>(), std::make_unique<MockFailedToGetImsi>()};
        auto result = handler.isVolteAllowed(baseChannelStub);
        REQUIRE(result == false);
    }

    SECTION("VolteCapabilityHandler support unknown = failed to get IMSI")
    {
        using namespace cellular::service;
        VolteCapabilityHandler handler{std::make_unique<VolteAllowedList>(), std::make_unique<MockFailedToGetImsi>()};
        auto result = handler.getSupportStatus(baseChannelStub);
        REQUIRE(result == ImsiParser::SupportStatus::Unsupported);
    }
}

M pure_changelog.md => pure_changelog.md +1 -0
@@ 5,6 5,7 @@
### Added

* Added VoLTE support in Poland, Germany, Denmark, United Kingdom, Netherlands, Canada and Austria
* Added "beta" label to VoLTE switch for selected countries

### Changed / Improved