Add foreach device in group API 18/185018/2
authorJihoon Jung <jh8801.jung@samsung.com>
Wed, 25 Jul 2018 08:32:27 +0000 (17:32 +0900)
committerJihoon Jung <jh8801.jung@samsung.com>
Wed, 25 Jul 2018 08:32:27 +0000 (17:32 +0900)
Change-Id: I5b2410743cf26697528ea4711994b1428d3baed3
Signed-off-by: Jihoon Jung <jh8801.jung@samsung.com>
include/mdg.h
include/mdg_internal.h [deleted file]
src/mdg.c
src/mdg_gdbus.xml
src/mdg_private.h
src/mdg_util.c
src/mdg_util.h
test/main.c
test/mdg-manager.c

index 4c8a88a..e491f82 100755 (executable)
@@ -16,8 +16,8 @@
  * limitations under the License.
  *
  */
-#ifndef __TIZEN_NETWORK_MULTI_DEVICE_GROUP_H__
-#define __TIZEN_NETWORK_MULTI_DEVICE_GROUP_H__
+#ifndef __TIZEN_NETWORK_MULTI_DEVICE_GROUP_MDG_H__
+#define __TIZEN_NETWORK_MULTI_DEVICE_GROUP_MDG_H__
 
 #include <tizen.h>
 
@@ -61,18 +61,15 @@ typedef enum {
        MDG_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY, /**< Out of memory */
        MDG_ERROR_PERMISSION_DENIED = TIZEN_ERROR_PERMISSION_DENIED, /**< Permission denied */
        MDG_ERROR_NOT_SUPPORTED = TIZEN_ERROR_NOT_SUPPORTED, /**< Not supported */
-       MDG_ERROR_NO_DATA = TIZEN_ERROR_MDG | 0x01, /**< Data not exists */
-       MDG_ERROR_OPERATION_FAILED = TIZEN_ERROR_MDG | 0x02, /**< operation failed */
+       MDG_ERROR_OPERATION_FAILED = TIZEN_ERROR_MDG | 0x01, /**< operation failed */
+       MDG_ERROR_NO_DATA = TIZEN_ERROR_MDG | 0x02, /**< Data not exists */
        MDG_ERROR_ALREADY_REGISTERED = TIZEN_ERROR_MDG | 0x03, /**< Request already registered */
        MDG_ERROR_IN_PROGRESS = TIZEN_ERROR_MDG | 0x04,/**< operation is in progress */
-       MDG_ERROR_COMM_ERROR = TIZEN_ERROR_MDG | 0x05, /**< communication error */
-       MDG_ERROR_RX = TIZEN_ERROR_MDG | 0x06, /**< RX error */
-       MDG_ERROR_TX = TIZEN_ERROR_MDG | 0x07, /**< TX error */
-       MDG_ERROR_PLUGIN_FAIL = TIZEN_ERROR_MDG | 0x08, /**< Plugin failed */
-       MDG_ERROR_ALREADY_IN_PROGRESS = TIZEN_ERROR_MDG | 0x09, /**< Already in progress */
-       MDG_ERROR_NOT_STARTED = TIZEN_ERROR_MDG | 0x0A, /**< Not Started */
-       MDG_ERROR_UNKNOWN = -999,
-
+       MDG_ERROR_COMM_ERROR = TIZEN_ERROR_MDG | 0x05, /**< communication error in lower layer */
+       MDG_ERROR_NOT_STARTED = TIZEN_ERROR_MDG | 0x06, /**< Service Not Started */
+       MDG_ERROR_DB = TIZEN_ERROR_MDG | 0x07, /**< Internal DB error */
+       MDG_ERROR_NOT_PROPER_GROUP = TIZEN_ERROR_MDG | 0x08, /**< Not proper group */
+       MDG_ERROR_NOT_PROPER_DEVICE = TIZEN_ERROR_MDG | 0x09, /**< Not proper device */
 } mdg_error_e;
 
 /**
@@ -119,7 +116,6 @@ typedef void *mdg_group_h; /**< Group handle */
  *
  * @return @c 0 on success, otherwise negative error value
  * @retval #MDG_ERROR_NONE Successful
