From 8a584f01b46d251fdc5de8b071eff755d99f0090 Mon Sep 17 00:00:00 2001 From: Tai Vuong Date: Fri, 27 Oct 2017 21:40:20 -0400 Subject: Add JSON object parameters for policy functions call, fix various bug and code stabilisation --- src/ahl-binding.c | 1245 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 886 insertions(+), 359 deletions(-) (limited to 'src/ahl-binding.c') diff --git a/src/ahl-binding.c b/src/ahl-binding.c index fe3dcee..cd89b64 100644 --- a/src/ahl-binding.c +++ b/src/ahl-binding.c @@ -1,6 +1,5 @@ /* * Copyright (C) 2017 "Audiokinetic Inc" - * Author Francois Thibault * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,10 +21,106 @@ #include "ahl-binding.h" #include "ahl-apidef.h" // Generated from JSON OpenAPI #include "wrap-json.h" +#include "ahl-policy.h" +#include "ahl-policy-utils.h" // Global high-level binding context AHLCtxT g_AHLCtx; +// TODO: Helpers that could be common +static EndpointTypeT EndpointTypeToEnum(char * in_pEndpointTypeStr) +{ + if (in_pEndpointTypeStr == NULL) { + return ENDPOINTTYPE_MAXVALUE; + } + else if (strcasecmp(in_pEndpointTypeStr,AHL_ENDPOINTTYPE_SOURCE)==0) { + return ENDPOINTTYPE_SOURCE; + } + else if (strcasecmp(in_pEndpointTypeStr,AHL_ENDPOINTTYPE_SINK)==0) { + return ENDPOINTTYPE_SINK; + } + else + return ENDPOINTTYPE_MAXVALUE; +} + +static StreamStateT StreamStateToEnum(char * in_pStreamStateStr) +{ + if (in_pStreamStateStr == NULL) { + return STREAM_STATE_MAXVALUE; + } + else if (strcasecmp(in_pStreamStateStr,AHL_STREAM_STATE_IDLE)==0) { + return STREAM_STATE_IDLE; + } + else if (strcasecmp(in_pStreamStateStr,AHL_STREAM_STATE_RUNNING)==0) { + return STREAM_STATE_RUNNING; + } + else if (strcasecmp(in_pStreamStateStr,AHL_STREAM_STATE_PAUSED)==0) { + return STREAM_STATE_PAUSED; + } + else + return STREAM_STATE_MAXVALUE; +} + +static StreamMuteT StreamMuteToEnum(char * in_pStreamMuteStr) +{ + if (in_pStreamMuteStr == NULL) { + return STREAM_MUTE_MAXVALUE; + } + else if (strcasecmp(in_pStreamMuteStr,AHL_STREAM_UNMUTED)==0) { + return STREAM_UNMUTED; + } + else if (strcasecmp(in_pStreamMuteStr,AHL_STREAM_MUTED)==0) { + return STREAM_MUTED; + } + else + return STREAM_MUTE_MAXVALUE; +} + +static char * DeviceURITypeEnumToStr(DeviceURITypeT in_eDeviceURIType) { + switch(in_eDeviceURIType) { + case DEVICEURITYPE_ALSA_HW: // Alsa hardware device URI + return AHL_DEVICEURITYPE_ALSA_HW; + case DEVICEURITYPE_ALSA_DMIX: // Alsa Dmix device URI (only for playback devices) + return AHL_DEVICEURITYPE_ALSA_DMIX; + case DEVICEURITYPE_ALSA_DSNOOP: // Alsa DSnoop device URI (only for capture devices) + return AHL_DEVICEURITYPE_ALSA_DSNOOP; + case DEVICEURITYPE_ALSA_SOFTVOL: // Alsa softvol device URI + return AHL_DEVICEURITYPE_ALSA_SOFTVOL; + case DEVICEURITYPE_ALSA_PLUG: // Alsa plug device URI + return AHL_DEVICEURITYPE_ALSA_PLUG; + case DEVICEURITYPE_ALSA_OTHER: // Alsa domain URI device of unspecified type + return AHL_DEVICEURITYPE_ALSA_OTHER; + case DEVICEURITYPE_NOT_ALSA: // Unknown (not ALSA domain) + return AHL_DEVICEURITYPE_NOT_ALSA; + default: + return "Unknown"; + } +} + +static char * StreamStateEnumToStr(StreamStateT in_eStreamState) { + switch(in_eStreamState) { + case STREAM_STATE_IDLE: + return AHL_STREAM_STATE_IDLE; + case STREAM_STATE_RUNNING: + return AHL_STREAM_STATE_RUNNING; + case STREAM_STATE_PAUSED: + return AHL_STREAM_STATE_PAUSED; + default: + return "Unknown"; + } +} + +static char * StreamMuteEnumToStr(StreamMuteT in_eStreamMute) { + switch(in_eStreamMute) { + case STREAM_UNMUTED: + return AHL_STREAM_UNMUTED; + case STREAM_MUTED: + return AHL_STREAM_MUTED; + default: + return "Unknown"; + } +} + static void AudioFormatStructToJSON(json_object **audioFormatJ, AudioFormatT * pAudioFormat) { wrap_json_pack(audioFormatJ, "{s:i,s:i,s:i}", @@ -34,29 +129,52 @@ static void AudioFormatStructToJSON(json_object **audioFormatJ, AudioFormatT * p "sample_type", pAudioFormat->sampleType); } -// Helper macros/func for packaging JSON objects from C structures +// Package only information that can useful to application clients when selecting endpoint static void EndpointInfoStructToJSON(json_object **endpointInfoJ, EndpointInfoT * pEndpointInfo) { json_object *formatInfoJ = NULL; - wrap_json_pack(endpointInfoJ, "{s:i,s:i,s:s,s:s,s:i}", + wrap_json_pack(endpointInfoJ, "{s:i,s:s,s:s,s:s,s:s,s:s,s:s}", "endpoint_id", pEndpointInfo->endpointID, - "endpoint_type", pEndpointInfo->type, - "device_name", pEndpointInfo->gsDeviceName->str, - "display_name", pEndpointInfo->gsDisplayName->str, - "device_uri_type", pEndpointInfo->deviceURIType); + "endpoint_type", (pEndpointInfo->type == ENDPOINTTYPE_SOURCE) ? AHL_ENDPOINTTYPE_SOURCE : AHL_ENDPOINTTYPE_SINK, + "device_name", pEndpointInfo->gsDeviceName, + "display_name", pEndpointInfo->gsDisplayName, + "audio_role", pEndpointInfo->pRoleName, + "device_domain",pEndpointInfo->gsDeviceDomain, + "device_uri_type", DeviceURITypeEnumToStr(pEndpointInfo->deviceURIType)); AudioFormatStructToJSON(&formatInfoJ,&pEndpointInfo->format); json_object_object_add(*endpointInfoJ,"format",formatInfoJ); + + // Properties + if (pEndpointInfo->pPropTable) { + json_object *pPropTableJ = json_object_new_array(); + + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init (&iter, pEndpointInfo->pPropTable); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + if((key!=NULL) && (value!=NULL)) + { + json_object *pPropertyJ = NULL; + json_object_get((json_object*)value); // Don't let the framework free our object when the request is done + wrap_json_pack(&pPropertyJ, "{s:s,s:o}","property_name", (char*)key, "property_value", (json_object*)value); + json_object_array_add(pPropTableJ, pPropertyJ); + } + } + json_object_object_add(*endpointInfoJ,"properties",pPropTableJ); + } } +// Package only information that can useful to application clients when opening a stream static void StreamInfoStructToJSON(json_object **streamInfoJ, StreamInfoT * pStreamInfo) { json_object *endpointInfoJ = NULL; EndpointInfoStructToJSON(&endpointInfoJ,pStreamInfo->pEndpointInfo); - wrap_json_pack(streamInfoJ, "{s:i,s:i,s:i,s:s}", + wrap_json_pack(streamInfoJ, "{s:i,s:s,s:s,s:s}", "stream_id", pStreamInfo->streamID, - "state", pStreamInfo->streamState, - "mute", pStreamInfo->streamMute, - "device_uri",pStreamInfo->pEndpointInfo->gsDeviceURI->str); + "state", StreamStateEnumToStr(pStreamInfo->streamState), + "mute", StreamMuteEnumToStr(pStreamInfo->streamMute), + "device_uri",pStreamInfo->pEndpointInfo->gsDeviceURI); // Need to open a stream to have access to the device URI json_object_object_add(*streamInfoJ,"endpoint_info",endpointInfoJ); } @@ -67,62 +185,91 @@ static streamID_t CreateNewStreamID() return newID; } -static int FindRoleIndex( const char * in_pAudioRole) +static EndpointInfoT * GetEndpointInfoWithRole(endpointID_t in_endpointID, EndpointTypeT in_endpointType, RoleInfoT * in_pRole) { - 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; + EndpointInfoT * pEndpointInfo = NULL; + GPtrArray * pDeviceArray = NULL; + if (in_endpointType == ENDPOINTTYPE_SOURCE){ + pDeviceArray = in_pRole->pSourceEndpoints; + } + else { + pDeviceArray = in_pRole->pSinkEndpoints; + } + g_assert_nonnull(pDeviceArray); + + for (int j = 0; j < pDeviceArray->len; j++) { + EndpointInfoT * pCurEndpointInfo = g_ptr_array_index(pDeviceArray,j); + g_assert_nonnull(pCurEndpointInfo); + if (pCurEndpointInfo->endpointID == in_endpointID) { + pEndpointInfo = pCurEndpointInfo; break; } } - return index; + + return pEndpointInfo; +} + +static int ReplaceEndpointInfoWithRole(endpointID_t in_endpointID, EndpointTypeT in_endpointType, RoleInfoT * in_pRole, EndpointInfoT * in_pNewEndpoint) +{ + GPtrArray * pDeviceArray = NULL; + if (in_endpointType == ENDPOINTTYPE_SOURCE){ + pDeviceArray = in_pRole->pSourceEndpoints; + } + else { + pDeviceArray = in_pRole->pSinkEndpoints; + } + g_assert_nonnull(pDeviceArray); + + for (int j = 0; j < pDeviceArray->len; j++) { + EndpointInfoT * pCurEndpointInfo = g_ptr_array_index(pDeviceArray,j); + g_assert_nonnull(pCurEndpointInfo); + if (pCurEndpointInfo->endpointID == in_endpointID) { + g_ptr_array_insert(pDeviceArray,j,in_pNewEndpoint); + g_ptr_array_remove_index(pDeviceArray,j+1); + TermEndpointInfo(pCurEndpointInfo); + // GLib automatically frees item upon array removal + return AHL_SUCCESS; + } + } + + return AHL_FAIL; } static EndpointInfoT * GetEndpointInfo(endpointID_t in_endpointID, EndpointTypeT in_endpointType) { EndpointInfoT * pEndpointInfo = NULL; - for (int i = 0; i < g_AHLCtx.policyCtx.iNumberRoles; i++) + + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init (&iter, g_AHLCtx.policyCtx.pRoleInfo); + while (pEndpointInfo == NULL && g_hash_table_iter_next (&iter, &key, &value)) { - GArray * pRoleDeviceArray = NULL; - if (in_endpointType == ENDPOINTTYPE_SOURCE){ - pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSourceEndpoints, i ); - } - else{ - pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, i ); - } - for (int j = 0; j < pRoleDeviceArray->len; j++) { - EndpointInfoT * pCurEndpointInfo = &g_array_index(pRoleDeviceArray,EndpointInfoT,j); - if (pCurEndpointInfo->endpointID == in_endpointID) { - pEndpointInfo = pCurEndpointInfo; - break; - } - } + RoleInfoT * pRoleInfo = (RoleInfoT*)value; + pEndpointInfo = GetEndpointInfoWithRole(in_endpointID,in_endpointType,pRoleInfo); } + return pEndpointInfo; } -static StreamInfoT * GetActiveStream(streamID_t in_streamID) +static StreamInfoT * GetStream(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; + if (g_AHLCtx.policyCtx.pStreams == NULL) + return NULL; + + return g_hash_table_lookup(g_AHLCtx.policyCtx.pStreams,GINT_TO_POINTER(&in_streamID)); +} + +static RoleInfoT * GetRole(char * in_pAudioRoleName) +{ + if (g_AHLCtx.policyCtx.pRoleInfo == NULL) + return NULL; + + return g_hash_table_lookup(g_AHLCtx.policyCtx.pRoleInfo,in_pAudioRoleName); } static AHLClientCtxT * AllocateClientContext() { AHLClientCtxT * pClientCtx = malloc(sizeof(AHLClientCtxT)); - pClientCtx->pEndpointAccessList = g_array_new(FALSE, TRUE, sizeof(endpointID_t)); pClientCtx->pStreamAccessList = g_array_new(FALSE, TRUE, sizeof(streamID_t)); return pClientCtx; } @@ -130,31 +277,50 @@ static AHLClientCtxT * AllocateClientContext() static void TerminateClientContext(void * ptr) { AHLClientCtxT * pClientCtx = (AHLClientCtxT *) ptr; - g_array_free( pClientCtx->pEndpointAccessList, TRUE); - g_array_free( pClientCtx->pStreamAccessList, TRUE); - free(ptr); -} + if (pClientCtx != NULL) { -static int CheckStreamAccessControl(AHLClientCtxT * pClientCtx, streamID_t streamID) -{ - int iAccessControl = AHL_ACCESS_CONTROL_DENIED; - for (int i = 0; i < pClientCtx->pStreamAccessList->len ; i++) { - streamID_t iID = g_array_index(pClientCtx->pStreamAccessList,streamID_t,i); - if (iID == streamID) { - iAccessControl = AHL_ACCESS_CONTROL_GRANTED; - } + // Avoid having policy in bad state if client loses WS connection (e.g. app termination without close stream) + // Force close streams in those cases. + + if (pClientCtx->pStreamAccessList != NULL) { +#ifndef AHL_DISCONNECT_POLICY + for (int i = 0; i < pClientCtx->pStreamAccessList->len; i++) + { + streamID_t streamID = g_array_index(pClientCtx->pStreamAccessList,streamID_t,i); + // Call policy to verify whether creating a new audio stream is allowed in current context and possibly take other actions + StreamInfoT * pStreamInfo = GetStream(streamID); + if (pStreamInfo == NULL) { + AFB_ERROR("Specified stream not currently active stream_id -> %d",streamID); + return; + } + + json_object *pPolicyStreamJ = NULL; + int err = PolicyStreamStructToJSON(pStreamInfo, &pPolicyStreamJ); + if (err == AHL_FAIL) + { + AFB_ERROR("Audio policy violation, Unable to get JSON object for Policy_CloseStream"); + return; + } + Policy_CloseStream(pPolicyStreamJ); + } +#endif + g_array_free( pClientCtx->pStreamAccessList, TRUE); + pClientCtx->pStreamAccessList = NULL; + } + free(pClientCtx); } - return iAccessControl; } -static int CheckEndpointAccessControl(AHLClientCtxT * pClientCtx, endpointID_t endpointID) +static int CheckStreamAccessControl(AHLClientCtxT * pClientCtx, streamID_t streamID) { int iAccessControl = AHL_ACCESS_CONTROL_DENIED; - for (int i = 0; i < pClientCtx->pEndpointAccessList->len ; i++) { - endpointID_t iID = g_array_index(pClientCtx->pEndpointAccessList,endpointID_t,i); - if (iID == endpointID) { - iAccessControl = AHL_ACCESS_CONTROL_GRANTED; - } + if (pClientCtx && pClientCtx->pStreamAccessList) { + for (int i = 0; i < pClientCtx->pStreamAccessList->len ; i++) { + streamID_t iID = g_array_index(pClientCtx->pStreamAccessList,streamID_t,i); + if (iID == streamID) { + iAccessControl = AHL_ACCESS_CONTROL_GRANTED; + } + } } return iAccessControl; } @@ -167,21 +333,21 @@ static int CreateEvents() err = !afb_event_is_valid(g_AHLCtx.policyCtx.propertyEvent); if (err) { AFB_ERROR("Could not create endpoint property change event"); - err++; + return 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++; + return err; } - g_AHLCtx.policyCtx.postEvent = afb_daemon_make_event(AHL_POST_EVENT); - err = !afb_event_is_valid(g_AHLCtx.policyCtx.postEvent); + g_AHLCtx.policyCtx.postActionEvent = afb_daemon_make_event(AHL_POST_ACTION_EVENT); + err = !afb_event_is_valid(g_AHLCtx.policyCtx.postActionEvent); if (err) { - AFB_ERROR("Could not create post event call event"); - err++; + AFB_ERROR("Could not create post action event call event"); + return err; } return err; @@ -189,90 +355,205 @@ static int CreateEvents() static void AhlBindingTerm() { +#ifndef AHL_DISCONNECT_POLICY // 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++) +#endif + + // Roles + if (g_AHLCtx.policyCtx.pRoleInfo != NULL) { + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init(&iter, g_AHLCtx.policyCtx.pRoleInfo); + while (g_hash_table_iter_next (&iter, &key, &value)) { - GString * gsEventName = &g_array_index(pRoleEventArray,GString,j); - g_string_free(gsEventName,TRUE); + RoleInfoT * pRole = (RoleInfoT *)value; + if (pRole) + { + if(pRole->pRoleName) { + g_free(pRole->pRoleName); + pRole->pRoleName = NULL; + } + // Actions + if (pRole->pActionList) { + for (int i = 0; i < pRole->pActionList->len; i++) + { + g_ptr_array_remove_index( pRole->pActionList, i ); // Free char * is called by GLib + } + } + // Source endpoints + if (pRole->pSourceEndpoints) { + for (int i = 0; i < pRole->pSourceEndpoints->len; i++) + { + EndpointInfoT * pEndpoint = g_ptr_array_remove_index( pRole->pSourceEndpoints, i ); // Free endpoint * is called by GLib + if (pEndpoint) { + TermEndpointInfo(pEndpoint); + } + } + } + // Sink endpoints + if (pRole->pSinkEndpoints) { + for (int i = 0; i < pRole->pSinkEndpoints->len; i++) + { + EndpointInfoT * pEndpoint = g_ptr_array_remove_index( pRole->pSinkEndpoints, i ); // Free endpoint * is called by GLib + if (pEndpoint) { + TermEndpointInfo(pEndpoint); + } + } + } + free(pRole); + } } - 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_AHLCtx.pHALList = NULL; - g_hash_table_remove_all(g_AHLCtx.policyCtx.pRolePriority); - g_hash_table_destroy(g_AHLCtx.policyCtx.pRolePriority); - g_AHLCtx.policyCtx.pRolePriority = NULL; - // TODO: Need to free g_strings in audio roles list - g_array_free(g_AHLCtx.policyCtx.pAudioRoles,TRUE); - g_AHLCtx.policyCtx.pAudioRoles = NULL; - g_array_free(g_AHLCtx.policyCtx.pInterruptBehavior,TRUE); - g_AHLCtx.policyCtx.pInterruptBehavior = NULL; - g_array_free(g_AHLCtx.policyCtx.pActiveStreams,TRUE); - g_AHLCtx.policyCtx.pActiveStreams = NULL; - - AFB_INFO("Audio high-level Binding succesTermination"); + g_hash_table_remove_all(g_AHLCtx.policyCtx.pRoleInfo); + g_hash_table_destroy(g_AHLCtx.policyCtx.pRoleInfo); + g_AHLCtx.policyCtx.pRoleInfo = NULL; + } + + if (g_AHLCtx.policyCtx.pStreams) { + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init (&iter, g_AHLCtx.policyCtx.pStreams); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + if (value) + free(value); + } + g_hash_table_remove_all(g_AHLCtx.policyCtx.pStreams); + g_hash_table_destroy(g_AHLCtx.policyCtx.pStreams); + g_AHLCtx.policyCtx.pStreams = NULL; + } + + if (g_AHLCtx.policyCtx.pHALList) { + g_ptr_array_free(g_AHLCtx.policyCtx.pHALList,TRUE); + g_AHLCtx.policyCtx.pHALList = NULL; + } + + AFB_INFO("Audio high-level binding termination success"); } // Binding initialization PUBLIC int AhlBindingInit() { - int errcount = 0; + int err = 0; + memset(&g_AHLCtx,0,sizeof(g_AHLCtx)); + + // Register exit function atexit(AhlBindingTerm); - // This API uses services from low level audio. TODO: This dependency can be removed. - errcount = afb_daemon_require_api_v2("alsacore",1) ; - if( errcount != 0 ) + //Create AGL Events + err=CreateEvents(); + if(err) { - AFB_ERROR("Audio high level API requires alsacore API to be available"); - return 1; + //Error messages already reported inside CreateEvents + return err; } - errcount += CreateEvents(); - // Parse high-level binding JSON configuration file (will build device lists) - errcount += ParseHLBConfig(); - + err = ParseHLBConfig(); + if(err) + { + //Error messages already reported inside ParseHLBConfig + return err; + } + +#ifndef AHL_DISCONNECT_POLICY // Policy initialization - errcount += Policy_Init(); + err = Policy_Init(); + if(err == AHL_POLICY_REJECT) + { + //Error messages already reported inside PolicyInit + return err; + } + + // for all audio Roles + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, g_AHLCtx.policyCtx.pRoleInfo); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + RoleInfoT * pRoleInfo = (RoleInfoT*)value; + if (pRoleInfo->pSourceEndpoints){ + // for all source endpoints + for (int j = 0; j < pRoleInfo->pSourceEndpoints->len; j++) { + EndpointInfoT * pCurEndpointInfo = g_ptr_array_index(pRoleInfo->pSourceEndpoints,j); + g_assert_nonnull(pCurEndpointInfo); + json_object *pPolicyEndpointJ = NULL; + err = PolicyEndpointStructToJSON(pCurEndpointInfo, &pPolicyEndpointJ); + if (err == AHL_FAIL) + { + AFB_ERROR("Unable to Create Endpoint Json object error:%s ",wrap_json_get_error_string(err)); + return err; + } + else + { + err = Policy_Endpoint_Init(pPolicyEndpointJ); + if (err == AHL_POLICY_REJECT) { + AFB_ERROR("Policy endpoint properties initalization failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); + } + //free pPolicyEndpointJ + json_object_put(pPolicyEndpointJ); + } + } + } + if (pRoleInfo->pSinkEndpoints){ + // for all sink endpoints + for (int j = 0; j < pRoleInfo->pSinkEndpoints->len; j++) { + EndpointInfoT * pCurEndpointInfo = g_ptr_array_index(pRoleInfo->pSinkEndpoints,j); + g_assert_nonnull(pCurEndpointInfo); + json_object *pPolicyEndpointJ = NULL; + err = PolicyEndpointStructToJSON(pCurEndpointInfo, &pPolicyEndpointJ); + if (err == AHL_FAIL) + { + AFB_ERROR("Unable to Create Endpoint Json object error:%s ",wrap_json_get_error_string(err)); + return err; + } + else + { + err = Policy_Endpoint_Init(pPolicyEndpointJ); + if (err== AHL_POLICY_REJECT) { + AFB_ERROR("Policy endpoint properties initalization failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); + } + //free pPolicyEndpointJ + json_object_put(pPolicyEndpointJ); + } + + } + } + } +#endif // Initialize list of active streams - g_AHLCtx.iNumActiveClients = 0; - g_AHLCtx.policyCtx.pActiveStreams = g_array_new(FALSE,TRUE,sizeof(StreamInfoT)); + g_AHLCtx.policyCtx.pStreams = g_hash_table_new(g_int_hash, g_int_equal); + if(g_AHLCtx.policyCtx.pStreams == NULL) + { + AFB_ERROR("Unable to create Active Stream List"); + return err; + } // TODO: Use AGL persistence framework to retrieve and set initial volumes/properties - AFB_DEBUG("Audio high-level Binding success errcount=%d", errcount); - return errcount; + AFB_DEBUG("Audio high-level Binding success"); + return err; } PUBLIC void AhlOnEvent(const char *evtname, json_object *eventJ) { AFB_DEBUG("AHL received event %s", evtname); + + // TODO: Handle event from the policy to update internal information (currently not possible since within the same binding) - //forward to policy to handle events +#ifndef AHL_DISCONNECT_POLICY + // Temp: currently forward to policy to handle events (they will be received directly when disconnected into separate binding) Policy_OnEvent(evtname, eventJ); +#endif } PUBLIC void audiohlapi_get_sources(struct afb_req req) { - json_object *sourcesJ = NULL; - json_object *sourceJ = NULL; + json_object *devicesJ = NULL; + json_object *deviceJ = NULL; json_object *queryJ = NULL; char * audioRole = NULL; @@ -284,36 +565,40 @@ PUBLIC void audiohlapi_get_sources(struct afb_req req) } AFB_DEBUG("Filtering devices according to specified audio role=%s", audioRole); - // List device only for current audio role and device type (pick the role device list) - int iRoleIndex = FindRoleIndex(audioRole); - if ( iRoleIndex < 0) + + RoleInfoT * pRole = GetRole(audioRole); + if ( pRole == NULL ) { afb_req_fail_f(req, "Invalid arguments", "Requested audio role does not exist in current configuration -> %s", json_object_get_string(queryJ)); return; } 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); - json_object_array_add(sourcesJ, sourceJ); + devicesJ = json_object_new_array(); + GPtrArray * pDeviceArray = pRole->pSourceEndpoints; + if (pDeviceArray) { + int iNumberDevices = pDeviceArray->len; + for ( int j = 0 ; j < iNumberDevices; j++) + { + EndpointInfoT * pEndpointInfo = g_ptr_array_index(pDeviceArray,j); + if (pEndpointInfo) { + EndpointInfoStructToJSON(&deviceJ, pEndpointInfo); + json_object_array_add(devicesJ, deviceJ); + } + } } } - afb_req_success(req, sourcesJ, "List of sources"); + afb_req_success(req, devicesJ, "List of sources"); } PUBLIC void audiohlapi_get_sinks(struct afb_req req) { - json_object *sinksJ = NULL; - json_object *sinkJ = NULL; + json_object *devicesJ = NULL; + json_object *deviceJ = NULL; json_object *queryJ = NULL; char * audioRole = NULL; - + queryJ = afb_req_json(req); int err = wrap_json_unpack(queryJ, "{s:s}", "audio_role", &audioRole); if (err) { @@ -322,136 +607,162 @@ PUBLIC void audiohlapi_get_sinks(struct afb_req req) } AFB_DEBUG("Filtering devices according to specified audio role=%s", audioRole); - // List device only for current audio role and device type (pick the role device list) - int iRoleIndex = FindRoleIndex(audioRole); - if ( iRoleIndex < 0) + + RoleInfoT * pRole = GetRole(audioRole); + if ( pRole == NULL ) { afb_req_fail_f(req, "Invalid arguments", "Requested audio role does not exist in current configuration -> %s", json_object_get_string(queryJ)); return; } 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); - json_object_array_add(sinksJ, sinkJ); + devicesJ = json_object_new_array(); + GPtrArray * pDeviceArray = pRole->pSinkEndpoints; + if (pDeviceArray) { + int iNumberDevices = pDeviceArray->len; + for ( int j = 0 ; j < iNumberDevices; j++) + { + EndpointInfoT * pEndpointInfo = g_ptr_array_index(pDeviceArray,j); + EndpointInfoStructToJSON(&deviceJ, pEndpointInfo); + json_object_array_add(devicesJ, deviceJ); + } } } - afb_req_success(req, sinksJ, "List of sinks"); + afb_req_success(req, devicesJ, "List of sinks"); } PUBLIC void audiohlapi_stream_open(struct afb_req req) { json_object *streamInfoJ = NULL; - StreamInfoT streamInfo; + StreamInfoT * pStreamInfo = NULL; json_object *queryJ = NULL; char * audioRole = NULL; + char * endpointTypeStr = NULL; EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; endpointID_t endpointID = AHL_UNDEFINED; - int policyAllowed = AHL_POLICY_REJECT; EndpointInfoT * pEndpointInfo = NULL; + EndpointSelectionModeT endpointSelMode = ENDPOINTSELMODEMAXVALUE; 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); + int err = wrap_json_unpack(queryJ, "{s:s,s:s,s?i}", "audio_role", &audioRole, "endpoint_type", &endpointTypeStr, "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 = audio_role:%s endpoint_type:%d endpoint_id:%d", audioRole,endpointType,endpointID); + AFB_DEBUG("Parsed input arguments = audio_role:%s endpoint_type:%s endpoint_id:%d", audioRole,endpointTypeStr,endpointID); + endpointType = EndpointTypeToEnum(endpointTypeStr); // Check if there is already an existing context for this client AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure if (pClientCtx == NULL) { - g_AHLCtx.iNumActiveClients++; pClientCtx = AllocateClientContext(); afb_req_context_set(req, pClientCtx, TerminateClientContext); } - int iRoleIndex = FindRoleIndex(audioRole); - if (iRoleIndex < 0) { + RoleInfoT * pRole = GetRole(audioRole); + if ( pRole == NULL ) + { afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole); return; } - GArray * pRoleDeviceArray = NULL; + GPtrArray * pDeviceArray = NULL; if (endpointType == ENDPOINTTYPE_SOURCE){ - pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSourceEndpoints, iRoleIndex ); + pDeviceArray = pRole->pSourceEndpoints; } else{ - pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, iRoleIndex ); + pDeviceArray = pRole->pSinkEndpoints; } - if (pRoleDeviceArray->len == 0) { - afb_req_fail_f(req, "No available devices", "No available devices for role:%s and device type:%d",audioRole,endpointType); + if (pDeviceArray == NULL || pDeviceArray->len == 0) { + afb_req_fail_f(req, "No available devices", "No available devices for role:%s and device type:%s",audioRole,endpointTypeStr); return; } if (endpointID == AHL_UNDEFINED) { // Assign a device based on configuration priority (first in the list for requested role and endpoint type) - pEndpointInfo = &g_array_index(pRoleDeviceArray,EndpointInfoT,0); - streamInfo.endpointSelMode = AHL_ENDPOINTSELMODE_AUTO; + pEndpointInfo = g_ptr_array_index(pDeviceArray,0); + endpointSelMode = ENDPOINTSELMODE_AUTO; + } else{ - streamInfo.endpointSelMode = AHL_ENDPOINTSELMODE_MANUAL; + endpointSelMode = ENDPOINTSELMODE_MANUAL; // Find specified endpoint ID in list of devices - int iNumberDevices = pRoleDeviceArray->len; + int iNumberDevices = pDeviceArray->len; for ( int j = 0 ; j < iNumberDevices; j++) { - pEndpointInfo = &g_array_index(pRoleDeviceArray,EndpointInfoT,j); - if (pEndpointInfo->endpointID == endpointID) { + pEndpointInfo = g_ptr_array_index(pDeviceArray,j); + if (pEndpointInfo && pEndpointInfo->endpointID == endpointID) { break; } + pEndpointInfo = NULL; } - 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; - } + } - // Create stream - streamInfo.streamID = CreateNewStreamID(); // create new ID - streamInfo.streamState = STREAM_STATE_IDLE; - streamInfo.streamMute = STREAM_UNMUTED; - streamInfo.pEndpointInfo = pEndpointInfo; + 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; + } + pStreamInfo = (StreamInfoT*) malloc(sizeof(StreamInfoT)); + memset(pStreamInfo,0,sizeof(StreamInfoT)); + + // Create stream + pStreamInfo->streamID = CreateNewStreamID(); // create new ID + pStreamInfo->streamState = STREAM_STATE_IDLE; + pStreamInfo->streamMute = STREAM_UNMUTED; + pStreamInfo->pEndpointInfo = pEndpointInfo; + pStreamInfo->endpointSelMode = endpointSelMode; + // Directly from role config for now, but could be programmatically overriden in the future + pStreamInfo->pRoleName = pRole->pRoleName; + pStreamInfo->iPriority = pRole->iPriority; + pStreamInfo->eInterruptBehavior = pRole->eInterruptBehavior; + +#ifndef AHL_DISCONNECT_POLICY // Call policy to verify whether creating a new audio stream is allowed in current context and possibly take other actions - policyAllowed = Policy_OpenStream(&streamInfo); + json_object *pPolicyStreamJ = NULL; + err = PolicyStreamStructToJSON(pStreamInfo, &pPolicyStreamJ); + if (err == AHL_FAIL) + { + afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_OpenStream"); + return; + } + + int policyAllowed = Policy_OpenStream(pPolicyStreamJ); if (policyAllowed == AHL_POLICY_REJECT) { afb_req_fail(req, "Audio policy violation", "Open stream not allowed in current context"); return; - } + } +#endif char streamEventName[128]; - snprintf(streamEventName,128,"ahl_streamstate_%d",streamInfo.streamID); + snprintf(streamEventName,128,"ahl_streamstate_%d",pStreamInfo->streamID); - streamInfo.streamStateEvent = afb_daemon_make_event(streamEventName); - err = !afb_event_is_valid(streamInfo.streamStateEvent); + pStreamInfo->streamStateEvent = afb_daemon_make_event(streamEventName); + err = !afb_event_is_valid(pStreamInfo->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); + err = afb_req_subscribe(req,pStreamInfo->streamStateEvent); if (err) { afb_req_fail(req, "Stream event subscription failure", "Could not subscribe to stream specific state change event"); return; } - // Add to client context stream ID and endpoint ID access rights - g_array_append_val(pClientCtx->pStreamAccessList, streamInfo.streamID); - g_array_append_val(pClientCtx->pEndpointAccessList, streamInfo.pEndpointInfo->endpointID); + // Add to client context stream ID access rights + g_array_append_val(pClientCtx->pStreamAccessList, pStreamInfo->streamID); // Push stream on active stream list - g_array_append_val( g_AHLCtx.policyCtx.pActiveStreams, streamInfo ); + if (g_AHLCtx.policyCtx.pStreams) + g_hash_table_insert( g_AHLCtx.policyCtx.pStreams, GINT_TO_POINTER(&pStreamInfo->streamID), pStreamInfo ); - StreamInfoStructToJSON(&streamInfoJ,&streamInfo); + StreamInfoStructToJSON(&streamInfoJ,pStreamInfo); afb_req_success(req, streamInfoJ, "Stream info structure"); } @@ -460,7 +771,6 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req) { json_object *queryJ = NULL; 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); @@ -470,11 +780,18 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req) } AFB_DEBUG("Parsed input arguments = stream_id:%d", streamID); + + StreamInfoT * pStreamInfo = GetStream(streamID); + if (pStreamInfo == NULL) { + afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID); + return; + } + // Check if there is already an existing context for this client AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure if (pClientCtx == NULL) { - afb_req_fail(req, "No context associated with the request", "No context associated with the request"); + afb_req_fail(req, "Bad state", "No client context associated with the request (is there an opened stream by this client?)"); return; } @@ -486,67 +803,55 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req) return; } - // Call policy to verify whether creating a new audio stream is allowed in current context and possibly take other actions - StreamInfoT * pStreamInfo = GetActiveStream(streamID); - if (pStreamInfo == NULL) { - afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID); +#ifndef AHL_DISCONNECT_POLICY + json_object *pPolicyStreamJ = NULL; + err = PolicyStreamStructToJSON(pStreamInfo, &pPolicyStreamJ); + if (err == AHL_FAIL) + { + afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_CloseStream"); return; - } - - policyAllowed = Policy_CloseStream(pStreamInfo); + } + // Call policy to verify whether creating a new audio stream is allowed in current context and possibly take other actions + int policyAllowed = Policy_CloseStream(pPolicyStreamJ); if (policyAllowed == AHL_POLICY_REJECT) { afb_req_fail(req, "Audio policy violation", "Close stream not allowed in current context"); return; } +#endif - // 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 unsubscribe 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; + // Unsubscribe client from stream events + char streamEventName[128]; + snprintf(streamEventName,128,"ahl_streamstate_%d",streamID); + int iValid = afb_event_is_valid(pStreamInfo->streamStateEvent); + if (iValid) { + err = afb_req_unsubscribe(req,pStreamInfo->streamStateEvent); + if (err) { + afb_req_fail(req, "Stream event subscription failure", "Could not unsubscribe to stream specific state change event"); + return; } } - if (iStreamFound == 0) { - afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID); - return; - } + // Remove from stream list (if present) + if (g_AHLCtx.policyCtx.pStreams) + g_hash_table_remove(g_AHLCtx.policyCtx.pStreams,GINT_TO_POINTER(&pStreamInfo->streamID)); + free(pStreamInfo); + pStreamInfo = NULL; // Find index for cases where there are multiple streams per client // Remove from client context stream ID and endpoint ID access rights - for (int i = 0; i < pClientCtx->pStreamAccessList->len ; i++) { - streamID_t iID = g_array_index(pClientCtx->pStreamAccessList,streamID_t,i); - if (iID == streamID) { - g_array_remove_index(pClientCtx->pStreamAccessList, i); - g_array_remove_index(pClientCtx->pEndpointAccessList, i); - } - } + if (pClientCtx->pStreamAccessList) { + for (int i = 0; i < pClientCtx->pStreamAccessList->len ; i++) { + streamID_t iID = g_array_index(pClientCtx->pStreamAccessList,streamID_t,i); + if (iID == streamID) { + g_array_remove_index(pClientCtx->pStreamAccessList, i); + } + } - if (pClientCtx->pStreamAccessList->len == 0 && pClientCtx->pEndpointAccessList == 0) { - // If no more streams/endpoints owner, clear session - afb_req_context_clear(req); - g_AHLCtx.iNumActiveClients--; + if (pClientCtx->pStreamAccessList->len == 0) { + // If no more streams/endpoints owner, clear session + afb_req_context_clear(req); + } } afb_req_success(req, NULL, "Stream close completed"); @@ -556,20 +861,19 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req) { json_object *queryJ = NULL; streamID_t streamID = AHL_UNDEFINED; - StreamStateT streamState = STREAM_STATUS_MAXVALUE; - int policyAllowed = AHL_POLICY_REJECT; + char * streamStateStr = NULL; queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:i,s:i}", "stream_id", &streamID,"state",&streamState); + int err = wrap_json_unpack(queryJ, "{s:i,s:s}", "stream_id", &streamID,"state",&streamStateStr); 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); + AFB_DEBUG("Parsed input arguments = stream_id:%d, state:%s", streamID,streamStateStr); + + StreamInfoT * pStreamInfo = GetStream(streamID); if (pStreamInfo == NULL) { - afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID); + afb_req_fail_f(req, "Stream not found", "Specified stream not found stream_id -> %d",streamID); return; } @@ -577,7 +881,7 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req) AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure if (pClientCtx == NULL) { - afb_req_fail(req, "No context associated with the request", "No context associated with the request"); + afb_req_fail(req, "Bad state", "No client context associated with the request (is there an opened stream by this client?)"); return; } @@ -588,14 +892,30 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req) afb_req_fail(req, "Access control denied", "Set stream state not allowed in current client context"); return; } - int AudioRoleIndex = FindRoleIndex(pStreamInfo->pEndpointInfo->gsAudioRole->str); - policyAllowed = Policy_SetStreamState(pStreamInfo, AudioRoleIndex, streamState); + StreamStateT streamState = StreamStateToEnum(streamStateStr); +#ifndef AHL_DISCONNECT_POLICY + json_object *pPolicyStreamJ = NULL; + err = PolicyStreamStructToJSON(pStreamInfo, &pPolicyStreamJ); + if (err == AHL_FAIL) + { + afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_SetStreamState"); + return; + } + + json_object *paramJ= json_object_new_int(streamState); + json_object_object_add(pPolicyStreamJ, "arg_stream_state", paramJ); + + int policyAllowed = Policy_SetStreamState(pPolicyStreamJ); if (policyAllowed == AHL_POLICY_REJECT) { afb_req_fail(req, "Audio policy violation", "Change stream state not allowed in current context"); return; } +#else + // Simulate that policy returns target state (accepted) + pStreamInfo->streamState = streamState; +#endif afb_req_success(req, NULL, "Set stream state"); } @@ -604,18 +924,17 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req) { json_object *queryJ = NULL; streamID_t streamID = AHL_UNDEFINED; - StreamMuteT muteState = STREAM_MUTE_MAXVALUE; - int policyAllowed = AHL_POLICY_REJECT; + char * pMuteStr = NULL; queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:i,s:i}", "stream_id", &streamID,"mute",&muteState); + int err = wrap_json_unpack(queryJ, "{s:i,s:s}", "stream_id", &streamID,"mute",&pMuteStr); 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); + AFB_DEBUG("Parsed input arguments = stream_id:%d, mute:%s", streamID,pMuteStr); - StreamInfoT * pStreamInfo = GetActiveStream(streamID); + StreamInfoT * pStreamInfo = GetStream(streamID); if (pStreamInfo == NULL) { afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID); return; @@ -625,7 +944,7 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req) AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure if (pClientCtx == NULL) { - afb_req_fail(req, "No context associated with the request", "No context associated with the request"); + afb_req_fail(req, "Bad state", "No client context associated with the request (is there an opened stream by this client?)"); return; } @@ -637,12 +956,30 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req) return; } - policyAllowed = Policy_SetStreamMute(pStreamInfo,muteState); + + StreamMuteT muteState = StreamMuteToEnum(pMuteStr); +#ifndef AHL_DISCONNECT_POLICY + json_object *pPolicyStreamJ = NULL; + err = PolicyStreamStructToJSON(pStreamInfo, &pPolicyStreamJ); + if (err == AHL_FAIL) + { + afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_SetStreamMute"); + return; + } + + json_object *paramJ= json_object_new_int(muteState); + json_object_object_add(pPolicyStreamJ, "mute_state", paramJ); + + int policyAllowed = Policy_SetStreamMute(pPolicyStreamJ); if (policyAllowed == AHL_POLICY_REJECT) { afb_req_fail(req, "Audio policy violation", "Mute stream not allowed in current context"); return; } +#else + // Simulate that policy returns target state (accepted) + pStreamInfo->streamMute = muteState; +#endif afb_req_success(req, NULL, "Set stream mute completed"); } @@ -661,7 +998,7 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req) } AFB_DEBUG("Parsed input arguments = stream_id:%d", streamID); - StreamInfoT * pStreamInfo = GetActiveStream(streamID); + StreamInfoT * pStreamInfo = GetStream(streamID); if (pStreamInfo == NULL) { afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID); return; @@ -676,17 +1013,18 @@ PUBLIC void audiohlapi_set_volume(struct afb_req req) { json_object *queryJ = NULL; endpointID_t endpointID = AHL_UNDEFINED; + char * pEndpointTypeStr = NULL; EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; char * volumeStr = 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,"volume",&volumeStr); + int err = wrap_json_unpack(queryJ, "{s:s,s:i,s:s}", "endpoint_type", &pEndpointTypeStr,"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", endpointType,endpointID,volumeStr); + AFB_DEBUG("Parsed input arguments = endpoint_type:%s endpoint_id:%d volume:%s", pEndpointTypeStr,endpointID,volumeStr); + endpointType = EndpointTypeToEnum(pEndpointTypeStr); EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); if (pEndpointInfo == NULL) @@ -695,28 +1033,28 @@ PUBLIC void audiohlapi_set_volume(struct afb_req req) return; } - // Check if there is already an existing context for this client - AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure - if (pClientCtx == NULL) +#ifndef AHL_DISCONNECT_POLICY + json_object *pPolicyEndpointJ = NULL; + err = PolicyEndpointStructToJSON(pEndpointInfo, &pPolicyEndpointJ); + if (err == AHL_FAIL) { - afb_req_fail(req, "No context associated with the request", "No context associated with the request"); + afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_SetVolume"); return; - } + } - // Verify that this client can control the stream - int iEndpointAccessControl = CheckEndpointAccessControl( pClientCtx, endpointID ); - if (iEndpointAccessControl == AHL_ACCESS_CONTROL_DENIED) - { - afb_req_fail(req, "Access control denied", "Set volume not allowed in current client context"); - return; - } + json_object *paramJ= json_object_new_string(volumeStr); + json_object_object_add(pPolicyEndpointJ, "arg_volume", paramJ); - policyAllowed = Policy_SetVolume(pEndpointInfo, volumeStr); + int policyAllowed = Policy_SetVolume(pPolicyEndpointJ); if (!policyAllowed) { afb_req_fail(req, "Audio policy violation", "Set volume not allowed in current context"); return; } +#else + // Simulate that policy returns target state (accepted) + pEndpointInfo->iVolume = atoi(volumeStr); +#endif afb_req_success(req, NULL, "Set volume completed"); } @@ -725,16 +1063,18 @@ PUBLIC void audiohlapi_get_volume(struct afb_req req) { json_object *queryJ = NULL; endpointID_t endpointID = AHL_UNDEFINED; + char * pEndpointTypeStr = NULL; EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; 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); + int err = wrap_json_unpack(queryJ, "{s:s,s:i}", "endpoint_type", &pEndpointTypeStr,"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); + AFB_DEBUG("Parsed input arguments = endpoint_type:%s endpoint_id:%d", pEndpointTypeStr,endpointID); + endpointType = EndpointTypeToEnum(pEndpointTypeStr); EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); if (pEndpointInfo == NULL) @@ -743,26 +1083,27 @@ PUBLIC void audiohlapi_get_volume(struct afb_req req) return; } - volumeJ = json_object_new_double((double)pEndpointInfo->iVolume); + volumeJ = json_object_new_int(pEndpointInfo->iVolume); afb_req_success(req, volumeJ, "Retrieved volume value"); } // Properties -PUBLIC void audiohlapi_get_list_properties(struct afb_req req) +PUBLIC void audiohlapi_get_endpoint_info(struct afb_req req) { json_object *queryJ = NULL; endpointID_t endpointID = AHL_UNDEFINED; + char * pEndpointTypeStr = NULL; 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); + int err = wrap_json_unpack(queryJ, "{s:s,s:i}", "endpoint_type", &pEndpointTypeStr,"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); + AFB_DEBUG("Parsed input arguments = endpoint_type:%s endpoint_id:%d", pEndpointTypeStr,endpointID); + endpointType = EndpointTypeToEnum(pEndpointTypeStr); EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); if (pEndpointInfo == NULL) @@ -771,38 +1112,29 @@ PUBLIC void audiohlapi_get_list_properties(struct afb_req req) 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); - } + json_object *endpointInfoJ = NULL; + EndpointInfoStructToJSON(&endpointInfoJ,pEndpointInfo); - afb_req_success(req, endpointPropsJ, "Retrieved property list for endpoint"); + afb_req_success(req, endpointInfoJ, "Retrieved endpoint information and properties"); } PUBLIC void audiohlapi_set_property(struct afb_req req) { json_object *queryJ = NULL; endpointID_t endpointID = AHL_UNDEFINED; + char * pEndpointTypeStr = NULL; EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; char * propertyName = NULL; json_object * propValueJ = NULL; - int policyAllowed = AHL_POLICY_REJECT; queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s:o}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"property_name",&propertyName,"value",&propValueJ); + int err = wrap_json_unpack(queryJ, "{s:s,s:i,s:s,s:o}", "endpoint_type", &pEndpointTypeStr,"endpoint_id",&endpointID,"property_name",&propertyName,"value",&propValueJ); 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); - + AFB_DEBUG("Parsed input arguments = endpoint_type:%s endpoint_id:%d property_name:%s", pEndpointTypeStr,endpointID,propertyName); + endpointType = EndpointTypeToEnum(pEndpointTypeStr); EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); if (pEndpointInfo == NULL) @@ -811,31 +1143,33 @@ PUBLIC void audiohlapi_set_property(struct afb_req req) return; } - // Check if there is already an existing context for this client - AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure - if (pClientCtx == NULL) +#ifndef AHL_DISCONNECT_POLICY + json_object *pPolicyEndpointJ = NULL; + err = PolicyEndpointStructToJSON(pEndpointInfo, &pPolicyEndpointJ); + if (err == AHL_FAIL) { - afb_req_fail(req, "No context associated with the request", "No context associated with the request"); + afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_SetVolume"); return; - } + } - // Verify that this client can control the stream - int iEndpointAccessControl = CheckEndpointAccessControl( pClientCtx, endpointID ); - if (iEndpointAccessControl == AHL_ACCESS_CONTROL_DENIED) - { - afb_req_fail(req, "Access control denied", "Set property not allowed in current client context"); - return; - } + json_object *paramJ= json_object_new_string(propertyName); + json_object_object_add(pPolicyEndpointJ, "arg_property_name", paramJ); + json_object_object_add(pPolicyEndpointJ, "arg_property_value", propValueJ); // Call policy to allow custom policy actions in current context - policyAllowed = Policy_SetProperty(pEndpointInfo, propertyName, propValueJ); + int policyAllowed = Policy_SetProperty(pPolicyEndpointJ); if (!policyAllowed) { afb_req_fail(req, "Audio policy violation", "Set endpoint property not allowed in current context"); return; } +#else + // Simulate that policy returns target state (accepted) + if (pEndpointInfo->pPropTable) + g_hash_table_insert(pEndpointInfo->pPropTable, propertyName, propValueJ); +#endif - afb_event_push(g_AHLCtx.policyCtx.propertyEvent,queryJ); + //afb_event_push(g_AHLCtx.policyCtx.propertyEvent,queryJ); afb_req_success(req, NULL, "Set property completed"); } @@ -844,16 +1178,18 @@ PUBLIC void audiohlapi_get_property(struct afb_req req) { json_object *queryJ = NULL; endpointID_t endpointID = AHL_UNDEFINED; + char * pEndpointTypeStr = NULL; EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; char * propertyName = NULL; 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:s,s:i,s:s}", "endpoint_type", &pEndpointTypeStr,"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); + AFB_DEBUG("Parsed input arguments = endpoint_type:%s endpoint_id:%d property_name:%s", pEndpointTypeStr,endpointID,propertyName); + endpointType = EndpointTypeToEnum(pEndpointTypeStr); EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); if (pEndpointInfo == NULL) @@ -870,17 +1206,19 @@ PUBLIC void audiohlapi_get_property(struct afb_req req) } json_object_get(propertyValJ); // Increase ref count so that framework does not free our JSON object + //AFB_WARNING("properties %s", json_object_get_string(propertyValJ)); + //json_object_get(propertyValJ); // Increase ref count so that framework does not free our JSON object afb_req_success(req, propertyValJ, "Retrieved property value"); } // Audio related events -PUBLIC void audiohlapi_get_list_events(struct afb_req req) +PUBLIC void audiohlapi_get_list_actions(struct afb_req req) { json_object *queryJ = NULL; char * audioRole = NULL; - json_object * roleEventsJ = NULL; + json_object * roleActionsJ = NULL; queryJ = afb_req_json(req); int err = wrap_json_unpack(queryJ, "{s:s}", "audio_role",&audioRole); @@ -890,83 +1228,85 @@ PUBLIC void audiohlapi_get_list_events(struct afb_req req) } AFB_DEBUG("Parsed input arguments = audio_role:%s",audioRole); - // Build and return list of events for specific audio role - int iRoleIndex = FindRoleIndex(audioRole); - if (iRoleIndex < 0) { + // Build and return list of actions for specific audio role + RoleInfoT * pRole = GetRole(audioRole); + if ( pRole == NULL ) + { afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole); return; } - 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++) - { - GString gsEventName = g_array_index(pRoleEventArray,GString,i); - json_object * eventJ = json_object_new_string(gsEventName.str); - json_object_array_add(roleEventsJ, eventJ); + roleActionsJ = json_object_new_array(); + if (pRole->pActionList) { + int iNumberActions = pRole->pActionList->len; + for ( int i = 0 ; i < iNumberActions; i++) + { + char * pActionName = g_ptr_array_index(pRole->pActionList,i); + json_object * actionJ = json_object_new_string(pActionName); + json_object_array_add(roleActionsJ, actionJ); + } } - afb_req_success(req, roleEventsJ, "Retrieved event list for audio role"); + afb_req_success(req, roleActionsJ, "Retrieved action list for audio role"); } -PUBLIC void audiohlapi_post_event(struct afb_req req) +PUBLIC void audiohlapi_post_action(struct afb_req req) { json_object *queryJ = NULL; - char * eventName = NULL; + char * actionName = NULL; char * audioRole = NULL; char * mediaName = NULL; - json_object *eventContext = NULL; - int policyAllowed = AHL_POLICY_REJECT; + json_object *actionContext = NULL; 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,"event_context",&eventContext); + int err = wrap_json_unpack(queryJ, "{s:s,s:s,s?s,s?o}", "action_name", &actionName,"audio_role",&audioRole,"media_name",&mediaName,"action_context",&actionContext); 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 = event_name:%s audio_role:%s", eventName,audioRole); + AFB_DEBUG("Parsed input arguments = action_name:%s audio_role:%s", actionName,audioRole); - // Verify if known event for audio role - int iRoleIndex = FindRoleIndex(audioRole); - if (iRoleIndex < 0) { + // Verify if known action for audio role + RoleInfoT * pRole = GetRole(audioRole); + if ( pRole == NULL ) + { afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole); return; } - 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 ) + // Check to find specific action + int iActionFound = 0; + if (pRole->pActionList) { + int iNumberActions = pRole->pActionList->len; + char * pTargetActionName = NULL; + for ( int i = 0 ; i < iNumberActions; i++) { - iEventFound = 1; - break; + pTargetActionName = g_ptr_array_index(pRole->pActionList,i); + if ( strcasecmp(pTargetActionName,actionName)==0) { + iActionFound = 1; + break; + } } } - if (!iEventFound) { - afb_req_fail_f(req, "Event not found for audio role", "Event not found for roke:%s",audioRole); + + if (!iActionFound) { + afb_req_fail_f(req, "Event not found for audio role", "Event -> %s not found for role:%s",actionName,audioRole); return; } +#ifndef AHL_DISCONNECT_POLICY // Call policy to allow custom policy actions in current context (e.g. cancel playback) - policyAllowed = Policy_PostEvent(eventName, audioRole, mediaName, (void*)eventContext); + int policyAllowed = Policy_PostAction(queryJ); if (!policyAllowed) { - afb_req_fail(req, "Audio policy violation", "Post sound event not allowed in current context"); + afb_req_fail(req, "Audio policy violation", "Post sound action not allowed in current context"); return; } +#endif - afb_event_push(g_AHLCtx.policyCtx.postEvent,queryJ); + //afb_event_push(g_AHLCtx.policyCtx.postEvent,queryJ); - afb_req_success(req, NULL, "Posted sound event"); + afb_req_success(req, NULL, "Posted sound action"); } @@ -989,7 +1329,11 @@ PUBLIC void audiohlapi_subscribe(struct afb_req req) 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)) { + if(pEventName == NULL) { + afb_req_fail(req, "failed", "Invalid event"); + return; + } + else if(!strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)) { afb_req_subscribe(req, g_AHLCtx.policyCtx.propertyEvent); AFB_DEBUG("Client subscribed to endpoint property events"); } @@ -997,8 +1341,8 @@ PUBLIC void audiohlapi_subscribe(struct afb_req req) 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); + else if(!strcasecmp(pEventName, AHL_POST_ACTION_EVENT)) { + afb_req_subscribe(req, g_AHLCtx.policyCtx.postActionEvent); AFB_DEBUG("Client subscribed to post event calls events"); } else { @@ -1028,7 +1372,11 @@ PUBLIC void audiohlapi_unsubscribe(struct afb_req req) 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)) { + if(pEventName == NULL) { + afb_req_fail(req, "failed", "Invalid event"); + return; + } + else if(!strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)) { afb_req_unsubscribe(req, g_AHLCtx.policyCtx.propertyEvent); AFB_DEBUG("Client unsubscribed to endpoint property events"); } @@ -1036,8 +1384,8 @@ PUBLIC void audiohlapi_unsubscribe(struct afb_req req) 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); + else if(!strcasecmp(pEventName, AHL_POST_ACTION_EVENT)) { + afb_req_unsubscribe(req, g_AHLCtx.policyCtx.postActionEvent); AFB_DEBUG("Client unsubscribed to post event calls events"); } else { @@ -1048,3 +1396,182 @@ PUBLIC void audiohlapi_unsubscribe(struct afb_req req) afb_req_success(req, NULL, "Unsubscribe to events finished"); } + +// Since the policy is currently in the same binding, it cannot raise events on its own +// This is a first step toward isolation, when policy is migrated in its own binding it can simply raise AGL events +// This binding will register for these policy events and will execute the code below upon event reception +PUBLIC void audiohlapi_raise_event(json_object * pEventDataJ) +{ + char * pEventName = NULL; + + int err = wrap_json_unpack(pEventDataJ,"{s:s}","event_name", &pEventName); + if(err) + { + AFB_ERROR("Unable to retrieve event name"); + return; + } + + if(strcasecmp(pEventName, AHL_ENDPOINT_INIT_EVENT)==0) { + char * pAudioRole = NULL; + endpointID_t endpointID = AHL_UNDEFINED; + EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; + int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i,s:s}", + "endpoint_id", &endpointID, + "endpoint_type", &endpointType, + "audio_role", &pAudioRole); + if(err) + { + AFB_ERROR("Unable to unpack property init event"); + return; + } + + RoleInfoT * pRole = GetRole(pAudioRole); + if ( pRole == NULL ){ + AFB_ERROR("Requested audio role does not exist in current configuration -> %s", pAudioRole); + return; + } + + EndpointInfoT * pEndpointInfo = InitEndpointInfo(); + g_assert_nonnull(pEndpointInfo); + PolicyCtxJSONToEndpoint(pEventDataJ,pEndpointInfo); + + err = ReplaceEndpointInfoWithRole(endpointID,endpointType,pRole,pEndpointInfo); + if(err == AHL_FAIL) + { + AFB_ERROR("Can't update EndpointInfo aborting"); + return; + } + + // Remove event name from object + // json_object_object_del(pEventDataJ,"event_name"); + //afb_event_push(g_AHLCtx.policyCtx.propertyEvent,pEventDataJ); // Not broadcasted to application at this time + } + else if(strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)==0) { + char * pAudioRole = NULL; + char * pPropertyName = NULL; + endpointID_t endpointID = AHL_UNDEFINED; + EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; + json_object * propValueJ = NULL; + int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i,s:s,s:o,s:s}", + "endpoint_id", &endpointID, + "endpoint_type", &endpointType, + "property_name", &pPropertyName, + "value",&propValueJ, + "audio_role", &pAudioRole); + if(err) + { + AFB_ERROR("Unable to unpack property event"); + return; + } + RoleInfoT * pRole = GetRole(pAudioRole); + if ( pRole == NULL ){ + AFB_ERROR("Requested audio role does not exist in current configuration -> %s", pAudioRole); + return; + } + EndpointInfoT * pEndpointInfo = GetEndpointInfoWithRole(endpointID,endpointType,pRole); + // update property value + if (pEndpointInfo->pPropTable) + { + json_type jType = json_object_get_type(propValueJ); + switch (jType) { + case json_type_double: + Add_Endpoint_Property_Double(pEndpointInfo,pPropertyName,json_object_get_double(propValueJ)); + break; + case json_type_int: + Add_Endpoint_Property_Int(pEndpointInfo,pPropertyName,json_object_get_int(propValueJ)); + break; + case json_type_string: + Add_Endpoint_Property_String(pEndpointInfo,pPropertyName,json_object_get_string(propValueJ)); + break; + default: + AFB_ERROR("Invalid property argument Property value not a valid json object query=%s", json_object_get_string(propValueJ)); + return ; + } + } + // Remove event name from object + json_object_object_del(pEventDataJ,"event_name"); + afb_event_push(g_AHLCtx.policyCtx.propertyEvent,pEventDataJ); + } + else if(strcasecmp(pEventName, AHL_ENDPOINT_VOLUME_EVENT)==0) { + char * pAudioRole = NULL; + endpointID_t endpointID = AHL_UNDEFINED; + EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; + int iVolume = 0; + int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i,s:i,s:s}", + "endpoint_id", &endpointID, + "endpoint_type", &endpointType, + "value",&iVolume, + "audio_role", &pAudioRole); + if(err) + { + AFB_ERROR("Unable to unpack volume event data"); + return; + } + RoleInfoT * pRole = GetRole(pAudioRole); + if ( pRole == NULL ){ + AFB_ERROR("Requested audio role does not exist in current configuration -> %s", pAudioRole); + return; + } + EndpointInfoT * pEndpointInfo = GetEndpointInfoWithRole(endpointID,endpointType,pRole); + // update volume value + pEndpointInfo->iVolume = iVolume; + // Remove event name from object + json_object_object_del(pEventDataJ,"event_name"); + afb_event_push(g_AHLCtx.policyCtx.volumeEvent,pEventDataJ); + } + else if(strcasecmp(pEventName, AHL_POST_ACTION_EVENT)==0) { + // Remove event name from object + json_object_object_del(pEventDataJ,"event_name"); + afb_event_push(g_AHLCtx.policyCtx.postActionEvent,pEventDataJ); + } + else if(strcasecmp(pEventName, AHL_STREAM_STATE_EVENT)==0) { + streamID_t streamID = AHL_UNDEFINED; + StreamEventT streamEvent = STREAM_EVENT_MAXVALUE; + int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i}", + "stream_id", &streamID, + "state_event", &streamEvent); + if(err) + { + AFB_ERROR("Unable to unpack stream event data"); + return; + } + + StreamInfoT * pStreamInfo = GetStream(streamID); + if (pStreamInfo == NULL) { + AFB_ERROR("Specified stream not currently active stream_id -> %d",streamID); + return; + } + + // update streamstate value + switch (streamEvent) { + case STREAM_EVENT_START: + pStreamInfo->streamState = STREAM_STATE_RUNNING; + break; + case STREAM_EVENT_STOP: + pStreamInfo->streamState = STREAM_STATE_IDLE; + break; + case STREAM_EVENT_PAUSE: + pStreamInfo->streamState = STREAM_STATE_PAUSED; + break; + case STREAM_EVENT_RESUME: + pStreamInfo->streamState = STREAM_STATE_RUNNING; + break; + case STREAM_EVENT_MUTED: + pStreamInfo->streamMute = STREAM_MUTED; + break; + case STREAM_EVENT_UNMUTED: + pStreamInfo->streamMute = STREAM_UNMUTED; + break; + default: + AFB_ERROR("Unknown stream event"); + } + + // Remove event name from object + json_object_object_del(pEventDataJ,"event_name"); + AFB_ERROR("pEventDataJ=%s", json_object_get_string(pEventDataJ)); + afb_event_push(pStreamInfo->streamStateEvent,pEventDataJ); + } + else { + AFB_ERROR("Unknown event name"); + } +} \ No newline at end of file -- cgit 1.2.3-korg