From d51fb6ea44cbda677d8120a7011c68e9e8726f6c Mon Sep 17 00:00:00 2001 From: Martin Koch Date: Fri, 19 Jun 2020 16:01:19 +0200 Subject: Add support for announcement and handling of pre-established audio connections through routing side As the complete initialization of the cockpit system takes some time, a few use-cases exist where information needs to be audible (or visible) right before the system is fully started. Here we mainly have: - system alerts + door open + seat belt missing + engine faults - parking assistant warnings + rear view camera screen + beeps if distance goes below limit Both routing- and control-side are extended to allow notifying about such connections Signed-off-by: Martin Koch --- AudioManagerCore/include/CAmControlReceiver.h | 2 + AudioManagerCore/include/CAmControlSender.h | 2 + AudioManagerCore/include/CAmDatabaseHandlerMap.h | 1 + AudioManagerCore/include/CAmRoutingReceiver.h | 3 ++ AudioManagerCore/include/CAmRoutingSender.h | 2 + AudioManagerCore/include/IAmDatabaseHandler.h | 1 + AudioManagerCore/src/CAmControlReceiver.cpp | 34 ++++++++++++++ AudioManagerCore/src/CAmControlSender.cpp | 7 +++ AudioManagerCore/src/CAmDatabaseHandlerMap.cpp | 60 +++++++++++++++++------- AudioManagerCore/src/CAmRoutingReceiver.cpp | 47 +++++++++++++++++++ AudioManagerCore/src/CAmRoutingSender.cpp | 13 +++++ include/IAmControl.h | 45 ++++++++++++++++++ include/IAmRouting.h | 47 +++++++++++++++++++ 13 files changed, 247 insertions(+), 17 deletions(-) diff --git a/AudioManagerCore/include/CAmControlReceiver.h b/AudioManagerCore/include/CAmControlReceiver.h index bd897de..a7cb394 100644 --- a/AudioManagerCore/include/CAmControlReceiver.h +++ b/AudioManagerCore/include/CAmControlReceiver.h @@ -127,6 +127,8 @@ public: void setRoutingRundown(); void confirmControllerReady(const am_Error_e error); void confirmControllerRundown(const am_Error_e error); + am_Error_e transferConnection(const 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 &listSoundProperties, const std::vector &listConnectionFormats, const std::vector &listMainSoundProperties); diff --git a/AudioManagerCore/include/CAmControlSender.h b/AudioManagerCore/include/CAmControlSender.h index 77e8e2c..494f7f3 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); 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); diff --git a/AudioManagerCore/include/CAmDatabaseHandlerMap.h b/AudioManagerCore/include/CAmDatabaseHandlerMap.h index 46da560..a1ec6a3 100644 --- a/AudioManagerCore/include/CAmDatabaseHandlerMap.h +++ b/AudioManagerCore/include/CAmDatabaseHandlerMap.h @@ -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..3f85ba7 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 std::vector< am_Connection_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..7756ccc 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(const am_Handle_s handle, am_domainID_t domainID + , const std::vector> &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); diff --git a/AudioManagerCore/include/IAmDatabaseHandler.h b/AudioManagerCore/include/IAmDatabaseHandler.h index 8e4118e..495f4e6 100644 --- a/AudioManagerCore/include/IAmDatabaseHandler.h +++ b/AudioManagerCore/include/IAmDatabaseHandler.h @@ -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..bbd1116 100644 --- a/AudioManagerCore/src/CAmControlReceiver.cpp +++ b/AudioManagerCore/src/CAmControlReceiver.cpp @@ -451,6 +451,40 @@ void CAmControlReceiver::confirmControllerRundown(const am_Error_e error) mSocketHandler->exit_mainloop(); } +am_Error_e CAmControlReceiver::transferConnection(const 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> 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..0d28dfb 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) +{ + assert(mController); + return mController->hookSystemRegisterEarlyMainConnection(domainID, mainConnectionData); +} + void CAmControlSender::hookSystemSinkVolumeTick(const am_Handle_s handle, const am_sinkID_t sinkID, const am_volume_t volume) { assert(mController); diff --git a/AudioManagerCore/src/CAmDatabaseHandlerMap.cpp b/AudioManagerCore/src/CAmDatabaseHandlerMap.cpp index 6e6e3f9..8a2c5e0 100644 --- a/AudioManagerCore/src/CAmDatabaseHandlerMap.cpp +++ b/AudioManagerCore/src/CAmDatabaseHandlerMap.cpp @@ -1169,6 +1169,20 @@ am_Error_e CAmDatabaseHandlerMap::enterConnectionDB(const am_Connection_s &conne 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 +1931,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..8f4d1ba 100644 --- a/AudioManagerCore/src/CAmRoutingReceiver.cpp +++ b/AudioManagerCore/src/CAmRoutingReceiver.cpp @@ -124,6 +124,19 @@ 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 errorID) +{ + +} + 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 +246,40 @@ am_Error_e CAmRoutingReceiver::deregisterDomain(const am_domainID_t domainID) return (mpControlSender->hookSystemDeregisterDomain(domainID)); } +am_Error_e CAmRoutingReceiver::registerEarlyConnection(am_domainID_t domainID + , const std::vector< am_Connection_s > &route, am_ConnectionState_e state) +{ + if (route.size() < 1) + { + logWarning(__METHOD_NAME__, "route empty"); + return E_NOT_POSSIBLE; + } + + am_MainConnection_s mainConnectionData; + mainConnectionData.sourceID = route.front().sourceID; + mainConnectionData.sinkID = route.back().sinkID; + mainConnectionData.connectionState = state; + mainConnectionData.listConnectionID.reserve(route.size()); + for (auto & conn : route) + { + am_connectionID_t connectionID; + am_Error_e success = mpDatabaseHandler->enterConnectionDB(conn, connectionID); + switch (success) + { + case E_OK: + case E_ALREADY_EXISTS: + case E_NO_CHANGE: + mainConnectionData.listConnectionID.push_back(connectionID); + break; + + default: + return success; + } + } + + return mpControlSender->hookSystemRegisterEarlyConnection(domainID, mainConnectionData); +} + 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..399de9a 100644 --- a/AudioManagerCore/src/CAmRoutingSender.cpp +++ b/AudioManagerCore/src/CAmRoutingSender.cpp @@ -921,6 +921,19 @@ void CAmRoutingSender::setRoutingRundown() } } +am_Error_e CAmRoutingSender::asyncTransferConnection(const am_Handle_s handle, am_domainID_t domainID + , const std::vector> &route, am_ConnectionState_e state) +{ + auto iter = mMapDomainInterface.find(domainID); + if (iter != mMapDomainInterface.end() && iter->second) + { + return iter->second->asyncTransferConnection(handle, route, state); + } + + // given domain not found in map + return E_NON_EXISTENT; +} + am_Error_e CAmRoutingSender::asyncSetVolumes(am_Handle_s &handle, const std::vector &listVolumes) { IAmRoutingSend *pRoutingInterface(NULL); diff --git a/include/IAmControl.h b/include/IAmControl.h index 32008a0..e8545ad 100644 --- a/include/IAmControl.h +++ b/include/IAmControl.h @@ -554,6 +554,20 @@ public: * confirmRoutingRundown. */ virtual void setRoutingRundown() =0; + + /** + * Hand-over to routing-side application any connection meant to survive AM shutdown + * + * @param handle: composite identifier used to map the response + * @param domainID: target domain which shall take over + * @param mainConnectionID: subject of this request + * + * @return E_OK if command was forwarded to routing adapter successfully, + * E_COMMUNICATION or other meaningful value otherwise + */ + virtual am_Error_e transferConnection(const am_Handle_s handle + , am_mainConnectionID_t mainConnectionID, am_domainID_t domainID) = 0; + /** * acknowledges the setControllerReady call. */ @@ -858,6 +872,24 @@ public: * @return E_OK on success, E_UNKNOWN on error, E_NON_EXISTENT if not found */ virtual am_Error_e hookSystemDeregisterCrossfader(const am_crossfaderID_t crossfaderID) =0; + + /** + * Support announcement of audio connections already active at AM startup + * + * @param domainID: home domain announcing this early connection + * @param mainConnectionData: details of main connection + * + * @return success indicator. Controller should use E_OK on success, + * E_ALREADY_EXISTS or E_NO_CHANGE if given connection is already registered, + * E_DATABASE_ERROR if any of the listed sources or sinks does not exist in the data base, + * E_NOT_POSSIBLE if feature is not supported by the controller + */ + virtual am_Error_e hookSystemRegisterEarlyMainConnection(am_domainID_t domainID + , const am_MainConnection_s &mainConnectionData) + { + return E_NOT_POSSIBLE; // empty default implementation + } + /** * volumeticks. therse are used to indicate volumechanges during a ramp */ @@ -905,6 +937,19 @@ public: * ack for disconnect */ virtual void cbAckDisconnect(const am_Handle_s handle, const am_Error_e errorID) =0; + + /** + * Hand-over acknowledgment of connections surviving shutdown of the AM, + * forwarded from routing side (see @ref IAmRoutingReceive::ackTransferConnection) + * + * @param handle: composite identifier mirrored from request + * @param errorID: success indicator as obtained from routing side application + */ + virtual void cbAckTransferConnection(const am_Handle_s /* handle */, const am_Error_e /* errorID */) + { + // empty default implementation + } + /** * ack for crossfading */ diff --git a/include/IAmRouting.h b/include/IAmRouting.h index 1acaac6..28e8ac9 100644 --- a/include/IAmRouting.h +++ b/include/IAmRouting.h @@ -139,6 +139,34 @@ public: * @return E_OK on succes, E_NON_EXISTENT if not found E_UNKOWN on error */ virtual am_Error_e deregisterDomain(const am_domainID_t domainID) =0; + + /** + * Support announcement of audio connections already active at AM startup + * + * @param domainID: home domain announcing this early connection + * @param route: list of sub-connections (populated with unspecified connectionID=0 field) + * @param state: either stable CS_CONNECTED, CS_DISCONNECTED, CS_SUSPENDED + * or transient CS_CONNECTING, CS_DISCONNECTING + * + * @return success indicator as obtained from the controller + * + * @note If the connection is announced with one of the transient states + * CS_CONNECTING or CS_DISCONNECTING, a secondary registerEarlyConnection() + * call is expected once a stable state is reached + */ + virtual am_Error_e registerEarlyConnection(am_domainID_t domainID, const std::vector< am_Connection_s > &route + , am_ConnectionState_e state) = 0; + + /** + * Notify hand-over acknowledgment of connections surviving shutdown of the AM + * + * @param handle: composite identifier used in the request + * @param errorID: success indicator as obtained from the routing-side application, + * e.g. E_OK if the application assumes full responsibility, + * any meaningful error condition otherwise + */ + virtual void ackTransferConnection(const am_Handle_s handle, const am_Error_e errorID) = 0; + /** * registers a converter. @return E_OK on succes, E_ALREADY_EXISTENT if already * registered E_UNKOWN on error @@ -385,6 +413,25 @@ public: * or be ready again. */ virtual void setRoutingRundown(const uint16_t handle) =0; + + /** + * Forward hand-over of a connection meant to survive AM shutdown + * in routing-side application + * + * @param handle: composite identifier used to map the response + * @param route: names of involved sources and sinks including intermediate gateways + * @param state: either stable CS_CONNECTED, CS_DISCONNECTED, CS_SUSPENDED + * or transient CS_CONNECTING, CS_DISCONNECTING + * + * @return success indicator as obtained from the plugins, e.g E_OK or E_COMMUNICATION + */ + virtual am_Error_e asyncTransferConnection(const am_Handle_s handle + , const std::vector> &route + , am_ConnectionState_e state) + { + return E_NOT_POSSIBLE; // default response if not supported by the plugin + } + /** * aborts an asynchronous action. * @return E_OK on success, E_UNKNOWN on error, E_NON_EXISTENT if handle was not -- cgit v1.2.1