- * @retval #MDG_ERROR_ALREADY_INITIALIZED Already initialized
  * @retval #MDG_ERROR_INVALID_PARAMETER Invalid parameter
  * @retval #MDG_ERROR_OPERATION_FAILED Operation failed
  * @retval #MDG_ERROR_OUT_OF_MEMORY Out of memory
@@ -280,6 +276,24 @@ int mdg_group_create(mdg_h handle, char *group_name);
 int mdg_group_delete(mdg_h handle, mdg_group_h group);
 
 /**
+ * @brief Called after mdg_group_invite_device().
+ * @details Called when the maximum wait time elapsed or after the mdg_group_invite_device()
+ * finished successfully.
+ *
+ * @since_tizen 5.0
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/internet
+ *
+ * @param[out] result Result of operation
+ * @param[out] user_data User data pointer
+ *
+ * @pre The callback must be registered with mdg_group_invite_device()
+ *
+ * @see mdg_group_invite_device()
+ */
+typedef bool (*mdg_group_foreach_cb)(mdg_device_h device, void *user_data);
+
+/**
  * @brief Gets devices list in a group resource.
  * @details This function retrieves a devices list of group resources.
  *
@@ -306,8 +320,8 @@ int mdg_group_delete(mdg_h handle, mdg_group_h group);
  * @see mdg_group_merge()
  *
  */
-int mdg_group_get_member_devices(mdg_h handle,
-               mdg_group_h group, mdg_device_h **devices, int *count);
+int mdg_group_foreach_device(mdg_h handle, mdg_group_h group,
+       mdg_group_foreach_cb foreach_cb, void *user_data);
 
 /**
  * @brief Called after mdg_group_invite_device().
@@ -996,4 +1010,4 @@ int mdg_device_info_get_type(mdg_device_h device,
 }
 #endif
 
-#endif /* __TIZEN_NETWORK_MULTI_DEVICE_GROUP_H__ */
+#endif /* __TIZEN_NETWORK_MULTI_DEVICE_GROUP_MDG_H__ */
diff --git a/include/mdg_internal.h b/include/mdg_internal.h
deleted file mode 100644 (file)
index 42266b7..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Network Configuration Module
- *
- * Copyright (c) 2017 Samsung Electronics Co., Ltd. All rights reserved.
- *
- * 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 __TIZEN_NETWORK_MDG_INTERNAL_H__
-#define __TIZEN_NETWORK_MDG_INTERNAL_H__
-
-#include <mdg.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * @brief Creates a group handle.
- *
- * @since_tizen 5.0
- *
- * @remarks The @a group should be released using mdg_group_info_destroy().
- *
- * @param[in] group Group handle
- *
- * @return 0 on success, otherwise a negative error value.
- * @retval #MDG_ERROR_NONE Successful
- * @retval #MDG_ERROR_INVALID_PARAMETER Invalid parameter
- *
- * @see mdg_group_info_clone()
- * @see mdg_group_info_destroy()
- * @see mdg_group_info_get_type()
- * @see mdg_group_info_get_resource_type()
- * @see mdg_group_info_get_uri_path()
- * @see mdg_group_info_get_name()
- * @see mdg_group_info_get_host_addr()
- *
- */
-int mdg_group_info_create(mdg_group_h* group);
-
-
-/**
- * @brief Creates a device handle.
- *
- * @since_tizen 5.0
- *
- * @remarks The @a device should be released using mdg_device_info_destroy().
- *
- * @param[out] device Device handle
- *
- * @return 0 on success, otherwise a negative error value.
- * @retval #MDG_ERROR_NONE Successful
- * @retval #MDG_ERROR_INVALID_PARAMETER Invalid parameter
- *
- * @see mdg_device_info_clone()
- * @see mdg_device_info_destroy()
- * @see mdg_device_info_get_device_id()
- * @see mdg_device_info_get_ip()
- * @see mdg_device_info_get_device_type()
- *
- */
-int mdg_device_info_create(mdg_device_h* device);
-
-/**
- * @brief Combines both groups to one.
- * @details This function combines two groups into one.
- *
- * @since_tizen 5.0
- * @privlevel public
- * @privilege %http://tizen.org/privilege/internet
- *
- * @param[in] handle The multi device group manager handle
- * @param[in] dest_group Group handle to be united
- * @param[in] src_group Group handle to be merged
- *
- *
- * @return 0 on success, otherwise a negative error value.
- * @retval #MDG_ERROR_NONE Successful
- * @retval #MDG_ERROR_INVALID_PARAMETER Invalid parameter
- *
- * @see mdg_group_create()
- * @see mdg_request_join_group()
- * @see mdg_request_leave_group ()
- * @see mdg_group_delete()
- * @see mdg_group_get_member_devices()
- *
- */
-int mdg_group_merge(mdg_h handle, mdg_group_h dest_group,
-               mdg_group_h src_group);
-
-
-/**
- * @brief Registers request result callback funtion.
- * @details This function registers the callback function for requested group management
- * functions and receive processing results after the request complete.
- *
- * @since_tizen 5.0
- *
- * @param[in] handle The multi device group manager handle
- * @param[in] callback Callback function pointer
- * @param[in] user_data User data pointer
- *
- *
- * @return 0 on success, otherwise a negative error value.
- * @retval #MDG_ERROR_NONE Successful
- * @retval #MDG_ERROR_INVALID_PARAMETER Invalid parameter
- *
- * @see mdg_request_create_group()
- * @see mdg_request_invite_device()
- * @see mdg_request_eject_device()
- * @see mdg_request_delete_group()
- * @see mdg_device_send_data()
- *
- */
-int mdg_request_result_callback(mdg_h handle,
-               mdg_request_result_cb callback, void *user_data);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __TIZEN_NETWORK_MDG_INTERNAL_H__ */
index 7f32107..5ac3e5b 100755 (executable)
--- a/src/mdg.c
+++ b/src/mdg.c
@@ -183,7 +183,7 @@ int __add_channel_cb(mdg_manager_s *handle, char *channel_id, mdg_channel_cb cha
                for (l2 = tmp_handle->channel_cb_list; l2 != NULL; l2 = l2->next) {
                        channel_cb_s *tmp_channel = (channel_cb_s *)l2->data;
                        if (g_strcmp0(tmp_channel->channel_id, channel_id) == 0)
-                               return MDG_ERROR_UNKNOWN;
+                               return MDG_ERROR_OPERATION_FAILED;
                }
        }
 
