diff options
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | ahl-binding/CMakeLists.txt | 2 | ||||
-rw-r--r-- | ahl-binding/ahl-apidef.h | 251 | ||||
-rw-r--r-- | ahl-binding/ahl-apidef.json | 160 | ||||
-rw-r--r-- | ahl-binding/ahl-binding.c | 951 | ||||
-rw-r--r-- | ahl-binding/ahl-binding.h | 36 | ||||
-rw-r--r-- | ahl-binding/ahl-config.c | 18 | ||||
-rw-r--r-- | ahl-binding/ahl-deviceenum.c | 63 | ||||
-rw-r--r-- | ahl-binding/ahl-json.c | 296 | ||||
-rw-r--r-- | ahl-binding/ahl-json.h | 26 | ||||
-rw-r--r-- | ahl-policy/ahl-interface.h | 3 | ||||
-rw-r--r-- | ahl-policy/ahl-policy.c | 572 | ||||
-rw-r--r-- | ahl-policy/ahl-policy.h | 20 | ||||
-rwxr-xr-x[-rw-r--r--] | ahl-utilities/ahl-policy-utils.c | 259 | ||||
-rwxr-xr-x[-rw-r--r--] | ahl-utilities/ahl-policy-utils.h | 61 | ||||
-rw-r--r-- | conf.d/project/ahl-audiok4a-config.json | 8 | ||||
-rw-r--r-- | htdocs/audiohl.html | 21 |
17 files changed, 1287 insertions, 1464 deletions
@@ -8,10 +8,6 @@ ``` # Initial clone with submodules git clone https://github.com/Audiokinetic-Automotive/afb-audiohighlevel.git -<<<<<<< HEAD -cd afb-audiohighlevel -======= ->>>>>>> 8a584f01b46d251fdc5de8b071eff755d99f0090 ``` diff --git a/ahl-binding/CMakeLists.txt b/ahl-binding/CMakeLists.txt index 44944e5..0dee304 100644 --- a/ahl-binding/CMakeLists.txt +++ b/ahl-binding/CMakeLists.txt @@ -20,7 +20,7 @@ PROJECT_TARGET_ADD(audiohighlevel) # Define project Targets - ADD_LIBRARY(${TARGET_NAME} MODULE ahl-binding.c ahl-deviceenum.c ahl-config.c) + ADD_LIBRARY(${TARGET_NAME} MODULE ahl-binding.c ahl-deviceenum.c ahl-config.c ahl-json.c) # Binder exposes a unique public entry point SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES diff --git a/ahl-binding/ahl-apidef.h b/ahl-binding/ahl-apidef.h index 9002c6f..2c77827 100644 --- a/ahl-binding/ahl-apidef.h +++ b/ahl-binding/ahl-apidef.h @@ -36,113 +36,86 @@ static const char _afb_description_v2_ahl_4a[] = "200\":{\"description\":\"A complex object array response\",\"content\":{" "\"application/json\":{\"schema\":{\"$ref\":\"#/components/schemas/afb-re" "ply\"}}}},\"400\":{\"description\":\"Invalid arguments\"}}},\"paths\":{\"" - "/get_sources\":{\"description\":\"Retrieve array of available audio sour" - "ces\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"audio_role\"" - ",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"2" - "00\":{\"$ref\":\"#/components/responses/200\",\"response\":{\"descriptio" - "n\":\"Array of endpoint info structures\",\"type\":\"array\",\"items\":{" - "\"$ref\":\"#/components/schemas/endpoint_info\"}}},\"400\":{\"$ref\":\"#" - "/components/responses/400\"}}}},\"/get_sinks\":{\"description\":\"Retrie" - "ve array of available audio sinks\",\"get\":{\"parameters\":[{\"in\":\"q" - "uery\",\"name\":\"audio_role\",\"required\":true,\"schema\":{\"type\":\"" - "string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/20" - "0\",\"response\":{\"description\":\"Array of endpoint info structures\"," - "\"type\":\"array\",\"items\":{\"$ref\":\"#/components/schemas/endpoint_i" - "nfo\"}}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/stream_" - "open\":{\"description\":\"Request opening a stream\",\"get\":{\"x-permis" - "sions\":{\"$ref\":\"#/components/x-permissions/audiostream\"},\"paramete" - "rs\":[{\"in\":\"query\",\"name\":\"audio_role\",\"required\":true,\"sche" - "ma\":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"endpoint_type\"" - ",\"required\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"n" - "ame\":\"endpoint_id\",\"required\":false,\"schema\":{\"type\":\"int\"}}]" - ",\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"respo" - "nse\":{\"description\":\"Stream information structure\",\"$ref\":\"#/com" - "ponents/schemas/stream_info\"}},\"400\":{\"$ref\":\"#/components/respons" - "es/400\"}}}},\"/stream_close\":{\"description\":\"Request closing a stre" - "am\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/a" - "udiostream\"},\"parameters\":[{\"in\":\"query\",\"name\":\"stream_id\",\"" - "required\":true,\"schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{" - "\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#/component" - "s/responses/400\"}}}},\"/set_stream_state\":{\"description\":\"Change st" - "ream active state\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/" - "x-permissions/streamcontrol\"},\"parameters\":[{\"in\":\"query\",\"name\"" - ":\"stream_id\",\"required\":true,\"schema\":{\"type\":\"int\"}},{\"in\":" - "\"query\",\"name\":\"state\",\"required\":true,\"schema\":{\"type\":\"in" - "t\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"}," - "\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/set_stream_mute\"" - ":{\"description\":\"Change stream mute state\",\"get\":{\"x-permissions\"" - ":{\"$ref\":\"#/components/x-permissions/streamcontrol\"},\"parameters\":" - "[{\"in\":\"query\",\"name\":\"stream_id\",\"required\":true,\"schema\":{" - "\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"mute\",\"required\":true" - ",\"schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/co" - "mponents/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/400" - "\"}}}},\"/get_stream_info\":{\"description\":\"Retrieve stream informati" - "on\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"stream_id\",\"" - "required\":true,\"schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{" - "\"$ref\":\"#/components/responses/200\",\"response\":{\"description\":\"" - "Stream information structure\",\"$ref\":\"#/components/schemas/stream_in" - "fo\"}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/set_volum" - "e\":{\"description\":\"Set volume on endpoint\",\"get\":{\"x-permissions" + "/get_endpoints\":{\"description\":\"Retrieve array of available audio en" + "dpoints\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"audio_ro" + "le\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query" + "\",\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"e" + "num\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"" + ",\"response\":{\"description\":\"Array of endpoint info structures\",\"t" + "ype\":\"array\",\"items\":{\"$ref\":\"#/components/schemas/endpoint_info" + "\"}}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/stream_ope" + "n\":{\"description\":\"Request opening a stream\",\"get\":{\"x-permissio" + "ns\":{\"$ref\":\"#/components/x-permissions/audiostream\"},\"parameters\"" + ":[{\"in\":\"query\",\"name\":\"audio_role\",\"required\":true,\"schema\"" + ":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"endpoint_type\",\"r" + "equired\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\"" + ":\"endpoint_id\",\"required\":false,\"schema\":{\"type\":\"int\"}}],\"re" + "sponses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"response\"" + ":{\"description\":\"Stream information structure\",\"$ref\":\"#/componen" + "ts/schemas/stream_info\"}},\"400\":{\"$ref\":\"#/components/responses/40" + "0\"}}}},\"/stream_close\":{\"description\":\"Request closing a stream\"," + "\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/audios" + "tream\"},\"parameters\":[{\"in\":\"query\",\"name\":\"stream_id\",\"requ" + "ired\":false,\"schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$" + "ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#/components/r" + "esponses/400\"}}}},\"/set_stream_state\":{\"description\":\"Change strea" + "m active and/or mute state\",\"get\":{\"x-permissions\":{\"$ref\":\"#/co" + "mponents/x-permissions/streamcontrol\"},\"parameters\":[{\"in\":\"query\"" + ",\"name\":\"stream_id\",\"required\":false,\"schema\":{\"type\":\"int\"}" + "},{\"in\":\"query\",\"name\":\"state\",\"required\":false,\"schema\":{\"" + "type\":\"int\"}},{\"in\":\"query\",\"name\":\"mute\",\"required\":false," + "\"schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/com" + "ponents/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"" + "}}}},\"/get_stream_info\":{\"description\":\"Retrieve stream information" + "\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"stream_id\",\"r" + "equired\":true,\"schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"" + "$ref\":\"#/components/responses/200\",\"response\":{\"description\":\"St" + "ream information structure\",\"$ref\":\"#/components/schemas/stream_info" + "\"}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/volume\":{\"" + "description\":\"Set or get volume on endpoint\",\"get\":{\"x-permissions" "\":{\"$ref\":\"#/components/x-permissions/endpointcontrol\"},\"parameter" "s\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\"required\":true,\"sc" "hema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"" "required\":true,\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\"" - ":\"volume\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"respo" - "nses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$re" - "f\":\"#/components/responses/400\"}}}},\"/get_volume\":{\"description\":" - "\"Get endpoint volume\",\"get\":{\"parameters\":[{\"in\":\"query\",\"nam" - "e\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enum\"}}," - "{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\"schema\":" - "{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/res" - "ponses/200\",\"response\":{\"description\":\"Endpoint volume value\",\"t" - "ype\":\"double\"}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}}," - "\"/get_endpoint_info\":{\"description\":\"Retrieve endpoint information " - "including its properties\",\"get\":{\"parameters\":[{\"in\":\"query\",\"" - "name\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enum\"" - "}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":false,\"schem" - "a\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components" - "/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"" - "/set_property\":{\"description\":\"Set endpoint property value\",\"get\"" - ":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/endpointcontr" - "ol\"},\"parameters\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\"req" - "uired\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":" - "\"endpoint_id\",\"required\":false,\"schema\":{\"type\":\"int\"}},{\"in\"" - ":\"query\",\"name\":\"property_name\",\"required\":true,\"schema\":{\"ty" - "pe\":\"string\"}},{\"in\":\"query\",\"name\":\"value\",\"required\":true" - ",\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#" - "/components/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/" - "400\"}}}},\"/get_property\":{\"description\":\"Get endpoint property val" - "ue\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"endpoint_type" - "\",\"required\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"" - "name\":\"endpoint_id\",\"required\":false,\"schema\":{\"type\":\"int\"}}" - ",{\"in\":\"query\",\"name\":\"property_name\",\"required\":true,\"schema" - "\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/componen" - "ts/responses/200\",\"response\":{\"description\":\"Property value\",\"ty" - "pe\":\"double\"}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"" - "/get_list_actions\":{\"description\":\"Retrieve a list of supported acti" - "ons for a particular audio role\",\"get\":{\"parameters\":[{\"in\":\"que" - "ry\",\"name\":\"audio_role\",\"required\":true,\"schema\":{\"type\":\"st" - "ring\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"" - "},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/post_action\":" - "{\"description\":\"Post sound or audio device related action event (exte" - "ndable mechanism)\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/" - "x-permissions/soundevent\"},\"parameters\":[{\"in\":\"query\",\"name\":\"" - "action_name\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"in\"" - ":\"query\",\"name\":\"audio_role\",\"required\":true,\"schema\":{\"type\"" - ":\"string\"}},{\"in\":\"query\",\"name\":\"media_name\",\"required\":fal" - "se,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"action" - "_context\",\"required\":false,\"schema\":{\"type\":\"object\"}}],\"respo" - "nses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$re" - "f\":\"#/components/responses/400\"}}}},\"/subscribe\":{\"description\":\"" - "Subscribe to audio high level events\",\"get\":{\"parameters\":[{\"in\":" - "\"query\",\"name\":\"events\",\"required\":true,\"schema\":{\"type\":\"a" - "rray\",\"items\":{\"type\":\"string\"}}}],\"responses\":{\"200\":{\"$ref" - "\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#/components/resp" - "onses/400\"}}}},\"/unsubscribe\":{\"description\":\"Unubscribe to audio " - "high level events\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":" - "\"events\",\"required\":true,\"schema\":{\"type\":\"array\",\"items\":{\"" - "type\":\"string\"}}}],\"responses\":{\"200\":{\"$ref\":\"#/components/re" - "sponses/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}}}}}" + ":\"volume\",\"required\":false,\"schema\":{\"type\":\"string\"}}],\"resp" + "onses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$r" + "ef\":\"#/components/responses/400\"}}}},\"/get_endpoint_info\":{\"descri" + "ption\":\"Retrieve endpoint information including its properties\",\"get" + "\":{\"parameters\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\"requi" + "red\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"" + "endpoint_id\",\"required\":true,\"schema\":{\"type\":\"int\"}}],\"respon" + "ses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref" + "\":\"#/components/responses/400\"}}}},\"/property\":{\"description\":\"S" + "et/get endpoint property value\",\"get\":{\"x-permissions\":{\"$ref\":\"" + "#/components/x-permissions/endpointcontrol\"},\"parameters\":[{\"in\":\"" + "query\",\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\"" + ":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":true" + ",\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"property_na" + "me\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query" + "\",\"name\":\"value\",\"required\":false,\"schema\":{\"type\":\"string\"" + "}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"4" + "00\":{\"$ref\":\"#/components/responses/400\"}}}},\"/get_list_actions\":" + "{\"description\":\"Retrieve a list of supported actions for a particular" + " audio role\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"audi" + "o_role\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"response" + "s\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\"" + ":\"#/components/responses/400\"}}}},\"/post_action\":{\"description\":\"" + "Post sound or audio device related action event (extendable mechanism)\"" + ",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/sound" + "event\"},\"parameters\":[{\"in\":\"query\",\"name\":\"action_name\",\"re" + "quired\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"name" + "\":\"audio_role\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"" + "in\":\"query\",\"name\":\"media_name\",\"required\":false,\"schema\":{\"" + "type\":\"string\"}},{\"in\":\"query\",\"name\":\"action_context\",\"requ" + "ired\":false,\"schema\":{\"type\":\"object\"}}],\"responses\":{\"200\":{" + "\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#/component" + "s/responses/400\"}}}},\"/event_subscription\":{\"description\":\"Subscri" + "be to audio high level events\",\"get\":{\"parameters\":[{\"in\":\"query" + "\",\"name\":\"events\",\"required\":true,\"schema\":{\"type\":\"array\"," + "\"items\":{\"type\":\"string\"}}},{\"in\":\"query\",\"name\":\"subscribe" + "\",\"required\":true,\"schema\":{\"type\":\"int\"}}],\"responses\":{\"20" + "0\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#/comp" + "onents/responses/400\"}}}}}}" ; static const struct afb_auth _afb_auths_v2_ahl_4a[] = { @@ -152,36 +125,24 @@ static const struct afb_auth _afb_auths_v2_ahl_4a[] = { { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:soundevent" } }; - void audiohlapi_get_sources(struct afb_req req); - void audiohlapi_get_sinks(struct afb_req req); + void audiohlapi_get_endpoints(struct afb_req req); void audiohlapi_stream_open(struct afb_req req); void audiohlapi_stream_close(struct afb_req req); void audiohlapi_set_stream_state(struct afb_req req); - void audiohlapi_set_stream_mute(struct afb_req req); void audiohlapi_get_stream_info(struct afb_req req); - void audiohlapi_set_volume(struct afb_req req); - void audiohlapi_get_volume(struct afb_req req); + void audiohlapi_volume(struct afb_req req); void audiohlapi_get_endpoint_info(struct afb_req req); - void audiohlapi_set_property(struct afb_req req); - void audiohlapi_get_property(struct afb_req req); + void audiohlapi_property(struct afb_req req); void audiohlapi_get_list_actions(struct afb_req req); void audiohlapi_post_action(struct afb_req req); - void audiohlapi_subscribe(struct afb_req req); - void audiohlapi_unsubscribe(struct afb_req req); + void audiohlapi_event_subscription(struct afb_req req); static const struct afb_verb_v2 _afb_verbs_v2_ahl_4a[] = { { - .verb = "get_sources", - .callback = audiohlapi_get_sources, - .auth = NULL, - .info = "Retrieve array of available audio sources", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "get_sinks", - .callback = audiohlapi_get_sinks, + .verb = "get_endpoints", + .callback = audiohlapi_get_endpoints, .auth = NULL, - .info = "Retrieve array of available audio sinks", + .info = "Retrieve array of available audio endpoints", .session = AFB_SESSION_NONE_V2 }, { @@ -202,14 +163,7 @@ static const struct afb_verb_v2 _afb_verbs_v2_ahl_4a[] = { .verb = "set_stream_state", .callback = audiohlapi_set_stream_state, .auth = &_afb_auths_v2_ahl_4a[1], - .info = "Change stream active state", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "set_stream_mute", - .callback = audiohlapi_set_stream_mute, - .auth = &_afb_auths_v2_ahl_4a[1], - .info = "Change stream mute state", + .info = "Change stream active and/or mute state", .session = AFB_SESSION_NONE_V2 }, { @@ -220,17 +174,10 @@ static const struct afb_verb_v2 _afb_verbs_v2_ahl_4a[] = { .session = AFB_SESSION_NONE_V2 }, { - .verb = "set_volume", - .callback = audiohlapi_set_volume, + .verb = "volume", + .callback = audiohlapi_volume, .auth = &_afb_auths_v2_ahl_4a[2], - .info = "Set volume on endpoint", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "get_volume", - .callback = audiohlapi_get_volume, - .auth = NULL, - .info = "Get endpoint volume", + .info = "Set or get volume on endpoint", .session = AFB_SESSION_NONE_V2 }, { @@ -241,17 +188,10 @@ static const struct afb_verb_v2 _afb_verbs_v2_ahl_4a[] = { .session = AFB_SESSION_NONE_V2 }, { - .verb = "set_property", - .callback = audiohlapi_set_property, + .verb = "property", + .callback = audiohlapi_property, .auth = &_afb_auths_v2_ahl_4a[2], - .info = "Set endpoint property value", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "get_property", - .callback = audiohlapi_get_property, - .auth = NULL, - .info = "Get endpoint property value", + .info = "Set/get endpoint property value", .session = AFB_SESSION_NONE_V2 }, { @@ -269,20 +209,13 @@ static const struct afb_verb_v2 _afb_verbs_v2_ahl_4a[] = { .session = AFB_SESSION_NONE_V2 }, { - .verb = "subscribe", - .callback = audiohlapi_subscribe, + .verb = "event_subscription", + .callback = audiohlapi_event_subscription, .auth = NULL, .info = "Subscribe to audio high level events", .session = AFB_SESSION_NONE_V2 }, { - .verb = "unsubscribe", - .callback = audiohlapi_unsubscribe, - .auth = NULL, - .info = "Unubscribe to audio high level events", - .session = AFB_SESSION_NONE_V2 - }, - { .verb = NULL, .callback = NULL, .auth = NULL, diff --git a/ahl-binding/ahl-apidef.json b/ahl-binding/ahl-apidef.json index 81d410c..59f7a32 100644 --- a/ahl-binding/ahl-apidef.json +++ b/ahl-binding/ahl-apidef.json @@ -137,8 +137,8 @@ } }, "paths": { - "/get_sources": { - "description": "Retrieve array of available audio sources", + "/get_endpoints": { + "description": "Retrieve array of available audio endpoints", "get": { "parameters": [ { @@ -146,30 +146,12 @@ "name": "audio_role", "required": true, "schema": { "type": "string" } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200", - "response": { - "description": "Array of endpoint info structures", - "type": "array", - "items": { "$ref": "#/components/schemas/endpoint_info"} - } }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/get_sinks": { - "description": "Retrieve array of available audio sinks", - "get": { - "parameters": [ { "in": "query", - "name": "audio_role", + "name": "endpoint_type", "required": true, - "schema": { "type": "string" } + "schema": { "type": "enum" } } ], "responses": { @@ -229,7 +211,7 @@ { "in": "query", "name": "stream_id", - "required": true, + "required": false, "schema": { "type": "int" } } ], @@ -240,7 +222,7 @@ } }, "/set_stream_state": { - "description": "Change stream active state", + "description": "Change stream active and/or mute state", "get": { "x-permissions": { "$ref": "#/components/x-permissions/streamcontrol" @@ -249,39 +231,19 @@ { "in": "query", "name": "stream_id", - "required": true, + "required": false, "schema": {"type": "int"} }, { "in": "query", "name": "state", - "required": true, - "schema": {"type": "int"} - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/set_stream_mute": { - "description": "Change stream mute state", - "get": { - "x-permissions": { - "$ref": "#/components/x-permissions/streamcontrol" - }, - "parameters": [ - { - "in": "query", - "name": "stream_id", - "required": true, + "required": false, "schema": {"type": "int"} }, { "in": "query", "name": "mute", - "required": true, + "required": false, "schema": {"type": "int"} } ], @@ -314,8 +276,8 @@ } } }, - "/set_volume": { - "description": "Set volume on endpoint", + "/volume": { + "description": "Set or get volume on endpoint", "get": { "x-permissions": { "$ref": "#/components/x-permissions/endpointcontrol" }, "parameters": [ @@ -334,7 +296,7 @@ { "in": "query", "name": "volume", - "required": true, + "required": false, "schema": { "type": "string" } } ], @@ -344,35 +306,6 @@ } } }, - "/get_volume": { - "description": "Get endpoint volume", - "get": { - "parameters": [ - { - "in": "query", - "name": "endpoint_type", - "required": true, - "schema": { "type": "enum" } - }, - { - "in": "query", - "name": "endpoint_id", - "required": true, - "schema": { "type": "int" } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200", - "response": { - "description": "Endpoint volume value", - "type": "double" - } - }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, "/get_endpoint_info": { "description": "Retrieve endpoint information including its properties", "get": { @@ -386,7 +319,7 @@ { "in": "query", "name": "endpoint_id", - "required": false, + "required": true, "schema": { "type": "int" } } ], @@ -396,8 +329,8 @@ } } }, - "/set_property": { - "description": "Set endpoint property value", + "/property": { + "description": "Set/get endpoint property value", "get": { "x-permissions": { "$ref": "#/components/x-permissions/endpointcontrol" }, "parameters": [ @@ -410,7 +343,7 @@ { "in": "query", "name": "endpoint_id", - "required": false, + "required": true, "schema": { "type": "int" } }, { @@ -422,47 +355,12 @@ { "in": "query", "name": "value", - "required": true, - "schema": { "type": "string" } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/get_property": { - "description": "Get endpoint property value", - "get": { - "parameters": [ - { - "in": "query", - "name": "endpoint_type", - "required": true, - "schema": { "type": "enum" } - }, - { - "in": "query", - "name": "endpoint_id", "required": false, - "schema": { "type": "int" } - }, - { - "in": "query", - "name": "property_name", - "required": true, "schema": { "type": "string" } } ], "responses": { - "200": { - "$ref": "#/components/responses/200", - "response": { - "description": "Property value", - "type": "double" - } - }, + "200": { "$ref": "#/components/responses/200" }, "400": { "$ref": "#/components/responses/400" } } } @@ -520,7 +418,7 @@ } } }, - "/subscribe": { + "/event_subscription": { "description": "Subscribe to audio high level events", "get": { "parameters": [ @@ -531,28 +429,12 @@ "schema": { "type": "array", "items": { "type": "string" } } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/unsubscribe": { - "description": "Unubscribe to audio high level events", - "get": { - "parameters": [ + }, { "in": "query", - "name": "events", + "name": "subscribe", "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } + "schema": { "type": "int" } } ], "responses": { diff --git a/ahl-binding/ahl-binding.c b/ahl-binding/ahl-binding.c index 264930f..c205c8e 100644 --- a/ahl-binding/ahl-binding.c +++ b/ahl-binding/ahl-binding.c @@ -14,7 +14,6 @@ * limitations under the License. */ -#define _GNU_SOURCE #include <stdio.h> #include <string.h> @@ -22,12 +21,11 @@ #include "ahl-apidef.h" // Generated from JSON OpenAPI #include "wrap-json.h" #include "ahl-policy.h" -#include "ahl-policy-utils.h" +#include "ahl-json.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) { @@ -76,108 +74,6 @@ static StreamMuteT StreamMuteToEnum(char * in_pStreamMuteStr) 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}", - "sample_rate", pAudioFormat->sampleRate, - "num_channels", pAudioFormat->numChannels, - "sample_type", pAudioFormat->sampleType); -} - -// 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:s,s:s,s:s,s:s,s:s,s:s}", - "endpoint_id", pEndpointInfo->endpointID, - "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:s,s:s,s:s}", - "stream_id", pStreamInfo->streamID, - "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); -} - static streamID_t CreateNewStreamID() { streamID_t newID = g_AHLCtx.nextStreamID; @@ -209,32 +105,6 @@ static EndpointInfoT * GetEndpointInfoWithRole(endpointID_t in_endpointID, Endpo 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; @@ -267,6 +137,79 @@ static RoleInfoT * GetRole(char * in_pAudioRoleName) return g_hash_table_lookup(g_AHLCtx.policyCtx.pRoleInfo,in_pAudioRoleName); } +static int CloseStream(AHLClientCtxT * in_pClientCtx, streamID_t streamID,struct afb_req * pReq) { + StreamInfoT * pStreamInfo = GetStream(streamID); + if (pStreamInfo == NULL) { + AFB_ERROR("Specified stream not currently active stream_id -> %d",streamID); + return AHL_FAIL; + } + +#ifndef AHL_DISCONNECT_POLICY + json_object *pPolicyStreamJ = NULL; + int err = StreamInfoToJSON(pStreamInfo, &pPolicyStreamJ); + if (err == AHL_POLICY_UTIL_FAIL) + { + AFB_ERROR("Audio policy violation, Unable to get JSON object for Policy_CloseStream"); + return AHL_FAIL; + } + int policyAllowed = Policy_CloseStream(pPolicyStreamJ); + if (policyAllowed == AHL_POLICY_REJECT) + { + AFB_ERROR("Close stream not allowed in current context"); + return AHL_FAIL; + } +#endif + // Unsubscribe client from stream events + if (pReq != NULL) { + char streamEventName[128]; + snprintf(streamEventName,128,"ahl_streamstate_%d",streamID); + int iValid = afb_event_is_valid(pStreamInfo->streamStateEvent); + if (iValid) { + err = afb_req_unsubscribe(*pReq,pStreamInfo->streamStateEvent); + if (err) { + AFB_ERROR("Could not unsubscribe to stream specific state change event"); + return AHL_FAIL; + } + } + } + + // 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 + if (in_pClientCtx->pStreamAccessList) { + for (int i = 0; i < in_pClientCtx->pStreamAccessList->len ; i++) { + streamID_t iID = g_array_index(in_pClientCtx->pStreamAccessList,streamID_t,i); + if (iID == streamID) { + g_array_remove_index(in_pClientCtx->pStreamAccessList, i); + } + } + } + + return AHL_SUCCESS; +} + +static int CloseAllClientStreams(AHLClientCtxT * in_pClientCtx, struct afb_req * pReq) +{ + g_assert_nonnull(in_pClientCtx); + if (in_pClientCtx->pStreamAccessList != NULL) { + while( in_pClientCtx->pStreamAccessList->len ) + { + streamID_t streamID = g_array_index(in_pClientCtx->pStreamAccessList,streamID_t,0); + int err = CloseStream(in_pClientCtx,streamID,pReq); + if (err) { + return err; + } + } + } + + return AHL_SUCCESS; +} + static AHLClientCtxT * AllocateClientContext() { AHLClientCtxT * pClientCtx = malloc(sizeof(AHLClientCtxT)); @@ -278,35 +221,13 @@ static void TerminateClientContext(void * ptr) { AHLClientCtxT * pClientCtx = (AHLClientCtxT *) ptr; if (pClientCtx != NULL) { - - // 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_POLICY_UTIL_FAIL) - { - AFB_ERROR("Audio policy violation, Unable to get JSON object for Policy_CloseStream"); - return; - } - Policy_CloseStream(pPolicyStreamJ); - } -#endif + CloseAllClientStreams(pClientCtx,NULL); + + if (pClientCtx->pStreamAccessList) { g_array_free( pClientCtx->pStreamAccessList, TRUE); pClientCtx->pStreamAccessList = NULL; } + free(pClientCtx); } } @@ -327,30 +248,28 @@ static int CheckStreamAccessControl(AHLClientCtxT * pClientCtx, streamID_t strea 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); + int err = !afb_event_is_valid(g_AHLCtx.policyCtx.propertyEvent); if (err) { AFB_ERROR("Could not create endpoint property change event"); - return err; + return AHL_FAIL; } 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"); - return err; + return AHL_FAIL; } 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 action event call event"); - return err; + return AHL_FAIL; } - return err; + return AHL_SUCCESS; } static void AhlBindingTerm() @@ -434,42 +353,36 @@ static void AhlBindingTerm() // Binding initialization PUBLIC int AhlBindingInit() { - int err = 0; - memset(&g_AHLCtx,0,sizeof(g_AHLCtx)); // Register exit function atexit(AhlBindingTerm); - //Create AGL Events - err=CreateEvents(); - if(err) - { - //Error messages already reported inside CreateEvents - return err; + // Create AGL Events + int err = CreateEvents(); + if(err) { + // Error messages already reported inside CreateEvents + return AHL_FAIL; } // Parse high-level binding JSON configuration file (will build device lists) err = ParseHLBConfig(); - if(err) - { - //Error messages already reported inside ParseHLBConfig - return err; + if(err) { + // Error messages already reported inside ParseHLBConfig + return AHL_FAIL; } #ifndef AHL_DISCONNECT_POLICY // Policy initialization err = Policy_Init(); - if(err == AHL_POLICY_REJECT) - { + if(err == AHL_POLICY_REJECT) { //Error messages already reported inside PolicyInit - return err; + return AHL_FAIL; } - // for all audio Roles + // Call policy for initalization of all source + sink endpoints 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)) { @@ -479,63 +392,73 @@ PUBLIC int AhlBindingInit() 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_POLICY_UTIL_FAIL) - { + json_object *pInPolicyEndpointJ = NULL; + err = EndpointInfoToJSON(pCurEndpointInfo, &pInPolicyEndpointJ); + if (err) { AFB_ERROR("Unable to Create Endpoint Json object error:%s ",wrap_json_get_error_string(err)); - return err; + return AHL_FAIL; } else { - err = Policy_Endpoint_Init(pPolicyEndpointJ); + json_object * pOutPolicyEndpointJ = NULL; + err = Policy_Endpoint_Init(pInPolicyEndpointJ,&pOutPolicyEndpointJ); 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); - } + AFB_WARNING("Policy endpoint properties initalization failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); + // continue + } + json_object_put(pInPolicyEndpointJ); + err = UpdateEndpointInfo(pCurEndpointInfo,pOutPolicyEndpointJ); + if (err) { + AFB_ERROR("Policy endpoint properties update failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); + return AHL_FAIL; + } + // json_object_put(pOutPolicyEndpointJ); + } } } + 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_POLICY_UTIL_FAIL) - { + json_object *pInPolicyEndpointJ = NULL; + err = EndpointInfoToJSON(pCurEndpointInfo, &pInPolicyEndpointJ); + if (err) { AFB_ERROR("Unable to Create Endpoint Json object error:%s ",wrap_json_get_error_string(err)); - return err; + return AHL_FAIL; } 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); + json_object *pOutPolicyEndpointJ = NULL; + err = Policy_Endpoint_Init(pInPolicyEndpointJ,&pOutPolicyEndpointJ); + if (err == AHL_POLICY_REJECT) { + AFB_WARNING("Policy endpoint properties initalization failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); + // continue } - //free pPolicyEndpointJ - json_object_put(pPolicyEndpointJ); - } - + json_object_put(pInPolicyEndpointJ); + err = UpdateEndpointInfo(pCurEndpointInfo,pOutPolicyEndpointJ); + if (err) { + AFB_ERROR("Policy endpoint properties update failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); + return AHL_FAIL; + } + //json_object_put(pOutPolicyEndpointJ); + } } } } -#endif +#endif // AHL_DISCONNECT_POLICY // Initialize list of active streams 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; + return AHL_FAIL; } - // TODO: Use AGL persistence framework to retrieve and set initial volumes/properties - AFB_DEBUG("Audio high-level Binding success"); - return err; + return AHL_SUCCESS; } PUBLIC void AhlOnEvent(const char *evtname, json_object *eventJ) @@ -550,21 +473,22 @@ PUBLIC void AhlOnEvent(const char *evtname, json_object *eventJ) #endif } -PUBLIC void audiohlapi_get_sources(struct afb_req req) +PUBLIC void audiohlapi_get_endpoints(struct afb_req req) { json_object *devicesJ = NULL; json_object *deviceJ = NULL; json_object *queryJ = NULL; char * audioRole = NULL; + char * pEndpointTypeStr = NULL; + EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s}", "audio_role", &audioRole); + int err = wrap_json_unpack(queryJ, "{s:s,s:s}", "audio_role", &audioRole,"endpoint_type",&pEndpointTypeStr); 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("Filtering devices according to specified audio role=%s", audioRole); + endpointType = EndpointTypeToEnum(pEndpointTypeStr); RoleInfoT * pRole = GetRole(audioRole); if ( pRole == NULL ) @@ -575,61 +499,25 @@ PUBLIC void audiohlapi_get_sources(struct afb_req req) else { devicesJ = json_object_new_array(); - GPtrArray * pDeviceArray = pRole->pSourceEndpoints; + GPtrArray * pDeviceArray = NULL; + if (endpointType == ENDPOINTTYPE_SOURCE) + pDeviceArray = pRole->pSourceEndpoints; + else + pDeviceArray = pRole->pSinkEndpoints; 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); + JSONPublicPackageEndpoint(pEndpointInfo,&deviceJ); json_object_array_add(devicesJ, deviceJ); } } } } - afb_req_success(req, devicesJ, "List of sources"); -} - -PUBLIC void audiohlapi_get_sinks(struct afb_req req) -{ - 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) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - - AFB_DEBUG("Filtering devices according to specified audio role=%s", audioRole); - - 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 - { - 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, devicesJ, "List of sinks"); + afb_req_success(req, devicesJ, "List of endpoints"); } PUBLIC void audiohlapi_stream_open(struct afb_req req) @@ -650,7 +538,6 @@ PUBLIC void audiohlapi_stream_open(struct afb_req req) 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:%s endpoint_id:%d", audioRole,endpointTypeStr,endpointID); endpointType = EndpointTypeToEnum(endpointTypeStr); // Check if there is already an existing context for this client @@ -685,7 +572,6 @@ PUBLIC void audiohlapi_stream_open(struct afb_req req) // Assign a device based on configuration priority (first in the list for requested role and endpoint type) pEndpointInfo = g_ptr_array_index(pDeviceArray,0); endpointSelMode = ENDPOINTSELMODE_AUTO; - } else{ endpointSelMode = ENDPOINTSELMODE_MANUAL; @@ -699,7 +585,6 @@ PUBLIC void audiohlapi_stream_open(struct afb_req req) } pEndpointInfo = NULL; } - } if (pEndpointInfo == NULL) { @@ -724,8 +609,8 @@ PUBLIC void audiohlapi_stream_open(struct afb_req req) #ifndef AHL_DISCONNECT_POLICY // Call policy to verify whether creating a new audio stream is allowed in current context and possibly take other actions json_object *pPolicyStreamJ = NULL; - err = PolicyStreamStructToJSON(pStreamInfo, &pPolicyStreamJ); - if (err == AHL_POLICY_UTIL_FAIL) + err = StreamInfoToJSON(pStreamInfo,&pPolicyStreamJ); + if (err) { afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_OpenStream"); return; @@ -762,7 +647,8 @@ PUBLIC void audiohlapi_stream_open(struct afb_req req) if (g_AHLCtx.policyCtx.pStreams) g_hash_table_insert( g_AHLCtx.policyCtx.pStreams, GINT_TO_POINTER(&pStreamInfo->streamID), pStreamInfo ); - StreamInfoStructToJSON(&streamInfoJ,pStreamInfo); + // Package and return stream information to client + JSONPublicPackageStream(pStreamInfo,&streamInfoJ); afb_req_success(req, streamInfoJ, "Stream info structure"); } @@ -773,19 +659,11 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req) streamID_t streamID = AHL_UNDEFINED; queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:i}", "stream_id", &streamID); + 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 = 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 @@ -795,150 +673,108 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req) return; } - // Verify that this client can control the stream - int iStreamAccessControl = CheckStreamAccessControl( pClientCtx, streamID ); - if (iStreamAccessControl == AHL_ACCESS_CONTROL_DENIED) - { - afb_req_fail(req, "Access control denied", "Close stream not allowed in current client context"); - return; - } - -#ifndef AHL_DISCONNECT_POLICY - json_object *pPolicyStreamJ = NULL; - err = PolicyStreamStructToJSON(pStreamInfo, &pPolicyStreamJ); - if (err == AHL_POLICY_UTIL_FAIL) - { - afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_CloseStream"); - return; - } - // 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 - - // 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 (streamID == AHL_UNDEFINED) { + err = CloseAllClientStreams(pClientCtx,&req); if (err) { - afb_req_fail(req, "Stream event subscription failure", "Could not unsubscribe to stream specific state change event"); + afb_req_fail(req, "Error closing streams", "Streams cannot close"); 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 - 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) { - // If no more streams/endpoints owner, clear session - afb_req_context_clear(req); + else { + err = CloseStream(pClientCtx,streamID,&req); + if (err) { + afb_req_fail_f(req, "Error closing stream", "Specified stream cannot close stream_id -> %d",streamID); + return; } } - + afb_req_success(req, NULL, "Stream close completed"); } - PUBLIC void audiohlapi_set_stream_state(struct afb_req req) - { - json_object *queryJ = NULL; - streamID_t streamID = AHL_UNDEFINED; - char * streamStateStr = NULL; - - queryJ = afb_req_json(req); - 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:%s", streamID,streamStateStr); - +static int SetStreamState(AHLClientCtxT * in_pClientCtx,struct afb_req * pReq, streamID_t streamID, char * pStreamStateStr, char * pMuteStr) { + StreamInfoT * pStreamInfo = GetStream(streamID); if (pStreamInfo == NULL) { - afb_req_fail_f(req, "Stream not found", "Specified stream not found 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, "Bad state", "No client context associated with the request (is there an opened stream by this client?)"); - return; + afb_req_fail_f(*pReq, "Stream not found", "Specified stream not found stream_id -> %d",streamID); + return AHL_FAIL; } // Verify that this client can control the stream - int iStreamAccessControl = CheckStreamAccessControl( pClientCtx, streamID ); + int iStreamAccessControl = CheckStreamAccessControl( in_pClientCtx, streamID ); if (iStreamAccessControl == AHL_ACCESS_CONTROL_DENIED) { - afb_req_fail(req, "Access control denied", "Set stream state not allowed in current client context"); - return; + afb_req_fail(*pReq, "Access control denied", "Set stream state not allowed in current client context"); + return AHL_FAIL; } - StreamStateT streamState = StreamStateToEnum(streamStateStr); + if (pStreamStateStr != NULL) { + StreamStateT streamState = StreamStateToEnum(pStreamStateStr); #ifndef AHL_DISCONNECT_POLICY - json_object *pPolicyStreamJ = NULL; - err = PolicyStreamStructToJSON(pStreamInfo, &pPolicyStreamJ); - if (err == AHL_POLICY_UTIL_FAIL) - { - afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_SetStreamState"); - return; - } + json_object *pPolicyStreamJ = NULL; + int err = StreamInfoToJSON(pStreamInfo, &pPolicyStreamJ); + if (err == AHL_POLICY_UTIL_FAIL) + { + afb_req_fail(*pReq, "Audio policy violation", "Unable to get JSON object for Policy_SetStreamState"); + return AHL_FAIL; + } + + 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(*pReq, "Audio policy violation", "Change stream state not allowed in current context"); + return AHL_FAIL; + } +#else + // Simulate that policy returns target state (accepted) + pStreamInfo->streamState = streamState; +#endif + } - json_object *paramJ= json_object_new_int(streamState); - json_object_object_add(pPolicyStreamJ, "arg_stream_state", paramJ); + if (pMuteStr != NULL) { + StreamMuteT muteState = StreamMuteToEnum(pMuteStr); +#ifndef AHL_DISCONNECT_POLICY + json_object *pPolicyStreamJ = NULL; + int err = StreamInfoToJSON(pStreamInfo, &pPolicyStreamJ); + if (err == AHL_POLICY_UTIL_FAIL) + { + afb_req_fail((*pReq), "Audio policy violation", "Unable to get JSON object for Policy_SetStreamMute"); + return AHL_FAIL; + } - 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; - } + 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(*pReq, "Audio policy violation", "Mute stream not allowed in current context"); + return AHL_FAIL; + } #else - // Simulate that policy returns target state (accepted) - pStreamInfo->streamState = streamState; + // Simulate that policy returns target state (accepted) + pStreamInfo->streamMute = muteState; #endif + } - afb_req_success(req, NULL, "Set stream state"); - } + return AHL_SUCCESS; +} - PUBLIC void audiohlapi_set_stream_mute(struct afb_req req) + PUBLIC void audiohlapi_set_stream_state(struct afb_req req) { json_object *queryJ = NULL; streamID_t streamID = AHL_UNDEFINED; + char * streamStateStr = NULL; char * pMuteStr = NULL; queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:i,s:s}", "stream_id", &streamID,"mute",&pMuteStr); + int err = wrap_json_unpack(queryJ, "{s?i,s?s,s?s}", "stream_id", &streamID,"state",&streamStateStr,"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:%s", streamID,pMuteStr); - - 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 @@ -948,40 +784,27 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req) return; } - // Verify that this client can control the stream - int iStreamAccessControl = CheckStreamAccessControl( pClientCtx, streamID ); - if (iStreamAccessControl == AHL_ACCESS_CONTROL_DENIED) - { - afb_req_fail(req, "Access control denied", "Set stream mute state not allowed in current client context"); - return; + if (streamID == AHL_UNDEFINED) { + // All stream for this client + if (pClientCtx->pStreamAccessList != NULL) { + for (int i = 0; i < pClientCtx->pStreamAccessList->len; i++) + { + streamID_t curStreamID = g_array_index(pClientCtx->pStreamAccessList,streamID_t,i); + err = SetStreamState(pClientCtx,&req,curStreamID,streamStateStr,pMuteStr); + if (err) { + return; + } + } + } } - - - StreamMuteT muteState = StreamMuteToEnum(pMuteStr); -#ifndef AHL_DISCONNECT_POLICY - json_object *pPolicyStreamJ = NULL; - err = PolicyStreamStructToJSON(pStreamInfo, &pPolicyStreamJ); - if (err == AHL_POLICY_UTIL_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 { + err = SetStreamState(pClientCtx,&req,streamID,streamStateStr,pMuteStr); + if (err) { + return; + } } -#else - // Simulate that policy returns target state (accepted) - pStreamInfo->streamMute = muteState; -#endif - afb_req_success(req, NULL, "Set stream mute completed"); + afb_req_success(req, NULL, "Set stream state"); } PUBLIC void audiohlapi_get_stream_info(struct afb_req req) @@ -996,7 +819,6 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req) 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 = GetStream(streamID); if (pStreamInfo == NULL) { @@ -1004,12 +826,12 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req) return; } - StreamInfoStructToJSON(&streamInfoJ,pStreamInfo); + JSONPublicPackageStream(pStreamInfo,&streamInfoJ); afb_req_success(req, streamInfoJ, "Get stream info completed"); } -PUBLIC void audiohlapi_set_volume(struct afb_req req) +PUBLIC void audiohlapi_volume(struct afb_req req) { json_object *queryJ = NULL; endpointID_t endpointID = AHL_UNDEFINED; @@ -1018,12 +840,11 @@ PUBLIC void audiohlapi_set_volume(struct afb_req req) char * volumeStr = NULL; queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s,s:i,s:s}", "endpoint_type", &pEndpointTypeStr,"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:%s endpoint_id:%d volume:%s", pEndpointTypeStr,endpointID,volumeStr); endpointType = EndpointTypeToEnum(pEndpointTypeStr); EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); @@ -1033,62 +854,36 @@ PUBLIC void audiohlapi_set_volume(struct afb_req req) return; } + if (volumeStr != NULL) { #ifndef AHL_DISCONNECT_POLICY - json_object *pPolicyEndpointJ = NULL; - err = PolicyEndpointStructToJSON(pEndpointInfo, &pPolicyEndpointJ); - if (err == AHL_POLICY_UTIL_FAIL) - { - afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_SetVolume"); - return; - } + json_object *pPolicyEndpointJ = NULL; + err = EndpointInfoToJSON(pEndpointInfo, &pPolicyEndpointJ); + if (err == AHL_POLICY_UTIL_FAIL) + { + afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_SetVolume"); + return; + } - json_object *paramJ= json_object_new_string(volumeStr); - json_object_object_add(pPolicyEndpointJ, "arg_volume", paramJ); + json_object *paramJ= json_object_new_string(volumeStr); + json_object_object_add(pPolicyEndpointJ, "arg_volume", paramJ); - int policyAllowed = Policy_SetVolume(pPolicyEndpointJ); - if (!policyAllowed) - { - afb_req_fail(req, "Audio policy violation", "Set volume not allowed in current context"); - return; - } + 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); + // Simulate that policy returns target state (accepted) + pEndpointInfo->iVolume = atoi(volumeStr); #endif - - afb_req_success(req, NULL, "Set volume completed"); -} - -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: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:%s endpoint_id:%d", pEndpointTypeStr,endpointID); - endpointType = EndpointTypeToEnum(pEndpointTypeStr); - 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; - } - - volumeJ = json_object_new_int(pEndpointInfo->iVolume); - - afb_req_success(req, volumeJ, "Retrieved volume value"); + json_object * volumeJ = json_object_new_int(pEndpointInfo->iVolume); + + afb_req_success(req, volumeJ, "Set/get volume completed"); } -// Properties PUBLIC void audiohlapi_get_endpoint_info(struct afb_req req) { json_object *queryJ = NULL; @@ -1102,7 +897,6 @@ PUBLIC void audiohlapi_get_endpoint_info(struct afb_req req) 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:%s endpoint_id:%d", pEndpointTypeStr,endpointID); endpointType = EndpointTypeToEnum(pEndpointTypeStr); EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); @@ -1113,12 +907,12 @@ PUBLIC void audiohlapi_get_endpoint_info(struct afb_req req) } json_object *endpointInfoJ = NULL; - EndpointInfoStructToJSON(&endpointInfoJ,pEndpointInfo); + EndpointInfoToJSON(pEndpointInfo,&endpointInfoJ); afb_req_success(req, endpointInfoJ, "Retrieved endpoint information and properties"); } -PUBLIC void audiohlapi_set_property(struct afb_req req) +PUBLIC void audiohlapi_property(struct afb_req req) { json_object *queryJ = NULL; endpointID_t endpointID = AHL_UNDEFINED; @@ -1128,12 +922,11 @@ PUBLIC void audiohlapi_set_property(struct afb_req req) json_object * propValueJ = NULL; queryJ = afb_req_json(req); - 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); + 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:%s endpoint_id:%d property_name:%s", pEndpointTypeStr,endpointID,propertyName); endpointType = EndpointTypeToEnum(pEndpointTypeStr); EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); @@ -1143,59 +936,32 @@ PUBLIC void audiohlapi_set_property(struct afb_req req) return; } -#ifndef AHL_DISCONNECT_POLICY - json_object *pPolicyEndpointJ = NULL; - err = PolicyEndpointStructToJSON(pEndpointInfo, &pPolicyEndpointJ); - if (err == AHL_POLICY_UTIL_FAIL) - { - afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_SetVolume"); - 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 - 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); + if (propValueJ != NULL) { + #ifndef AHL_DISCONNECT_POLICY + json_object *pPolicyEndpointJ = NULL; + err = EndpointInfoToJSON(pEndpointInfo, &pPolicyEndpointJ); + if (err == AHL_POLICY_UTIL_FAIL) + { + afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_SetVolume"); + return; + } - afb_req_success(req, NULL, "Set property completed"); -} + 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); -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: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:%s endpoint_id:%d property_name:%s", pEndpointTypeStr,endpointID,propertyName); - endpointType = EndpointTypeToEnum(pEndpointTypeStr); - - 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; + // Call policy to allow custom policy actions in current context + 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 } // Retrieve cached property value @@ -1206,14 +972,10 @@ 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"); + afb_req_success(req, propertyValJ, "Set/get property completed"); } -// Audio related events - PUBLIC void audiohlapi_get_list_actions(struct afb_req req) { json_object *queryJ = NULL; @@ -1226,7 +988,6 @@ PUBLIC void audiohlapi_get_list_actions(struct afb_req req) 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",audioRole); // Build and return list of actions for specific audio role RoleInfoT * pRole = GetRole(audioRole); @@ -1264,7 +1025,6 @@ PUBLIC void audiohlapi_post_action(struct afb_req req) 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 = action_name:%s audio_role:%s", actionName,audioRole); // Verify if known action for audio role RoleInfoT * pRole = GetRole(audioRole); @@ -1296,7 +1056,14 @@ PUBLIC void audiohlapi_post_action(struct afb_req req) #ifndef AHL_DISCONNECT_POLICY // Call policy to allow custom policy actions in current context (e.g. cancel playback) - int policyAllowed = Policy_PostAction(queryJ); + json_object * pActionInfo = NULL; + err = wrap_json_pack(&pActionInfo, "{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", "Could not create action JSON object arguments"); + return; + } + json_object_get(pActionInfo); + int policyAllowed = Policy_PostAction(pActionInfo); if (!policyAllowed) { afb_req_fail(req, "Audio policy violation", "Post sound action not allowed in current context"); @@ -1304,63 +1071,17 @@ PUBLIC void audiohlapi_post_action(struct afb_req req) } #endif - //afb_event_push(g_AHLCtx.policyCtx.postEvent,queryJ); - afb_req_success(req, NULL, "Posted sound action"); } - -// Monitoring -PUBLIC void audiohlapi_subscribe(struct afb_req req) -{ - json_object *queryJ = NULL; - json_object * eventArrayJ = NULL; - - 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(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"); - } - 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_ACTION_EVENT)) { - afb_req_subscribe(req, g_AHLCtx.policyCtx.postActionEvent); - 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) +PUBLIC void audiohlapi_event_subscription(struct afb_req req) { json_object *queryJ = NULL; json_object * eventArrayJ = NULL; + int iSubscribe = 1; queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:o}", "events", &eventArrayJ); + int err = wrap_json_unpack(queryJ, "{s:o,s:i}", "events", &eventArrayJ,"subscribe",&iSubscribe); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; @@ -1377,16 +1098,22 @@ PUBLIC void audiohlapi_unsubscribe(struct afb_req req) 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"); + if (iSubscribe) + afb_req_subscribe(req, g_AHLCtx.policyCtx.propertyEvent); + else + afb_req_unsubscribe(req, g_AHLCtx.policyCtx.propertyEvent); } else if(!strcasecmp(pEventName, AHL_ENDPOINT_VOLUME_EVENT)) { - afb_req_unsubscribe(req, g_AHLCtx.policyCtx.volumeEvent); - AFB_DEBUG("Client unsubscribed to endpoint volume events"); + if (iSubscribe) + afb_req_subscribe(req, g_AHLCtx.policyCtx.volumeEvent); + else + afb_req_unsubscribe(req, g_AHLCtx.policyCtx.volumeEvent); } 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"); + if (iSubscribe) + afb_req_subscribe(req, g_AHLCtx.policyCtx.postActionEvent); + else + afb_req_unsubscribe(req, g_AHLCtx.policyCtx.postActionEvent); } else { afb_req_fail(req, "failed", "Invalid event"); @@ -1394,7 +1121,7 @@ PUBLIC void audiohlapi_unsubscribe(struct afb_req req) } } - afb_req_success(req, NULL, "Unsubscribe to events finished"); + afb_req_success(req, NULL, "Event subscription update finished"); } // Since the policy is currently in the same binding, it cannot raise events on its own @@ -1411,42 +1138,7 @@ PUBLIC void audiohlapi_raise_event(json_object * pEventDataJ) 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) { + if(strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)==0) { char * pAudioRole = NULL; char * pPropertyName = NULL; endpointID_t endpointID = AHL_UNDEFINED; @@ -1470,18 +1162,18 @@ PUBLIC void audiohlapi_raise_event(json_object * pEventDataJ) } EndpointInfoT * pEndpointInfo = GetEndpointInfoWithRole(endpointID,endpointType,pRole); // update property value - if (pEndpointInfo->pPropTable) + if ((pEndpointInfo!=NULL) && (pEndpointInfo->pPropTable!=NULL)) { 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)); + g_hash_table_insert(pEndpointInfo->pPropTable, pPropertyName, json_object_new_double(json_object_get_double(propValueJ))); break; case json_type_int: - Add_Endpoint_Property_Int(pEndpointInfo,pPropertyName,json_object_get_int(propValueJ)); + g_hash_table_insert(pEndpointInfo->pPropTable, pPropertyName, json_object_new_int(json_object_get_int(propValueJ))); break; case json_type_string: - Add_Endpoint_Property_String(pEndpointInfo,pPropertyName,json_object_get_string(propValueJ)); + g_hash_table_insert(pEndpointInfo->pPropTable, pPropertyName, json_object_new_string(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)); @@ -1512,9 +1204,16 @@ PUBLIC void audiohlapi_raise_event(json_object * pEventDataJ) AFB_ERROR("Requested audio role does not exist in current configuration -> %s", pAudioRole); return; } - EndpointInfoT * pEndpointInfo = GetEndpointInfoWithRole(endpointID,endpointType,pRole); + EndpointInfoT * pEndpointInfo = GetEndpointInfoWithRole(endpointID,endpointType,pRole); // update volume value - pEndpointInfo->iVolume = iVolume; + if(pEndpointInfo) + { + pEndpointInfo->iVolume = iVolume; + } + else + { + AFB_ERROR("Unable to find endpoint"); + } // Remove event name from object json_object_object_del(pEventDataJ,"event_name"); afb_event_push(g_AHLCtx.policyCtx.volumeEvent,pEventDataJ); @@ -1522,6 +1221,7 @@ PUBLIC void audiohlapi_raise_event(json_object * pEventDataJ) else if(strcasecmp(pEventName, AHL_POST_ACTION_EVENT)==0) { // Remove event name from object json_object_object_del(pEventDataJ,"event_name"); + // BUG: This crashes... afb_event_push(g_AHLCtx.policyCtx.postActionEvent,pEventDataJ); } else if(strcasecmp(pEventName, AHL_STREAM_STATE_EVENT)==0) { @@ -1568,7 +1268,6 @@ PUBLIC void audiohlapi_raise_event(json_object * pEventDataJ) // 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 { diff --git a/ahl-binding/ahl-binding.h b/ahl-binding/ahl-binding.h index 9b4c37e..405144d 100644 --- a/ahl-binding/ahl-binding.h +++ b/ahl-binding/ahl-binding.h @@ -17,18 +17,15 @@ #ifndef AHL_BINDING_INCLUDE #define AHL_BINDING_INCLUDE -#define AFB_BINDING_VERSION 2 - //#define AHL_DISCONNECT_POLICY // define for debugging HLB in standalone only #include <json-c/json.h> #include <glib.h> - +#define AFB_BINDING_VERSION 2 #include <afb/afb-binding.h> #include "ahl-interface.h" #include "ahl-policy-utils.h" - #ifndef PUBLIC #define PUBLIC #endif @@ -39,9 +36,36 @@ #define AHL_ACCESS_CONTROL_GRANTED 1 #define AHL_ACCESS_CONTROL_DENIED 0 -#define AHL_UNDEFINED -1 #define AHL_STR_MAX_LENGTH 256 - + +typedef struct EndpointInfo +{ + endpointID_t endpointID; // Unique endpoint ID (per type) + EndpointTypeT type; // Source or sink device + char * gsDeviceName; // Unique device card name + char * gsDisplayName; // Application display name + char * gsDeviceURI; // Associated URI + char * gsDeviceDomain; // Device URI domain (e.g. alsa or pulse) + char * pRoleName; // Role assigned to this endpoint + DeviceURITypeT deviceURIType; // Device URI type (includes audio domain information) + char * gsHALAPIName; // HAL associated with the device (for volume control) + AlsaDeviceInfoT alsaInfo; // ALSA specific device information + AudioFormatT format; // Preferred audio format supported (later could be array of supported formats) + int iVolume; // Storage for current endpoint volume (policy effected). + GHashTable * pPropTable; // Storage for array of properties (policy effected) +} EndpointInfoT; + +typedef struct StreamInfo { + streamID_t streamID; // Stream unique ID + EndpointInfoT * pEndpointInfo; // Associated endpoint information (reference) + StreamStateT streamState; // Stream activity state + StreamMuteT streamMute; // Stream mute state + struct afb_event streamStateEvent; // Stream specific event for stream state changes + EndpointSelectionModeT endpointSelMode; // Automatic (priority based) or manual endpoint selection + char * pRoleName; // Role string identifier (from role config but could be programatically overriden later) + int iPriority; // Role normalized priority (0-100) (from role config but could be programatically overriden later) + InterruptBehaviorT eInterruptBehavior; // Role behavior when interrupting lower priority streams (from role config but could be programatically overriden later) +} StreamInfoT; typedef struct RoleInfo { char * pRoleName; // Role string identifier diff --git a/ahl-binding/ahl-config.c b/ahl-binding/ahl-config.c index 2a61b5e..c60cb9c 100644 --- a/ahl-binding/ahl-config.c +++ b/ahl-binding/ahl-config.c @@ -14,7 +14,6 @@ * limitations under the License. */ -#define _GNU_SOURCE #include <stdio.h> #include <string.h> #include <json-c/json.h> @@ -85,7 +84,7 @@ static char* CtlConfigSearch(const char *dirList, const char *prefix) { int ParseHLBConfig() { char * versionStr = NULL; - json_object * jAudioRoles = NULL; + json_object * jAudioRoles = NUL L; json_object * jHALList = NULL; char * policyModule = NULL; @@ -105,13 +104,13 @@ int ParseHLBConfig() { if(config_JFile == NULL) { AFB_ERROR("Error: Can't open configuration file -> %s",configfile_path); - return 1; + return AHL_FAIL; } int err = wrap_json_unpack(config_JFile, "{s:s,s:s,s:o,s:o}", "version", &versionStr,"policy_module", &policyModule,"audio_roles",&jAudioRoles,"hal_list",&jHALList); if (err) { AFB_ERROR("Invalid configuration file -> %s", configfile_path); - return 1; + return AHL_FAIL; } AFB_INFO("High-level audio API version: %s", "1.0.0"); AFB_INFO("Config version: %s", versionStr); @@ -136,7 +135,7 @@ int ParseHLBConfig() { if( err != 0 ) { AFB_ERROR("Audio high level API could not set dependency on API: %s",pHAL); - return 1; + return AHL_FAIL; } } } @@ -165,7 +164,7 @@ int ParseHLBConfig() { ); if (err) { AFB_ERROR("Invalid audio role configuration : %s", json_object_to_json_string(jAudioRole)); - return 1; + return AHL_FAIL; } if (jOutputDevices) @@ -198,7 +197,7 @@ int ParseHLBConfig() { err = EnumerateDevices(jInputDevices,pRoleName,ENDPOINTTYPE_SOURCE,pRoleInfo->pSourceEndpoints); if (err) { AFB_ERROR("Invalid input devices : %s", json_object_to_json_string(jInputDevices)); - return 1; + return AHL_FAIL; } } // Sinks @@ -207,15 +206,14 @@ int ParseHLBConfig() { err = EnumerateDevices(jOutputDevices,pRoleName,ENDPOINTTYPE_SINK,pRoleInfo->pSinkEndpoints); if (err) { AFB_ERROR("Invalid output devices : %s", json_object_to_json_string(jOutputDevices)); - return 1; + return AHL_FAIL; } } g_hash_table_insert(g_AHLCtx.policyCtx.pRoleInfo, pRoleInfo->pRoleName, pRoleInfo); - } // Build lists of all device URI referenced in config file (input/output) AFB_DEBUG ("Audio high-level - Parse high-level audio configuration done"); - return 0; + return AHL_SUCCESS; } diff --git a/ahl-binding/ahl-deviceenum.c b/ahl-binding/ahl-deviceenum.c index 7e67d7a..763b2ca 100644 --- a/ahl-binding/ahl-deviceenum.c +++ b/ahl-binding/ahl-deviceenum.c @@ -14,16 +14,9 @@ * limitations under the License. */ -#define _GNU_SOURCE -#include <stdio.h> -#include <string.h> #include <alsa/asoundlib.h> #include <alsa/pcm.h> -#include <json-c/json.h> -#include "wrap-json.h" - #include "ahl-binding.h" -#include "ahl-policy.h" extern AHLCtxT g_AHLCtx; @@ -59,7 +52,7 @@ static int SeparateDomainFromDeviceURI( char * in_pDeviceURI, char ** out_pDomai AFB_ERROR("Error tokenizing device URI -> %s",in_pDeviceURI); return 1; } - return 0; + return AHL_SUCCESS; } static int IsAlsaDomain(const char * in_pDomainStr) @@ -90,7 +83,6 @@ static int FillALSAPCMInfo( snd_pcm_t * in_pPcmHandle, EndpointInfoT * out_pEndp snd_pcm_info_t * pPcmInfo = NULL; int iAlsaRet = 0; const char * pCardName = NULL; - int retVal = 0; snd_ctl_t * ctlHandle = NULL; snd_ctl_card_info_t * ctlInfo = NULL; @@ -121,7 +113,7 @@ static int FillALSAPCMInfo( snd_pcm_t * in_pPcmHandle, EndpointInfoT * out_pEndp if (iAlsaRet < 0) { AFB_WARNING("Error retrieving PCM device info"); - return 1; + return AHL_FAIL; } // get card number @@ -129,7 +121,7 @@ static int FillALSAPCMInfo( snd_pcm_t * in_pPcmHandle, EndpointInfoT * out_pEndp if ( out_pEndpointInfo->alsaInfo.cardNum < 0 ) { AFB_WARNING("No Alsa card number available"); - return 1; + return AHL_FAIL; } // get device number @@ -137,7 +129,7 @@ static int FillALSAPCMInfo( snd_pcm_t * in_pPcmHandle, EndpointInfoT * out_pEndp if ( out_pEndpointInfo->alsaInfo.deviceNum < 0 ) { AFB_WARNING("No Alsa device number available"); - return 1; + return AHL_FAIL; } // get sub-device number @@ -145,7 +137,7 @@ static int FillALSAPCMInfo( snd_pcm_t * in_pPcmHandle, EndpointInfoT * out_pEndp if ( out_pEndpointInfo->alsaInfo.subDeviceNum < 0 ) { AFB_WARNING("No Alsa subdevice number available"); - return 1; + return AHL_FAIL; } char cardName[32]; @@ -154,7 +146,7 @@ static int FillALSAPCMInfo( snd_pcm_t * in_pPcmHandle, EndpointInfoT * out_pEndp if ( iAlsaRet < 0 ) { AFB_WARNING("Could not open ALSA card control"); - return 1; + return AHL_FAIL; } iAlsaRet = snd_ctl_card_info(ctlHandle, ctlInfo); @@ -162,7 +154,7 @@ static int FillALSAPCMInfo( snd_pcm_t * in_pPcmHandle, EndpointInfoT * out_pEndp { AFB_WARNING("Could not retrieve ALSA card info"); snd_ctl_close(ctlHandle); - return 1; + return AHL_FAIL; } // Populate unique target card name @@ -171,13 +163,13 @@ static int FillALSAPCMInfo( snd_pcm_t * in_pPcmHandle, EndpointInfoT * out_pEndp { AFB_WARNING("No Alsa card name available"); snd_ctl_close(ctlHandle); - return 1; + return AHL_FAIL; } - out_pEndpointInfo->gsDeviceName = g_strdup(pCardName); + g_strlcpy(out_pEndpointInfo->gsDeviceName,pCardName,AHL_STR_MAX_LENGTH); snd_ctl_close(ctlHandle); - return retVal; + return AHL_SUCCESS; } EndpointInfoT * InitEndpointInfo() @@ -193,6 +185,20 @@ EndpointInfoT * InitEndpointInfo() pEndpointInfo->format.sampleRate = AHL_UNDEFINED; pEndpointInfo->format.numChannels = AHL_UNDEFINED; pEndpointInfo->format.sampleType = AHL_FORMAT_UNKNOWN; + // Assigned by device enumeration + pEndpointInfo->gsDeviceName = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); + memset(pEndpointInfo->gsDeviceName,0,AHL_STR_MAX_LENGTH*sizeof(char)); + pEndpointInfo->gsDeviceDomain = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); + memset(pEndpointInfo->gsDeviceDomain,0,AHL_STR_MAX_LENGTH*sizeof(char)); + pEndpointInfo->pRoleName = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); + memset(pEndpointInfo->pRoleName,0,AHL_STR_MAX_LENGTH*sizeof(char)); + pEndpointInfo->gsDeviceURI = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); + memset(pEndpointInfo->gsDeviceURI,0,AHL_STR_MAX_LENGTH*sizeof(char)); + // Assigned by policy initialization + pEndpointInfo->gsDisplayName = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); + memset(pEndpointInfo->gsDisplayName,0,AHL_STR_MAX_LENGTH*sizeof(char)); + pEndpointInfo->gsHALAPIName = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); + memset(pEndpointInfo->gsHALAPIName,0,AHL_STR_MAX_LENGTH*sizeof(char)); pEndpointInfo->pPropTable = g_hash_table_new(g_str_hash, g_str_equal); return pEndpointInfo; } @@ -200,12 +206,12 @@ EndpointInfoT * InitEndpointInfo() void TermEndpointInfo( EndpointInfoT * out_pEndpointInfo ) { #define SAFE_FREE(__ptr__) if(__ptr__) g_free(__ptr__); __ptr__ = NULL; - SAFE_FREE(out_pEndpointInfo->gsDeviceName); - SAFE_FREE(out_pEndpointInfo->gsDisplayName); + SAFE_FREE(out_pEndpointInfo->gsDeviceName); SAFE_FREE(out_pEndpointInfo->gsDeviceDomain); SAFE_FREE(out_pEndpointInfo->pRoleName); SAFE_FREE(out_pEndpointInfo->gsDeviceURI); SAFE_FREE(out_pEndpointInfo->gsHALAPIName); + SAFE_FREE(out_pEndpointInfo->gsDisplayName); if (out_pEndpointInfo->pPropTable) { // Free json_object for all property values @@ -236,7 +242,7 @@ int EnumerateDevices(json_object * in_jDeviceArray, char * in_pAudioRole, Endpoi char * pDeviceURIDomain = NULL; char * pFullDeviceURI = NULL; char * pDeviceURIPCM = NULL; - int err = 0; + int err = AHL_SUCCESS; json_object * jDevice = json_object_array_get_idx(in_jDeviceArray,i); if (jDevice == NULL) { @@ -258,10 +264,10 @@ int EnumerateDevices(json_object * in_jDeviceArray, char * in_pAudioRole, Endpoi // non ALSA URI are simply passed to application (no validation) at this time // In Non ALSA case devices in config are assumed to be available, if not application can fallback on explicit device selection - pEndpointInfo->gsDeviceName = g_strdup(pDeviceURIPCM); - pEndpointInfo->gsDeviceDomain = g_strdup(pDeviceURIDomain); - pEndpointInfo->gsDeviceURI = g_strdup(pDeviceURIPCM); - pEndpointInfo->pRoleName = g_strdup(in_pAudioRole); + g_strlcpy(pEndpointInfo->gsDeviceName,pDeviceURIPCM,AHL_STR_MAX_LENGTH); + g_strlcpy(pEndpointInfo->gsDeviceDomain,pDeviceURIDomain,AHL_STR_MAX_LENGTH); + g_strlcpy(pEndpointInfo->gsDeviceURI,pDeviceURIPCM,AHL_STR_MAX_LENGTH); + g_strlcpy(pEndpointInfo->pRoleName ,in_pAudioRole,AHL_STR_MAX_LENGTH); g_free(pFullDeviceURICopy); pFullDeviceURICopy = NULL; @@ -315,11 +321,6 @@ int EnumerateDevices(json_object * in_jDeviceArray, char * in_pAudioRole, Endpoi pEndpointInfo->endpointID = in_deviceType == ENDPOINTTYPE_SOURCE ? CreateNewSourceID() : CreateNewSinkID(); pEndpointInfo->type = in_deviceType; - // Assigned by policy initialization - pEndpointInfo->gsDisplayName = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); - memset(pEndpointInfo->gsDisplayName,0,AHL_STR_MAX_LENGTH*sizeof(char)); - pEndpointInfo->gsHALAPIName = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); - memset(pEndpointInfo->gsDisplayName,0,AHL_STR_MAX_LENGTH*sizeof(char)); // add to structure to list of available devices g_ptr_array_add(out_pEndpointArray, pEndpointInfo); @@ -327,5 +328,5 @@ int EnumerateDevices(json_object * in_jDeviceArray, char * in_pAudioRole, Endpoi } // for all devices AFB_DEBUG ("Audio high-level - Enumerate devices done"); - return 0; + return AHL_SUCCESS; }
\ No newline at end of file diff --git a/ahl-binding/ahl-json.c b/ahl-binding/ahl-json.c new file mode 100644 index 0000000..5e8bf97 --- /dev/null +++ b/ahl-binding/ahl-json.c @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2017 "Audiokinetic Inc" + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define AFB_BINDING_VERSION 2 +#include <afb/afb-binding.h> +#include "wrap-json.h" +#include <json-c/json.h> +#include <glib.h> +#include "ahl-binding.h" + +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 int EndpointPropTableToJSON(GHashTable * pPropTable, json_object **ppProptableJ) +{ + if(pPropTable == NULL) + { + AFB_ERROR("Invalid EndpointPropTableToJSON arguments"); + return AHL_FAIL; + } + + // Create json object for PropTable + *ppProptableJ = json_object_new_array(); + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init (&iter, pPropTable); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + if ( key != NULL && value != NULL) { + json_object *pPropertyJ = NULL; + json_object_get(value); + int err = wrap_json_pack(&pPropertyJ, "{s:s,s:o}", + "property_name", (char*)key, + "property_value", value + ); + if(err) + { + AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err)); + return AHL_FAIL; + } + json_object_array_add(*ppProptableJ, pPropertyJ); + } + } + + return AHL_SUCCESS; +} + +int EndpointInfoToJSON(EndpointInfoT * pEndpointInfo, json_object **ppEndpointInfoJ) +{ + if(pEndpointInfo == NULL || pEndpointInfo->pPropTable == NULL) + { + AFB_ERROR("Invalid EndpointInfoToJSON arguments"); + return AHL_FAIL; + } + + json_object * pPropTableJ = NULL; + int err = EndpointPropTableToJSON(pEndpointInfo->pPropTable,&pPropTableJ); + if (err) { + return AHL_FAIL; + } + + // Create json object for EndpointInfo + err = wrap_json_pack(ppEndpointInfoJ, "{s:i,s:i,s:s,s:s,s:s,s:s,s:s,s:i,s:s,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:o}", + "endpoint_id", pEndpointInfo->endpointID, + "endpoint_type", pEndpointInfo->type, + "device_name", pEndpointInfo->gsDeviceName, + "display_name", pEndpointInfo->gsDisplayName, + "device_uri", pEndpointInfo->gsDeviceURI, + "device_domain", pEndpointInfo->gsDeviceDomain, + "audio_role",pEndpointInfo->pRoleName, + "device_uri_type", pEndpointInfo->deviceURIType, + "hal_api_name", pEndpointInfo->gsHALAPIName, + "alsa_cardNum", pEndpointInfo->alsaInfo.cardNum, + "alsa_deviceNum", pEndpointInfo->alsaInfo.deviceNum, + "alsa_subDeviceNum", pEndpointInfo->alsaInfo.subDeviceNum, + "format_samplerate", pEndpointInfo->format.sampleRate, + "format_numchannels", pEndpointInfo->format.numChannels, + "format_sampletype",pEndpointInfo->format.sampleType, + "volume", pEndpointInfo->iVolume, + "property_table", pPropTableJ + ); + if (err) { + AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err)); + return AHL_FAIL; + } + + return AHL_SUCCESS; +} + +int StreamInfoToJSON(StreamInfoT * pStreamInfo, json_object **ppStreamInfoJ) +{ + if(pStreamInfo == NULL) + { + AFB_ERROR("Invalid arguments to StreamInfoToJSON"); + return AHL_FAIL; + } + + json_object * pEndpointInfoJ = NULL; + int err = EndpointInfoToJSON(pStreamInfo->pEndpointInfo, &pEndpointInfoJ); + if (err) { + return AHL_FAIL; + } + + // Create json object for stream + err = wrap_json_pack(ppStreamInfoJ, "{s:i,s:i,s:i,s:I,s:i,s:s,s:i,s:i,s:o}", + "stream_id", pStreamInfo->streamID, + "stream_state", pStreamInfo->streamState, + "stream_mute", pStreamInfo->streamMute, + "stream_state_event", &pStreamInfo->streamStateEvent, + "endpoint_sel_mod", pStreamInfo->endpointSelMode, + "role_name", pStreamInfo->pRoleName, + "priority", pStreamInfo->iPriority, + "interrupt_behavior", pStreamInfo->eInterruptBehavior, + "endpoint_info", pEndpointInfoJ + ); + if (err) { + AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err)); + return AHL_FAIL; + } + + return AHL_SUCCESS; +} + +static int UpdatePropertyList(GHashTable * pPropTable, json_object * pPropTableJ) { + if (pPropTable == NULL || pPropTableJ == NULL) { + AFB_ERROR("Invalid arguments to UpdatePropertyList"); + return AHL_FAIL; + } + // Unpack prop table + int nbProperties = json_object_array_length(pPropTableJ); + for(int i = 0; i < nbProperties; i++) + { + json_object * propJ = json_object_array_get_idx(pPropTableJ,i); + if (propJ) { + char * pPropertyName = NULL; + json_object * pPropertyValueJ = NULL; + int err = wrap_json_unpack(propJ, "{s:s,s:o}", + "property_name", &pPropertyName, + "property_value", &pPropertyValueJ); + if (err) { + AFB_ERROR("Unable to unpack JSON property, = %s", wrap_json_get_error_string(err)); + return AHL_FAIL; + } + + // Object type detection for property value (string = state, numeric = property) + json_type jType = json_object_get_type(pPropertyValueJ); + switch (jType) { + case json_type_double: + g_hash_table_insert(pPropTable, pPropertyName, json_object_new_double(json_object_get_double(pPropertyValueJ))); + break; + case json_type_int: + g_hash_table_insert(pPropTable, pPropertyName, json_object_new_int(json_object_get_int(pPropertyValueJ))); + break; + case json_type_string: + g_hash_table_insert(pPropTable, pPropertyName, json_object_new_string(json_object_get_string(pPropertyValueJ))); + break; + default: + AFB_ERROR("Invalid property argument Property value not a valid json object query=%s", json_object_get_string(pPropertyValueJ)); + return AHL_FAIL; + } + } + } + + return AHL_SUCCESS; +} + +int UpdateEndpointInfo(EndpointInfoT * pEndpoint, json_object * pEndpointInfoJ) { + + if(pEndpoint == NULL || pEndpointInfoJ == NULL) + { + AFB_ERROR("Invalid arguments to UpdateEndpointInfo"); + return AHL_FAIL; + } + + // Push information to endpoint info struct + json_object * pPropTableJ = NULL; + char * pDisplayName = NULL; + char * pHALName = NULL; + int err = wrap_json_unpack(pEndpointInfoJ,"{s:i,s:s,s:s,s:o}", + "init_volume",&pEndpoint->iVolume, + "display_name",&pDisplayName, + "hal_name", &pHALName, + "property_table",&pPropTableJ); + if (err) { + AFB_ERROR("Unable to create Endpoint Json object error:%s ",wrap_json_get_error_string(err)); + return AHL_FAIL; + } + g_strlcpy(pEndpoint->gsDisplayName,pDisplayName,AHL_STR_MAX_LENGTH); + g_strlcpy(pEndpoint->gsHALAPIName,pHALName,AHL_STR_MAX_LENGTH); + + if (pEndpoint->pPropTable && pPropTableJ) { + err = UpdatePropertyList(pEndpoint->pPropTable,pPropTableJ); + if (err) { + AFB_ERROR("Unable to update property table Json object error:%s ",wrap_json_get_error_string(err)); + return AHL_FAIL; + } + } + + return AHL_SUCCESS; +} + +static void AudioFormatStructToJSON(json_object **audioFormatJ, AudioFormatT * pAudioFormat) +{ + wrap_json_pack(audioFormatJ, "{s:i,s:i,s:i}", + "sample_rate", pAudioFormat->sampleRate, + "num_channels", pAudioFormat->numChannels, + "sample_type", pAudioFormat->sampleType); +} + +// Package only information that can useful to application clients when selecting endpoint +void JSONPublicPackageEndpoint(EndpointInfoT * pEndpointInfo,json_object **endpointInfoJ) +{ + json_object *formatInfoJ = NULL; + 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 == 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); + + json_object *pPropTableJ = NULL; + EndpointPropTableToJSON(pEndpointInfo->pPropTable,&pPropTableJ); + json_object_object_add(*endpointInfoJ,"properties",pPropTableJ); +} + +// Package only information that can useful to application clients when opening a stream +void JSONPublicPackageStream(StreamInfoT * pStreamInfo,json_object **streamInfoJ) +{ + json_object *endpointInfoJ = NULL; + JSONPublicPackageEndpoint(pStreamInfo->pEndpointInfo,&endpointInfoJ); + wrap_json_pack(streamInfoJ, "{s:i,s:s,s:s,s:s}", + "stream_id", pStreamInfo->streamID, + "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); +}
\ No newline at end of file diff --git a/ahl-binding/ahl-json.h b/ahl-binding/ahl-json.h new file mode 100644 index 0000000..ffd683a --- /dev/null +++ b/ahl-binding/ahl-json.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 "Audiokinetic Inc" + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AHL_POLICY_JSON_INCLUDE +#define AHL_POLICY_JSON_INCLUDE + +int EndpointInfoToJSON(EndpointInfoT * pEndpointInfo, json_object **ppEndpointInfoJ); +int StreamInfoToJSON(StreamInfoT * pStreamInfo, json_object **ppStreamInfoJ); +int UpdateEndpointInfo(EndpointInfoT * pEndpoint,json_object * pEndpointInfoJ); +void JSONPublicPackageEndpoint(EndpointInfoT * pEndpointInfo,json_object **endpointInfoJ); +void JSONPublicPackageStream(StreamInfoT * pStreamInfo,json_object **streamInfoJ); + +#endif // AHL_POLICY_JSON_INCLUDE diff --git a/ahl-policy/ahl-interface.h b/ahl-policy/ahl-interface.h index 0488e96..fc0ac68 100644 --- a/ahl-policy/ahl-interface.h +++ b/ahl-policy/ahl-interface.h @@ -35,11 +35,8 @@ // Property/Volume/Action events #define AHL_ENDPOINT_PROPERTY_EVENT "ahl_endpoint_property_event" #define AHL_ENDPOINT_VOLUME_EVENT "ahl_endpoint_volume_event" -#define AHL_ENDPOINT_INIT_EVENT "ahl_endpoint_init_event" #define AHL_POST_ACTION_EVENT "ahl_post_action" #define AHL_STREAM_STATE_EVENT "ahl_stream_state_event" -#define AHL_ENDPOINT_INIT_EVENT "ahl_endpoint_init_event" - // Stream state event types #define AHL_STREAM_EVENT_START "start" // Stream is inactive diff --git a/ahl-policy/ahl-policy.c b/ahl-policy/ahl-policy.c index 5d2e324..4a34ed8 100644 --- a/ahl-policy/ahl-policy.c +++ b/ahl-policy/ahl-policy.c @@ -14,15 +14,38 @@ * limitations under the License. */ -#define _GNU_SOURCE #include <stdio.h> #include <string.h> #include <stdbool.h> +#define AFB_BINDING_VERSION 2 +#include <afb/afb-binding.h> +#include <glib.h> #include "wrap-json.h" #include "ahl-policy-utils.h" #include "ahl-interface.h" #include "ahl-policy.h" +#ifndef AHL_DISCONNECT_POLICY + +#define AK_POLICY_DEMO //For Audiokinetic demo only + +typedef struct StreamPolicyInfo { + streamID_t streamID; + int RolePriority; + char * pAudioRole; + InterruptBehaviorT interruptBehavior; + int iDuckVolume; //duck Volume +} StreamPolicyInfoT; + +typedef struct EndPointPolicyInfo { + endpointID_t endpointID; + EndpointTypeT type; + DeviceURITypeT deviceType; + char * pDeviceName; + char * pHalApiName; + int iVolume; //Current Volume + GArray * streamInfo; //List of playing or duck stream at a given endpoint +} EndPointPolicyInfoT; typedef enum SystemState { SYSTEM_STARTUP = 0, // Startup @@ -32,7 +55,6 @@ typedef enum SystemState { SYSTEM_MAXVALUE // Enum count, keep at the end } SystemStateT; - typedef struct HalInfo { char *pDevID; char *pAPIName; @@ -53,12 +75,9 @@ typedef struct PolicyLocalCtx { SystemStateT systemState; } PolicyLocalCtxT; -#ifndef AHL_DISCONNECT_POLICY - -// Global Context +// Policy context PolicyLocalCtxT g_PolicyCtx; - // Helper Functions static int getStreamConfig(char *pAudioRole, StreamConfigT *pStreamConfig) { @@ -120,7 +139,7 @@ static int getStreamConfig(char *pAudioRole, StreamConfigT *pStreamConfig) static int PolicySetVolume(int iEndpointID, int iEndpointType, char *pHalApiName, char *AudioRole, DeviceURITypeT deviceType, int iVolume, bool bMute) { - if(pHalApiName == NULL) + if(pHalApiName == NULL || (strcasecmp(pHalApiName, AHL_POLICY_UNDEFINED_HALNAME) == 0)) { AFB_WARNING("SetVolume cannot be accomplished without proper HAL association"); return POLICY_FAIL; @@ -156,15 +175,14 @@ static int PolicySetVolume(int iEndpointID, int iEndpointType, char *pHalApiName } break; default: - //Set volume to zero for display purpose only. - //Not support yet + // Not supported yet AFB_WARNING("Device Type %i is not support and can't set volume on HalName %s",deviceType, pHalApiName); return POLICY_FAIL; break; } // Set endpoint volume using HAL services (leveraging ramps etc.) - json_object *j_response, *j_query = NULL; + json_object *j_response = NULL, *j_query = NULL; // Package query int err = wrap_json_pack(&j_query,"{s:s,s:i}","label",gsHALControlName->str, "val",iVolume); @@ -174,7 +192,7 @@ static int PolicySetVolume(int iEndpointID, int iEndpointType, char *pHalApiName return POLICY_FAIL; } - //TODO implement Volume limitation based on policy + // TODO: implement Volume limitation based on policy // Set the volume using the HAL err = afb_service_call_sync(pHalApiName, "ctlset", j_query, &j_response); @@ -216,7 +234,7 @@ static int PolicyGetVolume(int iEndpointID, int iEndpointType, char *pHalApiName case DEVICEURITYPE_ALSA_PLUG: case DEVICEURITYPE_ALSA_SOFTVOL: gsHALControlName = g_string_new(AudioRole); - g_string_append(gsHALControlName,"_Vol"); // Or _Vol for direct control (no ramping) + g_string_append(gsHALControlName,"_Vol"); // Return target value break; default: // Set volume to zero for display purpose only. @@ -237,8 +255,6 @@ static int PolicyGetVolume(int iEndpointID, int iEndpointType, char *pHalApiName return POLICY_FAIL; } - //TODO implement Volume limitation based on policy - // Get the volume using the HAL err = afb_service_call_sync(pHalApiName, "ctlget", j_query, &j_response); if (err) @@ -262,8 +278,7 @@ static int PolicyGetVolume(int iEndpointID, int iEndpointType, char *pHalApiName if (err) { AFB_ERROR("Volume retrieve failed Could not retrieve volume value -> %s", json_object_get_string(jVal)); return POLICY_FAIL; - } - + } } else { @@ -272,7 +287,6 @@ static int PolicyGetVolume(int iEndpointID, int iEndpointType, char *pHalApiName AFB_ERROR("Volume retrieve failed Could not retrieve volume value -> %s", json_object_get_string(jVal)); return POLICY_FAIL; } - } *pVolume = val1; @@ -293,7 +307,6 @@ static int PolicyGetVolume(int iEndpointID, int iEndpointType, char *pHalApiName static void PolicyPostStateEvent(int iStreamID, StreamEventT eventState) { - json_object * eventDataJ = NULL; int err = wrap_json_pack(&eventDataJ,"{s:s,s:i,s:i}","event_name", AHL_STREAM_STATE_EVENT,"stream_id",iStreamID, "state_event",eventState); if (err) @@ -332,23 +345,22 @@ static EndPointPolicyInfoT *PolicySearchEndPoint(EndpointTypeT type, char *pDevi return NULL; } - -static int PolicyAddEndPoint(StreamInfoT *pStreamInfo) +static int PolicyAddEndPoint(StreamInterfaceInfoT *pStreamInfo) { - EndPointPolicyInfoT *pPolicyEndPoint = PolicySearchEndPoint(pStreamInfo->pEndpointInfo->type, pStreamInfo->pEndpointInfo->gsDeviceName); + EndPointPolicyInfoT *pPolicyEndPoint = PolicySearchEndPoint(pStreamInfo->endpoint.type, pStreamInfo->endpoint.gsDeviceName); if(pPolicyEndPoint == NULL) { //create EndPoint and add playing stream EndPointPolicyInfoT newEndPointPolicyInfo; - newEndPointPolicyInfo.endpointID = pStreamInfo->pEndpointInfo->endpointID; - newEndPointPolicyInfo.type = pStreamInfo->pEndpointInfo->type; - newEndPointPolicyInfo.deviceType = pStreamInfo->pEndpointInfo->deviceURIType; - newEndPointPolicyInfo.pDeviceName = strdup(pStreamInfo->pEndpointInfo->gsDeviceName); - newEndPointPolicyInfo.pHalApiName = strdup(pStreamInfo->pEndpointInfo->gsHALAPIName); - newEndPointPolicyInfo.iVolume = pStreamInfo->pEndpointInfo->iVolume; - newEndPointPolicyInfo.streamInfo = g_array_new(FALSE, TRUE, sizeof(StreamPolicyInfoT));; - - if(pStreamInfo->pEndpointInfo->type == ENDPOINTTYPE_SINK) + newEndPointPolicyInfo.endpointID = pStreamInfo->endpoint.endpointID; + newEndPointPolicyInfo.type = pStreamInfo->endpoint.type; + newEndPointPolicyInfo.deviceType = pStreamInfo->endpoint.deviceURIType; + newEndPointPolicyInfo.pDeviceName = strdup(pStreamInfo->endpoint.gsDeviceName); + newEndPointPolicyInfo.pHalApiName = strdup(pStreamInfo->endpoint.gsHALAPIName); + newEndPointPolicyInfo.iVolume = pStreamInfo->endpoint.iVolume; + newEndPointPolicyInfo.streamInfo = g_array_new(FALSE, TRUE, sizeof(StreamPolicyInfoT)); + + if(pStreamInfo->endpoint.type == ENDPOINTTYPE_SINK) { g_array_append_val(g_PolicyCtx.pSinkEndpoints, newEndPointPolicyInfo); } @@ -361,8 +373,7 @@ static int PolicyAddEndPoint(StreamInfoT *pStreamInfo) return POLICY_SUCCESS; } - -static int PolicyAddStream(EndPointPolicyInfoT *pCurrEndPointPolicy, StreamInfoT *pStreamInfo) +static int PolicyAddStream(EndPointPolicyInfoT *pCurrEndPointPolicy, StreamInterfaceInfoT *pStreamInfo) { StreamPolicyInfoT newStreamPolicyInfo; @@ -375,16 +386,15 @@ static int PolicyAddStream(EndPointPolicyInfoT *pCurrEndPointPolicy, StreamInfoT return POLICY_SUCCESS; } -static int PolicyRunningIdleTransition(EndPointPolicyInfoT *pCurrEndPointPolicy,StreamInfoT * pStreamInfo) +static int PolicyRunningIdleTransition(EndPointPolicyInfoT *pCurrEndPointPolicy,StreamInterfaceInfoT * pStreamInfo) { int err=0; if(pCurrEndPointPolicy == NULL || pCurrEndPointPolicy->streamInfo->len == 0) { - //Remove endpoint - AFB_ERROR("StreamID not found in active Endpoint when Running to Idle transition is request"); + AFB_ERROR("StreamID not found in active endpoint when running to idle transition is requested"); return POLICY_FAIL; } - //Search for the matching stream + // Search for the matching stream for(int i=0; i<pCurrEndPointPolicy->streamInfo->len; i++) { StreamPolicyInfoT currentPolicyStreamInfo = g_array_index(pCurrEndPointPolicy->streamInfo,StreamPolicyInfoT,i); @@ -400,7 +410,7 @@ static int PolicyRunningIdleTransition(EndPointPolicyInfoT *pCurrEndPointPolicy, { case INTERRUPTBEHAVIOR_CONTINUE: //unduck and set Volume back to original value - err= PolicySetVolume(pCurrEndPointPolicy->endpointID, + err = PolicySetVolume(pCurrEndPointPolicy->endpointID, pCurrEndPointPolicy->type, pCurrEndPointPolicy->pHalApiName, HighPriorityStreamInfo.pAudioRole, @@ -413,21 +423,15 @@ static int PolicyRunningIdleTransition(EndPointPolicyInfoT *pCurrEndPointPolicy, } return POLICY_SUCCESS; - break; case INTERRUPTBEHAVIOR_PAUSE: - //pInterruptStreamInfo->streamState = STREAM_STATE_RUNNING; PolicyPostStateEvent(HighPriorityStreamInfo.streamID,STREAM_EVENT_RESUME); return POLICY_SUCCESS; - break; - case INTERRUPTBEHAVIOR_CANCEL: PolicyPostStateEvent(HighPriorityStreamInfo.streamID,STREAM_EVENT_START); return POLICY_SUCCESS; - break; default: AFB_ERROR("Unsupported Intterupt Behavior"); return POLICY_FAIL; - break; } } } @@ -435,7 +439,7 @@ static int PolicyRunningIdleTransition(EndPointPolicyInfoT *pCurrEndPointPolicy, return POLICY_SUCCESS; } -static int PolicyIdleRunningTransition(EndPointPolicyInfoT *pCurrEndPointPolicy, StreamInfoT * pStreamInfo) +static int PolicyIdleRunningTransition(EndPointPolicyInfoT *pCurrEndPointPolicy, StreamInterfaceInfoT * pStreamInfo) { int err=0; if(pCurrEndPointPolicy->streamInfo == NULL) @@ -459,7 +463,7 @@ static int PolicyIdleRunningTransition(EndPointPolicyInfoT *pCurrEndPointPolicy, { case INTERRUPTBEHAVIOR_CONTINUE: //Save the current Volume and set the docking volume - pCurrentActiveStreamInfo->iDuckVolume = pStreamInfo->pEndpointInfo->iVolume; + pCurrentActiveStreamInfo->iDuckVolume = pStreamInfo->endpoint.iVolume; StreamConfigT StreamConfig; err = getStreamConfig(pStreamInfo->pRoleName, &StreamConfig); if(err == POLICY_FAIL) @@ -467,7 +471,7 @@ static int PolicyIdleRunningTransition(EndPointPolicyInfoT *pCurrEndPointPolicy, AFB_ERROR("Error getting stream configuration for audiorole: %s", pStreamInfo->pRoleName); return POLICY_FAIL; } - err= PolicySetVolume(pCurrEndPointPolicy->endpointID, + err = PolicySetVolume(pCurrEndPointPolicy->endpointID, pCurrEndPointPolicy->type, pCurrEndPointPolicy->pHalApiName, pCurrentActiveStreamInfo->pAudioRole, @@ -483,7 +487,6 @@ static int PolicyIdleRunningTransition(EndPointPolicyInfoT *pCurrEndPointPolicy, case INTERRUPTBEHAVIOR_PAUSE: PolicyPostStateEvent(pCurrentActiveStreamInfo->streamID,STREAM_EVENT_PAUSE); break; - case INTERRUPTBEHAVIOR_CANCEL: PolicyPostStateEvent(pCurrentActiveStreamInfo->streamID,STREAM_EVENT_STOP); g_array_remove_index(pCurrEndPointPolicy->streamInfo, pCurrEndPointPolicy->streamInfo->len-1); @@ -491,8 +494,6 @@ static int PolicyIdleRunningTransition(EndPointPolicyInfoT *pCurrEndPointPolicy, default: AFB_ERROR("Unsupported Intterupt Behavior"); return AHL_POLICY_REJECT; - break; - } //Add the playing stream at last @@ -503,9 +504,7 @@ static int PolicyIdleRunningTransition(EndPointPolicyInfoT *pCurrEndPointPolicy, //Higher Priority Stream is playing AFB_ERROR("Higher Priority Stream is playing"); return POLICY_FAIL; - } - - + } } return POLICY_SUCCESS; @@ -513,7 +512,6 @@ static int PolicyIdleRunningTransition(EndPointPolicyInfoT *pCurrEndPointPolicy, static void PolicySpeedModify(int speed) { - for(int i=0; i<g_PolicyCtx.pSinkEndpoints->len; i++) { EndPointPolicyInfoT * pCurEndpoint = &g_array_index(g_PolicyCtx.pSinkEndpoints,EndPointPolicyInfoT,i); @@ -545,8 +543,11 @@ static void PolicySpeedModify(int speed) } } -static int RetrieveAssociatedHALAPIName(EndpointInfoT * io_pEndpointInfo) +static int RetrieveAssociatedHALAPIName(int iAlsaCardNumber,char ** out_pDisplayName,char ** out_pHALName) { + *out_pDisplayName = NULL; + *out_pHALName = NULL; + if(g_PolicyCtx.pHALList) { for(int i=0; i<g_PolicyCtx.pHALList->len; i++) @@ -554,25 +555,21 @@ static int RetrieveAssociatedHALAPIName(EndpointInfoT * io_pEndpointInfo) HalInfoT *pHalInfo = g_ptr_array_index(g_PolicyCtx.pHALList, i); // Retrieve card number (e.g. hw:0) int iCardNum = atoi(pHalInfo->pDevID+3); - if (iCardNum == io_pEndpointInfo->alsaInfo.cardNum) { - io_pEndpointInfo->gsHALAPIName=strdup(pHalInfo->pAPIName); - io_pEndpointInfo->gsDisplayName=strdup(pHalInfo->pDisplayName); + if (iCardNum == iAlsaCardNumber) { + *out_pDisplayName = pHalInfo->pDisplayName; + *out_pHALName = pHalInfo->pAPIName; return POLICY_SUCCESS; } } } - io_pEndpointInfo->gsHALAPIName=strdup(AHL_POLICY_UNDEFINED_HALNAME); - io_pEndpointInfo->gsDisplayName=strdup(AHL_POLICY_UNDEFINED_DISPLAYNAME); - return POLICY_FAIL; } static int GetHALList(void) { - json_object *j_response, *j_query = NULL; - int err; - err = afb_service_call_sync("alsacore", "hallist", j_query, &j_response); + json_object *j_response = NULL, *j_query = NULL; + int err = afb_service_call_sync("alsacore", "hallist", j_query, &j_response); if (err) { AFB_ERROR("Could not retrieve list of HAL from ALSA core"); return POLICY_FAIL; @@ -616,16 +613,14 @@ static int GetHALList(void) // // Policy API Functions // -int Policy_OpenStream(json_object *pPolicyStreamJ) +int Policy_OpenStream(json_object *pStreamJ) { - StreamInfoT PolicyStream; - EndpointInfoT EndpointInfo; - PolicyStream.pEndpointInfo =&EndpointInfo; + StreamInterfaceInfoT Stream; - int err = PolicyCtxJSONToStream(pPolicyStreamJ, &PolicyStream); + int err = JSONToStream(pStreamJ, &Stream); if(err == AHL_POLICY_UTIL_FAIL) { - return AHL_POLICY_ACCEPT; + return AHL_POLICY_REJECT; } // Example rule -> when system is in shutdown or low power mode, no audio stream can be opened (return AHL_POLICY_REJECT) @@ -636,26 +631,27 @@ int Policy_OpenStream(json_object *pPolicyStreamJ) } StreamConfigT StreamConfig; - err = getStreamConfig(PolicyStream.pRoleName, &StreamConfig); + err = getStreamConfig(Stream.pRoleName, &StreamConfig); if(err == POLICY_FAIL) { - return AHL_POLICY_ACCEPT; + return AHL_POLICY_REJECT; } - if(PolicyStream.pEndpointInfo->deviceURIType != DEVICEURITYPE_NOT_ALSA) { - err=PolicyGetVolume(PolicyStream.pEndpointInfo->endpointID, - PolicyStream.pEndpointInfo->type, - PolicyStream.pEndpointInfo->gsHALAPIName, - PolicyStream.pEndpointInfo->pRoleName, - PolicyStream.pEndpointInfo->deviceURIType, - &PolicyStream.pEndpointInfo->iVolume); + // Volume support only possible through ALSA + if(Stream.endpoint.deviceURIType != DEVICEURITYPE_NOT_ALSA) { + err = PolicyGetVolume(Stream.endpoint.endpointID, + Stream.endpoint.type, + Stream.endpoint.gsHALAPIName, + Stream.endpoint.pRoleName, + Stream.endpoint.deviceURIType, + &Stream.endpoint.iVolume); if(err == POLICY_FAIL) { return AHL_POLICY_REJECT; } } - err = PolicyAddEndPoint(&PolicyStream); + err = PolicyAddEndPoint(&Stream); if(err == POLICY_FAIL) { return AHL_POLICY_REJECT; @@ -663,291 +659,298 @@ int Policy_OpenStream(json_object *pPolicyStreamJ) return AHL_POLICY_ACCEPT; } -int Policy_CloseStream(json_object *pPolicyStreamJ) +int Policy_CloseStream(json_object *pStreamJ) { - //TODO remove Endpoint when there is no stream - StreamInfoT PolicyStream; - EndpointInfoT EndpointInfo; - PolicyStream.pEndpointInfo =&EndpointInfo; - int err = PolicyCtxJSONToStream(pPolicyStreamJ, &PolicyStream); + StreamInterfaceInfoT Stream; + int err = JSONToStream(pStreamJ, &Stream); if(err == AHL_POLICY_UTIL_FAIL) { - return AHL_POLICY_ACCEPT; + return AHL_POLICY_REJECT; } return AHL_POLICY_ACCEPT; } -int Policy_SetStreamState(json_object *pPolicyStreamJ) -{ - //TODO - // Optional: Mute endpoint before activation, unmute afterwards (after a delay?) to avoid noises - StreamInfoT PolicyStream; - EndpointInfoT EndpointInfo; - PolicyStream.pEndpointInfo =&EndpointInfo; - +int Policy_SetStreamState(json_object *pStreamJ) +{ + // Optional TODO: Could mute endpoint before activation, unmute afterwards (after a delay?) to avoid noises StreamStateT streamState = 0; - StreamInfoT * pPolicyStream = &PolicyStream; - int err = PolicyCtxJSONToStream(pPolicyStreamJ, pPolicyStream); + StreamInterfaceInfoT Stream; + + int err = JSONToStream(pStreamJ, &Stream); if(err == AHL_POLICY_UTIL_FAIL) { - return AHL_POLICY_ACCEPT; + return AHL_POLICY_REJECT; } - json_object *streamStateJ=NULL; + json_object *streamStateJ = NULL; - if(!json_object_object_get_ex(pPolicyStreamJ, "arg_stream_state", &streamStateJ)) + if(!json_object_object_get_ex(pStreamJ, "arg_stream_state", &streamStateJ)) { - return AHL_POLICY_ACCEPT; + return AHL_POLICY_REJECT; } streamState = (StreamStateT)json_object_get_int(streamStateJ); - //Change of state - if(pPolicyStream->streamState != streamState) + // Change of state + if(Stream.streamState != streamState) { //seach corresponding endpoint and gather information on it - EndPointPolicyInfoT *pCurrEndPointPolicy = PolicySearchEndPoint(pPolicyStream->pEndpointInfo->type , pPolicyStream->pEndpointInfo->gsDeviceName); + EndPointPolicyInfoT *pCurrEndPointPolicy = PolicySearchEndPoint(Stream.endpoint.type , Stream.endpoint.gsDeviceName); - switch(pPolicyStream->streamState) + switch(Stream.streamState) { case STREAM_STATE_IDLE: switch(streamState) { case STREAM_STATE_RUNNING: - err = PolicyIdleRunningTransition(pCurrEndPointPolicy, pPolicyStream); + err = PolicyIdleRunningTransition(pCurrEndPointPolicy, &Stream); if(err) { return AHL_POLICY_REJECT; } - PolicyPostStateEvent(pPolicyStream->streamID,STREAM_EVENT_START); + PolicyPostStateEvent(Stream.streamID,STREAM_EVENT_START); break; case STREAM_STATE_PAUSED: - err = PolicyIdleRunningTransition(pCurrEndPointPolicy, pPolicyStream); + err = PolicyIdleRunningTransition(pCurrEndPointPolicy, &Stream); if(err) { return AHL_POLICY_REJECT; } - PolicyPostStateEvent(pPolicyStream->streamID,STREAM_EVENT_PAUSE); + PolicyPostStateEvent(Stream.streamID,STREAM_EVENT_PAUSE); break; default: return AHL_POLICY_REJECT; - break; } break; case STREAM_STATE_RUNNING: switch(streamState) { case STREAM_STATE_IDLE: - err = PolicyRunningIdleTransition(pCurrEndPointPolicy, pPolicyStream); + err = PolicyRunningIdleTransition(pCurrEndPointPolicy, &Stream); if(err) { return AHL_POLICY_REJECT; } - PolicyPostStateEvent(pPolicyStream->streamID,STREAM_EVENT_STOP); + PolicyPostStateEvent(Stream.streamID,STREAM_EVENT_STOP); break; case STREAM_STATE_PAUSED: - PolicyPostStateEvent(pPolicyStream->streamID,STREAM_EVENT_PAUSE); + PolicyPostStateEvent(Stream.streamID,STREAM_EVENT_PAUSE); break; default: return AHL_POLICY_REJECT; - break; } break; case STREAM_STATE_PAUSED: switch(streamState) { case STREAM_STATE_IDLE: - err = PolicyRunningIdleTransition(pCurrEndPointPolicy, pPolicyStream); + err = PolicyRunningIdleTransition(pCurrEndPointPolicy, &Stream); if(err) { return AHL_POLICY_REJECT; } - PolicyPostStateEvent(pPolicyStream->streamID,STREAM_EVENT_STOP); + PolicyPostStateEvent(Stream.streamID,STREAM_EVENT_STOP); break; case STREAM_STATE_RUNNING: - PolicyPostStateEvent(pPolicyStream->streamID,STREAM_EVENT_RESUME); + PolicyPostStateEvent(Stream.streamID,STREAM_EVENT_RESUME); break; default: return AHL_POLICY_REJECT; - break; } break; default: return AHL_POLICY_REJECT; - break; } } return AHL_POLICY_ACCEPT; } -int Policy_SetStreamMute(json_object *pPolicyStreamJ) +int Policy_SetStreamMute(json_object *pStreamJ) { + // Note: stream mute currently implemented directly using ALSA volume. It should later be implemented with a distinct mute switch control instead StreamMuteT streamMute = 0; - StreamInfoT PolicyStream; - EndpointInfoT EndpointInfo; - PolicyStream.pEndpointInfo =&EndpointInfo; - StreamInfoT * pPolicyStream = &PolicyStream; + StreamInterfaceInfoT Stream; - int err = PolicyCtxJSONToStream(pPolicyStreamJ, pPolicyStream); + int err = JSONToStream(pStreamJ, &Stream); if(err == AHL_POLICY_UTIL_FAIL) { - return AHL_POLICY_ACCEPT; + return AHL_POLICY_REJECT; } json_object *streamMuteJ=NULL; - if(!json_object_object_get_ex(pPolicyStreamJ, "mute_state", &streamMuteJ)) + if(!json_object_object_get_ex(pStreamJ, "mute_state", &streamMuteJ)) { - return AHL_POLICY_ACCEPT; + return AHL_POLICY_REJECT; } streamMute = (StreamMuteT)json_object_get_int(streamMuteJ); - if(streamMute != pPolicyStream->streamMute) + if(streamMute != Stream.streamMute) { if(streamMute == STREAM_MUTED) { - - err= PolicySetVolume(pPolicyStream->pEndpointInfo->endpointID, - pPolicyStream->pEndpointInfo->type, - pPolicyStream->pEndpointInfo->gsHALAPIName, - pPolicyStream->pRoleName, - pPolicyStream->pEndpointInfo->deviceURIType, - 0, - true); + err = PolicySetVolume(Stream.endpoint.endpointID, + Stream.endpoint.type, + Stream.endpoint.gsHALAPIName, + Stream.pRoleName, + Stream.endpoint.deviceURIType, + 0, // mute volume + true); // no ramp and no volume event if(err) { - AFB_ERROR("StreamID:%i Set Volume return with errorcode%i",pPolicyStream->streamID, err); + AFB_ERROR("StreamID:%i Set Volume return with errorcode%i",Stream.streamID, err); return AHL_POLICY_REJECT; } - PolicyPostStateEvent(pPolicyStream->streamID,STREAM_EVENT_MUTED); + PolicyPostStateEvent(Stream.streamID,STREAM_EVENT_MUTED); } else { - err= PolicySetVolume(pPolicyStream->pEndpointInfo->endpointID, - pPolicyStream->pEndpointInfo->type, - pPolicyStream->pEndpointInfo->gsHALAPIName, - pPolicyStream->pRoleName, - pPolicyStream->pEndpointInfo->deviceURIType, - pPolicyStream->pEndpointInfo->iVolume, - true); + err = PolicySetVolume(Stream.endpoint.endpointID, + Stream.endpoint.type, + Stream.endpoint.gsHALAPIName, + Stream.pRoleName, + Stream.endpoint.deviceURIType, + Stream.endpoint.iVolume, // restore volume + true); // no ramp and no volume event if(err) { - AFB_ERROR("Endpoint:%i Set Volume return with errorcode%i",pPolicyStream->streamID, err); + AFB_ERROR("Endpoint:%i Set Volume return with errorcode%i",Stream.streamID, err); return AHL_POLICY_REJECT; } - PolicyPostStateEvent(pPolicyStream->streamID,STREAM_EVENT_UNMUTED); - + PolicyPostStateEvent(Stream.streamID,STREAM_EVENT_UNMUTED); } - - pPolicyStream->streamMute = streamMute; } return AHL_POLICY_ACCEPT; } -int Policy_SetVolume(json_object *pPolicyEndpointJ) +int Policy_SetVolume(json_object *pEndpointJ) { char *volumeStr = NULL; - EndpointInfoT EndpointInfo; + EndPointInterfaceInfoT Endpoint; - int err = PolicyCtxJSONToEndpoint(pPolicyEndpointJ, &EndpointInfo); + int err = JSONToEndpoint(pEndpointJ, &Endpoint); if(err == AHL_POLICY_UTIL_FAIL) { - return AHL_POLICY_ACCEPT; + return AHL_POLICY_REJECT; } - json_object *volumeJ=NULL; - - if(!json_object_object_get_ex(pPolicyEndpointJ, "arg_volume", &volumeJ)) + json_object *volumeJ = NULL; + if(!json_object_object_get_ex(pEndpointJ, "arg_volume", &volumeJ)) { - return AHL_POLICY_ACCEPT; + return AHL_POLICY_REJECT; } volumeStr = (char*)json_object_get_string(volumeJ); - // TODO: Parse volume string to support increment/absolute/percent notation (or delegate to action / policy layer to interpret) + // TODO: Parse volume string to support increment/absolute/percent notation int vol = atoi(volumeStr); - //Set the volume - err= PolicySetVolume(EndpointInfo.endpointID, - EndpointInfo.type, - EndpointInfo.gsHALAPIName, - EndpointInfo.pRoleName, - EndpointInfo.deviceURIType, - vol, - false); + // Set the volume + err = PolicySetVolume(Endpoint.endpointID, + Endpoint.type, + Endpoint.gsHALAPIName, + Endpoint.pRoleName, + Endpoint.deviceURIType, + vol, + false); // Volume ramp and send events if (err) { - AFB_ERROR("Set Volume return with errorcode%i", err); + AFB_ERROR("Set Volume return with errorcode %i", err); return AHL_POLICY_REJECT; } return AHL_POLICY_ACCEPT; } -int Policy_SetProperty(json_object *pPolicyEndpointJ) +int Policy_SetProperty(json_object *pEndpointJ) { - char *propertyName = NULL; - EndpointInfoT EndpointInfo; + EndPointInterfaceInfoT Endpoint; - int err = PolicyCtxJSONToEndpoint(pPolicyEndpointJ, &EndpointInfo); + int err = JSONToEndpoint(pEndpointJ, &Endpoint); if(err == AHL_POLICY_UTIL_FAIL) { - return AHL_POLICY_ACCEPT; + return AHL_POLICY_REJECT; } - json_object *propertyNameJ=NULL; - - if(!json_object_object_get_ex(pPolicyEndpointJ, "arg_property_name", &propertyNameJ)) + json_object *propertyNameJ = NULL; + if(!json_object_object_get_ex(pEndpointJ, "arg_property_name", &propertyNameJ)) { - return AHL_POLICY_ACCEPT; + return AHL_POLICY_REJECT; } propertyName = (char*)json_object_get_string(propertyNameJ); - json_object *propValueJ; - if(!json_object_object_get_ex(pPolicyEndpointJ, "arg_property_value", &propValueJ)) + json_object *propValueJ = NULL; + if(!json_object_object_get_ex(pEndpointJ, "arg_property_value", &propValueJ)) { - return AHL_POLICY_ACCEPT; + return AHL_POLICY_REJECT; } + json_type currentTypeJ = json_object_get_type(propValueJ); - gpointer *key_value=NULL; - - key_value = g_hash_table_lookup(EndpointInfo.pPropTable,propertyName); - if(key_value == NULL) + json_object *propArray = NULL; + if(!json_object_object_get_ex(pEndpointJ, "properties", &propArray)) { - AFB_ERROR("Can't find property %s, request will be rejected", propertyName); - return AHL_POLICY_REJECT; + return AHL_POLICY_REJECT; } - //Get JsonObjectype - json_type currentjType = json_object_get_type((json_object*)key_value); - json_type newjType = json_object_get_type(propValueJ); - - //Apply policy on set property if needed here - //Here we only validate that the type is the same - if(currentjType != newjType) + int iPropArrayLen = json_object_array_length(propArray); + int foundProperty = 0; + + for (int i = 0; i < iPropArrayLen; i++) { - AFB_ERROR("Property Value Type is wrong"); - return AHL_POLICY_REJECT; + // get the i-th object in medi_array + json_object *propElementJ = json_object_array_get_idx(propArray, i); + if(propElementJ) + { + json_object *propElementNameJ=NULL; + if(json_object_object_get_ex(propElementJ, "property_name", &propElementNameJ)) + { + char *propElementName = (char*)json_object_get_string(propElementNameJ); + if(strcasecmp(propElementName,propertyName)==0) + { + json_object *propElementValueJ=NULL; + if(!json_object_object_get_ex(propElementJ, "property_value", &propElementValueJ)) + { + json_type elementTypeJ = json_object_get_type(propElementValueJ); + + // Apply policy on set property if needed here + // Here we only validate that the type is the same + if(currentTypeJ != elementTypeJ) + { + AFB_ERROR("Property Value Type is wrong"); + return AHL_POLICY_REJECT; + } + foundProperty = 1; + break; + } + } + } + } } + if(foundProperty== 0) + { + AFB_ERROR("Can't find property %s, request will be rejected", propertyName); + return AHL_POLICY_REJECT; + } - //Create a new Json Object + // Create a new Json Object json_object *pEventDataJ = NULL; err = wrap_json_pack(&pEventDataJ,"{s:s,s:i,s:i,s:s,s:o,s:s}", "event_name", AHL_ENDPOINT_PROPERTY_EVENT, - "endpoint_id", EndpointInfo.endpointID, - "endpoint_type", EndpointInfo.type, + "endpoint_id", Endpoint.endpointID, + "endpoint_type", Endpoint.type, "property_name", propertyName, "value",propValueJ, - "audio_role", EndpointInfo.pRoleName); + "audio_role", Endpoint.pRoleName); if(err) { AFB_ERROR("Unable to pack property event"); return AHL_POLICY_REJECT; } - //Raise Event to update HLB + + // Raise Event to update HLB audiohlapi_raise_event(pEventDataJ); return AHL_POLICY_ACCEPT; @@ -978,94 +981,137 @@ int Policy_PostAction(json_object *pPolicyActionJ) AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err)); return AHL_POLICY_REJECT; } + audiohlapi_raise_event(pEventDataJ); return AHL_POLICY_ACCEPT; } -int Policy_Endpoint_Init(json_object *pPolicyEndpointJ) +int Policy_Endpoint_Init(json_object *pInPolicyEndpointJ,json_object **pOutPolicyEndpointJ) { - EndpointInfoT EndpointInfo; - - int err = PolicyCtxJSONToEndpoint(pPolicyEndpointJ, &EndpointInfo); - if(err == AHL_POLICY_UTIL_FAIL) - { - return AHL_POLICY_REJECT; + endpointID_t endpointID = AHL_UNDEFINED; + EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; + DeviceURITypeT deviceURIType = DEVICEURITYPE_MAXVALUE; + int iAllocString = 0; + char * pRoleName = NULL; + int iAlsaCardNumber = AHL_UNDEFINED; + char * pDeviceName = NULL; + int err = wrap_json_unpack(pInPolicyEndpointJ,"{s:i,s:i,s:i,s:s,s:i,s:s}", + "endpoint_id",&endpointID, + "endpoint_type",&endpointType, + "device_uri_type",&deviceURIType, + "audio_role",&pRoleName, + "alsa_cardNum", &iAlsaCardNumber, + "device_name", &pDeviceName ); + if (err) { + AFB_ERROR("Unable to unpack JSON endpoint, =%s", wrap_json_get_error_string(err)); + goto OnError; } - if (EndpointInfo.deviceURIType != DEVICEURITYPE_NOT_ALSA) { + StreamConfigT StreamConfig; + getStreamConfig(pRoleName, &StreamConfig); + + char * pDisplayName = NULL; + char * pHALName = NULL; + if (deviceURIType != DEVICEURITYPE_NOT_ALSA) { // Update Hal Name - err = RetrieveAssociatedHALAPIName(&EndpointInfo); + err = RetrieveAssociatedHALAPIName(iAlsaCardNumber,&pDisplayName,&pHALName); if (err) { - AFB_ERROR("HAL not found for Device %s", EndpointInfo.gsDeviceName); - return AHL_POLICY_REJECT; + AFB_WARNING("HAL not found for device %s", pDeviceName); + pDisplayName = g_strdup(AHL_POLICY_UNDEFINED_DISPLAYNAME); + pHALName = g_strdup(AHL_POLICY_UNDEFINED_HALNAME); + iAllocString = 1; } - - //Set Init Volume - StreamConfigT StreamConfig; - getStreamConfig(EndpointInfo.pRoleName, &StreamConfig); - err = PolicySetVolume(EndpointInfo.endpointID, - EndpointInfo.type, - EndpointInfo.gsHALAPIName, - EndpointInfo.pRoleName, - EndpointInfo.deviceURIType, - StreamConfig.iVolumeInit, - false); - if(err) { - return AHL_POLICY_REJECT; + else + { + // Set initial Volume + err = PolicySetVolume(endpointID, + endpointType, + pHALName, + pRoleName, + deviceURIType, + StreamConfig.iVolumeInit, + true); // Do not raise event and no volume ramp + if(err) { + goto OnError; + } } } - - // Test example - Add_Endpoint_Property_Int(&EndpointInfo,AHL_PROPERTY_EQ_LOW,3); - Add_Endpoint_Property_Int(&EndpointInfo,AHL_PROPERTY_EQ_MID,0); - Add_Endpoint_Property_Int(&EndpointInfo,AHL_PROPERTY_EQ_HIGH,6); - Add_Endpoint_Property_Int(&EndpointInfo,AHL_PROPERTY_BALANCE,0); - Add_Endpoint_Property_Int(&EndpointInfo,AHL_PROPERTY_FADE,30); - Add_Endpoint_Property_String(&EndpointInfo,"preset_name","flat"); - - - gpointer *key_value = g_hash_table_lookup(EndpointInfo.pPropTable,AHL_PROPERTY_BALANCE); - if(key_value == NULL) - { - AFB_ERROR("Can't find property %s, request will be rejected", AHL_PROPERTY_BALANCE); - return AHL_POLICY_REJECT; + else { + // Set display / HAL for non ALSA devices (default) + pDisplayName = g_strdup(AHL_POLICY_UNDEFINED_DISPLAYNAME); + pHALName = g_strdup(AHL_POLICY_UNDEFINED_HALNAME); + iAllocString = 1; } - //Create a new Json Object - json_object *pNewPolicyEndpointJ = NULL; - err = PolicyEndpointStructToJSON(&EndpointInfo, &pNewPolicyEndpointJ); - if (err == AHL_POLICY_UTIL_FAIL) - { - return AHL_POLICY_REJECT; - } - json_object *paramJ= json_object_new_string(AHL_ENDPOINT_INIT_EVENT); - json_object_object_add(pNewPolicyEndpointJ, "event_name", paramJ); + // Populate special device property (TODO: Should be obtained from HAL) + // if (strcasecmp(gsHALAPIName,"Device")==0) + // { + // Create json object for PropTable + json_object *pPropTableJ = json_object_new_array(); + Add_Endpoint_Property_Int(pPropTableJ,AHL_PROPERTY_EQ_LOW,3); + Add_Endpoint_Property_Int(pPropTableJ,AHL_PROPERTY_EQ_MID,0); + Add_Endpoint_Property_Int(pPropTableJ,AHL_PROPERTY_EQ_HIGH,6); + Add_Endpoint_Property_Int(pPropTableJ,AHL_PROPERTY_BALANCE,0); + Add_Endpoint_Property_Int(pPropTableJ,AHL_PROPERTY_FADE,30); + Add_Endpoint_Property_String(pPropTableJ,"preset_name","flat"); + // } + + err = wrap_json_pack(pOutPolicyEndpointJ,"{s:i,s:s,s:s,s:o}", + "init_volume",StreamConfig.iVolumeInit, + "display_name",pDisplayName, + "hal_name",pHALName, + "property_table",pPropTableJ + ); + if (err) { + AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err)); + goto OnError; + } + + // TODO: Future policy binding to return request response with pOutPolicyEndpointJ (instead of output argument) + return AHL_POLICY_ACCEPT; // No errors - //Raise Event to update HLB - audiohlapi_raise_event(pNewPolicyEndpointJ); +OnError: + if (iAllocString) { + g_free(pDisplayName); + g_free(pHALName); + } + return AHL_POLICY_REJECT; - return AHL_POLICY_ACCEPT; // No errors } int Policy_Init() { + // Start fresh + memset(&g_PolicyCtx,0,sizeof(g_PolicyCtx)); + // Initialize Ressources - g_PolicyCtx.pSourceEndpoints =g_array_new(FALSE,TRUE,sizeof(EndPointPolicyInfoT)); + g_PolicyCtx.pSourceEndpoints = g_array_new(FALSE,TRUE,sizeof(EndPointPolicyInfoT)); g_PolicyCtx.pSinkEndpoints = g_array_new(FALSE,TRUE,sizeof(EndPointPolicyInfoT)); g_PolicyCtx.pHALList = g_ptr_array_new_with_free_func(g_free); - //Get HalList + //Require AlsaCore Dependency + int err = afb_daemon_require_api_v2(AHL_POLICY_ALSA_API,1) ; + if( err != 0 ) + { + AFB_ERROR("Audio Policy could not set dependency on alsacore API"); + return AHL_POLICY_REJECT; + } + + // Get HalList GetHALList(); - //Set System Normal for now, this should be set by an event - //TODO: Receive event from low level + // TODO: Register events from low level / HAL for dynamic changes + + // Set System Normal for now, this should be set by an event g_PolicyCtx.systemState = SYSTEM_NORMAL; - //register audio backend events - //This is to simulate can bus, only used for demo - json_object *queryurl, *responseJ, *eventsJ; - + + +#ifdef AK_POLICY_DEMO + // Register audio backend events (TODO: should instead do this with signal composer with appropriate dependency) + // This is to simulate can bus, only used for demo + json_object *queryurl = NULL, *responseJ = NULL, *eventsJ = NULL; eventsJ = json_object_new_array(); json_object_array_add(eventsJ, json_object_new_string("audiod_system_event")); queryurl = json_object_new_object(); @@ -1075,12 +1121,15 @@ int Policy_Init() AFB_ERROR("Fail subscribing to Audio Backend System events"); return AHL_POLICY_REJECT; } +#endif + + return AHL_POLICY_ACCEPT; } void Policy_Term() { - //Free Ressources + // Free Ressources if (g_PolicyCtx.pHALList) { g_ptr_array_free(g_PolicyCtx.pHALList,TRUE); g_PolicyCtx.pHALList = NULL; @@ -1106,6 +1155,7 @@ void Policy_Term() g_PolicyCtx.pSinkEndpoints = NULL; } +// For demo purpose only, should be listening to signal composer / CAN events instead void Policy_OnEvent(const char *evtname, json_object *eventJ) { AFB_DEBUG("Policy received event %s", evtname); @@ -1129,7 +1179,7 @@ void Policy_OnEvent(const char *evtname, json_object *eventJ) AFB_WARNING("Invalid arguments, Args not a valid json object query=%s", json_object_get_string(event_parameter)); return; } - //When speed change Modify volume on Endpoint where entertainment change + // When speed change Modify volume on Endpoint where entertainment change PolicySpeedModify(speed); } } diff --git a/ahl-policy/ahl-policy.h b/ahl-policy/ahl-policy.h index 942055a..0200973 100644 --- a/ahl-policy/ahl-policy.h +++ b/ahl-policy/ahl-policy.h @@ -16,6 +16,7 @@ #ifndef AHL_POLICY_INCLUDE #define AHL_POLICY_INCLUDE + #include "ahl-policy-utils.h" #ifndef AHL_DISCONNECT_POLICY @@ -27,19 +28,22 @@ #define AHL_POLICY_UNDEFINED_HALNAME "UNDEFINED" #define AHL_POLICY_UNDEFINED_DISPLAYNAME "DeviceNotFound" +#define AHL_POLICY_ALSA_API "alsacore" -int Policy_Endpoint_Init(json_object *pPolicyEndpointJ); -int Policy_OpenStream(json_object *pPolicyStreamJ); -int Policy_CloseStream(json_object *pPolicyStreamJ); -int Policy_SetStreamState(json_object *pPolicyStreamJ); -int Policy_SetStreamMute(json_object *pPolicyStreamJ); -int Policy_PostAction(json_object *pPolicyActionJ); -int Policy_SetVolume(json_object *pPolicyEndpointJ); -int Policy_SetProperty(json_object *pPolicyEndpointJ); +int Policy_Endpoint_Init(json_object *pInPolicyEndpointJ,json_object **pOutPolicyEndpointJ); +int Policy_OpenStream(json_object *pStreamJ); +int Policy_CloseStream(json_object *pStreamJ); +int Policy_SetStreamState(json_object *pStreamJ); +int Policy_SetStreamMute(json_object *pStreamJ); +int Policy_PostAction(json_object *pActionJ); +int Policy_SetVolume(json_object *pEndpointJ); +int Policy_SetProperty(json_object *pEndpointJ); int Policy_Init(); void Policy_Term(); void Policy_OnEvent(const char *evtname, json_object *eventJ); +// TODO: To be replaced by AGL events once policy is isolated in its own binding extern void audiohlapi_raise_event(json_object * pEventDataJ); + #endif // AHL_DISCONNECT_POLICY #endif // AHL_POLICY_INCLUDE
\ No newline at end of file diff --git a/ahl-utilities/ahl-policy-utils.c b/ahl-utilities/ahl-policy-utils.c index 60ddd36..195c591 100644..100755 --- a/ahl-utilities/ahl-policy-utils.c +++ b/ahl-utilities/ahl-policy-utils.c @@ -14,223 +14,168 @@ * limitations under the License. */ +#define AFB_BINDING_VERSION 2 +#include <afb/afb-binding.h> + #include "ahl-policy-utils.h" #include "wrap-json.h" #include <json-c/json.h> -#include <glib.h> -void Add_Endpoint_Property_Double( EndpointInfoT * io_pEndpointInfo, char * in_pPropertyName, double in_dPropertyValue) +void Add_Endpoint_Property_Double( json_object * io_pPropertyArray, char * in_pPropertyName, double in_dPropertyValue) { - json_object * propValueJ = json_object_new_double(in_dPropertyValue); - g_hash_table_insert(io_pEndpointInfo->pPropTable, in_pPropertyName, propValueJ); + json_object * pPropertyJ = NULL; + wrap_json_pack(&pPropertyJ, "{s:s,s:o}", + "property_name", in_pPropertyName, + "property_value", json_object_new_double(in_dPropertyValue) + ); + json_object_array_add(io_pPropertyArray, pPropertyJ); } - -void Add_Endpoint_Property_Int( EndpointInfoT * io_pEndpointInfo, char * in_pPropertyName, int in_iPropertyValue) +void Add_Endpoint_Property_Int( json_object * io_pPropertyArray, char * in_pPropertyName, int in_iPropertyValue) { - json_object * propValueJ = json_object_new_int(in_iPropertyValue); - g_hash_table_insert(io_pEndpointInfo->pPropTable, in_pPropertyName, propValueJ); + json_object * pPropertyJ = NULL; + wrap_json_pack(&pPropertyJ, "{s:s,s:o}", + "property_name", in_pPropertyName, + "property_value", json_object_new_int(in_iPropertyValue) + ); + json_object_array_add(io_pPropertyArray, pPropertyJ); } -void Add_Endpoint_Property_String( EndpointInfoT * io_pEndpointInfo, char * in_pPropertyName, const char * in_pPropertyValue) +void Add_Endpoint_Property_String( json_object * io_pPropertyArray, char * in_pPropertyName, const char * in_pPropertyValue) { - json_object * propValueJ = json_object_new_string(in_pPropertyValue); - g_hash_table_insert(io_pEndpointInfo->pPropTable, in_pPropertyName, propValueJ); + json_object * pPropertyJ = NULL; + wrap_json_pack(&pPropertyJ, "{s:s,s:o}", + "property_name", in_pPropertyName, + "property_value", json_object_new_string(in_pPropertyValue) + ); + json_object_array_add(io_pPropertyArray, pPropertyJ); } -int PolicyEndpointStructToJSON(EndpointInfoT * pEndpointInfo, json_object **ppPolicyEndpointJ) +int EndpointToJSON(EndPointInterfaceInfoT * pEndpoint, json_object **ppEndpointJ) { - if(pEndpointInfo == NULL || pEndpointInfo->pPropTable == NULL) + if(ppEndpointJ == NULL || pEndpoint == NULL) { - AFB_ERROR("Invalid PolicyEndpointStructToJSON arguments"); + AFB_ERROR("Invalid EndpointToJSON arguments"); return AHL_POLICY_UTIL_FAIL; } - - //Create json object for PropTable - json_object *pPropTableJ = json_object_new_array(); - if(pEndpointInfo->pPropTable) { - GHashTableIter iter; - gpointer key, value; - g_hash_table_iter_init (&iter, pEndpointInfo->pPropTable); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - json_object *pPropertyJ = NULL; - int error=wrap_json_pack(&pPropertyJ, "{s:s,s:o}", - "property_name", (char*)key, - "property_value", value - ); - if(error) - { - AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(error)); - return AHL_POLICY_UTIL_FAIL; - } - json_object_array_add(pPropTableJ, pPropertyJ); - } - AFB_DEBUG("json object query=%s", json_object_get_string(pPropTableJ)); - } - - //Create json object for Endpoint - int err= wrap_json_pack(ppPolicyEndpointJ, "{s:i,s:i,s:s,s:s,s:s,s:s,s:s,s:i,s:s,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:o}", - "endpoint_id", pEndpointInfo->endpointID, - "endpoint_type", pEndpointInfo->type, - "device_name", pEndpointInfo->gsDeviceName, - "display_name", pEndpointInfo->gsDisplayName, - "device_uri", pEndpointInfo->gsDeviceURI, - "device_domain", pEndpointInfo->gsDeviceDomain, - "audio_role",pEndpointInfo->pRoleName, - "device_uri_type", pEndpointInfo->deviceURIType, - "hal_api_name", pEndpointInfo->gsHALAPIName, - "alsa_cardNum", pEndpointInfo->alsaInfo.cardNum, - "alsa_deviceNum", pEndpointInfo->alsaInfo.deviceNum, - "alsa_subDeviceNum", pEndpointInfo->alsaInfo.subDeviceNum, - "format_samplerate", pEndpointInfo->format.sampleRate, - "format_numchannels", pEndpointInfo->format.numChannels, - "format_sampletype",pEndpointInfo->format.sampleType, - "volume", pEndpointInfo->iVolume, - "property_table", pPropTableJ + + // Create json object for Endpoint + int err = wrap_json_pack(ppEndpointJ, "{s:i,s:i,s:s,s:s,s:s,s:s,s:s,s:i,s:s,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s?o}", + "endpoint_id", pEndpoint->endpointID, + "endpoint_type", pEndpoint->type, + "device_name", pEndpoint->gsDeviceName, + "display_name", pEndpoint->gsDisplayName, + "device_uri", pEndpoint->gsDeviceURI, + "device_domain", pEndpoint->gsDeviceDomain, + "audio_role",pEndpoint->pRoleName, + "device_uri_type", pEndpoint->deviceURIType, + "hal_api_name", pEndpoint->gsHALAPIName, + "alsa_cardNum", pEndpoint->alsaInfo.cardNum, + "alsa_deviceNum", pEndpoint->alsaInfo.deviceNum, + "alsa_subDeviceNum", pEndpoint->alsaInfo.subDeviceNum, + "format_samplerate", pEndpoint->format.sampleRate, + "format_numchannels", pEndpoint->format.numChannels, + "format_sampletype",pEndpoint->format.sampleType, + "volume", pEndpoint->iVolume, + "property_table", pEndpoint->pPropTableJ ); if (err) { AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err)); return AHL_POLICY_UTIL_FAIL; } - AFB_DEBUG("JSON endpoint information=%s", json_object_get_string(*ppPolicyEndpointJ)); + AFB_DEBUG("JSON endpoint information=%s", json_object_get_string(*ppEndpointJ)); return AHL_POLICY_UTIL_SUCCESS; } -int PolicyStreamStructToJSON(StreamInfoT * pPolicyStream, json_object **ppPolicyStreamJ) +int StreamToJSON(StreamInterfaceInfoT * pStream, json_object **ppStreamJ) { - if(pPolicyStream == NULL) + if(pStream == NULL) { - AFB_ERROR("Invalid arguments to PolicyStreamStructToJSON"); + AFB_ERROR("Invalid arguments to StreamToJSON, stream structure is NULL"); return AHL_POLICY_UTIL_FAIL; } - json_object * pEndpointJ = NULL; - int iRet = PolicyEndpointStructToJSON(pPolicyStream->pEndpointInfo, &pEndpointJ); - if (iRet) { - return iRet; + json_object *EndpointJ = NULL; + int err = EndpointToJSON(&pStream->endpoint, &EndpointJ); + if (err) { + AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err)); + return AHL_POLICY_UTIL_FAIL; } - //Create json object for stream - int err = wrap_json_pack(ppPolicyStreamJ, "{s:i,s:i,s:i,s:I,s:i,s:s,s:i,s:i,s:o}", - "stream_id", pPolicyStream->streamID, - "stream_state", pPolicyStream->streamState, - "stream_mute", pPolicyStream->streamMute, - "stream_state_event", &pPolicyStream->streamStateEvent, - "endpoint_sel_mod", pPolicyStream->endpointSelMode, - "role_name", pPolicyStream->pRoleName, - "priority", pPolicyStream->iPriority, - "interrupt_behavior", pPolicyStream->eInterruptBehavior, - "endpoint_info", pEndpointJ + // Create json object for stream + err = wrap_json_pack(ppStreamJ, "{s:i,s:i,s:i,s:s,s:i,s:i,s:o}", + "stream_id", pStream->streamID, + "stream_state", pStream->streamState, + "stream_mute", pStream->streamMute, + "role_name", pStream->pRoleName, + "priority", pStream->iPriority, + "interrupt_behavior", pStream->eInterruptBehavior, + "endpoint_info", EndpointJ ); if (err) { - AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err)); + AFB_ERROR("Unable to pack JSON Stream, =%s", wrap_json_get_error_string(err)); return AHL_POLICY_UTIL_FAIL; } - AFB_DEBUG("JSON stream information=%s", json_object_get_string(*ppPolicyStreamJ)); + AFB_DEBUG("JSON stream information=%s", json_object_get_string(*ppStreamJ)); return AHL_POLICY_UTIL_SUCCESS; } -int PolicyCtxJSONToEndpoint(json_object *pEndpointJ, EndpointInfoT * pEndpointInfo) +//pEndpointInterfaceInfo must be pre-allocated by the caller +int JSONToEndpoint(json_object *pEndpointJ, EndPointInterfaceInfoT *pEndpoint) { - if(pEndpointJ == NULL || pEndpointInfo == NULL /*|| pEndpointInfo->pPropTable == NULL */ ) + if(pEndpointJ == NULL || pEndpoint == NULL) { - AFB_ERROR("Invalid arguments for PolicyCtxJSONToEndpoint"); + AFB_ERROR("Invalid arguments for JSONToEndpoint"); return AHL_POLICY_UTIL_FAIL; } //Unpack Endpoint - json_object *pPropTableJ = NULL; - int err = wrap_json_unpack(pEndpointJ, "{s:i,s:i,s:s,s:s,s:s,s:s,s:s,s:i,s:s,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:o}", - "endpoint_id", &pEndpointInfo->endpointID, - "endpoint_type", &pEndpointInfo->type, - "device_name", &pEndpointInfo->gsDeviceName, - "display_name", &pEndpointInfo->gsDisplayName, - "device_uri", &pEndpointInfo->gsDeviceURI, - "device_domain", &pEndpointInfo->gsDeviceDomain, - "audio_role", &pEndpointInfo->pRoleName, - "device_uri_type", &pEndpointInfo->deviceURIType, - "hal_api_name", &pEndpointInfo->gsHALAPIName, - "alsa_cardNum", &pEndpointInfo->alsaInfo.cardNum, - "alsa_deviceNum", &pEndpointInfo->alsaInfo.deviceNum, - "alsa_subDeviceNum", &pEndpointInfo->alsaInfo.subDeviceNum, - "format_samplerate", &pEndpointInfo->format.sampleRate, - "format_numchannels", &pEndpointInfo->format.numChannels, - "format_sampletype",&pEndpointInfo->format.sampleType, - "volume", &pEndpointInfo->iVolume, - "property_table", &pPropTableJ + int err = wrap_json_unpack(pEndpointJ, "{s:i,s:i,s:s,s:s,s:s,s:s,s:s,s:i,s:s,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s?o}", + "endpoint_id", &pEndpoint->endpointID, + "endpoint_type", &pEndpoint->type, + "device_name", &pEndpoint->gsDeviceName, + "display_name", &pEndpoint->gsDisplayName, + "device_uri", &pEndpoint->gsDeviceURI, + "device_domain", &pEndpoint->gsDeviceDomain, + "audio_role", &pEndpoint->pRoleName, + "device_uri_type", &pEndpoint->deviceURIType, + "hal_api_name", &pEndpoint->gsHALAPIName, + "alsa_cardNum", &pEndpoint->alsaInfo.cardNum, + "alsa_deviceNum", &pEndpoint->alsaInfo.deviceNum, + "alsa_subDeviceNum", &pEndpoint->alsaInfo.subDeviceNum, + "format_samplerate", &pEndpoint->format.sampleRate, + "format_numchannels", &pEndpoint->format.numChannels, + "format_sampletype",&pEndpoint->format.sampleType, + "volume", &pEndpoint->iVolume, + "property_table", &pEndpoint->pPropTableJ ); if (err) { AFB_ERROR("Unable to unpack JSON endpoint, =%s", wrap_json_get_error_string(err)); return AHL_POLICY_UTIL_FAIL; } - - // Unpack prop table - if(pPropTableJ) - { - pEndpointInfo->pPropTable = g_hash_table_new(g_str_hash, g_str_equal); - - int nbProperties = json_object_array_length(pPropTableJ); - for(int i=0; i<nbProperties; i++) - { - json_object * propJ = json_object_array_get_idx(pPropTableJ,i); - if (propJ) { - char * pPropertyName = NULL; - json_object * pPropertyValueJ = NULL; - - int err=wrap_json_unpack(propJ, "{s:s,s:o}", - "property_name", &pPropertyName, - "property_value", &pPropertyValueJ); - if (err) { - AFB_ERROR("Unable to unpack JSON endpoint, = %s", wrap_json_get_error_string(err)); - return AHL_POLICY_UTIL_FAIL; - } - - // Object type detection for property value (string = state, numeric = property) - json_type jType = json_object_get_type(pPropertyValueJ); - switch (jType) { - case json_type_double: - Add_Endpoint_Property_Double(pEndpointInfo,pPropertyName,json_object_get_double(pPropertyValueJ)); - break; - case json_type_int: - Add_Endpoint_Property_Int(pEndpointInfo,pPropertyName,json_object_get_int(pPropertyValueJ)); - break; - case json_type_string: - Add_Endpoint_Property_String(pEndpointInfo,pPropertyName,json_object_get_string(pPropertyValueJ)); - break; - default: - AFB_ERROR("Invalid property argument Property value not a valid json object query=%s", json_object_get_string(pPropertyValueJ)); - return AHL_POLICY_UTIL_FAIL; - } - } - } - } - return AHL_POLICY_UTIL_SUCCESS; } -int PolicyCtxJSONToStream(json_object *pStreamJ, StreamInfoT * pPolicyStream) +int JSONToStream(json_object *pStreamJ, StreamInterfaceInfoT * pStream) { - if(pStreamJ == NULL || pPolicyStream == NULL) + if(pStreamJ == NULL || pStream == NULL) { - AFB_ERROR("Invalid arguments for PolicyCtxJSONToStream"); + AFB_ERROR("Invalid arguments for InterfaceCtxJSONToStream"); return AHL_POLICY_UTIL_FAIL; } //Unpack StreamInfo json_object *pEndpointJ = NULL; AFB_WARNING("json object query=%s", json_object_get_string(pStreamJ)); - int err=wrap_json_unpack(pStreamJ, "{s:i,s:i,s:i,s:I,s:i,s:s,s:i,s:i,s:o}", - "stream_id", &pPolicyStream->streamID, - "stream_state", &pPolicyStream->streamState, - "stream_mute", &pPolicyStream->streamMute, - "stream_state_event", &pPolicyStream->streamStateEvent, - "endpoint_sel_mod", &pPolicyStream->endpointSelMode, - "role_name", &pPolicyStream->pRoleName, - "priority", &pPolicyStream->iPriority, - "interrupt_behavior", &pPolicyStream->eInterruptBehavior, + int err = wrap_json_unpack(pStreamJ, "{s:i,s:i,s:i,s:s,s:i,s:i,s:o}", + "stream_id", &pStream->streamID, + "stream_state", &pStream->streamState, + "stream_mute", &pStream->streamMute, + "role_name", &pStream->pRoleName, + "priority", &pStream->iPriority, + "interrupt_behavior", &pStream->eInterruptBehavior, "endpoint_info", &pEndpointJ ); @@ -239,9 +184,9 @@ int PolicyCtxJSONToStream(json_object *pStreamJ, StreamInfoT * pPolicyStream) return AHL_POLICY_UTIL_FAIL; } - int iRet = PolicyCtxJSONToEndpoint(pEndpointJ,pPolicyStream->pEndpointInfo); - if (iRet) { - return iRet; + err = JSONToEndpoint(pEndpointJ,&pStream->endpoint); + if (err) { + return AHL_POLICY_UTIL_FAIL; } return AHL_POLICY_UTIL_SUCCESS; } diff --git a/ahl-utilities/ahl-policy-utils.h b/ahl-utilities/ahl-policy-utils.h index 6adfa4e..3c20020 100644..100755 --- a/ahl-utilities/ahl-policy-utils.h +++ b/ahl-utilities/ahl-policy-utils.h @@ -17,16 +17,15 @@ #ifndef AHL_POLICY_UTILS_INCLUDE #define AHL_POLICY_UTILS_INCLUDE -#define AFB_BINDING_VERSION 2 #include <json-c/json.h> -#include <afb/afb-binding.h> -#include <glib.h> #define AHL_POLICY_ACCEPT 1 #define AHL_POLICY_REJECT 0 #define AHL_POLICY_UTIL_SUCCESS 0 #define AHL_POLICY_UTIL_FAIL 1 +#define AHL_UNDEFINED -1 + typedef int endpointID_t; typedef int streamID_t; @@ -118,60 +117,38 @@ typedef enum EndpointSelectionMode { ENDPOINTSELMODEMAXVALUE, // Enum count, keep at the end } EndpointSelectionModeT; -typedef struct EndpointInfo -{ +typedef struct EndPointInterfaceInfo { endpointID_t endpointID; // Unique endpoint ID (per type) EndpointTypeT type; // Source or sink device char * gsDeviceName; // Unique device card name char * gsDisplayName; // Application display name char * gsDeviceURI; // Associated URI char * gsDeviceDomain; // Device URI domain (e.g. alsa or pulse) - char * pRoleName; // Role assigned to this endpoint DeviceURITypeT deviceURIType; // Device URI type (includes audio domain information) char * gsHALAPIName; // HAL associated with the device (for volume control) + char * pRoleName; // Role string identifier (from role config but could be programatically overriden later) AlsaDeviceInfoT alsaInfo; // ALSA specific device information AudioFormatT format; // Preferred audio format supported (later could be array of supported formats) - int iVolume; // Storage for current endpoint volume (policy effected). - GHashTable * pPropTable; // Storage for array of properties (policy effected) -} EndpointInfoT; + int iVolume; // Storage for current endpoint volume (policy effected). + json_object *pPropTableJ; //Property Table +} EndPointInterfaceInfoT; -typedef struct StreamInfo { +typedef struct StreamInterfaceInfo { streamID_t streamID; // Stream unique ID - EndpointInfoT * pEndpointInfo; // Associated endpoint information (reference) StreamStateT streamState; // Stream activity state StreamMuteT streamMute; // Stream mute state - struct afb_event streamStateEvent; // Stream specific event for stream state changes - EndpointSelectionModeT endpointSelMode; // Automatic (priority based) or manual endpoint selection char * pRoleName; // Role string identifier (from role config but could be programatically overriden later) int iPriority; // Role normalized priority (0-100) (from role config but could be programatically overriden later) - InterruptBehaviorT eInterruptBehavior; // Role behavior when interrupting lower priority streams (from role config but could be programatically overriden later) -} StreamInfoT; - - -typedef struct StreamPolicyInfo { - streamID_t streamID; - int RolePriority; - char * pAudioRole; - InterruptBehaviorT interruptBehavior; - int iDuckVolume; //duck Volume -} StreamPolicyInfoT; - -typedef struct EndPointPolicyInfo { - endpointID_t endpointID; - EndpointTypeT type; - DeviceURITypeT deviceType; - char * pDeviceName; - char * pHalApiName; - int iVolume; //Current Volume - GArray * streamInfo; //List of playing or duck stream at a given endpoint -} EndPointPolicyInfoT; - -void Add_Endpoint_Property_Double( EndpointInfoT * io_pEndpointInfo, char * in_pPropertyName, double in_dPropertyValue); -void Add_Endpoint_Property_Int( EndpointInfoT * io_pEndpointInfo, char * in_pPropertyName, int in_iPropertyValue); -void Add_Endpoint_Property_String( EndpointInfoT * io_pEndpointInfo, char * in_pPropertyName, const char * in_pPropertyValue); -int PolicyEndpointStructToJSON(EndpointInfoT * pPolicyEndpoint, json_object **ppPolicyEndpointJ); -int PolicyCtxJSONToEndpoint(json_object *pEndpointJ, EndpointInfoT * pPolicyStream); -int PolicyStreamStructToJSON(StreamInfoT * pPolicyStream, json_object **ppPolicyStreamJ); -int PolicyCtxJSONToStream(json_object *pStreamJ, StreamInfoT * pPolicyStream); + InterruptBehaviorT eInterruptBehavior; // Role behavior when interrupting lower priority streams (from role config but could be programatically overriden later) + EndPointInterfaceInfoT endpoint; +} StreamInterfaceInfoT; + +void Add_Endpoint_Property_Double( json_object * io_pPropertyArray, char * in_pPropertyName, double in_dPropertyValue); +void Add_Endpoint_Property_Int( json_object * io_pPropertyArray, char * in_pPropertyName, int in_iPropertyValue); +void Add_Endpoint_Property_String( json_object * io_pPropertyArray, char * in_pPropertyName, const char * in_pPropertyValue); +int F(EndPointInterfaceInfoT * pEndpoint, json_object **ppEndpointJ); +int JSONToEndpoint(json_object *pEndpointJ, EndPointInterfaceInfoT * pStream); +int StreamToJSON(StreamInterfaceInfoT * pPolicyStream, json_object **ppStreamJ); +int JSONToStream(json_object *pStreamJ, StreamInterfaceInfoT * pPolicyStream); #endif // AHL_POLICY_UTILS_INCLUDE diff --git a/conf.d/project/ahl-audiok4a-config.json b/conf.d/project/ahl-audiok4a-config.json index f49c0be..74ab251 100644 --- a/conf.d/project/ahl-audiok4a-config.json +++ b/conf.d/project/ahl-audiok4a-config.json @@ -7,7 +7,6 @@ "audio_roles": [ { "name": "Warning", - "id": 0, "description": "Safety-relevant or critical alerts/alarms", "priority": 100, "output": [ @@ -23,7 +22,6 @@ }, { "name": "Guidance", - "id": 1, "description": "Important user information where user action is expected (e.g. navigation instruction)", "priority": 25, "output": [ @@ -39,7 +37,6 @@ }, { "name": "Notification", - "id": 2, "description": "HMI or else notifications (e.g. touchscreen events, speech recognition on/off,...)", "priority": 0, "output": [ @@ -57,7 +54,6 @@ }, { "name": "Communication", - "id": 3, "description": "Voice communications (e.g. handsfree, speech recognition)", "priority": 50, "output": [ @@ -76,7 +72,6 @@ }, { "name": "Entertainment", - "id": 4, "description": "Multimedia content (e.g. tuner, media player, etc.)", "priority": 0, "output": [ @@ -87,7 +82,6 @@ }, { "name": "System", - "id": 5, "description": "System level content or development", "priority": 100, "output": [ @@ -100,7 +94,6 @@ }, { "name": "Startup", - "id": 6, "description": "Early (startup) sound", "priority": 100, "output": [ @@ -113,7 +106,6 @@ }, { "name": "Shutdown", - "id": 7, "description": "Late (shutdown) sound", "priority": 100, "output": [ diff --git a/htdocs/audiohl.html b/htdocs/audiohl.html index a2cd247..0e153f5 100644 --- a/htdocs/audiohl.html +++ b/htdocs/audiohl.html @@ -59,23 +59,26 @@ <br> <ol> - <li><button onclick="callbinder('ahl-4a','get_sources', {audio_role:ar})">get_sources(audio_role)</button></li> - <li><button onclick="callbinder('ahl-4a','get_sinks', {audio_role:ar})">get_sinks(audio_role)</button></li> + <li><button onclick="callbinder('ahl-4a','get_endpoints', {audio_role:ar,endpoint_type:ep_type})">get_endpoints(audio_role,endpoint_type)</button></li> <li><button onclick="callbinder('ahl-4a','stream_open', {audio_role:ar,endpoint_type:ep_type})">stream_open(audio_role,endpoint_type)</button></li> <li><button onclick="callbinder('ahl-4a','stream_open', {audio_role:ar,endpoint_type:ep_type,endpoint_id:ep_id})">stream_open(audio_role,enpoint_type,endpoint_id)</button></li> <li><button onclick="callbinder('ahl-4a','stream_close', {stream_id:s_id})">stream_close(stream_id)</button></li> + <li><button onclick="callbinder('ahl-4a','stream_close', {})">stream_close()</button></li> <li><button onclick="callbinder('ahl-4a','get_stream_info', {stream_id:s_id})">get_stream_info(stream_id)</button></li> + <li><button onclick="callbinder('ahl-4a','set_stream_state', {stream_id:s_id})">set_stream_state(stream_id)</button></li> <li><button onclick="callbinder('ahl-4a','set_stream_state', {stream_id:s_id,state:st_state})">set_stream_state(stream_id,stream_state)</button></li> - <li><button onclick="callbinder('ahl-4a','set_stream_mute', {stream_id:s_id,mute:st_mute})">set_stream_mute(stream_id,mute)</button></li> - <li><button onclick="callbinder('ahl-4a','set_volume', {endpoint_type:ep_type,endpoint_id:ep_id,volume:vol_val})">set_volume(endpoint_type,endpoint_id,value)</button></li> - <li><button onclick="callbinder('ahl-4a','get_volume', {endpoint_type:ep_type,endpoint_id:ep_id})">get_volume(endpoint_type,endpoint_id)</button></li> - <li><button onclick="callbinder('ahl-4a','set_property', {endpoint_type:ep_type,endpoint_id:ep_id,property_name:p_name,value:prop_val})">set_property(endpoint_type,endpoint_id,property,value)</button></li> - <li><button onclick="callbinder('ahl-4a','get_property', {endpoint_type:ep_type,endpoint_id:ep_id,property_name:p_name})">get_property(endpoint_type,endpoint_id,property)</button></li> + <li><button onclick="callbinder('ahl-4a','set_stream_state', {stream_id:s_id,mute:st_mute})">set_stream_state(stream_id,mute)</button></li> + <li><button onclick="callbinder('ahl-4a','set_stream_state', {stream_id:s_id,state:st_state,mute:st_mute})">set_stream_state(stream_id,stream_state,mute)</button></li> + <li><button onclick="callbinder('ahl-4a','set_stream_state', {state:st_state,mute:st_mute})">set_stream_state(stream_state,mute)</button></li> + <li><button onclick="callbinder('ahl-4a','volume', {endpoint_type:ep_type,endpoint_id:ep_id,volume:vol_val})">volume(endpoint_type,endpoint_id,value)</button></li> + <li><button onclick="callbinder('ahl-4a','volume', {endpoint_type:ep_type,endpoint_id:ep_id})">volume(endpoint_type,endpoint_id)</button></li> + <li><button onclick="callbinder('ahl-4a','property', {endpoint_type:ep_type,endpoint_id:ep_id,property_name:p_name,value:prop_val})">property(endpoint_type,endpoint_id,property,value)</button></li> + <li><button onclick="callbinder('ahl-4a','property', {endpoint_type:ep_type,endpoint_id:ep_id,property_name:p_name})">property(endpoint_type,endpoint_id,property)</button></li> <li><button onclick="callbinder('ahl-4a','get_endpoint_info', {endpoint_type:ep_type,endpoint_id:ep_id})">get_endpoint_info(endpoint_type,endpoint_id)</button></li> <li><button onclick="callbinder('ahl-4a','get_list_actions', {audio_role:ar})">get_list_actions(audio_role)</button></li> <li><button onclick="callbinder('ahl-4a','post_action', {action_name:'emergency_brake',audio_role:ar,media_name:'Warning.wav'})">post_action(emergency_brake,audio_role,'Warning.wav')</button></li> - <li><button onclick="callbinder('ahl-4a','subscribe', {events:['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_action']})">subscribe(['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_action'])</button> - <li><button onclick="callbinder('ahl-4a','unsubscribe', {events:['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_action']})">unsubscribe(['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_action'])</button></li> + <li><button onclick="callbinder('ahl-4a','event_subscription', {events:['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_action'],subscribe:1})">subscribe(['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_action'])</button> + <li><button onclick="callbinder('ahl-4a','event_subscription', {events:['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_action'],subscribe:0})">unsubscribe(['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_action'])</button></li> </ol> <div id="main" style="visibility:hidden"> |