diff options
Diffstat (limited to 'AudioManagerCore')
-rw-r--r-- | AudioManagerCore/include/CAmControlReceiver.h | 3 | ||||
-rw-r--r-- | AudioManagerCore/include/CAmControlSender.h | 3 | ||||
-rw-r--r-- | AudioManagerCore/include/CAmDatabaseHandlerMap.h | 5 | ||||
-rw-r--r-- | AudioManagerCore/include/CAmRoutingReceiver.h | 3 | ||||
-rw-r--r-- | AudioManagerCore/include/CAmRoutingSender.h | 20 | ||||
-rw-r--r-- | AudioManagerCore/include/IAmDatabaseHandler.h | 5 | ||||
-rw-r--r-- | AudioManagerCore/src/CAmControlReceiver.cpp | 41 | ||||
-rw-r--r-- | AudioManagerCore/src/CAmControlSender.cpp | 13 | ||||
-rw-r--r-- | AudioManagerCore/src/CAmDatabaseHandlerMap.cpp | 90 | ||||
-rw-r--r-- | AudioManagerCore/src/CAmRoutingReceiver.cpp | 78 | ||||
-rw-r--r-- | AudioManagerCore/src/CAmRoutingSender.cpp | 25 | ||||
-rw-r--r-- | AudioManagerCore/test/AmMapHandlerTest/CAmMapHandlerTest.cpp | 14 | ||||
-rw-r--r-- | AudioManagerCore/test/AmRoutingInterfaceTest/CAmRoutingInterfaceTest.cpp | 118 | ||||
-rw-r--r-- | AudioManagerCore/test/MockIAmControlSend.h | 4 | ||||
-rw-r--r-- | AudioManagerCore/test/MockIAmRoutingSend.h | 6 |
15 files changed, 393 insertions, 35 deletions
diff --git a/AudioManagerCore/include/CAmControlReceiver.h b/AudioManagerCore/include/CAmControlReceiver.h index bd897de..d4a75b1 100644 --- a/AudioManagerCore/include/CAmControlReceiver.h +++ b/AudioManagerCore/include/CAmControlReceiver.h @@ -103,6 +103,7 @@ public: am_Error_e getGatewayInfoDB(const am_gatewayID_t gatewayID, am_Gateway_s &gatewayData) const; am_Error_e getConverterInfoDB(const am_converterID_t converterID, am_Converter_s &converterData) const; am_Error_e getCrossfaderInfoDB(const am_crossfaderID_t crossfaderID, am_Crossfader_s &crossfaderData) const; + am_Error_e getConnectionInfoDB(const am_connectionID_t connectionID, am_Connection_s &connectionData) const; am_Error_e getMainConnectionInfoDB(const am_mainConnectionID_t mainConnectionID, am_MainConnection_s &mainConnectionData) const; am_Error_e getListSinksOfDomain(const am_domainID_t domainID, std::vector<am_sinkID_t> &listSinkID) const; am_Error_e getListSourcesOfDomain(const am_domainID_t domainID, std::vector<am_sourceID_t> &listSourceID) const; @@ -127,6 +128,8 @@ public: void setRoutingRundown(); void confirmControllerReady(const am_Error_e error); void confirmControllerRundown(const am_Error_e error); + am_Error_e transferConnection(am_Handle_s &handle, am_mainConnectionID_t mainConnectionID + , am_domainID_t domainID) override; am_Error_e getSocketHandler(CAmSocketHandler * &socketHandler); void getInterfaceVersion(std::string &version) const; am_Error_e changeSourceDB(const am_sourceID_t sourceID, const am_sourceClass_t sourceClassID, const std::vector<am_SoundProperty_s> &listSoundProperties, const std::vector<am_CustomConnectionFormat_t> &listConnectionFormats, const std::vector<am_MainSoundProperty_s> &listMainSoundProperties); diff --git a/AudioManagerCore/include/CAmControlSender.h b/AudioManagerCore/include/CAmControlSender.h index 77e8e2c..71220d5 100644 --- a/AudioManagerCore/include/CAmControlSender.h +++ b/AudioManagerCore/include/CAmControlSender.h @@ -71,6 +71,8 @@ public: am_Error_e hookSystemDeregisterConverter(const am_converterID_t converterID); am_Error_e hookSystemRegisterCrossfader(const am_Crossfader_s &crossfaderData, am_crossfaderID_t &crossfaderID); am_Error_e hookSystemDeregisterCrossfader(const am_crossfaderID_t crossfaderID); + am_Error_e hookSystemRegisterEarlyConnection(am_domainID_t domainID + , const am_MainConnection_s &mainConnectionData, const am_Route_s &route); void hookSystemSinkVolumeTick(const am_Handle_s handle, const am_sinkID_t sinkID, const am_volume_t volume); void hookSystemSourceVolumeTick(const am_Handle_s handle, const am_sourceID_t sourceID, const am_volume_t volume); void hookSystemInterruptStateChange(const am_sourceID_t sourceID, const am_InterruptState_e interruptState); @@ -82,6 +84,7 @@ public: void hookSystemTimingInformationChanged(const am_mainConnectionID_t mainConnectionID, const am_timeSync_t time); void cbAckConnect(const am_Handle_s handle, const am_Error_e errorID); void cbAckDisconnect(const am_Handle_s handle, const am_Error_e errorID); + void cbAckTransferConnection(const am_Handle_s handle, const am_Error_e errorID); void cbAckCrossFade(const am_Handle_s handle, const am_HotSink_e hostsink, const am_Error_e error); void cbAckSetSinkVolumeChange(const am_Handle_s handle, const am_volume_t volume, const am_Error_e error); void cbAckSetSourceVolumeChange(const am_Handle_s handle, const am_volume_t voulme, const am_Error_e error); diff --git a/AudioManagerCore/include/CAmDatabaseHandlerMap.h b/AudioManagerCore/include/CAmDatabaseHandlerMap.h index 46da560..7c3f17d 100644 --- a/AudioManagerCore/include/CAmDatabaseHandlerMap.h +++ b/AudioManagerCore/include/CAmDatabaseHandlerMap.h @@ -123,13 +123,13 @@ public: }; am_Error_e enterDomainDB(const am_Domain_s &domainData, am_domainID_t &domainID); - am_Error_e enterMainConnectionDB(const am_MainConnection_s &mainConnectionData, am_mainConnectionID_t &connectionID); + am_Error_e enterMainConnectionDB(const am_MainConnection_s &mainConnectionData, am_mainConnectionID_t &connectionID, bool allowReserved = false) override; am_Error_e enterSinkDB(const am_Sink_s &sinkData, am_sinkID_t &sinkID); am_Error_e enterCrossfaderDB(const am_Crossfader_s &crossfaderData, am_crossfaderID_t &crossfaderID); am_Error_e enterGatewayDB(const am_Gateway_s &gatewayData, am_gatewayID_t &gatewayID); am_Error_e enterConverterDB(const am_Converter_s &converterData, am_converterID_t &converterID); am_Error_e enterSourceDB(const am_Source_s &sourceData, am_sourceID_t &sourceID); - am_Error_e enterConnectionDB(const am_Connection_s &connection, am_connectionID_t &connectionID); + am_Error_e enterConnectionDB(const am_Connection_s &connection, am_connectionID_t &connectionID, bool allowReserved = false) override; am_Error_e enterSinkClassDB(const am_SinkClass_s &sinkClass, am_sinkClass_t &sinkClassID); am_Error_e enterSourceClassDB(am_sourceClass_t &sourceClassID, const am_SourceClass_s &sourceClass); am_Error_e enterSystemProperties(const std::vector<am_SystemProperty_s> &listSystemProperties); @@ -175,6 +175,7 @@ public: am_Error_e getSinkInfoDB(const am_sinkID_t sinkID, am_Sink_s &sinkData) const; am_Error_e getSourceInfoDB(const am_sourceID_t sourceID, am_Source_s &sourceData) const; am_Error_e getCrossfaderInfoDB(const am_crossfaderID_t crossfaderID, am_Crossfader_s &crossfaderData) const; + am_Error_e getConnectionInfoDB(const am_connectionID_t connectionID, am_Connection_s &connectionData) const; am_Error_e getMainConnectionInfoDB(const am_mainConnectionID_t mainConnectionID, am_MainConnection_s &mainConnectionData) const; am_Error_e getSinkMainVolume(const am_sinkID_t sinkID, am_mainVolume_t &mainVolume) const; am_Error_e getSinkVolume(const am_sinkID_t sinkID, am_volume_t &volume) const; diff --git a/AudioManagerCore/include/CAmRoutingReceiver.h b/AudioManagerCore/include/CAmRoutingReceiver.h index 42f3e89..fe12ccd 100644 --- a/AudioManagerCore/include/CAmRoutingReceiver.h +++ b/AudioManagerCore/include/CAmRoutingReceiver.h @@ -44,6 +44,7 @@ public: CAmRoutingReceiver(IAmDatabaseHandler *iDatabaseHandler, CAmRoutingSender *iRoutingSender, CAmControlSender *iControlSender, CAmSocketHandler *iSocketHandler); CAmRoutingReceiver(IAmDatabaseHandler *iDatabaseHandler, CAmRoutingSender *iRoutingSender, CAmControlSender *iControlSender, CAmSocketHandler *iSocketHandler, CAmDbusWrapper *iDBusWrapper); ~CAmRoutingReceiver(); + void ackTransferConnection(const am_Handle_s handle, const am_Error_e errorID); void ackConnect(const am_Handle_s handle, const am_connectionID_t connectionID, const am_Error_e error); void ackDisconnect(const am_Handle_s handle, const am_connectionID_t connectionID, const am_Error_e error); void ackSetSinkVolumeChange(const am_Handle_s handle, const am_volume_t volume, const am_Error_e error); @@ -59,6 +60,8 @@ public: am_Error_e peekDomain(const std::string &name, am_domainID_t &domainID); am_Error_e registerDomain(const am_Domain_s &domainData, am_domainID_t &domainID); am_Error_e deregisterDomain(const am_domainID_t domainID); + am_Error_e registerEarlyConnection(am_domainID_t domainID, const am_Route_s &route + , am_ConnectionState_e state); am_Error_e registerGateway(const am_Gateway_s &gatewayData, am_gatewayID_t &gatewayID); am_Error_e registerConverter(const am_Converter_s &converterData, am_converterID_t &converterID); am_Error_e deregisterGateway(const am_gatewayID_t gatewayID); diff --git a/AudioManagerCore/include/CAmRoutingSender.h b/AudioManagerCore/include/CAmRoutingSender.h index 39b5c23..cbaddae 100644 --- a/AudioManagerCore/include/CAmRoutingSender.h +++ b/AudioManagerCore/include/CAmRoutingSender.h @@ -62,6 +62,8 @@ public: am_Error_e startupInterfaces(CAmRoutingReceiver *iRoutingReceiver); void setRoutingReady(); void setRoutingRundown(); + am_Error_e asyncTransferConnection(am_Handle_s &handle, am_domainID_t domainID + , const std::vector<std::pair<std::string, std::string>> &route, am_ConnectionState_e state); am_Error_e asyncAbort(const am_Handle_s &handle); am_Error_e asyncConnect(am_Handle_s &handle, am_connectionID_t &connectionID, const am_sourceID_t sourceID, const am_sinkID_t sinkID, const am_CustomConnectionFormat_t connectionFormat); am_Error_e asyncDisconnect(am_Handle_s &handle, const am_connectionID_t connectionID); @@ -262,6 +264,24 @@ public: CAmRoutingSender *mRoutingSender; }; + class handleTransfer : public handleDataBase + { + public: + handleTransfer(IAmRoutingSend *interface, const std::vector<std::pair<std::string, std::string>> &route + , am_ConnectionState_e state, IAmDatabaseHandler *databaseHandler) + : handleDataBase(interface, databaseHandler) + , mRoute(route) + , mState(state) + , mTransferPending(true) {} + ~handleTransfer() { }; + am_Error_e writeDataToDatabase() { return E_OK; }; + + private: + const std::vector<std::pair<std::string, std::string>> mRoute; + am_ConnectionState_e mState; + bool mTransferPending; + }; + class handleSetVolumes : public handleDataBase { public: diff --git a/AudioManagerCore/include/IAmDatabaseHandler.h b/AudioManagerCore/include/IAmDatabaseHandler.h index 8e4118e..281a93b 100644 --- a/AudioManagerCore/include/IAmDatabaseHandler.h +++ b/AudioManagerCore/include/IAmDatabaseHandler.h @@ -62,13 +62,13 @@ public: * The following interface methods must be implemented by the subclass. */ virtual am_Error_e enterDomainDB(const am_Domain_s &domainData, am_domainID_t &domainID) = 0; - virtual am_Error_e enterMainConnectionDB(const am_MainConnection_s &mainConnectionData, am_mainConnectionID_t &connectionID) = 0; + virtual am_Error_e enterMainConnectionDB(const am_MainConnection_s &mainConnectionData, am_mainConnectionID_t &connectionID, bool allowReserved) = 0; virtual am_Error_e enterSinkDB(const am_Sink_s &sinkData, am_sinkID_t &sinkID) = 0; virtual am_Error_e enterCrossfaderDB(const am_Crossfader_s &crossfaderData, am_crossfaderID_t &crossfaderID) = 0; virtual am_Error_e enterGatewayDB(const am_Gateway_s &gatewayData, am_gatewayID_t &gatewayID) = 0; virtual am_Error_e enterConverterDB(const am_Converter_s &converteData, am_converterID_t &converterID) = 0; virtual am_Error_e enterSourceDB(const am_Source_s &sourceData, am_sourceID_t &sourceID) = 0; - virtual am_Error_e enterConnectionDB(const am_Connection_s &connection, am_connectionID_t &connectionID) = 0; + virtual am_Error_e enterConnectionDB(const am_Connection_s &connection, am_connectionID_t &connectionID, bool allowReserved) = 0; virtual am_Error_e enterSinkClassDB(const am_SinkClass_s &sinkClass, am_sinkClass_t &sinkClassID) = 0; virtual am_Error_e enterSourceClassDB(am_sourceClass_t &sourceClassID, const am_SourceClass_s &sourceClass) = 0; virtual am_Error_e enterSystemProperties(const std::vector<am_SystemProperty_s> &listSystemProperties) = 0; @@ -114,6 +114,7 @@ public: virtual am_Error_e getSinkInfoDB(const am_sinkID_t sinkID, am_Sink_s &sinkData) const = 0; virtual am_Error_e getSourceInfoDB(const am_sourceID_t sourceID, am_Source_s &sourceData) const = 0; virtual am_Error_e getCrossfaderInfoDB(const am_crossfaderID_t crossfaderID, am_Crossfader_s &crossfaderData) const = 0; + virtual am_Error_e getConnectionInfoDB(const am_connectionID_t connectionID, am_Connection_s &connectionData) const = 0; virtual am_Error_e getMainConnectionInfoDB(const am_mainConnectionID_t mainConnectionID, am_MainConnection_s &mainConnectionData) const = 0; virtual am_Error_e getSinkMainVolume(const am_sinkID_t sinkID, am_mainVolume_t &mainVolume) const = 0; virtual am_Error_e getSinkVolume(const am_sinkID_t sinkID, am_volume_t &volume) const = 0; diff --git a/AudioManagerCore/src/CAmControlReceiver.cpp b/AudioManagerCore/src/CAmControlReceiver.cpp index 7a6072c..7efee95 100644 --- a/AudioManagerCore/src/CAmControlReceiver.cpp +++ b/AudioManagerCore/src/CAmControlReceiver.cpp @@ -128,7 +128,7 @@ am_Error_e CAmControlReceiver::enterDomainDB(const am_Domain_s &domainData, am_d am_Error_e CAmControlReceiver::enterMainConnectionDB(const am_MainConnection_s &mainConnectionData, am_mainConnectionID_t &connectionID) { - return (mDatabaseHandler->enterMainConnectionDB(mainConnectionData, connectionID)); + return (mDatabaseHandler->enterMainConnectionDB(mainConnectionData, connectionID, false)); } am_Error_e CAmControlReceiver::enterSinkDB(const am_Sink_s &sinkData, am_sinkID_t &sinkID) @@ -291,6 +291,11 @@ am_Error_e CAmControlReceiver::getSourceInfoDB(const am_sourceID_t sourceID, am_ return (mDatabaseHandler->getSourceInfoDB(sourceID, sourceData)); } +am_Error_e CAmControlReceiver::getConnectionInfoDB(const am_connectionID_t connectionID, am_Connection_s &connectionData) const +{ + return (mDatabaseHandler->getConnectionInfoDB(connectionID, connectionData)); +} + am_Error_e CAmControlReceiver::getMainConnectionInfoDB(const am_mainConnectionID_t mainConnectionID, am_MainConnection_s &mainConnectionData) const { return (mDatabaseHandler->getMainConnectionInfoDB(mainConnectionID, mainConnectionData)); @@ -451,6 +456,40 @@ void CAmControlReceiver::confirmControllerRundown(const am_Error_e error) mSocketHandler->exit_mainloop(); } +am_Error_e CAmControlReceiver::transferConnection(am_Handle_s &handle + , am_mainConnectionID_t mainConnectionID, am_domainID_t domainID) +{ + am_MainConnection_s mainConnectionData; + if (mDatabaseHandler->getMainConnectionInfoDB(mainConnectionID, mainConnectionData) != E_OK) + { + return E_DATABASE_ERROR; + } + + std::vector<std::pair<std::string, std::string>> route; + route.reserve(mainConnectionData.listConnectionID.size()); + for (auto iter : mainConnectionData.listConnectionID) + { + am_Connection_s connectionData; + if (mDatabaseHandler->getConnectionInfoDB(iter, connectionData) != E_OK) + { + return E_DATABASE_ERROR; + } + + // determine source and sink name, even if they are only peeked, but not fully registered + am_Source_s sourceData; + am_Sink_s sinkData; + if ( (mDatabaseHandler->getSourceInfoDB(connectionData.sourceID, sourceData) == E_NON_EXISTENT) + || (mDatabaseHandler->getSinkInfoDB(connectionData.sinkID, sinkData) == E_NON_EXISTENT)) + { + return E_DATABASE_ERROR; + } + + route.push_back({sourceData.name, sinkData.name}); + } + + return mRoutingSender->asyncTransferConnection(handle, domainID, route, mainConnectionData.connectionState); +} + am_Error_e CAmControlReceiver::getSocketHandler(CAmSocketHandler * &socketHandler) { socketHandler = mSocketHandler; diff --git a/AudioManagerCore/src/CAmControlSender.cpp b/AudioManagerCore/src/CAmControlSender.cpp index 0fa84ef..bb92198 100644 --- a/AudioManagerCore/src/CAmControlSender.cpp +++ b/AudioManagerCore/src/CAmControlSender.cpp @@ -319,6 +319,13 @@ am_Error_e CAmControlSender::hookSystemDeregisterCrossfader(const am_crossfaderI return (mController->hookSystemDeregisterCrossfader(crossfaderID)); } +am_Error_e CAmControlSender::hookSystemRegisterEarlyConnection(am_domainID_t domainID + , const am_MainConnection_s &mainConnectionData, const am_Route_s &route) +{ + assert(mController); + return mController->hookSystemRegisterEarlyMainConnection(domainID, mainConnectionData, route); +} + void CAmControlSender::hookSystemSinkVolumeTick(const am_Handle_s handle, const am_sinkID_t sinkID, const am_volume_t volume) { assert(mController); @@ -385,6 +392,12 @@ void CAmControlSender::cbAckDisconnect(const am_Handle_s handle, const am_Error_ mController->cbAckDisconnect(handle, errorID); } +void CAmControlSender::cbAckTransferConnection(const am_Handle_s handle, const am_Error_e errorID) +{ + assert(mController); + mController->cbAckTransferConnection(handle, errorID); +} + void CAmControlSender::cbAckCrossFade(const am_Handle_s handle, const am_HotSink_e hostsink, const am_Error_e error) { assert(mController); diff --git a/AudioManagerCore/src/CAmDatabaseHandlerMap.cpp b/AudioManagerCore/src/CAmDatabaseHandlerMap.cpp index 6e6e3f9..fc539bb 100644 --- a/AudioManagerCore/src/CAmDatabaseHandlerMap.cpp +++ b/AudioManagerCore/src/CAmDatabaseHandlerMap.cpp @@ -585,7 +585,7 @@ int16_t CAmDatabaseHandlerMap::calculateDelayForRoute(const std::vector<am_conne return delay; } -am_Error_e CAmDatabaseHandlerMap::enterMainConnectionDB(const am_MainConnection_s &mainConnectionData, am_mainConnectionID_t &connectionID) +am_Error_e CAmDatabaseHandlerMap::enterMainConnectionDB(const am_MainConnection_s &mainConnectionData, am_mainConnectionID_t &connectionID, bool allowReserved) { if (mainConnectionData.mainConnectionID != 0) { @@ -599,18 +599,34 @@ am_Error_e CAmDatabaseHandlerMap::enterMainConnectionDB(const am_MainConnection_ return (E_NOT_POSSIBLE); } - if (!existSink(mainConnectionData.sinkID)) + auto itMappedSink = mMappedData.mSinkMap.find(mainConnectionData.sinkID); + if ((itMappedSink == mMappedData.mSinkMap.end()) || (itMappedSink->second.reserved && !allowReserved)) { logError(__METHOD_NAME__, "sinkID must be valid!"); return (E_NOT_POSSIBLE); } - if (!existSource(mainConnectionData.sourceID)) + auto itMappedSource = mMappedData.mSourceMap.find(mainConnectionData.sourceID); + if ((itMappedSource == mMappedData.mSourceMap.end()) || (itMappedSource->second.reserved && !allowReserved)) { logError(__METHOD_NAME__, "sourceID must be valid!"); return (E_NOT_POSSIBLE); } + // check if we already have this connection + for (auto &mapped : mMappedData.mMainConnectionMap) + { + if ((mapped.second.sourceID != mainConnectionData.sourceID) || (mapped.second.sinkID != mainConnectionData.sinkID)) + { + continue; + } + + connectionID = mapped.second.mainConnectionID; + logWarning(__METHOD_NAME__, "main connection from source", mainConnectionData.sourceID + , "to sink", mainConnectionData.sinkID, "already exists with ID", connectionID); + return E_ALREADY_EXISTS; + } + int16_t delay = 0; int16_t nextID = 0; if (mMappedData.increaseMainConnectionID(nextID)) @@ -1149,7 +1165,7 @@ am_Error_e CAmDatabaseHandlerMap::enterSourceDB(const am_Source_s &sourceData, a return (E_OK); } -am_Error_e CAmDatabaseHandlerMap::enterConnectionDB(const am_Connection_s &connection, am_connectionID_t &connectionID) +am_Error_e CAmDatabaseHandlerMap::enterConnectionDB(const am_Connection_s &connection, am_connectionID_t &connectionID, bool allowReserved) { if (connection.connectionID != 0) { @@ -1157,18 +1173,34 @@ am_Error_e CAmDatabaseHandlerMap::enterConnectionDB(const am_Connection_s &conne return (E_NOT_POSSIBLE); } - if (!existSink(connection.sinkID)) + const AmMapSink::const_iterator &itMappedSink = mMappedData.mSinkMap.find(connection.sinkID); + if ((itMappedSink == mMappedData.mSinkMap.end()) || (itMappedSink->second.reserved && !allowReserved)) { logError(__METHOD_NAME__, "sinkID must exist!"); return (E_NOT_POSSIBLE); } - if (!existSource(connection.sourceID)) + const AmMapSource::const_iterator &itMappedSource = mMappedData.mSourceMap.find(connection.sourceID); + if ((itMappedSource == mMappedData.mSourceMap.end()) || (itMappedSource->second.reserved && !allowReserved)) { logError(__METHOD_NAME__, "sourceID must exist!"); return (E_NOT_POSSIBLE); } + // check if we already have this connection + for (auto &mapped : mMappedData.mConnectionMap) + { + if ((mapped.second.sourceID != connection.sourceID) || (mapped.second.sinkID != connection.sinkID)) + { + continue; + } + + connectionID = mapped.second.connectionID; + logWarning(__METHOD_NAME__, "connection from source", connection.sourceID + , "to sink", connection.sinkID, "already exists with ID", connectionID); + return E_ALREADY_EXISTS; + } + // connection format is not checked, because it's project specific int16_t nextID = 0; if (mMappedData.increaseConnectionID(nextID)) @@ -1917,54 +1949,66 @@ am_Error_e CAmDatabaseHandlerMap::getSourceClassInfoDB(const am_sourceID_t sourc am_Error_e CAmDatabaseHandlerMap::getSinkInfoDB(const am_sinkID_t sinkID, am_Sink_s &sinkData) const { - - if (!existSink(sinkID)) + auto iter = mMappedData.mSinkMap.find(sinkID); + if (iter == mMappedData.mSinkMap.end()) { logWarning(__METHOD_NAME__, "sinkID", sinkID, "does not exist"); return (E_NON_EXISTENT); } - am_Sink_Database_s mappedSink = mMappedData.mSinkMap.at(sinkID); - if ( true == mappedSink.reserved ) + sinkData = iter->second; // copy to output parameter even if only ID and name are valid + if ( iter->second.reserved ) { - return (E_NON_EXISTENT); + logWarning(__METHOD_NAME__, "sinkID", sinkID, "reserved for", sinkData.name, "but details are E_UNKNOWN"); + return E_UNKNOWN; } - sinkData = mappedSink; - return (E_OK); } am_Error_e CAmDatabaseHandlerMap::getSourceInfoDB(const am_sourceID_t sourceID, am_Source_s &sourceData) const { - - if (!existSource(sourceID)) + auto iter = mMappedData.mSourceMap.find(sourceID); + if (iter == mMappedData.mSourceMap.end()) { logWarning(__METHOD_NAME__, "sourceID", sourceID, "does not exist"); return (E_NON_EXISTENT); } - am_Source_Database_s mappedSource = mMappedData.mSourceMap.at(sourceID); - if ( true == mappedSource.reserved ) + sourceData = iter->second; // copy to output parameter even if only ID and name are valid + if ( true == iter->second.reserved ) { + logWarning(__METHOD_NAME__, "sourceID", sourceID, "reserved for", sourceData.name, "but details are E_UNKNOWN"); + return E_UNKNOWN; + } + + return (E_OK); +} + +am_Error_e am::CAmDatabaseHandlerMap::getConnectionInfoDB(const am_connectionID_t connectionID, am_Connection_s &connectionData) const +{ + auto iter = mMappedData.mConnectionMap.find(connectionID); + if (iter == mMappedData.mConnectionMap.end()) + { + logError(__METHOD_NAME__, "connectionID", connectionID, "does not exist"); return (E_NON_EXISTENT); } - sourceData = mappedSource; + connectionData = iter->second; - return (E_OK); + return E_OK; } am_Error_e am::CAmDatabaseHandlerMap::getMainConnectionInfoDB(const am_mainConnectionID_t mainConnectionID, am_MainConnection_s &mainConnectionData) const { - if (!existMainConnection(mainConnectionID)) + auto iter = mMappedData.mMainConnectionMap.find(mainConnectionID); + if (iter == mMappedData.mMainConnectionMap.end()) { - logError(__METHOD_NAME__, "mainConnectionID must exist"); + logError(__METHOD_NAME__, "mainConnectionID", mainConnectionID, "does not exist"); return (E_NON_EXISTENT); } - am_MainConnection_s temp = mMappedData.mMainConnectionMap.at(mainConnectionID); - mainConnectionData = temp; + mainConnectionData = iter->second; return (E_OK); } diff --git a/AudioManagerCore/src/CAmRoutingReceiver.cpp b/AudioManagerCore/src/CAmRoutingReceiver.cpp index be13474..56fb5e2 100644 --- a/AudioManagerCore/src/CAmRoutingReceiver.cpp +++ b/AudioManagerCore/src/CAmRoutingReceiver.cpp @@ -124,6 +124,28 @@ void CAmRoutingReceiver::ackDisconnect(const am_Handle_s handle, const am_connec mpControlSender->cbAckDisconnect(handle, error); } +/** + * Support hand-over acknowledgment of connections surviving shutdown of the AM + * + * @param handle: composite identifier used in the request + * @param errorID:success indicator (E_OK if application takes over, + * E_NOT_POSSIBLE if the routing adapter is not prepared to take over + * full responsibility for all involved sources and sinks) + */ +void CAmRoutingReceiver::ackTransferConnection(const am_Handle_s handle, const am_Error_e error) +{ + if (error == E_OK) + { + mpRoutingSender->writeToDatabaseAndRemove(handle); + } + else + { + mpRoutingSender->removeHandle(handle); + } + + mpControlSender->cbAckTransferConnection(handle, error); +} + void CAmRoutingReceiver::ackSetSinkVolumeChange(const am_Handle_s handle, const am_volume_t volume, const am_Error_e error) { logInfo(__METHOD_NAME__, "handle=", handle, "volume=", volume, "error=", error); @@ -233,6 +255,62 @@ am_Error_e CAmRoutingReceiver::deregisterDomain(const am_domainID_t domainID) return (mpControlSender->hookSystemDeregisterDomain(domainID)); } +am_Error_e CAmRoutingReceiver::registerEarlyConnection(am_domainID_t domainID + , const am_Route_s &route, am_ConnectionState_e state) +{ + const auto &segmentList = route.route; + if (segmentList.size() < 1) + { + logError(__METHOD_NAME__, "empty route from domain", domainID); + return E_NOT_POSSIBLE; + } + + am_MainConnection_s mainConnectionData; + mainConnectionData.mainConnectionID = 0; + mainConnectionData.sourceID = segmentList.front().sourceID; + mainConnectionData.sinkID = segmentList.back().sinkID; + mainConnectionData.connectionState = state; + mainConnectionData.listConnectionID.reserve(segmentList.size()); + for (const auto &segment : segmentList) + { + am_Connection_s conn; + conn.sourceID = segment.sourceID; + conn.sinkID = segment.sinkID; + conn.connectionFormat = segment.connectionFormat; + conn.connectionID = 0; + am_Error_e success = mpDatabaseHandler->enterConnectionDB(conn, conn.connectionID, true); + switch (success) + { + case E_OK: + case E_ALREADY_EXISTS: + case E_NO_CHANGE: + mainConnectionData.listConnectionID.push_back(conn.connectionID); + break; + + default: + logError(__METHOD_NAME__, "failed to enter connection segment", conn.sourceID + , "to", conn.sinkID, "from domain", domainID, "error=", success); + return success; + } + } + + am_Error_e success = mpDatabaseHandler->enterMainConnectionDB(mainConnectionData, mainConnectionData.mainConnectionID, true); + switch (success) + { + case E_OK: + case E_ALREADY_EXISTS: + case E_NO_CHANGE: + break; + + default: + logError(__METHOD_NAME__, "failed to enter main connection", mainConnectionData.sourceID + , "to", mainConnectionData.sinkID, "from domain", domainID, "error=", success); + return success; + } + + return mpControlSender->hookSystemRegisterEarlyConnection(domainID, mainConnectionData, route); +} + am_Error_e CAmRoutingReceiver::registerGateway(const am_Gateway_s &gatewayData, am_gatewayID_t &gatewayID) { return (mpControlSender->hookSystemRegisterGateway(gatewayData, gatewayID)); diff --git a/AudioManagerCore/src/CAmRoutingSender.cpp b/AudioManagerCore/src/CAmRoutingSender.cpp index 36f5c1a..fc9f879 100644 --- a/AudioManagerCore/src/CAmRoutingSender.cpp +++ b/AudioManagerCore/src/CAmRoutingSender.cpp @@ -285,7 +285,7 @@ am_Error_e CAmRoutingSender::asyncConnect(am_Handle_s &handle, am_connectionID_t tempConnection.connectionID = 0; tempConnection.delay = -1; - am_Error_e connError(mpDatabaseHandler->enterConnectionDB(tempConnection, connectionID)); + am_Error_e connError(mpDatabaseHandler->enterConnectionDB(tempConnection, connectionID, false)); if (connError) { return(connError); @@ -921,6 +921,29 @@ void CAmRoutingSender::setRoutingRundown() } } +am_Error_e CAmRoutingSender::asyncTransferConnection(am_Handle_s &handle, am_domainID_t domainID + , const std::vector<std::pair<std::string, std::string>> &route, am_ConnectionState_e state) +{ + auto iter = mMapDomainInterface.find(domainID); + if (iter != mMapDomainInterface.end() && iter->second) + { + auto handleData = std::make_shared<handleTransfer>(iter->second, route, state, mpDatabaseHandler); + handle = createHandle(handleData, H_TRANSFERCONNECTION); + + logInfo(__METHOD_NAME__, "handle=", handle); + + am_Error_e success = iter->second->asyncTransferConnection(handle, domainID, route, state); + if (success != E_OK) + { + removeHandle(handle); + } + return success; + } + + // given domain not found in map + return E_NON_EXISTENT; +} + am_Error_e CAmRoutingSender::asyncSetVolumes(am_Handle_s &handle, const std::vector<am_Volumes_s> &listVolumes) { IAmRoutingSend *pRoutingInterface(NULL); diff --git a/AudioManagerCore/test/AmMapHandlerTest/CAmMapHandlerTest.cpp b/AudioManagerCore/test/AmMapHandlerTest/CAmMapHandlerTest.cpp index 6e10d19..cbbfc06 100644 --- a/AudioManagerCore/test/AmMapHandlerTest/CAmMapHandlerTest.cpp +++ b/AudioManagerCore/test/AmMapHandlerTest/CAmMapHandlerTest.cpp @@ -2817,8 +2817,8 @@ TEST_F(CAmMapHandlerTest, connectionIDBoundary) ASSERT_EQ(E_OK, pDatabaseHandler.enterSourceDB(source, forgetSource)); ASSERT_EQ(E_OK, pDatabaseHandler.enterConnectionDB(connection,connectionID)); ASSERT_EQ(12, connectionID); - ASSERT_EQ(E_UNKNOWN, pDatabaseHandler.enterConnectionDB(connection,connectionID)); - ASSERT_EQ(0, connectionID); + ASSERT_EQ(E_ALREADY_EXISTS, pDatabaseHandler.enterConnectionDB(connection,connectionID)); + ASSERT_EQ(12, connectionID); } TEST_F(CAmMapHandlerTest, mainConnectionIDBoundary) @@ -2892,10 +2892,12 @@ TEST_F(CAmMapHandlerTest, mainConnectionIDBoundary) #else EXPECT_CALL(*MockDatabaseObserver::getMockObserverObject(), timingInformationChanged(_, _)).Times(1); #endif - EXPECT_CALL(*MockDatabaseObserver::getMockObserverObject(), removedMainConnection(_)).Times(2); - EXPECT_CALL(*MockDatabaseObserver::getMockObserverObject(), mainConnectionStateChanged(_, _)).Times(3); + EXPECT_CALL(*MockDatabaseObserver::getMockObserverObject(), removedMainConnection(_)).Times(3); + EXPECT_CALL(*MockDatabaseObserver::getMockObserverObject(), mainConnectionStateChanged(_, _)).Times(4); ASSERT_EQ(E_OK, pDatabaseHandler.removeMainConnectionDB(10)); ASSERT_EQ(E_OK, pDatabaseHandler.removeMainConnectionDB(12)); + // drop also last dynamic connection before entering again + ASSERT_EQ(E_OK, pDatabaseHandler.removeMainConnectionDB(mainConnectionID)); ASSERT_EQ(E_OK, pDatabaseHandler.enterMainConnectionDB(mainConnection,mainConnectionID)); ASSERT_EQ(10, mainConnectionID); mainConnection.sinkID = 77; @@ -2917,8 +2919,8 @@ TEST_F(CAmMapHandlerTest, mainConnectionIDBoundary) ASSERT_EQ(E_OK, pDatabaseHandler.enterSourceDB(source, forgetSource)); ASSERT_EQ(E_OK, pDatabaseHandler.enterMainConnectionDB(mainConnection,mainConnectionID)); ASSERT_EQ(12, mainConnectionID); - ASSERT_EQ(E_UNKNOWN, pDatabaseHandler.enterMainConnectionDB(mainConnection,mainConnectionID)); - ASSERT_EQ(0, mainConnectionID); + ASSERT_EQ(E_ALREADY_EXISTS, pDatabaseHandler.enterMainConnectionDB(mainConnection,mainConnectionID)); + ASSERT_EQ(12, mainConnectionID); } TEST_F(CAmMapHandlerTest, increaseID) diff --git a/AudioManagerCore/test/AmRoutingInterfaceTest/CAmRoutingInterfaceTest.cpp b/AudioManagerCore/test/AmRoutingInterfaceTest/CAmRoutingInterfaceTest.cpp index e5e9636..e2df6e7 100644 --- a/AudioManagerCore/test/AmRoutingInterfaceTest/CAmRoutingInterfaceTest.cpp +++ b/AudioManagerCore/test/AmRoutingInterfaceTest/CAmRoutingInterfaceTest.cpp @@ -643,6 +643,124 @@ TEST_F(CAmRoutingInterfaceTest,handleOverflowAbsolute) ASSERT_EQ(handleOverflowCheck1.handle,0); } +/** + * Validate handling of early connection announcement and transfer + */ +TEST_F(CAmRoutingInterfaceTest, registerEarlyConnection) +{ + // prepare additional domain + am_Domain_s domain; + pCF.createDomain(domain); + domain.name = "mock"; + domain.busname = "mock"; + + am_SourceClass_s sourceclass; + sourceclass.name="sClass"; + sourceclass.sourceClassID=5; + ASSERT_EQ(E_OK, pDatabaseHandler.enterSourceClassDB(sourceclass.sourceClassID,sourceclass)); + am_Source_s source; + pCF.createSource(source); + source.sourceID=1; + + ASSERT_EQ(E_OK, pDatabaseHandler.enterDomainDB(domain, domain.domainID)); + ASSERT_EQ(E_OK,pRoutingSender.addDomainLookup(domain)); + ASSERT_EQ(E_OK, pDatabaseHandler.enterSourceDB(source, source.sourceID)); + + am_sinkID_t sinkID; + ASSERT_EQ(E_OK, pRoutingReceiver.peekSink("AnySink", sinkID)); + + am_Route_s route; + route.sourceID = source.sourceID; + route.sinkID = sinkID; + route.route.push_back({source.sourceID, sinkID, domain.domainID, CF_GENIVI_STEREO}); + EXPECT_CALL(pMockControlInterface, hookSystemRegisterEarlyMainConnection(_, _, _)) + .WillOnce(Return(E_OK)); + ASSERT_EQ(E_OK, pRoutingReceiver.registerEarlyConnection(domain.domainID, route, CS_CONNECTED)); + + std::vector<am::am_MainConnection_s> listMainConnections; + ASSERT_EQ(E_OK, pControlReceiver.getListMainConnections(listMainConnections)); + + ASSERT_EQ(1, listMainConnections.size()); + EXPECT_EQ(CS_CONNECTED, listMainConnections[0].connectionState); +} + +TEST_F(CAmRoutingInterfaceTest, transferConnection) +{ + // prepare additional domain + am_Domain_s domain; + pCF.createDomain(domain); + domain.name = "mock"; + domain.busname = "mock"; + + am_SinkClass_s sinkclass; + sinkclass.sinkClassID=5; + sinkclass.name="sname"; + ASSERT_EQ(E_OK, pDatabaseHandler.enterSinkClassDB(sinkclass,sinkclass.sinkClassID)); + + am_Sink_s sink; + pCF.createSink(sink); + sink.sinkID = 2; + sink.domainID = DYNAMIC_ID_BOUNDARY; + + am_SourceClass_s sourceclass; + sourceclass.name="sClass"; + sourceclass.sourceClassID=5; + ASSERT_EQ(E_OK, pDatabaseHandler.enterSourceClassDB(sourceclass.sourceClassID,sourceclass)); + am_Source_s source; + pCF.createSource(source); + source.sourceID=1; + + ASSERT_EQ(E_OK, pDatabaseHandler.enterDomainDB(domain, domain.domainID)); + ASSERT_EQ(E_OK,pRoutingSender.addDomainLookup(domain)); + ASSERT_EQ(E_OK, pDatabaseHandler.enterSinkDB(sink, sink.sinkID)); + ASSERT_EQ(E_OK, pDatabaseHandler.enterSourceDB(source,source.sourceID)); + + am_Connection_s con; + con.connectionID = 0; + con.sourceID = source.sourceID; + con.sinkID = sink.sinkID; + con.delay = 0; + con.connectionFormat = CF_GENIVI_STEREO; + ASSERT_EQ(E_OK, pDatabaseHandler.enterConnectionDB(con, con.connectionID)); + + am_MainConnection_s mainConnection; + mainConnection.mainConnectionID = 0; + mainConnection.connectionState = CS_CONNECTED; + mainConnection.sinkID = sink.sinkID; + mainConnection.sourceID = source.sourceID; + mainConnection.delay = 0; + mainConnection.listConnectionID.push_back(con.connectionID); + ASSERT_EQ(E_OK, pDatabaseHandler.enterMainConnectionDB(mainConnection, mainConnection.mainConnectionID)); + + // execute + EXPECT_CALL(pMockInterface, asyncTransferConnection(_, _, _, CS_CONNECTED)) + .WillOnce(Invoke(&pMockInterface, &MockIAmRoutingSend::defaultAsyncTransferConnection)); + am_Handle_s handle; + EXPECT_EQ(E_NOT_POSSIBLE, pControlReceiver.transferConnection(handle, mainConnection.mainConnectionID, domain.domainID)); + + // validate that all handles have vanished + std::vector<am_Handle_s> listHandles; + ASSERT_EQ(E_OK, pControlReceiver.getListHandles(listHandles)); + EXPECT_TRUE(listHandles.empty()); + + // prepare routing side to accept the transfer and try again + EXPECT_CALL(pMockInterface, asyncTransferConnection(_, _, _, CS_CONNECTED)) + .WillOnce(Return(E_OK)); + EXPECT_EQ(E_OK, pControlReceiver.transferConnection(handle, mainConnection.mainConnectionID, domain.domainID)); + + // validate that a proper handle is memorized + EXPECT_EQ(E_OK, pControlReceiver.getListHandles(listHandles)); + EXPECT_EQ(1, listHandles.size()); + EXPECT_EQ(H_TRANSFERCONNECTION, listHandles[0].handleType); + + // inject acknowledgment + EXPECT_CALL(pMockControlInterface, cbAckTransferConnection(_, E_OK)); + pRoutingReceiver.ackTransferConnection(listHandles[0], E_OK); + + // validate that all handles have vanished + ASSERT_EQ(E_OK, pControlReceiver.getListHandles(listHandles)); + ASSERT_TRUE(listHandles.empty()); +} int main(int argc, char **argv) diff --git a/AudioManagerCore/test/MockIAmControlSend.h b/AudioManagerCore/test/MockIAmControlSend.h index 9115d00..74ccc4f 100644 --- a/AudioManagerCore/test/MockIAmControlSend.h +++ b/AudioManagerCore/test/MockIAmControlSend.h @@ -105,10 +105,14 @@ class MockIAmControlSend : public IAmControlSend { void(const am_speed_t speed)); MOCK_METHOD2(hookSystemTimingInformationChanged, void(const am_mainConnectionID_t mainConnectionID, const am_timeSync_t time)); + MOCK_METHOD3(hookSystemRegisterEarlyMainConnection, + am_Error_e(am_domainID_t domainID, const am_MainConnection_s &mainConnectionData, const am_Route_s &route)); MOCK_METHOD2(cbAckConnect, void(const am_Handle_s handle, const am_Error_e errorID)); MOCK_METHOD2(cbAckDisconnect, void(const am_Handle_s handle, const am_Error_e errorID)); + MOCK_METHOD2(cbAckTransferConnection, + void(const am_Handle_s handle, const am_Error_e errorID)); MOCK_METHOD3(cbAckCrossFade, void(const am_Handle_s handle, const am_HotSink_e hostsink, const am_Error_e error)); MOCK_METHOD3(cbAckSetSinkVolumeChange, diff --git a/AudioManagerCore/test/MockIAmRoutingSend.h b/AudioManagerCore/test/MockIAmRoutingSend.h index 748e64e..21ea8f1 100644 --- a/AudioManagerCore/test/MockIAmRoutingSend.h +++ b/AudioManagerCore/test/MockIAmRoutingSend.h @@ -45,6 +45,12 @@ class MockIAmRoutingSend : public IAmRoutingSend { am_Error_e(const am_Handle_s handle, const am_connectionID_t connectionID, const am_sourceID_t sourceID, const am_sinkID_t sinkID, const am_CustomAvailabilityReason_t connectionFormat)); MOCK_METHOD2(asyncDisconnect, am_Error_e(const am_Handle_s handle, const am_connectionID_t connectionID)); + MOCK_METHOD4(asyncTransferConnection, + am_Error_e(const am_Handle_s handle, am_domainID_t domainID, const std::vector<std::pair<std::string, std::string>> &route, am_ConnectionState_e state)); + am_Error_e defaultAsyncTransferConnection(const am_Handle_s handle, am_domainID_t domainID + , const std::vector<std::pair<std::string, std::string>> &route + , am_ConnectionState_e state) // allow usage of default implementation from IAmRoutingSend + { return IAmRoutingSend::asyncTransferConnection(handle, domainID, route, state); } MOCK_METHOD5(asyncSetSinkVolume, am_Error_e(const am_Handle_s handle, const am_sinkID_t sinkID, const am_volume_t volume, const am_CustomRampType_t ramp, const am_time_t time)); MOCK_METHOD5(asyncSetSourceVolume, |