@@ -255,7 +255,7 @@ EXPORT_API int mdg_device_unregist_channel(mdg_h handle, char *channel_id)
        }
 
        if (is_exist == false)
-               ret = MDG_ERROR_UNKNOWN;
+               ret = MDG_ERROR_OPERATION_FAILED;
 
        if (ret == MDG_ERROR_NONE) {
                group_call_unregist_channel_sync(_handle->group_proxy, channel_id, &ret, NULL, &error);
@@ -369,17 +369,45 @@ EXPORT_API int mdg_group_delete(mdg_h handle, mdg_group_h group)
        return ret;
 }
 
-EXPORT_API int mdg_group_get_member_devices(mdg_h handle,
-       mdg_group_h group, mdg_device_h **devices, int *count)
+EXPORT_API int mdg_group_foreach_device(mdg_h handle, mdg_group_h group,
+       mdg_group_foreach_cb foreach_cb, void *user_data)
 {
        int ret = MDG_ERROR_NONE;
+       GError *error = NULL;
+       GVariant *va = NULL;
+       int count;
 
        CHECK_FEATURE_SUPPORTED(MDG_FEATURE);
 
-       NOTUSED(handle);
-       NOTUSED(group);
-       NOTUSED(devices);
-       NOTUSED(count);
+       mdg_manager_s *_handle = handle;
+       mdg_check_null_ret_error("handle", handle, MDG_ERROR_INVALID_PARAMETER);
+       CHECK_HANDLE_IS_VALID(handle);
+
+       mdg_group_s *grp = (mdg_group_s *)group;
+       mdg_check_null_ret_error("grp", grp, MDG_ERROR_INVALID_PARAMETER);
+
+       group_call_foreach_device_sync(_handle->group_proxy, grp->group_name, &ret, &va, NULL, &error);
+       count = g_variant_n_children(va);
+
+       if (count > 0) {
+               GVariantIter *iter = NULL;
+               GVariant *device_va = NULL;
+               int result;
+
+               g_variant_get(va, "aa{sv}", &iter);
+               while ((device_va = g_variant_iter_next_value(iter)) != NULL) {
+                       mdg_device_s *device;
+
+                       device = mdg_get_device_from_variant(device_va);
+
+                       result = foreach_cb((mdg_device_h)device, user_data);
+                       if (result == false)
+                               break;
+               }
+               g_variant_iter_free(iter);
+       }
+
+       g_variant_unref(va);
 
        return ret;
 }
