From a5da9bcbc5b3eb9a23cb97c8d3ef711c713e6c83 Mon Sep 17 00:00:00 2001 From: Kazumasa Mitsunari Date: Wed, 6 Jun 2018 10:59:44 +0900 Subject: Add High Level APIs * stream_close key : stream_id Calls disconnect function * set_stream_state key : stream_id key : mute value: 0 = unmute, 1 = mute Calls connect when mute is set to 0, then "stream_state_event" is published if success. This event is same as asyncSetSourceState like here. "event":"soundmanager\/stream_state_event", "data":{ "handle":value1, "sourceID":value2, "sourceState":"on|off|paused", "event_name":"ahl_stream_state_event", "stream_id":value1, "state_event":value } asyncSetSourceState is also published. And when mute is set to 1, then calls disconnect. "stream_id" is the returned value when application calls "stream_open". Change-Id: Ib8a032ed2c407605b6015441eccbb7660d9932d8 Signed-off-by: Kazumasa Mitsunari --- src/CMakeLists.txt | 1 + src/sm-def.h | 21 +++++++- src/sm-helper.h | 21 ++++---- src/sm-pending.c | 76 +++++++++++++++++++++++++++ src/sm-pending.h | 38 ++++++++++++++ src/soundmanager.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++------- src/soundmanager.h | 24 ++++----- 7 files changed, 289 insertions(+), 41 deletions(-) create mode 100644 src/sm-pending.c create mode 100644 src/sm-pending.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f157b7d..66e640f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,6 +26,7 @@ set(binding_sm_sources soundmanager.c sm-helper.c sm-error.c + sm-pending.c audiomanager_proxy.c dbus/audio_manager_interface.c) diff --git a/src/sm-def.h b/src/sm-def.h index 0bab88d..bd565d5 100644 --- a/src/sm-def.h +++ b/src/sm-def.h @@ -53,7 +53,7 @@ #define KEY_DOMAIN_ID "domainID" #define KEY_HANDLE "handle" -#define KEY_APPNAME "appname" +#define KEY_APPNAME "audio_role" #define KEY_RAMP "ramp" #define KEY_TIME "time" #define KEY_SOURCE_STATE "sourceState" @@ -72,11 +72,30 @@ #define KEY_RESPONSE "response" +#define SM_EVENT_VOLUME_CHANGED "volumeChanged" +#define SM_EVENT_NEW_MAIN_CONNECTION "newMainConnection" +#define SM_EVENT_REMOVED_MAIN_CONNECTION "removedMainConnection" +#define SM_EVENT_SINK_MUTE_STATE_CHANGED "sinkMuteStateChanged" +#define SM_EVENT_MAIN_CONNECTION_STATE_CHANGED "mainConnectionStateChanged" +/* Routing event*/ +#define SM_EVENT_SET_ROUTING_READY "setRoutingReady" +#define SM_EVENT_SET_ROUTING_RUNDOWN "setRoutingRundown" +#define SM_EVENT_ASYNC_CONNECT "asyncConnect" +#define SM_EVENT_ASYNC_SET_SOURCE_STATE "asyncSetSourceState" +#define SM_EVENT_ASYNC_DISCONNECT "asyncDisconnect" +#define SM_EVENT_STREAM_STATE_EVENT "stream_state_event" + #ifdef ENABLE_AGL_AHL #define KEY_AHL_AUDIO_ROLE "audio_role" #define KEY_AHL_ENDPOINT_ID "endpoint_id" #define KEY_AHL_ENDPOINT_TYPE "endpoint_type" #define KEY_AHL_REP_STREAM_ID "stream_id" +#define KEY_AHL_EVENT_NAME "event_name" +#define KEY_AHL_STATE_EVENT "state_event" +#define AHL_EVENT_NAME "ahl_stream_state_event" +#define KEY_AHL_MUTE "mute" +#define AHL_STREAM_UNMUTE 0 +#define AHL_STREAM_MUTE 1 typedef enum { ENDPOINT_SINK, diff --git a/src/sm-helper.h b/src/sm-helper.h index a6d09f1..ec35ad2 100644 --- a/src/sm-helper.h +++ b/src/sm-helper.h @@ -27,20 +27,21 @@ #define SEND_RESULT_NO_RESP(...) send_result_no_resp(__VA_ARGS__, __FUNCTION__) static const char* cmd_evlist[] = { - "volumeChanged", - "newMainConnection", - "removedMainConnection", - "sinkMuteStateChanged", - "mainConnectionStateChanged" + SM_EVENT_VOLUME_CHANGED, + SM_EVENT_NEW_MAIN_CONNECTION, + SM_EVENT_REMOVED_MAIN_CONNECTION, + SM_EVENT_SINK_MUTE_STATE_CHANGED, + SM_EVENT_MAIN_CONNECTION_STATE_CHANGED }; static const char* route_evlist[] = { /* Routing event*/ - "setRoutingReady", - "setRoutingRundown", - "asyncConnect", - "asyncSetSourceState", - "asyncDisconnect" + SM_EVENT_SET_ROUTING_READY, + SM_EVENT_SET_ROUTING_RUNDOWN, + SM_EVENT_ASYNC_CONNECT, + SM_EVENT_ASYNC_SET_SOURCE_STATE, + SM_EVENT_ASYNC_DISCONNECT, + SM_EVENT_STREAM_STATE_EVENT }; REQ_ERROR get_value_uint16(const struct afb_req request, const char *source, uint16_t *out_id); diff --git a/src/sm-pending.c b/src/sm-pending.c new file mode 100644 index 0000000..28a2382 --- /dev/null +++ b/src/sm-pending.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * 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. + */ +#include +#include "sm-pending.h" + +struct pending* add_pending(struct pending *list, int source_id) +{ + struct pending* pd; + + if(list == NULL){ + pd = malloc(sizeof(struct pending)); + if(NULL == pd){ + AFB_WARNING("memory allocation error"); + return NULL; + } + pd->source.sourceID = source_id; + pd->next = NULL; + return pd; + } + else{ + list->next = add_pending(list->next, source_id); + return list; + } +} + +struct pending* get_pending(struct pending *list, int source_id){ + if(list == NULL){ + return NULL; + } + if(list->source.sourceID == source_id){ + return list; + } + else{ + struct pending* pd = get_pending(list->next, source_id); + return pd; + } +} + +struct pending* del_pending(struct pending* list, int source_id){ + struct pending* tmp; + if(list == NULL){ + return NULL; + } + + if(list->source.sourceID == source_id){ + tmp = list->next; + free(list); + return tmp; + } + else{ + list->next = del_pending(list->next, source_id); + return list; + } +} + +void del_all_pendings(struct pending *list){ + struct pending* tmp; + if(list != NULL){ + tmp = list->next; + free(list); + del_all_pendings(tmp); + } +} \ No newline at end of file diff --git a/src/sm-pending.h b/src/sm-pending.h new file mode 100644 index 0000000..ab753cd --- /dev/null +++ b/src/sm-pending.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * 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 SOUNDMANAGER_PENDING_H +#define SOUNDMANAGER_PENDING_H + +#define _GNU_SOURCE +#define AFB_BINDING_VERSION 2 +#include + +struct pending_source{ + int sourceID; +}; +struct pending{ + struct pending_source source; + struct pending* next; +}; + + +struct pending* add_pending(struct pending *list, int source_id); +struct pending* get_pending(struct pending *list, int source_id); +struct pending* del_pending(struct pending* list, int source_id); +void del_all_pendings(struct pending *list); + +#endif //SOUNDMANAGER_PENDING_H \ No newline at end of file diff --git a/src/soundmanager.c b/src/soundmanager.c index 1d10a1e..cb2815d 100644 --- a/src/soundmanager.c +++ b/src/soundmanager.c @@ -26,12 +26,15 @@ #include "sm-def.h" #include "sm-error.h" #include "sm-helper.h" +#include "sm-pending.h" struct event{ char* name; struct afb_event* event; }; +static struct pending* pending_list = NULL; + static int SOUNDMANAGER_DOMAIN_ID; static struct event command_event_list[COMMAND_EVENT_NUM]; static struct event routing_event_list[ROUTING_EVENT_NUM]; @@ -48,7 +51,7 @@ static struct afb_event ev_set_routing_rundown; static struct afb_event ev_async_connect; static struct afb_event ev_async_disconnect; static struct afb_event ev_async_set_source_state; - +static struct afb_event ev_stream_state_event; /* Client context */ typedef struct source { @@ -531,8 +534,8 @@ void registerSource(struct afb_req request) const gchar* name = afb_req_value(request, KEY_APPNAME); /* s */ if(!name) { - char* info = "Must specify the name. Please input json arg such as {\"appname\":\"radio\"}"; - afb_req_fail(request, NULL, info); + char* info = "Must specify the name. Please input json arg such as {\"audio_role\":\"radio\"}"; + afb_req_fail(request, "wrong-request", info); return; } if(REQ_OK != get_value_uint16(request, KEY_SOURCE_CLASS_ID, &source_class_id)){ @@ -698,7 +701,7 @@ void unsubscribe(struct afb_req request) #ifdef ENABLE_AGL_AHL -void streamOpen(struct afb_req request){ +void stream_open(struct afb_req request){ AFB_DEBUG("call %s", __FUNCTION__); // register audio role and endpoint // get audio_role @@ -734,7 +737,7 @@ void streamOpen(struct afb_req request){ AFB_DEBUG("Get response success: %s", json_object_to_json_string_ext(j_resp, JSON_C_TO_STRING_PRETTY)); json_object_object_get_ex(j_resp, KEY_SOURCE_ID, &j_sid); json_object_object_get_ex(j_resp, KEY_ERROR, &j_err); - // requestSurface must return sourceID and error then I don't check whether sid and ret is in json_object. + // registerSource must return sourceID and error then I don't check whether sid and ret is in json_object. sid = json_object_get_int(j_sid); ret = json_object_get_int(j_err); json_object_put(j_resp); @@ -742,7 +745,7 @@ void streamOpen(struct afb_req request){ json_object_put(j_err); json_object_put(response); }else { - afb_req_fail(request, NULL, "Failed streamOpen"); + afb_req_fail(request, "unknown-error", "Failed stream_open"); json_object_put(response); return; } @@ -753,22 +756,112 @@ void streamOpen(struct afb_req request){ KEY_AHL_REP_STREAM_ID, sid); const char* info = get_response_audiomanager_massage_error(ret); create_client_context(request, sid, endpoint_id, endpoint_type); + int index = sm_search_routing_event_name_index(SM_EVENT_STREAM_STATE_EVENT); + afb_req_subscribe(request, *routing_event_list[index].event); afb_req_success(request, res, info); } -void streamClose(struct afb_req request){ -// TODO : wtite function -/* smClientCtxt* ctxt = afb_req_context_get(request); +void stream_close(struct afb_req request){ + ErrorCode ec; + gint16 source_id = 0; + smClientCtxt* ctxt = afb_req_context_get(request); + if(NULL == ctxt){ + AFB_ERROR("Context is not registered"); + afb_req_fail(request, "wrong-request", "call stream_open at first"); + return; + } + if(REQ_OK != get_value_uint16(request, KEY_SOURCE_ID, &source_id)){ + if(REQ_OK != get_value_uint16(request, KEY_AHL_REP_STREAM_ID, &source_id)){ + afb_req_fail(request, "wrong-request", "Unable to find sourceID"); + return; + } + } + if(source_id != ctxt->source.sourceID){ + AFB_ERROR("requested sourceID is %d, but your sourceID is %d", source_id, ctxt->source.sourceID); + afb_req_fail(request, "wrong-request", "sourceID is not yours"); + return; + } if(ctxt->source.mainConnectionID > 0){ - json_object* jreq = json_object_new_object(); - json_object_object_add(jreq, KEY_MAIN_CONNECTION_ID, ctxt->source.mainConnectionID); - afb_service_call_sync("soundmanager", "disconnect", jreq, &response); - json_object_object_get_ex(response, KEY_RESPONSE, &j_resp); - } */ + pending_list = add_pending(pending_list, source_id); + ec = am_proxy_disconnect(ctxt->source.mainConnectionID); + if(!SEND_RESULT(ec, request)) { + del_pending(pending_list, source_id); + return; + } + ctxt->source.mainConnectionID = -1; + } + else{ + AFB_NOTICE("Stream %d doesn't have connection. Ignore disconnect", ctxt->source.sourceID); + return; + } + /*create response json object*/ + struct json_object *res = json_object_new_object(); + sm_add_object_to_json_object_func(res, __FUNCTION__, 2, + KEY_ERROR, ec); + const char* info = get_response_audiomanager_massage_error(ec); + afb_req_success(request, res, info); } -void setStreamState(struct afb_req request){ -// TODO : wtite function +void set_stream_state(struct afb_req request){ + gint16 source_id = 0; + int main_connection_id = -1; + ErrorCode ec = OK; + int mute_state; + smClientCtxt* ctxt = afb_req_context_get(request); + if(NULL == ctxt){ + AFB_ERROR("Context is not registered"); + afb_req_fail(request, "wrong-request", "call stream_open at first"); + return; + } + + // get sourceID from request + if(REQ_OK != get_value_uint16(request, KEY_SOURCE_ID, &source_id)){ + if(REQ_OK != get_value_uint16(request, KEY_AHL_REP_STREAM_ID, &source_id)){ + afb_req_fail(request, "wrong-request", "Unable to find sourceID"); + return; + } + } + if(source_id != ctxt->source.sourceID){ + AFB_ERROR("requested sourceID is %d, but your sourceID is %d", source_id, ctxt->source.sourceID); + afb_req_fail(request, "wrong-request", "sourceID is not yours"); + return; + } + if(REQ_OK != get_value_int32(request, KEY_AHL_MUTE, &mute_state)){ + mute_state = AHL_STREAM_UNMUTE; + AFB_INFO("Mute state is not set. Set mute state %d(unmute) as default.", mute_state); + } + AFB_INFO("souceID: %d , mute : %d", source_id, mute_state); + + if(AHL_STREAM_MUTE == mute_state){ + if(ctxt->source.mainConnectionID > 0){ + pending_list = add_pending(pending_list, source_id); + ec = am_proxy_disconnect(ctxt->source.mainConnectionID); + if(!SEND_RESULT(ec, request)){ + del_pending(pending_list, source_id); + return; + } + ctxt->source.mainConnectionID = -1; + } + else{ + AFB_NOTICE("Stream %d doesn't have connection. Ignore disconnect", ctxt->source.sourceID); + ec = ACTION_IMPOSSIBLE; + } + } + else{ + pending_list = add_pending(pending_list, source_id); + ec = am_proxy_connect(source_id, ctxt->sink.sinkID, &main_connection_id); + ctxt->source.mainConnectionID = main_connection_id; + if(!SEND_RESULT(ec, request)) { + del_pending(pending_list, source_id); + return; + } + } + /*create response json object*/ + struct json_object *res = json_object_new_object(); + sm_add_object_to_json_object_func(res, __FUNCTION__, 2, + KEY_ERROR, ec); + const char* info = get_response_audiomanager_massage_error(ec); + afb_req_success(request, res, info); } #endif @@ -909,13 +1002,27 @@ static void on_async_set_sink_volume(int handle, int sinkID, static void on_async_set_source_state(int handle, int sourceID, int sourceState) { - AFB_DEBUG( "%s called", __FUNCTION__); + AFB_INFO( "%s called. handle : %d, sourceID: %d, state: %s", __FUNCTION__, handle, sourceID, get_source_state_key(sourceState)); struct json_object* ev_obj = json_object_new_object(); const char* ss_key = get_source_state_key(sourceState); sm_add_object_to_json_object(ev_obj, 4, KEY_HANDLE, handle, KEY_SOURCE_ID, sourceID); json_object_object_add(ev_obj, KEY_SOURCE_STATE, json_object_new_string(ss_key)); +#ifdef ENABLE_AGL_AHL + struct pending* pd = get_pending(pending_list, sourceID); + if(pd != NULL){ + int ack_ok = 0; + am_proxy_ack_set_source_state(handle, ack_ok); + pending_list = del_pending(pending_list, sourceID); + } + json_object_object_add(ev_obj, KEY_AHL_EVENT_NAME, json_object_new_string(AHL_EVENT_NAME)); + sm_add_object_to_json_object(ev_obj, 4, + KEY_AHL_REP_STREAM_ID, sourceID, + KEY_AHL_STATE_EVENT, sourceState); + json_object_get(ev_obj); + afb_event_push(ev_stream_state_event, ev_obj); +#endif afb_event_push(ev_async_set_source_state, ev_obj); /* Applications must return ackSetSourceState to look sourceID, then Sound Manager doen't return ackSetSourceState */ /*audiomanager_routinginterface_call_ack_set_source_state_sync( @@ -962,7 +1069,6 @@ static void create_client_context(afb_req request, guint16 source_id, guint16 si ctxt->sink.endpointID = sink_id; ctxt->sink.sinkID = sink_id; ctxt->sink.endpointType = endpoint_type; - ctxt->events.asyncSetSourceState = afb_daemon_make_event("asyncSetSourceState"); afb_req_context_set(request, ctxt, on_client_context_terminated); } @@ -1045,6 +1151,9 @@ int sm_init() ev_async_connect = afb_daemon_make_event(route_evlist[2]); ev_async_set_source_state = afb_daemon_make_event(route_evlist[3]); ev_async_disconnect = afb_daemon_make_event(route_evlist[4]); +#ifdef ENABLE_AGL_AHL + ev_stream_state_event = afb_daemon_make_event(route_evlist[5]); +#endif routing_event_list[0].name = strdup(route_evlist[0]); routing_event_list[0].event = &ev_set_routing_ready; @@ -1056,6 +1165,10 @@ int sm_init() routing_event_list[3].event = &ev_async_set_source_state; routing_event_list[4].name = strdup(route_evlist[4]); routing_event_list[4].event = &ev_async_disconnect; +#ifdef ENABLE_AGL_AHL + routing_event_list[5].name = strdup(route_evlist[5]); + routing_event_list[5].event = &ev_stream_state_event; +#endif am_event callback = { .on_new_main_connection = on_new_main_connection, diff --git a/src/soundmanager.h b/src/soundmanager.h index 3666473..f79643b 100644 --- a/src/soundmanager.h +++ b/src/soundmanager.h @@ -328,7 +328,7 @@ void unsubscribe(struct afb_req request); * #### Note * TODO : write note */ -void streamOpen(struct afb_req req); +void stream_open(struct afb_req req); /** * Application High Level API for AGL @@ -344,23 +344,23 @@ void streamOpen(struct afb_req req); * #### Note * TODO : write note */ -void streamClose(struct afb_req req); +void stream_close(struct afb_req req); /** * Application High Level API for AGL * This function set stream state. - * This function set the availability and calls connect function of Audio Manager - * and returns the result of policy check. + * This function set the availability and calls connect function of Audio Manager. + * Policy check result will be notified by event.. * * #### Parameters * - stream_id : Stream id which is returned in stream_open. - * - mute : Stream state as int + * - mute : Stream state as int. 0 is unmute, and 1 is mute. Sound manager set this parameter as unmute as default * #### Return * - error : Error status number. If error is 0, it means the request is accepted, otherwise error message is attached with error code in reply message. * #### Note - * TODO : write note + * After set unmute, then 'asyncSetSourceState' and 'stream_state_event' is pushed. These events contains information of state judged by AudioManager. */ -void setStreamState(struct afb_req req); +void set_stream_state(struct afb_req req); /* ********** Event list from Sound Manager ********** @@ -379,12 +379,12 @@ void setStreamState(struct afb_req req); const struct afb_verb_v2 binding_verbs[]= { #ifdef ENABLE_AGL_AHL // High Level API of AGL -{ .verb = "stream_open", .callback = streamOpen, .auth = NULL, - .info = "Open stream." , .session = AFB_SESSION_NONE}, -{ .verb = "stream_close", .callback = streamClose, .auth = NULL, +{ .verb = "stream_open", .callback = stream_open, .auth = NULL, + .info = "Open stream." , .session = AFB_SESSION_NONE}, +{ .verb = "stream_close", .callback = stream_close, .auth = NULL, .info = "Close stream" , .session = AFB_SESSION_NONE}, -{ .verb = "set_stream_state", .callback = setStreamState, .auth = NULL, - .info = "Set stream state" , .session = AFB_SESSION_NONE}, +{ .verb = "set_stream_state", .callback = set_stream_state, .auth = NULL, + .info = "Set stream state" , .session = AFB_SESSION_NONE}, #endif // Adaption API of Audio Manager { .verb = "connect", .callback = connect, .auth = NULL, -- cgit 1.2.3-korg