summaryrefslogtreecommitdiffstats
path: root/src/ahl-binding.c
diff options
context:
space:
mode:
authorTai Vuong <tvuong@audiokinetic.com>2017-10-04 14:21:28 -0400
committerTai Vuong <tvuong@audiokinetic.com>2017-10-04 14:21:28 -0400
commit539f65bb5ad87238422a5ca26c5b524c3be44bd1 (patch)
tree01fa955edf1e9b597a4a4247b3a35390c7b6ddbc /src/ahl-binding.c
parentd69dc9732886074e9f400961b500e70d5c8305d7 (diff)
Post AudioWorkshop Work phase v1
Diffstat (limited to 'src/ahl-binding.c')
-rw-r--r--src/ahl-binding.c730
1 files changed, 541 insertions, 189 deletions
diff --git a/src/ahl-binding.c b/src/ahl-binding.c
index 4b075c0..3f8d71d 100644
--- a/src/ahl-binding.c
+++ b/src/ahl-binding.c
@@ -26,61 +26,44 @@
// Global high-level binding context
AHLCtxT g_AHLCtx;
-// Workshop development topics:
-// - Associating streamID with a client id of afb. Has to be done with sessions?
-// - Declaring dependencies on other binding services (examples)
-// - json_object_put to free JSON objects? potential leaks currently
-// - no events on new HAL registered to alsacore?
-// - unregistering event subscription examples?
-// - discussion how to use property and event system to dispatch external parameterization (e.g. Wwise/Fiberdyne)
-// - discussion where to best isolate policy (controller or HLB plugin)
-// - Other HLB attributes to pass through (e.g. interrupted behavior)
-// - DBScale TLV warning
-// - GLib (internal) dependency
-// - HAL registration dependency (initialization order)
-// - Binding startup arguments for config file path
-// - Can we use the same HAL for different card numbers?
-// - Example use of volume ramping in HAL?
-// - Binding termination function
-// - AGL persistence framework?
-// - How to provide API services with config.xml (secrets and all)
+//afb_req_context_set ??
+// usr = afb_req_context_get(req);
+// afb_req_context_clear(req);
-
-// Helper macros/func for packaging JSON objects from C structures
-#define EndpointInfoStructToJSON(__JSON_OBJECT__, __ENDPOINTINFOSTRUCT__) \
- wrap_json_pack(&__JSON_OBJECT__, "{s:i,s:i,s:s,s:i}",\
- "endpoint_id", __ENDPOINTINFOSTRUCT__.endpointID, \
- "endpoint_type", __ENDPOINTINFOSTRUCT__.type, \
- "device_name", __ENDPOINTINFOSTRUCT__.deviceName, \
- "device_uri_type", __ENDPOINTINFOSTRUCT__.deviceURIType);
-
-static void StreamInfoStructToJSON(json_object **streamInfoJ, StreamInfoT streamInfo)
+static void AudioFormatStructToJSON(json_object **audioFormatJ, AudioFormatT * pAudioFormat)
{
- json_object *endpointInfoJ;
- EndpointInfoStructToJSON(endpointInfoJ,streamInfo.endpointInfo);
- wrap_json_pack(streamInfoJ, "{s:i}", "stream_id", streamInfo.streamID);
- json_object_object_add(*streamInfoJ,"endpoint_info",endpointInfoJ);
+ wrap_json_pack(audioFormatJ, "{s:i,s:i,s:i}",
+ "sample_rate", pAudioFormat->sampleRate,
+ "num_channels", pAudioFormat->numChannels,
+ "sample_type", pAudioFormat->sampleType);
}
-static streamID_t CreateNewStreamID()
+// Helper macros/func for packaging JSON objects from C structures
+static void EndpointInfoStructToJSON(json_object **endpointInfoJ, EndpointInfoT * pEndpointInfo)
{
- streamID_t newID = g_AHLCtx.nextStreamID;
- g_AHLCtx.nextStreamID++;
- return newID;
+ json_object *formatInfoJ = NULL;
+ wrap_json_pack(endpointInfoJ, "{s:i,s:i,s:s,s:i}",
+ "endpoint_id", pEndpointInfo->endpointID,
+ "endpoint_type", pEndpointInfo->type,
+ "device_name", pEndpointInfo->gsDeviceName->str,
+ "device_uri_type", pEndpointInfo->deviceURIType);
+ AudioFormatStructToJSON(&formatInfoJ,&pEndpointInfo->format);
+ json_object_object_add(*endpointInfoJ,"format",formatInfoJ);
}
-
-static int FindRoleIndex( const char * in_pAudioRole)
+
+static void StreamInfoStructToJSON(json_object **streamInfoJ, StreamInfoT * pStreamInfo)
{
- int index = -1; // Not found
- for (int i = 0; i < g_AHLCtx.policyCtx.iNumberRoles; i++)
- {
- GString gs = g_array_index( g_AHLCtx.policyCtx.pAudioRoles, GString, i );
- if ( strcasecmp(gs.str,in_pAudioRole) == 0 )
- index = i;
- }
- return index;
+ json_object *endpointInfoJ = NULL;
+ EndpointInfoStructToJSON(&endpointInfoJ,pStreamInfo->pEndpointInfo);
+ wrap_json_pack(streamInfoJ, "{s:i,s:i,s:i,s:s}",
+ "stream_id", pStreamInfo->streamID,
+ "state", pStreamInfo->streamState,
+ "mute", pStreamInfo->streamMute,
+ "device_uri",pStreamInfo->pEndpointInfo->gsDeviceURI->str);
+ json_object_object_add(*streamInfoJ,"endpoint_info",endpointInfoJ);
}
+
static EndpointInfoT * GetEndpointInfo(endpointID_t in_endpointID, EndpointTypeT in_endpointType)
{
EndpointInfoT * pEndpointInfo = NULL;
@@ -104,6 +87,104 @@ static EndpointInfoT * GetEndpointInfo(endpointID_t in_endpointID, EndpointTypeT
return pEndpointInfo;
}
+static StreamInfoT * GetActiveStream(streamID_t in_streamID)
+{
+ int iNumActiveStreams = g_AHLCtx.policyCtx.pActiveStreams->len;
+ StreamInfoT * pStreamInfo = NULL;
+ for ( int i = 0; i < iNumActiveStreams ; i++ ) {
+ StreamInfoT * pCurStreamInfo = &g_array_index(g_AHLCtx.policyCtx.pActiveStreams,StreamInfoT,i);
+ if (pCurStreamInfo->streamID == in_streamID){
+ pStreamInfo = pCurStreamInfo;
+ break;
+ }
+ }
+ return pStreamInfo;
+}
+
+static int CreateEvents()
+{
+ int err = 0;
+
+ g_AHLCtx.policyCtx.propertyEvent = afb_daemon_make_event(AHL_ENDPOINT_PROPERTY_EVENT);
+ err = !afb_event_is_valid(g_AHLCtx.policyCtx.propertyEvent);
+ if (err) {
+ AFB_ERROR("Could not create endpoint property change event");
+ err++;
+ }
+
+ g_AHLCtx.policyCtx.volumeEvent = afb_daemon_make_event(AHL_ENDPOINT_VOLUME_EVENT);
+ err = !afb_event_is_valid(g_AHLCtx.policyCtx.volumeEvent);
+ if (err) {
+ AFB_ERROR("Could not create endpoint volume change event");
+ err++;
+ }
+
+ g_AHLCtx.policyCtx.postEvent = afb_daemon_make_event(AHL_POST_EVENT);
+ err = !afb_event_is_valid(g_AHLCtx.policyCtx.postEvent);
+ if (err) {
+ AFB_ERROR("Could not create post event call event");
+ err++;
+ }
+
+ return err;
+}
+
+static void AhlBindingTerm()
+{
+ // Policy termination
+ Policy_Term();
+
+ // Events
+ for (int i = 0; i < g_AHLCtx.policyCtx.pEventList->len; i++)
+ {
+ // For each event within the role
+ GArray * pRoleEventArray = g_ptr_array_index( g_AHLCtx.policyCtx.pEventList, i );
+ for (int j = 0 ; j < pRoleEventArray->len; j++)
+ {
+ GString * gsEventName = &g_array_index(pRoleEventArray,GString,j);
+ g_string_free(gsEventName,TRUE);
+ }
+ g_array_free(pRoleEventArray,TRUE);
+ pRoleEventArray = NULL;
+ }
+ g_ptr_array_free(g_AHLCtx.policyCtx.pEventList,TRUE);
+ g_AHLCtx.policyCtx.pEventList = NULL;
+
+ // Endpoints
+ TermEndpoints();
+
+ // TODO: Need to free g_strings in HAL list
+ g_array_free(g_AHLCtx.pHALList,TRUE);
+ g_hash_table_remove_all(g_AHLCtx.policyCtx.pRolePriority);
+ g_hash_table_destroy(g_AHLCtx.policyCtx.pRolePriority);
+ // TODO: Need to free g_strings in audio roles list
+ g_array_free(g_AHLCtx.policyCtx.pAudioRoles,TRUE);
+ g_array_free(g_AHLCtx.policyCtx.pInterruptBehavior,TRUE);
+ g_array_free(g_AHLCtx.policyCtx.pActiveStreams,TRUE);
+
+ AFB_INFO("Audio high-level Binding succesTermination");
+}
+
+static streamID_t CreateNewStreamID()
+{
+ streamID_t newID = g_AHLCtx.nextStreamID;
+ g_AHLCtx.nextStreamID++;
+ return newID;
+}
+
+static int FindRoleIndex( const char * in_pAudioRole)
+{
+ int index = -1; // Not found
+ for (int i = 0; i < g_AHLCtx.policyCtx.iNumberRoles; i++)
+ {
+ GString gs = g_array_index( g_AHLCtx.policyCtx.pAudioRoles, GString, i );
+ if ( strcasecmp(gs.str,in_pAudioRole) == 0 )
+ index = i;
+ }
+ return index;
+}
+
+
// Binding initialization
PUBLIC int AhlBindingInit()
{
@@ -121,12 +202,28 @@ PUBLIC int AhlBindingInit()
// Parse high-level binding JSON configuration file (will build device lists)
errcount += ParseHLBConfig();
+ atexit(AhlBindingTerm);
+
+ // This API uses services from low level audio
+ errcount = afb_daemon_require_api_v2("alsacore",1) ;
+ if( errcount != 0 )
+ {
+ AFB_ERROR("Audio high level API requires alsacore API to be available");
+ return 1;
+ }
+
+ errcount += CreateEvents();
+
+ // Parse high-level binding JSON configuration file (will build device lists)
+ errcount += ParseHLBConfig();
+
+ // Policy initialization
+ errcount += Policy_Init();
+
// Initialize list of active streams
g_AHLCtx.policyCtx.pActiveStreams = g_array_new(FALSE,TRUE,sizeof(StreamInfoT));
- // TODO: Register for device changes ALSA low level audio service
-
- // TODO: Perform other binding initialization tasks (e.g. broadcast service ready event?)
+ // TODO: Register for device changes ALSA low level audio service or HAL
// TODO: Use AGL persistence framework to retrieve and set inital state/volumes/properties
@@ -134,11 +231,15 @@ PUBLIC int AhlBindingInit()
return errcount;
}
-// TODO: AhlBindingTerm()?
-// // TODO: Use AGL persistence framework to retrieve and set inital state/volumes
+PUBLIC void AhlOnEvent(const char *evtname, json_object *eventJ)
+{
+ // TODO: Implement handling events from HAL...
+ AFB_DEBUG("AHL received event %s", evtname);
+}
-// TODO: OnEventFunction
-// if ALSA device availability change -> update source / sink list
+// TODO: OnEventFunction when it actually subscribe to other binding events
+// TODO: Dynamic device handling
+// if HAL device availability change -> update source / sink list
// Call policy to attempt to assign role based on information (e.g. device name)
// Policy should also determine priority (insert device in right spot in the list)
@@ -156,26 +257,23 @@ PUBLIC void audiohlapi_get_sources(struct afb_req req)
return;
}
- sourcesJ = json_object_new_array();
-
AFB_DEBUG("Filtering devices according to specified audio role=%s", audioRole);
- // Check that the role requested exists in current configuration
- int roleIndex = FindRoleIndex(audioRole);
- if ( roleIndex == -1)
+ // List device only for current audio role and device type (pick the role device list)
+ int iRoleIndex = FindRoleIndex(audioRole);
+ if ( iRoleIndex < 0)
{
afb_req_fail_f(req, "Invalid arguments", "Requested audio role does not exist in current configuration -> %s", json_object_get_string(queryJ));
return;
}
- // List device only for current audio role and device type (pick the role device list)
- int iRoleIndex = FindRoleIndex(audioRole);
- if (iRoleIndex >= 0)
+ else
{
+ sourcesJ = json_object_new_array();
GArray * pRoleSourceDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSourceEndpoints, iRoleIndex );
int iNumberDevices = pRoleSourceDeviceArray->len;
for ( int j = 0 ; j < iNumberDevices; j++)
{
EndpointInfoT sourceInfo = g_array_index(pRoleSourceDeviceArray,EndpointInfoT,j);
- EndpointInfoStructToJSON(sourceJ, sourceInfo);
+ EndpointInfoStructToJSON(&sourceJ, &sourceInfo);
json_object_array_add(sourcesJ, sourceJ);
}
}
@@ -197,26 +295,23 @@ PUBLIC void audiohlapi_get_sinks(struct afb_req req)
return;
}
- sinksJ = json_object_new_array();
-
AFB_DEBUG("Filtering devices according to specified audio role=%s", audioRole);
- // Check that the role requested exists in current configuration
- int roleIndex = FindRoleIndex(audioRole);
- if ( roleIndex == -1)
+ // List device only for current audio role and device type (pick the role device list)
+ int iRoleIndex = FindRoleIndex(audioRole);
+ if ( iRoleIndex < 0)
{
afb_req_fail_f(req, "Invalid arguments", "Requested audio role does not exist in current configuration -> %s", json_object_get_string(queryJ));
return;
}
- // List device only for current audio role and device type (pick the role device list)
- int iRoleIndex = FindRoleIndex(audioRole);
- if (iRoleIndex >= 0)
+ else
{
+ sinksJ = json_object_new_array();
GArray * pRoleSinkDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, iRoleIndex );
int iNumberDevices = pRoleSinkDeviceArray->len;
for ( int j = 0 ; j < iNumberDevices; j++)
{
EndpointInfoT sinkInfo = g_array_index(pRoleSinkDeviceArray,EndpointInfoT,j);
- EndpointInfoStructToJSON(sinkJ, sinkInfo);
+ EndpointInfoStructToJSON(&sinkJ, &sinkInfo);
json_object_array_add(sinksJ, sinkJ);
}
}
@@ -230,10 +325,10 @@ PUBLIC void audiohlapi_stream_open(struct afb_req req)
StreamInfoT streamInfo;
json_object *queryJ = NULL;
char * audioRole = NULL;
- EndpointTypeT endpointType;
- endpointID_t endpointID = UNDEFINED_ID;
- int policyAllowed = 0;
- EndpointInfoT endpointInfo;
+ EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
+ endpointID_t endpointID = AHL_UNDEFINED;
+ int policyAllowed = AHL_POLICY_REJECT;
+ EndpointInfoT * pEndpointInfo = NULL;
queryJ = afb_req_json(req);
int err = wrap_json_unpack(queryJ, "{s:s,s:i,s?i}", "audio_role", &audioRole, "endpoint_type", &endpointType, "endpoint_id", &endpointID);
@@ -261,32 +356,32 @@ PUBLIC void audiohlapi_stream_open(struct afb_req req)
return;
}
- if (endpointID == UNDEFINED_ID)
+ if (endpointID == AHL_UNDEFINED)
{
// Assign a device based on configuration priority (first in the list for requested role and endpoint type)
- endpointInfo = g_array_index(pRoleDeviceArray,EndpointInfoT,0);
+ pEndpointInfo = &g_array_index(pRoleDeviceArray,EndpointInfoT,0);
+ streamInfo.endpointSelMode = AHL_ENDPOINTSELMODE_AUTO;
}
else{
+ streamInfo.endpointSelMode = AHL_ENDPOINTSELMODE_MANUAL;
// Find specified endpoint ID in list of devices
int iNumberDevices = pRoleDeviceArray->len;
- int iEndpointFound = 0;
for ( int j = 0 ; j < iNumberDevices; j++)
{
- endpointInfo = g_array_index(pRoleDeviceArray,EndpointInfoT,j);
- if (endpointInfo.endpointID == endpointID) {
- iEndpointFound = 1;
+ pEndpointInfo = &g_array_index(pRoleDeviceArray,EndpointInfoT,j);
+ if (pEndpointInfo->endpointID == endpointID) {
break;
}
}
- if (iEndpointFound == 0) {
+ if (pEndpointInfo == NULL) {
afb_req_fail_f(req, "Endpoint not available", "Specified endpoint not available for role:%s and device type:%d endpoint id %d",audioRole,endpointType,endpointID);
return;
}
}
// Call policy to verify whether creating a new audio stream is allowed in current context and possibly take other actions
- policyAllowed = Policy_OpenStream(audioRole, endpointType, endpointID);
- if (policyAllowed == AUDIOHL_POLICY_REJECT)
+ policyAllowed = Policy_OpenStream(audioRole, endpointType, pEndpointInfo->endpointID);
+ if (policyAllowed == AHL_POLICY_REJECT)
{
afb_req_fail(req, "Audio policy violation", "Open stream not allowed in current context");
return;
@@ -294,12 +389,30 @@ PUBLIC void audiohlapi_stream_open(struct afb_req req)
// Create stream
streamInfo.streamID = CreateNewStreamID(); // create new ID
- streamInfo.endpointInfo = endpointInfo;
+ streamInfo.streamState = STREAM_STATUS_READY;
+ streamInfo.streamMute = STREAM_UNMUTED;
+ streamInfo.pEndpointInfo = pEndpointInfo;
+
+ char streamEventName[128];
+ snprintf(streamEventName,128,"ahl_streamstate_%d",streamInfo.streamID);
+
+ streamInfo.streamStateEvent = afb_daemon_make_event(streamEventName);
+ err = !afb_event_is_valid(streamInfo.streamStateEvent);
+ if (err) {
+ afb_req_fail(req, "Stream event creation failure", "Could not create stream specific state change event");
+ return;
+ }
+
+ err = afb_req_subscribe(req,streamInfo.streamStateEvent);
+ if (err) {
+ afb_req_fail(req, "Stream event subscription failure", "Could not subscribe to stream specific state change event");
+ return;
+ }
// Push stream on active stream list
g_array_append_val( g_AHLCtx.policyCtx.pActiveStreams, streamInfo );
- StreamInfoStructToJSON(&streamInfoJ,streamInfo);
+ StreamInfoStructToJSON(&streamInfoJ,&streamInfo);
afb_req_success(req, streamInfoJ, "Stream info structure");
}
@@ -307,7 +420,8 @@ PUBLIC void audiohlapi_stream_open(struct afb_req req)
PUBLIC void audiohlapi_stream_close(struct afb_req req)
{
json_object *queryJ = NULL;
- streamID_t streamID = UNDEFINED_ID;
+ streamID_t streamID = AHL_UNDEFINED;
+ int policyAllowed = AHL_POLICY_REJECT;
queryJ = afb_req_json(req);
int err = wrap_json_unpack(queryJ, "{s:i}", "stream_id", &streamID);
@@ -319,12 +433,36 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req)
// TODO: Validate that the application ID from which the stream close is coming is the one that opened it, otherwise fail and do nothing
+ // Call policy to verify whether creating a new audio stream is allowed in current context and possibly take other actions
+ policyAllowed = Policy_CloseStream(streamID);
+ if (policyAllowed == AHL_POLICY_REJECT)
+ {
+ afb_req_fail(req, "Audio policy violation", "Close stream not allowed in current context");
+ return;
+ }
+
// Remove from active stream list (if present)
int iNumActiveStreams = g_AHLCtx.policyCtx.pActiveStreams->len;
int iStreamFound = 0;
for ( int i = 0; i < iNumActiveStreams ; i++ ) {
StreamInfoT streamInfo = g_array_index(g_AHLCtx.policyCtx.pActiveStreams,StreamInfoT,i);
if (streamInfo.streamID == streamID){
+ // Unsubscribe client from stream events
+ char streamEventName[128];
+ snprintf(streamEventName,128,"ahl_streamstate_%d",streamInfo.streamID);
+ int iValid = afb_event_is_valid(streamInfo.streamStateEvent);
+ if (iValid) {
+ err = afb_req_unsubscribe(req,streamInfo.streamStateEvent);
+ if (err) {
+ afb_req_fail(req, "Stream event subscription failure", "Could not subscribe to stream specific state change event");
+ return;
+ }
+ }
+ else{
+ AFB_WARNING("Stream event no longer valid and therefore not unsubscribed");
+ break;
+ }
+
g_array_remove_index(g_AHLCtx.policyCtx.pActiveStreams,i);
iStreamFound = 1;
break;
@@ -339,35 +477,142 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req)
afb_req_success(req, NULL, "Stream close completed");
}
-// Endpoints
+ PUBLIC void audiohlapi_set_stream_state(struct afb_req req)
+ {
+ json_object *queryJ = NULL;
+ streamID_t streamID = AHL_UNDEFINED;
+ StreamStateT streamState = STREAM_STATUS_MAXVALUE;
+ int policyAllowed = AHL_POLICY_REJECT;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:i,s:i}", "stream_id", &streamID,"state",&streamState);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+ AFB_DEBUG("Parsed input arguments = stream_id:%d, state:%d", streamID,streamState);
+
+ StreamInfoT * pStreamInfo = GetActiveStream(streamID);
+ if (pStreamInfo == NULL) {
+ afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID);
+ return;
+ }
+
+ policyAllowed = Policy_SetStreamState(streamID,streamState);
+ if (policyAllowed == AHL_POLICY_REJECT)
+ {
+ afb_req_fail(req, "Audio policy violation", "Change stream state not allowed in current context");
+ return;
+ }
+
+ pStreamInfo->streamState = streamState;
+ // Package event data
+ json_object * eventDataJ = NULL;
+ err = wrap_json_pack(&eventDataJ,"{s:i,s:i}","mute",pStreamInfo->streamMute,"state",streamState);
+ if (err) {
+ afb_req_fail_f(req, "Invalid event data for stream state event", "Invalid event data for stream state event: %s",json_object_to_json_string(eventDataJ));
+ return;
+ }
+ afb_event_push(pStreamInfo->streamStateEvent,eventDataJ);
+
+ afb_req_success(req, NULL, "Set stream state");
+ }
+
+ PUBLIC void audiohlapi_set_stream_mute(struct afb_req req)
+ {
+ json_object *queryJ = NULL;
+ streamID_t streamID = AHL_UNDEFINED;
+ StreamMuteT muteState = STREAM_MUTE_MAXVALUE;
+ int policyAllowed = AHL_POLICY_REJECT;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:i,s:i}", "stream_id", &streamID,"mute",&muteState);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+ AFB_DEBUG("Parsed input arguments = stream_id:%d, mute:%d", streamID,muteState);
+
+ StreamInfoT * pStreamInfo = GetActiveStream(streamID);
+ if (pStreamInfo == NULL) {
+ afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID);
+ return;
+ }
+
+ policyAllowed = Policy_SetStreamMute(streamID,muteState);
+ if (policyAllowed == AHL_POLICY_REJECT)
+ {
+ afb_req_fail(req, "Audio policy violation", "Mute stream not allowed in current context");
+ return;
+ }
+
+ pStreamInfo->streamMute = muteState;
+
+ // Package event data
+ json_object * eventDataJ = NULL;
+ err = wrap_json_pack(&eventDataJ,"{s:i,s:i}","mute",muteState,"state",pStreamInfo->streamState);
+ if (err) {
+ afb_req_fail_f(req, "Invalid event data for stream state event", "Invalid event data for stream state event: %s",json_object_to_json_string(eventDataJ));
+ return;
+ }
+ afb_event_push(pStreamInfo->streamStateEvent,eventDataJ);
+
+ afb_req_success(req, NULL, "Set stream mute completed");
+ }
+
+PUBLIC void audiohlapi_get_stream_info(struct afb_req req)
+ {
+ json_object *queryJ = NULL;
+ streamID_t streamID = AHL_UNDEFINED;
+ json_object * streamInfoJ = NULL;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:i}", "stream_id", &streamID);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+ AFB_DEBUG("Parsed input arguments = stream_id:%d", streamID);
+
+ StreamInfoT * pStreamInfo = GetActiveStream(streamID);
+ if (pStreamInfo == NULL) {
+ afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID);
+ return;
+ }
+
+ StreamInfoStructToJSON(&streamInfoJ,pStreamInfo);
+
+ afb_req_success(req, streamInfoJ, "Get stream info completed");
+ }
+
PUBLIC void audiohlapi_set_volume(struct afb_req req)
{
json_object *queryJ = NULL;
- endpointID_t endpointID = UNDEFINED_ID;
+ endpointID_t endpointID = AHL_UNDEFINED;
EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
char * volumeStr = NULL;
- int rampTimeMS = 0;
- int policyAllowed = 0;
+ int policyAllowed = AHL_POLICY_REJECT;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s?i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"volume",&volumeStr,"ramp_time_ms",&rampTimeMS);
+ int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"volume",&volumeStr);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d volume:%s ramp_time_ms: %d", endpointType,endpointID,volumeStr,rampTimeMS);
-
- // TODO: Parse volume string to support increment/absolute/percent notation
+ AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d volume:%s", endpointType,endpointID,volumeStr);
+
+ // TODO: Parse volume string to support increment/absolute/percent notation (or delegate to action / policy layer to interpret)
int vol = atoi(volumeStr);
- policyAllowed = Policy_SetVolume(endpointType, endpointID, volumeStr, rampTimeMS); // TODO: Potentially retrieve modified value by policy (e.g. volume limit)
+ // TODO: Policy needs way to set cached endpoint volume value (eg.)
+ policyAllowed = Policy_SetVolume(endpointType, endpointID, volumeStr); // TODO: Potentially retrieve modified value by policy (e.g. volume limit)
if (!policyAllowed)
{
afb_req_fail(req, "Audio policy violation", "Set volume not allowed in current context");
return;
}
- // TODO: Cache during device enumeration for efficiency
+ // TODO: Cache HAL control name during device enumeration for efficiency
EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType);
if (pEndpointInfo == NULL)
{
@@ -376,27 +621,36 @@ PUBLIC void audiohlapi_set_volume(struct afb_req req)
}
// Using audio role available from endpoint to target the right HAL control (build string based on convention)
- char halControlName[AUDIOHL_MAXHALCONTROLNAME_LENGTH];
- strncpy(halControlName,pEndpointInfo->audioRole,AUDIOHL_MAXHALCONTROLNAME_LENGTH);
- strcat(halControlName,"_Ramp"); // Or _Vol for direct control (no ramping)
+ GString * gsHALControlName = g_string_new(pEndpointInfo->gsAudioRole->str);
+ g_string_append(gsHALControlName,"_Ramp"); // Or _Vol for direct control (no ramping)
// Set endpoint volume using HAL services (leveraging ramps etc.)
json_object *j_response, *j_query = NULL;
// Package query
- err = wrap_json_pack(&j_query,"{s:s,s:i}","label",halControlName, "val",vol);
+ err = wrap_json_pack(&j_query,"{s:s,s:i}","label",gsHALControlName->str, "val",vol);
if (err) {
afb_req_fail_f(req, "Invalid query for HAL ctlset", "Invalid query for HAL ctlset: %s",json_object_to_json_string(j_query));
return;
}
+ // TODO: Move this to ref implmentation policy
// Set the volume using the HAL
- err = afb_service_call_sync(pEndpointInfo->halAPIName, "ctlset", j_query, &j_response);
+ err = afb_service_call_sync(pEndpointInfo->gsHALAPIName->str, "ctlset", j_query, &j_response);
if (err) {
- afb_req_fail_f(req, "HAL ctlset failure", "Could not ctlset on HAL: %s",pEndpointInfo->halAPIName);
+ afb_req_fail_f(req, "HAL ctlset failure", "Could not ctlset on HAL: %s",pEndpointInfo->gsHALAPIName->str);
return;
}
AFB_DEBUG("HAL ctlset response=%s", json_object_to_json_string(j_response));
+
+ // Package event data
+ json_object * eventDataJ = NULL;
+ err = wrap_json_pack(&eventDataJ,"{s:i,s:i,s:i}","endpoint_id",endpointID,"endpoint_type",endpointType,"value",vol);
+ if (err) {
+ afb_req_fail_f(req, "Invalid event data for volume event", "Invalid event data for volume event: %s",json_object_to_json_string(eventDataJ));
+ return;
+ }
+ afb_event_push(g_AHLCtx.policyCtx.volumeEvent,eventDataJ);
afb_req_success(req, NULL, "Set volume completed");
}
@@ -404,9 +658,9 @@ PUBLIC void audiohlapi_set_volume(struct afb_req req)
PUBLIC void audiohlapi_get_volume(struct afb_req req)
{
json_object *queryJ = NULL;
- endpointID_t endpointID = UNDEFINED_ID;
+ endpointID_t endpointID = AHL_UNDEFINED;
EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
- json_object *volumeJ;
+ json_object * volumeJ = NULL;
queryJ = afb_req_json(req);
int err = wrap_json_unpack(queryJ, "{s:i,s:i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID);
@@ -416,7 +670,7 @@ PUBLIC void audiohlapi_get_volume(struct afb_req req)
}
AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d", endpointType,endpointID);
- // TODO: Cache during device enumeration for efficiency
+ // TODO: Cache HAL control name during device enumeration for efficiency
EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType);
if (pEndpointInfo == NULL)
{
@@ -425,24 +679,25 @@ PUBLIC void audiohlapi_get_volume(struct afb_req req)
}
// Using audio role available from endpoint to target the right HAL control (build string based on convention)
- char halControlName[AUDIOHL_MAXHALCONTROLNAME_LENGTH];
- strncpy(halControlName,pEndpointInfo->audioRole,AUDIOHL_MAXHALCONTROLNAME_LENGTH);
- strcat(halControlName,"_Vol"); // Use current value, not ramp target
+ GString * gsHALControlName = g_string_new(pEndpointInfo->gsAudioRole->str);
+ g_string_append(gsHALControlName,"_Vol"); // Use current value, not ramp target
// Set endpoint volume using HAL services (leveraging ramps etc.)
json_object *j_response, *j_query = NULL;
+ // TODO: Returned cached endpoint volume value (controlled by policy)
// Package query
- err = wrap_json_pack(&j_query,"{s:s}","label",halControlName);
+ err = wrap_json_pack(&j_query,"{s:s}","label",gsHALControlName->str);
if (err) {
afb_req_fail_f(req, "Invalid query for HAL ctlget", "Invalid query for HAL ctlget: %s",json_object_to_json_string(j_query));
return;
}
+ // TODO: Return cached value or move to policy
// Set the volume using the HAL
- err = afb_service_call_sync(pEndpointInfo->halAPIName, "ctlget", j_query, &j_response);
+ err = afb_service_call_sync(pEndpointInfo->gsHALAPIName->str, "ctlget", j_query, &j_response);
if (err) {
- afb_req_fail_f(req, "HAL ctlget failure", "Could not ctlget on HAL: %s",pEndpointInfo->halAPIName);
+ afb_req_fail_f(req, "HAL ctlget failure", "Could not ctlget on HAL: %s",pEndpointInfo->gsHALAPIName->str);
return;
}
AFB_INFO("HAL ctlget response=%s", json_object_to_json_string(j_response));
@@ -464,38 +719,77 @@ PUBLIC void audiohlapi_get_volume(struct afb_req req)
afb_req_success(req, volumeJ, "Retrieved volume value");
}
+// Properties
+PUBLIC void audiohlapi_get_list_properties(struct afb_req req)
+{
+ json_object *queryJ = NULL;
+ endpointID_t endpointID = AHL_UNDEFINED;
+ EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
+ json_object * endpointPropsJ = NULL;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:i,s:i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+ AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d", endpointType,endpointID);
+
+ EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType);
+ if (pEndpointInfo == NULL)
+ {
+ afb_req_fail_f(req, "Endpoint not found", "Endpoint information not found for id:%d type%d",endpointID,endpointType);
+ return;
+ }
+
+ // Build and return list of properties for specific endpoint
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, pEndpointInfo->pPropTable);
+ endpointPropsJ = json_object_new_array();
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ json_object * propJ = json_object_new_string(key);
+ json_object_array_add(endpointPropsJ, propJ);
+ }
+
+ afb_req_success(req, endpointPropsJ, "Retrieved property list for endpoint");
+}
+
PUBLIC void audiohlapi_set_property(struct afb_req req)
{
json_object *queryJ = NULL;
- endpointID_t endpointID = UNDEFINED_ID;
+ endpointID_t endpointID = AHL_UNDEFINED;
EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
char * propertyName = NULL;
char * propValueStr = NULL;
- int rampTimeMS = 0;
- int policyAllowed = 0;
+ int policyAllowed = AHL_POLICY_REJECT;
+
+ // TODO: object type detection (string = state, numeric = property)
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s:s,s?i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"property_name",&propertyName,"value",&propValueStr,"ramp_time_ms",&rampTimeMS);
+ int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s:s}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"property_name",&propertyName,"value",&propValueStr);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d property_name:%s value:%s ramp_time_ms:%d", endpointType,endpointID,propertyName,propValueStr,rampTimeMS);
+ AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d property_name:%s value:%s", endpointType,endpointID,propertyName,propValueStr);
// TODO: Parse property value string to support increment/absolute/percent notation
// Call policy to allow custom policy actions in current context
- policyAllowed = Policy_SetProperty(endpointType, endpointID, propertyName, propValueStr, rampTimeMS); // TODO: Potentially retrieve modified value by policy (e.g. parameter limit)
+ policyAllowed = Policy_SetProperty(endpointType, endpointID, propertyName, propValueStr); // TODO: Potentially retrieve modified value by policy (e.g. parameter limit)
if (!policyAllowed)
{
afb_req_fail(req, "Audio policy violation", "Set endpoint property not allowed in current context");
return;
}
- // TODO: Set endpoint property (dispatch on right service target)
- // Property targets (Internal,Wwise,Fiberdyne) (e.g. Wwise.ParamX, Fiberdyne.ParamY, Internal.ParamZ)
- // Cache value in property list
- // TBD
+ // TODO: Policy to dispatch on right service target
+ // TODO: Policy tp cache value in property list
+
+ afb_event_push(g_AHLCtx.policyCtx.propertyEvent,queryJ);
afb_req_success(req, NULL, "Set property completed");
}
@@ -503,21 +797,22 @@ PUBLIC void audiohlapi_set_property(struct afb_req req)
PUBLIC void audiohlapi_get_property(struct afb_req req)
{
json_object *queryJ = NULL;
- endpointID_t endpointID = UNDEFINED_ID;
+ endpointID_t endpointID = AHL_UNDEFINED;
EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
char * propertyName = NULL;
json_object *propertyValJ;
double value = 0.0;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:i,s?i,s:s}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"property_name",&propertyName);
+ int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"property_name",&propertyName);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d property_name:%s", endpointType,endpointID,propertyName);
- // TODO: Retrieve cached property value
+ // TODO: Retriev189e cached property value
+ // TODO Account for properties with string types
value = 93.0; // TODO: Get actual property value
propertyValJ = json_object_new_double(value);
@@ -525,94 +820,97 @@ PUBLIC void audiohlapi_get_property(struct afb_req req)
afb_req_success(req, propertyValJ, "Retrieved property value");
}
-PUBLIC void audiohlapi_set_state(struct afb_req req)
+// Audio related events
+
+PUBLIC void audiohlapi_get_list_events(struct afb_req req)
{
json_object *queryJ = NULL;
- endpointID_t endpointID = UNDEFINED_ID;
- EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
- char * stateName = NULL;
- char * stateValue = NULL;
- int policyAllowed = 0;
+ char * audioRole = NULL;
+ json_object * roleEventsJ = NULL;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s:s}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"state_name",&stateName,"state_value",&stateValue);
+ int err = wrap_json_unpack(queryJ, "{s:s}", "audio_role",&audioRole);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d state_name:%s state_value:%s", endpointType,endpointID,stateName,stateValue);
+ AFB_DEBUG("Parsed input arguments = audio_role:%s",audioRole);
- // Check that state provided is within list of known state for this config
- char * pDefaultStateValue = g_hash_table_lookup(g_AHLCtx.pDefaultStatesHT, stateName);
- if (pDefaultStateValue == NULL)
- {
- afb_req_fail_f(req, "Invalid arguments", "State provided is not known to configuration query=%s", stateName);
+ // Build and return list of events for specific audio role
+ int iRoleIndex = FindRoleIndex(audioRole);
+ if (iRoleIndex < 0) {
+ afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole);
return;
}
- // Call policy to allow custom policy actions in current context
- policyAllowed = Policy_SetState(endpointType, endpointID, stateName, stateValue); // TODO: Potentially retrieve modified value by policy (e.g. state change)
- if (!policyAllowed)
+ GArray * pRoleEventArray = g_ptr_array_index( g_AHLCtx.policyCtx.pEventList, iRoleIndex );
+ roleEventsJ = json_object_new_array();
+ int iNumberEvents = pRoleEventArray->len;
+ for ( int i = 0 ; i < iNumberEvents; i++)
{
- afb_req_fail(req, "Audio policy violation", "Set endpoint state not allowed in current context");
- return;
+ GString gsEventName = g_array_index(pRoleEventArray,GString,i);
+ json_object * eventJ = json_object_new_string(gsEventName.str);
+ json_object_array_add(roleEventsJ, eventJ);
}
-
- // Change the state of the endpoint as requested
-
- afb_req_success(req, NULL, "Set endpoint state completed");
+
+ afb_req_success(req, roleEventsJ, "Retrieved event list for audio role");
}
-PUBLIC void audiohlapi_get_state(struct afb_req req)
+PUBLIC void audiohlapi_post_event(struct afb_req req)
{
json_object *queryJ = NULL;
- endpointID_t endpointID = UNDEFINED_ID;
- EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
- json_object *stateValJ;
- char * stateName = NULL;
- char * stateValue = NULL;
+ char * eventName = NULL;
+ char * audioRole = NULL;
+ char * mediaName = NULL;
+ json_object *eventContext = NULL;
+ int policyAllowed = AHL_POLICY_REJECT;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"state_name",&stateName);
+ int err = wrap_json_unpack(queryJ, "{s:s,s:s,s?s,s?o}", "event_name", &eventName,"audio_role",&audioRole,"media_name",&mediaName,"event_context",&eventContext);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d state_name:%s", endpointType,endpointID,stateName);
-
- stateValJ = json_object_new_string(stateValue);
- // return cached value
+ AFB_DEBUG("Parsed input arguments = event_name:%s audio_role:%s", eventName,audioRole);
- afb_req_success(req, stateValJ, "Retrieved state value");
-}
+ // Verify if known event for audio role
+ int iRoleIndex = FindRoleIndex(audioRole);
+ if (iRoleIndex < 0) {
+ afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole);
+ return;
+ }
-// Sound events
-PUBLIC void audiohlapi_post_sound_event(struct afb_req req)
-{
- json_object *queryJ = NULL;
- char * eventName = NULL;
- char * mediaName = NULL;
- char * audioRole = NULL;
- json_object *audioContext = NULL;
- int policyAllowed = 0;
-
- queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:s,s:s,s?s,s?o}", "event_name", &eventName,"audio_role",&audioRole,"media_name",&mediaName,"audio_context",&audioContext);
- if (err) {
- afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ GArray * pRoleEventArray = NULL;
+ pRoleEventArray = g_ptr_array_index( g_AHLCtx.policyCtx.pEventList, iRoleIndex );
+ if (pRoleEventArray->len == 0) {
+ afb_req_fail_f(req, "No available events", "No available events for role:%s",audioRole);
+ return;
+ }
+ // Check to find specific event
+ int iEventFound = 0;
+ for (int i = 0; i < pRoleEventArray->len; i++)
+ {
+ GString gs = g_array_index( pRoleEventArray, GString, i );
+ if ( strcasecmp(gs.str,eventName) == 0 )
+ {
+ iEventFound = 1;
+ break;
+ }
+ }
+ if (!iEventFound) {
+ afb_req_fail_f(req, "Event not found for audio role", "Event not found for roke:%s",audioRole);
return;
}
- AFB_DEBUG("Parsed input arguments = event_name:%s audio_role:%s media_name:%s", eventName,audioRole,mediaName);
// Call policy to allow custom policy actions in current context (e.g. cancel playback)
- policyAllowed = Policy_PostSoundEvent(eventName, audioRole, mediaName, (void*)audioContext); // TODO: Potentially retrieve modified value by policy (e.g. change media)
+ policyAllowed = Policy_PostEvent(eventName, audioRole, mediaName, (void*)eventContext);
if (!policyAllowed)
{
afb_req_fail(req, "Audio policy violation", "Post sound event not allowed in current context");
return;
}
- // TODO: Post sound event to rendering services (e.g. gstreamer file player wrapper or simple ALSA player)
+ afb_event_push(g_AHLCtx.policyCtx.postEvent,queryJ);
afb_req_success(req, NULL, "Posted sound event");
}
@@ -621,24 +919,78 @@ PUBLIC void audiohlapi_post_sound_event(struct afb_req req)
// Monitoring
PUBLIC void audiohlapi_subscribe(struct afb_req req)
{
- // json_object *queryJ = NULL;
-
- // queryJ = afb_req_json(req);
+ json_object *queryJ = NULL;
+ json_object * eventArrayJ = NULL;
- // TODO: Iterate through array length, parsing the string value to actual events
- // TODO: Subscribe to appropriate events from other services
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:o}", "events", &eventArrayJ);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+
+ int iNumEvents = json_object_array_length(eventArrayJ);
+ for (int i = 0; i < iNumEvents; i++)
+ {
+ char * pEventName = NULL;
+ json_object * jEvent = json_object_array_get_idx(eventArrayJ,i);
+ pEventName = (char *)json_object_get_string(jEvent);
+ if(!strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)) {
+ afb_req_subscribe(req, g_AHLCtx.policyCtx.propertyEvent);
+ AFB_DEBUG("Client subscribed to endpoint property events");
+ }
+ else if(!strcasecmp(pEventName, AHL_ENDPOINT_VOLUME_EVENT)) {
+ afb_req_subscribe(req, g_AHLCtx.policyCtx.volumeEvent);
+ AFB_DEBUG("Client subscribed to endpoint volume events");
+ }
+ else if(!strcasecmp(pEventName, AHL_POST_EVENT)) {
+ afb_req_subscribe(req, g_AHLCtx.policyCtx.postEvent);
+ AFB_DEBUG("Client subscribed to post event calls events");
+ }
+ else {
+ afb_req_fail(req, "failed", "Invalid event");
+ return;
+ }
+ }
afb_req_success(req, NULL, "Subscribe to events finished");
}
PUBLIC void audiohlapi_unsubscribe(struct afb_req req)
{
- // json_object *queryJ = NULL;
-
- // queryJ = afb_req_json(req);
-
- // TODO: Iterate through array length, parsing the string value to actual events
- // TODO: Unsubscribe to appropriate events from other services
+ json_object *queryJ = NULL;
+ json_object * eventArrayJ = NULL;
- afb_req_success(req, NULL, "Subscribe to events finished");
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:o}", "events", &eventArrayJ);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+
+ int iNumEvents = json_object_array_length(eventArrayJ);
+ for (int i = 0; i < iNumEvents; i++)
+ {
+ char * pEventName = NULL;
+ json_object * jEvent = json_object_array_get_idx(eventArrayJ,i);
+ pEventName = (char *)json_object_get_string(jEvent);
+ if(!strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)) {
+ afb_req_unsubscribe(req, g_AHLCtx.policyCtx.propertyEvent);
+ AFB_DEBUG("Client unsubscribed to endpoint property events");
+ }
+ else if(!strcasecmp(pEventName, AHL_ENDPOINT_VOLUME_EVENT)) {
+ afb_req_unsubscribe(req, g_AHLCtx.policyCtx.volumeEvent);
+ AFB_DEBUG("Client unsubscribed to endpoint volume events");
+ }
+ else if(!strcasecmp(pEventName, AHL_POST_EVENT)) {
+ afb_req_unsubscribe(req, g_AHLCtx.policyCtx.postEvent);
+ AFB_DEBUG("Client unsubscribed to post event calls events");
+ }
+ else {
+ afb_req_fail(req, "failed", "Invalid event");
+ return;
+ }
+ }
+
+ afb_req_success(req, NULL, "Unsubscribe to events finished");
}