@@ -390,6 +418,7 @@ EXPORT_API int mdg_group_invite_device(mdg_h handle,
 {
        int ret = MDG_ERROR_NONE;
        GError *error = NULL;
+       GVariant *va = NULL;
 
        CHECK_FEATURE_SUPPORTED(MDG_FEATURE);
 
@@ -406,11 +435,13 @@ EXPORT_API int mdg_group_invite_device(mdg_h handle,
        mdg_device_h cloned_device = NULL;
        mdg_device_info_clone(&cloned_device, device);
 
+       va = mdg_create_variant_device(dev);
+
        _handle->device_invite_finish_cb.finish_cb = finish_cb;
        _handle->device_invite_finish_cb.user_data = user_data;
        _handle->device_invite_finish_cb.device = cloned_device;
 
-       group_call_invite_device_sync(_handle->group_proxy, grp->group_name, dev->device_id, PIN,
+       group_call_invite_device_sync(_handle->group_proxy, grp->group_name, va, PIN,
                &ret, NULL, &error);
 
        return ret;
index 57e2a0f..a2c466e 100755 (executable)
@@ -32,7 +32,7 @@
                </method>
                <method name="InviteDevice">
                        <arg type="s" name="group_name" direction="in"/>
-                       <arg type="s" name="uuid" direction="in"/>
+                       <arg type="a{sv}" name="device_info" direction="in"/>
                        <arg type="s" name="pin" direction="in" />
                        <arg type="i" name="result" direction="out"/>
                </method>
@@ -44,8 +44,9 @@
                <method name="GetLocalDevice">
                        <arg type="a{sv}" name="local_device" direction="out" />
                </method>
-               <method name="GetMemberDevices">
+               <method name="ForeachDevice">
                        <arg type="s" name="group_name" direction="in"/>
+                       <arg type="i" name="result" direction="out" />
                        <arg type="aa{sv}" name="devices" direction="out" />
                </method>
                <method name="RegistChannel">
index 486de50..1ea0eae 100755 (executable)
@@ -155,7 +155,7 @@ typedef struct _mdg_manager_s {
 
 #define CHECK_HANDLE_IS_VALID(handle) { \
        if (g_slist_find(handle_list, handle) == NULL) \
-               return MDG_ERROR_UNKNOWN; \
+               return MDG_ERROR_OPERATION_FAILED; \
 }
 
 /**
index 9282b3a..fcbf729 100755 (executable)
@@ -180,3 +180,43 @@ void mdg_get_data_from_variant(GVariant *va, char **device_id, char **channel_id
        g_variant_iter_free(iter);
 }
 
+GVariant *mdg_create_variant_device(mdg_device_s *device)
+{
+       GVariant *va;
+
+       GVariantBuilder *builder;
+
+       builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+
+       g_variant_builder_add(builder, "{sv}", "DeviceID",
+                                                 g_variant_new_string(device->device_id));
+       g_variant_builder_add(builder, "{sv}", "Port",
+                                                 g_variant_new_int32(device->port));
+       g_variant_builder_add(builder, "{sv}", "Addr",
+                                                 g_variant_new_string(device->addr));
+       g_variant_builder_add(builder, "{sv}", "ConnType",
+                                                 g_variant_new_int32(device->conn_type));
+       g_variant_builder_add(builder, "{sv}", "SecurePort",
+                                                 g_variant_new_int32(device->secure_port));
+       g_variant_builder_add(builder, "{sv}", "ModelName",
+                                                 g_variant_new_string(device->model_name));
+       g_variant_builder_add(builder, "{sv}", "DeviceName",
+                                                 g_variant_new_string(device->device_name));
+       g_variant_builder_add(builder, "{sv}", "PlatformVer",
+                                                 g_variant_new_string(device->platform_ver));
+       g_variant_builder_add(builder, "{sv}", "VendorID",
+                                                 g_variant_new_string(device->vendor_id));
+       g_variant_builder_add(builder, "{sv}", "Profile",
+                                                 g_variant_new_string(device->profile));
+       g_variant_builder_add(builder, "{sv}", "IsInvited",
+                                                 g_variant_new_int32(device->is_invited));
+       g_variant_builder_add(builder, "{sv}", "Type",
+                                                 g_variant_new_int32(device->type));
+
+       va = g_variant_builder_end(builder);
+
+       g_variant_builder_unref(builder);
+
+       return va;
+}
+
index 8ad2bcc..0a5e141 100755 (executable)
@@ -25,6 +25,7 @@ extern "C"
 {
 #endif
 
+GVariant *mdg_create_variant_device(mdg_device_s *device);
 mdg_device_s *mdg_get_device_from_variant(GVariant *va);
 mdg_group_s *mdg_get_group_from_variant(GVariant *va);
 void mdg_get_channel_from_variant(GVariant *va, char **device_id, char **channel_id);
index 3d807f8..b522dcd 100755 (executable)
@@ -26,7 +26,6 @@
 #include <glib.h>
 #include <gio/gio.h>
 
-#include <mdg_internal.h>
 #include "menu.h"
 #include "common.h"
 
@@ -52,13 +51,15 @@ const char* mdg_error_to_string(mdg_error_e err)
        CASE_TO_STR(MDG_ERROR_PERMISSION_DENIED)
        CASE_TO_STR(MDG_ERROR_NOT_SUPPORTED)
        CASE_TO_STR(MDG_ERROR_OPERATION_FAILED)
+       CASE_TO_STR(MDG_ERROR_NO_DATA)
        CASE_TO_STR(MDG_ERROR_ALREADY_REGISTERED)
        CASE_TO_STR(MDG_ERROR_IN_PROGRESS)
-       CASE_TO_STR(MDG_ERROR_RX)
-       CASE_TO_STR(MDG_ERROR_TX)
-       CASE_TO_STR(MDG_ERROR_PLUGIN_FAIL)
-       CASE_TO_STR(MDG_ERROR_ALREADY_IN_PROGRESS)
+       CASE_TO_STR(MDG_ERROR_COMM_ERROR)
        CASE_TO_STR(MDG_ERROR_NOT_STARTED)
+       CASE_TO_STR(MDG_ERROR_DB)
+       CASE_TO_STR(MDG_ERROR_NOT_PROPER_GROUP)
+       CASE_TO_STR(MDG_ERROR_NOT_PROPER_DEVICE)
+
        default :
                return "MDG_ERROR_UNKNOWN";
        }
@@ -85,7 +86,7 @@ static int __init_func(MManager *mm, struct menu_data *menu)
                                mdg_error_to_string(ret), ret);
                return RET_FAILURE;
        }
-
+/*
        ret = mdg_request_result_callback(handle, receive_request_result,
                                        NULL);
        if (ret != 0) {
@@ -93,7 +94,7 @@ static int __init_func(MManager *mm, struct menu_data *menu)
                                mdg_error_to_string(ret), ret);
                return RET_FAILURE;
        }
-
+*/
        return RET_SUCCESS;
 }
 
index b030ce1..a131433 100755 (executable)
@@ -158,6 +158,52 @@ static int run_device_eject(MManager *mm, struct menu_data *menu)
        return RET_SUCCESS;
 }
 
+bool __group_foreach_cb(mdg_device_h device, void *user_data)
+{
+       char *addr;
+       char *device_id;
+
+       mdg_device_info_get_addr(device, &addr);
+       mdg_device_info_get_device_id(device, &device_id);
+       msgb("\rDevice is [%s], ADDR [%s]", device_id, addr);
+
+       return true;
+}
+
+static int run_group_foreach_device(MManager *mm, struct menu_data *menu)
+{
+       int ret;
+       int idx;
+       mdg_group_h group = NULL;
+
+       msg("Foreach Device");
+
+       if (strlen(group_idx)) {
+               idx = (unsigned short)strtol(group_idx, NULL, 10);
+               if (0 >= idx) {
+                       msgp("Invalid index. set to 1");
+                       idx = 1;
+               }
+       }
+
+       if (found_group_list) {
+               group = g_list_nth_data(found_group_list, idx - 1);
+               if (NULL == group) {
+                       msgr("Failed to g_hash_table_find");
+                       return RET_FAILURE;
+               }
+       }
+
+       ret = mdg_group_foreach_device(handle, group, __group_foreach_cb, NULL);
+       if (MDG_ERROR_NONE != ret) {
+               msgr("Failed to foreach device: [%s(0x%X)]", mdg_error_to_string(ret), ret);
+               return RET_FAILURE;
+       }
+       msg(" - mdg_group_foreach_device() ret: [0x%X] [%s]", ret, mdg_error_to_string(ret));
+
+       return RET_SUCCESS;
+}
+
 void _device_invite_result_cb(int result, mdg_device_h device, void *user_data)
 {
        bool is_invited;
@@ -1229,6 +1275,13 @@ static struct menu_data menu_group_eject_device[] = {
        { NULL, NULL, },
 };
 
+static struct menu_data menu_group_foreach_device[] = {
+       { "0", "Show Found Group(s)", NULL, run_group_show, NULL },
+       { "1", "Group Index", NULL, NULL, group_idx },
+       { "2", "Run", NULL, run_group_foreach_device, NULL },
+       { NULL, NULL, },
+};
+
 static struct menu_data menu_send_data[] = {
        { "0", "Show Found Invited Device(s)", NULL, run_invited_devices_show, NULL },
        { "1", "Show Channel List", NULL, run_channels_show, NULL },
@@ -1311,14 +1364,15 @@ struct menu_data menu_mdg_manager[] = {
        { "7", "Show Device(s)", NULL, run_devices_show, NULL }, // 0
        { "8", "Invite Device", menu_group_invite_device, NULL, NULL }, // 0
        { "9", "Eject Device", menu_group_eject_device, NULL, NULL }, // 0
-       { "10", "Find Invited Device(s)", menu_devices_find_invited_device, NULL, NULL }, // 0
-       { "11", "Show Invited Device(s)", NULL, run_invited_devices_show, NULL }, // 0
-       { "12", "Request Create Group", menu_request_create_group, NULL, NULL }, // 0
-       { "13", "Request Invite", menu_request_invite, NULL, NULL }, // 0
-       { "14", "Request Eject", menu_request_eject, NULL, NULL }, // 0
-       { "15", "Request Channel List", menu_request_channel_list, NULL, NULL }, // 0
-       { "16", "Regist Channel", menu_regist_channel, NULL, NULL },
-       { "17", "Unregist Channel", menu_unregist_channel, NULL, NULL },
-       { "18", "Send Message", menu_send_data, NULL, NULL }, // 0
-       { "19", "Send File", menu_send_file, NULL, NULL }, // 0
+       { "10", "Foreach Device", menu_group_foreach_device, NULL, NULL },
+       { "11", "Find Invited Device(s)", menu_devices_find_invited_device, NULL, NULL }, // 0
+       { "12", "Show Invited Device(s)", NULL, run_invited_devices_show, NULL }, // 0
+       { "13", "Request Create Group", menu_request_create_group, NULL, NULL }, // 0
+       { "14", "Request Invite", menu_request_invite, NULL, NULL }, // 0
+       { "15", "Request Eject", menu_request_eject, NULL, NULL }, // 0
+       { "16", "Request Channel List", menu_request_channel_list, NULL, NULL }, // 0
+       { "17", "Regist Channel", menu_regist_channel, NULL, NULL },
+       { "18", "Unregist Channel", menu_unregist_channel, NULL, NULL },
+       { "19", "Send Message", menu_send_data, NULL, NULL }, // 0
+       { "20", "Send File", menu_send_file, NULL, NULL }, // 0
 };