/* * 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 #include #include "audiomanager_proxy.h" #include "sm-helper.h" #include "dbus/audio_manager_interface.h" static AudiomanagerCommandinterface *am_cmd_bus; static AudiomanagerRoutinginterface *am_route_bus; static AudiomanagerRoutingSoundmanager *sm_adapter; static AudiomanagerRoutingSoundmanagerIface* sm_itf; static GDBusConnection* system_conn = NULL; static am_event _am_event = {0}; static am_instruction _am_instruction = {0}; static void *dbus_event_loop_run(void *args); /* ********** Callback Function invoked by Audio Manager Command Interface********** */ static void _on_new_main_connection(AudiomanagerCommandinterface* interface, GVariant* mainConnection) { AFB_DEBUG("%s is called",__FUNCTION__); guint16 mcid, srcid, sinkid; gint16 delay, constate; g_variant_get( mainConnection,"(qqqnn)", &mcid, &srcid, &sinkid, &delay, &constate); _am_event.on_new_main_connection(mcid, srcid, sinkid, delay, constate); } static void _on_removed_main_connection( AudiomanagerCommandinterface* interface, guint16 mainConnectionID) { AFB_DEBUG("%s is called",__FUNCTION__); _am_event.on_removed_main_connection(mainConnectionID); } static void _on_main_connection_state_changed( AudiomanagerCommandinterface* interface, guint16 connectionID, gint16 connectionState) { AFB_DEBUG("%s is called",__FUNCTION__); _am_event.on_main_connection_state_changed(connectionID, connectionState); } static void _on_volume_changed( AudiomanagerCommandinterface* interface, guint16 sinkID, gint16 volume) { AFB_DEBUG("%s is called",__FUNCTION__); _am_event.on_volume_changed(sinkID, volume); } static void _on_sink_mute_state_changed( AudiomanagerCommandinterface* interface, guint16 sinkID, gint16 mute) { AFB_DEBUG("%s is called",__FUNCTION__); _am_event.on_sink_mute_state_changed(sinkID, mute); } /* ********** Callback Function invoked by Audio Manager Routing Interface********** */ static void _on_set_routing_ready( AudiomanagerRoutinginterface* interface) { AFB_DEBUG("%s is called",__FUNCTION__); _am_event.on_set_routing_ready(); } static void _on_set_routing_rundown( AudiomanagerRoutinginterface* interface) { AFB_DEBUG("%s is called",__FUNCTION__); _am_event.on_set_routing_rundown(); } static ErrorCode check_send_error(GError *err, guint16 result){ if(err != NULL){ return UNABLE_SEND; } return result; } static gboolean _on_async_abort( AudiomanagerRoutingSoundmanager *object, GDBusMethodInvocation *invocation, guint16 arg_handle) { AFB_DEBUG( "%s called", __FUNCTION__); _am_instruction.on_async_abort((int)arg_handle); return TRUE; } static gboolean _on_async_connect( AudiomanagerRoutingSoundmanager *object, GDBusMethodInvocation *invocation, guint16 arg_handle, guint16 arg_connectionID, guint16 arg_sourceID, guint16 arg_sinkID, gint arg_connectionFormat) { AFB_DEBUG( "%s called", __FUNCTION__); int handle = (int)arg_handle; int connection = (int)arg_connectionID; int source = (int)arg_sourceID; int sink = (int)arg_sinkID; int connection_format = (int)arg_connectionFormat; _am_instruction.on_async_connect(handle, connection, source, sink, connection_format); int ack_ok = 0; ErrorCode ec = am_proxy_ack_connect(handle, connection, ack_ok); if(ec == UNABLE_SEND) { AFB_ERROR( "Can't send ackConnect", __FUNCTION__); return FALSE; } return TRUE; } static gboolean _on_async_disconnect( AudiomanagerRoutingSoundmanager *object, GDBusMethodInvocation *invocation, guint16 arg_handle, guint16 arg_connectionID) { AFB_DEBUG( "%s called", __FUNCTION__); int handle = (int)arg_handle; int connection = (int)arg_connectionID; _am_instruction.on_async_disconnect(handle, connection); GError* err = NULL; int ack_ok = 0; ErrorCode ec = am_proxy_ack_disconnect(handle, connection, ack_ok); if(ec == UNABLE_SEND) { AFB_ERROR( "Can't send ack to sound manager adapter %s", __FUNCTION__); return FALSE; } return TRUE; } static gboolean _on_async_set_sink_volume( AudiomanagerRoutingSoundmanager *object, GDBusMethodInvocation *invocation, guint16 arg_handle, guint16 arg_sinkID, gint16 arg_volume, gint16 arg_ramp, guint16 arg_time) { AFB_DEBUG( "%s called", __FUNCTION__); int handle = (int)arg_handle; int sink = (int)arg_sinkID; int volume = (int)arg_volume; int ramp = (int)arg_ramp; int time = (int)arg_time; GError* err = NULL; int ack_ok = 0; ErrorCode ec = audiomanager_routinginterface_call_ack_set_sink_volume_sync( am_route_bus, arg_handle, arg_volume, ack_ok, NULL, &err); if(ec == UNABLE_SEND);{ AFB_ERROR( "Can't send ack to sound manager adapter %s", __FUNCTION__); return FALSE; } return TRUE; } static gboolean _on_async_set_source_state( AudiomanagerRoutingSoundmanager *object, GDBusMethodInvocation *invocation, guint16 arg_handle, guint16 arg_sourceID, gint arg_sourceState) { int handle = (int)arg_handle; int source = (int)arg_sourceID; int source_state = (int)arg_sourceState; AFB_DEBUG( "%s called", __FUNCTION__); _am_instruction.on_async_set_source_state(handle, source, source_state); return TRUE; } /* ********** API which calls audio manager API ********** */ ErrorCode am_proxy_connect(int source, int sink, int *main_connection_id){ if(is_range_over_guint16(source) == OUT_RANGE || is_range_over_guint16(sink) == OUT_RANGE){ return OUT_RANGE; } AFB_DEBUG("Call %s: source:%d sink:%d", __FUNCTION__, source, sink); guint16 connection_id = 0, ret = 0; ErrorCode ec; GError *err = NULL; assert(am_cmd_bus != NULL); audiomanager_commandinterface_call_connect_sync( am_cmd_bus, (guint16)source, (guint16)sink, &ret, &connection_id, NULL, &err); *main_connection_id = (int)connection_id; ec = check_send_error(err, ret); return ec; } ErrorCode am_proxy_disconnect(int main_connection_id){ AFB_DEBUG("Call %s: mainConnectionID:%d", __FUNCTION__, main_connection_id); if(is_range_over_guint16(main_connection_id) == OUT_RANGE){ return OUT_RANGE; } guint16 ret = 0; ErrorCode ec; GError *err = NULL; assert(am_cmd_bus != NULL); audiomanager_commandinterface_call_disconnect_sync( am_cmd_bus, (guint16)main_connection_id, &ret, NULL, &err); ec = check_send_error(err, ret); return ec; } ErrorCode am_proxy_set_volume(int sink, int volume){ AFB_DEBUG("Call %s: sink:%d volume:%d", __FUNCTION__, sink, volume); if(is_range_over_guint16(sink) == OUT_RANGE && is_range_over_gint16(volume) == OUT_RANGE){ return OUT_RANGE; } guint16 ret = 0; ErrorCode ec; GError *err = NULL; assert(am_cmd_bus != NULL); audiomanager_commandinterface_call_set_volume_sync( am_cmd_bus, (guint16)sink, (gint16)volume, &ret, NULL, &err); ec = check_send_error(err, ret); return ec; } ErrorCode am_proxy_volume_step(int sink, int volume){ AFB_DEBUG("Call %s: sink:%d volume:%d", __FUNCTION__, sink, volume); if(is_range_over_guint16(sink) == OUT_RANGE && is_range_over_gint16(volume) == OUT_RANGE){ return OUT_RANGE; } guint16 ret = 0; ErrorCode ec; GError *err = NULL; assert(am_cmd_bus != NULL); audiomanager_commandinterface_call_volume_step_sync( am_cmd_bus, (guint16)sink, (gint16)volume, &ret, NULL, &err); ec = check_send_error(err, ret); return ec; } ErrorCode am_proxy_set_sink_mute_state(int sink, int mute_state){ AFB_DEBUG("Call %s: sink:%d mute_state:%d", __FUNCTION__, sink, mute_state); if(is_range_over_guint16(sink) == OUT_RANGE || is_range_over_gint16(mute_state) == OUT_RANGE){ return OUT_RANGE; } guint16 ret = 0; ErrorCode ec; GError *err = NULL; assert(am_cmd_bus != NULL); audiomanager_commandinterface_call_set_sink_mute_state_sync( am_cmd_bus, (guint16)sink, (gint16)mute_state, &ret, NULL, &err); ec = check_send_error(err, ret); return ec; } ErrorCode am_proxy_get_list_main_connections(GVariant** connection_list){ guint16 ret = 0; ErrorCode ec; GError *err = NULL; AFB_DEBUG("Call %s", __FUNCTION__); assert(am_cmd_bus != NULL); audiomanager_commandinterface_call_get_list_main_connections_sync( am_cmd_bus, &ret, connection_list, NULL, &err ); ec = check_send_error(err, ret); return ec; } ErrorCode am_proxy_get_list_main_sources(GVariant** source_list){ guint16 ret = 0; ErrorCode ec; GError *err = NULL; AFB_DEBUG("Call %s", __FUNCTION__); assert(am_cmd_bus != NULL); audiomanager_commandinterface_call_get_list_main_sources_sync( am_cmd_bus, &ret, source_list, NULL, &err ); ec = check_send_error(err, ret); return ec; } ErrorCode am_proxy_get_list_main_sinks(GVariant** sink_list){ guint16 ret = 0; ErrorCode ec; GError *err = NULL; AFB_DEBUG("Call %s", __FUNCTION__); assert(am_cmd_bus != NULL); audiomanager_commandinterface_call_get_list_main_sinks_sync( am_cmd_bus, &ret, sink_list, NULL, &err ); ec = check_send_error(err, ret); return ec; } ErrorCode am_proxy_ack_connect(int handle, int connection_id, int usr_err){ AFB_DEBUG("Call %s handle:%d connectionID:%d error:%d", __FUNCTION__, handle, connection_id, usr_err); if(is_range_over_guint16(handle) == OUT_RANGE || is_range_over_guint16(connection_id) == OUT_RANGE || is_range_over_guint16(usr_err) == OUT_RANGE){ return OUT_RANGE; } assert(am_route_bus != NULL); GError *err = NULL; audiomanager_routinginterface_call_ack_connect_sync( am_route_bus, (guint16)handle, (guint16)connection_id, (guint16)usr_err, NULL, &err); return OK; } ErrorCode am_proxy_ack_disconnect(int handle, int connection_id, int usr_err){ AFB_DEBUG("Call %s handle:%d connectionID:%d error:%d", __FUNCTION__, handle, connection_id, usr_err); if(is_range_over_guint16(handle) == OUT_RANGE || is_range_over_guint16(connection_id) == OUT_RANGE || is_range_over_guint16(usr_err) == OUT_RANGE){ return OUT_RANGE; } assert(am_route_bus != NULL); GError *err = NULL; audiomanager_routinginterface_call_ack_disconnect_sync( am_route_bus, (guint16)handle, (guint16)connection_id, (guint16)usr_err,NULL, &err); return OK; } ErrorCode am_proxy_ack_set_source_state(int handle, int usr_err){ AFB_DEBUG("Call %s handle:%d error:%d", __FUNCTION__, handle, usr_err); if(is_range_over_guint16(handle) == OUT_RANGE || is_range_over_guint16(usr_err) == OUT_RANGE){ return OUT_RANGE; } assert(am_route_bus != NULL); GError *err = NULL; audiomanager_routinginterface_call_ack_set_source_state_sync( am_route_bus, (guint16)handle, (guint16)usr_err, NULL, &err); return OK; } ErrorCode am_proxy_register_source(GVariant *source_data, int *source){ AFB_DEBUG("Call %s", __FUNCTION__); assert(am_route_bus != NULL); guint16 ret = 0, g_source = 0; ErrorCode ec; GError *err = NULL; audiomanager_routinginterface_call_register_source_sync( am_route_bus, source_data, &g_source, &ret, NULL, &err); *source = (int)g_source; ec = check_send_error(err, ret); return ec; } ErrorCode am_proxy_deregister_source(int source){ AFB_DEBUG("Call %s source:%d", __FUNCTION__, source); if(is_range_over_guint16(source) == OUT_RANGE){ return OUT_RANGE; } guint16 ret = 0; ErrorCode ec; GError *err = NULL; assert(am_route_bus != NULL); audiomanager_routinginterface_call_deregister_source_sync( am_route_bus, (guint16)source, &ret, NULL, &err ); ec = check_send_error(err, ret); return ec; } ErrorCode am_proxy_register_domain(GVariant* domain_data, int *domain){ guint16 ret = 0, g_domain = 0; ErrorCode ec; GError *err = NULL; assert(am_route_bus != NULL); assert(domain_data); AFB_DEBUG("Call %s", __FUNCTION__); audiomanager_routinginterface_call_register_domain_sync( am_route_bus, domain_data, SOUND_MANAGER_BUS_NAME, SOUND_MANAGER_PATH, SOUND_MANAGER_RETURN_INTERFACE, &g_domain, &ret, NULL, &err); *domain = (int)g_domain; ec = check_send_error(err, ret); return ec; } GVariant* create_domain_data(struct domain_data* domain){ /* * This function doesn't check the range of guint32 or gint16 * Soundmanager must keep the input data is between the size of type. * This function is considered to be only used by soundmanager. */ GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE ("(qsssbbn)")); g_variant_builder_add (&builder, "q", (guint16)domain->domainID); g_variant_builder_add (&builder, "s", domain->name); g_variant_builder_add (&builder, "s", domain->busname); g_variant_builder_add (&builder, "s", domain->nodename); g_variant_builder_add (&builder, "b", (gboolean)domain->early); g_variant_builder_add (&builder, "b", (gboolean)domain->complete); g_variant_builder_add (&builder, "n", (gint16)domain->state); AFB_DEBUG("create domainData"); return g_variant_builder_end (&builder); } GVariant* create_source_data(int sourceID, int domainID, const char* appname, int sourceClassID, int sourceState, int volume, bool visible, struct availability_s availables, int interrupt, struct sound_property_s soundPropertyList, int connectionFormatList, struct main_sound_property_s mainPropertyList, struct notification_config_s NConfRouting, struct notification_config_s NConfCommand) { GVariantBuilder builder; if(is_range_over_guint16(sourceID) == OUT_RANGE || is_range_over_guint16(domainID) == OUT_RANGE || is_range_over_gint16(volume) == OUT_RANGE || is_range_over_guint16(interrupt) == OUT_RANGE || is_range_over_gint16(soundPropertyList.value) == OUT_RANGE || is_range_over_gint16(mainPropertyList.value) == OUT_RANGE || is_range_over_gint16(NConfRouting.parameter) == OUT_RANGE || is_range_over_gint16(NConfCommand.parameter) == OUT_RANGE) { return NULL; } g_variant_builder_init (&builder, G_VARIANT_TYPE ("(qqsqinb(ii)qa(in)aia(in)a(iin)a(iin))")); g_variant_builder_add (&builder, "q", (guint16)sourceID); g_variant_builder_add (&builder, "q", (guint16)domainID); g_variant_builder_add (&builder, "s", appname); g_variant_builder_add (&builder, "q", (guint16)sourceClassID); g_variant_builder_add (&builder, "i", (gint32)sourceState); g_variant_builder_add (&builder, "n", (gint16)volume); g_variant_builder_add (&builder, "b", (gboolean)visible); g_variant_builder_add (&builder, "(ii)", (gint32)availables.availability, (gint32)availables.avalilable_reason); g_variant_builder_add (&builder, "q", (guint16)interrupt); g_variant_builder_open(&builder, G_VARIANT_TYPE("a(in)")); g_variant_builder_open(&builder, G_VARIANT_TYPE("(in)")); g_variant_builder_add (&builder, "i", (gint32)soundPropertyList.type); g_variant_builder_add (&builder, "n", (gint16)soundPropertyList.value); g_variant_builder_close(&builder); g_variant_builder_close (&builder); g_variant_builder_open(&builder, G_VARIANT_TYPE("ai")); g_variant_builder_add (&builder, "i", (gint32)connectionFormatList); g_variant_builder_close (&builder); g_variant_builder_open(&builder, G_VARIANT_TYPE("a(in)")); g_variant_builder_open(&builder, G_VARIANT_TYPE("(in)")); g_variant_builder_add (&builder, "i", (gint32)mainPropertyList.type); g_variant_builder_add (&builder, "n", (gint16)mainPropertyList.value); g_variant_builder_close (&builder); g_variant_builder_close(&builder); g_variant_builder_open(&builder, G_VARIANT_TYPE("a(iin)")); g_variant_builder_open(&builder, G_VARIANT_TYPE("(iin)")); g_variant_builder_add (&builder, "i", (gint32)NConfRouting.type); g_variant_builder_add (&builder, "i", (gint32)NConfRouting.status); g_variant_builder_add (&builder, "n", (gint16)NConfRouting.parameter); g_variant_builder_close(&builder); g_variant_builder_close (&builder); g_variant_builder_open(&builder, G_VARIANT_TYPE("a(iin)")); g_variant_builder_open(&builder, G_VARIANT_TYPE("(iin)")); g_variant_builder_add (&builder, "i", (gint32)NConfCommand.type); g_variant_builder_add (&builder, "i", (gint32)NConfCommand.status); g_variant_builder_add (&builder, "n", (gint16)NConfCommand.parameter); g_variant_builder_close(&builder); g_variant_builder_close (&builder); AFB_DEBUG("create sourceData"); return g_variant_builder_end (&builder); } static void *dbus_event_loop_run(void *args) { GMainLoop* loop = g_main_loop_new(NULL, FALSE); g_main_loop_run(loop); } ErrorCode initialize_proxy(){ if(am_cmd_bus || am_route_bus) { return NOT_INITIALIZED; } am_cmd_bus = audiomanager_commandinterface_proxy_new_for_bus_sync( G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, AM_NAME, AM_CMD_PATH, NULL, NULL ); if(!am_cmd_bus){ AFB_ERROR("Failed to create command interface"); } am_route_bus = audiomanager_routinginterface_proxy_new_for_bus_sync( G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, AM_NAME, AM_ROUTE_PATH, NULL, NULL ); if(!am_route_bus){ AFB_ERROR("Failed to create routing interface"); } return (am_cmd_bus && am_route_bus) ? OK : UNABLE_SEND; } void close_proxy(){ if(am_cmd_bus) free(am_cmd_bus); if(am_route_bus) free(am_route_bus); } void set_event_callback(const am_event* callback){ assert(am_cmd_bus != NULL); assert(am_route_bus != NULL); /* initialize signal from audio manager command interface */ _am_event.on_main_connection_state_changed = callback->on_main_connection_state_changed; _am_event.on_new_main_connection = callback->on_new_main_connection; _am_event.on_removed_main_connection = callback->on_removed_main_connection; _am_event.on_set_routing_ready = callback->on_set_routing_ready; _am_event.on_set_routing_rundown = callback->on_set_routing_rundown; _am_event.on_sink_mute_state_changed = callback->on_sink_mute_state_changed; _am_event.on_volume_changed = callback->on_volume_changed; g_signal_connect(am_cmd_bus, "volume_changed", G_CALLBACK(_on_volume_changed), NULL); g_signal_connect(am_cmd_bus, "new_main_connection", G_CALLBACK(_on_new_main_connection), NULL); g_signal_connect(am_cmd_bus, "removed_main_connection", G_CALLBACK(_on_removed_main_connection),NULL); g_signal_connect(am_cmd_bus, "sink_mute_state_changed", G_CALLBACK(_on_sink_mute_state_changed),NULL); g_signal_connect(am_cmd_bus, "main_connection_state_changed", G_CALLBACK(_on_main_connection_state_changed), NULL); g_signal_connect(am_route_bus, "set_routing_ready", G_CALLBACK(_on_set_routing_ready), NULL); g_signal_connect(am_route_bus, "set_routing_rundown", G_CALLBACK(_on_set_routing_rundown), NULL); } static void on_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data){ GError* error = NULL; AFB_DEBUG("%s is called", __FUNCTION__); sm_adapter = audiomanager_routing_soundmanager_skeleton_new(); sm_itf = AUDIOMANAGER_ROUTING_SOUNDMANAGER_GET_IFACE(sm_adapter); /* initialize sound manager adapter */ sm_itf->handle_async_abort = _on_async_abort; sm_itf->handle_async_connect = _on_async_connect; sm_itf->handle_async_disconnect = _on_async_disconnect; sm_itf->handle_async_set_sink_volume = _on_async_set_sink_volume; sm_itf->handle_async_set_source_state = _on_async_set_source_state; int sigret = g_signal_connect(sm_adapter, "handle-async-abort", G_CALLBACK(_on_async_abort),NULL); sigret = g_signal_connect(sm_adapter, "handle-async-connect", G_CALLBACK(_on_async_connect),NULL); sigret = g_signal_connect(sm_adapter, "handle-async-disconnect", G_CALLBACK(_on_async_disconnect),NULL); sigret = g_signal_connect(sm_adapter, "handle-async-set-sink-volume", G_CALLBACK(_on_async_set_sink_volume),NULL); sigret = g_signal_connect(sm_adapter, "handle-async-set-source-state", G_CALLBACK(_on_async_set_source_state),NULL); AFB_DEBUG("register %s", AUDIOMANAGER_ROUTING_SOUNDMANAGER_SKELETON(sm_adapter)); gboolean rc = g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(sm_adapter), connection, SOUND_MANAGER_PATH, &error); AFB_DEBUG("export soundmanager path result: %d", rc); if (FALSE == rc) { AFB_ERROR( "failed to export"); g_error_free(error); g_object_unref(connection); } } static void on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data){ AFB_DEBUG("%s is called", __FUNCTION__); } static void on_name_lost(GDBusConnection *connection, const gchar *name, gpointer user_data){ AFB_DEBUG("bus name is lost"); // TODO: soundmanager should register current status? } ErrorCode open_soundmanager_interface(const am_instruction *callback){ _am_instruction.on_async_abort = callback->on_async_abort; _am_instruction.on_async_connect = callback->on_async_connect; _am_instruction.on_async_disconnect = callback->on_async_disconnect; _am_instruction.on_async_set_sink_volume = callback->on_async_set_sink_volume; _am_instruction.on_async_set_source_state = callback->on_async_set_source_state; guint owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, SOUND_MANAGER_BUS_NAME, G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired, on_name_acquired, on_name_lost, NULL/* (gpointer)callback */, NULL); // see https://developer.gnome.org/gio/stable/gio-Owning-Bus-Names.html#g-bus-own-name pthread_t thread_id; int ret = pthread_create(&thread_id, NULL, dbus_event_loop_run, NULL); if(ret != 0) { AFB_ERROR("Failed to create thread"); g_bus_unown_name(owner_id); close_soundmanager_inerface(); close_proxy(); return NOT_INITIALIZED; } return OK; } void close_soundmanager_inerface(){ g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(sm_adapter)); }