aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorin Sarbu <florin.sarbu@windriver.com>2014-09-30 11:07:48 +0300
committerHolger Behrens <holger.behrens@windriver.com>2014-10-17 17:29:11 +0200
commit5f9ae083a9d8248c8e7d4d2a922d98ba054c4a3b (patch)
tree0e9549928ea5a82cdf8760ed38ad934e7d9c6bb0
parentf236270082c65e379c7e8538638519c475c5d105 (diff)
downloadmeta-ivi-5f9ae083a9d8248c8e7d4d2a922d98ba054c4a3b.tar.gz
meta-ivi-5f9ae083a9d8248c8e7d4d2a922d98ba054c4a3b.tar.bz2
meta-ivi-5f9ae083a9d8248c8e7d4d2a922d98ba054c4a3b.zip
audiomanager: Add control and routing plugins
Signed-off-by: Florin Sarbu <florin.sarbu@windriver.com> Signed-off-by: Holger Behrens <holger.behrens@windriver.com>
-rw-r--r--meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager/0001-Porting-Pulse-Control-Interface-from-AM-v1.x-to-AM-v.patch2555
-rw-r--r--meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager/0001-Porting-Pulse-Routing-Interface-from-AM-v1.x-to-AM-v.patch2342
-rw-r--r--meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager_6.1.bbappend10
3 files changed, 4907 insertions, 0 deletions
diff --git a/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager/0001-Porting-Pulse-Control-Interface-from-AM-v1.x-to-AM-v.patch b/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager/0001-Porting-Pulse-Control-Interface-from-AM-v1.x-to-AM-v.patch
new file mode 100644
index 0000000..bcf043d
--- /dev/null
+++ b/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager/0001-Porting-Pulse-Control-Interface-from-AM-v1.x-to-AM-v.patch
@@ -0,0 +1,2555 @@
+From 3e41213883357530c83c96f2eef2d0614056d497 Mon Sep 17 00:00:00 2001
+From: Adrian Scarlat <adrian.scarlat@windriver.com>
+Date: Mon, 8 Sep 2014 16:51:55 +0300
+Subject: [PATCH] Porting Pulse Control Interface from AM v1.x to AM v3.0
+
+This Control Interface is the "heart" of the AM; It is needed
+by any application that will be developed on top of AM and
+will use PulseAudio Sound Server to control the sources and
+sinks present on the system. It must be loaded by AM;
+
+The interface can be built by supplying cmake with the -DWITH_PULSE_CONTROL_PLUGIN=ON;
+There is one configuration file that is used at the moment by the Control Interface:
+ 1. libPluginControlInterface.conf - configuration file for defining the Sources,
+ Source Classes, Sinks, Sink Classes, Gateways, Routes and Mixing rules form them;
+
+Modified files:
+ CMakeLists.txt
+
+Added new folders:
+ PluginControlInterfacePulse/
+
+Added new files:
+ PluginControlInterfacePulse/CMakeLists.txt
+ PluginControlInterfacePulse/data/libPluginControlInterface.conf
+ PluginControlInterfacePulse/include/ControlConfig.h
+ PluginControlInterfacePulse/include/ControlSender.h
+ PluginControlInterfacePulse/src/ControlSender.cpp
+
+Signed-off-by: Adrian Scarlat <adrian.scarlat@windriver.com>
+---
+ CMakeLists.txt | 7 +
+ PluginControlInterfacePulse/CMakeLists.txt | 93 ++
+ .../data/libPluginControlInterface.conf | 93 ++
+ .../include/ControlConfig.h | 221 +++
+ .../include/ControlSender.h | 283 ++++
+ PluginControlInterfacePulse/src/ControlSender.cpp | 1763 ++++++++++++++++++++
+ 6 files changed, 2460 insertions(+)
+ create mode 100644 PluginControlInterfacePulse/CMakeLists.txt
+ create mode 100755 PluginControlInterfacePulse/data/libPluginControlInterface.conf
+ create mode 100644 PluginControlInterfacePulse/include/ControlConfig.h
+ create mode 100644 PluginControlInterfacePulse/include/ControlSender.h
+ create mode 100644 PluginControlInterfacePulse/src/ControlSender.cpp
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 48e7ae2..25c8e95 100755
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -98,6 +98,9 @@ OPTION ( WITH_NSM
+ OPTION( WITH_PULSE_ROUTING_PLUGIN
+ "Enable PULSE Audio routing plugin interface" OFF )
+
++OPTION( WITH_PULSE_CONTROL_PLUGIN
++ "Enable PULSE Audio control plugin interface" OFF)
++
+ SET (WITH_COMMON_API_GEN ON CACHE INTERNAL "hide this!" FORCE)
+
+ IF (WITH_PULSE_ROUTING_PLUGIN)
+@@ -260,7 +263,11 @@ if(WITH_PLUGIN_ROUTING)
+ endif(WITH_PLUGIN_ROUTING)
+
+ if(WITH_PLUGIN_CONTROL)
++ if(WITH_PULSE_CONTROL_PLUGIN)
++ add_subdirectory (PluginControlInterfacePulse)
++ else ()
+ add_subdirectory (PluginControlInterface)
++ endif(WITH_PULSE_CONTROL_PLUGIN)
+ endif(WITH_PLUGIN_CONTROL)
+
+ add_subdirectory (AudioManagerDaemon)
+diff --git a/PluginControlInterfacePulse/CMakeLists.txt b/PluginControlInterfacePulse/CMakeLists.txt
+new file mode 100644
+index 0000000..567fc35
+--- /dev/null
++++ b/PluginControlInterfacePulse/CMakeLists.txt
+@@ -0,0 +1,93 @@
++############################################################################
++# SPDX license identifier: MPL-2.0
++#
++# Copyright (C) 2012-2014, Wind River Systems
++# Copyright (C) 2014, GENIVI Alliance
++#
++# This file is part of Pulse Audio Interface Control Plugin.
++#
++# This Source Code Form is subject to the terms of the Mozilla Public
++# License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
++# file, You can obtain one at http://mozilla.org/MPL/2.0/.
++#
++# For further information see http://www.genivi.org/.
++#
++# List of changes:
++#
++# 08.09.2014, Adrian Scarlat, First version of the code;
++# Porting code from AM ver1.x;
++############################################################################
++
++cmake_minimum_required(VERSION 2.6)
++
++PROJECT(PluginControlInterfacePulse)
++
++set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic")
++set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
++
++FIND_PACKAGE(PkgConfig)
++
++OPTION( WITH_DOCUMENTATION
++ "Build together with Doxygen Documentation" OFF )
++
++SET(PLUGINS_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin/plugins)
++set(LIBRARY_OUTPUT_PATH ${PLUGINS_OUTPUT_PATH}/control)
++set(DOC_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/doc/ControlPlugin)
++set(INCLUDE_FOLDER "include")
++SET(LIB_INSTALL_SUFFIX "audioManager")
++
++FIND_PATH(AUDIO_INCLUDE_FOLDER audiomanagertypes.h /usr/include)
++
++if(DEFINED AUDIO_INCLUDE_FOLDER)
++ message(STATUS "Found AudioManager include: ${AUDIO_INCLUDE_FOLDER}")
++else(DEFINED AUDIO_INCLUDE_FOLDER)
++ message(STATUS "Did not found AudioManager include!")
++endif(DEFINED AUDIO_INCLUDE_FOLDER)
++
++FILE(READ "${AUDIO_INCLUDE_FOLDER}/control/IAmControlSend.h" VERSION_BUFFER LIMIT 6000)
++STRING(REGEX MATCH "ControlSendVersion*.[^0-9]*[0-9]" LIB_INTERFACE_VERSION_STRING ${VERSION_BUFFER})
++STRING(REGEX REPLACE "[^0-9]" "" LIB_INTERFACE_VERSION ${LIB_INTERFACE_VERSION_STRING})
++MESSAGE(STATUS "Building against control interface version ${LIB_INTERFACE_VERSION}")
++
++FIND_PACKAGE(NSM REQUIRED)
++
++INCLUDE_DIRECTORIES(
++ ${CMAKE_SOURCE_DIR}
++ ${STD_INCLUDE_DIRS}
++ ${CMAKE_CURRENT_BINARY_DIR}
++ ${AUDIO_INCLUDE_FOLDER}
++ ${PROJECT_INCLUDE_FOLDER}
++ ${INCLUDE_FOLDER}
++ ${NSM_INCLUDE_DIR}
++)
++
++# all source files go here
++file(GLOB PLUGIN_CONTROL_SRCS_CXX "src/*.cpp")
++
++add_library(PluginControlInterface SHARED ${PLUGIN_CONTROL_SRCS_CXX})
++
++TARGET_LINK_LIBRARIES(PluginControlInterface
++ ${DLT_LIBRARIES})
++
++IF(WITH_DOCUMENTATION)
++ file(MAKE_DIRECTORY ${DOC_OUTPUT_PATH})
++ configure_file(${DOXY_FILE} ${DOC_OUTPUT_PATH}/Doxyfile @ONLY IMMEDIATE)
++ add_custom_target (PluginControlInterfaceDBusDocs ALL
++ COMMAND ${DOXYGEN_EXECUTABLE} ${DOC_OUTPUT_PATH}/Doxyfile WORKING_DIRECTORY ${DOC_OUTPUT_PATH}
++ SOURCES ${PROJECT_BINARY_DIR} ${DOC_OUTPUT_PATH}/Doxyfile
++ )
++ENDIF(WITH_DOCUMENTATION)
++
++INSTALL(TARGETS PluginControlInterface
++ DESTINATION "lib/${LIB_INSTALL_SUFFIX}/control/"
++ PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ
++ COMPONENT sampleplugins
++)
++
++INSTALL(FILES data/libPluginControlInterface.conf
++ DESTINATION "lib/${LIB_INSTALL_SUFFIX}/control/"
++ PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ
++ COMPONENT sampleplugins
++)
++
++CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/PluginControlInterfacePulse/data/libPluginControlInterface.conf ${PLUGINS_OUTPUT_PATH}/control/libPluginControlInterface.conf)
+diff --git a/PluginControlInterfacePulse/data/libPluginControlInterface.conf b/PluginControlInterfacePulse/data/libPluginControlInterface.conf
+new file mode 100755
+index 0000000..2976c97
+--- /dev/null
++++ b/PluginControlInterfacePulse/data/libPluginControlInterface.conf
+@@ -0,0 +1,93 @@
++############################################################################
++# SPDX license identifier: MPL-2.0
++#
++# Copyright (C) 2012-2014, Wind River Systems
++# Copyright (C) 2014, GENIVI Alliance
++#
++# This file is part of Pulse Audio Interface Routing Plugin.
++#
++# This Source Code Form is subject to the terms of the Mozilla Public
++# License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
++# file, You can obtain one at http://mozilla.org/MPL/2.0/.
++#
++# For further information see http://www.genivi.org/.
++#
++# List of changes:
++#
++# 08.09.2014, Adrian Scarlat, First version of the code;
++# Porting code from AM ver1.x to AM ver3.0;
++# Added Copyright and License information;
++#
++# 19.09.2014, Adrian Scarlat, Added information regarding configuration options;
++#
++############################################################################
++
++# SourceClass has the following format: SourceClassName|SourceClassType
++[SourceClass]
++Entertainment|Main
++Browser|Main
++Navigation|Interrupt
++TTS|Interrupt
++Analogic|Main
++Digital|Main
++############################################################################
++# SinkClass has the following format: SinkClassName|SinkClassType
++[SinkClass]
++HifiAudio|Main
++############################################################################
++# Sinks and sources are clustered into independent parts which are capable of
++# exchanging audio with each other (AudioDomains).
++# Examples for possible AudioDomains:
++# - PulseAudio, Alsa, Jack, DSP, FPGA, MOST, In-chip switching matrix;
++# Between these AudioDomains, Audio can be interchanged via Gateways;
++# The format for a Gateway is: Sink from DomainX -> Source from DomainY;
++# Gateways always have a direction and can only transport one stream at a time.
++# Several gateways connecting the same domains together can exist in parallel
++# so that more than one source can be connected to more than one sink from the
++# same domains at the same time.
++#[Gateway]
++#Pulse2Dirana Primary:Alsa Primary>Primary Channel
++#Pulse2Dirana Secondary:Alsa Secondary>Secondary Channel
++############################################################################
++# Routes ar defined from a SourceClass to a Sink. A route is a ">" separated list
++# of Sources and Sinks. Routes have the following format:
++# SOURCE_CATEGORY:[SOURCE_NAME>SINK_NAME]
++# Where:
++# - SOURCE_CATEGORY = "Source" or "SourceClass";
++# If SOURCE_CATEGORY=SourceClass, the first SOURCE_NAME must be the name of a source class
++# Otherwise SOURCE_NAME and SINK_NAME should be the name of a source and a sink.
++# A Gateway might be used to replace a Sink-Source pair:
++# Route1: SourceClass1 > Sink1 > Source2 > Sink2
++# Is equivalent to
++# Route2: SourceClass1 > Gateway1 > Sink 2
++# If and only if Gateway1 = Sink1 > Source2
++[Route]
++Entertainment>Entertainment>HifiAudio
++Browser>Browser>HifiAudio
++Navigation>Navigation>HifiAudio
++Analogic>Analogic>HifiAudio
++Digital>Digital>HifiAudio
++#Entertainment>Pulse2Dirana Primary>HifiAudio
++#Navigation>Pulse2Dirana Secondary>HifiAudio
++#Analogic>HifiAudio
++############################################################################
++# Mixing rule should be something like:
++# - if a new source is in the class XXX
++# - if there is a connection like [SourceClass->Sink]
++# - apply an action in: Domain/
++# Mixing line format: [SourceClass]>[ACTION]|[ACTION]...
++# Where ACTION = SourceClass-Sink:ACTION_TYPE,ACTION_TARGET:ActionTarget
++# Where ACTION_TYPE = Mute, Unmute, Pause, Resume, DecreaseVolume, IncreaseVolume, Disconnect
++# ActionTarget = Domain/Sink or Domain/Source
++[Mixing]
++#Entertainment:Entertainment>Hifi Audio:Disconnect:PulseAudio:Source|Entertainment>Hifi Audio:Disconnect:RoutingSenderDIRANA:Source|Browser>Hifi Audio:Disconnect:PulseAudio:Source|Browser>Hifi Audio:Disconnect:RoutingSenderDIRANA:Source|Analogic>Hifi Audio:Disconnect:RoutingSenderDIRANA:Source
++Entertainment:Entertainment>HifiAudio:Disconnect:PulseAudio:Source|Entertainment>HifiAudio:Disconnect:RoutingSenderPulse:Source|Browser>HifiAudio:Disconnect:PulseAudio:Source|Browser>HifiAudio:Disconnect:RoutingSenderPulse:Source|Analogic>HifiAudio:Disconnect:RoutingSenderPulse:Source
++#Analogic:Entertainment>Hifi Audio:Disconnect:PulseAudio:Source|Entertainment>Hifi Audio:Disconnect:RoutingSenderDIRANA:Source|Browser>Hifi Audio:Disconnect:PulseAudio:Source|Browser>Hifi Audio:Disconnect:RoutingSenderDIRANA:Source|Analogic>Hifi Audio:Disconnect:RoutingSenderDIRANA:Source
++Analogic:Entertainment>HifiAudio:Disconnect:PulseAudio:Source|Entertainment>HifiAudio:Disconnect:RoutingSenderPulse:Source|Browser>HifiAudio:Disconnect:PulseAudio:Source|Browser>HifiAudio:Disconnect:RoutingSenderPulse:Source|Analogic>HifiAudio:Disconnect:RoutingSenderPulse:Source
++#Navigation:Entertainment>Hifi Audio:DecreaseVolume:RoutingSenderDIRANA:Source|Analogic>Hifi Audio:DecreaseVolume:RoutingSenderDIRANA:Source|Browser>Hifi Audio:Mute:PulseAudio:Source|
++#Browser:Entertainment>Hifi Audio:Disconnect:PulseAudio:Source|Entertainment>Hifi Audio:Disconnect:RoutingSenderDIRANA:Source|Analogic>Hifi Audio:Disconnect:RoutingSenderDIRANA:Source
++#|TTS>HifiAudio Sink:Disconnect:Pulse:Source
++#TTS:Entertainment>HifiAudio Sink:DecreaseVolume:Dirana:Source
++#
++#TODO: define default mixing rule ?
++#
+diff --git a/PluginControlInterfacePulse/include/ControlConfig.h b/PluginControlInterfacePulse/include/ControlConfig.h
+new file mode 100644
+index 0000000..7dbf73e
+--- /dev/null
++++ b/PluginControlInterfacePulse/include/ControlConfig.h
+@@ -0,0 +1,221 @@
++/**
++ * SPDX license identifier: MPL-2.0
++ *
++ * Copyright (C) 2011-2014, Wind River Systems
++ * Copyright (C) 2014, GENIVI Alliance
++ *
++ * This file is part of Pulse Audio Interface Control Plugin.
++ *
++ * This Source Code Form is subject to the terms of the Mozilla Public
++ * License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
++ *
++ * For further information see http://www.genivi.org/.
++ *
++ * List of changes:
++ *
++ * 08.09.2014, Adrian Scarlat, First version of the code;
++ * Porting from AM ver 1.x;
++ */
++
++#ifndef CONTROLCONFIG_H_
++#define CONTROLCONFIG_H_
++
++#include "audiomanagertypes.h"
++#include <string>
++
++
++/* Defines */
++
++#define SOURCE_TYPE_INTERRUPT (1)
++#define SOURCE_TYPE_MAIN (2)
++#define SINK_TYPE_MAIN (2)
++
++#define ACTION_TYPE_STR(x) (\
++(x == AT_MUTE) ? "AT_MUTE" : \
++(x == AT_UNMUTE) ? "AT_UNMUTE" : \
++(x == AT_PAUSE) ? "AT_PAUSE" : \
++(x == AT_RESUME) ? "AT_RESUME" : \
++(x == AT_VOLUME_DECREASE) ? "AT_VOLUME_DECREASE" : \
++(x == AT_VOLUME_INCREASE) ? "AT_VOLUME_INCREASE" : \
++(x == AT_DISCONNECT) ? "AT_DISCONNECT" : \
++(x == AT_CONNECT) ? "AT_CONNECT" : \
++#x":Unknown")
++
++
++/**
++ * Action types sorted by priority: lowest - the most important
++ */
++enum am_ActionType_e
++{
++ AT_DISCONNECT=0,
++ AT_PAUSE=1,
++ AT_MUTE=2,
++ AT_UNMUTE=3,
++ AT_VOLUME_DECREASE=4,
++ AT_CONNECT=5,
++ AT_RESUME=6,
++ AT_VOLUME_INCREASE=7,
++ AT_MAX
++};
++
++
++enum am_ActionTarget_e
++{
++ ATG_SINK=0,
++ ATG_SOURCE=1,
++ ATG_GW_SINK=2,
++ ATG_GW_SOURCE=3,
++ ATG_MAX
++};
++
++
++/*
++ * e.g. Entertainment : Entertainment>HifiAudio Sink:Disconnect:Pulse:Source
++ */
++struct am_ConnectAction_s {
++ bool interrupting; /* The action is triggered by an interrupting source class: e.g. TTS, Phone */
++ bool pending; /* The action was not applied yet. */
++ /* e.g. there is an action with higher priority which is already applied to this connection */
++ am::am_sourceClass_t activeSourceClassID; /* The name of the source class of existing connections - the one to be controlled */
++ am::am_sinkID_t activeSinkID; /* The name of the sink of existing connections - the one to be controlled */
++ am_ActionType_e actionType; /* What kind of action the controller decided: mute, pause, volume adjustment */
++ am::am_domainID_t targetDomain; /* The domain of target element */
++ am_ActionTarget_e targetElement; /* What element is controlled: sink, source or gateway */
++ /**
++ * Define a list of inverse actions: if the controller applied an action during connection procedure,
++ * the inverse action (of any) shall be applied on disconnect.
++ */
++ static am_ActionType_e inverseAction(am_ActionType_e type)
++ {
++ switch(type)
++ {
++ case AT_MUTE: return (AT_UNMUTE);
++ case AT_UNMUTE: return (AT_MUTE);
++
++ case AT_PAUSE: return (AT_RESUME);
++ case AT_RESUME: return (AT_PAUSE);
++
++ case AT_VOLUME_DECREASE: return (AT_VOLUME_INCREASE);
++ case AT_VOLUME_INCREASE: return (AT_VOLUME_DECREASE);
++
++ case AT_DISCONNECT: return (AT_CONNECT);//disconnection is not an invertible action; maybe it is
++ case AT_CONNECT: return (AT_MAX);//connection is not an invertible action
++
++ default: return (AT_MAX); // default return value
++ }
++ }
++
++
++ /**
++ * Compare everything except action type.
++ */
++ bool weekCompare(struct am_ConnectAction_s &o) const
++ {
++ return ((this->activeSourceClassID == o.activeSourceClassID) &&
++ (this->activeSinkID == o.activeSinkID) &&
++ (this->targetDomain == o.targetDomain) &&
++ (this->targetElement == o.targetElement));
++ }
++
++ /**
++ * Compare action type
++ */
++ bool operator < (struct am_ConnectAction_s &o) const
++ {
++ return this->actionType < o.actionType;
++ }
++
++};
++
++
++struct am_MixingRule_s {
++ am::am_sourceClass_t mixingSourceClassID; /* the name of the source class to be connected/disconnected */
++ std::vector<am_ConnectAction_s> actions; /* actions: e.g decrease volume for source of class Entertainment */
++};
++
++#endif
++
++
++/*
++USE-CASE 1
++==========
++
++Start Entertainment(connectionID = 1)
++
++Start Phone (connectionID = 2)
++ (A1)- actions caused by connectionID[2]: action on connectionID = 1 - mute source
++
++Start Navigation (connectionID = 3)
++ (A2) - actions cause by connectionID[3]: action on connectionID = 1 - decrease volume (pending): A1 - higher priority than A2
++ (A3) - action caused by connectionID[2]: action on connectionID = 3 - decrease volume (applied)
++
++
++End Phone
++ - inverse action (A1) - unmute connectionID = 1
++ - Check if there is any pending action that could be applied, but was blocked by A1
++ (A2) - actions cause by connectionID[3]: action on connectionID = 1 - decrease volume
++ - inverse action (A3) - increase volume connectionID = 3
++ - Check if there is any pending action that could be applied, but was blocked by A3 - none
++
++
++
++USE-CASE 2
++==========
++
++
++Start Entertainment(connectionID = 1)
++
++Start Navigation (connectionID = 2)
++ (A1) - actions cause by connectionID[2]: action on connectionID = 1 - decrease volume (applied)
++
++
++Start Phone (connectionID = 3)
++ (A2) - action caused by connectionID[3]: action on connectionID = 2 - decrease volume (applied)
++ (A3) - actions caused by connectionID[1]: action on connectionID = 1 - mute source; A1 - pending
++
++
++End Phone
++ - inverse action (A2) - increase volume connectionID = 2
++ - Check if there is any pending action that could be applied, but was blocked by A3 - none
++ - inverse action (A3) - unmute connectionID = 1
++ - Check if there is any pending action that could be applied, but was blocked by A3
++ (A1) - actions cause by connectionID[2]: action on connectionID = 1 - decrease volume (applied)
++
++
++
++
++USE-CASE 3
++==========
++
++
++Start Entertainment(connectionID = 1)
++
++Start Navigation (connectionID = 2)
++ (A1) - actions cause by connectionID[2]: action on connectionID = 1 - decrease volume (applied)
++
++
++Start Phone (connectionID = 3)
++ (A2) - action caused by connectionID[3]: action on connectionID = 2 - decrease volume (applied)
++ (A3) - actions caused by connectionID[3]: action on connectionID = 1 - mute source; A1 - pending
++
++End Navigation
++ - inverse action (A1) - A1 is pending ... no action to apply
++
++End Phone
++ - inverse action (A2) - increase volume connectionID = 2
++ - connection does not exits
++ - inverse action (A3) - unmute connectionID = 1
++*/
++
++
++
++/*
++
++MainAction -> list of subconnection, each subconnection, a list of actions
++ each action has a triggering main connection (other)
++
++//what actions this main connection was causing
++MainAction -> a map of subconnections and corresponding action
++
++*/
+diff --git a/PluginControlInterfacePulse/include/ControlSender.h b/PluginControlInterfacePulse/include/ControlSender.h
+new file mode 100644
+index 0000000..940661b
+--- /dev/null
++++ b/PluginControlInterfacePulse/include/ControlSender.h
+@@ -0,0 +1,283 @@
++/**
++ * SPDX license identifier: MPL-2.0
++ *
++ * Copyright (C) 2011-2014, Wind River Systems
++ * Copyright (C) 2014, GENIVI Alliance
++ *
++ * This file is part of Pulse Audio Interface Control Plugin.
++ *
++ * This Source Code Form is subject to the terms of the Mozilla Public
++ * License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
++ *
++ * For further information see http://www.genivi.org/.
++ *
++ * List of changes:
++ *
++ * 08.09.2014, Adrian Scarlat, First version of the code;
++ * porting from AM ver 1.x;
++ */
++
++#ifndef CONTROLSENDER_H_
++#define CONTROLSENDER_H_
++
++#include "ControlConfig.h"
++#include "control/IAmControlSend.h"
++#include <map>
++
++using namespace am;
++
++class ControlSenderPlugin: public IAmControlSend
++{
++public:
++ ControlSenderPlugin();
++
++ virtual ~ControlSenderPlugin();
++
++ am_Error_e startupController(IAmControlReceive* controlreceiveinterface);
++ void setControllerReady();
++ void setControllerRundown(const int16_t signal);
++
++ am_Error_e hookUserConnectionRequest(
++ const am_sourceID_t sourceID,
++ const am_sinkID_t sinkID,
++ am_mainConnectionID_t& mainConnectionID);
++
++ am_Error_e hookUserDisconnectionRequest(const am_mainConnectionID_t connectionID);
++
++ am_Error_e hookUserSetMainSinkSoundProperty(
++ const am_sinkID_t sinkID,
++ const am_MainSoundProperty_s& soundProperty);
++
++ am_Error_e hookUserSetMainSourceSoundProperty(
++ const am_sourceID_t sourceID,
++ const am_MainSoundProperty_s& soundProperty);
++
++ am_Error_e hookUserSetSystemProperty(const am_SystemProperty_s& property);
++
++ am_Error_e hookUserVolumeChange(
++ const am_sinkID_t SinkID,
++ const am_mainVolume_t newVolume);
++
++ am_Error_e hookUserVolumeStep(
++ const am_sinkID_t SinkID,
++ const int16_t increment);
++
++ am_Error_e hookUserSetSinkMuteState(
++ const am_sinkID_t sinkID,
++ const am_MuteState_e muteState);
++
++ am_Error_e hookSystemRegisterDomain(
++ const am_Domain_s& domainData,
++ am_domainID_t& domainID);
++
++ am_Error_e hookSystemDeregisterDomain(const am_domainID_t domainID);
++
++ void hookSystemDomainRegistrationComplete(const am_domainID_t domainID);
++
++ am_Error_e hookSystemRegisterSink(
++ const am_Sink_s& sinkData,
++ am_sinkID_t& sinkID);
++
++ am_Error_e hookSystemDeregisterSink(const am_sinkID_t sinkID);
++
++ am_Error_e hookSystemRegisterSource(
++ const am_Source_s& sourceData,
++ am_sourceID_t& sourceID);
++
++ am_Error_e hookSystemDeregisterSource(const am_sourceID_t sourceID);
++
++ am_Error_e hookSystemRegisterGateway(
++ const am_Gateway_s& gatewayData,
++ am_gatewayID_t& gatewayID);
++
++ am_Error_e hookSystemDeregisterGateway(const am_gatewayID_t gatewayID);
++
++ am_Error_e hookSystemRegisterCrossfader(
++ const am_Crossfader_s& crossfaderData,
++ am_crossfaderID_t& crossfaderID);
++
++ am_Error_e hookSystemDeregisterCrossfader(
++ const am_crossfaderID_t crossfaderID);
++
++ 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);
++
++ void hookSystemSinkAvailablityStateChange(
++ const am_sinkID_t sinkID,
++ const am_Availability_s& availability);
++
++ void hookSystemSourceAvailablityStateChange(
++ const am_sourceID_t sourceID,
++ const am_Availability_s& availability);
++
++ void hookSystemDomainStateChange(
++ const am_domainID_t domainID,
++ const am_DomainState_e state);
++
++ void hookSystemReceiveEarlyData(const std::vector<am_EarlyData_s>& data);
++
++ void hookSystemSpeedChange(const am_speed_t speed);
++
++ 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 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);
++
++ void cbAckSetSourceState(
++ const am_Handle_s handle,
++ const am_Error_e error);
++
++ void cbAckSetSourceSoundProperty(
++ const am_Handle_s handle,
++ const am_Error_e error);
++
++ void cbAckSetSourceSoundProperties(
++ const am_Handle_s handle,
++ const am_Error_e error);
++
++ void cbAckSetSinkSoundProperty(
++ const am_Handle_s handle,
++ const am_Error_e error);
++
++ void cbAckSetSinkSoundProperties(
++ const am_Handle_s handle,
++ const am_Error_e error);
++
++ am_Error_e getConnectionFormatChoice(
++ const am_sourceID_t sourceID,
++ const am_sinkID_t sinkID,
++ const am_Route_s listRoute,
++ const std::vector<am_CustomConnectionFormat_t> listPossibleConnectionFormats,
++ std::vector<am_CustomConnectionFormat_t>& listPrioConnectionFormats);
++ void getInterfaceVersion(std::string& version) const;
++ void confirmCommandReady(const am_Error_e error);
++ void confirmRoutingReady(const am_Error_e error);
++ void confirmCommandRundown(const am_Error_e error);
++ void confirmRoutingRundown(const am_Error_e error);
++
++ am_Error_e hookSystemUpdateSink(const am_sinkID_t sinkID, const am_sinkClass_t sinkClassID, const std::vector<am_SoundProperty_s>& listSoundProperties, const std::vector<am_CustomConnectionFormat_t>& listConnectionFormats, const std::vector<am_MainSoundProperty_s>& listMainSoundProperties);
++ am_Error_e hookSystemUpdateSource(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);
++ am_Error_e hookSystemUpdateGateway(const am_gatewayID_t gatewayID, const std::vector<am_CustomConnectionFormat_t>& listSourceConnectionFormats, const std::vector<am_CustomConnectionFormat_t>& listSinkConnectionFormats, const std::vector<bool>& convertionMatrix);
++ void cbAckSetVolumes(const am_Handle_s handle, const std::vector<am_Volumes_s>& listVolumes, const am_Error_e error) ;
++ void cbAckSetSinkNotificationConfiguration(const am_Handle_s handle, const am_Error_e error) ;
++ void cbAckSetSourceNotificationConfiguration(const am_Handle_s handle, const am_Error_e error) ;
++ void hookSinkNotificationDataChanged(const am_sinkID_t sinkID, const am_NotificationPayload_s& payload) ;
++ void hookSourceNotificationDataChanged(const am_sourceID_t sourceID, const am_NotificationPayload_s& payload) ;
++ am_Error_e hookUserSetMainSinkNotificationConfiguration(const am_sinkID_t sinkID, const am_NotificationConfiguration_s& notificationConfiguration) ;
++ am_Error_e hookUserSetMainSourceNotificationConfiguration(const am_sourceID_t sourceID, const am_NotificationConfiguration_s& notificationConfiguration) ;
++ void hookSystemNodeStateChanged(const NsmNodeState_e NodeStateId) ;
++ void hookSystemNodeApplicationModeChanged(const NsmApplicationMode_e ApplicationModeId) ;
++ void hookSystemSessionStateChanged(const std::string& sessionName, const NsmSeat_e seatID, const NsmSessionState_e sessionStateID) ;
++ NsmErrorStatus_e hookSystemLifecycleRequest(const uint32_t Request, const uint32_t RequestId) ;
++
++private:
++ bool isStaticRoutingEnabled() {return true;}
++
++ /**
++ * Load controller configuration file. The name and path of the configuration file
++ * should be identical to library (except the extensions which is .conf instead of .so)
++ */
++ am_Error_e loadConfig();
++
++ /**
++ * Apply some actions on top of main connection specified by (connectionID)
++ * If interrupt=true, the action like "disconnect" is reversible (e.g. when the main connection
++ * triggering this action, is destroyed, connectionID will be re-established, otherwise actions like "disconnect" are permanent)
++ * If inverse = true, action is inversely applied: (e.g. action = MUTE and inverse = true, we apply UNMUTE)
++ */
++ am_Error_e applyActionOnConnection(
++ am_mainConnectionID_t connectionID,
++ am_ConnectAction_s &action,
++ bool interrupt,
++ bool inverse,
++ bool force = false);
++
++ am_Error_e applyActionOnSubConnection(
++ am_mainConnectionID_t connectionID,
++ am_Connection_s subConnection,
++ am_ConnectAction_s &action,
++ bool interrupt,
++ bool inverse);
++
++ am_Error_e getStaticRoute(
++ const bool onlyfree,
++ const am_sourceID_t sourceID,
++ const am_sinkID_t sinkID,
++ std::vector<am_Route_s>& returnList);
++
++ am_Error_e getSourceInfo(
++ const am_sourceID_t sourceID,
++ am_Source_s &sourceData);
++
++/*
++ * TODO: REVIEW DESCRIPTION
++ * A mapping between connection and controller actions taken during connection.
++ * To be used to apply inverse actions during disconnection:
++ * am_mainConnectionID_t - the main connection request triggering the action
++ * am_connectionID_t - the sub-connection target of the action
++ * list of am_ConnectAction_s - actions to be applied on this connection
++ * in this list, only one action is not pending
++ * and the non pending action has the higher priority
++ * (e.g. disconnect > mute > decrease volume)
++ */
++ std::map<am_mainConnectionID_t,
++ std::map<am_mainConnectionID_t,
++ std::vector<am_ConnectAction_s> > > mConnectionID2Actions;
++/*
++ * A mapping between main connection and and the list of associated sub-connections.
++ * First element in the list has the same connectionID as the map key.
++ */
++ std::map<am_mainConnectionID_t,
++ std::vector<am_Connection_s> > mConnectionID2Subconnections;
++
++ IAmControlReceive *mControlReceiveInterface;
++ std::vector<am_Route_s> mStaticRoutes;
++ std::vector<am_MixingRule_s> mMixingRules;
++
++/*
++ * map of structures registered during initialization/configuration phase;
++ * DO NOT USE DIRECTLY
++ */
++ std::map<std::string, am_Source_s> mNames2Sources;
++ std::map<std::string, am_Sink_s> mNames2Sinks;
++ std::map<std::string, am_SinkClass_s> mNames2SinksClasses;
++ std::map<std::string, am_SourceClass_s> mNames2SourceClasses;
++ std::map<std::string, am_Domain_s> mNames2Domains;
++ std::map<std::string, am_Gateway_s> mNames2Gateways;
++};
++
++#endif /* CONTROLSENDER_H_ */
+diff --git a/PluginControlInterfacePulse/src/ControlSender.cpp b/PluginControlInterfacePulse/src/ControlSender.cpp
+new file mode 100644
+index 0000000..eb02b0e
+--- /dev/null
++++ b/PluginControlInterfacePulse/src/ControlSender.cpp
+@@ -0,0 +1,1763 @@
++/**
++ * SPDX license identifier: MPL-2.0
++ *
++ * Copyright (C) 2011-2014, Wind River Systems
++ * Copyright (C) 2014, GENIVI Alliance
++ *
++ * This file is part of Pulse Audio Interface Control Plugin.
++ *
++ * This Source Code Form is subject to the terms of the Mozilla Public
++ * License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
++ *
++ * For further information see http://www.genivi.org/.
++ *
++ * List of changes:
++ *
++ * 08.09.2014, Adrian Scarlat, First version of the code;
++ * Porting from AM ver 1.0;
++ */
++
++/*Includes*/
++#include "ControlSender.h"
++#include "ControlConfig.h"
++#include "control/IAmControlReceive.h"
++#include "shared/CAmDltWrapper.h"
++
++#include <cassert>
++#include <string.h>
++#include <stdio.h>
++#include <iostream>
++#include <sys/types.h>
++#include <unistd.h>
++#include <errno.h>
++
++
++/*Defines*/
++#define LIBNAMECTL "libPluginControlInterface.so"
++#define CFGNAMECTL "libPluginControlInterface.conf"
++
++DLT_DECLARE_CONTEXT(controlSender)
++
++using namespace am;
++
++extern "C" IAmControlSend* PluginControlInterfaceFactory()
++{
++ return (new ControlSenderPlugin());
++}
++
++extern "C" void destroyControlPluginInterface(IAmControlSend* controlSendInterface)
++{
++ delete controlSendInterface;
++}
++
++ControlSenderPlugin::ControlSenderPlugin() :
++ mControlReceiveInterface(NULL)
++{
++}
++
++ControlSenderPlugin::~ControlSenderPlugin()
++{
++}
++
++am_Error_e ControlSenderPlugin::startupController(IAmControlReceive *controlreceiveinterface)
++{
++ assert(controlreceiveinterface);
++ mControlReceiveInterface = controlreceiveinterface;
++ //here is a good place to insert SystemProperties into the database...
++ return E_OK;
++}
++
++void ControlSenderPlugin::setControllerReady()
++{
++ //here is a good place to insert Source and SinkClasses into the database...
++ loadConfig();
++ mControlReceiveInterface->setRoutingReady();
++ mControlReceiveInterface->setCommandReady();
++}
++
++void ControlSenderPlugin::setControllerRundown(const int16_t signal)
++{
++ logInfo("CAmControlSenderBase::setControllerRundown() was called signal=",signal);
++ if (signal==2)
++ mControlReceiveInterface->confirmControllerRundown(E_UNKNOWN);
++
++ mControlReceiveInterface->confirmControllerRundown(E_OK);
++}
++
++am_Error_e ControlSenderPlugin::hookUserConnectionRequest(const am_sourceID_t sourceID, const am_sinkID_t sinkID, am_mainConnectionID_t & mainConnectionID)
++{
++ //TODO: set sink / source volume depending on mixing rules - delta - system property
++ //TODO: the mixing depends on source CLASSES?
++ //
++ //TODO: shall we implement some kind of source auto-exclusion ? e.g. two source from te same class are not allowed simultaneously (fifo)
++ //TODO: shall we implement an action = destroy? apart from disconnect? disconnect is reversible, destroy is not reversible and the main connection is deleted?
++ //TODO: maybe we simply should return E_ALREADY_EXISTS - and tis should not be a real error code (if connection already exists, maybe that's fine)
++
++ //TODO: apply actions only on active main connections!!!!
++
++ //TODO: interrupting sources on top of main sources -> main src:temporary disconnection;
++ //TODO: main sources on top of main sources -> old main src:permanent disconnection;
++
++
++
++ std::vector<am_Route_s> audioRoute;
++ if (!this->isStaticRoutingEnabled())
++ {
++ this->mControlReceiveInterface->getRoute(true, sourceID, sinkID, audioRoute);
++ }
++ else
++ {
++ this->getStaticRoute(true, sourceID, sinkID, audioRoute);
++ }
++
++//TODO: if there is no static route? is there any default route ?
++//TODO: if there is no static route? can we use dynamic routing ?
++ logInfo("ControlSenderPlugin::hookUserConnectionRequest audioRoute is empty ? ", audioRoute.empty());
++ if (audioRoute.empty())
++ {
++ return am::E_NOT_POSSIBLE;
++ }
++
++/*
++ * Find out the source class info for the new main connection.
++ * Also find out if this connection is interrupting or not
++ */
++ am_SourceClass_s newClassInfo;
++ this->mControlReceiveInterface->getSourceClassInfoDB(sourceID, newClassInfo);
++
++ bool interrupting = false;
++ for (uint16_t i = 0; i < newClassInfo.listClassProperties.size(); i++)
++ {
++ if (newClassInfo.listClassProperties[i].classProperty == CP_GENIVI_SOURCE_TYPE &&
++ newClassInfo.listClassProperties[i].value == SOURCE_TYPE_INTERRUPT)
++ {
++ interrupting = true;
++ }
++ }
++
++ logInfo("{BEGIN Connection request Source class:",
++ newClassInfo.name,
++ " [ sourceID:", sourceID, ", sinkID:", sinkID, "] - interrupting:" ,interrupting);
++
++ std::vector<am_MainConnection_s> existingConnections;
++
++ this->mControlReceiveInterface->getListMainConnections(existingConnections);
++ std::vector<am_MainConnection_s>::iterator iter = existingConnections.begin();
++ std::vector<am_MainConnection_s>::iterator iterEnd = existingConnections.end();
++
++ iter = existingConnections.begin();
++/*
++* iterate connections to remove already existing connections for the same source/sink
++*/
++ bool listChanged = false;
++ for (; iter < iterEnd; ++iter)
++ {
++ if (iter->sinkID == sinkID && iter->sourceID == sourceID)
++ {
++ //main connection already exists - exclude it
++ logInfo("END Connection request - ALREADY EXISTS}");
++ return E_ALREADY_EXISTS;
++ }
++ }
++ if (listChanged)
++ {
++ this->mControlReceiveInterface->getListMainConnections(existingConnections);
++ iter = existingConnections.begin();
++ iterEnd = existingConnections.end();
++ }
++
++//TODO: first connect everything in the audio path
++//TODO: if one sub-connection is not successful - disconnect everything
++//TODO: only if every sub-connection was successful - apply the mixing rules
++
++ std::map<am_mainConnectionID_t, std::vector<am_ConnectAction_s> > mainConnection2Actions;
++
++/***************************************************************
++ * START MIXING RULES EVALUATION
++ * - maintain a list of actions associated to a connection;
++ * according to the mixing rules
++ * - the inverse actions shall be applied during disconnection
++ ***************************************************************/
++
++/*
++ * Iterate audio mixing rules, then iterate existing connections;
++ * if the new source class match the mixing source class
++ * find out if any action should be taken, find out the target
++ * of the action -> domain/sink/source.
++ */
++ for (uint16_t i = 0; i < mMixingRules.size(); i++)
++ {
++/*
++ * the source class of the new connection matches the source class of the mixing rule
++ */
++ if (newClassInfo.sourceClassID == mMixingRules[i].mixingSourceClassID)
++ {
++ iter = existingConnections.begin();
++/*
++ * iterate main connections
++ */
++ for (; iter < iterEnd; ++iter)
++ {
++ am_SourceClass_s srcClassOfConn;
++ am_sinkID_t sinkIdOfConn = iter->sinkID;
++
++ this->mControlReceiveInterface->getSourceClassInfoDB(
++ iter->sourceID, srcClassOfConn);
++
++ am_sourceClass_t srcClassIdOfConn = srcClassOfConn.sourceClassID;
++/*
++ * iterate the actions associated to the mixing rule
++ */
++ for (uint16_t j = 0; j < mMixingRules[i].actions.size(); j++)
++ {
++/*
++ * if the source class of the action in the mixing rule matches the source class
++ of an existing connection AND the sink of the action in the mixing rule matches
++ the sink of an existing connection - then the action must be applied on
++ existing connection
++ */
++ if (mMixingRules[i].actions[j].activeSourceClassID == srcClassIdOfConn &&
++ mMixingRules[i].actions[j].activeSinkID == sinkIdOfConn)
++ {
++ mainConnection2Actions[iter->mainConnectionID].push_back(mMixingRules[i].actions[j]);
++ logInfo("Apply action on existing connection:", iter->mainConnectionID,
++ "caused by new connection - unknow ID yet");
++ this->applyActionOnConnection(iter->mainConnectionID, mMixingRules[i].actions[j], interrupting, false);
++
++ }//end if (existingClassInfo.name.compare(
++ }//end for actionsCount
++ }//end for existingConnections
++ }//end if (newClassInfo.name.compare(mixingRules[i].sourceClassName
++ }//end for mixingRules
++
++/**
++ * After actions cause by the new connections were applied on existing connections,
++ * connect all elements in the route to create sub-connections;
++ * for each connection, store the reference in: mConnectionID2Subconnections
++ */
++ //for static routing - only one route in the list of audioRoute
++ std::vector<am_RoutingElement_s>::iterator iterRoute = audioRoute[0].route.begin();
++ std::vector<am_RoutingElement_s>::iterator iterRouteEnd = audioRoute[0].route.end();
++
++ std::vector<am_Connection_s> subconnections;
++ am_MainConnection_s mainConnection;
++
++ for (; iterRoute < iterRouteEnd; iterRoute++)
++ {
++ am_Handle_s h;
++
++ am_mainConnectionID_t subConnectionID;
++
++ am_Error_e ret = mControlReceiveInterface->connect(
++ h, subConnectionID, iterRoute->connectionFormat,
++ iterRoute->sourceID, iterRoute->sinkID);
++
++ am_SourceClass_s srcClassOfConn;
++
++ this->mControlReceiveInterface->getSourceClassInfoDB(
++ iterRoute->sourceID, srcClassOfConn);
++
++ logInfo("Subconnection created: subConnectionID", subConnectionID);
++ logInfo("Subconnection: sourceID:", iterRoute->sourceID ,
++ ", sourceClassID:", srcClassOfConn.sourceClassID,
++ ", sinkID:", iterRoute->sinkID, "]");
++
++ if (ret == E_OK || ret == E_ALREADY_EXISTS)
++ {
++ am_Connection_s tempConnection;
++ tempConnection.sinkID = iterRoute->sinkID;
++ tempConnection.sourceID = iterRoute->sourceID;
++ tempConnection.connectionID = subConnectionID;
++
++ subconnections.push_back(tempConnection);
++ mainConnection.listConnectionID.push_back(tempConnection.connectionID);
++ }
++ else
++ {
++ //TODO: disconnect already connected in case of error
++ logInfo("END Connection request with error code:", ret, "}");
++ return ret;
++ }
++ }
++
++/*
++ * Once the sub-connections were created, we are ready to store the main connections
++ */
++
++ mainConnection.mainConnectionID = 0;
++ mainConnection.connectionState = CS_CONNECTED;
++ mainConnection.sinkID = sinkID;
++ mainConnection.sourceID = sourceID;
++ am_Error_e ret = this->mControlReceiveInterface->enterMainConnectionDB(
++ mainConnection, mainConnectionID);
++ mainConnection.mainConnectionID = mainConnectionID;
++
++ this->mConnectionID2Subconnections[mainConnectionID] = subconnections;
++ this->mConnectionID2Actions[mainConnectionID] = mainConnection2Actions;
++
++ if (ret != E_OK)
++ {
++ logInfo("END Connection request with error code:", ret, "}");
++ return ret;
++ }
++ else
++ {
++ logInfo("Mainconnection: mainConnectionID", mainConnectionID);
++ }
++ //TODO - check the status
++
++
++/*
++ * Iterate audio mixing rules, then iterate actions,
++ * if the source class of an action matches the new source class name
++ * find out if there is an existing connection matching the mixingSourceClass
++ */
++ for (uint16_t i = 0; i < mMixingRules.size(); i++)
++ {
++ for (uint16_t j = 0; j < mMixingRules[i].actions.size(); j++)
++ {
++/*
++ * if the source class of the action in the mixing rule matches the source class
++ of an existing connection AND the sink of the action in the mixing rule matches
++ the sink of an existing connection - then the action must be applied on
++ existing connection
++ */
++ if (mMixingRules[i].actions[j].activeSourceClassID == newClassInfo.sourceClassID)
++ {
++ iter = existingConnections.begin();
++ /*
++ * iterate connections
++ */
++ for (; iter < iterEnd; ++iter)
++ {
++ am_SourceClass_s srcClassOfConn;
++
++ this->mControlReceiveInterface->getSourceClassInfoDB(
++ iter->sourceID, srcClassOfConn);
++
++ am_sourceClass_t srcClassIdOfConn = srcClassOfConn.sourceClassID;
++ if (srcClassIdOfConn == mMixingRules[i].mixingSourceClassID)
++ {
++ if (this->mConnectionID2Actions[mainConnectionID].find(
++ iter->mainConnectionID) !=
++ this->mConnectionID2Actions[mainConnectionID].end())
++ {
++ //there was already an action applied for iter->connectionID caused by mainConnection.connectionID
++ //connections should not exclude each other
++ logInfo("No action to apply on new connection:", mainConnection.mainConnectionID,
++ "since there was already an action applyed to existing connection", iter->mainConnectionID);
++ }
++ else
++ {
++ logInfo("Apply action on new connection:", mainConnection.mainConnectionID,
++ "caused by existing connection", iter->mainConnectionID);
++
++ interrupting = false;
++ for (uint16_t x = 0; x < srcClassOfConn.listClassProperties.size(); x++)
++ {
++ if (srcClassOfConn.listClassProperties[x].classProperty == CP_GENIVI_SOURCE_TYPE &&
++ srcClassOfConn.listClassProperties[x].value == SOURCE_TYPE_INTERRUPT)
++ {
++ interrupting = true;
++ }
++ }
++
++ this->mConnectionID2Actions[iter->mainConnectionID][mainConnectionID].push_back(mMixingRules[i].actions[j]);
++ this->applyActionOnConnection(mainConnection.mainConnectionID, mMixingRules[i].actions[j], interrupting, false);
++ }
++ }
++ }
++ }
++ }
++ }
++
++ logInfo("END Connection request - SUCCESS}");
++ return E_OK;
++}
++
++
++am_Error_e ControlSenderPlugin::hookUserDisconnectionRequest(const am_mainConnectionID_t connectionID)
++{
++ am_Handle_s h;
++
++ if (connectionID == 0)
++ {
++ logError("Disconnect what :-o ? connection ID = 0");
++ return E_NON_EXISTENT;
++ }
++
++ //TODO: restore other sources - resume ? or restore volume? - then disconnect
++ am_SourceClass_s oldClassInfo;
++ bool interrupting = false;
++
++ std::vector<am_MainConnection_s> existingConnections;
++
++ this->mControlReceiveInterface->getListMainConnections(existingConnections);
++ logInfo("{BEGIN Disconnection request: connectionID:", connectionID);
++ std::vector<am_MainConnection_s>::iterator iter = existingConnections.begin();
++ std::vector<am_MainConnection_s>::iterator iterEnd = existingConnections.end();
++
++ for (; iter < iterEnd; ++iter)
++ {
++ if (iter->mainConnectionID == connectionID)
++ {//check other existing connection , maybe volume should be restored
++ this->mControlReceiveInterface->getSourceClassInfoDB(iter->sourceID, oldClassInfo);
++ for (unsigned int i = 0; i < oldClassInfo.listClassProperties.size(); i++)
++ {
++ if (oldClassInfo.listClassProperties[i].classProperty == CP_GENIVI_SOURCE_TYPE &&
++ oldClassInfo.listClassProperties[i].value == SOURCE_TYPE_INTERRUPT)
++ {
++ interrupting = true;
++ }
++ }
++ break;
++ }
++ }
++
++ //save the list of inverse actions
++ std::map<am_mainConnectionID_t,
++ std::vector<am_ConnectAction_s> > connectionID2Actions = this->mConnectionID2Actions[connectionID];
++
++ this->mConnectionID2Actions[connectionID].clear();
++
++ //apply the inverse action - of actions caused by this connection
++ iter = existingConnections.begin();
++ for (; iter < iterEnd; ++iter)
++ {
++ logInfo("Check apply inverse action on existing connection:", iter->mainConnectionID,
++ "caused by disconnecion connection", connectionID);
++
++ std::vector<am_ConnectAction_s>::iterator actionsIter
++ = connectionID2Actions[iter->mainConnectionID].begin();
++ std::vector<am_ConnectAction_s>::iterator actionsIterEnd
++ = connectionID2Actions[iter->mainConnectionID].end();
++
++ for (; actionsIter < actionsIterEnd; actionsIter++)
++ {
++ //apply the inverse action
++ logInfo("Apply inverse action on existing connection:", iter->mainConnectionID,
++ "caused by disconnecion connection", connectionID);
++
++ applyActionOnConnection(iter->mainConnectionID, *actionsIter, interrupting, true);
++ }//end for actions
++ }//end for existing connections
++
++
++ //apply the inverse action - of actions applyed to this connection
++ iter = existingConnections.begin();
++ for (; iter < iterEnd; ++iter)
++ {
++ std::map<am_mainConnectionID_t,
++ std::vector<am_ConnectAction_s> > connectionID2Actions = this->mConnectionID2Actions[iter->mainConnectionID];
++
++ logInfo("Check apply inverse action on existing connection:", iter->mainConnectionID,
++ "caused by disconnecion connection", connectionID);
++
++ std::vector<am_ConnectAction_s>::iterator actionsIter
++ = connectionID2Actions[connectionID].begin();
++ std::vector<am_ConnectAction_s>::iterator actionsIterEnd
++ = connectionID2Actions[connectionID].end();
++
++ for (; actionsIter < actionsIterEnd; actionsIter++)
++ {
++ //apply the inverse action
++ logInfo("Apply inverse action on disconnecion connection", connectionID);
++
++ applyActionOnConnection(connectionID, *actionsIter, interrupting, true, true);
++ }//end for actions
++
++ }
++
++ //iterate sub-connections and disconnect
++ std::vector<am_Connection_s>::iterator subConnIter =
++ this->mConnectionID2Subconnections[connectionID].begin();
++ std::vector<am_Connection_s>::iterator subConnIterEnd =
++ this->mConnectionID2Subconnections[connectionID].end();
++
++ for (; subConnIter < subConnIterEnd; subConnIter++)
++ {
++ logInfo("Subdisconnection: subConnectionID", subConnIter->connectionID);
++ mControlReceiveInterface->disconnect(h, subConnIter->connectionID);
++ }
++
++ //TODO: return value depends on all disconnect() results.
++ this->mConnectionID2Subconnections[connectionID].clear();
++
++ this->mControlReceiveInterface->changeMainConnectionStateDB(
++ connectionID, CS_DISCONNECTED);
++ this->mControlReceiveInterface->removeMainConnectionDB(connectionID);
++
++
++ logInfo("END Disconnection request}");
++
++ return E_OK;
++}
++
++
++
++/**
++ * Find out the sub-connection target of this action
++ * apply the action and return the connectionID of the affected
++ * sub-connection
++ */
++am_Error_e ControlSenderPlugin::applyActionOnConnection(
++ am_mainConnectionID_t connectionID,
++ am_ConnectAction_s &action,
++ bool interrupt,
++ bool inverse,
++ bool force)
++{
++ am_Error_e ret;
++
++ /**
++ * Check if current action is conflicting an existing action,
++ * do not apply, or cancel existing action and apply this one.
++ * if action is not inverse then:
++ * - current action may not be applied
++ * - current action might cause other actions to become pending
++ */
++ std::vector<am_MainConnection_s> existingConnections;
++
++ this->mControlReceiveInterface->getListMainConnections(existingConnections);
++
++ std::vector<am_MainConnection_s>::iterator iter1 = existingConnections.begin();
++ std::vector<am_MainConnection_s>::iterator iter1End = existingConnections.end();
++
++ std::vector<am_MainConnection_s>::iterator iter2 = existingConnections.begin();
++ std::vector<am_MainConnection_s>::iterator iter2End = existingConnections.end();
++
++ if (!force)
++ {
++
++ for (; iter1 < iter1End; ++iter1)
++ {
++ std::map<am_mainConnectionID_t,
++ std::vector<am_ConnectAction_s> > connectionID2Actions =
++ this->mConnectionID2Actions[iter1->mainConnectionID];
++
++ iter2 = existingConnections.begin();
++ for (; iter2 < iter2End; ++iter2)
++ {
++ std::vector<am_ConnectAction_s>::iterator actionsIter
++ = connectionID2Actions[iter2->mainConnectionID].begin();
++ std::vector<am_ConnectAction_s>::iterator actionsIterEnd
++ = connectionID2Actions[iter2->mainConnectionID].end();
++
++ for (; actionsIter < actionsIterEnd; actionsIter++)
++ {
++ if (action.weekCompare(*actionsIter))
++ {
++ if (!inverse)
++ {
++ if (actionsIter->actionType < action.actionType)
++ {
++ logInfo("Abort action", ACTION_TYPE_STR(action.actionType),
++ "on connection:", connectionID,
++ "since", ACTION_TYPE_STR(actionsIter->actionType),
++ "is active; triggering mainConnection:", iter1->mainConnectionID);
++ return am::E_ABORTED;
++ }
++ }
++ else
++ {
++ if (actionsIter->actionType < am_ConnectAction_s::inverseAction(action.actionType))
++ {
++ logInfo("Abort action", ACTION_TYPE_STR(am_ConnectAction_s::inverseAction(action.actionType)),
++ "on connection:", connectionID,
++ "since", ACTION_TYPE_STR(actionsIter->actionType),
++ "is active; triggering mainConnection:", iter1->mainConnectionID);
++ return am::E_ABORTED;
++ }
++ }
++ }
++ }
++ }
++ }
++
++ }
++ //if action is inverse then:
++ // - some pending connections might become active
++
++ //iterate sub-connections for main connection to find out the targetElement
++ std::vector<am_Connection_s>::iterator iter = mConnectionID2Subconnections[connectionID].begin();
++ std::vector<am_Connection_s>::iterator iterEnd = mConnectionID2Subconnections[connectionID].end();
++
++ for (; iter < iterEnd; iter++)
++ {
++ am_Connection_s currentConn = *iter;
++ am_Source_s currentSrc;
++
++ //find out the domain of currentConn.
++ this->getSourceInfo(currentConn.sourceID, currentSrc);
++
++ if (action.targetDomain == currentSrc.domainID)
++ {
++ ret = applyActionOnSubConnection(
++ connectionID,
++ currentConn,
++ action,
++ interrupt,
++ inverse);
++ }
++ }
++
++
++ iter1 = existingConnections.begin();
++ iter2 = existingConnections.begin();
++ //apply existing action excluded by currently disappearing action
++ for (; iter1 < iter1End; ++iter1)
++ {
++ std::map<am_mainConnectionID_t,
++ std::vector<am_ConnectAction_s> > connectionID2Actions =
++ this->mConnectionID2Actions[iter1->mainConnectionID];
++
++ iter2 = existingConnections.begin();
++ for (; iter2 < iter2End; ++iter2)
++ {
++ std::vector<am_ConnectAction_s>::iterator actionsIter
++ = connectionID2Actions[iter2->mainConnectionID].begin();
++ std::vector<am_ConnectAction_s>::iterator actionsIterEnd
++ = connectionID2Actions[iter2->mainConnectionID].end();
++
++ for (; actionsIter < actionsIterEnd; actionsIter++)
++ {
++ if (action.weekCompare(*actionsIter))
++ {
++ if (inverse)
++ {
++ if (actionsIter->actionType > action.actionType)
++ {
++ applyActionOnConnection(iter2->mainConnectionID, *actionsIter, false, false);
++ }
++ }
++ }
++ }
++ }
++ }
++
++ return ret;
++}
++
++am_Error_e ControlSenderPlugin::applyActionOnSubConnection(
++ am_mainConnectionID_t connectionID,
++ am_Connection_s subConnection,
++ am_ConnectAction_s &action,
++ bool interrupt,
++ bool inverse)
++{
++ am_Handle_s h;
++ am_ActionType_e actionType = inverse ?
++ am_ConnectAction_s::inverseAction(action.actionType) :
++ action.actionType;
++
++ logInfo("Apply action", ACTION_TYPE_STR(actionType),
++ "on sub-connectionID:", subConnection.connectionID,
++ "[sourceID:", subConnection.sourceID,
++ ", sinkID:", subConnection.sinkID, "]");
++
++ //TODO: is this main connection suspended or restored?
++ //TODO: - should check all sub-connections - current implementation is wrong overall connection state is decided by one sub-connection
++
++ switch (actionType)
++ {
++ case AT_CONNECT:
++ {
++ logInfo("Apply connect: sub-connectionID:",
++ subConnection.connectionID,
++ "[sourceID:", subConnection.sourceID,
++ ", sinkID:", subConnection.sinkID, "]");
++
++ if (subConnection.sourceID == 0 ||
++ subConnection.sinkID == 0)
++ {
++ logError("Can not apply re-connect sink or source not defined");
++ return E_NOT_POSSIBLE;
++ }
++ mControlReceiveInterface->connect(h,
++ subConnection.connectionID,
++ subConnection.connectionFormat,
++ subConnection.sourceID,
++ subConnection.sinkID);
++
++ am_Connection_s tempConnection;
++ tempConnection.sinkID = subConnection.sinkID;
++ tempConnection.sourceID = subConnection.sourceID;
++ tempConnection.connectionID = subConnection.connectionID;
++
++ //this old connection shall be replaced with a new one.
++ this->mConnectionID2Subconnections[connectionID].push_back(
++ tempConnection
++ );
++
++ //TODO: this new connection id shall replace the subconnection.
++
++ this->mControlReceiveInterface->
++ changeMainConnectionStateDB(
++ connectionID,
++ CS_CONNECTED);
++ break;
++ }
++ case AT_DISCONNECT:
++ {
++ this->mControlReceiveInterface->changeMainConnectionStateDB(
++ connectionID, CS_DISCONNECTED);
++
++ logInfo("Apply disconnect: sub-connectionID:",
++ subConnection.connectionID,
++ "[sourceID:", subConnection.sourceID,
++ ", sinkID:", subConnection.sinkID, "]");
++
++ mControlReceiveInterface->disconnect(h, subConnection.connectionID);
++
++ this->mControlReceiveInterface->
++ changeMainConnectionStateDB(
++ connectionID,
++ interrupt ? CS_SUSPENDED : CS_DISCONNECTED);
++
++ if (!interrupt)
++ {
++ this->mControlReceiveInterface->
++ removeMainConnectionDB(connectionID);
++ }
++ break;
++ }
++ case AT_VOLUME_DECREASE:
++ {
++ if (action.targetElement == ATG_SOURCE)
++ {
++ logInfo("Apply decrease volume: sourceID:", subConnection.sourceID);
++ mControlReceiveInterface->setSourceVolume(
++ h, subConnection.sourceID, 50, am::RAMP_GENIVI_NO_PLOP, 0);
++ }
++ if (action.targetElement == ATG_SINK)
++ {
++ logInfo("Apply decrease volume: sinkID:", subConnection.sinkID);
++ mControlReceiveInterface->setSinkVolume(
++ h, subConnection.sinkID, 50, am::RAMP_GENIVI_NO_PLOP, 0);
++ }
++ }
++ break;
++
++ case AT_VOLUME_INCREASE:
++ {
++ if (action.targetElement == ATG_SOURCE)
++ {
++ logInfo("Apply increase volume: sourceID:", subConnection.sourceID);
++ mControlReceiveInterface->setSourceVolume(
++ h, subConnection.sourceID, 100, am::RAMP_GENIVI_NO_PLOP, 0);
++ }
++ if (action.targetElement == ATG_SINK)
++ {
++ logInfo("Apply increase volume: sinkID:", subConnection.sinkID);
++ mControlReceiveInterface->setSinkVolume(
++ h, subConnection.sinkID, 100, am::RAMP_GENIVI_NO_PLOP, 0);
++ }
++ break;
++ }
++ case AT_MUTE:
++ {
++ if (action.targetElement == ATG_SOURCE)
++ {
++ logInfo("Apply mute: sourceID:", subConnection.sourceID);
++ mControlReceiveInterface->setSourceVolume(
++ h, subConnection.sourceID, 0, am::RAMP_GENIVI_NO_PLOP, 0);
++ }
++ if (action.targetElement == ATG_SINK)
++ {
++ //TODO: replace setVolume(0) with a real setMute function
++ logInfo("Apply mute: sinkID:", subConnection.sinkID);
++ mControlReceiveInterface->setSinkVolume(
++ h, subConnection.sinkID, 0, am::RAMP_GENIVI_NO_PLOP, 0);
++ }
++
++ this->mControlReceiveInterface->
++ changeMainConnectionStateDB(
++ connectionID,
++ CS_SUSPENDED);
++ break;
++ }
++ case AT_UNMUTE:
++ {
++ if (action.targetElement == ATG_SOURCE)
++ {
++ logInfo("Apply unmute: sourceID:", subConnection.sourceID);
++ mControlReceiveInterface->setSourceVolume(
++ h, subConnection.sourceID, 100, am::RAMP_GENIVI_NO_PLOP, 0);
++ }
++ if (action.targetElement == ATG_SINK)
++ {
++ //TODO: replace setVolume(100) with a real setMute function
++ logInfo("Apply unmute: sinkID:", subConnection.sinkID);
++ mControlReceiveInterface->setSinkVolume(
++ h, subConnection.sinkID, 100, am::RAMP_GENIVI_NO_PLOP, 0);
++ }
++
++ this->mControlReceiveInterface->
++ changeMainConnectionStateDB(
++ connectionID,
++ CS_CONNECTED);
++ }
++
++ //TODO: case for PAUSE/RESUME
++ default:
++ break;
++ }//end switch (mixingRules[i].actions[j].actionType
++ return E_OK;
++}
++
++am_Error_e ControlSenderPlugin::hookUserSetMainSinkSoundProperty(const am_sinkID_t sinkID, const am_MainSoundProperty_s & soundProperty)
++{
++ (void) sinkID;
++ (void) soundProperty;
++ return E_NOT_USED;
++}
++
++am_Error_e ControlSenderPlugin::hookUserSetMainSourceSoundProperty(const am_sourceID_t sourceID, const am_MainSoundProperty_s & soundProperty)
++{
++ (void) sourceID;
++ (void) soundProperty;
++ return E_NOT_USED;
++}
++
++am_Error_e ControlSenderPlugin::hookUserSetSystemProperty(const am_SystemProperty_s & property)
++{
++ (void) property;
++ return E_NOT_USED;
++}
++
++am_Error_e ControlSenderPlugin::hookUserVolumeChange(const am_sinkID_t SinkID, const am_mainVolume_t newVolume)
++{
++ am_Handle_s h;
++ return this->mControlReceiveInterface->setSinkVolume(
++ h, SinkID, newVolume, am::RAMP_GENIVI_NO_PLOP, 0);
++}
++
++am_Error_e ControlSenderPlugin::hookUserVolumeStep(const am_sinkID_t SinkID, const int16_t increment)
++{
++ (void) SinkID;
++ (void) increment;
++ return E_NOT_USED;
++}
++
++am_Error_e ControlSenderPlugin::hookUserSetSinkMuteState(const am_sinkID_t sinkID, const am_MuteState_e muteState)
++{
++ (void) sinkID;
++ (void) muteState;
++ return E_NOT_USED;
++}
++
++am_Error_e ControlSenderPlugin::hookSystemRegisterDomain(const am_Domain_s & domainData, am_domainID_t & domainID)
++{
++ am_Error_e retVal = mControlReceiveInterface->enterDomainDB(domainData, domainID);
++ if (retVal == E_OK) {
++ this->mNames2Domains[domainData.name] = domainData;
++ this->mNames2Domains[domainData.name].domainID = domainID;
++ }
++ logInfo("Register Domain: name:" , domainData.name, ", domainId:", domainID);
++ return retVal;
++}
++
++am_Error_e ControlSenderPlugin::hookSystemDeregisterDomain(const am_domainID_t domainID)
++{
++ (void) domainID;
++ return E_NOT_USED;
++}
++
++void ControlSenderPlugin::hookSystemDomainRegistrationComplete(const am_domainID_t domainID)
++{
++/*
++ * refresh config after each domain registration.
++ * elements already registered will not register again
++*/
++ logInfo("Domain registration completed", domainID);
++ loadConfig();
++}
++
++am_Error_e ControlSenderPlugin::hookSystemRegisterSink(const am_Sink_s & sinkData, am_sinkID_t & sinkID)
++{
++ logInfo("Register Sink: name:" , sinkData.name, ", sinkId:", sinkID);
++ am_Error_e retVal = mControlReceiveInterface->enterSinkDB(sinkData, sinkID);
++ if (retVal == E_OK) {
++ this->mNames2Sinks[sinkData.name] = sinkData;
++ this->mNames2Sinks[sinkData.name].sinkID = sinkID;
++ }
++ logInfo("Register Sink: name:" , sinkData.name, ", sinkId:", sinkID);
++ return retVal;
++}
++
++am_Error_e ControlSenderPlugin::hookSystemDeregisterSink(const am_sinkID_t sinkID)
++{
++ (void) sinkID;
++ return E_NOT_USED;
++}
++
++am_Error_e ControlSenderPlugin::hookSystemRegisterSource(const am_Source_s & sourceData, am_sourceID_t & sourceID)
++{
++ am_Error_e retVal = mControlReceiveInterface->enterSourceDB(sourceData, sourceID);
++ if (retVal == E_OK)
++ {
++ this->mNames2Sources[sourceData.name] = sourceData;
++ this->mNames2Sources[sourceData.name].sourceID = sourceID;
++ }
++ logInfo("Register Source: name:" , sourceData.name, ", sourceId:", sourceID);
++ return retVal;
++}
++
++am_Error_e ControlSenderPlugin::hookSystemDeregisterSource(const am_sourceID_t sourceID)
++{
++ (void) sourceID;
++ return E_NOT_USED;
++}
++
++am_Error_e ControlSenderPlugin::hookSystemRegisterGateway(const am_Gateway_s & gatewayData, am_gatewayID_t & gatewayID)
++{
++ (void) gatewayData;
++ (void) gatewayID;
++ return E_NOT_USED;
++}
++
++am_Error_e ControlSenderPlugin::hookSystemDeregisterGateway(const am_gatewayID_t gatewayID)
++{
++ (void) gatewayID;
++ return E_NOT_USED;
++}
++
++am_Error_e ControlSenderPlugin::hookSystemRegisterCrossfader(const am_Crossfader_s & crossfaderData, am_crossfaderID_t & crossfaderID)
++{
++ (void) crossfaderData;
++ (void) crossfaderID;
++ return E_NOT_USED;
++}
++
++am_Error_e ControlSenderPlugin::hookSystemDeregisterCrossfader(const am_crossfaderID_t crossfaderID)
++{
++ (void) crossfaderID;
++ return E_NOT_USED;
++}
++
++void ControlSenderPlugin::hookSystemSinkVolumeTick(const am_Handle_s handle, const am_sinkID_t sinkID, const am_volume_t volume)
++{
++ (void) handle;
++ (void) sinkID;
++ (void) volume;
++}
++
++void ControlSenderPlugin::hookSystemSourceVolumeTick(const am_Handle_s handle, const am_sourceID_t sourceID, const am_volume_t volume)
++{
++ (void) handle;
++ (void) sourceID;
++ (void) volume;
++}
++
++void ControlSenderPlugin::hookSystemInterruptStateChange(const am_sourceID_t sourceID, const am_InterruptState_e interruptState)
++{
++ (void) sourceID;
++ (void) interruptState;
++}
++
++void ControlSenderPlugin::hookSystemSinkAvailablityStateChange(const am_sinkID_t sinkID, const am_Availability_s & availability)
++{
++ (void) sinkID;
++ (void) availability;
++}
++
++void ControlSenderPlugin::hookSystemSourceAvailablityStateChange(const am_sourceID_t sourceID, const am_Availability_s & availability)
++{
++ (void) sourceID;
++ (void) availability;
++}
++
++void ControlSenderPlugin::hookSystemDomainStateChange(const am_domainID_t domainID, const am_DomainState_e state)
++{
++ (void) domainID;
++ (void) state;
++}
++
++void ControlSenderPlugin::hookSystemReceiveEarlyData(const std::vector<am_EarlyData_s> & data)
++{
++ (void) data;
++}
++
++void ControlSenderPlugin::hookSystemSpeedChange(const am_speed_t speed)
++{
++ (void) speed;
++}
++
++void ControlSenderPlugin::hookSystemTimingInformationChanged(const am_mainConnectionID_t mainConnectionID, const am_timeSync_t time)
++{
++ (void) mainConnectionID;
++ (void) time;
++}
++
++void ControlSenderPlugin::cbAckConnect(const am_Handle_s handle, const am_Error_e errorID)
++{
++ (void) handle;
++ (void) errorID;
++}
++
++void ControlSenderPlugin::cbAckDisconnect(const am_Handle_s handle, const am_Error_e errorID)
++{
++ (void) handle;
++ (void) errorID;
++}
++
++void ControlSenderPlugin::cbAckCrossFade(const am_Handle_s handle, const am_HotSink_e hostsink, const am_Error_e error)
++{
++ (void) handle;
++ (void) hostsink;
++ (void) error;
++}
++
++void ControlSenderPlugin::cbAckSetSinkVolumeChange(const am_Handle_s handle, const am_volume_t volume, const am_Error_e error)
++{
++ (void) error;
++ (void) volume;
++ (void) handle;
++}
++
++void ControlSenderPlugin::cbAckSetSourceVolumeChange(const am_Handle_s handle, const am_volume_t voulme, const am_Error_e error)
++{
++ (void) error;
++ (void) voulme;
++ (void) handle;
++}
++
++void ControlSenderPlugin::cbAckSetSourceState(const am_Handle_s handle, const am_Error_e error)
++{
++ (void) error;
++ (void) handle;
++}
++
++void ControlSenderPlugin::cbAckSetSourceSoundProperty(const am_Handle_s handle, const am_Error_e error)
++{
++ (void) error;
++ (void) handle;
++}
++
++void ControlSenderPlugin::cbAckSetSinkSoundProperty(const am_Handle_s handle, const am_Error_e error)
++{
++ (void) error;
++ (void) handle;
++}
++
++void ControlSenderPlugin::cbAckSetSourceSoundProperties(const am_Handle_s handle, const am_Error_e error)
++{
++ (void) error;
++ (void) handle;
++}
++
++void ControlSenderPlugin::cbAckSetSinkSoundProperties(const am_Handle_s handle, const am_Error_e error)
++{
++ (void) error;
++ (void) handle;
++}
++
++am_Error_e ControlSenderPlugin::getConnectionFormatChoice(const am_sourceID_t sourceID, const am_sinkID_t sinkID, const am_Route_s listRoute, const std::vector<am_CustomConnectionFormat_t> listPossibleConnectionFormats, std::vector<am_CustomConnectionFormat_t> & listPrioConnectionFormats)
++{
++ (void) sourceID;
++ (void) sinkID;
++ (void) listRoute;
++ //ok, this is cheap. In a real product you have your preferences, right?
++ listPrioConnectionFormats = listPossibleConnectionFormats;
++ return (E_OK);
++}
++
++void ControlSenderPlugin::getInterfaceVersion(std::string & version) const
++{
++ version = "3.0";
++}
++
++void ControlSenderPlugin::confirmCommandReady(const am_Error_e error)
++{
++ (void) error;
++ logInfo("ControlSenderPlugin got Routing Ready confirmed");
++}
++
++void ControlSenderPlugin::confirmRoutingReady(const am_Error_e error)
++{
++ (void) error;
++ logInfo("ControlSenderPlugin got Command Ready confirmed");
++}
++
++void ControlSenderPlugin::confirmCommandRundown(const am_Error_e error)
++{
++ (void) error;
++ logInfo("ControlSenderPlugin got Routing Rundown confirmed");
++}
++
++void ControlSenderPlugin::confirmRoutingRundown(const am_Error_e error)
++{
++ (void) error;
++ logInfo("ControlSenderPlugin got Command Rundown confirmed");
++}
++
++am_Error_e ControlSenderPlugin::getStaticRoute(
++ const bool onlyfree,
++ const am_sourceID_t sourceID,
++ const am_sinkID_t sinkID,
++ std::vector<am_Route_s>& returnList)
++{
++ std::vector<am_Route_s>::iterator iter = this->mStaticRoutes.begin();
++ std::vector<am_Route_s>::iterator iterEnd = this->mStaticRoutes.end();
++ for (; iter < iterEnd; iter++)
++ {
++ if (iter->sourceID == sourceID &&
++ iter->sinkID == sinkID)
++ {
++ returnList.push_back(*iter);
++ break;
++ }
++ }
++ return E_OK;
++}
++
++/**
++ * Load audio policy and priority controller config. This SHOULD be called after
++ * all the plugins were loaded and initialized.
++ */
++am_Error_e ControlSenderPlugin::loadConfig()
++{
++ //get current library path - search: /proc/< getpid() >/maps
++ char proc_maps_file_name[256];
++ char line[4096];
++ char lib_name[256];
++ char *tmp;
++ pid_t pid = getpid();
++ int configGroup; /* 0-SourceClass,1-SinkClass,2-Gw,3-Route,4-Mixing */
++
++ sprintf(proc_maps_file_name, "/proc/%d/maps", pid);
++ FILE *proc_maps = fopen(proc_maps_file_name, "r");
++
++ lib_name[0] = '\0';
++
++ while (!feof(proc_maps))
++ {
++ char *cnt = fgets(line, 4095, proc_maps);
++ if (strlen(line) == 0 || line[0] == '#')
++ {
++ continue;
++ }
++ if (cnt == NULL) continue;
++ //tmp0 tmp1 tmp2 tmp3 tmp4 lib_name);
++ tmp = strtok(line, " ");//address-interval
++ if(tmp == NULL) continue;
++
++ tmp = strtok(NULL, " ");//rights
++ if(tmp == NULL) continue;
++
++ tmp = strtok(NULL, " ");//offset
++ if(tmp == NULL) continue;
++
++ strtok(NULL, " ");//dev
++ if(tmp == NULL) continue;
++
++ tmp = strtok(NULL, " \n");//inode
++ if(tmp == NULL) continue;
++
++ tmp = strtok(NULL, " \n");
++ if(tmp == NULL) continue;
++
++ strcpy(lib_name, tmp);
++ if ((lib_name != NULL) && (strstr(lib_name, LIBNAMECTL) >= lib_name))
++ {
++ strcpy(strrchr(lib_name, '/') + 1, CFGNAMECTL);
++ logInfo("CONTROLER - Load config file:", lib_name);
++
++ break;
++ }
++ }
++
++ fclose(proc_maps);
++
++ if (lib_name[0] == 0)
++ {
++ logError("Error loading config file:", lib_name);
++ return E_UNKNOWN;
++ }
++
++ FILE *config = fopen(lib_name, "r");
++
++ while (!feof(config))
++ {
++ fgets(line, 4095, config);
++
++ if (strlen(line) == 0 || line[0] == '#') continue;
++
++ char *tmp = strtok(line, "|\n:>");
++ if (tmp == NULL) continue;
++/*
++ * Set the current config group
++ */
++ if (strcmp("[SourceClass]", tmp) == 0)
++ {
++ configGroup = 0;
++ continue;
++ }
++ if (strcmp("[SinkClass]", tmp) == 0)
++ {
++ configGroup = 1;
++ continue;
++ }
++ if (strcmp("[Gateway]", tmp) == 0)
++ {
++ configGroup = 2;
++ continue;
++ }
++ if (strcmp("[Route]", tmp) == 0)
++ {
++ configGroup = 3;
++ continue;
++ }
++ if (strcmp("[Mixing]", tmp) == 0)
++ {
++ configGroup = 4;
++ continue;
++ }
++ switch (configGroup) /* 0-SourceClass,1-SinkClass,2-Route,3-Mixing */
++ {
++ case 0:
++/*
++ * SourceClass format: NAME|TYPE
++ * TYPE=Main|Interrupt
++ */
++ {
++ am_SourceClass_s l_newSourceClass;
++ l_newSourceClass.sourceClassID = 0;
++ l_newSourceClass.name = std::string(tmp);
++
++ if (this->mNames2SourceClasses[
++ l_newSourceClass.name
++ ].sourceClassID != 0)
++ {
++ //source class already added
++ break;
++ }
++
++ am_ClassProperty_s newClassProperty;
++ newClassProperty.classProperty = CP_GENIVI_SOURCE_TYPE;
++
++ char *tmp = strtok(NULL, "|\n");//type
++ if (tmp != NULL && (strcmp(tmp, "Interrupt") == 0))
++ {
++ newClassProperty.value = SOURCE_TYPE_INTERRUPT;
++ }
++ if (tmp != NULL && (strcmp(tmp, "Main") == 0))
++ {
++ newClassProperty.value = SOURCE_TYPE_MAIN;
++ }
++
++ l_newSourceClass.listClassProperties.push_back(newClassProperty);
++
++ am_Error_e e = this->mControlReceiveInterface->enterSourceClassDB(
++ l_newSourceClass.sourceClassID,
++ l_newSourceClass);
++ logInfo("ControlSenderPlugin::loadConfig: Source registered?? '", l_newSourceClass.name ,
++ "', id=", l_newSourceClass.sourceClassID, ", error=", e);
++ if (e != E_OK) break;
++
++ this->mNames2SourceClasses[l_newSourceClass.name] =
++ l_newSourceClass;
++
++ break;
++ }//end case 0 - source class
++ case 1:
++/*
++ * SinkClass: NAME|TYPE
++ * TYPE=Main; currently sink class type is unused:
++ * might be - audio output; recording app; phone sink
++ */
++ {
++ am_SinkClass_s l_newSinkClass;
++ l_newSinkClass.sinkClassID = 0;
++ l_newSinkClass.name = std::string(tmp);
++
++ if (this->mNames2SinksClasses[
++ l_newSinkClass.name
++ ].sinkClassID != 0)
++ {
++ //sink class already added
++ break;
++ }
++
++ am_ClassProperty_s newClassProperty;
++ newClassProperty.classProperty = CP_GENIVI_SOURCE_TYPE;
++
++ char *tmp = strtok(line, "|\n");//type
++ if (tmp != NULL && strcmp(tmp, "Main"))
++ {
++ newClassProperty.value = SINK_TYPE_MAIN;
++ }
++
++ l_newSinkClass.listClassProperties.push_back(newClassProperty);
++
++ am_Error_e e = mControlReceiveInterface->enterSinkClassDB(
++ l_newSinkClass,
++ l_newSinkClass.sinkClassID);
++
++ if (e != E_OK) break;
++
++ this->mNames2SinksClasses[l_newSinkClass.name] =
++ l_newSinkClass;
++
++ logInfo("New sink class name:", l_newSinkClass.name, "sinkClassId:", l_newSinkClass.sinkClassID);
++ std::cout << "New sink class name:" << l_newSinkClass.name << "sinkClassId:" << l_newSinkClass.sinkClassID << "\n";
++ break;
++ }//end case 1 - sink class
++ case 2:
++/*
++ * Gateway format: NAME:SOURCE>SINK
++ * SOURCE and SINK must be the name of an already registered Source/Sink
++ */
++ {
++ am_Gateway_s l_newGateway;
++ l_newGateway.gatewayID = 0;
++ l_newGateway.name = std::string(tmp);
++
++ if (this->mNames2Gateways[
++ l_newGateway.name
++ ].gatewayID!= 0)
++ {
++ //sink class already added
++ break;
++ }
++
++ tmp = strtok(NULL, ">");
++ l_newGateway.sinkID = this->mNames2Sinks[std::string(tmp)].sinkID;
++ l_newGateway.domainSinkID = this->mNames2Sinks[std::string(tmp)].domainID;
++ l_newGateway.listSinkFormats = this->mNames2Sinks[std::string(tmp)].listConnectionFormats;
++
++ tmp = strtok(NULL, "\n");
++ l_newGateway.sourceID = this->mNames2Sources[tmp].sourceID;
++ l_newGateway.domainSourceID = this->mNames2Sources[tmp].domainID;
++ l_newGateway.listSourceFormats = this->mNames2Sources[std::string(tmp)].listConnectionFormats;
++ l_newGateway.controlDomainID = l_newGateway.domainSourceID;
++ l_newGateway.convertionMatrix.push_back(true);
++
++ if (l_newGateway.sinkID == 0 ||
++ l_newGateway.sourceID == 0 ||
++ l_newGateway.domainSourceID == 0 ||
++ l_newGateway.domainSinkID == 0) break;
++
++
++ am_Error_e e = this->mControlReceiveInterface->enterGatewayDB(
++ l_newGateway,
++ l_newGateway.gatewayID);
++ if (e != E_OK) break;
++
++ this->mNames2Gateways[l_newGateway.name] = l_newGateway;
++ logInfo("New sink class name:", l_newGateway.name, "sinkId:",
++ l_newGateway.sinkID, "sourceID:", l_newGateway.sourceID);
++ break;
++ }//end case 2 - gateway
++ case 3:
++/*
++ * Route format: SourceClass>Sink>Source>...Sink>Source>Sink
++ * : SourceClass>Sink>Source>Gateway>Sink...>Source>Sink
++ */
++ {
++ //first element - source class
++ am_SourceClass_s routeSrcClass =
++ this->mNames2SourceClasses[std::string(tmp)];
++ //Do nothing if source class is not registered yet
++ if (routeSrcClass.sourceClassID == 0) break;
++
++ std::string prevElement;
++ bool prevElementWasSrc = true;
++ am_sourceID_t prevSrcID = 0;
++ am_sourceID_t prevSinkID = 0;
++ std::vector<am_RoutingElement_s> route;
++
++ while (tmp != NULL && *tmp != 0)
++ {
++ tmp = strtok(NULL, "\n>");
++ if (tmp == NULL || *tmp == 0) break;
++
++ std::string name(tmp);
++ if (prevElementWasSrc)
++ {
++ //next element should be a sink or a gw
++ am_Sink_s sink = this->mNames2Sinks[name];
++ if (sink.sinkID)
++ {
++ am_RoutingElement_s newRouteElement;
++ newRouteElement.sourceID = prevSrcID;
++ newRouteElement.sinkID = sink.sinkID;
++ newRouteElement.domainID= sink.domainID;
++ newRouteElement.connectionFormat = am::CF_GENIVI_STEREO;
++ //TODO: connection format should not be hardcoded
++ route.push_back(newRouteElement);
++ prevSinkID = newRouteElement.sinkID;
++ prevElementWasSrc = false;
++ }
++ else
++ {
++ am_Gateway_s gw = this->mNames2Gateways[name];
++ if (gw.gatewayID)
++ {
++ am_RoutingElement_s newRouteElement;
++ newRouteElement.sourceID = prevSrcID;
++ newRouteElement.sinkID = gw.sinkID;
++ newRouteElement.domainID= gw.domainSinkID;
++ newRouteElement.connectionFormat = am::CF_GENIVI_STEREO;
++ //TODO: connection format should not be hardcoded
++ route.push_back(newRouteElement);
++
++ prevSrcID = gw.sourceID;
++ prevElementWasSrc = true;
++ }
++ else
++ {
++ break;
++ }
++ }
++ }
++ else
++ {//prevElementWasSink
++ am_Source_s source = this->mNames2Sources[name];
++ if (source.sourceID)
++ {
++ prevElementWasSrc = true;
++ prevSrcID = source.sourceID;
++ }
++ else
++ {
++ break;
++ }
++
++ }
++ }
++
++ //the route should end with a Sink
++ if (!prevElementWasSrc)
++ {
++ std::vector<am_Source_s> allSources;
++ this->mControlReceiveInterface->getListSources(allSources);
++ for (unsigned int i = 0; i < allSources.size(); i++)
++ {
++ if (allSources[i].sourceClassID ==
++ routeSrcClass.sourceClassID)
++ {
++ am_Route_s newRoute;
++ newRoute.sinkID = prevSinkID;
++ newRoute.sourceID = allSources[i].sourceID;
++ newRoute.route = route;
++
++ am_RoutingElement_s firstRouteElement = newRoute.route[0];
++ firstRouteElement.sourceID = newRoute.sourceID;
++ newRoute.route[0] = firstRouteElement;
++
++ //check if route already exists
++ bool routeAlreadyExists = false;
++ for (unsigned int m = 0; m < this->mStaticRoutes.size(); m++)
++ {
++ if (this->mStaticRoutes[m].sourceID == newRoute.sourceID &&
++ this->mStaticRoutes[m].sinkID == newRoute.sinkID)
++ {
++ routeAlreadyExists = true;
++ }
++ }
++ if (!routeAlreadyExists)
++ {
++ this->mStaticRoutes.push_back(newRoute);
++ }
++ }
++ }
++ }
++ else
++ {
++ logError("Wrong route format:", line);
++ }
++ break;
++ }//end case 3 - route
++ case 4:
++/*
++ * Mixing: SourceClass>ACTIONS
++ */
++ {
++ am_SourceClass_s mixingSrcClass =
++ this->mNames2SourceClasses[std::string(tmp)];
++ if (mixingSrcClass.sourceClassID == 0) break;
++ std::vector<am_ConnectAction_s> actions;
++ while (tmp != NULL && *tmp != 0)
++ {
++ am_ConnectAction_s newAction;
++ tmp = strtok(NULL, "\n>:|");//activeSourceClass
++ if (tmp == NULL)
++ {
++ logError("Wrong active source class");
++ break;
++ }
++
++ am_SourceClass_s activeSrcClass =
++ this->mNames2SourceClasses[std::string(tmp)];
++ if (activeSrcClass.sourceClassID == 0)
++ {
++ logError("Wrong active source class - no ID");
++ break;
++ }
++
++ tmp = strtok(NULL, "\n>:|");//activeSink
++ if (tmp == NULL)
++ {
++ logError("Wrong active sink");
++ break;
++ }
++
++ am_Sink_s activeSink =
++ this->mNames2Sinks[std::string(tmp)];
++ if (activeSink.sinkID == 0)
++ {
++ logError("Wrong active sink - no ID");
++ break;
++ }
++
++ tmp = strtok(NULL, "\n>:|");//actionType
++ if (tmp == NULL)
++ {
++ logError("Wrong action type");
++ break;
++ }
++
++ am_ActionType_e actionType = AT_DISCONNECT;
++ if (strcmp("Mute", tmp) == 0)
++ actionType = AT_MUTE;
++ else if (strcmp("Unmute", tmp) == 0)
++ actionType = AT_UNMUTE;
++ else if (strcmp("Pause", tmp) == 0)
++ actionType = AT_PAUSE;
++ else if (strcmp("Resume", tmp) == 0)
++ actionType = AT_RESUME;
++ else if (strcmp("DecreaseVolume", tmp) == 0)
++ actionType = AT_VOLUME_DECREASE;
++ else if (strcmp("IncreaseVolume", tmp) == 0)
++ actionType = AT_VOLUME_DECREASE;
++ else if (strcmp("Disconnect", tmp) == 0)
++ actionType = AT_DISCONNECT;
++ else
++ {
++ logError("Wrong action type - enum");
++ break;
++ }
++
++ tmp = strtok(NULL, "\n>:|");//targetDomain
++ if (tmp == NULL)
++ {
++ logError("Target domain is wrong");
++ break;
++ }
++
++ am_Domain_s targetDomain =
++ this->mNames2Domains[std::string(tmp)];
++
++ if (targetDomain.domainID == 0)
++ {
++ logError("Target domain is wrong - no ID");
++ break;
++ }
++
++ am_ActionTarget_e targetElement;
++ tmp = strtok(NULL, "\n>:|");//targetElement
++ if (strcmp("Source", tmp) == 0)
++ targetElement = ATG_SOURCE;
++ else if (strcmp("Sink", tmp) == 0)
++ targetElement = ATG_SINK;
++ else
++ {
++ logError("Target element is wrong");
++ break;
++ }
++
++ newAction.activeSourceClassID = activeSrcClass.sourceClassID;
++ newAction.activeSinkID = activeSink.sinkID;
++ newAction.actionType = actionType;
++ newAction.targetDomain = targetDomain.domainID;
++ newAction.targetElement = targetElement;
++ actions.push_back(newAction);
++ }
++ if (actions.size() > 0)
++ {
++ am_MixingRule_s newMixingRule;
++ newMixingRule.mixingSourceClassID = mixingSrcClass.sourceClassID;
++ newMixingRule.actions = actions;
++ //do not add duplicates
++ bool ruleAlreadyExists = false;
++ for (unsigned int n = 0; n < this->mMixingRules.size(); n++)
++ {
++ if (this->mMixingRules[n].mixingSourceClassID == newMixingRule.mixingSourceClassID)
++ {
++ //TODO: is it necessary to compare the actions, too?
++ ruleAlreadyExists = true;
++ break;
++ }
++ }
++ if (!ruleAlreadyExists)
++ {
++ this->mMixingRules.push_back(newMixingRule);
++ }
++ }
++ break;
++ }//end case 4: - mixing
++ }
++ }
++
++
++ std::vector<am_Route_s>::iterator iter = mStaticRoutes.begin();
++ std::vector<am_Route_s>::iterator iterEnd = mStaticRoutes.end();
++ for (; iter != iterEnd; iter++)
++ {
++ logInfo("{BEGIN Add route: STATRT:SourceID:", iter->sourceID, "-> END:SinkID:", iter->sinkID);
++
++ std::vector<am_RoutingElement_s>::iterator iter1 = iter->route.begin();
++ std::vector<am_RoutingElement_s>::iterator iter1End = iter->route.end();
++ for (; iter1 < iter1End; iter1++)
++ {
++ logInfo("Subroute: SourceID:", iter1->sourceID, "-> SinkID:", iter1->sinkID);
++ }
++ logInfo("END Add route}");
++ }
++
++ std::vector<am_MixingRule_s>::iterator iter1 = mMixingRules.begin();
++ std::vector<am_MixingRule_s>::iterator iter1End = mMixingRules.end();
++ for (; iter1 != iter1End; iter1++)
++ {
++ logInfo("{BEGIN Add mixing rule: STATRT:mixingSourceClassID:", iter1->mixingSourceClassID);
++
++ std::vector<am_ConnectAction_s>::iterator iter2 = iter1->actions.begin();
++ std::vector<am_ConnectAction_s>::iterator iter2End = iter1->actions.end();
++ for (; iter2 < iter2End; iter2++)
++ {
++ logInfo("Action: activeSourceClassID:", iter2->activeSourceClassID,
++ ";activeSinkID:", iter2->activeSinkID,
++ ";actionType:", iter2->actionType,
++ ";targetDomain:", iter2->targetDomain,
++ ";targetElement:", iter2->targetElement);
++ }
++ logInfo("END Add mixing rule}");
++ }
++
++ fclose(config);
++ return E_OK;
++}
++
++am_Error_e ControlSenderPlugin::getSourceInfo(
++ const am_sourceID_t sourceID,
++ am_Source_s &sourceData)
++{
++ std::vector<am_Source_s> listSources;
++ this->mControlReceiveInterface->getListSources(listSources);
++ std::vector<am_Source_s>::iterator iter = listSources.begin();
++ std::vector<am_Source_s>::iterator iterEnd = listSources.end();
++ for (; iter < iterEnd; iter++)
++ {
++ if (iter->sourceID == sourceID)
++ {
++ sourceData = *iter;
++ return E_OK;
++ }
++ }
++ return E_NON_EXISTENT;
++}
++
++am_Error_e ControlSenderPlugin::hookSystemUpdateSink(const am_sinkID_t sinkID, const am_sinkClass_t sinkClassID, const std::vector<am_SoundProperty_s>& listSoundProperties, const std::vector<am_CustomConnectionFormat_t>& listConnectionFormats, const std::vector<am_MainSoundProperty_s>& listMainSoundProperties)
++{
++ (void) sinkID;
++ (void) sinkClassID;
++ (void) listMainSoundProperties;
++ (void) listConnectionFormats;
++ (void) listSoundProperties;
++ return (E_NOT_USED);
++}
++
++am_Error_e ControlSenderPlugin::hookSystemUpdateSource(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)
++{
++ (void) sourceID;
++ (void) sourceClassID;
++ (void) listSoundProperties;
++ (void) listMainSoundProperties;
++ (void) listConnectionFormats;
++ return (E_NOT_USED);
++}
++
++am_Error_e ControlSenderPlugin::hookSystemUpdateGateway(const am_gatewayID_t gatewayID, const std::vector<am_CustomConnectionFormat_t>& listSourceConnectionFormats, const std::vector<am_CustomConnectionFormat_t>& listSinkConnectionFormats, const std::vector<bool>& convertionMatrix)
++{
++ (void) gatewayID;
++ (void) listSourceConnectionFormats;
++ (void) listSinkConnectionFormats;
++ (void) convertionMatrix;
++ return (E_NOT_USED);
++}
++
++void ControlSenderPlugin::cbAckSetVolumes(const am_Handle_s handle, const std::vector<am_Volumes_s>& listVolumes, const am_Error_e error)
++{
++ (void) handle;
++ (void) listVolumes;
++ (void) error;
++}
++
++void ControlSenderPlugin::cbAckSetSinkNotificationConfiguration(const am_Handle_s handle, const am_Error_e error)
++{
++ (void) handle;
++ (void) error;
++}
++
++
++void ControlSenderPlugin::cbAckSetSourceNotificationConfiguration(const am_Handle_s handle, const am_Error_e error)
++{
++ (void) handle;
++ (void) error;
++}
++
++
++void ControlSenderPlugin::hookSinkNotificationDataChanged(const am_sinkID_t sinkID, const am_NotificationPayload_s& payload)
++{
++ (void) sinkID;
++ (void) payload;
++}
++
++void ControlSenderPlugin::hookSourceNotificationDataChanged(const am_sourceID_t sourceID, const am_NotificationPayload_s& payload)
++{
++ (void) sourceID;
++ (void) payload;
++}
++
++am_Error_e ControlSenderPlugin::hookUserSetMainSinkNotificationConfiguration(const am_sinkID_t sinkID, const am_NotificationConfiguration_s& notificationConfiguration)
++{
++ (void) sinkID;
++ (void) notificationConfiguration;
++ return (E_NOT_USED);
++}
++
++am_Error_e ControlSenderPlugin::hookUserSetMainSourceNotificationConfiguration(const am_sourceID_t sourceID, const am_NotificationConfiguration_s& notificationConfiguration)
++{
++ (void) sourceID;
++ (void) notificationConfiguration;
++ return (E_NOT_USED);
++}
++
++void ControlSenderPlugin::hookSystemNodeStateChanged(const NsmNodeState_e NodeStateId)
++{
++ (void) NodeStateId;
++ //here you can process informations about the notestate
++}
++
++void ControlSenderPlugin::hookSystemNodeApplicationModeChanged(const NsmApplicationMode_e ApplicationModeId)
++{
++ (void) ApplicationModeId;
++}
++
++void ControlSenderPlugin::hookSystemSessionStateChanged(const std::string& sessionName, const NsmSeat_e seatID, const NsmSessionState_e sessionStateID)
++{
++ (void) sessionName;
++ (void) seatID;
++ (void) sessionStateID;
++}
++
++NsmErrorStatus_e ControlSenderPlugin::hookSystemLifecycleRequest(const uint32_t Request, const uint32_t RequestId)
++{
++ (void) Request;
++ (void) RequestId;
++ logInfo("CAmControlSenderBase::hookSystemLifecycleRequest request=",Request," requestID=",RequestId);
++ return (NsmErrorStatus_Error);
++}
++
+--
+1.7.9.5
+
diff --git a/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager/0001-Porting-Pulse-Routing-Interface-from-AM-v1.x-to-AM-v.patch b/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager/0001-Porting-Pulse-Routing-Interface-from-AM-v1.x-to-AM-v.patch
new file mode 100644
index 0000000..d01854d
--- /dev/null
+++ b/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager/0001-Porting-Pulse-Routing-Interface-from-AM-v1.x-to-AM-v.patch
@@ -0,0 +1,2342 @@
+From 68c0d6ef9b1070505c1664fe567948a9f661da28 Mon Sep 17 00:00:00 2001
+From: Adrian Scarlat <adrian.scarlat@windriver.com>
+Date: Thu, 21 Aug 2014 14:52:46 +0300
+Subject: [PATCH] Porting Pulse Routing Interface from AM v1.x to AM v3.0
+
+This Routing Interface is needed for any application that will
+be developed on top of AM and will use PulseAudio Sound Server
+to control the sources and sinks present on the system. It must
+be loaded by AM;
+
+The interface can be built by supplying cmake with the -DWITH_PULSE_ROUTING_PLUGIN=ON;
+There are two configuration files that are use for the moment:
+ 1. libPluginRoutingInterfacePULSE.conf - configuration file for
+ Pulse Routing Plugin; it will be loaded at runtime by the Pulse Routing Interface;
+ 2. genivi-amgr.pa - configuration file for PulseAudio;
+ It must be used to start PulseAudio daemon on the system;
+
+Changed files:
+ CMakeLists.txt
+
+Added new folders:
+ PluginRoutingInterfacePulse/
+
+Added new files:
+ PluginRoutingInterfacePulse/CMakeLists.txt
+ PluginRoutingInterfacePulse/README
+ PluginRoutingInterfacePulse/cmake/FindDBUS.cmake
+ PluginRoutingInterfacePulse/data/asound.conf
+ PluginRoutingInterfacePulse/data/client.conf
+ PluginRoutingInterfacePulse/data/daemon.conf
+ PluginRoutingInterfacePulse/data/genivi-amgr.pa
+ PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf
+ PluginRoutingInterfacePulse/include/RoutingSenderMainloopPULSE.h
+ PluginRoutingInterfacePulse/include/RoutingSenderPULSE.h
+ PluginRoutingInterfacePulse/src/RoutingSenderMainloopPULSE.cpp
+ PluginRoutingInterfacePulse/src/RoutingSenderPULSE.cpp
+
+Signed-off-by: Adrian Scarlat <adrian.scarlat@windriver.com>
+---
+ CMakeLists.txt | 24 +-
+ PluginRoutingInterfacePulse/CMakeLists.txt | 120 +++
+ PluginRoutingInterfacePulse/README | 11 +
+ PluginRoutingInterfacePulse/cmake/FindDBUS.cmake | 38 +
+ PluginRoutingInterfacePulse/data/asound.conf | 35 +
+ PluginRoutingInterfacePulse/data/client.conf | 22 +
+ PluginRoutingInterfacePulse/data/daemon.conf | 44 +
+ PluginRoutingInterfacePulse/data/genivi-amgr.pa | 68 ++
+ .../data/libPluginRoutingInterfacePULSE.conf | 59 ++
+ .../include/RoutingSenderMainloopPULSE.h | 97 ++
+ .../include/RoutingSenderPULSE.h | 131 +++
+ .../src/RoutingSenderMainloopPULSE.cpp | 610 +++++++++++++
+ .../src/RoutingSenderPULSE.cpp | 923 ++++++++++++++++++++
+ 13 files changed, 2176 insertions(+), 6 deletions(-)
+ create mode 100644 PluginRoutingInterfacePulse/CMakeLists.txt
+ create mode 100644 PluginRoutingInterfacePulse/README
+ create mode 100644 PluginRoutingInterfacePulse/cmake/FindDBUS.cmake
+ create mode 100644 PluginRoutingInterfacePulse/data/asound.conf
+ create mode 100644 PluginRoutingInterfacePulse/data/client.conf
+ create mode 100644 PluginRoutingInterfacePulse/data/daemon.conf
+ create mode 100644 PluginRoutingInterfacePulse/data/genivi-amgr.pa
+ create mode 100644 PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf
+ create mode 100644 PluginRoutingInterfacePulse/include/RoutingSenderMainloopPULSE.h
+ create mode 100644 PluginRoutingInterfacePulse/include/RoutingSenderPULSE.h
+ create mode 100644 PluginRoutingInterfacePulse/src/RoutingSenderMainloopPULSE.cpp
+ create mode 100644 PluginRoutingInterfacePulse/src/RoutingSenderPULSE.cpp
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 3e70e4f..48e7ae2 100755
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -95,7 +95,15 @@ OPTION ( WITH_NSM
+ OPTION ( WITH_DATABASE_STORAGE
+ "build with sqlite as in memory storage" OFF)
+
++OPTION( WITH_PULSE_ROUTING_PLUGIN
++ "Enable PULSE Audio routing plugin interface" OFF )
++
+ SET (WITH_COMMON_API_GEN ON CACHE INTERNAL "hide this!" FORCE)
++
++IF (WITH_PULSE_ROUTING_PLUGIN)
++ SET (WITH_DBUS_WRAPPER ON CACHE INTERNAL "hide this!" FORCE)
++ SET (WITH_CAPI_WRAPPER OFF CACHE INTERNAL "hide this!" FORCE)
++ENDIF (WITH_PULSE_ROUTING_PLUGIN)
+
+ IF (WITH_ENABLED_IPC STREQUAL "DBUS")
+ SET (WITH_DBUS_WRAPPER ON CACHE INTERNAL "hide this!" FORCE)
+@@ -239,12 +247,16 @@ if(WITH_PLUGIN_COMMAND)
+ endif(WITH_PLUGIN_COMMAND)
+
+ if(WITH_PLUGIN_ROUTING)
+- add_subdirectory (PluginRoutingInterfaceAsync)
+- if(WITH_DBUS_WRAPPER)
+- add_subdirectory (PluginRoutingInterfaceDbus)
+- elseif(WITH_CAPI_WRAPPER)
+- add_subdirectory (PluginRoutingInterfaceCAPI)
+- endif()
++ if (WITH_PULSE_ROUTING_PLUGIN)
++ add_subdirectory (PluginRoutingInterfacePulse)
++ else ()
++ add_subdirectory (PluginRoutingInterfaceAsync)
++ if(WITH_DBUS_WRAPPER)
++ add_subdirectory (PluginRoutingInterfaceDbus)
++ elseif(WITH_CAPI_WRAPPER)
++ add_subdirectory (PluginRoutingInterfaceCAPI)
++ endif()
++ endif()
+ endif(WITH_PLUGIN_ROUTING)
+
+ if(WITH_PLUGIN_CONTROL)
+diff --git a/PluginRoutingInterfacePulse/CMakeLists.txt b/PluginRoutingInterfacePulse/CMakeLists.txt
+new file mode 100644
+index 0000000..b889f6a
+--- /dev/null
++++ b/PluginRoutingInterfacePulse/CMakeLists.txt
+@@ -0,0 +1,120 @@
++############################################################################
++# SPDX license identifier: MPL-2.0
++#
++# Copyright (C) 2012-2014, Wind River Systems
++# Copyright (C) 2014, GENIVI Alliance
++#
++# This file is part of Pulse Audio Interface Routing Plugin.
++#
++# This Source Code Form is subject to the terms of the Mozilla Public
++# License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
++# file, You can obtain one at http://mozilla.org/MPL/2.0/.
++#
++# For further information see http://www.genivi.org/.
++#
++# List of changes:
++#
++# 21.08.2014, Adrian Scarlat, First version of the code;
++# Porting code from AM ver1.x to AM ver3.0;
++# Added Copyright and License information;
++############################################################################
++
++cmake_minimum_required(VERSION 2.6)
++
++PROJECT(PluginRoutingInterfacePULSE)
++
++set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic")
++set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
++FILE(GLOB pulseConf "${CMAKE_CURRENT_SOURCE_DIR}/README*")
++FIND_PACKAGE(PkgConfig)
++
++OPTION( WITH_DOCUMENTATION
++ "Build together with Doxygen Documentation" OFF )
++
++SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
++SET(PLUGINS_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin/plugins)
++set(LIBRARY_OUTPUT_PATH ${PLUGINS_OUTPUT_PATH}/routing)
++set(DOC_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/doc/RoutingPlugin)
++set(INCLUDES_FOLDER "include")
++SET(LIB_INSTALL_SUFFIX "audioManager")
++
++FIND_PACKAGE(DBUS REQUIRED)
++FIND_PATH(AUDIO_INCLUDE_FOLDER audiomanagertypes.h /usr/include)
++
++if(DEFINED AUDIO_INCLUDE_FOLDER)
++ message(STATUS "Found AudioManager include: ${AUDIO_INCLUDE_FOLDER}")
++else(DEFINED AUDIO_INCLUDE_FOLDER)
++ message(STATUS "Did not found AudioManager include!")
++endif(DEFINED AUDIO_INCLUDE_FOLDER)
++
++FILE(READ "${AUDIO_INCLUDE_FOLDER}/routing/IAmRoutingSend.h" VERSION_BUFFER LIMIT 6000)
++STRING(REGEX MATCH "RoutingSendVersion*.[^0-9]*[0-9]" LIB_INTERFACE_VERSION_STRING ${VERSION_BUFFER})
++STRING(REGEX REPLACE "[^0-9]" "" LIB_INTERFACE_VERSION ${LIB_INTERFACE_VERSION_STRING})
++MESSAGE(STATUS "Building against routing interface version ${LIB_INTERFACE_VERSION}")
++
++SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
++FIND_PACKAGE(DBUS REQUIRED)
++
++INCLUDE_DIRECTORIES(
++ ${CMAKE_SOURCE_DIR}
++ ${CMAKE_CURRENT_BINARY_DIR}
++ ${DBUS_INCLUDE_DIR}
++ ${DBUS_ARCH_INCLUDE_DIR}
++ ${AUDIO_INCLUDE_FOLDER}
++ ${INCLUDES_FOLDER}
++)
++
++# all source files go here
++file(GLOB PLUGINDBUS_SRCS_CXX "src/*.cpp")
++
++add_library(PluginRoutingInterfacePULSE SHARED ${PLUGINDBUS_SRCS_CXX})
++
++TARGET_LINK_LIBRARIES(PluginRoutingInterfacePULSE
++ pulse
++ ${DLT_LIBRARIES}
++ ${DBUS_LIBRARY}
++)
++
++IF(WITH_DOCUMENTATION)
++ file(MAKE_DIRECTORY ${DOC_OUTPUT_PATH})
++ configure_file(${DOXY_FILE} ${DOC_OUTPUT_PATH}/Doxyfile @ONLY IMMEDIATE)
++ add_custom_target (PluginRoutingInterfacePULSEDocs ALL
++ COMMAND ${DOXYGEN_EXECUTABLE} ${DOC_OUTPUT_PATH}/Doxyfile WORKING_DIRECTORY ${DOC_OUTPUT_PATH}
++ SOURCES ${PROJECT_BINARY_DIR} ${DOC_OUTPUT_PATH}/Doxyfile
++ )
++ENDIF(WITH_DOCUMENTATION)
++
++INSTALL(TARGETS PluginRoutingInterfacePULSE
++ DESTINATION "lib/${LIB_INSTALL_SUFFIX}/routing"
++ PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ
++ COMPONENT sampleplugins
++)
++
++INSTALL(FILES data/libPluginRoutingInterfacePULSE.conf
++ DESTINATION "lib/${LIB_INSTALL_SUFFIX}/routing"
++ PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ
++ COMPONENT sampleplugins
++)
++INSTALL(FILES data/client.conf
++ DESTINATION "lib/${LIB_INSTALL_SUFFIX}/routing"
++ PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ
++ COMPONENT sampleplugins
++)
++INSTALL(FILES data/daemon.conf
++ DESTINATION "lib/${LIB_INSTALL_SUFFIX}/routing"
++ PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ
++ COMPONENT sampleplugins
++)
++INSTALL(FILES data/asound.conf
++ DESTINATION "lib/${LIB_INSTALL_SUFFIX}/routing"
++ PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ
++ COMPONENT sampleplugins
++)
++INSTALL(FILES data/genivi-amgr.pa
++ DESTINATION "lib/${LIB_INSTALL_SUFFIX}/routing"
++ PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ
++ COMPONENT sampleplugins
++)
++
++CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf ${PLUGINS_OUTPUT_PATH}/routing/libPluginRoutingInterfacePULSE.conf )
++CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/PluginRoutingInterfacePulse/data/genivi-amgr.pa ${PLUGINS_OUTPUT_PATH}/routing/genivi-amgr.pa )
+diff --git a/PluginRoutingInterfacePulse/README b/PluginRoutingInterfacePulse/README
+new file mode 100644
+index 0000000..7c9b082
+--- /dev/null
++++ b/PluginRoutingInterfacePulse/README
+@@ -0,0 +1,11 @@
++Description of PluginRoutingInterfacePulse
++------------------------------------------
++- The PluginRoutingInterfacePulse is used by the AM PoC application to communicate with Pulse
++ present on the system on which the AM PoC application will be deployed;
++
++Build intstructions
++-------------------
++- To build the Pulse Routing Plugin pass the following option to CMake: -DWITH_PULSE_ROUTING_PLUGIN=ON
++
++
++
+diff --git a/PluginRoutingInterfacePulse/cmake/FindDBUS.cmake b/PluginRoutingInterfacePulse/cmake/FindDBUS.cmake
+new file mode 100644
+index 0000000..9a24f18
+--- /dev/null
++++ b/PluginRoutingInterfacePulse/cmake/FindDBUS.cmake
+@@ -0,0 +1,38 @@
++############################################################################
++# SPDX license identifier: MPL-2.0
++#
++# Copyright (C) 2012-2014, Wind River Systems
++# Copyright (C) 2014, GENIVI Alliance
++#
++# This file is part of Pulse Audio Interface Routing Plugin.
++#
++# This Source Code Form is subject to the terms of the Mozilla Public
++# License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
++# file, You can obtain one at http://mozilla.org/MPL/2.0/.
++#
++# For further information see http://www.genivi.org/.
++#
++# List of changes:
++#
++# 21.08.2014, Adrian Scarlat, First version of the code;
++# Porting code from AM ver1.x to AM ver3.0;
++# Added Copyright and License information;
++############################################################################
++
++FIND_PATH(DBUS_INCLUDE_DIR dbus/dbus.h /usr/include/dbus-1.0)
++FIND_PATH(DBUS_ARCH_INCLUDE_DIR dbus/dbus-arch-deps.h /usr/lib/dbus-1.0/include /usr/lib/*/dbus-1.0/include )
++FIND_LIBRARY(DBUS_LIBRARY NAMES dbus-1 PATHS /lib)
++
++SET( DBUS_FOUND "NO" )
++IF(DBUS_LIBRARY)
++ SET( DBUS_FOUND "YES" )
++ message(STATUS "Found DBUS libs: ${DBUS_LIBRARY}")
++ message(STATUS "Found DBUS include: ${DBUS_INCLUDE_DIR}")
++ message(STATUS "Found DBUS arch dependent include: ${DBUS_ARCH_INCLUDE_DIR}")
++ENDIF(DBUS_LIBRARY)
++
++MARK_AS_ADVANCED(
++ DBUS_INCLUDE_DIR
++ DBUS_ARCH_INCLUDE_DIR
++ DBUS_LIBRARY
++)
+diff --git a/PluginRoutingInterfacePulse/data/asound.conf b/PluginRoutingInterfacePulse/data/asound.conf
+new file mode 100644
+index 0000000..e17f837
+--- /dev/null
++++ b/PluginRoutingInterfacePulse/data/asound.conf
+@@ -0,0 +1,35 @@
++############################################################################
++# SPDX license identifier: MPL-2.0
++#
++# Copyright (C) 2012-2014, Wind River Systems
++# Copyright (C) 2014, GENIVI Alliance
++#
++# This file is part of Pulse Audio Interface Routing Plugin.
++#
++# This Source Code Form is subject to the terms of the Mozilla Public
++# License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
++# file, You can obtain one at http://mozilla.org/MPL/2.0/.
++#
++# For further information see http://www.genivi.org/.
++#
++# List of changes:
++#
++# 21.08.2014, Adrian Scarlat, First version of the code;
++# Porting code from AM ver1.x to AM ver3.0;
++# Added Copyright and License information;
++############################################################################
++pcm.pulse {
++ type pulse
++}
++
++ctl.pulse {
++ type pulse
++}
++
++pcm.!default {
++ type pulse
++}
++
++ctl.!default {
++ type pulse
++}
+diff --git a/PluginRoutingInterfacePulse/data/client.conf b/PluginRoutingInterfacePulse/data/client.conf
+new file mode 100644
+index 0000000..c607b8d
+--- /dev/null
++++ b/PluginRoutingInterfacePulse/data/client.conf
+@@ -0,0 +1,22 @@
++############################################################################
++# SPDX license identifier: MPL-2.0
++#
++# Copyright (C) 2012-2014, Wind River Systems
++# Copyright (C) 2014, GENIVI Alliance
++#
++# This file is part of Pulse Audio Interface Routing Plugin.
++#
++# This Source Code Form is subject to the terms of the Mozilla Public
++# License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
++# file, You can obtain one at http://mozilla.org/MPL/2.0/.
++#
++# For further information see http://www.genivi.org/.
++#
++# List of changes:
++#
++# 21.08.2014, Adrian Scarlat, First version of the code;
++# Porting code from AM ver1.x to AM ver3.0;
++# Added Copyright and License information;
++############################################################################
++
++default-server = 127.0.0.1
+diff --git a/PluginRoutingInterfacePulse/data/daemon.conf b/PluginRoutingInterfacePulse/data/daemon.conf
+new file mode 100644
+index 0000000..0a291d4
+--- /dev/null
++++ b/PluginRoutingInterfacePulse/data/daemon.conf
+@@ -0,0 +1,44 @@
++# SPDX license identifier: LGPL-2.0
++#
++# This file is part of PulseAudio.
++#
++# PulseAudio is free software; you can redistribute it and/or modify
++# it under the terms of the GNU Lesser General Public License as published by
++# the Free Software Foundation; either version 2 of the License, or
++# (at your option) any later version.
++#
++# PulseAudio is distributed in the hope that it will be useful, but
++# WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++# General Public License for more details.
++#
++# You should have received a copy of the GNU Lesser General Public License
++# along with PulseAudio; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
++# USA.
++
++## Configuration file for the PulseAudio daemon. See pulse-daemon.conf(5) for
++## more information. Default values are commented out. Use either ; or # for
++## commenting.
++#
++# For further information see http://www.genivi.org/.
++#
++# List of changes:
++#
++# 21.08.2014, Adrian Scarlat, First version of the code;
++# Porting code from AM ver1.x to AM ver3.0;
++############################################################################
++enable-shm=yes
++shm-size-bytes=8388608 #8MB # setting this 0 will use the system-default, usually 64 MiB
++exit-idle-time=-1
++high-priority=yes
++nice-level=-11
++realtime-scheduling=yes
++realtime-priority=5
++resample-method=trivial
++flat-volumes=no
++default-sample-format = s16le
++default-sample-rate = 44100
++default-sample-channels = 2
++default-fragment-size-msec = 3
++default-fragments = 8
+diff --git a/PluginRoutingInterfacePulse/data/genivi-amgr.pa b/PluginRoutingInterfacePulse/data/genivi-amgr.pa
+new file mode 100644
+index 0000000..596ec19
+--- /dev/null
++++ b/PluginRoutingInterfacePulse/data/genivi-amgr.pa
+@@ -0,0 +1,68 @@
++############################################################################
++# SPDX license identifier: MPL-2.0
++#
++# Copyright (C) 2012-2014, Wind River Systems
++# Copyright (C) 2014, GENIVI Alliance
++#
++# This file is part of Pulse Audio Interface Routing Plugin.
++#
++# This Source Code Form is subject to the terms of the Mozilla Public
++# License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
++# file, You can obtain one at http://mozilla.org/MPL/2.0/.
++#
++# For further information see http://www.genivi.org/.
++#
++# List of changes:
++#
++# 21.08.2014, Adrian Scarlat, First version of the code;
++# Porting code from AM ver1.x to AM ver3.0;
++# Added Copyright and License information;
++############################################################################
++.nofail
++
++.fail
++
++### Automatically restore the volume of streams and devices
++load-module module-device-restore
++load-module module-stream-restore
++load-module module-card-restore
++
++### Automatically load driver modules depending on the hardware available
++.ifexists module-udev-detect.so
++load-module module-udev-detect
++.else
++### Use the static hardware detection module (for systems that lack udev support)
++load-module module-detect
++.endif
++
++### Load several protocols
++.ifexists module-esound-protocol-unix.so
++load-module module-esound-protocol-unix
++.endif
++load-module module-native-protocol-unix
++
++### Automatically restore the default sink/source when changed by the user
++### during runtime
++### NOTE: This should be loaded as early as possible so that subsequent modules
++### that look up the default sink/source get the right value
++load-module module-default-device-restore
++
++### Automatically move streams to the default sink if the sink they are
++### connected to dies, similar for sources
++load-module module-rescue-streams
++
++### Make sure we always have a sink around, even if it is a null sink.
++load-module module-always-sink
++
++### Modules to allow autoloading of filters (such as echo cancellation)
++### on demand. module-filter-heuristics tries to determine what filters
++### make sense, and module-filter-apply does the heavy-lifting of
++### loading modules and rerouting streams.
++load-module module-filter-heuristics
++load-module module-filter-apply
++
++#Make the specified sink (identified by its symbolic name) the default sink.
++set-default-sink 0
++# Make the specified source (identified by its symbolic name) the default source.
++set-default-source 0
++
+diff --git a/PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf b/PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf
+new file mode 100644
+index 0000000..43fae53
+--- /dev/null
++++ b/PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf
+@@ -0,0 +1,59 @@
++############################################################################
++# SPDX license identifier: MPL-2.0
++#
++# Copyright (C) 2012-2014, Wind River Systems
++# Copyright (C) 2014, GENIVI Alliance
++#
++# This file is part of Pulse Audio Interface Routing Plugin.
++#
++# This Source Code Form is subject to the terms of the Mozilla Public
++# License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
++# file, You can obtain one at http://mozilla.org/MPL/2.0/.
++#
++# For further information see http://www.genivi.org/.
++#
++# List of changes:
++#
++# 21.08.2014, Adrian Scarlat, First version of the code;
++# Porting code from AM ver1.x to AM ver3.0;
++# Added Copyright and License information;
++############################################################################
++#
++# config line format:
++#
++# TYPE|PULSE TYPE|CLASS|NAME|PROPERTY_NAME|PROPERTY_VALUE
++#
++# TYPE="Source" or "Sink"
++#
++# PULSE_TYPE="Sink Input" or "Source" for TYPE="Source"
++# PULSE_TYPE="Source Output" or "Sink" for TYPE="Sink"
++#
++# NAME=Any string not containing separator |
++#
++# CLASS=Any string not containing separator | - name should be consistent with Controller config
++#
++# PROPERTY_NAME=a Pulse recognized element property, e.g. "application.process.binary" or "device.class"
++# for PULSE_TYPE=Source or PULSE_TYPE=Sink, take a look ad device string
++# PROPERTY_VALUE=any string corresponding to the property value
++#
++# TODO: provide support for multiple attributes filtering
++# TODO: comment at eof is mandatory! this looks like a bug for the moment
++####################
++# Sources
++####################
++## Test purpose audio sources
++Source|Sink Input|Entertainment|Media player|application.process.binary|MediaServiceGlueLogic
++Source|Sink Input|Entertainment|Browser|application.process.binary|WebRenderingGlueLogic
++Source|Sink Input|Navigation|Navigation|application.name|Test AMGRv2 TTS
++## Real audio sources
++#Source|Sink Input|Entertainment|Browser|application.process.binary|PilotHMI
++#Source|Sink Input|Entertainment|Gst Launch command line player|application.process.binary|gst-launch-0.10
++#Source|Sink Input|Navigation|ALSA command line player|application.process.binary|aplay
++#Source|Sink Input|Entertainment|Microphone Loopback|media.icon_name|audio-input-microphone
++#Source|Source|Entertainment|Microphone|device.string|hw:0,1
++####################
++# Sinks
++####################
++Sink|Sink|HifiAudio|Alsa Primary|ievice.string|hw:0,1
++Sink|Sink|HifiAudio|Alsa Secondary|device.string|hw:0,0
++# !END
+diff --git a/PluginRoutingInterfacePulse/include/RoutingSenderMainloopPULSE.h b/PluginRoutingInterfacePulse/include/RoutingSenderMainloopPULSE.h
+new file mode 100644
+index 0000000..ff0c1d8
+--- /dev/null
++++ b/PluginRoutingInterfacePulse/include/RoutingSenderMainloopPULSE.h
+@@ -0,0 +1,97 @@
++/**
++ * SPDX license identifier: MPL-2.0
++ *
++ * Copyright (C) 2011-2014, Wind River Systems
++ * Copyright (C) 2014, GENIVI Alliance
++ *
++ * This file is part of Pulse Audio Interface Routing Plugin.
++ *
++ * This Source Code Form is subject to the terms of the Mozilla Public
++ * License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
++ *
++ * For further information see http://www.genivi.org/.
++ *
++ * List of changes:
++ *
++ * 21.08.2014, Adrian Scarlat, First version of the code;
++ * Porting code from AM ver1.x to AM ver3.0;
++ * Added Copyright and License information;
++ */
++
++#ifndef ROUTINGSENDERMAINLOPPPULSE_H_
++#define ROUTINGSENDERMAINLOPPPULSE_H_
++
++/* Defines */
++/* In PulseAudio the volume value range from 0 (silence) to 0x10000U=65536 ("maximum" sensible volume). */
++#define MAX_PULSE_VOLUME (0x10000U)
++
++bool routing_sender_create_mainloop(void *thiz);
++
++void * routing_sender_start_mainloop(void *thiz);
++
++void routing_sender_get_sink_info_callback(
++ pa_context *c,
++ const pa_sink_info *i,
++ int is_last, void *thiz);
++
++void routing_sender_context_state_callback(pa_context *c, void *thiz);
++
++bool routing_sender_get_source_info(pa_context *c, void *thiz);
++
++void routing_sender_pa_event_callback(
++ pa_context *c,
++ pa_subscription_event_type_t t,
++ uint32_t idx, void *thiz);
++
++void routing_sender_get_sink_input_info_callback(
++ pa_context *c,
++ const pa_sink_input_info *i,
++ int eol,
++ void *thiz);
++
++void routing_sender_get_source_output_info_callback(
++ pa_context *c,
++ const pa_source_output_info *i,
++ int eol,
++ void *userdata);
++
++bool routing_sender_move_sink_input(
++ pa_context *c,
++ uint32_t sink_input_index,
++ uint32_t sink_index,
++ void *thiz);
++
++bool routing_sender_move_source_output(
++ pa_context *c,
++ uint32_t source_output_index,
++ uint32_t source_index,
++ void *thiz);
++
++bool routing_sender_sink_input_volume_ramp(
++ pa_context *c,
++ uint32_t sink_input_index,
++ uint32_t crt_volume,
++ uint32_t volume,
++ uint16_t ramp_time,
++ void *thiz);
++
++bool routing_sender_sink_input_volume(
++ pa_context *c,
++ uint32_t sink_input_index,
++ uint32_t volume,
++ void *thiz);
++
++bool routing_sender_sink_input_mute(
++ pa_context *c,
++ uint32_t sink_input_index,
++ bool mute,
++ void *thiz);
++
++bool routing_sender_sink_volume(
++ pa_context *c,
++ uint32_t sink_index,
++ uint32_t volume,
++ void *thiz);
++
++#endif
+diff --git a/PluginRoutingInterfacePulse/include/RoutingSenderPULSE.h b/PluginRoutingInterfacePulse/include/RoutingSenderPULSE.h
+new file mode 100644
+index 0000000..0fc2197
+--- /dev/null
++++ b/PluginRoutingInterfacePulse/include/RoutingSenderPULSE.h
+@@ -0,0 +1,131 @@
++/**
++ * SPDX license identifier: MPL-2.0
++ *
++ * Copyright (C) 2011-2014, Wind River Systems
++ * Copyright (C) 2014, GENIVI Alliance
++ *
++ * This file is part of Pulse Audio Interface Routing Plugin.
++ *
++ * This Source Code Form is subject to the terms of the Mozilla Public
++ * License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
++ *
++ * For further information see http://www.genivi.org/.
++ *
++ * List of changes:
++ *
++ * 21.08.2014, Adrian Scarlat, First version of the code;
++ * Porting code from AM ver1.x to AM ver3.0;
++ * Added Copyright and License information;
++ */
++
++#ifndef ROUTINGSENDERPULSE_H_
++#define ROUTINGSENDERPULSE_H_
++
++/* Includes */
++#include "routing/IAmRoutingSend.h"
++#include <signal.h>
++#include <string.h>
++#include <errno.h>
++#include <unistd.h>
++#include <assert.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <limits.h>
++#include <getopt.h>
++#include <locale.h>
++#include <map>
++#include <pulse/pulseaudio.h>
++
++using namespace am;
++
++struct RoutingSenderPULSEConnection
++{
++ am_connectionID_t connectionID;
++ am_sourceID_t sourceID;
++ am_sinkID_t sinkID;
++ am_Handle_s handle;
++ bool pending;
++};
++
++
++struct RoutingSenderPULSESourceSinkConfig
++{
++ am_Source_s source;
++ am_Sink_s sink;
++
++ std::string name;//e.g. {"gst-launch-0.10", "mono", "aplay"};
++ std::string clazz;//e.g. {"Entertainment", "Navigation", "TTS"};
++ std::string propertyName;//e.g. {"application.process.binary", "application.process.binary", "application.process.app"};
++ std::string propertyValue;//
++};
++
++
++/* Prototypes */
++class RoutingSenderPULSE : public IAmRoutingSend
++{
++public:
++ RoutingSenderPULSE(pa_context *p_paContext);
++ ~RoutingSenderPULSE();
++
++ am::am_Error_e startupInterface(am::IAmRoutingReceive* p_routingReceiver);
++ void setRoutingReady(uint16_t handle);
++ void setRoutingRundown(uint16_t handle);
++ am_Error_e asyncAbort(const am_Handle_s handle);
++ am_Error_e asyncConnect(const am_Handle_s handle, const am_connectionID_t connectionID, const am_sourceID_t sourceID, const am_sinkID_t sinkID, const am_CustomConnectionFormat_t connectionFormat);
++ am_Error_e asyncDisconnect(const am_Handle_s handle, const am_connectionID_t connectionID);
++ am_Error_e asyncSetSinkVolume(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);
++ am_Error_e asyncSetSourceVolume(const am_Handle_s handle, const am_sourceID_t sourceID, const am_volume_t volume, const am_CustomRampType_t ramp, const am_time_t time);
++ am_Error_e asyncSetSourceState(const am_Handle_s handle, const am_sourceID_t sourceID, const am_SourceState_e state);
++ am_Error_e asyncSetSinkSoundProperties(const am_Handle_s handle, const am_sinkID_t sinkID, const std::vector<am_SoundProperty_s>& listSoundProperties);
++ am_Error_e asyncSetSinkSoundProperty(const am_Handle_s handle, const am_sinkID_t sinkID, const am_SoundProperty_s& soundProperty);
++ am_Error_e asyncSetSourceSoundProperties(const am_Handle_s handle, const am_sourceID_t sourceID, const std::vector<am_SoundProperty_s>& listSoundProperties);
++ am_Error_e asyncSetSourceSoundProperty(const am_Handle_s handle, const am_sourceID_t sourceID, const am_SoundProperty_s& soundProperty);
++ am_Error_e asyncCrossFade(const am_Handle_s handle, const am_crossfaderID_t crossfaderID, const am_HotSink_e hotSink, const am_CustomRampType_t rampType, const am_time_t time);
++ am_Error_e setDomainState(const am_domainID_t domainID, const am_DomainState_e domainState);
++ am_Error_e returnBusName(std::string& BusName) const;
++ void getInterfaceVersion(std::string& out_ver) const;
++
++ void setPAContext(pa_context *p_paContext) {
++ this->m_paContext = p_paContext;
++ }
++ am_Error_e asyncSetVolumes(const am_Handle_s handle, const std::vector<am_Volumes_s>& listVolumes);
++ am_Error_e asyncSetSinkNotificationConfiguration(const am_Handle_s handle, const am_sinkID_t sinkID, const am_NotificationConfiguration_s& notificationConfiguration);
++ am_Error_e asyncSetSourceNotificationConfiguration(const am_Handle_s handle, const am_sourceID_t sourceID, const am_NotificationConfiguration_s& notificationConfiguration);
++//Pulse Audio callbacks
++ void getSinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata);
++ void getSourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *userdata);
++ void getSinkInputInfoCallback(pa_context *c, const pa_sink_input_info *i, void *userdata);
++ void getSourceOutputInfoCallback(pa_context *c, const pa_source_output_info *i, void *userdata);
++
++private:
++ void loadConfig();
++
++ am_Domain_s m_domain;
++
++ std::vector<RoutingSenderPULSESourceSinkConfig> m_sinks;
++ std::vector<RoutingSenderPULSESourceSinkConfig> m_sources;
++
++ std::map<uint16_t, uint32_t> m_sourceToPASinkInput;
++ std::map<uint16_t, uint32_t> m_sourceToPASource;
++ std::map<uint16_t, uint32_t> m_sinkToPASourceOutput;
++ std::map<uint16_t, uint32_t> m_sinkToPASink;
++
++ uint16_t m_paSinkNullIndex;
++ uint16_t m_paSourceNullIndex;
++
++ IAmRoutingReceive *m_routingReceiver;
++ pa_context *m_paContext;
++
++/**
++ * Maintain a list of pending actions: there is a high change that the HMI first call connect,
++ * then the audio client start to play, therefore, sink-input is not yet created by the time "connect" method was called.
++ * same for volume? not sure - probably the sink input is created when the user change the volume.
++ * same for disconnect? not sure - probably the sink input was already created by the time the user is calling disconnect
++ */
++ std::vector<RoutingSenderPULSEConnection> m_activeConnections;
++ std::map<uint16_t, uint16_t> m_sinkToVolume;
++ std::map<uint16_t, uint16_t> m_sourceToVolume;
++};
++
++#endif
+diff --git a/PluginRoutingInterfacePulse/src/RoutingSenderMainloopPULSE.cpp b/PluginRoutingInterfacePulse/src/RoutingSenderMainloopPULSE.cpp
+new file mode 100644
+index 0000000..0def61f
+--- /dev/null
++++ b/PluginRoutingInterfacePulse/src/RoutingSenderMainloopPULSE.cpp
+@@ -0,0 +1,610 @@
++/**
++ * SPDX license identifier: MPL-2.0
++ *
++ * Copyright (C) 2011-2014, Wind River Systems
++ * Copyright (C) 2014, GENIVI Alliance
++ *
++ * This file is part of Pulse Audio Interface Routing Plugin.
++ *
++ * This Source Code Form is subject to the terms of the Mozilla Public
++ * License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
++ *
++ * For further information see http://www.genivi.org/.
++ *
++ * List of changes:
++ *
++ * 21.08.2014, Adrian Scarlat, First version of the code;
++ * Porting code from AM ver1.x to AM ver3.0;
++ * Added Copyright and License information;
++ */
++
++/* Includes */
++
++#include <pthread.h>
++#include "shared/CAmDltWrapper.h"
++#include "RoutingSenderPULSE.h"
++#include "RoutingSenderMainloopPULSE.h"
++
++static pthread_t *p_thread;
++static pa_mainloop *main_loop;
++
++/* struct used for ramp_volume changing */
++typedef struct ramp_volume
++{
++ uint32_t sink_input_index;
++ uint32_t volume_ini;
++ uint32_t volume_end;
++ /* max ramp time in ms */
++ uint16_t ramp_max_time;
++ /* current delay between calls in ms */
++ uint16_t ramp_crt_elapsed;
++ /* aux used to calculate delay between calls */
++ timespec start_time;
++};
++/* map used for storing ramp_volume information for sources */
++std::map<uint32_t, ramp_volume> g_sinkInputId2rampVolume;
++
++/* Defines */
++#define QUIT_REASON_PA_DOWN 1
++#define QUIT_REASON_AMGR_DOWN 2
++
++DLT_IMPORT_CONTEXT(routingPulse)
++
++bool routing_sender_create_mainloop(void *thiz)
++{
++ if (!thiz) {
++ logError("Can not create an working thread for Pulse Audio without a Routing plugin\n");
++ return false;
++ }
++ p_thread = (pthread_t *) malloc(sizeof(pthread_t));
++ pthread_create(p_thread, NULL, routing_sender_start_mainloop, thiz);
++ return true;
++}
++
++
++void* routing_sender_start_mainloop(void *thiz)
++{
++ pa_mainloop_api *mainloop_api;
++ pa_proplist *proplist;
++ pa_context *context;
++ int ret = 0;
++
++ if (!thiz) {
++ logError("Can not create an working thread for Pulse Audio without a Routing plugin\n");
++ return NULL;
++ }
++
++ proplist = pa_proplist_new();
++ if (pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "RoutingPULSE") < 0) {
++ logError("Can not prepare Pulse Audio main loop: pa_proplist_sets\n");
++ goto end;
++ }
++
++ if (!(main_loop = pa_mainloop_new()))
++ {
++ logError("Can not prepare Pulse Audio main loop: pa_mainloop_new\n");
++ goto end;
++ }
++
++ if (!(mainloop_api = pa_mainloop_get_api(main_loop)))
++ {
++ logError("Can not prepare Pulse Audio main loop: pa_mainloop_get_api\n");
++ goto end;
++ }
++
++ if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist)))
++ {
++ logError("Can not prepare Pulse Audio main loop: pa_context_new_with_proplist\n");
++ goto end;
++ }
++
++ pa_context_set_state_callback(context, routing_sender_context_state_callback, thiz);
++
++ if (pa_context_connect(context, NULL, pa_context_flags_t(0), NULL) < 0)
++ {
++ logError("Can not prepare Pulse Audio main loop: pa_context_new_with_proplist\n");
++ goto end;
++ }
++ ((RoutingSenderPULSE *) thiz)->setPAContext(context);
++
++ pa_mainloop_run(main_loop, &ret);
++
++end:
++ if (ret == QUIT_REASON_PA_DOWN)
++ {
++ ret = 0;
++ //mail loop ended
++ if (context)
++ {
++ pa_context_disconnect(context);
++ pa_context_unref(context);
++ context = NULL;
++ }
++
++ if (proplist)
++ {
++ pa_proplist_free(proplist);
++ proplist = NULL;
++ }
++
++ if (main_loop)
++ {
++ //pa_signal_done();
++ pa_signal_done();
++ pa_mainloop_free(main_loop);
++ main_loop = NULL;
++ }
++
++ //... pulse audio is down ... retry to connect
++ usleep(100000);
++ routing_sender_start_mainloop(thiz);
++ }
++ //TDOO: else if -check other value for "ret"
++ else
++ {
++ return thiz;
++ }
++}
++
++
++void routing_sender_pa_event_callback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *thiz)
++{
++ switch(t)
++ {
++ case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
++ {
++ pa_context_get_sink_input_info(
++ c, idx, routing_sender_get_sink_input_info_callback, thiz);
++ break;
++ }
++ case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
++ {
++ pa_context_get_sink_input_info(
++ c, idx, routing_sender_get_sink_input_info_callback, thiz);
++ break;
++ }
++ default:
++ {
++ logInfo("Pulse Audio event", t, "was ignored");
++ }
++ }
++}
++
++
++void routing_sender_get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata)
++{
++ (void) eol;
++
++ RoutingSenderPULSE* thiz = (RoutingSenderPULSE*) userdata;
++ if (!thiz)
++ {
++ logError("pa_context_get_sink_input_info was called with wrong params\n");
++ return;
++ }
++
++ /* init map of ramp_volumes, usefull later */
++ if (i != NULL && strcmp(i->name,"null") != 0)
++ {
++ ramp_volume source_ramp_volume;
++ source_ramp_volume.sink_input_index = i->index;
++ source_ramp_volume.ramp_crt_elapsed = 0;
++ g_sinkInputId2rampVolume.insert(std::make_pair(i->index, source_ramp_volume));
++ }
++
++ thiz->getSinkInputInfoCallback(c, i, userdata);
++}
++
++
++void routing_sender_get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int eol, void *userdata)
++{
++ (void) eol;
++
++ RoutingSenderPULSE* thiz = (RoutingSenderPULSE*) userdata;
++ if (!thiz)
++ {
++ logError("pa_context_get_surce_output_info was called with wrong params\n");
++ return;
++ }
++ thiz->getSourceOutputInfoCallback(c, i, userdata);
++}
++
++
++
++void routing_sender_get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata)
++{
++ RoutingSenderPULSE* thiz = (RoutingSenderPULSE*) userdata;
++ if (!thiz)
++ {
++ logError("pa_context_get_sink_info was called with wrong params\n");
++ return;
++ }
++ thiz->getSinkInfoCallback(c, i, is_last, userdata);
++}
++
++
++void routing_sender_get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata)
++{
++ RoutingSenderPULSE* thiz = (RoutingSenderPULSE*) userdata;
++ if (!thiz)
++ {
++ logError("pa_context_get_source_info was called with wrong params\n");
++ return;
++ }
++ thiz->getSourceInfoCallback(c, i, is_last, userdata);
++}
++
++
++void routing_sender_subscriber_callback(pa_context *c, int success, void *thiz) {
++ if (success)
++ {
++ pa_operation *o = pa_context_get_sink_info_list(c, routing_sender_get_sink_info_callback, thiz);
++ if (o)
++ {
++ pa_operation_unref(o);
++ }
++ else
++ {
++ logError("Unable to create Pulse Audio operation:",
++ "pa_context_get_sink_info_list");
++ }
++ }
++ else
++ {
++ logError("routing_sender_subscriber_callback: success = false");
++ }
++}
++
++
++void routing_sender_context_state_callback(pa_context *c, void *thiz)
++ {
++
++ if (pa_context_get_state(c) == PA_CONTEXT_FAILED)
++ {
++ pa_mainloop_quit(main_loop, QUIT_REASON_PA_DOWN);
++ }
++ if (pa_context_get_state(c) == PA_CONTEXT_READY)
++ {
++ //when context is ready - subscriber for server events (we are mainly interested in sink inputs & source outputs)
++ pa_context_set_subscribe_callback(c, routing_sender_pa_event_callback, thiz);
++ pa_operation *o = pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_ALL, routing_sender_subscriber_callback, thiz);
++ if (o)
++ {
++ pa_operation_unref(o);
++ }
++ else
++ {
++ logError("Unable to create Pulse Audio operation:",
++ "pa_context_subscribe");
++ }
++ }
++ //other states are not relevant
++}
++
++
++bool routing_sender_get_source_info(pa_context *c, void *thiz) {
++ if (pa_context_get_state(c) == PA_CONTEXT_READY)
++ {
++ pa_operation *o = pa_context_get_source_info_list(c, routing_sender_get_source_info_callback, thiz);
++ if (o)
++ {
++ pa_operation_unref(o);
++ }
++ else
++ {
++ logError("Unable to create Pulse Audio operation:",
++ "pa_context_get_sink_info_list");
++ return false;
++ }
++ }
++ else
++ {
++ logError("Can not get Pulse Audio sources info - context not ready\n");
++ return false;
++ }
++
++ return true;
++}
++
++
++bool routing_sender_move_sink_input(pa_context *c, uint32_t sink_input_index, uint32_t sink_index, void *thiz)
++{
++ if (pa_context_get_state(c) == PA_CONTEXT_READY)
++ {
++ pa_operation *o = pa_context_move_sink_input_by_index(c, sink_input_index, sink_index, NULL, thiz);
++ if (o)
++ {
++ pa_operation_unref(o);
++ }
++ else
++ {
++ logError("Unable to create Pulse Audio operation:",
++ "pa_context_move_sink_input_by_index");
++ return false;
++ }
++ }
++ else
++ {
++ logError("Can not move sink input - context not ready\n");
++ return false;
++ }
++ return true;
++}
++
++
++bool routing_sender_move_source_output(pa_context *c, uint32_t source_output_index, uint32_t source_index, void *thiz)
++{
++ if (pa_context_get_state(c) == PA_CONTEXT_READY)
++ {
++ pa_operation *o = pa_context_move_source_output_by_index(c, source_output_index, source_index, NULL, thiz);
++ if (o)
++ {
++ pa_operation_unref(o);
++ }
++ else
++ {
++ logError("Unable to create Pulse Audio operation:",
++ "pa_context_set_sink_input_volume");
++ return false;
++ }
++ }
++ else
++ {
++ logError("Can not move source output - context not ready\n");
++ return false;
++ }
++ return true;
++}
++
++static inline uint16_t timespec2mili(const timespec & time)
++{
++ return (uint16_t)((time.tv_nsec == -1 && time.tv_sec == -1) ?
++ -1 :
++ time.tv_sec * 1000 + time.tv_nsec / 1000000);
++}
++
++/* Considers that time2 > time1 */
++static inline uint16_t timespec2DeltaMili(const timespec & time1, const timespec & time2)
++{
++ timespec l_deltaTime;
++ l_deltaTime.tv_sec = time2.tv_sec - time1.tv_sec;
++ l_deltaTime.tv_nsec = time2.tv_nsec - time1.tv_nsec;
++ return timespec2mili(l_deltaTime);
++}
++
++static void routing_sender_sink_input_volume_cb(pa_context *c, int success, void *data)
++{
++ logInfo("routing_sender_sink_input_volume_cb: success=", success, " data=", data);
++ if (success)
++ {
++ ramp_volume * l_ramp_volume = (ramp_volume *)data;
++ timespec l_endTime;
++ clock_gettime(0, &l_endTime);
++ l_ramp_volume->ramp_crt_elapsed = timespec2DeltaMili(l_ramp_volume->start_time, l_endTime);
++ if (l_ramp_volume->ramp_crt_elapsed >= l_ramp_volume->ramp_max_time)
++ {
++ return;
++ }
++
++ logInfo("routing_sender_sink_input_volume_cb: ms elapsed=", l_ramp_volume->ramp_crt_elapsed, " of ", l_ramp_volume->ramp_max_time);
++
++ /* ######## Calculate new volume with formula: ##########
++ crt_time x ( vol_end - vol_ini )
++ new_vol = vol_ini + ----------------------------------
++ max_time
++ ###################################################### */
++ uint32_t new_volume =
++ l_ramp_volume->volume_ini +
++ ( ( l_ramp_volume->ramp_crt_elapsed * ( l_ramp_volume->volume_end - l_ramp_volume->volume_ini ) ) / l_ramp_volume->ramp_max_time );
++ logInfo("routing_sender_sink_input_volume_cb: vol_ini=",l_ramp_volume->volume_ini,"vol_crt=",new_volume,"vol_end=",l_ramp_volume->volume_end);
++
++
++ /* ***** Set volume again ***** */
++ pa_cvolume *volumeCh = (pa_cvolume *) malloc(sizeof(pa_cvolume));
++ volumeCh->channels = 1;//TODO: check is stream is mono / stereo
++ volumeCh->values[0] = new_volume;
++
++ pa_operation *o = pa_context_set_sink_input_volume(c, l_ramp_volume->sink_input_index, volumeCh, NULL, NULL);
++ if (o)
++ {
++ pa_operation_unref(o);
++ }
++ else
++ {
++ logError("Unable to create Pulse Audio operation:",
++ "pa_context_set_sink_input_volume");
++ return ;
++ }
++ usleep(10000);
++
++ volumeCh->channels = 2;//TODO: check is stream is mono / stereo
++ volumeCh->values[0] = new_volume;
++ volumeCh->values[1] = new_volume;
++ logInfo("routing_sender_sink_input_volume_cb: will set vol=", new_volume);
++ o = pa_context_set_sink_input_volume(c, l_ramp_volume->sink_input_index, volumeCh, routing_sender_sink_input_volume_cb, l_ramp_volume);
++ if (o)
++ {
++ pa_operation_unref(o);
++ }
++ else
++ {
++ logError("Unable to create Pulse Audio operation:",
++ "pa_context_set_sink_input_volume");
++ return ;
++ }
++ }
++
++}
++
++bool routing_sender_sink_input_volume_ramp(pa_context *c, uint32_t sink_input_index, uint32_t crt_volume, uint32_t volume, uint16_t ramp_time, void *thiz)
++{
++ if (pa_context_get_state(c) == PA_CONTEXT_READY)
++ {
++ /* before everything, check to see if ramp_volume struct exists */
++ std::map<uint32_t, ramp_volume>::iterator iter = g_sinkInputId2rampVolume.find(sink_input_index);
++ std::map<uint32_t, ramp_volume>::iterator iterEnd = g_sinkInputId2rampVolume.end();
++ if (iter != iterEnd)
++ {
++ /* set test volume with only 1 unit more or less, just to see how callback responds */
++ pa_volume_t test_volume = ((crt_volume * MAX_PULSE_VOLUME) / 100);
++ test_volume += volume > crt_volume ? 1 : -1;
++
++ pa_cvolume *volumeCh = (pa_cvolume *) malloc(sizeof(pa_cvolume));
++ volumeCh->channels = 1;//TODO: check is stream is mono / stereo
++ volumeCh->values[0] = test_volume;
++
++ pa_operation *o = pa_context_set_sink_input_volume(c, sink_input_index, volumeCh, NULL, NULL);
++ if (o)
++ {
++ pa_operation_unref(o);
++ }
++ else
++ {
++ logError("Unable to create Pulse Audio operation:",
++ "pa_context_set_sink_input_volume");
++ return false;
++ }
++
++ volumeCh->channels = 2;//TODO: check is stream is mono / stereo
++ volumeCh->values[0] = test_volume;
++ volumeCh->values[1] = test_volume;
++
++ ramp_volume * l_ramp_volume = (ramp_volume *) &iter->second;
++ logInfo("routing_sender_sink_input_volume_ramp: searching ",sink_input_index,"found ramp_vlume struct with sinkInputId ",l_ramp_volume->sink_input_index," (should be equal) ");
++ l_ramp_volume->volume_ini = (crt_volume * MAX_PULSE_VOLUME) / 100;
++ l_ramp_volume->volume_end = (volume * MAX_PULSE_VOLUME) / 100;
++ l_ramp_volume->ramp_max_time = ramp_time;
++ clock_gettime(0, &l_ramp_volume->start_time);
++ l_ramp_volume->ramp_crt_elapsed = 0;
++ logInfo("routing_sender_sink_input_volume_ramp: will set vol=", test_volume);
++ o = pa_context_set_sink_input_volume(c, sink_input_index, volumeCh, routing_sender_sink_input_volume_cb, l_ramp_volume);
++ if (o)
++ {
++ pa_operation_unref(o);
++ }
++ else
++ {
++ logError("Unable to create Pulse Audio operation:",
++ "pa_context_set_sink_input_volume");
++ return false;
++ }
++ }
++ else
++ {
++ logInfo("routing_sender_sink_input_volume_ramp: didn't find struct with sinkInputId ", sink_input_index);
++ /* make-it the old traditional way */
++ return routing_sender_sink_input_volume(c, sink_input_index, volume, thiz);
++ }
++
++
++ }
++ else
++ {
++ logError("Can not set sink input volume - context not ready\n");
++ return false;
++ }
++ return true;
++}
++
++bool routing_sender_sink_input_volume(pa_context *c, uint32_t sink_input_index, uint32_t volume, void *thiz)
++{
++ if (pa_context_get_state(c) == PA_CONTEXT_READY)
++ {
++ pa_cvolume *volumeCh = (pa_cvolume *) malloc(sizeof(pa_cvolume));
++ volumeCh->channels = 1;//TODO: check is stream is mono / stereo
++ volumeCh->values[0] = (volume * MAX_PULSE_VOLUME) / 100;
++
++ pa_operation *o = pa_context_set_sink_input_volume(c, sink_input_index, volumeCh, NULL, thiz);
++ if (o)
++ {
++ pa_operation_unref(o);
++ }
++ else
++ {
++ logError("Unable to create Pulse Audio operation:",
++ "pa_context_set_sink_input_volume");
++ return false;
++ }
++
++ volumeCh->channels = 2;//TODO: check is stream is mono / stereo
++ volumeCh->values[0] = (volume * MAX_PULSE_VOLUME) / 100;
++ volumeCh->values[1] = (volume * MAX_PULSE_VOLUME) / 100;
++
++ o = pa_context_set_sink_input_volume(c, sink_input_index, volumeCh, NULL, NULL);
++ if (o)
++ {
++ pa_operation_unref(o);
++ }
++ else
++ {
++ logError("Unable to create Pulse Audio operation:",
++ "pa_context_set_sink_input_volume");
++ return false;
++ }
++ }
++ else
++ {
++ logError("Can not set sink input volume - context not ready\n");
++ return false;
++ }
++ return true;
++}
++
++
++bool routing_sender_sink_input_mute(pa_context *c, uint32_t sink_input_index, bool mute, void *thiz)
++{
++ if (pa_context_get_state(c) == PA_CONTEXT_READY)
++ {
++ pa_operation *o = pa_context_set_sink_input_mute(
++ c, sink_input_index, mute ? 1 : 0, NULL, thiz);
++ if (o)
++ {
++ pa_operation_unref(o);
++ }
++ else
++ {
++ logError("Unable to create Pulse Audio operation:",
++ "pa_context_set_sink_input_mute");
++ return false;
++ }
++ }
++ else
++ {
++ logError("Can not set sink input volume - context not ready\n");
++ return false;
++ }
++ return true;
++}
++
++
++bool routing_sender_sink_volume(pa_context *c, uint32_t sink_index, uint32_t volume, void *thiz)
++{
++ if (pa_context_get_state(c) == PA_CONTEXT_READY)
++ {
++ pa_cvolume *volumeCh = (pa_cvolume *) malloc(sizeof(pa_cvolume));
++ volumeCh->channels = 2;//TODO: check is stream is mono / stereo
++ volumeCh->values[0] = (volume * MAX_PULSE_VOLUME) / 100;
++ volumeCh->values[1] = (volume * MAX_PULSE_VOLUME) / 100;
++
++ pa_operation *o = pa_context_set_sink_volume_by_index(c, sink_index, volumeCh, NULL, thiz);
++ if (o)
++ {
++ pa_operation_unref(o);
++ }
++ else
++ {
++ logError("Unable to create Pulse Audio operation:",
++ "pa_context_set_sink_input_volume");
++ return false;
++ }
++ }
++ else
++ {
++ logError("Can not set sink input volume - context not ready\n");
++ return false;
++ }
++ return true;
++}
++
++//TODO - implements mute/un-mute and sink suspend(pause)
++//TODO - IMPORTANT !! implement volume change for sink input even multiple sink inputs are created during one connection
+diff --git a/PluginRoutingInterfacePulse/src/RoutingSenderPULSE.cpp b/PluginRoutingInterfacePulse/src/RoutingSenderPULSE.cpp
+new file mode 100644
+index 0000000..05259b0
+--- /dev/null
++++ b/PluginRoutingInterfacePulse/src/RoutingSenderPULSE.cpp
+@@ -0,0 +1,923 @@
++/**
++ * SPDX license identifier: MPL-2.0
++ *
++ * Copyright (C) 2011-2014, Wind River Systems
++ * Copyright (C) 2014, GENIVI Alliance
++ *
++ * This file is part of Pulse Audio Interface Routing Plugin.
++ *
++ * This Source Code Form is subject to the terms of the Mozilla Public
++ * License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
++ *
++ * For further information see http://www.genivi.org/.
++ *
++ * List of changes:
++ *
++ * 21.08.2014, Adrian Scarlat, First version of the code;
++ * Porting code from AM ver1.x to AM ver3.0;
++ * Added Copyright and License information;
++ *
++ *
++ * DESCRIPTION
++ *
++ * This module is handling requests form AudioManager daemon and redirect them to Pulse Audio server.
++ * It keeps track of existing audio sink, sources, sink-input, sources-input and performs connection/disconnection
++ * and other operations upon AudioManager daemon request.
++ *
++ * The modul is configured with a static list of sink and sources.
++ * Sinks are: audio output(e.g. speakers) or recording applications.
++ * Sources are: playback applications or audio input(e.g. microphone)
++ *
++ * In PulseAudio server those are classified as follows:
++ * - audio output - sinks: identified by name or properties: device.api (e.g = "alsa") & device.class(e.g. = "sound")
++ * - audio input - sources: identified by name or properties: device.api (e.g = "alsa") & device.class(e.g. = "sound")
++ * - playback applications - sink inputs: identified by application.name (e.g. "ALSA plug-in [chromium-browser]",
++ * - recording applications - source outputs: identified by application.name (e.g. "ALSA plug-in [chromium-browser]",
++ * application.process.user = "popai", application.process.binary = "chromium-browser")
++ *
++ */
++
++#include <stdio.h>
++#include <string.h>
++#include <iostream>
++
++#include "shared/CAmDltWrapper.h"
++
++#include "RoutingSenderPULSE.h"
++#include "RoutingSenderMainloopPULSE.h"
++
++
++
++#define LIBNAME "libPluginRoutingInterfacePULSE.so"
++#define CFGNAME "libPluginRoutingInterfacePULSE.conf"
++
++/* Globals */
++
++
++/* Defines */
++DLT_DECLARE_CONTEXT(routingPulse)
++/* Maximum source volume measured in percentage. Minimum value is 0% */
++#define MAX_SOURCE_VOLUME (100)
++
++
++/**
++ * Factory function for the plug-in to be used by audio manager daemon with dlopen & dlsym functions.
++ * Pattern "libraryName"FACTORY
++ *
++ * @author Ionut Popa (ionut.popa@windriver.com)
++ * @return an instance of RoutingSendInterface or type RoutingSenderPULSE.
++ */
++extern "C" IAmRoutingSend* PluginRoutingInterfacePULSEFactory()
++{
++
++ return (new RoutingSenderPULSE(NULL));
++}
++
++/**
++ * Destructor function for the plug-in to be used by audio manager daemon with dl_open & dl_sym functions.
++ *
++ * @param routingSendInterface - the instance created by PluginRoutingInterfaceDbusFactory
++ * @author Ionut Popa (ionut.popa@windriver.com)
++ */
++extern "C" void destroyPluginRoutingInterfacePULSE(IAmRoutingSend* routingSendInterface)
++{
++ delete routingSendInterface;//virtual destructor -> our constructor will be called too
++}
++
++
++/**
++ * Constructor.
++ * @param p_paContext - reference to PulseAudio context
++ */
++RoutingSenderPULSE::RoutingSenderPULSE(pa_context *p_paContext)
++{
++ this->m_paSinkNullIndex = -1;
++ this->m_paSourceNullIndex = -1;
++ this->m_paContext = p_paContext;
++ this->loadConfig();
++}
++
++
++void RoutingSenderPULSE::loadConfig()
++{
++ //get current library path - search: /proc/< getpid() >/maps
++ char proc_maps_file_name[256];
++ char line[256];
++ char lib_name[256];
++ char *tmp;
++ pid_t pid = getpid();
++ snprintf(proc_maps_file_name, 256, "/proc/%d/maps", pid);
++ FILE *proc_maps = fopen(proc_maps_file_name, "r");
++
++ while (!feof(proc_maps))
++ {
++ char *cnt = fgets(line, 256, proc_maps);
++ if (strlen(line) == 0 || line[0] == '#')
++ {
++ continue;
++ }
++ if (cnt == NULL) continue;
++ //tmp0 tmp1 tmp2 tmp3 tmp4 lib_name);
++ tmp = strtok(line, " ");//address-interval
++ if(tmp == NULL) continue;
++
++ tmp = strtok(NULL, " ");//rights
++ if(tmp == NULL) continue;
++
++ tmp = strtok(NULL, " ");//offset
++ if(tmp == NULL) continue;
++
++ strtok(NULL, " ");//dev
++ if(tmp == NULL) continue;
++
++ tmp = strtok(NULL, " \n");//inode
++ if(tmp == NULL) continue;
++
++ tmp = strtok(NULL, " \n");
++ if(tmp == NULL) continue;
++
++ strcpy(lib_name, tmp);
++ if ((lib_name != NULL) && (strstr(lib_name, LIBNAME) >= lib_name))
++ {
++ strcpy(strrchr(lib_name, '/') + 1, CFGNAME);
++ logInfo("PULSE - config file name: %s\n", lib_name);
++
++ FILE *config = fopen(lib_name, "r");
++
++ while (config && !feof(config))
++ {
++ char *cnt = fgets(line, 256, config);
++ if (!line || strlen(line) == 0) continue;
++ //config format line: TYPE|PULSE TYPE|NAME|CLASS|PROPERTY_NAME|PROPERTY_VALUE
++ //TYPE="Source" or "Sink"
++
++ char *tmp = strtok(line, "|");//type
++ if (strcmp("Sink", tmp) == 0)
++ {
++ //add sink config
++ RoutingSenderPULSESourceSinkConfig sinkConfig;
++
++ tmp = strtok(NULL, "|");//pulse type - not used for the moment
++
++ tmp = strtok(NULL, "|");//class
++ sinkConfig.clazz = std::string(tmp);
++
++ tmp = strtok(NULL, "|");//name
++ sinkConfig.name = std::string(tmp);
++
++ tmp = strtok(NULL, "|");//property name
++ sinkConfig.propertyName = std::string(tmp);
++
++ tmp = strtok(NULL, "|\n");//property value
++ sinkConfig.propertyValue = std::string(tmp);
++
++ m_sinks.push_back(sinkConfig);
++ logInfo("sinkConfig: sinkConfig.clazz=", sinkConfig.clazz, " sinkConfig.name=", sinkConfig.name, " sinkConfig.propertyName=", sinkConfig.propertyName, " sinkConfig.propertyValue=", sinkConfig.propertyValue);
++ }
++ if (strcmp("Source", tmp) == 0)
++ {
++ //add source config
++ RoutingSenderPULSESourceSinkConfig sourceConfig;
++
++ tmp = strtok(NULL, "|");//pulse type - not used for the moment
++
++ tmp = strtok(NULL, "|");//class
++ sourceConfig.clazz = std::string(tmp);
++
++ tmp = strtok(NULL, "|");//name
++ sourceConfig.name = std::string(tmp);
++
++ tmp = strtok(NULL, "|");//property name
++ sourceConfig.propertyName = std::string(tmp);
++
++ tmp = strtok(NULL, "|\n");//property value
++ sourceConfig.propertyValue = std::string(tmp);
++
++ m_sources.push_back(sourceConfig);
++ logInfo("sourceConfig: sourceConfig.clazz=", sourceConfig.clazz, " sourceConfig.name=", sourceConfig.name, " sourceConfig.propertyName=", sourceConfig.propertyName, " sourceConfig.propertyValue=", sourceConfig.propertyValue);
++ }
++ }
++
++ if (config)
++ fclose(config);
++ break;
++ }
++ }
++
++ fclose(proc_maps);
++}
++
++
++/**
++ * Destructor.
++ */
++RoutingSenderPULSE::~RoutingSenderPULSE()
++{
++ //TODO: Disconnect from pulse: quit main loop and free the context and stuff
++}
++
++/**
++ * Connecting sender & receiver
++ * @author Ionut Popa (ionut.popa@windriver.com)
++ */
++am_Error_e RoutingSenderPULSE::startupInterface(am::IAmRoutingReceive *p_routingReceiver)
++{
++ this->m_routingReceiver = p_routingReceiver;
++ return am::E_OK;
++}
++
++void RoutingSenderPULSE::setRoutingReady(uint16_t handle)
++{
++ //TODO: do not register sinks with the same name
++
++ int i;
++
++ //first register Domain = PulseAudio
++ this->m_domain.name = "PulseAudio";
++ this->returnBusName(this->m_domain.busname);//set domain bus name = current interface bus name
++ this->m_domain.nodename = "PulseAudio";
++ this->m_domain.early = false;
++ this->m_domain.complete = true;
++ this->m_domain.state = am::DS_CONTROLLED;
++
++ this->m_domain.domainID = 0;
++ this->m_routingReceiver->registerDomain(this->m_domain, this->m_domain.domainID);
++
++ am_SoundProperty_s l_spTreble;
++ l_spTreble.type = SP_GENIVI_BASS;
++ l_spTreble.value = 0;
++
++ am_SoundProperty_s l_spMid;
++ l_spMid.type = SP_GENIVI_MID;
++ l_spMid.value = 0;
++
++ am_SoundProperty_s l_spBass;
++ l_spBass.type = SP_GENIVI_BASS;
++ l_spBass.value = 0;
++
++ //register sources (sink inputs & sinks)
++ for (i = 0; i < m_sources.size(); i++)
++ {
++ am_sourceID_t l_newSourceID = 0;
++ this->m_sources[i].source.sourceID = l_newSourceID;
++ this->m_sources[i].source.name = m_sources[i].name;
++ this->m_sources[i].source.sourceState = am::SS_ON;
++ this->m_sources[i].source.domainID = this->m_domain.domainID;
++ this->m_sources[i].source.visible = true;
++ this->m_sources[i].source.volume = MAX_SOURCE_VOLUME; /* initialize source volume to 100% */
++
++ this->m_sources[i].source.listConnectionFormats.push_back(am::CF_GENIVI_STEREO);
++ this->m_routingReceiver->peekSourceClassID(
++ this->m_sources[i].clazz,
++ this->m_sources[i].source.sourceClassID);
++
++ this->m_routingReceiver->registerSource(this->m_sources[i].source, l_newSourceID);
++
++ this->m_sources[i].source.sourceID = l_newSourceID;
++ m_sourceToPASinkInput[l_newSourceID] = -1;
++ m_sourceToPASource[l_newSourceID] = -1;
++
++ logInfo("PULSE - register source:"
++ ,m_sources[i].name
++ , "(", m_sources[i].propertyName , ", ", m_sources[i].propertyValue, ")");
++ m_sourceToVolume[l_newSourceID] = MAX_SOURCE_VOLUME;//initially all the sources are at 100%
++ }
++
++ //register sinks (source outputs & sources)
++ for (i = 0; i < m_sinks.size(); i++)
++ {
++ am_sinkID_t l_newsinkID = 0;
++ this->m_sinks[i].sink.sinkID = l_newsinkID;
++ this->m_sinks[i].sink.name = this->m_sinks[i].name;
++ this->m_sinks[i].sink.muteState = am::MS_MUTED;
++ this->m_sinks[i].sink.domainID = this->m_domain.domainID;
++ this->m_sinks[i].sink.visible = true;
++
++ this->m_sinks[i].sink.listSoundProperties.push_back(l_spTreble);
++ this->m_sinks[i].sink.listSoundProperties.push_back(l_spMid);
++ this->m_sinks[i].sink.listSoundProperties.push_back(l_spBass);
++
++ this->m_sinks[i].sink.listConnectionFormats.push_back(am::CF_GENIVI_STEREO);
++
++ //printf("\nADS:Before: this->m_sinks[i].clazz = %s\n", this->m_sinks[i].clazz.c_str());
++ //printf("\nADS:Before: this->m_sinks[i].sink.sinkClassID = %d\n", this->m_sinks[i].sink.sinkClassID);
++ this->m_routingReceiver->peekSinkClassID(
++ this->m_sinks[i].clazz,
++ this->m_sinks[i].sink.sinkClassID);
++ //this->m_sinks[i].sink.sinkClassID = 1;
++ //printf("\nADS:After: this->m_sinks[i].clazz = %s\n", this->m_sinks[i].clazz.c_str());
++ //printf("\nADS:After: this->m_sinks[i].sink.sinkClassID = %d\n", this->m_sinks[i].sink.sinkClassID);
++
++ this->m_routingReceiver->registerSink(this->m_sinks[i].sink, l_newsinkID);
++ this->m_sinks[i].sink.sinkID = l_newsinkID;
++ m_sinkToPASourceOutput[l_newsinkID] = -1;
++ m_sinkToPASink[l_newsinkID] = -1;
++
++ logInfo("PULSE - register sink:"
++ ,m_sinks[i].name
++ , "(", m_sinks[i].propertyName , ", ", m_sinks[i].propertyValue, ")");
++ }
++
++ logInfo("PULSE - routingInterfacesReady");
++ this->m_routingReceiver->confirmRoutingReady(handle, am::E_OK);
++
++ //register pulse sink & sources, sink inputs & source outputs - > start the main PA loop
++ routing_sender_create_mainloop((void *) this);
++}
++
++void RoutingSenderPULSE::setRoutingRundown(uint16_t handle)
++{
++ this->m_routingReceiver->confirmRoutingRundown(handle, am::E_OK);
++ //TODO: implement this
++}
++
++am_Error_e RoutingSenderPULSE::asyncAbort(const am_Handle_s handle)
++{
++ (void) handle;
++ return E_NOT_USED;
++}
++
++
++am_Error_e RoutingSenderPULSE::asyncConnect(
++ const am_Handle_s handle,
++ const am_connectionID_t connectionID,
++ const am_sourceID_t sourceID,
++ const am_sinkID_t sinkID,
++ const am_CustomConnectionFormat_t connectionFormat)
++{
++ //TODO: check stuff like connectionFormat
++ //printf("ADS--> asyncConnect\n");
++ logInfo("PULSE - asyncConnect() - start");
++ //add source,sink & connectionID to a list of connections maintained by Routing Pulse Engine
++ RoutingSenderPULSEConnection l_newConnection;
++ l_newConnection.sinkID = sinkID;
++ l_newConnection.sourceID = sourceID;
++ l_newConnection.connectionID = connectionID;
++ l_newConnection.handle = handle;
++
++ //by default - sources ar connected at 100% -> controller is responsible to setSourcevolume if needed
++
++ m_sourceToVolume[sourceID] = MAX_SOURCE_VOLUME;
++
++
++ if (m_sinkToPASink[sinkID] != -1)
++ {
++ if (m_sourceToPASinkInput[sourceID] != -1)
++ {
++ if (routing_sender_move_sink_input(
++ this->m_paContext,
++ m_sourceToPASinkInput[sourceID],
++ m_sinkToPASink[sinkID],
++ this))
++ {
++ //TODO: add callback for pulse move sink input -> to send confirmation; for the moment directly send confirmation
++
++ logInfo("PULSE - asyncConnect() - connectionID:", connectionID,
++ "move sinkInputIndex:", m_sourceToPASinkInput[sourceID], "to sinkIndex:", m_sinkToPASink[sinkID]);
++ }
++ else
++ {
++ this->m_routingReceiver->ackConnect(handle, connectionID, am::E_NOT_POSSIBLE);
++ return am::E_NOT_POSSIBLE;
++ }
++ }//else move_sink_input will be called later
++ }
++ else if (m_sourceToPASource[sourceID] != -1)
++ {
++ if (m_sinkToPASourceOutput[sinkID] != -1)
++ {
++ if (routing_sender_move_source_output(
++ this->m_paContext,
++ m_sinkToPASourceOutput[sinkID],
++ m_sourceToPASource[sourceID],
++ this))
++ {
++ //TODO: add callback for pulse move sink input -> to send confirmation; for the moment directly send confirmation
++
++ logInfo("PULSE - asyncConnect() - connectionID:", connectionID,
++ "move sourceOutputIndex:", m_sinkToPASourceOutput[sinkID], "to sourceIndex:", m_sourceToPASource[sourceID]);
++
++ }
++ else
++ {
++ this->m_routingReceiver->ackConnect(handle, connectionID, am::E_NOT_POSSIBLE);
++ return am::E_NOT_POSSIBLE;
++ }
++ }//else move_sink_input will be called later
++ }
++ else
++ {
++ logError("Sink and source for connection not identified:",
++ sinkID, sourceID, connectionID);
++ return am::E_NOT_POSSIBLE;
++ }
++
++ m_activeConnections.push_back(l_newConnection);
++
++ this->m_routingReceiver->ackConnect(handle, connectionID, am::E_OK);
++
++/**
++ * TODO: connection is always possible ? check that
++*/
++
++ return am::E_OK;
++}
++
++
++am_Error_e RoutingSenderPULSE::asyncDisconnect(const am_Handle_s handle, const am_connectionID_t connectionID)
++{
++ //get connection by ID ... not to many connections, therefore linear search is fast enough
++ std::vector<RoutingSenderPULSEConnection>::iterator iter = m_activeConnections.begin();
++ std::vector<RoutingSenderPULSEConnection>::iterator iterEnd = m_activeConnections.end();
++ for (; iter < iterEnd; ++iter)
++ {
++ if (iter->connectionID == connectionID)
++ {
++ if (m_sourceToPASinkInput[iter->sourceID] != -1)
++ {
++ if (this->m_paSinkNullIndex >= 0)
++ {
++ //if null sink is defined - disconnect = move sink input to null
++ logInfo("PULSE - asyncDisconnect() - connection found - move sinkInputIndex:",
++ m_sourceToPASinkInput[iter->sourceID], "to NULL sinkIndex:", this->m_paSinkNullIndex);
++
++ routing_sender_move_sink_input(
++ this->m_paContext,
++ this->m_sourceToPASinkInput[iter->sourceID],
++ this->m_paSinkNullIndex,
++ this);
++ //TODO: add callback for pulse move sink input -> to send confirmation; for the moment directly send confirmation
++ this->m_routingReceiver->ackDisconnect(handle, connectionID, am::E_OK);
++
++ }
++ }
++ else if (m_sinkToPASourceOutput[iter->sinkID] != -1)
++ {
++ if (this->m_paSourceNullIndex >= 0)
++ {
++ //if null source is defined - disconnect = move source output to null
++ logInfo("PULSE - asyncDisconnect() - connection found - move sourceOutputIndex:",
++ m_sinkToPASourceOutput[iter->sinkID], "to NULL sourceIndex:", this->m_paSourceNullIndex);
++
++ routing_sender_move_source_output(
++ this->m_paContext,
++ m_sourceToPASinkInput[iter->sourceID],
++ this->m_paSinkNullIndex,
++ this);
++ //TODO: add callback for pulse move sink input -> to send confirmation; for the moment directly send confirmation
++ this->m_routingReceiver->ackDisconnect(handle, connectionID, am::E_OK);
++
++ //remove connection from the list of active connections
++ iter = m_activeConnections.erase(iter);
++
++ break;
++ }
++ }
++ else
++ {
++ logInfo("PULSE - asyncDisconnect() - connection found - but no sink input or source");
++ this->m_routingReceiver->ackDisconnect(handle, connectionID, am::E_OK);
++ }
++ //remove connection from the list of active connections
++ iter = m_activeConnections.erase(iter);
++
++ break;
++ }
++ }
++ return am::E_OK;
++}
++
++am_Error_e RoutingSenderPULSE::asyncSetSinkVolume(
++ 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)
++{
++ (void) ramp;
++ (void) time;
++
++ logInfo("PULSE - asyncSetSinkVolume() - volume:", volume, "sink index:", this->m_sinkToPASink[sinkID]);
++
++ routing_sender_sink_volume(
++ this->m_paContext,
++ this->m_sinkToPASink[sinkID],
++ volume,
++ this);
++ this->m_routingReceiver->ackSetSinkVolumeChange(handle, volume, E_OK);
++ return E_OK;
++}
++
++am_Error_e RoutingSenderPULSE::asyncSetSourceVolume(
++ const am_Handle_s handle,
++ const am_sourceID_t sourceID,
++ const am_volume_t volume,
++ const am_CustomRampType_t ramp,
++ const am_time_t time)
++{
++ (void) ramp;
++ (void) time;
++
++ am_volume_t crt_volume = this->m_sourceToVolume[sourceID];
++ this->m_sourceToVolume[sourceID] = volume;
++
++ logInfo("PULSE - asyncSetSourceVolume() - volume:", volume, "sink input index:", this->m_sourceToPASinkInput[sourceID]);
++ if (m_sourceToPASinkInput[sourceID] != -1)
++ {
++ if (time == 0)
++ {/* without ramp time */
++ routing_sender_sink_input_volume(
++ this->m_paContext,
++ this->m_sourceToPASinkInput[sourceID],
++ volume,
++ this);
++ }
++ else
++ {/* with ramp time */
++ routing_sender_sink_input_volume_ramp(
++ this->m_paContext,
++ this->m_sourceToPASinkInput[sourceID],
++ crt_volume,
++ volume,
++ (uint16_t)time,
++ this);
++ }
++ }
++ else
++ {
++ logInfo("PULSE - sink input not registered yet - should wait for registration before update the volume");
++ }
++ this->m_routingReceiver->ackSetSourceVolumeChange(handle, volume, E_OK);
++
++}
++
++am_Error_e RoutingSenderPULSE::asyncSetSourceState(
++ const am_Handle_s handle,
++ const am_sourceID_t sourceID,
++ const am_SourceState_e state)
++{
++ logInfo("PULSE - asyncSetSourceState", state);
++ switch (state)
++ {
++ case SS_ON:
++ {
++ routing_sender_sink_input_mute(
++ this->m_paContext,
++ this->m_sourceToPASinkInput[sourceID],
++ false,
++ this
++ );
++ break;
++ }
++ case SS_OFF:
++ case SS_PAUSED:
++ {
++ //TODO: mute source in case of PAUSE or OFF - is there a better way to pause ? maybe suspending the associated sink?
++ routing_sender_sink_input_mute(
++ this->m_paContext,
++ this->m_sourceToPASinkInput[sourceID],
++ true,
++ this
++ );
++ break;
++ }
++ default:
++ {
++ logError("RoutingSenderPULSE::asyncSetSourceState - wrong source state\n");
++ this->m_routingReceiver->ackSetSourceState(handle, E_NOT_POSSIBLE);
++ return E_NOT_POSSIBLE;
++ }
++ }
++ this->m_routingReceiver->ackSetSourceState(handle, E_OK);
++ return E_OK;
++}
++
++am_Error_e RoutingSenderPULSE::asyncSetSinkSoundProperties(
++ const am_Handle_s handle,
++ const am_sinkID_t sinkID,
++ const std::vector<am_SoundProperty_s>& listSoundProperties)
++{
++ (void) handle;
++ (void) sinkID;
++ (void) listSoundProperties;
++ return E_NOT_USED;
++}
++
++am_Error_e RoutingSenderPULSE::asyncSetSinkSoundProperty(
++ const am_Handle_s handle,
++ const am_sinkID_t sinkID,
++ const am_SoundProperty_s& soundProperty)
++{
++ (void) handle;
++ (void) sinkID;
++ (void) soundProperty;
++ return E_NOT_USED;
++}
++
++am_Error_e RoutingSenderPULSE::asyncSetSourceSoundProperties(
++ const am_Handle_s handle,
++ const am_sourceID_t sourceID,
++ const std::vector<am_SoundProperty_s>& listSoundProperties)
++{
++ (void) handle;
++ (void) sourceID;
++ (void) listSoundProperties;
++ return E_NOT_USED;
++}
++
++am_Error_e RoutingSenderPULSE::asyncSetSourceSoundProperty(
++ const am_Handle_s handle,
++ const am_sourceID_t sourceID,
++ const am_SoundProperty_s& soundProperty)
++{
++ (void) handle;
++ (void) sourceID;
++ (void) soundProperty;
++ return E_NOT_USED;
++}
++
++am_Error_e RoutingSenderPULSE::asyncCrossFade(
++ const am_Handle_s handle,
++ const am_crossfaderID_t crossfaderID,
++ const am_HotSink_e hotSink,
++ const am_CustomRampType_t rampType,
++ const am_time_t time)
++{
++ (void) handle;
++ (void) crossfaderID;
++ (void) hotSink;
++ (void) rampType;
++ (void) time;
++ return E_NOT_USED;
++}
++
++am_Error_e RoutingSenderPULSE::setDomainState(
++ const am_domainID_t domainID,
++ const am_DomainState_e domainState)
++{
++ (void) domainID;
++ (void) domainState;
++ return E_NOT_USED;
++}
++
++am_Error_e RoutingSenderPULSE::returnBusName(std::string& BusName) const {
++ BusName = "RoutingPULSE";
++ return E_OK;
++}
++
++void RoutingSenderPULSE::getInterfaceVersion(std::string& out_ver) const
++{
++ out_ver = "3.0";
++}
++
++am_Error_e RoutingSenderPULSE::asyncSetVolumes(const am_Handle_s handle, const std::vector<am_Volumes_s>& listVolumes)
++{
++ (void) handle;
++ (void) listVolumes;
++ //todo: implement asyncSetVolumes;
++ return (E_NOT_USED);
++}
++
++am_Error_e RoutingSenderPULSE::asyncSetSinkNotificationConfiguration(const am_Handle_s handle, const am_sinkID_t sinkID, const am_NotificationConfiguration_s& notificationConfiguration)
++{
++ (void) handle;
++ (void) sinkID;
++ (void) notificationConfiguration;
++ //todo: implement asyncSetSinkNotificationConfiguration;
++ return (E_NOT_USED);
++}
++
++am_Error_e RoutingSenderPULSE::asyncSetSourceNotificationConfiguration(const am_Handle_s handle, const am_sourceID_t sourceID, const am_NotificationConfiguration_s& notificationConfiguration)
++{
++ (void) handle;
++ (void) sourceID;
++ (void) notificationConfiguration;
++ //todo: implement asyncSetSourceNotificationConfiguration;
++ return (E_NOT_USED);
++}
++/*******************************************************************************
++ * Private methods
++ ******************************************************************************/
++
++
++void RoutingSenderPULSE::getSinkInputInfoCallback(pa_context *c, const pa_sink_input_info *i, void *userdata)
++{
++ if (i == NULL)
++ {
++ return;
++ }
++
++ //search for corresponding Source
++ std::vector<RoutingSenderPULSESourceSinkConfig>::iterator iter = m_sources.begin();
++ std::vector<RoutingSenderPULSESourceSinkConfig>::iterator iterEnd = m_sources.end();
++ for (; iter < iterEnd; ++iter)
++ {
++ //try to match source PulseAudio properties against config properties
++ const char *property_value = pa_proplist_gets(i->proplist, iter->propertyName.c_str());
++
++ if (property_value &&
++ ( std::string::npos != std::string(property_value).find(iter->propertyValue) ||
++ std::string::npos != iter->propertyValue.find(property_value)) )
++ {
++ logInfo("PULSE - sink input registered:"
++ , " sinkInputIndex:", i->index, "sourceID:", iter->source.sourceID);
++
++ logInfo("PULSE - sink input details:"
++ , " prop_val: ", property_value, " iter->prop_val: ", iter->propertyValue);
++
++ m_sourceToPASinkInput[iter->source.sourceID] = i->index;
++
++ //iterate pending connection request
++ // -> if there is a connection pending such that sink input "i" matches source from Connect() - create the connection in pulse
++ std::vector<RoutingSenderPULSEConnection>::iterator iterConn = m_activeConnections.begin();
++ std::vector<RoutingSenderPULSEConnection>::iterator iterConnEnd = m_activeConnections.end();
++ for (; iterConn < iterConnEnd; ++iterConn)
++ {
++ if (iterConn->sourceID == iter->source.sourceID)
++ {
++ logInfo("PULSE - asyncConnect() - connectionID:", iterConn->connectionID,
++ "move sinkInputIndex:", m_sourceToPASinkInput[iterConn->sourceID], "to sinkIndex:", m_sinkToPASink[iterConn->sinkID]);
++
++ routing_sender_move_sink_input(
++ this->m_paContext,
++ m_sourceToPASinkInput[iterConn->sourceID],
++ m_sinkToPASink[iterConn->sinkID],
++ this);
++
++ //TODO: add callback for pulse move sink input -> to send confirmation; for the moment directly send confirmation
++ this->m_routingReceiver->ackConnect(iterConn->handle, iterConn->connectionID, am::E_OK);
++ }
++ }
++ //check of controller already requested vol adjustment for this source
++ bool requiresVolUpdate = false;
++ for (int j = 0; j < i->volume.channels; j++)
++ {
++ if ((i->volume.values[j]*MAX_SOURCE_VOLUME / MAX_PULSE_VOLUME) != m_sourceToVolume[iter->source.sourceID])
++ {
++ requiresVolUpdate = true;
++ logInfo("PULSE - sink registerd with vol:", (i->volume.values[j]*MAX_SOURCE_VOLUME / MAX_PULSE_VOLUME),
++ "; should be changed to:",
++ m_sourceToVolume[iter->source.sourceID]);
++ break;
++ }
++ }
++ if (requiresVolUpdate)
++ {
++ routing_sender_sink_input_volume(
++ this->m_paContext,
++ m_sourceToPASinkInput[iter->source.sourceID],
++ m_sourceToVolume[iter->source.sourceID],
++ this);
++ }
++ //TODO: check mute state was requested by controller.
++ break;
++ }
++ }
++}
++
++
++void RoutingSenderPULSE::getSourceOutputInfoCallback(pa_context *c, const pa_source_output_info *i, void *userdata)
++{
++ if (i == NULL)
++ {
++ return;
++ }
++
++ //search for corresponding Source
++ std::vector<RoutingSenderPULSESourceSinkConfig>::iterator iter = m_sinks.begin();
++ std::vector<RoutingSenderPULSESourceSinkConfig>::iterator iterEnd = m_sinks.end();
++ for (; iter < iterEnd; ++iter)
++ {
++ //try to match source PulseAudio properties agains config properties
++ const char *property_value = pa_proplist_gets(i->proplist, iter->propertyName.c_str());
++
++ if (property_value &&
++ ( std::string::npos != std::string(property_value).find(iter->propertyValue) ||
++ std::string::npos != iter->propertyValue.find(property_value)) )
++ {
++ logInfo("PULSE - source output registered:"
++ , " sourceOutputIndex:", i->index, "sinkID:", iter->sink.sinkID);
++
++ m_sinkToPASourceOutput[iter->sink.sinkID] = i->index;
++
++ //iterate pending connection request
++ // -> if there is a connection pending such that sink input "i" matches source from Connect() - create the connection in pulse
++ std::vector<RoutingSenderPULSEConnection>::iterator iterConn = m_activeConnections.begin();
++ std::vector<RoutingSenderPULSEConnection>::iterator iterConnEnd = m_activeConnections.end();
++ for (; iterConn < iterConnEnd; ++iterConn)
++ {
++ if (iterConn->sinkID == iter->sink.sinkID)
++ {
++ logInfo("PULSE - asyncConnect() - connectionID:", iterConn->connectionID,
++ "move sourceOutputIndex:", m_sinkToPASourceOutput[iterConn->sinkID], "to sourceIndex:", m_sourceToPASource[iterConn->sourceID]);
++
++ routing_sender_move_source_output(
++ this->m_paContext,
++ m_sinkToPASourceOutput[iterConn->sinkID],
++ m_sourceToPASource[iterConn->sourceID],
++ this);
++
++ //TODO: add callback for pulse move source output -> to send confirmation; for the moment directly send confirmation
++ this->m_routingReceiver->ackConnect(iterConn->handle, iterConn->connectionID, am::E_OK);
++ }
++ }
++
++ break;
++ }
++ }
++}
++
++
++void RoutingSenderPULSE::getSinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata)
++{
++ if (i != NULL)
++ {
++ if (strcmp("null", i->name) == 0)
++ {
++ this->m_paSinkNullIndex = i->index;
++ }
++
++ //search for corresponding (already registered) Sink
++ std::vector<RoutingSenderPULSESourceSinkConfig>::iterator iter = m_sinks.begin();
++ std::vector<RoutingSenderPULSESourceSinkConfig>::iterator iterEnd = m_sinks.end();
++ for (; iter < iterEnd; ++iter)
++ {
++ //first try to match the sink name from pulse audio sink name
++ if (iter->sink.name == std::string(i->name))
++ {
++ logInfo("PULSE sink name PA:", i->name, "config name:" ,iter->sink.name);
++ logInfo("PULSE - PA sink:", i->index,
++ "corresponding to AMGR sink:", iter->sink.sinkID, " - found");
++ m_sinkToPASink[iter->sink.sinkID] = i->index;
++ }
++ else
++ {
++ //try to match sink PulseAudio properties against config properties
++ const char *property_value = pa_proplist_gets(i->proplist, iter->propertyName.c_str());
++
++ if (!property_value) continue;
++
++ if (std::string::npos != iter->propertyValue.find(property_value))
++ {
++ logInfo("PULSE - PA sink:", i->index,
++ "corresponding to AMGR sink:", iter->sink.sinkID, " - found");
++
++ m_sinkToPASink[iter->sink.sinkID] = i->index;
++ }
++ }
++ }
++ }
++ else if (is_last)
++ {
++ routing_sender_get_source_info(this->m_paContext, this);
++ logInfo("PULSE - PA sinks registration completed");
++ }
++}
++
++void RoutingSenderPULSE::getSourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *userdata)
++{
++ if (i != NULL)
++ {
++ if (strcmp("null", i->name) == 0)
++ {
++ this->m_paSourceNullIndex = i->index;
++ }
++
++ //search for corresponding (already registered) Source
++ std::vector<RoutingSenderPULSESourceSinkConfig>::iterator iter = m_sources.begin();
++ std::vector<RoutingSenderPULSESourceSinkConfig>::iterator iterEnd = m_sources.end();
++ for (; iter < iterEnd; ++iter)
++ {
++ //first try to match the sink name from pulse audio sink name
++ if (iter->sink.name == std::string(i->name))
++ {
++ logInfo("PULSE - PA source:", i->index,
++ "corresponding to AMGR source:", iter->source.sourceID, " - found");
++ m_sourceToPASource[iter->source.sourceID] = i->index;
++ }
++ else
++ {
++ //try to match source PulseAudio properties against config properties
++ const char *property_value = pa_proplist_gets(i->proplist, iter->propertyName.c_str());
++
++ if (!property_value) continue;
++
++ if (std::string::npos != iter->propertyValue.find(property_value))
++ {
++ logInfo("PULSE - PA source:", i->index,
++ "corresponding to AMGR source:", iter->source.sourceID, " - found");
++
++ m_sourceToPASource[iter->source.sourceID] = i->index;
++ }
++ }
++
++ }
++ }
++ else if (is_last)
++ {
++ this->m_routingReceiver->hookDomainRegistrationComplete(this->m_domain.domainID);
++ logInfo("PULSE - PA sinks and source registration completed");
++ //TODO: - search for existing sink inputs & sources outputs
++ }
++}
+--
+1.7.9.5
+
diff --git a/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager_6.1.bbappend b/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager_6.1.bbappend
new file mode 100644
index 0000000..b16c4b0
--- /dev/null
+++ b/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager_6.1.bbappend
@@ -0,0 +1,10 @@
+FILESEXTRAPATHS_append := ":${THISDIR}/${PN}"
+
+SRC_URI_append = " file://0001-Porting-Pulse-Routing-Interface-from-AM-v1.x-to-AM-v.patch \
+ file://0001-Porting-Pulse-Control-Interface-from-AM-v1.x-to-AM-v.patch \
+ "
+EXTRA_OECMAKE += "-DWITH_PULSE_ROUTING_PLUGIN=ON -DWITH_PULSE_CONTROL_PLUGIN=ON -DWITH_ENABLED_IPC=DBUS"
+
+FILES_${PN} += "${libdir}/audioManager/control/* \
+ ${libdir}/audioManager/routing/* \
+ "