--- /dev/null
+// Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by a apache 2.0 license that can be
+// found in the LICENSE file.
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <mdg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "groupctl/menu_descriptor.h"
+#include "groupctl/menu_item.h"
+
+namespace {
+
+const int kRequestTimeout = 3;
+const char kGroupName[] = "capmgrgroup";
+const char kPIN[] = "12341234";
+
+const std::map<int, std::string> kMDGErrorString = {
+ {MDG_ERROR_NONE, "MDG_ERROR_NONE"},
+ {MDG_ERROR_IO_ERROR, "MDG_ERROR_IO_ERROR"},
+ {MDG_ERROR_INVALID_PARAMETER, "MDG_ERROR_INVALID_PARAMETER"},
+ {MDG_ERROR_OUT_OF_MEMORY, "MDG_ERROR_OUT_OF_MEMORY"},
+ {MDG_ERROR_PERMISSION_DENIED, "MDG_ERROR_PERMISSION_DENIED"},
+ {MDG_ERROR_NOT_SUPPORTED, "MDG_ERROR_NOT_SUPPORTED"},
+ {MDG_ERROR_OPERATION_FAILED, "MDG_ERROR_OPERATION_FAILED"},
+ {MDG_ERROR_NO_DATA, "MDG_ERROR_NO_DATA"},
+ {MDG_ERROR_ALREADY_REGISTERED, "MDG_ERROR_ALREADY_REGISTERED"},
+ {MDG_ERROR_IN_PROGRESS, "MDG_ERROR_IN_PROGRESS"},
+ {MDG_ERROR_COMM_ERROR, "MDG_ERROR_COMM_ERROR"},
+ {MDG_ERROR_NOT_STARTED, "MDG_ERROR_NOT_STARTED"},
+ {MDG_ERROR_DB, "MDG_ERROR_DB"},
+ {MDG_ERROR_NOT_PROPER_GROUP, "MDG_ERROR_NOT_PROPER_GROUP"},
+ {MDG_ERROR_NOT_PROPER_DEVICE, "MDG_ERROR_NOT_PROPER_DEVICE"},
+};
+
+std::string MDGErrorToString(int error) {
+ auto it = kMDGErrorString.find(error);
+ if (it == kMDGErrorString.end())
+ return "MDG_ERROR_UNKNOWN";
+ else
+ return it->second;
+}
+
+struct MDGContext {
+ mdg_h mdg_handle;
+ mdg_group_h group_handle;
+ std::vector<mdg_device_h> device_list;
+};
+
+struct MDGContext mdg_ctx;
+
+void CleanMDGDeviceList() {
+ std::for_each(mdg_ctx.device_list.begin(), mdg_ctx.device_list.end(),
+ [](mdg_device_h device) { mdg_device_info_destroy(device); });
+ mdg_ctx.device_list.clear();
+}
+
+bool GroupFoundCb(mdg_group_type_e type, mdg_group_h group, void* user_data) {
+ char* group_name;
+ int ret = mdg_group_info_get_name(group, &group_name);
+ if (ret != MDG_ERROR_NONE) {
+ std::cout << "Failed to get group name: " << MDGErrorToString(ret)
+ << std::endl;
+ return false;
+ }
+
+ ret = strcmp(group_name, kGroupName);
+ free(group_name);
+
+ // find local capmgrgroup only
+ if (ret == 0 && type == MDG_GROUP_TYPE_LOCAL) {
+ ret = mdg_group_info_clone(&mdg_ctx.group_handle, group);
+ if (ret != MDG_ERROR_NONE)
+ std::cout << "Failed to clone group info: " << MDGErrorToString(ret)
+ << std::endl;
+ }
+
+ return true;
+}
+
+void GroupFinishCb(int result, void* user_data) {
+ std::cout << "Find group finished: " << MDGErrorToString(result) << std::endl;
+}
+
+bool DeviceFoundCb(mdg_device_h device, void* user_data) {
+ char* device_id;
+ bool is_invited;
+ char* model_name;
+
+ int ret = mdg_device_info_get_device_id(device, &device_id);
+ if (ret != MDG_ERROR_NONE) {
+ std::cout << "Failed to get device id: " << MDGErrorToString(ret);
+ return true;
+ }
+
+ ret = mdg_device_info_is_invited(device, &is_invited);
+ if (ret != MDG_ERROR_NONE) {
+ std::cout << "Failed to get is_invited: " << MDGErrorToString(ret)
+ << std::endl;
+ free(device_id);
+ return true;
+ }
+
+ ret = mdg_device_info_get_model_name(device, &model_name);
+ if (ret != MDG_ERROR_NONE) {
+ std::cout << "Failed to get model_name: " << MDGErrorToString(ret)
+ << std::endl;
+ free(device_id);
+ return true;
+ }
+
+ std::cout << "device_id: " << device_id
+ << ", is_invited: " << is_invited
+ << ", model_name: " << model_name
+ << std::endl;
+
+ free(model_name);
+ free(device_id);
+
+ // TODO(jeremy.jang): Do not add device which doesn't have capmgr group
+ mdg_device_h clone;
+ ret = mdg_device_info_clone(&clone, device);
+ if (ret != MDG_ERROR_NONE) {
+ std::cout << "Failed to clone device: " << MDGErrorToString(ret)
+ << std::endl;
+ }
+
+ mdg_ctx.device_list.emplace_back(clone);
+
+ return true;
+}
+
+void DeviceFinishCb(int result, void* user_data) {
+ std::cout << "Find device finished: " << MDGErrorToString(result)
+ << std::endl;
+}
+
+void GroupInviteFinishCb(int result, mdg_device_h invited_device,
+ void* user_data) {
+ std::cout << "Invite device finished: " << MDGErrorToString(result)
+ << std::endl;
+}
+
+} // namespace
+
+namespace capmgr {
+
+class InviteDeviceMenuItem : public MenuItem {
+ public:
+ explicit InviteDeviceMenuItem(MenuDescriptor* descriptor);
+
+ void SelectDeviceId();
+ void Invite();
+ private:
+ int idx_;
+};
+
+InviteDeviceMenuItem::InviteDeviceMenuItem(MenuDescriptor* descriptor)
+ : MenuItem(descriptor), idx_(0) {
+ items_ = {
+ {"Select Device Id", [=] { this->SelectDeviceId(); }},
+ {"Invite", [=] { this->Invite(); }},
+ };
+}
+
+void InviteDeviceMenuItem::SelectDeviceId() {
+ int i = 1;
+
+ for (const auto& device : mdg_ctx.device_list) {
+ char* device_id;
+ int ret = mdg_device_info_get_device_id(device, &device_id);
+ if (ret != MDG_ERROR_NONE)
+ continue;
+ std::cout << i++ << " - " << device_id << std::endl;
+ free(device_id);
+ }
+
+ // TODO(jeremy.jang): revise this
+ char input[8];
+ std::cout << "input idx > ";
+ std::cin.getline(input, sizeof(input));
+ if (std::cin.fail())
+ std::cin.clear();
+
+ try {
+ i = std::stoi(input);
+ if (i < 1 || i > static_cast<int>(mdg_ctx.device_list.size())) {
+ std::cout << "invalid number" << std::endl;
+ return;
+ }
+ idx_ = i - 1;
+ } catch (const std::invalid_argument& e) {
+ // wrong input
+ }
+}
+
+void InviteDeviceMenuItem::Invite() {
+ if (mdg_ctx.device_list.size() == 0) {
+ std::cout << "Please search device first!" << std::endl;
+ return;
+ }
+
+ if (!mdg_ctx.group_handle) {
+ std::cout << "Please search group first!" << std::endl;
+ return;
+ }
+
+ mdg_device_h device = mdg_ctx.device_list.at(idx_);
+ char* device_id;
+ int ret = mdg_device_info_get_device_id(device, &device_id);
+ if (ret != MDG_ERROR_NONE) {
+ std::cout << "Failed to get device id: " << MDGErrorToString(ret)
+ << std::endl;
+ }
+
+ std::cout << "Invite device " << device_id << std::endl;
+ free(device_id);
+
+ ret = mdg_group_invite_device(mdg_ctx.mdg_handle, mdg_ctx.group_handle,
+ device, const_cast<char*>(kPIN), &GroupInviteFinishCb, nullptr);
+ std::cout << "mdg_group_invite_device() returns: " << MDGErrorToString(ret)
+ << std::endl;
+}
+
+class MainMenuItem : public MenuItem {
+ public:
+ explicit MainMenuItem(MenuDescriptor* descriptor);
+
+ void CreateGroup();
+ void SearchGroup();
+ void FindDevices();
+ void InviteDevice();
+};
+
+MainMenuItem::MainMenuItem(MenuDescriptor* descriptor) : MenuItem(descriptor) {
+ items_ = {
+ {"Create capmgr Group", [=] { this->CreateGroup(); }},
+ {"Search Groups", [=] { this->SearchGroup(); }},
+ {"Find Devices", [=] { this->FindDevices(); }},
+ {"Invite Device", [=] { this->InviteDevice(); }},
+ };
+}
+
+void MainMenuItem::CreateGroup() {
+ int ret = mdg_group_create(mdg_ctx.mdg_handle,
+ const_cast<char*>(kGroupName));
+ std::cout << "mdg_group_create() returns: " << MDGErrorToString(ret)
+ << std::endl;
+}
+
+void MainMenuItem::SearchGroup() {
+ if (mdg_ctx.group_handle) {
+ std::cout << "Local capmgrgroup already found" << std::endl;
+ return;
+ }
+
+ int ret = mdg_group_find(mdg_ctx.mdg_handle, kRequestTimeout,
+ &GroupFoundCb, &GroupFinishCb, this);
+ std::cout << "mdg_group_find() returns: " << MDGErrorToString(ret)
+ << std::endl;
+}
+
+void MainMenuItem::FindDevices() {
+ int ret = mdg_device_find(mdg_ctx.mdg_handle, kRequestTimeout, false,
+ &DeviceFoundCb, &DeviceFinishCb, nullptr);
+ std::cout << "mdg_device_find() returns: " << MDGErrorToString(ret)
+ << std::endl;
+}
+
+void MainMenuItem::InviteDevice() {
+ descriptor_->PushMenu(
+ std::shared_ptr<MenuItem>(new InviteDeviceMenuItem(descriptor_)));
+}
+
+class InitMenuItem : public MenuItem {
+ public:
+ explicit InitMenuItem(MenuDescriptor* descriptor);
+ ~InitMenuItem();
+
+ void Init();
+};
+
+InitMenuItem::InitMenuItem(MenuDescriptor* descriptor) : MenuItem(descriptor) {
+ items_ = {
+ {"Init", [=] { this->Init(); }},
+ };
+}
+
+InitMenuItem::~InitMenuItem() {
+ int ret;
+
+ if (mdg_ctx.mdg_handle) {
+ ret = mdg_deinitialize(mdg_ctx.mdg_handle);
+ if (ret != MDG_ERROR_NONE)
+ std::cout << "Failed to deinitialize mdg: " << MDGErrorToString(ret)
+ << std::endl;
+ }
+
+ if (mdg_ctx.group_handle)
+ mdg_group_info_destroy(mdg_ctx.group_handle);
+
+ CleanMDGDeviceList();
+}
+
+void InitMenuItem::Init() {
+ if (!mdg_ctx.mdg_handle) {
+ int ret = mdg_initialize(&mdg_ctx.mdg_handle);
+ if (ret != MDG_ERROR_NONE) {
+ std::cout << "Failed to initialize mdg: " << MDGErrorToString(ret);
+ return;
+ }
+ } else {
+ std::cout << "Already initialized" << std::endl;
+ }
+
+ descriptor_->PushMenu(
+ std::shared_ptr<MenuItem>(new MainMenuItem(descriptor_)));
+}
+
+} // namespace capmgr
+
+gboolean KeyboardInputCb(GIOChannel* src, GIOCondition cond, gpointer data) {
+ capmgr::MenuDescriptor* descriptor =
+ reinterpret_cast<capmgr::MenuDescriptor*>(data);
+
+ char buf[1024];
+ if (!fgets(buf, sizeof(buf), stdin))
+ return FALSE;
+ // remove '\r'
+ buf[strlen(buf) - 1] = '\0';
+ descriptor->Eval(buf);
+ descriptor->PrintPrompt();
+
+ return TRUE;
+}
+
+int main(int argc, char* argv[]) {
+ capmgr::MenuDescriptor descriptor;
+ std::shared_ptr<capmgr::MenuItem> mainmenu =
+ std::shared_ptr<capmgr::MenuItem>(new capmgr::InitMenuItem(&descriptor));
+ descriptor.PushMenu(mainmenu);
+ descriptor.PrintPrompt();
+
+ GMainLoop* mainloop = g_main_loop_new(nullptr, FALSE);
+ if (!mainloop) {
+ std::cout << "Failed to create main loop!" << std::endl;
+ return 1;
+ }
+
+ GIOChannel* channel = g_io_channel_unix_new(STDIN_FILENO);
+ if (!channel) {
+ std::cout << "Failed to create stdin GIOChannel!" << std::endl;
+ g_main_loop_unref(mainloop);
+ return 1;
+ }
+
+ guint sid = g_io_add_watch(channel, G_IO_IN, KeyboardInputCb, &descriptor);
+
+ g_main_loop_run(mainloop);
+
+ g_source_remove(sid);
+ g_io_channel_unref(channel);
+ g_main_loop_unref(mainloop);
+
+ return 0;
+}