Документация
ОС Аврора 5.2.1

Использование D-Bus интерфейса для взаимодействия с Market API

Ниже приведены фрагменты кода или же цельные файлы, которые реализуют клиент для работы с D-Bus API пакетного менеджера Market. Данный менеджер предоставляет функционал для получения свойств системы и установленных пакетов, управления операциями (установка, удаление) и обработки сигналов об изменении статусов операций через пользовательские D-Bus типы данных.

  1. .spec-файл:

    ...
    # Указываем зависимость для сборки: требуется модуль Qt5DBus для работы с D-Bus
    BuildRequires:  pkgconfig(Qt5DBus)
    ...
    
  2. .pro-файл:

    ...
    # Подключаем модуль Qt D-Bus к проекту
    QT += dbus \
    ...
    # Добавляем исходный файл реализации интерфейса в сборку
    SOURCES += \
        src/{путь_к_файлу}/dbusinterface.cpp \
    ...
    # Добавляем заголовочные файлы: объявление пользовательского типа и самого интерфейса
    HEADERS += \
        src/{путь_к_файлу}/customdbastype.h \
        src/{путь_к_файлу}/dbusinterface.h \
    ...
    
  3. customdbastype.h:

    // SPDX-FileCopyrightText: 2025 Open Mobile Platform LLC <community@omp.ru>
    // SPDX-License-Identifier: BSD-3-Clause
    
    #pragma once
    
    #include <QtDBus/QtDBus>
    
    // Данная структура служит для сериализации/десериализации данных операции (operation) 
    // D-Bus структуры типа (ssiii) - (string, string, int, int, int).
    struct Operation {
        QString operationUuid;      // Уникальный идентификатор операции
        QString packageId;          // Идентификатор пакета, с которым производится операция
        int operationType;          // Тип операции (например, установка или удаление)
        int operationStatus;        // Текущий статус выполнения операции
        int operationErrorCode;     // Код ошибки, если операция завершилась неудачно
    
        /*!
        Перегрузка оператора << для сериализации данных операции в формат аргумента D-Bus.
        Это необходимо, чтобы Qt мог передать структуру Operation через шину D-Bus.
        argument — объект QDBusArgument, в который записываются данные.
        data — постоянная ссылка на данные операции для преобразования.
        Возвращает ссылку на аргумент D-Bus с записанными данными.
        */
        friend QDBusArgument &operator << (QDBusArgument &argument, const Operation &data) {
            argument.beginStructure(); // Начало записи структуры D-Bus
            // Последовательная запись полей структуры в строгом соответствии с сигнатурой (ssiii)
            argument << data.operationUuid << data.packageId << data.operationType << data.operationStatus
                    << data.operationErrorCode;
            argument.endStructure();   // Конец записи структуры D-Bus
    
            return argument;
        }
    
        /*!
        Перегрузка оператора >> для десериализации (извлечения) данных операции из аргумента D-Bus.
        Это необходимо, чтобы Qt мог преобразовать полученные по D-Bus данные обратно в структуру Operation.
        argument — постоянная ссылка на QDBusArgument, из которого читаются данные.
        data — ссылка на объект Operation, который будет заполнен извлеченными данными.
        Возвращает постоянную ссылку на аргумент D-Bus.
        */
        friend const QDBusArgument &operator >> (const QDBusArgument &argument, Operation &data) {
            argument.beginStructure(); // Начало чтения структуры D-Bus
            // Последовательное чтение полей в том же порядке, в котором они были записаны
            argument >> data.operationUuid >> data.packageId >> data.operationType >> data.operationStatus
                >> data.operationErrorCode;
            argument.endStructure();   // Конец чтения структуры D-Bus
    
            return argument;
        }
    
        /*!
        Перегрузка оператора вывода в QDebug для удобного логирования структуры Operation.
        debug — экземпляр класса QDebug.
        operation — постоянная ссылка на данные операции, предназначенные для вывода.
        Возвращает экземпляр класса QDebug для цепочки вызовов.
        */
        friend QDebug operator << (QDebug debug, const Operation &operation)
        {
            debug << "operationUuid =" << operation.operationUuid 
                << "packageId =" << operation.packageId
                << "operationType =" << operation.operationType 
                << "operationStatus =" << operation.operationStatus
                << "operationErrorCode =" << operation.operationErrorCode;
    
            return debug;
        }
    };
    
    // Регистрация пользовательского типа Operation в системе метаобъектов Qt.
    // Это обязательный шаг для использования типа в сигналах, слотах и свойствах Qt.
    Q_DECLARE_METATYPE(Operation)
    
  4. dbusinterface.h:

    // SPDX-FileCopyrightText: 2025 Open Mobile Platform LLC <community@omp.ru>
    // SPDX-License-Identifier: BSD-3-Clause
    
    #pragma once
    
    #include "customdbastype.h"
    
    /*!
    Класс DbusInterface предоставляет высокоуровневый C++ интерфейс для работы 
    с D-Bus API сервиса пакетного менеджера ru.omp.Market.
    Наследование от QDBusAbstractInterface является стандартным способом обёртки D-Bus интерфейса в Qt.
    */
    class DbusInterface final : public QDBusAbstractInterface
    {
        Q_OBJECT // Макрос, необходимый для поддержки сигналов, слотов и системы метаобъектов Qt
    
    public:
        // Конструктор инициализирует соединение с D-Bus сервисом.
        explicit DbusInterface(QObject *parent = nullptr);
        
        // Деструктор по умолчанию (final override гарантирует, что он не будет переопределён в наследниках)
        ~DbusInterface() final override = default;
    
        // Метод для асинхронного запроса всех свойств сервиса Market
        void allProperties();
        
        // Метод для асинхронного запроса на удаление пакета по его идентификатору
        void removePackage(const QString &packageID);
    
        // Геттер для получения списка установленных пакетов (синхронный, возвращает кэшированные данные)
        QStringList installedPackages() const;
    
    public slots:
        // Слот-обработчик сигнала D-Bus об успешном удалении пакета
        void handlePackageRemoved(const QString &packageId);
        
        // Слот-обработчик сигнала D-Bus об изменении статуса операции
        void handleOperationStatusChanged(const Operation &operation);
    
    private:
        // Внутренний кэш списка установленных пакетов, обновляемый при запросе свойств
        QStringList m_installedPackages;
    
    };
    
  5. dbusinterface.cpp:

    // SPDX-FileCopyrightText: 2025 Open Mobile Platform LLC <community@omp.ru>
    // SPDX-License-Identifier: BSD-3-Clause
    
    #include "dbusinterface.h"
    
    #include <QDebug>
    
    // Анонимное пространство имён для хранения констант D-Bus, чтобы избежать загрязнения глобальной области видимости
    namespace {
        const QString marketService = QStringLiteral("ru.omp.Market");          // Имя сервиса в шине D-Bus
        const QString marketObjectPath = QStringLiteral("/ru/omp/Market");      // Путь к объекту в сервисе
        const char *marketInterface = "ru.omp.Market";                          // Имя интерфейса D-Bus
    } 
    
    // Конструктор регистрирует типы и подключается к D-Bus сервису ru.omp.Market.
    // parent — родительский экземпляр QObject для управления временем жизни объекта.
    DbusInterface::DbusInterface(QObject *parent)
        // Инициализация базового класса QDBusAbstractInterface с параметрами подключения к сессионной шине (sessionBus)
        : QDBusAbstractInterface(marketService, marketObjectPath, marketInterface, QDBusConnection::sessionBus(), parent)
    {
        // Регистрация пользовательского типа Operation для использования в сигналах/слотах Qt
        qRegisterMetaType<Operation>("Operation");
        // Регистрация типа Operation специально для системы D-Bus (для сериализации/десериализации)
        qDBusRegisterMetaType<Operation>();
    
        // Подключение к сигналу D-Bus "PackageRemoved"
        // Связываем сигнал сервиса с локальным слотом handlePackageRemoved
        bool isConnected = connection().connect(marketService, marketObjectPath, marketInterface,
            QStringLiteral("PackageRemoved"), this, SLOT(handlePackageRemoved(const QString &)));
        if (!isConnected) {
            qWarning() << "Can't connect to PackageRemoved() signal !";
        }
    
        // Подключение к сигналу D-Bus "OperationStatusChanged"
        // Связываем сигнал сервиса с локальным слотом handleOperationStatusChanged
        isConnected = connection().connect(marketService, marketObjectPath, marketInterface,
            QStringLiteral("OperationStatusChanged"), this, SLOT(handleOperationStatusChanged(const Operation &)));
        if (!isConnected) {
            qWarning() << "Can't connect to OperationStatusChanged() signal !";
        }
    }
    
    /*!
    Запрашивает все свойства, необходимые для работы с пакетами, через D-Bus метод GetAllProperties().
    Используя GetAllProperties(), можно получить словарь со всеми требуемыми свойствами:
                    - a{sv}: {
                        'Architecture':        String,
                        'AuroraVersion':       String,
                        'Packages':            List<Package>      a(ssssa{sv})
                        'Operations':          List<Operations>,  a(ssiii)
                        'CompletedOperations': List<Operations>,  a(ssiii)
                    }
    Данный метод не демонстрирует получение всех данных, а показывает принцип парсинга ответа.
    */   
    void DbusInterface::allProperties()
    {
        // Инициируем асинхронный вызов метода D-Bus. Он не блокирует основной поток.
        const QDBusPendingReply<QVariantMap> asyncReply = asyncCall(QStringLiteral("GetAllProperties"));
        
        // Создаем наблюдателя (watcher) для отслеживания завершения асинхронного вызова
        QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(asyncReply, this);
    
        // Подключаемся к сигналу завершения вызова через лямбда-функцию
        QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) {
            if (watcher->isError()) {
                // Если при вызове произошла ошибка D-Bus, логируем её
                qWarning() << "AllProperties: asyncCall() error: " << watcher->error().message();
            } else {
                // Получаем результат вызова
                const QDBusPendingReply<QVariantMap> asyncReply = *watcher;
                QVariantMap properties = asyncReply.value();
    
                // Извлекаем и логируем простые строковые свойства
                qDebug() << "AllProperties: Architecture = " << properties.value("Architecture", QVariant()).toString();
                qDebug() << "AllProperties: AuroraVersion = " << properties.value("AuroraVersion", QVariant()).toString();
    
                // Извлекаем список пакетов. qdbus_cast безопасно преобразует QVariant в QVariantList
                QVariantList packagesVariant = qdbus_cast<QVariantList>(properties.value("Packages", QVariant()));
                m_installedPackages.reserve(packagesVariant.size()); // Оптимизация выделения памяти
                
                // Проходим по списку и сохраняем идентификаторы пакетов в кэш
                for (const auto &package : std::as_const(packagesVariant)) {
                    m_installedPackages.append(package.toString());
                }
                qDebug() << "AllProperties: Packages: " << m_installedPackages;
    
                // Извлекаем список активных операций, используя qdbus_cast для преобразования в QList<Operation>
                QList<Operation> activeOperations =
                    qdbus_cast<QList<Operation>>(properties.value("Operations", QVariant()));
                for (const auto &operation : std::as_const(activeOperations)) {
                    qDebug() << "AllProperties: Operations: " << operation; // Использует перегруженный operator<<
                }
    
                // Извлекаем список завершённых операций аналогичным образом
                QList<Operation> completedOperations =
                    qdbus_cast<QList<Operation>>(properties.value("CompletedOperations", QVariant()));
                for (const auto &completedOperation : std::as_const(completedOperations)) {
                    qDebug() << "AllProperties: CompletedOperations: " << completedOperation;
                }
            }
            // Обязательно удаляем watcher после завершения обработки, чтобы избежать утечки памяти
            watcher->deleteLater();
        });
    }
    
    /*!
    Удаляет пакет по его packageID через D-Bus метод Remove().
    Ответ на вызов метода удаления пакета будет содержать структуру с данными операции (ssiii).
    packageID — идентификатор пакета в формате: "ru.auroraos.ApplicationTemplate".
    */
    void DbusInterface::removePackage(const QString &packageID)
    {
        // Инициируем асинхронный вызов метода "Remove", передавая packageID как QVariant
        const QDBusPendingReply<Operation> asyncReply = asyncCall(QStringLiteral("Remove"), QVariant::fromValue(packageID));
        
        // Создаем наблюдателя для отслеживания завершения вызова
        QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(asyncReply, this);
    
        QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [](QDBusPendingCallWatcher *watcher) {
            if (watcher->isError()) {
                // Логируем ошибку, если удаление не удалось (например, пакет не найден или не хватает прав)
                qWarning() << "Remove: error deleting package: " << watcher->error().message();
            } else {
                // Получаем структуру Operation, которую вернул сервис Market
                const QDBusPendingReply<Operation> asyncReply = *watcher;
                Operation reply = asyncReply.value();
                qDebug() << "Remove: removed package operation: " << reply;
            }
            // Удаляем watcher после обработки
            watcher->deleteLater();
        });
    }
    
    /*!
    Геттер для установленных пакетов.
    Возвращает ссылку на внутренний список кэшированных установленных пакетов.
    */
    QStringList DbusInterface::installedPackages() const
    {
        return m_installedPackages;
    }
    
    /*!
    Обработчик сигнала PackageRemoved.
    Вызывается автоматически шиной D-Bus, когда сервис Market удаляет пакет.
    packageId — идентификатор удалённого пакета.
    */
    void DbusInterface::handlePackageRemoved(const QString &packageId)
    {
        qDebug() <<  "PackageRemoved, packageId = " << packageId;
        // Здесь можно добавить логику обновления UI или внутреннего кэша m_installedPackages
    }
    
    /*!
    Обработчик сигнала OperationStatusChanged.
    Вызывается автоматически шиной D-Bus при изменении статуса любой операции.
    operation — данные операции, содержащие новый статус.
    */
    void DbusInterface::handleOperationStatusChanged(const Operation &operation)
    {
        qDebug() << "OperationStatusChanged: " << operation;
        // Здесь можно добавить логику обновления прогресс-баров или статуса операции в UI
    }
    
  6. main.cpp:

    ...
    // Подключаем заголовочный файл нашего D-Bus интерфейса
    #include "{путь к файлу}/dbusinterface.h"
    ...
    
    int main(int argc, char *argv[])
    {
        ... // Инициализация приложения (QGuiApplication/QCoreApplication и т.д.)
    
        // Создаём экземпляр интерфейса. 
        // В конструкторе автоматически произойдёт подключение к сессионной шине D-Bus и подписка на сигналы.
        DbusInterface marketInterface;
        
        // Запрашиваем все свойства сервиса (вызов асинхронный, результат придёт в слот watcher'а)
        marketInterface.allProperties();
    
        // Инициируем асинхронное удаление конкретного пакета по его ID
        marketInterface.removePackage(QStringLiteral("ru.auroraos.ApplicationTemplate"));
        
        /*!
            Пример: удаляет первый пакет из списка установленных пакетов (если список не пуст):
            if (marketInterface.installedPackages().size()) {
                marketInterface.removePackage(*marketInterface.installedPackages().constBegin());
            }
        */
        ... // Запуск event loop (app.exec())
    }
    

Мы используем cookies для персонализации сайта и его более удобного использования. Вы можете запретить cookies в настройках браузера.

Пожалуйста ознакомьтесь с политикой использования файлов cookies.