Add new APIs to set or get preferred built-in device 57/210257/14
authorSangchul Lee <sc11.lee@samsung.com>
Wed, 3 Jul 2019 05:18:35 +0000 (14:18 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Tue, 6 Aug 2019 22:59:56 +0000 (07:59 +0900)
These new APIs are to set or get a specific built-in device when
the system has multiple devices of the same built-in device type.

When there's only one device for a built-in device type in the system,
nothing will happen even if these functions succeed in operation.

 - sound_manager_set_preferred_device
 - sound_manager_get_preferred_device

[Version] 0.5.32
[Issue Type] New feature

Change-Id: I7a709462031238de4127d9e6a5087577245f1627
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
include/sound_manager.h
include/sound_manager_private.h
packaging/capi-media-sound-manager.spec
src/sound_manager.c
src/sound_manager_private.c
test/sound_manager_test.c

index 9ed2da8..f4f79d8 100644 (file)
@@ -161,6 +161,13 @@ typedef enum {
 } sound_behavior_e;
 
 /**
+ * @brief Definition for the value indicating that the preferred device for a stream information was not set."
+ * @since_tizen 5.5
+ * @see sound_manager_get_stream_preferred_device()
+ */
+#define SOUND_MANAGER_STREAM_NO_PREFERRED_DEVICE    0
+
+/**
  * @}
  */
 
@@ -789,6 +796,62 @@ int sound_manager_remove_all_devices_for_stream_routing(sound_stream_info_h stre
 int sound_manager_apply_stream_routing(sound_stream_info_h stream_info);
 
 /**
+ * @brief Sets the preferred built-in device for the stream routing.
+ * @since_tizen 5.5
+ *
+ * @remarks    This function is to set a specific built-in device when the system has multiple devices of the same built-in device type.
+ *     When there's only one device for a built-in device type in the system, nothing will happen even if this function succeeds in operation.
+ *     If @a device is not supported by @a stream_info, #SOUND_MANAGER_ERROR_POLICY will be returned.
+ *     After this function is called, @a device is not needed for the function's work. It's okay to destroy it by destroying the list from which it was obtained.
+ *
+ * @param[in]  stream_info     The handle of stream information
+ * @param[in]  io_direction    The IO direction of the device
+ * @param[in]  device  The preferred device (this can be NULL to unset)
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SOUND_MANAGER_ERROR_POLICY Noncompliance with the sound system policy
+ * @retval #SOUND_MANAGER_ERROR_INTERNAL Internal error inside the sound system
+ * @pre Call sound_manager_create_stream_information() before calling this function.
+ * @pre Get a device by calling sound_manager_get_device_list() and sound_manager_get_next_device().
+ * @post You can get preferred devices by calling sound_manager_get_stream_preferred_device().
+ * @post Call sound_manager_free_device_list() to free the devices.
+ * @see sound_manager_get_stream_preferred_device()
+ * @see sound_manager_create_stream_information()
+ * @see sound_manager_destroy_stream_information()
+ * @see sound_manager_get_device_list()
+ * @see sound_manager_get_next_device()
+ * @see sound_manager_free_device_list()
+ */
+int sound_manager_set_stream_preferred_device(sound_stream_info_h stream_info, sound_device_io_direction_e io_direction, sound_device_h device);
+
+/**
+ * @brief Gets the preferred device id of the stream info handle.
+ * @since_tizen 5.5
+ *
+ * @remarks    This function is to return the preferred device id per each direction if it has been set.
+ *     If there is no preferred device for a given direction, the corresponding output value will be set to
+ *     #SOUND_MANAGER_STREAM_NO_PREFERRED_DEVICE.
+ *     You can get a device handle of the id value by using sound_manager_get_device_list(),
+ *     sound_manager_get_next_device() and sound_manager_get_device_id().
+ *
+ * @param[in]  stream_info     The handle of stream information
+ * @param[out] in_device_id    The preferred input device id
+ * @param[out] out_device_id   The preferred output device id
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @see sound_manager_set_stream_preferred_device()
+ * @see sound_manager_get_device_list()
+ * @see sound_manager_get_next_device()
+ * @see sound_manager_get_device_id()
+ * @see sound_manager_free_device_list()
+ */
+int sound_manager_get_stream_preferred_device(sound_stream_info_h stream_info, int *in_device_id, int *out_device_id);
+
+/**
  * @brief Acquires the stream focus.
  * @since_tizen 3.0
  *
index 2434576..e2f97db 100644 (file)
@@ -52,7 +52,7 @@ if (condition) { \
 _CHECK_CONDITION(handle != NULL, SOUND_MANAGER_ERROR_INVALID_PARAMETER, "SOUND_MANAGER_ERROR_INVALID_PARAMETER")
 
 #define SM_NULL_ARG_CHECK(arg) \
-_CHECK_CONDITION(arg != NULL, SOUND_MANAGER_ERROR_INVALID_PARAMETER, "SOUND_MANAGER_ERROR_INVALID_PARAMETER")
+_CHECK_CONDITION(!!(arg) != false, SOUND_MANAGER_ERROR_INVALID_PARAMETER, "SOUND_MANAGER_ERROR_INVALID_PARAMETER")
 
 #define SM_STATE_CHECK(handle, expected_state) \
 _CHECK_CONDITION(handle->state == expected_state, SOUND_MANAGER_ERROR_INVALID_STATE, "SOUND_MANAGER_ERROR_INVALID_STATE")
@@ -65,7 +65,7 @@ _CHECK_CONDITION(arg >= min, SOUND_MANAGER_ERROR_INVALID_PARAMETER, "SOUND_MANAG
 _CHECK_CONDITION(handle != NULL, MM_ERROR_INVALID_ARGUMENT, "MM_ERROR_INVALID_ARGUMENT")
 
 #define SM_NULL_ARG_CHECK_FOR_PRIV(arg) \
-_CHECK_CONDITION(arg != NULL, MM_ERROR_INVALID_ARGUMENT, "MM_ERROR_INVALID_ARGUMENT")
+_CHECK_CONDITION(!!(arg) != false, MM_ERROR_INVALID_ARGUMENT, "MM_ERROR_INVALID_ARGUMENT")
 
 #define SM_STATE_CHECK_FOR_PRIV(handle, expected_state) \
 _CHECK_CONDITION(handle->state == expected_state, MM_ERROR_SOUND_INVALID_STATE, "MM_ERROR_SOUND_INVALID_STATE")
@@ -201,6 +201,11 @@ typedef struct _sound_pa_info_s {
        unsigned int index;
 } sound_pa_info_s;
 
+typedef struct _preferred_device_info_s {
+       int in;
+       int out;
+} preferred_device_info_s;
+
 typedef struct _sound_stream_info_s {
        int focus_id;
        char *stream_type;
@@ -213,6 +218,7 @@ typedef struct _sound_stream_info_s {
        sound_stream_focus_state_changed_cb user_cb;
        void *user_data;
        manual_route_info_s manual_route_info;
+       preferred_device_info_s preferred_device_info;
        pthread_mutex_t focus_state_mutex;
        pthread_mutex_t focus_cb_mutex;
 } sound_stream_info_s;
@@ -366,6 +372,10 @@ int _activate_ducking(uint32_t stream_index, bool enable, const char *target_str
 
 int _get_ducking_state(uint32_t stream_index, bool *is_ducked);
 
+int _set_preferred_device(sound_stream_info_s *stream_info, sound_device_io_direction_e direction, sound_device_h device);
+
+int _get_preferred_device(sound_stream_info_s *stream_info, int *in_device_id, int *out_device_id);
+
 #ifdef __cplusplus
 }
 #endif
index 13b9125..0c957ee 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-sound-manager
 Summary:    Sound Manager library
-Version:    0.5.31
+Version:    0.5.32
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index ce9db51..3734003 100644 (file)
@@ -268,6 +268,31 @@ int sound_manager_apply_stream_routing(sound_stream_info_h stream_info)
        return _convert_sound_manager_error_code(__func__, ret);
 }
 
+int sound_manager_set_stream_preferred_device(sound_stream_info_h stream_info, sound_device_io_direction_e io_direction, sound_device_h device)
+{
+       int ret = MM_ERROR_NONE;
+       sound_stream_info_s *stream_h = (sound_stream_info_s*)stream_info;
+
+       SM_NULL_ARG_CHECK(stream_h);
+
+       ret = _set_preferred_device(stream_h, io_direction, device);
+
+       return _convert_sound_manager_error_code(__func__, ret);
+}
+
+int sound_manager_get_stream_preferred_device(sound_stream_info_h stream_info, int *in_device_id, int *out_device_id)
+{
+       int ret = MM_ERROR_NONE;
+       sound_stream_info_s *stream_h = (sound_stream_info_s*)stream_info;
+
+       SM_NULL_ARG_CHECK(stream_h);
+       SM_NULL_ARG_CHECK(in_device_id || out_device_id);
+
+       ret = _get_preferred_device(stream_h, in_device_id, out_device_id);
+
+       return _convert_sound_manager_error_code(__func__, ret);
+}
+
 int sound_manager_set_focus_reacquisition(sound_stream_info_h stream_info, bool enable)
 {
        int ret = MM_ERROR_NONE;
index 225aa4e..0ca9088 100644 (file)
@@ -26,6 +26,7 @@
 #define PA_STREAM_MANAGER_METHOD_NAME_GET_STREAM_INFO           "GetStreamInfo"
 #define PA_STREAM_MANAGER_METHOD_NAME_SET_STREAM_ROUTE_DEVICES  "SetStreamRouteDevices"
 #define PA_STREAM_MANAGER_METHOD_NAME_SET_STREAM_ROUTE_OPTION   "SetStreamRouteOption"
+#define PA_STREAM_MANAGER_METHOD_NAME_SET_STREAM_PREFERRED_DEVICE   "SetStreamPreferredDevice"
 #define PA_STREAM_MANAGER_METHOD_NAME_GET_VOLUME_MAX_LEVEL      "GetVolumeMaxLevel"
 #define PA_STREAM_MANAGER_METHOD_NAME_GET_VOLUME_LEVEL          "GetVolumeLevel"
 #define PA_STREAM_MANAGER_METHOD_NAME_SET_VOLUME_LEVEL          "SetVolumeLevel"
@@ -2126,6 +2127,19 @@ static int __check_manual_route_type(sound_stream_info_s *stream_info)
        return MM_ERROR_NONE;
 }
 
+static int __check_auto_route_type(sound_stream_info_s *stream_info)
+{
+       SM_INSTANCE_CHECK_FOR_PRIV(stream_info);
+
+       if (stream_info->stream_conf_info.route_type != STREAM_ROUTE_TYPE_AUTO &&
+               stream_info->stream_conf_info.route_type != STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED) {
+               LOGE("route type is not auto or auto-last-connected");
+               return MM_ERROR_POLICY_INTERNAL;
+       }
+
+       return MM_ERROR_NONE;
+}
+
 static int __add_device_to_stream_info(sound_stream_info_s *stream_info, int device_id, mm_sound_device_io_direction_e device_direction, char *device_type_str)
 {
        int i, j;
@@ -2232,6 +2246,63 @@ static int __remove_device_from_stream_info(sound_stream_info_s *stream_info, in
        return MM_ERROR_NONE;
 }
 
+static int __is_available_device(sound_stream_info_s *stream_info, sound_device_h device, bool *available)
+{
+       int ret = MM_ERROR_NONE;
+       int i = 0;
+       mm_sound_device_type_e mm_sound_device_type;
+       sound_device_type_e device_type;
+       char *device_type_str = NULL;
+       char *avail_device_item = NULL;
+       mm_sound_device_io_direction_e device_direction;
+       bool found = false;
+
+       SM_INSTANCE_CHECK_FOR_PRIV(stream_info);
+       SM_INSTANCE_CHECK_FOR_PRIV(device);
+
+       if ((ret = mm_sound_get_device_type(device, &mm_sound_device_type)))
+               return ret;
+       if ((ret = _convert_device_type(mm_sound_device_type, &device_type)))
+               return ret;
+       if ((ret = _convert_device_type_enum_to_str(device_type, &device_type_str)))
+               return ret;
+       if ((ret = mm_sound_get_device_io_direction(device, &device_direction)))
+               return ret;
+
+       if (device_direction & MM_SOUND_DEVICE_IO_DIRECTION_OUT) {
+               for (i = 0; i < AVAIL_DEVICES_MAX; i++) {
+                       if (!(avail_device_item = stream_info->stream_conf_info.avail_out_devices[i]))
+                               break;
+                       if (!strncmp(avail_device_item, device_type_str, strlen(device_type_str)))
+                               found = true;
+               }
+               if (!found) {
+                       LOGE("[OUT] this device(%s) is not available for this stream_info(%s)", device_type_str, stream_info->stream_type);
+                       *available = false;
+                       return MM_ERROR_NONE;
+               }
+       }
+
+       if (device_direction & MM_SOUND_DEVICE_IO_DIRECTION_IN) {
+               found = false;
+               for (i = 0; i < AVAIL_DEVICES_MAX; i++) {
+                       if (!(avail_device_item = stream_info->stream_conf_info.avail_in_devices[i]))
+                               break;
+                       if (!strncmp(avail_device_item, device_type_str, strlen(device_type_str)))
+                               found = true;
+               }
+               if (!found) {
+                       LOGE("[IN] this device(%s) is not available for this stream_info(%s)", device_type_str, stream_info->stream_type);
+                       *available = false;
+                       return MM_ERROR_NONE;
+               }
+       }
+
+       *available = true;
+
+       return MM_ERROR_NONE;
+}
+
 int _add_device_for_stream_routing(sound_stream_info_s *stream_info, sound_device_h device)
 {
        int ret = MM_ERROR_NONE;
@@ -2715,7 +2786,6 @@ LEAVE:
 
        return ret;
 }
-//LCOV_EXCL_STOP
 
 int _activate_ducking(uint32_t stream_index, bool enable, const char *target_stream, uint32_t duration, double ratio)
 {
@@ -2814,3 +2884,118 @@ LEAVE:
 
        return ret;
 }
+
+
+int _set_preferred_device(sound_stream_info_s *stream_info, sound_device_io_direction_e direction, sound_device_h device)
+{
+       int ret = MM_ERROR_NONE;
+       GVariant *result = NULL;
+       GDBusConnection *conn = NULL;
+       GError *err = NULL;
+       const gchar *dbus_ret = NULL;
+       const gchar *direction_str;
+       bool available = false;
+       int device_id = 0;
+       mm_sound_device_io_direction_e io_direction;
+       int i;
+
+       SM_INSTANCE_CHECK_FOR_PRIV(stream_info);
+
+       /* allow only auto route type */
+       if ((ret = __check_auto_route_type(stream_info)))
+               return ret;
+
+       /* check if this device belongs to available devices of the stream info */
+       /* In case device is null, it's for unsetting preferred device, device_id will be 0. */
+       if (device) {
+               if ((ret = __is_available_device(stream_info, device, &available)))
+                       return ret;
+               if (!available)
+                       return  MM_ERROR_POLICY_INTERNAL;
+               if ((ret = mm_sound_get_device_id(device, &device_id)))
+                       return ret;
+               if ((ret = mm_sound_get_device_io_direction(device, &io_direction)))
+                       return ret;
+               if (!(io_direction & (direction + 1)) || ((int)io_direction < (int)(direction + 1))) {
+                       LOGE("device direction(0x%x), request direction(0x%x)", io_direction, (direction + 1));
+                       return MM_ERROR_INVALID_ARGUMENT;
+               }
+       }
+
+       if ((ret = __get_dbus_connection(&conn)))
+               return ret;
+
+       for (i = SOUND_DEVICE_IO_DIRECTION_IN; i < SOUND_DEVICE_IO_DIRECTION_BOTH; i++) {
+               if (device) {
+                       if (!(io_direction & (i + 1)))
+                               continue;
+               } else {
+                       if (!((direction + 1) & (i + 1)))
+                               continue;
+               }
+               direction_str = (i == SOUND_DEVICE_IO_DIRECTION_IN) ? "in" : "out";
+
+               result = g_dbus_connection_call_sync(conn,
+                       PA_BUS_NAME,
+                       PA_STREAM_MANAGER_OBJECT_PATH,
+                       PA_STREAM_MANAGER_INTERFACE,
+                       PA_STREAM_MANAGER_METHOD_NAME_SET_STREAM_PREFERRED_DEVICE,
+                       g_variant_new("(usu)", stream_info->pa_info.index, direction_str, (unsigned int)device_id),
+                       NULL,
+                       G_DBUS_CALL_FLAGS_NONE,
+                       2000,
+                       NULL,
+                       &err);
+               if (!result || err) {
+                       LOGE("g_dbus_connection_call_sync() for SET PREFERRED DEVICE error (%s)", err ? err->message : NULL);
+                       ret = _convert_dbus_error(err ? err->message : NULL);
+                       if (err)
+                               g_error_free(err);
+                       if (result)
+                               g_variant_unref(result);
+                       goto LEAVE;
+               }
+
+               LOGI("Preferred device(id:%d, direction:%s) is set to PA context(%u)", device_id, direction_str, stream_info->pa_info.index);
+
+               g_variant_get(result, "(&s)", &dbus_ret);
+               LOGI("g_dbus_connection_call_sync() success, method return value is (%s)", dbus_ret);
+               if (!strncmp("STREAM_MANAGER_RETURN_ERROR_DEVICE_NOT_FOUND", dbus_ret, strlen(dbus_ret)))
+                       ret = MM_ERROR_INVALID_ARGUMENT;
+               else if (!strncmp("STREAM_MANAGER_RETURN_POLICY", dbus_ret, strlen(dbus_ret)))
+                       ret = MM_ERROR_POLICY_INTERNAL;
+               else if (strncmp("STREAM_MANAGER_RETURN_OK", dbus_ret, strlen(dbus_ret)))
+                       ret = MM_ERROR_SOUND_INTERNAL;
+
+               g_variant_unref(result);
+               if (ret)
+                       goto LEAVE;
+
+               if (i == SOUND_DEVICE_IO_DIRECTION_IN)
+                       stream_info->preferred_device_info.in = device_id;
+               else
+                       stream_info->preferred_device_info.out = device_id;
+       }
+
+LEAVE:
+       g_object_unref(conn);
+
+       return ret;
+}
+
+int _get_preferred_device(sound_stream_info_s *stream_info, int *in_device_id, int *out_device_id)
+{
+       SM_INSTANCE_CHECK_FOR_PRIV(stream_info);
+
+       if (in_device_id) {
+               *in_device_id = stream_info->preferred_device_info.in;
+               LOGI("preferred device id[in:%d]", *in_device_id);
+       }
+       if (out_device_id) {
+               *out_device_id = stream_info->preferred_device_info.out;
+               LOGI("preferred device id[out:%d]", *out_device_id);
+       }
+
+       return MM_ERROR_NONE;
+}
+//LCOV_EXCL_STOP
\ No newline at end of file
index 64314b9..36e2e66 100644 (file)
@@ -68,6 +68,8 @@ enum {
        CURRENT_STATUS_ADD_DEVICE_ID_FOR_STREAM_ROUTING,
        CURRENT_STATUS_REMOVE_DEVICE_ID_FOR_STREAM_ROUTING,
        CURRENT_STATUS_APPLY_STREAM_ROUTING,
+       CURRENT_STATUS_SET_STREAM_PREFERRED_DEVICE,
+       CURRENT_STATUS_GET_STREAM_PREFERRED_DEVICE,
        CURRENT_STATUS_SET_STREAM_ROUTING_OPTION,
        CURRENT_STATUS_ACQUIRE_FOCUS,
        CURRENT_STATUS_RELEASE_FOCUS,
@@ -123,6 +125,10 @@ int g_device_running_cb_id;
 
 static const char *g_device_direction_str[] = {"IN", "OUT", "BOTH"};
 static const char *g_device_state_str[] = {"De-Activated", "Activated"};
+static const char *g_device_type_str[] = {"builtin-speaker", "builtin-receiver", "builtin-mic",
+                                                       "audio-jack", "bt-media", "hdmi",
+                                                       "forwarding", "usb-audio", "bt-voice",
+                                                       "network"};
 
 void focus_callback(sound_stream_info_h stream_info,
                                        sound_stream_focus_mask_e focus_mask,
@@ -273,6 +279,10 @@ void _interpret_main_menu(char *cmd)
                g_menu_state = CURRENT_STATUS_APPLY_STREAM_ROUTING;
        else if (strncmp(cmd, "sso", MAX_CMD_LEN) == 0)
                g_menu_state = CURRENT_STATUS_SET_STREAM_ROUTING_OPTION;
+       else if (strncmp(cmd, "spd", MAX_CMD_LEN) == 0)
+               g_menu_state = CURRENT_STATUS_SET_STREAM_PREFERRED_DEVICE;
+       else if (strncmp(cmd, "gpd", MAX_CMD_LEN) == 0)
+               g_menu_state = CURRENT_STATUS_GET_STREAM_PREFERRED_DEVICE;
        else if (strncmp(cmd, "afc", MAX_CMD_LEN) == 0)
                g_menu_state = CURRENT_STATUS_ACQUIRE_FOCUS;
        else if (strncmp(cmd, "rfc", MAX_CMD_LEN) == 0)
@@ -411,6 +421,8 @@ void display_sub_basic()
        g_print("rdi. *Remove Device ID for Stream Routing\n");
        g_print("aps. Apply devices for Stream Routing\t");
        g_print("rad. Remove all devices for Stream Routing\n");
+       g_print("spd. Set preferred device\t");
+       g_print("gpd. Get preferred device\n");
        g_print("afc. Acquire Focus\t");
        g_print("rfc. Release Focus\t");
        g_print("gfs. Get Focus State\n");
@@ -525,6 +537,10 @@ static void displaymenu()
                g_print("*** input device type to remove (0:built-in mic, 1:built-in spk, 2:built-in rcv, 3:audio-jack, 4:bt-a2dp, 5:bt-sco 6:usb)\n");
        else if (g_menu_state == CURRENT_STATUS_APPLY_STREAM_ROUTING)
                g_print("*** press enter to apply devices for stream routing\n");
+       else if (g_menu_state == CURRENT_STATUS_SET_STREAM_PREFERRED_DEVICE)
+               g_print("*** input direction to set preferred device (0:out, 1:in, 2:both)\n");
+       else if (g_menu_state == CURRENT_STATUS_GET_STREAM_PREFERRED_DEVICE)
+               g_print("*** press enter to get preferred device\n");
        else if (g_menu_state == CURRENT_STATUS_SET_STREAM_ROUTING_OPTION)
                g_print("*** input option(name/value) for routing (0:option_1/0, 1:option_1/1, 2:option_2/0, 3:option_2:1)\n");
        else if (g_menu_state == CURRENT_STATUS_ACQUIRE_FOCUS)
@@ -1020,6 +1036,7 @@ static void interpret(char *cmd)
                                                type, io_direction, id, name, is_running ? "Running" : "Not running");
                } else {
                        g_print("failed to get next device, ret[0x%x]\n", ret);
+                       g_device = NULL;
                }
                reset_menu_state();
                break;
@@ -1340,7 +1357,7 @@ static void interpret(char *cmd)
                default:
                        g_print("invalid argument, device_type(%d) is not valid for this feature\n", device_type);
                        reset_menu_state();
-                       break;
+                       goto end;
                }
                if (!(ret = sound_manager_get_device_list(SOUND_DEVICE_ALL_MASK, &g_device_list))) {
                        g_print("success to get current device list\n");
@@ -1409,7 +1426,7 @@ static void interpret(char *cmd)
                default:
                        g_print("invalid argument, device_type(%d) is not valid for this feature\n", device_type);
                        reset_menu_state();
-                       break;
+                       goto end;
                }
                if (!(ret = sound_manager_get_device_list(SOUND_DEVICE_ALL_MASK, &g_device_list))) {
                        g_print("success to get current device list\n");
@@ -1489,7 +1506,7 @@ static void interpret(char *cmd)
                default:
                        g_print("invalid argument, device_type(%d) is not valid for this feature\n", device_type);
                        reset_menu_state();
-                       break;
+                       goto end;
                }
                if (!(ret = sound_manager_get_device_list(SOUND_DEVICE_ALL_MASK, &g_device_list))) {
                        g_print("success to get current device list\n");
@@ -1564,7 +1581,7 @@ static void interpret(char *cmd)
                default:
                        g_print("invalid argument, device_type(%d) is not valid for this feature\n", device_type);
                        reset_menu_state();
-                       break;
+                       goto end;
                }
                if (!(ret = sound_manager_get_device_list(SOUND_DEVICE_ALL_MASK, &g_device_list))) {
                        g_print("success to get current device list\n");
@@ -1641,7 +1658,7 @@ static void interpret(char *cmd)
                default:
                        g_print("invalid argument, try again..\n");
                        reset_menu_state();
-                       break;
+                       goto end;
                }
                ret = sound_manager_set_stream_routing_option(g_stream_info_h, name, value);
                if (ret)
