add publish and allow remote access internal api 66/239166/25 accepted/tizen/unified/20200819.100412 submit/tizen/20200818.081651 submit/tizen/20200819.045110
authorJaechul Lee <jcsing.lee@samsung.com>
Wed, 22 Jul 2020 08:18:31 +0000 (17:18 +0900)
committerJaechul Lee <jcsing.lee@samsung.com>
Tue, 18 Aug 2020 06:14:22 +0000 (15:14 +0900)
[Version] 0.6.27
[Issue Type] Improvement

Change-Id: I715ffd51c837e01bd01dccd74936a6caa9425763
Signed-off-by: Jaechul Lee <jcsing.lee@samsung.com>
include/sound_manager_internal.h
include/sound_manager_private.h
packaging/capi-media-sound-manager.spec
src/sound_manager_internal.c
src/sound_manager_private.c
test/sound_manager_test.c

index c326ee0..8f00a13 100644 (file)
@@ -1074,6 +1074,130 @@ typedef enum {
 int sound_manager_get_latest_stream_pid(int stream_type, unsigned int *pid);
 
 /**
+ * @internal
+ * @brief Called when the remote client is connected or disconnected.
+ * @since_tizen 6.0
+ * @remarks This callback indicates that the new remote client is connected or existing remote client is disconnected. \n
+            The parameters are the identification of the remote client such as unique id, name, description, and recording or not, \n
+            which can help the user to make a decision to allow access from the remote client using sound_manager_set_remote_permission().
+ * @param[in]  id      The id of the remote client
+ * @param[in]  name    The name of the remote client
+ * @param[in]  is_recording    The flag indicating whether it's recording client or not
+ * @param[in]  description     The brief description of the remote client
+ * @param[in]  is_connected    The state of remote client connection: (@c true = connected, @c false = disconnected)
+ * @param[in]  user_data       The user data passed from the callback registration function
+ * @pre You should register this callback using sound_manager_publish_local_device().
+ * @see sound_manager_publish_local_device()
+ * @see sound_manager_unpublish_local_device()
+ * @see sound_manager_set_allow_remote_access()
+ */
+typedef void (*sound_manager_remote_client_connected_cb) (int id, const char *name, bool is_recording,
+                                                          const char *description, bool is_connected, void *user_data);
+
+/**
+ * @internal
+ * @brief Publishes device's built-in audio devices to the local network.
+ * @since_tizen 6.0
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/internet
+ * @remarks This function publishes the device's built-in audio devices such as speakers and microphones to the local network. \n
+ *          The registered callback function will be invoked when the remote clients discover published audio devices and the connection established.
+ * @param[in]  callback        The remote client connection state changed callback function
+ * @param[in]  user_data       The user data to be passed to the callback function
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_NOT_SUPPORTED Not supported
+ * @retval #SOUND_MANAGER_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SOUND_MANAGER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SOUND_MANAGER_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #SOUND_MANAGER_ERROR_INTERNAL Internal error inside the sound system
+ * @post sound_manager_remote_client_connected_cb() will be invoked.
+ * @see sound_manager_unpublish_local_device()
+ * @see sound_manager_set_remote_permission()
+ */
+int sound_manager_publish_local_device(sound_manager_remote_client_connected_cb callback, void *user_data);
+
+/**
+ * @internal
+ * @brief Unpublishes device's built-in audio devices to local network.
+ * @since_tizen 6.0
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/internet
+ * @remarks This function unpublishes the device's built-in audio devices to the local network.
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_NOT_SUPPORTED Not supported
+ * @retval #SOUND_MANAGER_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SOUND_MANAGER_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #SOUND_MANAGER_ERROR_INTERNAL Internal error inside the sound system
+ * @pre You should have already published using sound_manager_publish_local_device().
+ * @see sound_manager_publish_local_device()
+ * @see sound_manager_set_remote_permission()
+ */
+int sound_manager_unpublish_local_device(void);
+
+/**
+ * @internal
+ * @brief Sets the permission of accessing the local device from the remote client.
+ * @since_tizen 6.0
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/internet
+ * @remarks
+ * @param[in]  id The remote client id
+ * @param[in]  allowed The permission to allow accessing from the remote client.
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_NOT_SUPPORTED Not supported
+ * @retval #SOUND_MANAGER_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SOUND_MANAGER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SOUND_MANAGER_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #SOUND_MANAGER_ERROR_INTERNAL Internal error inside the sound system
+ * @see sound_manager_publish_local_device()
+ * @see sound_manager_unpublish_local_device()
+ */
+int sound_manager_set_remote_permission(int id, bool allowed);
+
+/**
+ * @internal
+ * @brief Starts discovering of published remote devices on the local network.
+ * @since_tizen 6.0
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/internet
+ * @remarks
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_NOT_SUPPORTED Not supported
+ * @retval #SOUND_MANAGER_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SOUND_MANAGER_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #SOUND_MANAGER_ERROR_INTERNAL Internal error inside the sound system
+ * @see sound_manager_stop_discover_remote_device()
+ */
+int sound_manager_start_discover_remote_device(void);
+
+/**
+ * @internal
+ * @brief Stops discovering of published remote devices on the local network.
+ * @since_tizen 6.0
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/internet
+ * @remark
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_NOT_SUPPORTED Not supported
+ * @retval #SOUND_MANAGER_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SOUND_MANAGER_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #SOUND_MANAGER_ERROR_INTERNAL Internal error inside the sound system
+ * @pre You should start discovering by sound_manager_start_discover_remote_device().
+ * @see sound_manager_start_discover_remote_device()
+ */
+int sound_manager_stop_discover_remote_device(void);
+
+/**
  * @}
  */
 
index f5f3b1c..40af984 100644 (file)
@@ -356,6 +356,14 @@ int _create_virtual_stream(sound_stream_info_s *stream_info, virtual_sound_strea
 
 int _destroy_virtual_stream(virtual_sound_stream_info_s *virtual_stream);
 
+int _discover_remote_device(bool enable);
+
+int _publish_local_device(sound_manager_remote_client_connected_cb callback, void *user_data);
+
+int _unpublish_local_device(void);
+
+int _set_remote_permission(int index, bool allowed);
+
 int _start_virtual_stream(virtual_sound_stream_info_s *virtual_stream);
 
 int _stop_virtual_stream(virtual_sound_stream_info_s *virtual_stream);
index c959b0f..764eecd 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-sound-manager
 Summary:    Sound Manager library
-Version:    0.6.26
+Version:    0.6.27
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index dcc616c..ea65f07 100644 (file)
@@ -866,3 +866,31 @@ int sound_manager_get_stream_preemptive_device(sound_stream_type_e stream_type,
 
        return _get_preemptive_device(stream_type, in_device_id, out_device_id);
 }
+
+int sound_manager_start_discover_remote_device(void)
+{
+       return _discover_remote_device(true);
+}
+
+int sound_manager_stop_discover_remote_device(void)
+{
+       return _discover_remote_device(false);
+}
+
+int sound_manager_publish_local_device(sound_manager_remote_client_connected_cb callback, void *user_data)
+{
+       SM_ARG_CHECK(callback);
+
+       return _publish_local_device(callback, user_data);
+}
+
+int sound_manager_unpublish_local_device(void)
+{
+       return _unpublish_local_device();
+}
+
+int sound_manager_set_remote_permission(int id, bool allowed)
+{
+       return _set_remote_permission(id, allowed);
+}
+
index 9c3ad15..d333def 100644 (file)
 #define PA_STREAM_MANAGER_METHOD_NAME_ACTIVATE_DUCKING          "ActivateDucking"
 #define PA_STREAM_MANAGER_METHOD_NAME_GET_DUCKING_STATE         "GetDuckingState"
 #define PA_STREAM_MANAGER_METHOD_NAME_GET_LASTEST_STREAM_PID    "GetPidOfLatestStream"
+#define PA_STREAM_MANAGER_METHOD_NAME_PUBLISH_LOCAL_DEVICE      "PublishLocalDevice"
+#define PA_STREAM_MANAGER_METHOD_NAME_DISCOVER_REMOTE_DEVICE    "DiscoverRemoteDevice"
+#define PA_STREAM_MANAGER_METHOD_NAME_SET_REMOTE_PERMISSION     "SetRemotePermission"
+
+#define PA_STREAM_MANAGER_SIGNAL_NAME_REMOTE_FOUND              "RemoteFound"
 
 #define PA_DEVICE_MANAGER_OBJECT_PATH                           "/org/pulseaudio/DeviceManager"
 #define PA_DEVICE_MANAGER_INTERFACE                             "org.pulseaudio.DeviceManager"
@@ -2243,6 +2248,212 @@ int _destroy_pa_connection_and_unregister_focus(sound_stream_info_s *stream_h)
        return ret;
 }
 
+static int __dbus_method_call(const char *method, GVariant *param, GVariant **reply)
+{
+       GDBusConnection *conn = NULL;
+       GVariant *_reply = NULL;
+       GError *err = NULL;
+       int ret;
+
+       if (!method || !param) {
+               LOGE("invalid parameter");
+               ret = SOUND_MANAGER_ERROR_INVALID_PARAMETER;
+               goto error;
+       }
+
+       if ((ret = __get_dbus_connection(&conn)) != SOUND_MANAGER_ERROR_NONE) {
+               ret = SOUND_MANAGER_ERROR_INVALID_OPERATION;
+               goto error;
+       }
+
+       _reply = g_dbus_connection_call_sync(conn, PA_BUS_NAME,
+               PA_STREAM_MANAGER_OBJECT_PATH,
+               PA_STREAM_MANAGER_INTERFACE,
+               method, param, NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err);
+
+       g_object_unref(conn);
+
+       if (!_reply) {
+               ret =  _convert_dbus_error(err->message);
+               LOGE("g_dbus_connection_call_sync() method(%s), err-msg(%s)", method, err->message);
+               g_error_free(err);
+               return ret;
+       }
+
+       if (reply)
+               *reply = _reply;
+       else
+               g_variant_unref(_reply);
+
+       return SOUND_MANAGER_ERROR_NONE;
+
+error:
+       if (param)
+               g_variant_unref(param);
+
+       return ret;
+}
+
+// not thread-safe
+static struct _publish_info {
+       GDBusConnection *conn;
+       guint subs_id;
+       struct _cb_info {
+               sound_manager_remote_client_connected_cb cb;
+               void *userdata;
+       } cb_info;
+} publish_info;
+
+#define REMOTE_CLIENT_BIT_TYPE_MASK 0x80000000
+#define REMOTE_CLIENT_BIT_TYPE 31
+
+static void __internal_cb(GDBusConnection *connection, const gchar *sender_name,
+                                       const gchar *object_path, const gchar *interface_name,
+                                       const gchar *signal_name,GVariant *params, gpointer userdata)
+{
+       struct _cb_info *cb_info = (struct _cb_info *)userdata;
+
+       if (cb_info && cb_info->cb) {
+               gint32 type;
+               guint32 id;
+               gboolean is_connected;
+               gchar *name, *description;
+
+               g_variant_get(params, "(iub&s&s)", &type, &id, &is_connected, &name, &description);
+
+               LOGE("type(%d), id(%u), is_connected(%d), name(%s), description(%s)",
+                               type, id, is_connected, name, description);
+
+               /* MSB:input or output, others:index */
+               id = id | (type << REMOTE_CLIENT_BIT_TYPE);
+               cb_info->cb(id, name, type, description, is_connected, cb_info->userdata);
+       }
+}
+
+static int __subscribe_remote_devices(sound_manager_remote_client_connected_cb callback, void *userdata)
+{
+       int ret;
+       struct _cb_info *cb_info = &(publish_info.cb_info);
+
+       if (publish_info.subs_id > 0) {
+               LOGE("already subscribed remote device");
+               return SOUND_MANAGER_ERROR_INTERNAL;
+       }
+
+       if ((ret = __get_dbus_connection(&publish_info.conn)) != SOUND_MANAGER_ERROR_NONE)
+               return ret;
+
+       cb_info->cb = callback;
+       cb_info->userdata = userdata;
+
+       publish_info.subs_id = g_dbus_connection_signal_subscribe(publish_info.conn, NULL,
+                                       PA_STREAM_MANAGER_INTERFACE,
+                                       PA_STREAM_MANAGER_SIGNAL_NAME_REMOTE_FOUND,
+                                       PA_STREAM_MANAGER_OBJECT_PATH, NULL, G_DBUS_SIGNAL_FLAGS_NONE,
+                                       __internal_cb, cb_info, NULL);
+       if (publish_info.subs_id == 0) {
+               g_object_unref(publish_info.conn);
+               LOGE("subscription failed");
+               return SOUND_MANAGER_ERROR_INTERNAL;
+       }
+
+       LOGI("subscribe remote devices. subs_id %d", publish_info.subs_id);
+
+       return SOUND_MANAGER_ERROR_NONE;
+}
+
+static int __unsubscribe_remote_devices(void)
+{
+       int ret;
+
+       if (publish_info.subs_id == 0) {
+               LOGE("invalid subs_id(%d)", publish_info.subs_id);
+               return SOUND_MANAGER_ERROR_INTERNAL;
+       }
+
+       if ((ret = __get_dbus_connection(&publish_info.conn)) != SOUND_MANAGER_ERROR_NONE)
+               return ret;
+
+       g_dbus_connection_signal_unsubscribe(publish_info.conn, publish_info.subs_id);
+
+       if (publish_info.conn) {
+               g_object_unref(publish_info.conn);
+               publish_info.conn = NULL;
+       }
+
+       publish_info.subs_id = 0;
+
+       LOGI("unsubscribe remote devices state subs_id %d", publish_info.subs_id);
+
+       return SOUND_MANAGER_ERROR_NONE;
+}
+
+int _publish_local_device(sound_manager_remote_client_connected_cb callback, void *user_data)
+{
+       int ret;
+
+       LOGI("publish local device callback(%p), userdata(%p)", callback, user_data);
+
+       ret = __subscribe_remote_devices(callback, user_data);
+       if (ret != SOUND_MANAGER_ERROR_NONE) {
+               LOGE("subscribe failed");
+               return ret;
+       }
+
+       ret = __dbus_method_call(PA_STREAM_MANAGER_METHOD_NAME_PUBLISH_LOCAL_DEVICE, g_variant_new("(b)", TRUE), NULL);
+       if (ret != SOUND_MANAGER_ERROR_NONE) {
+               if (__unsubscribe_remote_devices())
+                       LOGE("unsubscribe failed");
+
+               LOGE("publish local device failed");
+
+               return ret;
+       }
+
+       return ret;
+}
+
+int _unpublish_local_device(void)
+{
+       int ret;
+
+       ret = __dbus_method_call(PA_STREAM_MANAGER_METHOD_NAME_PUBLISH_LOCAL_DEVICE, g_variant_new("(b)", FALSE), NULL);
+       if (ret != SOUND_MANAGER_ERROR_NONE) {
+               LOGE("unpublish local device failed");
+               return ret;
+       }
+
+       ret = __unsubscribe_remote_devices();
+       if (ret != SOUND_MANAGER_ERROR_NONE) {
+               LOGE("unsubscribe failed");
+               return ret;
+       }
+
+       return ret;
+}
+
+int _discover_remote_device(bool enable)
+{
+       return __dbus_method_call(PA_STREAM_MANAGER_METHOD_NAME_DISCOVER_REMOTE_DEVICE,
+                                       g_variant_new("(b)", (gboolean)enable), NULL);
+}
+
+int _set_remote_permission(int id, bool allowed)
+{
+       int ret;
+       int index = (id << 1) >> 1;
+       char *type = id & REMOTE_CLIENT_BIT_TYPE_MASK ? "source-output" : "sink-input";
+
+       LOGI("id(%d), allowed(%d), index(%d), type(%s)", id, allowed, index, type);
+
+       ret = __dbus_method_call(PA_STREAM_MANAGER_METHOD_NAME_SET_REMOTE_PERMISSION,
+                                       g_variant_new("(sub)", type, index, (gboolean)allowed), NULL);
+       if (ret != SOUND_MANAGER_ERROR_NONE)
+               LOGE("update proplist failed");
+
+       return ret;
+}
+
 static int __check_manual_route_type(sound_stream_info_s *stream_info)
 {
        SM_ARG_CHECK(stream_info);
index e79ee4e..5abb753 100644 (file)
@@ -117,6 +117,9 @@ enum {
        CURRENT_STATUS_ACTIVATE_DUCKING,
        CURRENT_STATUS_DEACTIVATE_DUCKING,
        CURRENT_STATUS_GET_DUCKING_STATE,
+       CURRENT_STATUS_DISCOVER_REMOTE_DEVICE,
+       CURRENT_STATUS_PUBLISH_REMOTE_DEVICE,
+       CURRENT_STATUS_SET_REMOTE_PERMISSION,
 };
 
 
@@ -385,6 +388,12 @@ void _interpret_main_menu(char *cmd)
                g_menu_state = CURRENT_STATUS_DEACTIVATE_DUCKING;
        else if (strncmp(cmd, "gds", MAX_CMD_LEN) == 0)
                g_menu_state = CURRENT_STATUS_GET_DUCKING_STATE;
+       else if (strncmp(cmd, "dis", MAX_CMD_LEN) == 0)
+               g_menu_state = CURRENT_STATUS_DISCOVER_REMOTE_DEVICE;
+       else if (strncmp(cmd, "pub", MAX_CMD_LEN) == 0)
+               g_menu_state = CURRENT_STATUS_PUBLISH_REMOTE_DEVICE;
+       else if (strncmp(cmd, "alw", MAX_CMD_LEN) == 0)
+               g_menu_state = CURRENT_STATUS_SET_REMOTE_PERMISSION;
        else if (strncmp(cmd, "q", MAX_CMD_LEN) == 0) {
                g_print("closing the test suite\n");
                quit_program();
@@ -495,6 +504,9 @@ void display_sub_basic()
 #ifndef TIZEN_FEATURE_TV_PROD
        g_print("acm. *Set ACM Master mode\n");
 #endif
+       g_print("dis. *Discover remote devices\n");
+       g_print("pub. *Publish remote devices\n");
+       g_print("alw. *Allow remote device\n");
        g_print("                                                                 * is for internal usage.\n");
        g_print("=========================================================================================\n");
 }
@@ -689,6 +701,12 @@ static void displaymenu()
                g_print("*** press enter to deactivate ducking\n");
        else if (g_menu_state == CURRENT_STATUS_GET_DUCKING_STATE)
                g_print("*** press enter to get ducking state\n");
+       else if (g_menu_state == CURRENT_STATUS_DISCOVER_REMOTE_DEVICE)
+               g_print("*** press enter 0:disable, 1:enable\n");
+       else if (g_menu_state == CURRENT_STATUS_PUBLISH_REMOTE_DEVICE)
+               g_print("*** press enter 0:disable, 1:enable\n");
+       else if (g_menu_state == CURRENT_STATUS_SET_REMOTE_PERMISSION)
+               g_print("*** press enter number num, allow(0,1)\n");
        else {
                g_print("*** unknown status.\n");
                quit_program();
@@ -767,7 +785,7 @@ void _volume_changed_cb_internal(sound_type_internal_e type, unsigned int volume
 
 /* If failed to get some property, just give some default value */
 void _get_device_props_simple(sound_device_h device, int *id, const char **type, char **name,
-               const char **direc, const char **state, int *vendor_id, int *product_id, bool *is_running)
+               const char **direction, const char **state, int *vendor_id, int *product_id, bool *is_running)
 {
        int ret;
        sound_device_type_e _type;
@@ -804,9 +822,9 @@ void _get_device_props_simple(sound_device_h device, int *id, const char **type,
 
        if ((ret = sound_manager_get_device_io_direction(device, &_direc))) {
                g_print("failed to get device io direction, ret[0x%x]\n", ret);
-               *direc = NOT_AVAIL;
+               *direction = NOT_AVAIL;
        } else {
-               *direc = g_device_direction_str[_direc];
+               *direction = g_device_direction_str[_direc];
        }
 
        if ((ret = sound_manager_get_device_state(device, &_state))) {
@@ -830,23 +848,14 @@ void _device_connected_cb(sound_device_h device, bool is_connected, void *user_d
        int vendor_id = -1;
        int product_id = -1;
        char *name;
-       const char *type, *direc, *state;
+       const char *type, *direction, *state;
        bool is_running = false;
-       int ret;
-       sound_device_type_e device_type;
-
-       _get_device_props_simple(device, &id, &type, &name, &direc, &state, &vendor_id, &product_id, &is_running);
 
-       g_print("[ Device #%d %s %s ] %s\n", id, type, name, is_connected ? "Connected" : "Disconnected");
-       g_print("    Direc[ %-4s ] State[ %-12s ] Running[ %d ] VendorID[ %04x ], ProductID[ %04x ]\n",
-               direc, state, is_running, vendor_id, product_id);
+       _get_device_props_simple(device, &id, &type, &name, &direction, &state, &vendor_id, &product_id, &is_running);
 
-
-       ret = sound_manager_get_current_media_playback_device_type(&device_type);
-       if (ret)
-               g_print("fail to sound_manager_get_current_media_playback_device_type, ret(0x%x)\n", ret);
-       else
-               g_print("current media playback device type is [%s]\n", g_device_type_str[device_type]);
+       g_print("\n[ Device #%d %20s %s ] %15s : ", id, type, name, is_connected ? "Connected" : "Disconnected");
+       g_print("    Direction[ %-4s ] State[ %-12s ] Running[ %d ] VendorID[ %04x ], ProductID[ %04x ]\n",
+            direction, state, is_running, vendor_id, product_id);
 }
 
 void _device_running_changed_cb(sound_device_h device, bool is_running, void *user_data)
@@ -855,14 +864,14 @@ void _device_running_changed_cb(sound_device_h device, bool is_running, void *us
        int vendor_id = -1;
        int product_id = -1;
        char *name;
-       const char *type, *direc, *state;
+       const char *type, *direction, *state;
        bool _is_running = false;
 
-       _get_device_props_simple(device, &id, &type, &name, &direc, &state, &vendor_id, &product_id, &_is_running);
+       _get_device_props_simple(device, &id, &type, &name, &direction, &state, &vendor_id, &product_id, &_is_running);
 
-       g_print("[Device #%d %s %s] running changed to [ %d ]\n", id, type, name, is_running);
-       g_print("    Direc[ %-4s ] State[ %-12s ] Running[ %d ] VendorID[ %04x ], ProductID[ %04x ]\n",
-               direc, state, _is_running, vendor_id, product_id);
+       g_print("\n[ Device #%d %20s %s ] running changed to [ %d ] : ", id, type, name, is_running);
+       g_print("    Direction[ %-4s ] State[ %-12s ] Running[ %d ] VendorID[ %04x ], ProductID[ %04x ]\n",
+            direction, state, _is_running, vendor_id, product_id);
 }
 
 void reset_menu_state(void)
@@ -1138,7 +1147,7 @@ static void interpret(char *cmd)
                int product_id = -1;
                char *name;
                const char *type;
-               const char *direc;
+               const char *direction;
                const char *state;
                bool is_running;
                unsigned int num;
@@ -1148,9 +1157,10 @@ static void interpret(char *cmd)
                if (!(ret = sound_manager_get_device_list(g_device_mask, &device_list))) {
                        g_print("success to get current device list\n");
                        while (!sound_manager_get_next_device(device_list, &device)) {
-                               _get_device_props_simple(device, &id, &type, &name, &direc, &state, &vendor_id, &product_id, &is_running);
-                               g_print("[ Device #%d %s %s ]\n", id, type, name);
-                               g_print("    Direc[ %-4s ] State[ %-12s ] Running[ %d ] VendorID[ %04x ] ProductID[ %04x ]\n", direc, state, is_running, vendor_id, product_id);
+                               _get_device_props_simple(device, &id, &type, &name, &direction, &state, &vendor_id, &product_id, &is_running);
+                               g_print("[ Device #%d %20s %s ] : ", id, type, name);
+                               g_print("    Direction[ %-4s ] State[ %-12s ] Running[ %d ] VendorID[ %04x ] ProductID[ %04x ]\n",
+                                       direction, state, is_running, vendor_id, product_id);
 
                                if (!(ret = sound_manager_get_supported_sample_formats(device, &formats, &num))) {
                                        while (num--)
@@ -2755,6 +2765,61 @@ static void interpret(char *cmd)
                reset_menu_state();
                break;
        }
+       case CURRENT_STATUS_DISCOVER_REMOTE_DEVICE: {
+               int ret;
+               int discover = atoi(cmd);
+
+               if (discover)
+                       ret = sound_manager_start_discover_remote_device();
+               else
+                       ret = sound_manager_stop_discover_remote_device();
+
+               if (!ret)
+                       g_print("success to discover devices\n");
+               else
+                       g_print("fail to discover devices\n");
+
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_PUBLISH_REMOTE_DEVICE: {
+               int ret = SOUND_MANAGER_ERROR_NONE;
+               int publish = atoi(cmd);
+
+               void remote_client_connected_cb(int id, const char *name, bool is_recording, const char *description, bool is_connected, void *user_data) {
+                       g_print("\nid %u, name %s, is_recording %d, description %s, connected %d\n",
+                                       id, name, is_recording, description, is_connected);
+               }
+
+               if (publish)
+                       ret = sound_manager_publish_local_device(remote_client_connected_cb, NULL);
+               else
+                       ret = sound_manager_unpublish_local_device();
+
+               if (!ret)
+                       g_print("success to publish devices\n");
+               else
+                       g_print("fail to publish devices\n");
+
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_SET_REMOTE_PERMISSION: {
+               int ret;
+               unsigned int id;
+               int allow;
+
+               ret = sscanf(cmd, "%u %d", &id, &allow);
+
+               ret = sound_manager_set_remote_permission(id, !!allow);
+               if (!ret)
+                       g_print("allow id %u, allow %d\n", id, allow);
+               else
+                       g_print("sound_manager_set_allow_remote_device failed\n");
+
+               reset_menu_state();
+               break;
+       }
        }
 end:
        g_timeout_add(100, timeout_menu_display, 0);