bool DBusObjectManagerStub::emitInterfacesAddedSignal(std::shared_ptr<DBusStubAdapter> dbusStubAdapter,
                                                      const std::shared_ptr<DBusProxyConnection>& dbusConnection) const {
    assert(dbusConnection);
    assert(dbusConnection->isConnected());

    const auto& dbusStubObjectPath = dbusStubAdapter->getObjectPath();
    const auto& dbusStubInterfaceName = dbusStubAdapter->getInterfaceName();
    DBusMessage dbusSignal = DBusMessage::createSignal(dbusObjectPath_, getInterfaceName(), "InterfacesAdded", "oa{sa{sv}}");
    DBusOutputStream dbusOutputStream(dbusSignal);
    DBusInterfacesAndPropertiesDict dbusInterfacesAndPropertiesDict({
        { dbusStubInterfaceName, DBusPropertiesChangedDict() }
    });

    if (dbusStubAdapter->isManagingInterface()) {
        dbusInterfacesAndPropertiesDict.insert({ getInterfaceName(), DBusPropertiesChangedDict() });
    }

    if (dbusStubAdapter->hasFreedesktopProperties()) {
        dbusInterfacesAndPropertiesDict.insert({ "org.freedesktop.DBus.Properties", DBusPropertiesChangedDict() });
    }

    dbusOutputStream << dbusStubObjectPath;
    dbusOutputStream << dbusInterfacesAndPropertiesDict;
    dbusOutputStream.flush();

    const bool dbusSignalEmitted = dbusConnection->sendDBusMessage(dbusSignal);
    return dbusSignalEmitted;
}
bool DBusObjectManagerStub::onInterfaceDBusMessage(const DBusMessage& dbusMessage) {
    auto dbusConnection = dbusConnection_.lock();

    if (!dbusConnection || !dbusConnection->isConnected()) {
        return false;
    }

    if (!dbusMessage.isMethodCallType() || !dbusMessage.hasMemberName("GetManagedObjects")) {
        return false;
    }

    std::lock_guard<std::mutex> dbusObjectManagerStubLock(dbusObjectManagerStubLock_);
    DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict;

    for (const auto& registeredDBusObjectPathIterator : registeredDBusObjectPathsMap_) {
        const std::string& registeredDBusObjectPath = registeredDBusObjectPathIterator.first;
        const auto& registeredDBusInterfacesMap = registeredDBusObjectPathIterator.second;
        DBusInterfacesAndPropertiesDict dbusInterfacesAndPropertiesDict;

        assert(registeredDBusObjectPath.length() > 0);
        assert(registeredDBusInterfacesMap.size() > 0);

        for (const auto& registeredDBusInterfaceIterator : registeredDBusInterfacesMap) {
            const std::string& registeredDBusInterfaceName = registeredDBusInterfaceIterator.first;
            const auto& registeredDBusStubAdapter = registeredDBusInterfaceIterator.second;

            assert(registeredDBusInterfaceName.length() > 0);

            dbusInterfacesAndPropertiesDict.insert({ registeredDBusInterfaceName, DBusPropertiesChangedDict() });

            if (registeredDBusStubAdapter->isManagingInterface()) {
                dbusInterfacesAndPropertiesDict.insert({ getInterfaceName(), DBusPropertiesChangedDict() });
            }
        }

        dbusObjectPathAndInterfacesDict.insert({ registeredDBusObjectPath, std::move(dbusInterfacesAndPropertiesDict) });
    }

    DBusMessage dbusMessageReply = dbusMessage.createMethodReturn("a{oa{sa{sv}}}");
    DBusOutputStream dbusOutputStream(dbusMessageReply);

    dbusOutputStream << dbusObjectPathAndInterfacesDict;
    dbusOutputStream.flush();

    const bool dbusMessageReplySent = dbusConnection->sendDBusMessage(dbusMessageReply);
    return dbusMessageReplySent;
}
bool DBusObjectManagerStub::emitInterfacesRemovedSignal(std::shared_ptr<DBusStubAdapter> dbusStubAdapter,
                                                        const std::shared_ptr<DBusProxyConnection>& dbusConnection) const {
    assert(dbusConnection);
    assert(dbusConnection->isConnected());

    const auto& dbusStubObjectPath = dbusStubAdapter->getObjectPath();
    const auto& dbusStubInterfaceName = dbusStubAdapter->getInterfaceName();
    DBusMessage dbusSignal = DBusMessage::createSignal(dbusObjectPath_, getInterfaceName(), "InterfacesRemoved", "oas");
    DBusOutputStream dbusOutputStream(dbusSignal);
    std::vector<std::string> removedInterfaces({ { dbusStubInterfaceName } });

    if (dbusStubAdapter->isManagingInterface()) {
        removedInterfaces.push_back(getInterfaceName());
    }

    dbusOutputStream << dbusStubObjectPath;
    dbusOutputStream << removedInterfaces;
    dbusOutputStream.flush();

    const bool dbusSignalEmitted = dbusConnection->sendDBusMessage(dbusSignal);
    return dbusSignalEmitted;
}
TEST_F(DBusConnectionTest, SendingAsyncDBusMessagesWorks) {
    const char busName[] = "commonapi.dbus.test.TestInterfaceHandler";
    const char objectPath[] = "/common/api/dbus/test/TestObject";
    const char interfaceName[] = "commonapi.dbus.test.TestInterface";
    const char methodName[] = "TestMethod";

    auto interfaceHandlerDBusConnection = CommonAPI::DBus::DBusConnection::getSessionBus();

    ASSERT_TRUE(interfaceHandlerDBusConnection->connect());
    ASSERT_TRUE(interfaceHandlerDBusConnection->requestServiceNameAndBlock(busName));

    uint32_t serviceHandlerDBusMessageCount = 0;
    uint32_t clientReplyHandlerDBusMessageCount = 0;

    interfaceHandlerDBusConnection->setObjectPathMessageHandler(
                    [&serviceHandlerDBusMessageCount, &interfaceHandlerDBusConnection] (CommonAPI::DBus::DBusMessage dbusMessage) -> bool {
                        ++serviceHandlerDBusMessageCount;
                        CommonAPI::DBus::DBusMessage dbusMessageReply = dbusMessage.createMethodReturn("");
                        interfaceHandlerDBusConnection->sendDBusMessage(dbusMessageReply);
                        return true;
                    }
                    );

    interfaceHandlerDBusConnection->registerObjectPath(objectPath);

    ASSERT_TRUE(dbusConnection_->connect());

    CommonAPI::DBus::DBusMessage dbusReplyMessage;

    for (uint32_t expectedDBusMessageCount = 1; expectedDBusMessageCount <= 10; expectedDBusMessageCount++) {
        CommonAPI::DBus::DBusMessage dbusMessageCall = CommonAPI::DBus::DBusMessage::createMethodCall(
                        busName,
                        objectPath,
                        interfaceName,
                        methodName,
                        "");

        CommonAPI::DBus::DBusOutputStream dbusOutputStream(dbusMessageCall);

        interfaceHandlerDBusConnection->sendDBusMessageWithReplyAsync(
                        dbusMessageCall,
                        CommonAPI::DBus::DBusProxyAsyncCallbackHandler<>::create(
                                        [&clientReplyHandlerDBusMessageCount](CommonAPI::CallStatus status) {
                                            ASSERT_EQ(CommonAPI::CallStatus::SUCCESS, status);
                                            ++clientReplyHandlerDBusMessageCount;
                                        })
                                        );

        for (int i = 0; i < 10 && serviceHandlerDBusMessageCount < expectedDBusMessageCount; i++) {
            interfaceHandlerDBusConnection->readWriteDispatch(100);
        }

        ASSERT_EQ(serviceHandlerDBusMessageCount, expectedDBusMessageCount);

        for (int i = 0; i < 10 && clientReplyHandlerDBusMessageCount < expectedDBusMessageCount; i++) {
            dbusConnection_->readWriteDispatch(100);
        }

        ASSERT_EQ(clientReplyHandlerDBusMessageCount, expectedDBusMessageCount);
    }

    dbusConnection_->disconnect();

    interfaceHandlerDBusConnection->unregisterObjectPath(objectPath);

    ASSERT_TRUE(interfaceHandlerDBusConnection->releaseServiceName(busName));
    interfaceHandlerDBusConnection->disconnect();
}
bool DBusObjectManager::onIntrospectableInterfaceDBusMessage(const DBusMessage& dbusMessage) {
    std::shared_ptr<DBusProxyConnection> dbusConnection = dbusConnection_.lock();

    if (!dbusConnection || !dbusMessage.isMethodCallType() || !dbusMessage.hasMemberName("Introspect")) {
        return false;
    }

    bool foundRegisteredObjects = false;
    std::stringstream xmlData(std::ios_base::out);

    xmlData << "<!DOCTYPE node PUBLIC \"" DBUS_INTROSPECT_1_0_XML_PUBLIC_IDENTIFIER "\"\n\""
                    DBUS_INTROSPECT_1_0_XML_SYSTEM_IDENTIFIER"\">\n"
                    "<node name=\"" << dbusMessage.getObjectPath() << "\">\n"
                        "<interface name=\"org.freedesktop.DBus.Introspectable\">\n"
                            "<method name=\"Introspect\">\n"
                                "<arg type=\"s\" name=\"xml_data\" direction=\"out\"/>\n"
                            "</method>\n"
                        "</interface>\n";

    std::unordered_set<std::string> nodeSet;
    for (auto& registeredObjectsIterator : dbusRegisteredObjectsTable_) {
        const DBusInterfaceHandlerPath& handlerPath = registeredObjectsIterator.first;
        const std::string& dbusObjectPath = handlerPath.first;
        const std::string& dbusInterfaceName = handlerPath.second;
        std::shared_ptr<DBusInterfaceHandler> dbusStubAdapterBase = registeredObjectsIterator.second;
        std::vector<std::string> elems = CommonAPI::split(dbusObjectPath, '/');

        if (dbusMessage.hasObjectPath(dbusObjectPath)) {
            foundRegisteredObjects = true;

            xmlData << "<interface name=\"" << dbusInterfaceName << "\">\n"
                            << dbusStubAdapterBase->getMethodsDBusIntrospectionXmlData() << "\n"
                            "</interface>\n";
            nodeSet.insert(elems.back());
        } else {
            if (dbusMessage.hasObjectPath("/") && elems.size() > 1) {
                if (nodeSet.find(elems[1]) == nodeSet.end()) {
                    if (nodeSet.size() == 0) {
                        xmlData.str("");
                        xmlData << "<!DOCTYPE node PUBLIC \"" DBUS_INTROSPECT_1_0_XML_PUBLIC_IDENTIFIER "\"\n\""
                        DBUS_INTROSPECT_1_0_XML_SYSTEM_IDENTIFIER"\">\n"
                        "<node>\n";
                    }
                    xmlData << "    <node name=\"" << elems[1] << "\"/>\n";
                    nodeSet.insert(elems[1]);
                    foundRegisteredObjects = true;
                }
            } else {
                for (unsigned int i = 1; i < elems.size() - 1; i++) {
                    std::string build;
                    for (unsigned int j = 1; j <= i; j++) {
                        build = build + "/" + elems[j];
                        if (dbusMessage.hasObjectPath(build)) {
                            if (nodeSet.find(elems[j + 1]) == nodeSet.end()) {
                                if (nodeSet.size() == 0) {
                                    xmlData.str("");
                                    xmlData << "<!DOCTYPE node PUBLIC \"" DBUS_INTROSPECT_1_0_XML_PUBLIC_IDENTIFIER "\"\n\""
                                            DBUS_INTROSPECT_1_0_XML_SYSTEM_IDENTIFIER"\">\n"
                                            "<node>\n";
                                }
                                xmlData << "    <node name=\"" << elems[j + 1] << "\"/>\n";
                                nodeSet.insert(elems[j + 1]);
                                foundRegisteredObjects = true;
                            }
                            break;
                        }
                    }
                }
            }
        }
    }

    if (foundRegisteredObjects) {
        DBusMessage dbusMessageReply = dbusMessage.createMethodReturn("s");
        DBusOutputStream dbusOutputStream(dbusMessageReply);

        xmlData << "</node>"
                        "";

        dbusOutputStream << xmlData.str();
        dbusOutputStream.flush();

        return dbusConnection->sendDBusMessage(dbusMessageReply);
    }

    return false;
}