@@ -1650,6 +1667,77 @@ static void interpret(char *cmd)
                reset_menu_state();
                break;
        }
+       case CURRENT_STATUS_SET_STREAM_PREFERRED_DEVICE: {
+               int ret = SOUND_MANAGER_ERROR_NONE;
+               int selection = 0;
+               sound_device_io_direction_e direction;
+
+               selection = atoi(cmd);
+               switch (selection) {
+               case 0:
+                       direction = SOUND_DEVICE_IO_DIRECTION_OUT;
+                       break;
+               case 1:
+                       direction = SOUND_DEVICE_IO_DIRECTION_IN;
+                       break;
+               case 2:
+                       direction = SOUND_DEVICE_IO_DIRECTION_BOTH;
+                       break;
+               default:
+                       g_print("invalid argument, try again..\n");
+                       reset_menu_state();
+                       goto end;
+               }
+
+               ret = sound_manager_set_stream_preferred_device(g_stream_info_h, direction, g_device);
+               if (ret)
+                       g_print("failed to sound_manager_set_stream_preferred_device(), ret(0x%x)\n", ret);
+
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_GET_STREAM_PREFERRED_DEVICE: {
+               int ret = SOUND_MANAGER_ERROR_NONE;
+               int in_device_id = 0;
+               int out_device_id = 0;
+
+               ret = sound_manager_get_stream_preferred_device(g_stream_info_h, &in_device_id, &out_device_id);
+               if (ret) {
+                       g_print("failed to sound_manager_get_stream_preferred_device(), ret(0x%x)\n", ret);
+               } else {
+                       sound_device_list_h device_list;
+                       sound_device_h device;
+                       sound_device_type_e type;
+                       int id;
+
+                       if (in_device_id == SOUND_MANAGER_STREAM_NO_PREFERRED_DEVICE)
+                               g_print("preferred [ IN] device was not set\n");
+                       if (out_device_id == SOUND_MANAGER_STREAM_NO_PREFERRED_DEVICE)
+                               g_print("preferred [OUT] device was not set\n");
+                       if ((in_device_id == SOUND_MANAGER_STREAM_NO_PREFERRED_DEVICE)  &&
+                               (out_device_id == SOUND_MANAGER_STREAM_NO_PREFERRED_DEVICE)) {
+                               reset_menu_state();
+                               break;
+                       }
+
+                       sound_manager_get_device_list(SOUND_DEVICE_TYPE_INTERNAL_MASK, &device_list);
+                       while (!sound_manager_get_next_device(device_list, &device)) {
+                               if (!sound_manager_get_device_id(device, &id)) {
+                                       if (in_device_id && id == in_device_id) {
+                                               sound_manager_get_device_type(device, &type);
+                                               g_print("preferred [ IN] device type[%s], id[%d]\n", g_device_type_str[type], in_device_id);
+                                       }
+                                       if (out_device_id && id == out_device_id) {
+                                               sound_manager_get_device_type(device, &type);
+                                               g_print("preferred [OUT] device type[%s], id[%d]\n", g_device_type_str[type], out_device_id);
+                                       }
+                               }
+                       }
+                       sound_manager_free_device_list(device_list);
+               }
+               reset_menu_state();
+               break;
+       }
        case CURRENT_STATUS_ACQUIRE_FOCUS: {
                int ret = SOUND_MANAGER_ERROR_NONE;
                int focus_type = 0;
@@ -2299,6 +2387,7 @@ static void interpret(char *cmd)
                break;
        }
        }
+end:
        g_timeout_add(100, timeout_menu_display, 0);
 }