From: Sehong Na Date: Sat, 31 May 2014 04:19:46 +0000 (+0900) Subject: Initialize Tizen 2.3 X-Git-Tag: 2.3a_release X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fheads%2Ftizen_2.3;p=framework%2Fconvergence%2Fweconn.git Initialize Tizen 2.3 --- 942bc875412bc7b5739cc05ac3425299f1058b1b diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..17eecd4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ +*.[oa] +*~ +build-stamp +cmake_build_tmp +configure-stamp +.project +.cproject +.settings diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..8834b34 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,4 @@ +Danny Jeongseok Seo +Misun Kim +Sanghoon Cho +Kyoungyoup Park diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..368db4f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,87 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(weconn) + +SET(PREFIX ${CMAKE_INSTALL_PREFIX}) +SET(LIBDIR "${PREFIX}/lib") +SET(INCLUDEDIR "${PREFIX}/include") +SET(PKGCONFIGDIR "${PREFIX}/lib/pkgconfig" CACHE PATH PKGCONFIGDIR) +SET(INTROSPECTION "${CMAKE_SOURCE_DIR}/introspection") +SET(WECONNLIB "${LIBDIR}/${PROJECT_NAME}") +SET(BINDIR "${PREFIX}/sbin") +SET(main_PROGRAM "weconnd") + + +INCLUDE(FindPkgConfig) +pkg_check_modules(pkgs REQUIRED + dlog + vconf + gio-2.0 + glib-2.0 + gio-unix-2.0 + alarm-service + sap-client-stub-api + capi-appfw-application + capi-network-bluetooth + capi-system-info +) + +FOREACH(flag ${pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/include/) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -Wall -Werror -Wextra -fvisibility=hidden -fdata-sections -ffunction-sections -Wl,--gc-sections") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-parameter -Wno-missing-field-initializers -Wdeclaration-after-statement -Wmissing-declarations") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-local-typedefs -Wcast-align -Wconversion") +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g") +SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed -Wl,-z,nodelete") + +ADD_DEFINITIONS("-DVERSION=\"${VERSION}\"") +ADD_DEFINITIONS("-DEXPORT_API=__attribute__((visibility(\"default\")))") +ADD_DEFINITIONS("-DWECONN_PLUGIN_PATH=\"${WECONNLIB}\"") + +MESSAGE(${CMAKE_C_FLAGS}) +MESSAGE(${pkgs_LDFLAGS}) + +REMOVE_DEFINITIONS("-DLOG_TAG=\"CAPI_NETWORK_WECONN\"") +ADD_DEFINITIONS("-DLOG_TAG=\"WECONN\"") + + +SET(SRCS + src/dbus.c + src/main.c + src/util.c + src/error.c + src/driver.c + src/object.c + src/service.c + src/technology.c + src/control/control.c +) + +CONFIGURE_FILE(client/include/weconn_type.h include/weconn_type.h COPYONLY) + +ADD_CUSTOM_COMMAND( + WORKING_DIRECTORY + OUTPUT ${CMAKE_BINARY_DIR}/generated-code.c + COMMAND gdbus-codegen --interface-prefix net.weconn. --generate-c-code generated-code --c-namespace weconn --c-generate-object-manager --generate-docbook generated-docs + ${INTROSPECTION}/service.xml + ${INTROSPECTION}/technology.xml + COMMENT "Generating GDBus .c/.h") + +ADD_EXECUTABLE(${main_PROGRAM} ${SRCS} ${CMAKE_BINARY_DIR}/generated-code.c) +TARGET_LINK_LIBRARIES(${main_PROGRAM} ${pkgs_LDFLAGS} "-ldl -L${CMAKE_BINARY_DIR}") + +INSTALL(TARGETS ${main_PROGRAM} DESTINATION ${BINDIR}) + + +# Wearable device connection controller plugins +ADD_SUBDIRECTORY(plugins) + +# Wearable device connection controller API library in TIZEN C API (Development) +ADD_SUBDIRECTORY(client) + +# Wearable connection manager API sample +ADD_SUBDIRECTORY(test) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d8522e9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,203 @@ +Copyright (c) 2013 - 2014 Samsung Electronics Co., Ltd. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt new file mode 100755 index 0000000..eb7ad84 --- /dev/null +++ b/client/CMakeLists.txt @@ -0,0 +1,26 @@ +SET(CLIENT "weconn") + +REMOVE_DEFINITIONS("-DLOG_TAG=\"WECONN\"") +ADD_DEFINITIONS("-DLOG_TAG=\"CAPI_NETWORK_WECONN\"") + + +CONFIGURE_FILE(../src/util.c src/util.c COPYONLY) + +SET(CLIENT_SRCS + src/util.c + src/weconnection.c +) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include/) + +ADD_LIBRARY(${CLIENT} SHARED ${CLIENT_SRCS}) +TARGET_LINK_LIBRARIES(${CLIENT} ${pkgs_LDFLAGS}) +SET_TARGET_PROPERTIES(${CLIENT} PROPERTIES VERSION ${VERSION} SOVERSION 0 OUTPUT_NAME ${CLIENT}) + +# pkgconfig file +CONFIGURE_FILE(${CLIENT}.pc.in ${CLIENT}.pc @ONLY) + +# install +INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION include/${CLIENT}) +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${CLIENT}.pc DESTINATION ${LIBDIR}/pkgconfig) +INSTALL(TARGETS ${CLIENT} DESTINATION ${LIBDIR}) diff --git a/client/include/weconn.h b/client/include/weconn.h new file mode 100644 index 0000000..056b53c --- /dev/null +++ b/client/include/weconn.h @@ -0,0 +1,163 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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 __WECONN_CLIENT_H__ +#define __WECONN_CLIENT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "weconn_type.h" + +/** + * @addtogroup CAPI_WEARABLE_DEVICE_CONNECTION_MANAGER_MODULE + * @{ + */ + +/** + * @brief The wearable connection handle for all connection functions. + */ +typedef void *weconn_h; + +/** +* @brief Called after weconn_connect_service() is completed. +* @param[in] result The result +* @param[in] user_data The user data passed from weconn_connect_service() +* @pre weconn_connect_service() will invoke this callback function. +* @see weconn_connect_service() +*/ +typedef void (*weconn_connected_cb) (int result, void *user_data); + +/** + * @brief Creates a handle for managing wearable connections. + * @remarks @a handle must be released with weconn_destroy(). + * @param[out] conn The handle of the wearable connection + * @return On success, 0 is returned. On error, -1 or -errno is returned. + * All the errno numbers specified by POSIX, the ISO C standard. + * @see weconn_destroy() + */ +int weconn_create(weconn_h *conn); + +/** + * @brief Destroys the wearable connection handle. + * @param[in] conn The handle of the wearable connection + * @return On success, 0 is returned. On error, -1 or -errno is returned. + * All the errno numbers specified by POSIX, the ISO C standard. + * @see weconn_create() + */ +int weconn_destroy(weconn_h conn); + +/** + * @brief Gets the wearable connection service state. + * @details The returned state is for the wearable connection service type. + * @param[in] conn The handle of the wearable connection + * @param[in] type The wearable connection service type + * @param[out] state The state of wearable connection service type + * @return On success, 0 is returned. On error, -1 or -errno is returned. + * All the errno numbers specified by POSIX, the ISO C standard. + */ +int weconn_get_service_state(weconn_h conn, + weconn_service_type_e type, weconn_service_state_e *state); + +/** + * @brief Gets the wearable device state. + * @details The returned state is for the connection device type. + * @param[in] conn The handle of the wearable connection + * @param[in] type The wearable connection device type + * @param[out] state The state of wearable connection device type + * @return On success, 0 is returned. On error, -1 or -errno is returned. + * All the errno numbers specified by POSIX, the ISO C standard. + */ +int weconn_get_device_state(weconn_h conn, + weconn_device_type_e type, weconn_device_state_e *state); + +/** + * @brief Connect a service.. + * @param[in] conn The handle of the wearable connection + * @param[in] type The wearable connection service type + * @param[in] callback The callback function to be called. + * This can be NULL if you don't want to get the notification. + * @param[in] user_data The user data passed to the callback function + * @return On success, 0 is returned. On error, -1 or -errno is returned. + * All the errno numbers specified by POSIX, the ISO C standard. + */ +int weconn_connect_service(weconn_h conn, weconn_service_type_e type, + weconn_connected_cb callback, void *user_data); + +/** + * @brief Disconnect a service.. + * @param[in] conn The handle of the wearable connection + * @param[in] type The wearable connection service type + * @param[in] callback The callback function to be called. + * This can be NULL if you don't want to get the notification. + * @param[in] user_data The user data passed to the callback function + * @return On success, 0 is returned. On error, -1 or -errno is returned. + * All the errno numbers specified by POSIX, the ISO C standard. + */ +int weconn_disconnect_service(weconn_h conn, weconn_service_type_e type, + weconn_connected_cb callback, void *user_data); + +/** + * @brief Called when the state of service is changed + * @param[out] state The state of service + * @param[out] user_data The user data passed from the callback + * registration function + * @see weconn_set_service_state_change_cb() + * @see weconn_unset_service_state_change_cb() + */ +typedef void(*weconn_service_state_changed_cb) + (weconn_service_state_e state, void *user_data); + +/** + * @brief Registers the callback called when the state of service is + * changed. + * @param[in] conn The handle of the wearable connection + * @param[in] callback The callback function to be called + * @param[in] type The wearable connection service type + * @param[in] user_data The user data passed from the callback + * registration function + * @return On success, 0 is returned. On error, -1 or -errno is returned. + * All the errno numbers specified by POSIX, the ISO C standard. + */ +int weconn_set_service_state_change_cb(weconn_h conn, + weconn_service_state_changed_cb callback, + weconn_service_type_e type, void *user_data); + +/** + * @brief Unregisters the callback called when the state of service is + * changed. + * @param[in] conn The handle of the wearable connection + * @param[in] type The wearable connection service type + * @return On success, 0 is returned. On error, -1 or -errno is returned. + * All the errno numbers specified by POSIX, the ISO C standard. + */ +int weconn_unset_service_state_change_cb(weconn_h conn, + weconn_service_type_e type); +/** +* @} +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* __WECONN_CLIENT_H__ */ diff --git a/client/include/weconn_type.h b/client/include/weconn_type.h new file mode 100644 index 0000000..7ad8a9b --- /dev/null +++ b/client/include/weconn_type.h @@ -0,0 +1,97 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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 __WECONN_TYPE_H__ +#define __WECONN_TYPE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup CAPI_WEARABLE_DEVICE_CONNECTION_ENUMERATION_TYPE + * @{ + */ + +/** + * @brief Enumerations of wearable connection service type. + */ +typedef enum +{ + /* Generic connectivity */ + W_SERVICE_TYPE_HOST_TO_WEARABLE_CONNECTIVITY = 0x01, + /* Generic connectivity between host and wearable devices */ + W_SERVICE_TYPE_INTERNET_CONNECTIVITY = 0x02, + /* Generic connectivity into the Internet accessibility */ + + /* Specific technology based connectivity */ + W_SERVICE_TYPE_BT_HFP = 0x11, /* Bluetooth Hands-Free Profile */ + W_SERVICE_TYPE_BT_SPP = 0x12, /* Bluetooth Serial Port Profile */ + W_SERVICE_TYPE_BT_PAN = 0x13, /* Bluetooth Personal Area Network */ + W_SERVICE_TYPE_BT_GATT = 0x14, + /* Bluetooth Generic Attribute Profile */ + W_SERVICE_TYPE_CELLULAR = 0x15, /* Cellular Network */ + W_SERVICE_TYPE_WIFI = 0x16, /* Wi-Fi Network */ + W_SERVICE_TYPE_WIFI_P2P = 0x17, /* Wi-Fi P2P Network */ + W_SERVICE_TYPE_WIFI_ADHOC = 0x18, /* Wi-Fi Ad-hoc Network */ + W_SERVICE_TYPE_ETHERNET = 0x19, /* Cable Network */ +} weconn_service_type_e; + +/** + * @brief Enumerations of wearable connection service state. + */ +typedef enum +{ + W_SERVICE_STATE_DISCONNECTED = 0x01, + W_SERVICE_STATE_CONNECTING = 0x02, + W_SERVICE_STATE_CONNECTED = 0x03, + W_SERVICE_STATE_DISCONNECTING = 0x04, +} weconn_service_state_e; + +/** + * @brief Enumerations of wearable connection device type. + */ +typedef enum +{ + W_DEVICE_TYPE_BT = 0x01, /* Bluetooth */ + W_DEVICE_TYPE_CELLULAR = 0x02, /* Cellular */ + W_DEVICE_TYPE_WIFI = 0x03, /* Wi-Fi */ + W_DEVICE_TYPE_WIFI_P2P = 0x04, /* Wi-Fi P2P */ + W_DEVICE_TYPE_WIFI_ADHOC = 0x05, /* Wi-Fi Ad-hoc */ + W_DEVICE_TYPE_ETHERNET = 0x06, /* Cable */ +} weconn_device_type_e; + +/** + * @brief Enumerations of wearable connection device state. + */ +typedef enum +{ + W_DEVICE_STATE_DISABLED = 0x01, + W_DEVICE_STATE_ENABLED = 0x02, +} weconn_device_state_e; + +/** +* @} +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* __WECONN_TYPE_H__ */ diff --git a/client/src/weconnection.c b/client/src/weconnection.c new file mode 100644 index 0000000..93a1d95 --- /dev/null +++ b/client/src/weconnection.c @@ -0,0 +1,858 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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. + * + */ + +#include +#include +#include +#include + +#include "log.h" +#include "dbus.h" +#include "util.h" +#include "weconn.h" + +struct weconn_connection { + GDBusConnection *connection; + GCancellable *cancellable; + + weconn_service_type_e connect_service_type; + weconn_connected_cb connected_callback; + void *connect_user_data; + + weconn_service_type_e disconnect_service_type; + weconn_connected_cb disconnected_callback; + void *disconnect_user_data; + + int ref_count; + bool lazy_destroy; +}; + +struct weconn_service_handle { + weconn_h conn; + weconn_service_state_changed_cb callback; + void *user_data; +}; + +static __thread void *handle_libweconn = NULL; +static __thread GHashTable *weconn_handle_hash = NULL; + +static __thread GHashTable *wc_internet_service_con_changed_cb_hash = NULL; +static __thread GHashTable *wc_wearable_service_con_changed_cb_hash = NULL; + +static __thread guint weconn_conn_subscribe_id_weconn_state = 0; + +static bool __weconn_check_handle_validity(weconn_h conn) +{ + struct weconn_connection *h; + + if (!conn || !weconn_handle_hash) + return false; + + h = g_hash_table_lookup(weconn_handle_hash, conn); + if (!h) + return false; + + if (h->lazy_destroy) + return false; + + return true; +} + +static GDBusConnection *__weconn_call_ref(struct weconn_connection *h) +{ + if (h->lazy_destroy) + return NULL; + + g_object_ref(h->connection); + + __sync_fetch_and_add(&h->ref_count, 1); + + return h->connection; +} + +static void __weconn_call_unref(struct weconn_connection *h) +{ + __sync_synchronize(); + if (h->ref_count < 1) + return; + + g_object_unref(h->connection); + + if (__sync_sub_and_fetch(&h->ref_count, 1) < 1 && h->lazy_destroy) + g_hash_table_remove(weconn_handle_hash, h); +} + +static const char *__weconn_get_technology_path4service( + weconn_service_type_e service_type) +{ + switch (service_type) { + case W_SERVICE_TYPE_BT_HFP: + case W_SERVICE_TYPE_BT_SPP: + case W_SERVICE_TYPE_BT_PAN: + case W_SERVICE_TYPE_BT_GATT: + return WECONN_TECHNOLOGY_BLUETOOTH_PATH; + case W_SERVICE_TYPE_CELLULAR: + return WECONN_TECHNOLOGY_CELLULAR_PATH; + case W_SERVICE_TYPE_WIFI: + return WECONN_TECHNOLOGY_WIFI_PATH; + case W_SERVICE_TYPE_WIFI_P2P: + return WECONN_TECHNOLOGY_WIFI_P2P_PATH; + case W_SERVICE_TYPE_WIFI_ADHOC: + return WECONN_TECHNOLOGY_WIFI_ADHOC_PATH; + case W_SERVICE_TYPE_ETHERNET: + return WECONN_TECHNOLOGY_ETHERNET_PATH; + default: + break; + } + + return NULL; +} + +static const char *__weconn_get_technology_path4device( + weconn_device_type_e device_type) +{ + switch (device_type) { + case W_DEVICE_TYPE_BT: + return WECONN_TECHNOLOGY_BLUETOOTH_PATH; + case W_DEVICE_TYPE_CELLULAR: + return WECONN_TECHNOLOGY_CELLULAR_PATH; + case W_DEVICE_TYPE_WIFI: + return WECONN_TECHNOLOGY_WIFI_PATH; + case W_DEVICE_TYPE_WIFI_P2P: + return WECONN_TECHNOLOGY_WIFI_P2P_PATH; + case W_DEVICE_TYPE_WIFI_ADHOC: + return WECONN_TECHNOLOGY_WIFI_ADHOC_PATH; + case W_DEVICE_TYPE_ETHERNET: + return WECONN_TECHNOLOGY_ETHERNET_PATH; + } + + return NULL; +} + +static GDBusConnection *__weconn_setup_dbus(void) +{ + GError *error = NULL; + GDBusConnection *connection = NULL; + + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + if (!connection) { + ERR("Failed to get system bus %s", error->message); + g_error_free(error); + return NULL; + } + + return connection; +} + +static void __weconn_destroy_handle(gpointer data) +{ + g_free(data); + + if (handle_libweconn && g_hash_table_size(weconn_handle_hash) < 1) { + dlclose(handle_libweconn); + handle_libweconn = NULL; + } +} + +static void __weconn_bt_connection_response(struct weconn_connection *conn, + int result, gpointer user_data) +{ + struct weconn_connection *h = conn; + if (!h) + return; + + h->connected_callback(result, h->connect_user_data); + h->connected_callback = NULL; + h->connect_service_type = 0; +} + +static void __weconn_bt_disconnection_response(struct weconn_connection *conn, + int result, gpointer user_data) +{ + struct weconn_connection *h = conn; + if (!h) + return; + + h->disconnected_callback(result, h->disconnect_user_data); + h->disconnected_callback = NULL; + h->disconnect_service_type = 0; +} + +static void __weconn_connection_response(gpointer key, gpointer value, + gpointer user_data) +{ + struct weconn_connection *h = NULL; + int *result; + + + h = (struct weconn_connection *)key; + if (!h) + return; + + result = (int *)user_data; + + if (h->connected_callback) { + switch (h->connect_service_type) { + case W_SERVICE_TYPE_BT_HFP: + case W_SERVICE_TYPE_BT_SPP: + case W_SERVICE_TYPE_BT_PAN: + case W_SERVICE_TYPE_BT_GATT: + __weconn_bt_connection_response(h, *result, user_data); + break; + case W_SERVICE_TYPE_CELLULAR: + break; + case W_SERVICE_TYPE_WIFI: + break; + case W_SERVICE_TYPE_WIFI_P2P: + break; + case W_SERVICE_TYPE_WIFI_ADHOC: + break; + case W_SERVICE_TYPE_ETHERNET: + break; + default: + break; + } + } +} + +static void __weconn_disconnection_response(gpointer key, gpointer value, + gpointer user_data) +{ + struct weconn_connection *h = NULL; + int *result; + + + h = (struct weconn_connection *)key; + if (!h) + return; + + result = (int *)user_data; + + if (h->disconnected_callback) { + switch (h->disconnect_service_type) { + case W_SERVICE_TYPE_BT_HFP: + case W_SERVICE_TYPE_BT_SPP: + case W_SERVICE_TYPE_BT_PAN: + case W_SERVICE_TYPE_BT_GATT: + __weconn_bt_disconnection_response(h, *result, user_data); + break; + case W_SERVICE_TYPE_CELLULAR: + break; + case W_SERVICE_TYPE_WIFI: + break; + case W_SERVICE_TYPE_WIFI_P2P: + break; + case W_SERVICE_TYPE_WIFI_ADHOC: + break; + case W_SERVICE_TYPE_ETHERNET: + break; + default: + break; + } + } +} + +static void __weconn_pan_service_state_changed(gpointer key, gpointer value, + gpointer user_data) +{ + weconn_h conn; + struct weconn_service_handle *h_service = NULL; + int *state = NULL; + + conn = (weconn_h)key; + if (!conn) + return; + + h_service = (struct weconn_service_handle *)value; + if (!h_service) + return; + + state = (int *)user_data; + if (!state) + return; + + if (h_service->callback) { + h_service->callback(*state, h_service->user_data); + } +} + +static void __weconn_technology_signal_filter(GDBusConnection *conn, + const gchar *name, const gchar *path, const gchar *interface, + const gchar *sig, GVariant *param, gpointer user_data) +{ + const char *service; + int result; + + if (g_strcmp0(sig, WECONN_TECHNOLOGY_SIGNAL_CONNECTION_RESULT) == 0) { + g_variant_get(param, "(si)", &service, &result); + + if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_HFP), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_SPP), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_PAN), service) == 0) { + g_hash_table_foreach(weconn_handle_hash, + __weconn_connection_response, &result); + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_GATT), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_CELLULAR), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_WIFI), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_WIFI_P2P), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_WIFI_ADHOC), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_ETHERNET), service) == 0) { + } + } else if (g_strcmp0(sig, WECONN_TECHNOLOGY_SIGNAL_DISCONNECTION_RESULT) == 0) { + g_variant_get(param, "(si)", &service, &result); + + if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_HFP), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_SPP), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_PAN), service) == 0) { + g_hash_table_foreach(weconn_handle_hash, + __weconn_disconnection_response, &result); + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_GATT), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_CELLULAR), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_WIFI), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_WIFI_P2P), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_WIFI_ADHOC), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_ETHERNET), service) == 0) { + } + } else if (g_strcmp0(sig, WECONN_TECHNOLOGY_SIGNAL_SERVICE_STATE_CHANGED) == 0) { + g_variant_get(param, "(si)", &service, &result); + + if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_HFP), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_SPP), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_PAN), service) == 0) { + g_hash_table_foreach(wc_internet_service_con_changed_cb_hash, + __weconn_pan_service_state_changed, &result); + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_GATT), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_CELLULAR), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_WIFI), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_WIFI_P2P), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_WIFI_ADHOC), service) == 0) { + } else if (g_strcmp0(XSTR(W_SERVICE_TYPE_ETHERNET), service) == 0) { + } + } else { + ERR("No handle signal(%s)", sig); + } +} + +static bool __weconn_register_signal(void) +{ + GDBusConnection *connection = NULL; + guint id; + + connection = __weconn_setup_dbus(); + if (connection == NULL) + return false; + + id = g_dbus_connection_signal_subscribe( + connection, + WECONN_SERVICE_DBUS, + WECONN_TECHNOLOGY_INTERFACE, + NULL, + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + __weconn_technology_signal_filter, + NULL, + NULL); + if (id == 0) { + ERR("Failed register signals (%d)", id); + return false; + } + + weconn_conn_subscribe_id_weconn_state = id; + + return true; +} + +static void __weconn_deregister_signal(void) +{ + GDBusConnection *connection = NULL; + + connection = __weconn_setup_dbus(); + if (connection == NULL) + return; + + g_dbus_connection_signal_unsubscribe(connection, + weconn_conn_subscribe_id_weconn_state); +} + +EXPORT_API int weconn_create(weconn_h *conn) +{ + struct weconn_connection *h; + + if (!conn || __weconn_check_handle_validity(*conn)) + return -EINVAL; + + if (!weconn_handle_hash) { + g_type_init(); + + weconn_handle_hash = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, __weconn_destroy_handle); + } + + if (!handle_libweconn) + handle_libweconn = dlopen("/usr/lib/libweconn.so", RTLD_LAZY); + + if (!wc_internet_service_con_changed_cb_hash) + wc_internet_service_con_changed_cb_hash = g_hash_table_new_full( + g_str_hash, g_str_equal, NULL, g_free); + + if (!wc_wearable_service_con_changed_cb_hash) + wc_wearable_service_con_changed_cb_hash = g_hash_table_new_full( + g_str_hash, g_str_equal, NULL, g_free); + + h = g_try_new0(struct weconn_connection, 1); + if (!h) + return -ENOMEM; + + h->connection = __weconn_setup_dbus(); + if (!h->connection) { + g_free(h); + return -EIO; + } + + h->cancellable = g_cancellable_new(); + *conn = (weconn_h)h; + + g_hash_table_replace(weconn_handle_hash, *conn, *conn); + + __weconn_register_signal(); + + return 0; +} + +EXPORT_API int weconn_destroy(weconn_h conn) +{ + struct weconn_connection *h; + + if (!__weconn_check_handle_validity(conn)) + return -EINVAL; + + h = (struct weconn_connection *)conn; + g_object_unref(h->connection); + g_cancellable_cancel(h->cancellable); + + if (wc_internet_service_con_changed_cb_hash) { + g_hash_table_destroy(wc_internet_service_con_changed_cb_hash); + wc_internet_service_con_changed_cb_hash = NULL; + } + + if (wc_wearable_service_con_changed_cb_hash) { + g_hash_table_destroy(wc_wearable_service_con_changed_cb_hash); + wc_wearable_service_con_changed_cb_hash = NULL; + } + + __sync_synchronize(); + if (h->ref_count > 0) + h->lazy_destroy = true; + else + g_hash_table_remove(weconn_handle_hash, conn); + + __weconn_deregister_signal(); + + return 0; +} + +EXPORT_API int weconn_get_service_state(weconn_h conn, + weconn_service_type_e type, weconn_service_state_e *state) +{ + GVariant *reply, *inner; + GError *error = NULL; + struct weconn_connection *h = (struct weconn_connection *)conn; + const char *service_type = NULL; + const char *value = NULL; + weconn_service_state_e value_e; + const char *technology_path = NULL; + + if (!state || !__weconn_check_handle_validity(conn)) + return -EINVAL; + + service_type = wc_service_type_enum2string(type); + if (!service_type) { + + /* TODO: please make more general to care all of technologies */ + + if (type == W_SERVICE_TYPE_HOST_TO_WEARABLE_CONNECTIVITY) + type = W_SERVICE_TYPE_BT_SPP; + else if (type == W_SERVICE_TYPE_INTERNET_CONNECTIVITY) + type = W_SERVICE_TYPE_BT_PAN; + else + return -EINVAL; + + service_type = wc_service_type_enum2string(type); + } + + technology_path = __weconn_get_technology_path4service(type); + if (!technology_path) + return -EINVAL; + + reply = g_dbus_connection_call_sync(__weconn_call_ref(h), + WECONN_SERVICE_DBUS, + technology_path, + WECONN_TECHNOLOGY_INTERFACE, + "GetProperty", + g_variant_new("(s)", service_type), + NULL, + G_DBUS_CALL_FLAGS_NONE, + DBUS_REPLY_TIMEOUT, + h->cancellable, + &error); + + __weconn_call_unref(h); + + if (error) { + ERR("Failed to request (%s)", error->message); + g_error_free(error); + + if (reply) + g_variant_unref(reply); + + return -EIO; + } + + if (!reply) { + ERR("Failed to request"); + return -EIO; + } + + g_variant_get(reply, "(v)", &inner); + value = g_variant_get_string(inner, NULL); + g_variant_unref(inner); + + value_e = wc_service_state_string2enum(value); + g_variant_unref(reply); + + if (value_e) + *state = value_e; + else + return -EIO; + + return 0; +} + +EXPORT_API int weconn_get_device_state(weconn_h conn, + weconn_device_type_e type, weconn_device_state_e *state) +{ + GVariant *reply, *inner; + GError *error = NULL; + struct weconn_connection *h = (struct weconn_connection *)conn; + const char *device_type = NULL; + const char *value = NULL; + weconn_device_state_e value_e; + const char *technology_path = NULL; + + if (!state || !__weconn_check_handle_validity(conn)) + return -EINVAL; + + device_type = wc_device_type_enum2string(type); + if (!device_type) + return -EINVAL; + + technology_path = __weconn_get_technology_path4device(type); + if (!technology_path) + return -EINVAL; + + reply = g_dbus_connection_call_sync(__weconn_call_ref(h), + WECONN_SERVICE_DBUS, + technology_path, + WECONN_TECHNOLOGY_INTERFACE, + "GetProperty", + g_variant_new("(s)", device_type), + NULL, + G_DBUS_CALL_FLAGS_NONE, + DBUS_REPLY_TIMEOUT, + h->cancellable, + &error); + + __weconn_call_unref(h); + + if (error) { + ERR("Failed to request (%s)", error->message); + g_error_free(error); + + if (reply) + g_variant_unref(reply); + + return -EIO; + } + + if (!reply) { + ERR("Failed to request"); + return -EIO; + } + + g_variant_get(reply, "(v)", &inner); + value = g_variant_get_string(inner, NULL); + g_variant_unref(inner); + + value_e = wc_device_state_string2enum(value); + g_variant_unref(reply); + + if (value_e) + *state = value_e; + else + return -EIO; + + return 0; +} + +EXPORT_API int weconn_connect_service(weconn_h conn, + weconn_service_type_e type, weconn_connected_cb callback, + void *user_data) +{ + int err = -EIO; + GVariant *reply; + GError *error = NULL; + struct weconn_connection *h = (struct weconn_connection *)conn; + const char *service_type = NULL; + const char *technology_path = NULL; + + if (!__weconn_check_handle_validity(conn)) + return -EINVAL; + + if (h->connected_callback) { + DBG("connection is inprogress"); + return -EINPROGRESS; + } + + service_type = wc_service_type_enum2string(type); + if (!service_type) { + /* TODO: please make more general to care all of technologies */ + + if (type == W_SERVICE_TYPE_HOST_TO_WEARABLE_CONNECTIVITY) + type = W_SERVICE_TYPE_BT_SPP; + else if (type == W_SERVICE_TYPE_INTERNET_CONNECTIVITY) + type = W_SERVICE_TYPE_BT_PAN; + else + return -EINVAL; + + service_type = wc_service_type_enum2string(type); + } + + technology_path = __weconn_get_technology_path4service(type); + if (!technology_path) + return -EINVAL; + + reply = g_dbus_connection_call_sync(__weconn_call_ref(h), + WECONN_SERVICE_DBUS, + technology_path, + WECONN_TECHNOLOGY_INTERFACE, + "Connect", + g_variant_new("(s)", service_type), + NULL, + G_DBUS_CALL_FLAGS_NONE, + DBUS_REPLY_TIMEOUT, + h->cancellable, + &error); + + __weconn_call_unref(h); + + if (error) { + ERR("Failed to request (%s)", error->message); + if (g_strrstr(error->message, "AlreadyConnected")) + err = -EISCONN; + else if (g_strrstr(error->message, "NotConnected")) + err = -ENOTCONN; + else if (g_strrstr(error->message, "InProgress")) + err = -EINPROGRESS; + else if (g_strrstr(error->message, "LostConnection")) + err = -EIO; + + g_error_free(error); + + if (reply) + g_variant_unref(reply); + + return err; + } + + if (!reply) { + ERR("Failed to request"); + return err; + } + + g_variant_unref(reply); + + if (callback) { + h->connect_service_type = type; + h->connected_callback = callback; + if (user_data) + h->connect_user_data = user_data; + } + + return 0; +} + +EXPORT_API int weconn_disconnect_service(weconn_h conn, + weconn_service_type_e type, weconn_connected_cb callback, + void *user_data) +{ + int err = -EIO; + GVariant *reply; + GError *error = NULL; + struct weconn_connection *h = (struct weconn_connection *)conn; + const char *service_type = NULL; + const char *technology_path = NULL; + + if (!__weconn_check_handle_validity(conn)) + return -EINVAL; + + if (h->disconnected_callback) { + DBG("disconnection is inprogress"); + return -EINPROGRESS; + } + + service_type = wc_service_type_enum2string(type); + if (!service_type) { + + /* TODO: please make more general to care all of technologies */ + + if (type == W_SERVICE_TYPE_HOST_TO_WEARABLE_CONNECTIVITY) + type = W_SERVICE_TYPE_BT_SPP; + else if (type == W_SERVICE_TYPE_INTERNET_CONNECTIVITY) + type = W_SERVICE_TYPE_BT_PAN; + else + return -EINVAL; + + service_type = wc_service_type_enum2string(type); + } + + technology_path = __weconn_get_technology_path4service(type); + if (!technology_path) + return -EINVAL; + + reply = g_dbus_connection_call_sync(__weconn_call_ref(h), + WECONN_SERVICE_DBUS, + technology_path, + WECONN_TECHNOLOGY_INTERFACE, + "Disconnect", + g_variant_new("(s)", service_type), + NULL, + G_DBUS_CALL_FLAGS_NONE, + DBUS_REPLY_TIMEOUT, + h->cancellable, + &error); + + __weconn_call_unref(h); + + if (error) { + ERR("Failed to request (%s)", error->message); + if (g_strrstr(error->message, "AlreadyConnected")) + err = -EISCONN; + else if (g_strrstr(error->message, "NotConnected")) + err = -ENOTCONN; + + g_error_free(error); + + if (reply) + g_variant_unref(reply); + + return err; + } + + if (!reply) { + ERR("Failed to request"); + return err; + } + + g_variant_unref(reply); + + if (callback) { + h->disconnect_service_type = type; + h->disconnected_callback = callback; + if (user_data) + h->disconnect_user_data = user_data; + } + + return 0; +} + +EXPORT_API int weconn_set_service_state_change_cb(weconn_h conn, + weconn_service_state_changed_cb callback, + weconn_service_type_e type, void *user_data) +{ + struct weconn_service_handle *h_service = NULL; + + if (!__weconn_check_handle_validity(conn) || callback == NULL) + return -EINVAL; + + h_service = g_try_malloc0(sizeof(struct weconn_service_handle)); + if (h_service == NULL) + return -EINVAL; + + h_service->conn = conn; + h_service->callback = callback; + h_service->user_data = user_data; + + switch (type) { + case W_SERVICE_TYPE_HOST_TO_WEARABLE_CONNECTIVITY: + g_hash_table_replace(wc_wearable_service_con_changed_cb_hash, + conn, h_service); + break; + case W_SERVICE_TYPE_INTERNET_CONNECTIVITY: + g_hash_table_replace(wc_internet_service_con_changed_cb_hash, + conn, h_service); + break; + case W_SERVICE_TYPE_BT_PAN: + case W_SERVICE_TYPE_BT_HFP: + case W_SERVICE_TYPE_BT_SPP: + case W_SERVICE_TYPE_BT_GATT: + case W_SERVICE_TYPE_CELLULAR: + case W_SERVICE_TYPE_WIFI: + case W_SERVICE_TYPE_WIFI_P2P: + case W_SERVICE_TYPE_WIFI_ADHOC: + case W_SERVICE_TYPE_ETHERNET: + default: + ERR("Not support service type (%d)", type); + g_free(h_service); + break; + } + + return 0; +} + +EXPORT_API int weconn_unset_service_state_change_cb(weconn_h conn, + weconn_service_type_e type) +{ + if (!__weconn_check_handle_validity(conn)) + return -EINVAL; + + switch (type) { + case W_SERVICE_TYPE_HOST_TO_WEARABLE_CONNECTIVITY: + g_hash_table_remove(wc_wearable_service_con_changed_cb_hash, + conn); + break; + case W_SERVICE_TYPE_INTERNET_CONNECTIVITY: + g_hash_table_remove(wc_internet_service_con_changed_cb_hash, + conn); + break; + case W_SERVICE_TYPE_BT_PAN: + case W_SERVICE_TYPE_BT_HFP: + case W_SERVICE_TYPE_BT_SPP: + case W_SERVICE_TYPE_BT_GATT: + case W_SERVICE_TYPE_CELLULAR: + case W_SERVICE_TYPE_WIFI: + case W_SERVICE_TYPE_WIFI_P2P: + case W_SERVICE_TYPE_WIFI_ADHOC: + case W_SERVICE_TYPE_ETHERNET: + default: + break; + } + + return 0; +} diff --git a/client/weconn.pc.in b/client/weconn.pc.in new file mode 100644 index 0000000..b51db8a --- /dev/null +++ b/client/weconn.pc.in @@ -0,0 +1,11 @@ +prefix=@PREFIX@ +exec_prefix=@PREFIX@ +libdir=@LIBDIR@ +includedir=@INCLUDEDIR@/weconn + +Name: weconn +Description: Wearable device connection controller API library in TIZEN C API (Development) +Requires: dlog glib-2.0 gobject-2.0 +Version: @VERSION@ +Libs: -L${libdir} -lweconn +Cflags: -I${includedir} diff --git a/include/conn.h b/include/conn.h new file mode 100644 index 0000000..3765fa9 --- /dev/null +++ b/include/conn.h @@ -0,0 +1,32 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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 __WECONN_H__ +#define __WECONN_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* __WECONN_H__ */ diff --git a/include/control.h b/include/control.h new file mode 100644 index 0000000..11e4d5c --- /dev/null +++ b/include/control.h @@ -0,0 +1,45 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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 __WECONN_CONTROL_H__ +#define __WECONN_CONTROL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "log.h" +#include "events.h" +#include "object.h" + +#define PROP_CONTROL_TYPE_HOST 0x00 +#define PROP_CONTROL_TYPE_WEARABLE 0x01 + +int wc_control_add_bearer(WcObject * wo_bearer); +WcObject *wc_control_get_object(void); +int wc_control_get_device_type(void); + +int control_init(void); +void control_cleanup(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __WECONN_CONTROL_H__ */ diff --git a/include/dbus.h b/include/dbus.h new file mode 100644 index 0000000..ba7df57 --- /dev/null +++ b/include/dbus.h @@ -0,0 +1,64 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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 __WECONN_DBUS_H__ +#define __WECONN_DBUS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "conn.h" + +#define DBUS_REPLY_TIMEOUT (120 * 1000) + +#define WECONN_SERVICE_DBUS "net.weconn" +#define WECONN_PATH_DBUS "/net/weconn" + +#define WECONN_SERVICE_INTERFACE WECONN_SERVICE_DBUS ".Service" +#define WECONN_TECHNOLOGY_INTERFACE WECONN_SERVICE_DBUS ".Technology" + +#define WECONN_SERVICE_PATH WECONN_PATH_DBUS "/service" +#define WECONN_TECHNOLOGY_PATH WECONN_PATH_DBUS "/technology" +#define WECONN_TECHNOLOGY_BLUETOOTH_PATH WECONN_TECHNOLOGY_PATH "/bluetooth" +#define WECONN_TECHNOLOGY_CELLULAR_PATH WECONN_TECHNOLOGY_PATH "/cellular" +#define WECONN_TECHNOLOGY_WIFI_PATH WECONN_TECHNOLOGY_PATH "/wifi" +#define WECONN_TECHNOLOGY_WIFI_P2P_PATH WECONN_TECHNOLOGY_PATH "/p2p" +#define WECONN_TECHNOLOGY_WIFI_ADHOC_PATH WECONN_TECHNOLOGY_PATH "/adhoc" +#define WECONN_TECHNOLOGY_ETHERNET_PATH WECONN_TECHNOLOGY_PATH "/ethernet" + +#define WECONN_TECHNOLOGY_SIGNAL_CONNECTION_RESULT "SignalConnectionResult" +#define WECONN_TECHNOLOGY_SIGNAL_DISCONNECTION_RESULT "SignalDisconnectionResult" +#define WECONN_TECHNOLOGY_SIGNAL_SERVICE_STATE_CHANGED "SignalServiceStateChanged" + +int dbus_init(GBusType bus_type, const char *bus_name, const char *obj_path, + void (*__init_cb)(void)); +void dbus_cleanup(void); + +GDBusConnection *dbus_get_connection(void); + +const char *dbus_name2path(const char *name); + +#ifdef __cplusplus +} +#endif + +#endif /* __WECONN_DBUS_H__ */ diff --git a/include/driver.h b/include/driver.h new file mode 100644 index 0000000..c0e5da9 --- /dev/null +++ b/include/driver.h @@ -0,0 +1,44 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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 __WECONN_DRIVER_H__ +#define __WECONN_DRIVER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "object.h" + +int wc_bearer_driver_init(WcObject *wo, const char *desc_name); +int wc_bearer_driver_activate(WcObject *wo, const char *desc_name); +int wc_bearer_driver_deactivate(WcObject *wo, const char *desc_name); +int wc_bearer_driver_connect(WcObject *wo, const char *address, + const char *desc_name); +int wc_bearer_driver_disconnect(WcObject *wo, const char *desc_name); +void wc_bearer_driver_exit(WcObject *wo, const char *desc_name); + +int driver_init(const char *driver_path); +void driver_cleanup(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __WECONN_DRIVER_H__ */ diff --git a/include/error.h b/include/error.h new file mode 100644 index 0000000..09756b3 --- /dev/null +++ b/include/error.h @@ -0,0 +1,36 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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 __WECONN_ERROR_H__ +#define __WECONN_ERROR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void wc_error_add(const gchar *msg, ...); +void wc_error_return(GDBusMethodInvocation *invocation); + +#ifdef __cplusplus +} +#endif + +#endif /* __WECONN_ERROR_H__ */ diff --git a/include/events.h b/include/events.h new file mode 100644 index 0000000..786c3fc --- /dev/null +++ b/include/events.h @@ -0,0 +1,66 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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 __WECONN_EVENTSH__ +#define __WECONN_EVENTSH__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Generic property changed */ +#define WC_OBJECT_EVENT_PROPERTY_CHANGED "PropertiesChanged" + +/* Bearer events */ +#define WC_BEARER_EVENT_BT_CONNECTED_SPP "BtConnectedSpp" +#define WC_BEARER_EVENT_BT_CONNECTED_GATT "BtConnectedGatt" +#define WC_BEARER_EVENT_BT_CONNECTED_HFP "BtConnectedHfp" +#define WC_BEARER_EVENT_BT_CONNECTED_PAN "BtConnectedPan" + +/* Technology events */ + +/* Service events */ + +/* Bearer (plug-in) events */ +#define WC_OBJECT_EVENT_BEARER_ENABLED "BearerEnabled" +#define WC_OBJECT_EVENT_BEARER_DISABLED "BearerDisabled" + +/* eSAP events */ +/* For example, to send data uses WC_OBJECT_EVENT_SAP_SEND_DATA + * wc_object_emit_callback(wo, WC_OBJECT_EVENT_SAP_SEND_DATA, data); + * to receive data uses + * WC_OBJECT_EVENT_SAP_RECEIVE_DATA[main_cmd][sub_cmd][response_type] + * wc_object_add_callback(wo, + * WC_OBJECT_EVENT_SAP_RECEIVE_DATA[main_cmd][sub_cmd][response_type], + * user_data); + */ +#define WC_OBJECT_EVENT_SAP_SEND_DATA "eSAP-send" +#define WC_OBJECT_EVENT_SAP_RECEIVE_DATA "eSAP-receive" + + +/* TODO: to plug-in developer, + * develop each plug-in APIs and define each event for those APIs. + */ +#define WC_OBJECT_EVENT_SAP_BCMSERVICE_STATUS "bcmservice_status" + +#ifdef __cplusplus +} +#endif + +#endif /* __WECONN_EVENTSH__ */ diff --git a/include/log.h b/include/log.h new file mode 100644 index 0000000..04c2f7a --- /dev/null +++ b/include/log.h @@ -0,0 +1,41 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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 __WECONN_LOG_H__ +#define __WECONN_LOG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define ERR(fmt, args...) do { LOGE(fmt, ## args); } while(0) +#define WARN(fmt, args...) do { LOGW(fmt, ## args); } while(0) +#define DBG(fmt, args...) do { LOGI(fmt, ## args); } while(0) + +#define SECURE_ERR(fmt, args...) do { SECURE_LOGE(fmt, ## args); } while(0) +#define SECURE_WARN(fmt, args...) do { SECURE_LOGW(fmt, ## args); } while(0) +#define SECURE_DBG(fmt, args...) do { SECURE_LOGI(fmt, ## args); } while(0) + +#ifdef __cplusplus +} +#endif + +#endif /* __WECONN_LOG_H__ */ diff --git a/include/object.h b/include/object.h new file mode 100644 index 0000000..b90658d --- /dev/null +++ b/include/object.h @@ -0,0 +1,100 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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 __WECONN_OBJECT_H__ +#define __WECONN_OBJECT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "log.h" + +#define WC_OBJECT_TYPE_DEFAULT 0xFF000000 +#define WC_OBJECT_TYPE_BEARER_DRIVER (WC_OBJECT_TYPE_DEFAULT | 0x00010000) +#define WC_OBJECT_TYPE_SERVICE (WC_OBJECT_TYPE_DEFAULT | 0x00020000) +#define WC_OBJECT_TYPE_TECHNOLOGY (WC_OBJECT_TYPE_DEFAULT | 0x00030000) +#define WC_OBJECT_TYPE_CONTROL (WC_OBJECT_TYPE_DEFAULT | 0x00040000) + +#define WC_OBJECT_CHECK(o,t) \ + if (!o) { WARN("WcObject is NULL"); return; } \ + if (wc_object_get_type(o) != t) \ + { WARN("type(0x%x != 0x%x) mismatch", wc_object_get_type(o), t); return; } +#define WC_OBJECT_CHECK_RETURN(o,t,r) \ + if (!o) { WARN("WcObject is NULL"); return r; } \ + if (wc_object_get_type(o) != t) \ + { WARN("type(0x%x != 0x%x) mismatch", wc_object_get_type(o), t); return r; } + +#define WC_OBJECT_KEY_FIND(keys, k) \ + g_slist_find_custom((keys), (k), (GCompareFunc)g_strcmp0) + +typedef struct wc_object WcObject; +typedef gboolean (*WcObjectCallback)(WcObject *wo, const void *event_info, + void *user_data); + +WcObject *wc_object_new(const char *name, unsigned int type); +void wc_object_free(WcObject *wo); +WcObject *wc_object_find(const char *name); + +const char *wc_object_get_name(WcObject *wo); +const char *wc_object_peek_name(WcObject *wo); +unsigned int wc_object_get_type(WcObject *wo); + +int wc_object_append_element(WcObject *wo, + const char *element_name, void *element); +void *wc_object_get_element(WcObject *wo, const char *element_name); + +typedef void (*wc_object_foreach_get_elements_cb)(WcObject *wo, + const char *element_name, void *element, void *user_data); +int wc_object_foreach_get_elements(WcObject *wo, + wc_object_foreach_get_elements_cb callback, void *user_data); + +int wc_object_set_dbus_interface(WcObject *wo, GDBusInterfaceSkeleton *di); +GDBusInterfaceSkeleton *wc_object_get_dbus_interface(WcObject *wo); + +int wc_object_export(WcObject *wo, const char *path); +int wc_object_unexport(WcObject *wo); + +int wc_object_add_callback(WcObject *wo, const char *event, + WcObjectCallback callback, void *user_data); +int wc_object_del_callback(WcObject *wo, const char *event, + WcObjectCallback callback); +int wc_object_emit_callback(WcObject *wo, const char *event, + const void *event_info); + +#define wc_object_set_property(co, ...) \ + wc_object_set_property_full(co, __VA_ARGS__, NULL, NULL) + +int wc_object_set_property_full(WcObject *wo, const char *first_property, ...); +char *wc_object_get_property(WcObject *wo, const char *key); + +const char *wc_object_peek_property(WcObject *wo, const char *key); +GHashTable *wc_object_peek_property_hash(WcObject *wo); + +void wc_object_set_timeout_connecting(WcObject *wo, + guint timeout, const char *service); +guint wc_object_get_timeout_connecting(WcObject *wo); +char *wc_object_get_connecting_service(WcObject *wo); +void wc_object_set_timeout_disconnecting(WcObject *wo, + guint timeout, const char *service); +guint wc_object_get_timeout_disconnecting(WcObject *wo); +char *wc_object_get_disconnecting_service(WcObject *wo); +#endif /* __WECONN_OBJECT_H__ */ diff --git a/include/service.h b/include/service.h new file mode 100644 index 0000000..fc8865e --- /dev/null +++ b/include/service.h @@ -0,0 +1,34 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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 __WECONN_SERVICE_H__ +#define __WECONN_SERVICE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +int service_init(void); +void service_cleanup(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __WECONN_SERVICE_H__ */ diff --git a/include/technology.h b/include/technology.h new file mode 100644 index 0000000..f07cad2 --- /dev/null +++ b/include/technology.h @@ -0,0 +1,62 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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 __WECONN_TECHNOLOGY_H__ +#define __WECONN_TECHNOLOGY_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define WC_TECHNOLOGY_BLUETOOTH "bluetooth" +#define WC_TECHNOLOGY_CELLULAR "cellular" +#define WC_TECHNOLOGY_WIFI "Wi-Fi" +#define WC_TECHNOLOGY_ETHERNET "ethernet" + + +#define CONNMAN_SERVICE "net.connman" +#define CONNMAN_SERVICE_INTERFACE CONNMAN_SERVICE ".Service" +#define CONNMAN_SIGNAL_PROPERTY_CHANGED "PropertyChanged" + +/** + * @brief Enumerations of wearable connection service state. + */ +typedef enum +{ + WC_SERVICE_STATE_IDLE = 0x00, + WC_SERVICE_STATE_FAILURE = 0x01, + WC_SERVICE_STATE_DISCONNECTED = 0x02, + WC_SERVICE_STATE_ASSOCIATION = 0x03, + WC_SERVICE_STATE_CONFIGURATION = 0x04, + WC_SERVICE_STATE_READY = 0x05, + WC_SERVICE_STATE_ONLINE = 0x06, +} wc_technology_service_state_e; + + +int wc_technology_add_bearer(WcObject *wo); +WcObject *wc_technology_get_bearer(const char *technology); + +int technology_init(void); +void technology_cleanup(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __WECONN_TECHNOLOGY_H__ */ diff --git a/include/util.h b/include/util.h new file mode 100644 index 0000000..f3ff540 --- /dev/null +++ b/include/util.h @@ -0,0 +1,57 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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 __WECONN_UTIL_H__ +#define __WECONN_UTIL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "weconn_type.h" + +#define XSTR(X) #X + +/** + * @brief Enumerations of wearable connection popup type. + */ +typedef enum +{ + WC_POPUP_TYPE_PAN_CONNECT = 0x01, + WC_POPUP_TYPE_PAN_DISCONNECT = 0x02, +} weconn_popup_type_e; + +weconn_service_type_e wc_service_type_string2enum(const char *service_type); +const char *wc_service_type_enum2string(weconn_service_type_e service_type); +weconn_service_state_e wc_service_state_string2enum(const char *service_state); +const char *wc_service_state_enum2string(weconn_service_state_e service_state); +weconn_device_type_e wc_device_type_string2enum(const char *device_type); +const char *wc_device_type_enum2string(weconn_device_type_e device_type); +weconn_device_state_e wc_device_state_string2enum(const char *device_state); +const char *wc_device_state_enum2string(weconn_device_state_e device_state); +weconn_device_type_e wc_device_get_type_from_path(const char *path); +int wc_launch_popup(weconn_popup_type_e type, service_reply_cb callback, + void *user_data); + +#ifdef __cplusplus +} +#endif + +#endif /* __WECONN_UTIL_H__ */ diff --git a/introspection/service.xml b/introspection/service.xml new file mode 100644 index 0000000..d6774b8 --- /dev/null +++ b/introspection/service.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/introspection/technology.xml b/introspection/technology.xml new file mode 100644 index 0000000..7b18481 --- /dev/null +++ b/introspection/technology.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packaging/weconn.spec b/packaging/weconn.spec new file mode 100644 index 0000000..676989a --- /dev/null +++ b/packaging/weconn.spec @@ -0,0 +1,110 @@ +Name: weconn +Summary: WEarable device CONNection controller framework (We connect all) +Version: 0.1.56 +Release: 1 +Group: System/Network +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz +BuildRequires: cmake +BuildRequires: python-xml +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(vconf) +BuildRequires: pkgconfig(gio-2.0) +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(capi-base-common) +BuildRequires: pkgconfig(sap-client-stub-api) +BuildRequires: pkgconfig(capi-appfw-application) +BuildRequires: pkgconfig(capi-network-bluetooth) +BuildRequires: pkgconfig(capi-system-info) +BuildRequires: pkgconfig(alarm-service) +Requires(post): /sbin/ldconfig +Requires(post): /usr/bin/vconftool +Requires(postun): /sbin/ldconfig +Requires: /usr/bin/vconftool + +%description +Wearable device requires an automatic controller to manage various connection bearers +without UI based control panel. Wearable connection controller provides client and +daemon for managing various connection bearers within wearable devices running the +Linux operating system. + +%package devel +Summary: WEarable device CONNection controller API library (devel) +Group: System/Network +License: Apache +Requires: %{name} = %{version}-%{release} + +%description devel +Wearable device connection controller API library in TIZEN C API (Development) + +%prep +%setup -q + + +%build +cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} -DVERSION=%{version} +make %{?_smp_mflags} + + +%install +%make_install + +#Systemd service file +mkdir -p %{buildroot}%{_libdir}/systemd/system/ +cp resources/usr/lib/systemd/system/weconn.service %{buildroot}%{_libdir}/systemd/system/weconn.service +mkdir -p %{buildroot}%{_libdir}/systemd/system/multi-user.target.wants/ +ln -s ../weconn.service %{buildroot}%{_libdir}/systemd/system/multi-user.target.wants/weconn.service + +mkdir -p %{buildroot}%{_datadir}/dbus-1/services/ +cp resources/usr/share/dbus-1/services/net.weconn.service %{buildroot}%{_datadir}/dbus-1/services/net.weconn.service + +#DBus DAC (manifest enables DBus SMACK) +mkdir -p %{buildroot}%{_sysconfdir}/dbus-1/system.d/ +cp resources/etc/dbus-1/system.d/weconn.conf %{buildroot}%{_sysconfdir}/dbus-1/system.d/ + +#appcessory +mkdir -p %{buildroot}%{_datadir}/appcessory/ +cp resources/usr/share/appcessory/wearable-connection.xml %{buildroot}%{_datadir}/appcessory/ + +#FOTA patch +mkdir -p %{buildroot}%{_sysconfdir}/opt/upgrade/ +cp resources/etc/opt/upgrade/500.net.weconn.patch.sh %{buildroot}%{_sysconfdir}/opt/upgrade/ + +#License +mkdir -p %{buildroot}%{_datadir}/license +cp LICENSE %{buildroot}%{_datadir}/license/weconn + +%post + +#Systemd enhanced BT power on +mkdir -p %{_sysconfdir}/systemd/default-extra-dependencies/ignore-units.d/ +ln -s %{_libdir}/systemd/system/weconn.service %{_sysconfdir}/systemd/default-extra-dependencies/ignore-units.d/ + +vconftool set -t int file/private/weconn/connected_bt_version 0 -s system::vconf_network +vconftool set -t int file/private/weconn/disconnected_manually 0 -s system::vconf_network +vconftool set -t string file/private/weconn/connected_manufacturer "" -s system::vconf_network +vconftool set -t string file/private/weconn/autoconnectable_unique_bt_target_address "" -s system::vconf_network +vconftool set -t int file/private/weconn/last_bt_status 0 -s system::vconf_network +vconftool set -tf int memory/private/weconn/all_connected 0 -s system::vconf_network -i + +%postun -p /sbin/ldconfig + + +%files +%manifest weconn.manifest +%attr(644,-,-) %{_libdir}/*.so.* +%attr(500,root,root) %{_sbindir}/* +%attr(400,root,root) %{_libdir}/weconn/*.so +%attr(644,root,root) %{_datadir}/dbus-1/services/* +#DBus DAC +%attr(644,root,root) %{_sysconfdir}/dbus-1/system.d/* +%attr(644,root,root) %{_libdir}/systemd/system/weconn.service +%attr(644,root,root) %{_libdir}/systemd/system/multi-user.target.wants/weconn.service +%attr(700,root,root) %{_sysconfdir}/opt/upgrade/500.net.weconn.patch.sh +%{_datadir}/license/weconn +%{_datadir}/appcessory/wearable-connection.xml + +%files devel +%{_libdir}/libweconn.so +%{_includedir}/weconn +%{_libdir}/pkgconfig/weconn.pc diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt new file mode 100755 index 0000000..0061e36 --- /dev/null +++ b/plugins/CMakeLists.txt @@ -0,0 +1,16 @@ +REMOVE_DEFINITIONS("-DLOG_TAG=\"CAPI_NETWORK_WECONN\"") +ADD_DEFINITIONS("-DLOG_TAG=\"WECONN\"") + + +SET(PLUGINS + pan + esap + bluetooth +) + +FOREACH(plugin ${PLUGINS}) + ADD_LIBRARY(${plugin} SHARED ${plugin}.c ../src/util.c) + TARGET_LINK_LIBRARIES(${plugin} ${pkgs_LDFLAGS} "-L${CMAKE_BINARY_DIR}") + SET_TARGET_PROPERTIES(${plugin} PROPERTIES PREFIX "" OUTPUT_NAME ${plugin}) + INSTALL(TARGETS ${plugin} LIBRARY DESTINATION ${WECONNLIB}) +ENDFOREACH(plugin) diff --git a/plugins/bluetooth.c b/plugins/bluetooth.c new file mode 100644 index 0000000..6d7f5fa --- /dev/null +++ b/plugins/bluetooth.c @@ -0,0 +1,1468 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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. + * + */ + +#include +#include +#include +#include + +#include "esap.h" +#include "plugin.h" + +#define BT_ADVERTISING_INTERVAL_FAST 150.0f //msec +#define BT_ADVERTISING_INTERVAL_NORMAL 500.0f //msec +#define FAST_ADVERTISING_TIMEOUT 30 //sec + +#define BT_ADV_MANUFACTURER_COMPANY 0x75 +#define BT_ADV_MANUFACTURER_SERVICE_ID 0x02 +#define BT_ADV_MANUFACTURER_VERSION 0x01 +#define BT_ADV_MANUFACTURER_PKG_NAME "watchmanager" + +#define VCONFKEY_WECONN_LAST_BT_STATUS \ + "file/private/weconn/last_bt_status" + +static int sap_rfcomm_ready = 0; +static int sap_connected = 0; +static bool hf_connected = false; + +static gboolean mBTRecoveryState = FALSE; + +static alarm_id_t fast_advertising_timer_id = 0; + +static void __bt_start_advertising(bool fast); +static void __bt_page_scan(bool on); + +static bool __bt_check_bonded_device_cb(bt_device_info_s *device_info, + void *user_data) +{ + if (device_info && device_info->bt_class.major_device_class + == BT_MAJOR_DEVICE_CLASS_AUDIO_VIDEO) { + int ret; + bool is_a2dp_connected = false; + + DBG("Major class[0x%x]", device_info->bt_class.major_device_class); + + ret = bt_device_is_profile_connected(device_info->remote_address, + BT_PROFILE_A2DP, &is_a2dp_connected); + + DBG("ret[%d], is_a2dp_connected [%d]", ret, is_a2dp_connected); + + if (ret == BT_ERROR_NONE && is_a2dp_connected == false) + __bt_page_scan(true); + } + + return true; +} + +static void __bt_notify_all_connected(int connected) +{ + int all_connected = 0; + + if (vconf_get_int(VCONFKEY_WECONN_ALL_CONNECTED, &all_connected) < 0) { + ERR("Failed to get all connected status"); + } else { + if (all_connected == connected) { + ERR("Not changed all connected state"); + return; + } + } + + if (connected == 0) + vconf_set_int(VCONFKEY_WECONN_ALL_CONNECTED, 0); + else + vconf_set_int(VCONFKEY_WECONN_ALL_CONNECTED, 1); + + DBG("All connected"); +} + +static void __bt_page_scan(bool on) +{ + int err; + bool prev_status = false; + + err = bt_adapter_get_connectable(&prev_status); + if (err != BT_ERROR_NONE) { + ERR("Failed to get bt_adapter_get_connectable(%d)", err); + return; + } + + if (prev_status == false && on == true) { + err = bt_adapter_set_connectable(true); + if (err != BT_ERROR_NONE) + ERR("Failed to start page scan(%d)", err); + else + DBG("Page scan started"); + } else if (prev_status == true && on == false) { + err = bt_adapter_set_connectable(false); + if (err != BT_ERROR_NONE) + ERR("Failed to stop page scan(%d)", err); + else + DBG("Page scan stopped"); + + /* check bonded device and enable page scan if AV headset is paired*/ + if (bt_adapter_foreach_bonded_device(__bt_check_bonded_device_cb, + NULL) != BT_ERROR_NONE) + ERR("Failed to check bonded devices"); + } else { + DBG("Skip setting: old(%d), new(%d)", prev_status, on); + } +} + +static int __get_setup_wizard_state(void) +{ + int ret = 0, state = 0; + + ret = vconf_get_bool(VCONFKEY_SETUP_WIZARD_FIRST_BOOT, &state); + if (ret < 0) { + ERR("Failed to get state(%d)", ret); + return -EIO; + } + + DBG("Setup wizard running state(%d)", state); + + return state; +} + +static void __bt_set_advertising_manufacturer_data(int setup_wizard_state) +{ + int err = 0; + static char prev_data[24] = {0, }; + char data[24] = {0, }; + int len = 0; + + /* Complete manufacturer data */ + // Company + data[0] = 0x00; + data[1] = BT_ADV_MANUFACTURER_COMPANY; + + // TODO: Read value from somewhere + // Version + data[2] = BT_ADV_MANUFACTURER_VERSION; + + // Service ID + data[3] = 0x00; + data[4] = BT_ADV_MANUFACTURER_SERVICE_ID; + + // TODO : Read value from somewhere + // Device ID (b2 : 0x01, wingtip : 0x02) + data[5] = 0x00; + data[6] = 0x01; + + if (setup_wizard_state == 1) { + // Purpose (setup : 0x01, auto connection : 0x02) + data[7] = 0x01; + + /* Data for Quick Connect */ + // Length of package name + data[8] = 0x0c; + + // Package name for auto installation + memcpy(&data[9], BT_ADV_MANUFACTURER_PKG_NAME, + sizeof(BT_ADV_MANUFACTURER_PKG_NAME)); + len = 9 + strlen(BT_ADV_MANUFACTURER_PKG_NAME); + + // BT rssi calibrating constant + data[len] = 0x00; + len++; + } else {/* setup wizard is not running */ + data[7] = 0x02; + len = 8; + } + + DBG("len(%d)", len); + memcpy(prev_data, data, sizeof(data)); + + err = bt_adapter_set_advertising_manufacturer_data(data, len); + if (err != BT_ERROR_NONE) + ERR("Failed to set manufacturer data(%d)", err); +} + +static bool __device_check_gatt_cb(bt_profile_e profile, void *user_data) +{ + bool *is_connected = (bool *)user_data; + + if (profile == BT_PROFILE_GATT) { + *is_connected = true; + DBG("LE is connected"); + return false; + } + + return true; +} + +static bool __device_check_hfp_cb(bt_profile_e profile, void *user_data) +{ + bool *is_connected = (bool *)user_data; + + if (profile == BT_PROFILE_AG) { + *is_connected = true; + DBG("HFP is connected"); + return false; + } + + return true; +} + +static bool __bt_check_connection_status(const char *addr, + bt_device_connected_profile func) +{ + int ret; + bool is_connected = false; + + if (!addr || !func || addr[0] == '\0') + return false; + + SECURE_DBG("Addr[%s]", addr); + ret = bt_device_foreach_connected_profiles(addr, func, &is_connected); + if (ret != BT_ERROR_NONE) { + ERR("Failed to bt_device_foreach_connected_profiles(0x%08x)", ret); + return false; + } + + return is_connected; +} + +static int __fast_advertising_timeout_cb(alarm_id_t alarm_id, void * data) +{ + DBG("fast advertising timer expired!!"); + + if (fast_advertising_timer_id > 0) { + alarmmgr_remove_alarm(fast_advertising_timer_id); + fast_advertising_timer_id = 0; + } + + if (sap_connected != SAP_CONNECTED && hf_connected != true) + __bt_start_advertising(false); + + return 0; +} + +static void __fast_advertising_timer_stop(void) +{ + if (fast_advertising_timer_id <= 0) + return; + + DBG("Alarm unregistered(%d)", fast_advertising_timer_id); + alarmmgr_remove_alarm(fast_advertising_timer_id); + fast_advertising_timer_id = 0; +} + +static void __fast_advertising_timer_start(void) +{ + int result = 0; + + __fast_advertising_timer_stop(); + + result = alarmmgr_set_cb(__fast_advertising_timeout_cb, NULL); + if (result != ALARMMGR_RESULT_SUCCESS) { + ERR("Failed to set timer(%d)", result); + return; + } + + result = alarmmgr_add_alarm(ALARM_TYPE_VOLATILE, FAST_ADVERTISING_TIMEOUT, + 0, NULL, &fast_advertising_timer_id); + if (result != ALARMMGR_RESULT_SUCCESS) { + ERR("Failed to add alarm(%d)", result); + return; + } + + DBG("Alarm registered(%d)", fast_advertising_timer_id); +} + +static void __bt_update_white_list(const char *bt_target_address, + int setup_wizard_state) +{ + int ret = 0; + bool is_advertising = false; + + ret = bt_adapter_is_advertising(&is_advertising); + if (ret == BT_ERROR_NONE && is_advertising == true) + bt_adapter_stop_advertising(); + + /* should remove the previous connected addresses */ + DBG("Clear white list"); + ret = bt_adapter_clear_white_list(); + if (ret != BT_ERROR_NONE) + ERR("Failed to clear white list(%d)", ret); + + if (!bt_target_address) { + DBG("target address is NULL"); + return; + } + + if (strlen(bt_target_address) < 17) { + /* BT address formatted [xx:xx:xx:xx:xx:xx] */ + DBG("Invalid mac address"); + return; + } + + if (setup_wizard_state == 1) { + DBG("Setup Wizard is running"); + return; + } + + WARN("Adding white list[%c%c:%c%c:%c%c]", + bt_target_address[0], bt_target_address[1], + bt_target_address[3], bt_target_address[4], + bt_target_address[15], bt_target_address[16]); + SECURE_DBG("white list[%s]", bt_target_address); + ret = bt_adapter_add_white_list(bt_target_address); + if (ret != BT_ERROR_NONE) + ERR("Failed to add white list(%d)", ret); +} + +static void __bt_start_advertising(bool fast) +{ + int ret = 0; + bool is_advertising = false; + bt_adapter_advertising_params_s adv_params = { 0, }; + bt_adapter_state_e device_status; + int setup_wizard_state = 0; + int disconnected_manually = 0; + const char *autoconnectable_bt_address = NULL; + + bt_adapter_get_state(&device_status); + if (device_status != BT_ADAPTER_ENABLED) { + ERR("BT disabled"); + return; + } + + if (sap_connected == SAP_CONNECTED || hf_connected == true) { + ERR("SPP(%d) HFP(%d)", sap_connected, hf_connected); + return; + } + + /* page scan should be on, even if LE does not advertised */ + __bt_page_scan(true); + + vconf_get_int(VCONFKEY_WECONN_DISCONNECTED_MANUALLY, &disconnected_manually); + if (disconnected_manually) { + DBG("Disconnected manually(%d)", disconnected_manually); + return; + } + + /* Important: BT auto-connectable address should be set by WMS, + * when EULA acceptance has been made by an user. + */ + autoconnectable_bt_address = + vconf_get_str(VCONFKEY_WECONN_AUTOCONNECTABLE_BT_ADDRESS); + if (autoconnectable_bt_address && strlen(autoconnectable_bt_address) > 0 && + __bt_check_connection_status(autoconnectable_bt_address, + __device_check_gatt_cb) == true) { + return; + } + + DBG("fast(%d)", fast); + if (fast) { + __fast_advertising_timer_start(); + + adv_params.interval_max = BT_ADVERTISING_INTERVAL_FAST; + adv_params.interval_min = BT_ADVERTISING_INTERVAL_FAST; + } else { + adv_params.interval_max = BT_ADVERTISING_INTERVAL_NORMAL; + adv_params.interval_min = BT_ADVERTISING_INTERVAL_NORMAL; + } + + setup_wizard_state = __get_setup_wizard_state(); + + __bt_update_white_list(autoconnectable_bt_address, setup_wizard_state); + + if (setup_wizard_state == 1) { + adv_params.filter_policy = BT_ADAPTER_ADVERTISING_FILTER_ALLOW_CONN_WL; + adv_params.interval_max = BT_ADVERTISING_INTERVAL_FAST; + adv_params.interval_min = BT_ADVERTISING_INTERVAL_FAST; + } else { + if (autoconnectable_bt_address && + strlen(autoconnectable_bt_address) > 0) { + adv_params.filter_policy = + BT_ADAPTER_ADVERTISING_FILTER_ALLOW_SCAN_CONN_WL; + } else { + WARN("Start advertising without white list"); + adv_params.filter_policy = BT_ADAPTER_ADVERTISING_FILTER_DEFAULT; + } + } + + g_free((gpointer)autoconnectable_bt_address); + + ret = bt_adapter_is_advertising(&is_advertising); + if (ret == BT_ERROR_NONE && is_advertising == true) + bt_adapter_stop_advertising(); + + __bt_set_advertising_manufacturer_data(setup_wizard_state); + + ret = bt_adapter_start_advertising(&adv_params); + if (ret == BT_ERROR_NONE) { + DBG("LE Advertising is started(%.2fms)", adv_params.interval_max); + return; + } + + ERR("Failed to start advertising(%d)", ret); +} + +static void __bt_stop_advertising(void) +{ + int ret = 0; + bool is_advertising = FALSE; + + /* Either HFP or SPP connected, stop advertising and disable page scan */ + if (fast_advertising_timer_id > 0) { + alarmmgr_remove_alarm(fast_advertising_timer_id); + fast_advertising_timer_id = 0; + } + + ret = bt_adapter_is_advertising(&is_advertising); + if (ret == BT_ERROR_NONE && is_advertising == false) + goto page_scan_off; + + ret = bt_adapter_stop_advertising(); + if (ret == BT_ERROR_NONE) + DBG("LE Advertising is stopped"); + else + ERR("Fail to stop advertising(%d)", ret); + +page_scan_off: + if(__get_setup_wizard_state() != 1) + __bt_page_scan(false); +} + +static void __bt_set_visibility(bt_adapter_visibility_mode_e mode) +{ + int ret = 0; + bt_adapter_visibility_mode_e visibility = 0; + int setup_wizard_state = __get_setup_wizard_state(); + + if (setup_wizard_state == 1) { + ERR("Setup wizard is running and no need to set visibility"); + return; + } + + ret = bt_adapter_get_visibility(&visibility, NULL); + if (ret != BT_ERROR_NONE) { + ERR("Failed to get bt_adapter_get_visibility(%d)", ret); + return; + } + + DBG("Visibility(%d), mode(%d)", visibility, mode); + if (visibility != mode) { + bt_adapter_set_visibility(mode, 0); + if (ret != BT_ERROR_NONE) + ERR("Failed to set bt_adapter_set_visibility(%d)", ret); + } +} + +static void __setup_wizard_state_cb(keynode_t *node, void *user_data) +{ + int err = 0, state = 0; + WcObject * wo = (WcObject*)user_data; + + if (!wo) + return; + + if (node) + state = vconf_keynode_get_bool(node); + else + err = vconf_get_bool(VCONFKEY_SETUP_WIZARD_FIRST_BOOT, &state); + if (err < 0) { + ERR("Failed to get setup wizard state"); + return; + } + + DBG("setup wizard state: %d", state); + if (state == 0) + __bt_page_scan(false); +} + +static void __power_saving_mode_cb(keynode_t *node, void *user_data) +{ + int ret = 0; + int ps_mode = 0; + bt_adapter_state_e last_bt_status = BT_ADAPTER_DISABLED; + WcObject * wo = (WcObject*)user_data; + + if (!wo) + return; + + if (node) + ps_mode = vconf_keynode_get_int(node); + else { + ret = vconf_get_int(VCONFKEY_SETAPPL_PSMODE, &ps_mode); + if (ret < 0) { + ERR("Failed to get power saving mode"); + return; + } + } + + if (ps_mode == SETTING_PSMODE_WEARABLE) { + ret = bt_adapter_get_state(&last_bt_status); + if (ret != BT_ERROR_NONE) { + ERR("Failed to get bluetooth status (%d)", ret); + return; + } + + DBG("bluetooth status : %d", last_bt_status); + if (last_bt_status != BT_ADAPTER_ENABLED) + return; + + vconf_set_int(VCONFKEY_WECONN_LAST_BT_STATUS, last_bt_status); + + ret = bt_adapter_disable(); + if (ret != BT_ERROR_NONE) + ERR("Failed to disable bluetooth (%d)", ret); + } else if (ps_mode == SETTING_PSMODE_NORMAL) { + vconf_get_int(VCONFKEY_WECONN_LAST_BT_STATUS, (int*)&last_bt_status); + if (last_bt_status == BT_ADAPTER_ENABLED) { + ret = bt_adapter_enable(); + if (ret != BT_ERROR_NONE) + ERR("Failed to enable bluetooth (%d)", ret); + } else + DBG("bluetooth was not enabled"); + } else + DBG("Invalid value : %d", ps_mode); +} + +static void __rfcomm_ready_status_changed_cb(keynode_t *node, void *user_data) +{ + int err = 0, rfcomm_ready = 0; + WcObject * wo = (WcObject*)user_data; + + if (!wo) + return; + + if (node) + rfcomm_ready = vconf_keynode_get_int(node); + else + err = vconf_get_int(VCONFKEY_RFCOMM_READY_STATUS, &rfcomm_ready); + + if (err < 0) { + ERR("Failed to get rfcomm ready status"); + return; + } + + sap_rfcomm_ready = rfcomm_ready; + DBG("rfcomm_ready(%d), SPP(%d), HFP(%d)", + rfcomm_ready, sap_connected, hf_connected); + + if (rfcomm_ready == 1) { + if (sap_connected != SAP_CONNECTED && hf_connected != true) + __bt_start_advertising(true); + } else + __bt_stop_advertising(); +} + +static void __pm_key_ignore_cb(keynode_t *node, void *user_data) +{ + int ret = 0; + int mode = 0; + bt_adapter_state_e bt_status = BT_ADAPTER_DISABLED; + static bt_adapter_state_e prev_bt_status = BT_ADAPTER_DISABLED; + + if (node) + mode = vconf_keynode_get_int(node); + else + ret = vconf_get_int(VCONFKEY_PM_KEY_IGNORE, &mode); + + DBG("current pm mode : %s mode", mode ? "clock" : "normal"); + + ret = bt_adapter_get_state(&bt_status); + if (ret != BT_ERROR_NONE) { + ERR("Failed to get bluetooth status (%d)", ret); + return; + } + DBG("bluetooth status : %d", bt_status); + + if (mode == 1) { + if (bt_status != BT_ADAPTER_ENABLED) + return; + + ret = bt_adapter_disable(); + if (ret != BT_ERROR_NONE) + ERR("Failed to disable bluetooth (%d)", ret); + } else { + if (bt_status != BT_ADAPTER_DISABLED) + return; + else if (prev_bt_status == BT_ADAPTER_ENABLED) { + ret = bt_adapter_enable(); + if (ret != BT_ERROR_NONE) + ERR("Failed to enable bluetooth (%d)", ret); + } + } + + prev_bt_status = bt_status; +} + +static void __bt_fatal_recovery(void) +{ + int ret; + bt_adapter_state_e state = BT_ADAPTER_DISABLED; + + ret = bt_adapter_get_state(&state); + if (ret != BT_ERROR_NONE) + ERR("Fatally failed to get BT state(%d)", ret); + + WARN("BT state(%d)", state); + bt_adapter_disable(); + + mBTRecoveryState = TRUE; +} + +static void __bt_check_fatal_recovery(void) +{ + const char *autoconnectable_bt_address = NULL; + + /* Important: BT auto-connectable address should be set by WMS, + * when EULA acceptance has been made by an user. + */ + autoconnectable_bt_address = + vconf_get_str(VCONFKEY_WECONN_AUTOCONNECTABLE_BT_ADDRESS); + if (autoconnectable_bt_address && strlen(autoconnectable_bt_address) > 0 && + __bt_check_connection_status(autoconnectable_bt_address, + __device_check_gatt_cb) == true) { + g_free((gpointer)autoconnectable_bt_address); + + ERR("Fatal error: both EDR and LE was connected"); + + __bt_fatal_recovery(); + return; + } + + g_free((gpointer)autoconnectable_bt_address); +} + +static void __sap_connection_status_changed_cb(keynode_t *node, void *user_data) +{ + int err = 0, sap_conn = 0; + int disconnected_manually = 0; + WcObject * wo = (WcObject*)user_data; + + if (!wo) + return; + + if (node) + sap_conn = vconf_keynode_get_int(node); + else + err = vconf_get_int(VCONFKEY_SAP_CONNECTION_STATUS, &sap_conn); + + if (err < 0) { + ERR("Failed to get sap connection status"); + return; + } + + if (sap_connected == sap_conn) { + ERR("sap connection is not changed(%d)", sap_conn); + return; + } + + sap_connected = sap_conn; + DBG("rfcomm_ready(%d), SPP(%d), HFP(%d)", sap_rfcomm_ready, sap_conn, hf_connected); + if (sap_conn == SAP_CONNECTED) { + __bt_stop_advertising(); + __bt_set_visibility(BT_ADAPTER_VISIBILITY_MODE_NON_DISCOVERABLE); + + if (hf_connected) { + __bt_notify_all_connected(1); + + vconf_get_int(VCONFKEY_WECONN_DISCONNECTED_MANUALLY, + &disconnected_manually); + if (disconnected_manually) + vconf_set_int(VCONFKEY_WECONN_DISCONNECTED_MANUALLY, 0); + } + + wc_object_set_property(wo, XSTR(W_SERVICE_TYPE_BT_SPP), + XSTR(W_SERVICE_STATE_CONNECTED)); + } else { + __bt_notify_all_connected(0); + + if (hf_connected != true) + __bt_check_fatal_recovery(); + + if (sap_rfcomm_ready == 1 && hf_connected != true) { + /* start LE advertising */ + DBG("HFP is disconnected"); + __bt_start_advertising(false); + } + + wc_object_set_property(wo, XSTR(W_SERVICE_TYPE_BT_SPP), + XSTR(W_SERVICE_STATE_DISCONNECTED)); + } +} + +static void __on_bt_adapter_state_changed(int result, bt_adapter_state_e state, + void *user_data) +{ + int sap_conn = SAP_DISCONNECTED; + const char *autoconnectable_bt_address = NULL; + WcObject *wo = (WcObject *)user_data; + + if (mBTRecoveryState == TRUE && state == BT_ADAPTER_DISABLED) { + ERR("BT fatal recovery tries to turn BT power on"); + + bt_adapter_enable(); + mBTRecoveryState = FALSE; + + return; + } + + if (!wo) + return; + + DBG("result = %d, state = %d", result, state); + switch (state) { + case BT_ADAPTER_ENABLED: + /* BT ON */ + wc_object_emit_callback(wo, WC_OBJECT_EVENT_BEARER_ENABLED, NULL); + + /* Update SPP state */ + if (vconf_get_int(VCONFKEY_SAP_CONNECTION_STATUS, &sap_conn) < 0) { + ERR("Failed to get sap connection status, but go ahead."); + sap_connected = SAP_DISCONNECTED; + } else + sap_connected = sap_conn; + + autoconnectable_bt_address = + vconf_get_str(VCONFKEY_WECONN_AUTOCONNECTABLE_BT_ADDRESS); + SECURE_DBG("last connected BT address(%s)", autoconnectable_bt_address); + if (autoconnectable_bt_address && + strlen(autoconnectable_bt_address) > 0) { + /* Update HFP status */ + hf_connected = __bt_check_connection_status( + autoconnectable_bt_address, __device_check_hfp_cb); + } else { + /* If not, clear white list, for example, factory reset */ + __bt_update_white_list(NULL, 0); + } + + g_free((gpointer)autoconnectable_bt_address); + + vconf_set_int(VCONFKEY_WECONN_LAST_BT_STATUS, 0); + + DBG("BT power(%d), rfcomm_ready(%d), SPP(%d) HFP(%d)", + state, sap_rfcomm_ready, sap_connected, hf_connected); + if (sap_rfcomm_ready == 1 && + sap_connected != SAP_CONNECTED && hf_connected != true) + __bt_start_advertising(true); + + wc_object_set_property(wo, XSTR(W_DEVICE_TYPE_BT), + XSTR(W_DEVICE_STATE_ENABLED)); + + break; + case BT_ADAPTER_DISABLED: + /* BT OFF */ + wc_object_emit_callback(wo, WC_OBJECT_EVENT_BEARER_DISABLED, NULL); + + hf_connected = false; + sap_connected = false; + + __fast_advertising_timer_stop(); + + wc_object_set_property(wo, XSTR(W_DEVICE_TYPE_BT), + XSTR(W_DEVICE_STATE_DISABLED)); + + break; + } +} + +static void __on_bt_audio_connection_state_changed_cb(int result, + bool connected, const char *remote_address, + bt_audio_profile_type_e type, void *user_data) +{ + int disconnected_manually = 0; + WcObject *wo = (WcObject *)user_data; + + DBG("result %d, type %d, rfcomm_ready(%d), SPP(%d), HFP(%d)", + result, type, sap_rfcomm_ready, sap_connected, connected); + SECURE_DBG("remote(%s)", remote_address); + + if (type != BT_AUDIO_PROFILE_TYPE_AG) { + if (type == BT_AUDIO_PROFILE_TYPE_A2DP) { + if (connected == false) + __bt_page_scan(true); + else + if (hf_connected == true || sap_connected == SAP_CONNECTED) + bt_adapter_set_connectable(false); + } + return; + } + + if (!wo) + return; + + hf_connected = connected; + if (connected) { + __bt_stop_advertising(); + __bt_set_visibility(BT_ADAPTER_VISIBILITY_MODE_NON_DISCOVERABLE); + + if (sap_connected == SAP_CONNECTED) { + __bt_notify_all_connected(1); + + vconf_get_int(VCONFKEY_WECONN_DISCONNECTED_MANUALLY, + &disconnected_manually); + if (disconnected_manually) + vconf_set_int(VCONFKEY_WECONN_DISCONNECTED_MANUALLY, 0); + } + + wc_object_set_property(wo, XSTR(W_SERVICE_TYPE_BT_HFP), + XSTR(W_SERVICE_STATE_CONNECTED)); + } else { + __bt_notify_all_connected(0); + + if (sap_connected != SAP_CONNECTED) + __bt_check_fatal_recovery(); + + if (sap_rfcomm_ready == 1 && sap_connected != SAP_CONNECTED) { + /* start LE advertising */ + DBG("SAP is disconnected"); + __bt_start_advertising(false); + } + + wc_object_set_property(wo, XSTR(W_SERVICE_TYPE_BT_HFP), + XSTR(W_SERVICE_STATE_DISCONNECTED)); + } +} + +static void __on_bt_socket_connection_state_changed_cb(int result, + bt_socket_connection_state_e state, + bt_socket_connection_s *connection, void *user_data) +{ + WcObject *wo = (WcObject *)user_data; + + DBG("result %d, state %d", result, state); + + if (!wo) + return; + +/* + if (state == BT_SOCKET_CONNECTED) + wc_object_set_property(wo, XSTR(W_SERVICE_TYPE_BT_SPP), + XSTR(W_SERVICE_STATE_CONNECTED)); + else + wc_object_set_property(wo, XSTR(W_SERVICE_TYPE_BT_SPP), + XSTR(W_SERVICE_STATE_DISCONNECTED)); +*/ +} + +static void __on_bt_device_connection_state_changed_cb(bool connected, + const char *remote_address, void *user_data) +{ + WcObject *wo = (WcObject *)user_data; + + DBG("result %d ", connected); + if(remote_address != NULL) + SECURE_DBG("remote address [%s]", remote_address); + + if (!wo) + return; + +/* This callback is triggered by ACL connected / disconnected event + if (connected) + wc_object_set_property(wo, XSTR(W_SERVICE_TYPE_BT_HFP), + XSTR(W_SERVICE_STATE_CONNECTED)); + else + wc_object_set_property(wo, XSTR(W_SERVICE_TYPE_BT_HFP), + XSTR(W_SERVICE_STATE_DISCONNECTED)); +*/ +} + +static void __on_bt_device_bond_destroy_cb(int result, + char *remote_address, void *user_data) +{ + WcObject *wo = (WcObject *)user_data; + + if (!wo || !remote_address) + return; + + DBG("result(%d) address(%s)", result, remote_address); + if (result == BT_ERROR_NONE) { + // Disable page scan if HFP or SAP is connected + if (hf_connected == true || sap_connected == SAP_CONNECTED) + __bt_page_scan(false); + } + + return; +} + +static void __on_bt_device_bond_created_cb(int result, + bt_device_info_s *device_info, void *user_data) +{ + WcObject *wo = (WcObject *)user_data; + + if (!wo || !device_info) + return; + + DBG("result(%d), device class(%d)", result, + device_info->bt_class.major_device_class); + if (device_info->bt_class.major_device_class != BT_MAJOR_DEVICE_CLASS_PHONE) + return; +} + +static gboolean __on_bt_sap_cmd_cb(WcObject *wo, + const void *event_info, void *user_data) +{ + struct SAP_event_data *evt_data = NULL; + struct SAP_event_data *new_data = NULL; + char buf[SAP_DATA_LEN_MAX] = { '\0', }; + char *bt_address = NULL; + // TODO: All of them should be from BT framework. + char *profiles = "HFP SPP GATT PAN"; + char cod[3] = {0x28, 0x07, 0x04}; + char services = 0x07; + unsigned int pos = 0; + int res, mainCmd, subCmd, cmdType, btVersion; + + evt_data = (struct SAP_event_data *)event_info; + if (evt_data == NULL) + return TRUE; + + mainCmd = (int)evt_data->data[1]; + if (mainCmd != SAP_PDU_CMD_BLUETOOTH) { + ERR("Invalid bluetooth command %d", mainCmd); + return TRUE; + } + + subCmd = (int)evt_data->data[2]; + switch(subCmd) { + case SAP_PDU_BT_CMD_GET_VERSION: + DBG("SAP_PDU_BT_CMD_GET_VERSION"); + + cmdType = (int)evt_data->data[3]; + switch (cmdType) { + case SAP_PDU_CMD_TYPE_REQ: + new_data = g_try_malloc0(sizeof(struct SAP_event_data)); + if (new_data == NULL) + return TRUE; + + new_data->count = evt_data->count; + new_data->index = evt_data->index; + new_data->data_len = SAP_DATA_LEN_MIN + 1; + + buf[pos++] = (char)new_data->data_len; + buf[pos++] = (char)SAP_PDU_CMD_BLUETOOTH; + buf[pos++] = (char)SAP_PDU_BT_CMD_GET_VERSION; + buf[pos++] = (char)SAP_PDU_CMD_TYPE_REQ_OK; + buf[pos++] = (char)SAP_PDU_BT_VER_40_P; + memcpy(new_data->data, buf, new_data->data_len); + + wc_object_emit_callback(wo, WC_OBJECT_EVENT_SAP_SEND_DATA, new_data); + g_free(new_data); + break; + case SAP_PDU_CMD_TYPE_REQ_OK: + btVersion = (int)evt_data->data[SAP_DATA_LEN_MIN]; + switch (btVersion) { + case SAP_PDU_BT_VER_30: + vconf_set_int(VCONFKEY_WECONN_CONNECTED_BT_VERSION, + SAP_PDU_BT_VER_30); + break; + case SAP_PDU_BT_VER_40_LE: + vconf_set_int(VCONFKEY_WECONN_CONNECTED_BT_VERSION, + SAP_PDU_BT_VER_40_LE); + break; + case SAP_PDU_BT_VER_40_DUAL: + vconf_set_int(VCONFKEY_WECONN_CONNECTED_BT_VERSION, + SAP_PDU_BT_VER_40_DUAL); + break; + case SAP_PDU_BT_VER_40_P: + vconf_set_int(VCONFKEY_WECONN_CONNECTED_BT_VERSION, + SAP_PDU_BT_VER_40_P); + break; + case SAP_PDU_BT_VER_41: + vconf_set_int(VCONFKEY_WECONN_CONNECTED_BT_VERSION, + SAP_PDU_BT_VER_41); + break; + default: + DBG("Unknown BT version"); + break; + } + break; + } + + break; + case SAP_PDU_BT_CMD_GET_ADDRESS: + DBG("SAP_PDU_BT_CMD_GET_ADDRESS"); + + cmdType = (int)evt_data->data[3]; + switch (cmdType) { + case SAP_PDU_CMD_TYPE_REQ: + new_data = g_try_malloc0(sizeof(struct SAP_event_data)); + if (new_data == NULL) + return TRUE; + + new_data->count = evt_data->count; + new_data->index = evt_data->index; + + res = bt_adapter_get_address(&bt_address); + if (res == BT_ERROR_NONE && bt_address) { + new_data->data_len = SAP_DATA_LEN_MIN + strlen(bt_address); + + buf[pos++] = (char)new_data->data_len; + buf[pos++] = (char)SAP_PDU_CMD_BLUETOOTH; + buf[pos++] = (char)SAP_PDU_BT_CMD_GET_ADDRESS; + buf[pos++] = (char)SAP_PDU_CMD_TYPE_REQ_OK; + memcpy(&buf[pos], bt_address, strlen(bt_address)); + memcpy(new_data->data, buf, new_data->data_len); + + free(bt_address); + } else { + new_data->data_len = SAP_DATA_LEN_MIN; + buf[pos++] = (char)new_data->data_len; + buf[pos++] = (char)SAP_PDU_CMD_BLUETOOTH; + buf[pos++] = (char)SAP_PDU_BT_CMD_GET_ADDRESS; + buf[pos++] = (char)SAP_PDU_CMD_TYPE_RESP_ERR; + memcpy(new_data->data, buf, new_data->data_len); + } + + wc_object_emit_callback(wo, WC_OBJECT_EVENT_SAP_SEND_DATA, new_data); + g_free(new_data); + break; + case SAP_PDU_CMD_TYPE_REQ_OK: + pos = evt_data->data_len - (unsigned int)SAP_DATA_LEN_MIN; + + bt_address = g_strndup(&evt_data->data[SAP_DATA_LEN_MIN], pos); + vconf_set_str(VCONFKEY_WECONN_AUTOCONNECTABLE_BT_ADDRESS, + bt_address); + g_free(bt_address); + + break; + } + + break; + case SAP_PDU_BT_CMD_GET_PROFILES: + DBG("SAP_PDU_BT_CMD_GET_PROFILES"); + + cmdType = (int)evt_data->data[3]; + switch (cmdType) { + case SAP_PDU_CMD_TYPE_REQ: + new_data = g_try_malloc0(sizeof(struct SAP_event_data)); + if (new_data == NULL) + return TRUE; + + new_data->count = evt_data->count; + new_data->index = evt_data->index; + new_data->data_len = SAP_DATA_LEN_MIN + strlen(profiles); + + buf[pos++] = (char)new_data->data_len; + buf[pos++] = (char)SAP_PDU_CMD_BLUETOOTH; + buf[pos++] = (char)SAP_PDU_BT_CMD_GET_PROFILES; + buf[pos++] = (char)SAP_PDU_CMD_TYPE_REQ_OK; + memcpy(&buf[pos], profiles, strlen(profiles)); + memcpy(new_data->data, buf, new_data->data_len); + + wc_object_emit_callback(wo, WC_OBJECT_EVENT_SAP_SEND_DATA, new_data); + g_free(new_data); + break; + } + + break; + case SAP_PDU_BT_CMD_GET_COD: + DBG("SAP_PDU_BT_CMD_GET_COD"); + + cmdType = (int)evt_data->data[3]; + switch (cmdType) { + case SAP_PDU_CMD_TYPE_REQ: + new_data = g_try_malloc0(sizeof(struct SAP_event_data)); + if (new_data == NULL) + return TRUE; + + new_data->count = evt_data->count; + new_data->index = evt_data->index; + new_data->data_len = SAP_DATA_LEN_MIN + sizeof(cod); + + buf[pos++] = (char)new_data->data_len; + buf[pos++] = (char)SAP_PDU_CMD_BLUETOOTH; + buf[pos++] = (char)SAP_PDU_BT_CMD_GET_COD; + buf[pos++] = (char)SAP_PDU_CMD_TYPE_REQ_OK; + memcpy(&buf[pos], cod, sizeof(cod)); + memcpy(new_data->data, buf, new_data->data_len); + + wc_object_emit_callback(wo, WC_OBJECT_EVENT_SAP_SEND_DATA, new_data); + g_free(new_data); + break; + case SAP_PDU_CMD_TYPE_REQ_OK: + break; + } + + break; + case SAP_PDU_BT_CMD_GET_SERVICES: + DBG("SAP_PDU_BT_CMD_GET_SERVICES"); + + cmdType = (int)evt_data->data[3]; + switch (cmdType) { + case SAP_PDU_CMD_TYPE_REQ: + new_data = g_try_malloc0(sizeof(struct SAP_event_data)); + if (new_data == NULL) + return TRUE; + + new_data->count = evt_data->count; + new_data->index = evt_data->index; + new_data->data_len = SAP_DATA_LEN_MIN + 1; + + buf[pos++] = (char)new_data->data_len; + buf[pos++] = (char)SAP_PDU_CMD_BLUETOOTH; + buf[pos++] = (char)SAP_PDU_BT_CMD_GET_SERVICES; + buf[pos++] = (char)SAP_PDU_CMD_TYPE_REQ_OK; + buf[pos++] = services; + memcpy(new_data->data, buf, new_data->data_len); + + wc_object_emit_callback(wo, WC_OBJECT_EVENT_SAP_SEND_DATA, new_data); + g_free(new_data); + break; + case SAP_PDU_CMD_TYPE_REQ_OK: + break; + } + + break; + case SAP_PDU_BT_CMD_DISCONNECTED_MANUALLY: + DBG("SAP_PDU_BT_CMD_DISCONNECTED_MANUALLY"); + + cmdType = (int)evt_data->data[3]; + switch (cmdType) { + case SAP_PDU_CMD_TYPE_NOTI: + new_data = g_try_malloc0(sizeof(struct SAP_event_data)); + if (new_data == NULL) + return TRUE; + + vconf_set_int(VCONFKEY_WECONN_DISCONNECTED_MANUALLY, 1); + + new_data->count = evt_data->count; + new_data->index = evt_data->index; + new_data->data_len = SAP_DATA_LEN_MIN; + + buf[pos++] = (char)new_data->data_len; + buf[pos++] = (char)SAP_PDU_CMD_BLUETOOTH; + buf[pos++] = evt_data->data[2]; + buf[pos++] = (char)SAP_PDU_CMD_TYPE_REQ_OK; + memcpy(new_data->data, buf, new_data->data_len); + + wc_object_emit_callback(wo, WC_OBJECT_EVENT_SAP_SEND_DATA, new_data); + g_free(new_data); + break; + default: + ERR("Invalid bt sub command %d", evt_data->data[3]); + break; + } + + break; + default: + ERR("Invalid bt sub command %d", evt_data->data[2]); + break; + } + + return TRUE; +} + +static void _bt_bearer_register_events(WcObject *wo) +{ + enum SAP_PDU_commands_type cmd_type = SAP_PDU_CMD_TYPE_REQ; + enum SAP_PDU_BT_commands bt_cmds = SAP_PDU_BT_CMD_GET_VERSION; + char *cmd = NULL; + char *event = NULL; + + cmd = g_strdup_printf("%s[%d]", + WC_OBJECT_EVENT_SAP_RECEIVE_DATA, SAP_PDU_CMD_BLUETOOTH); + + while (cmd_type < SAP_PDU_CMD_TYPE_MAX ) { + while (bt_cmds < SAP_PDU_BT_CMD_MAX) { + event = g_strdup_printf("%s[%d][%d]", cmd, bt_cmds, cmd_type); + wc_object_add_callback(wo, event, __on_bt_sap_cmd_cb, NULL); + g_free(event); + + bt_cmds++; + + if (bt_cmds == SAP_PDU_BT_CMD_FX_MAX) + bt_cmds = SAP_PDU_BT_CMD_DISCONNECTED_MANUALLY; + } + + bt_cmds = SAP_PDU_BT_CMD_GET_VERSION; + cmd_type++; + } + + g_free(cmd); +} + +static void _bt_bearer_deregister_events(WcObject *wo) +{ + enum SAP_PDU_commands_type cmd_type = SAP_PDU_CMD_TYPE_REQ; + enum SAP_PDU_BT_commands bt_cmds = SAP_PDU_BT_CMD_GET_VERSION; + char *cmd = NULL; + char *event = NULL; + + cmd = g_strdup_printf("%s[%d]", + WC_OBJECT_EVENT_SAP_RECEIVE_DATA, SAP_PDU_CMD_BLUETOOTH); + + while (cmd_type < SAP_PDU_CMD_TYPE_MAX ) { + while (bt_cmds < SAP_PDU_BT_CMD_MAX) { + event = g_strdup_printf("%s[%d][%d]", cmd, bt_cmds, cmd_type); + wc_object_del_callback(wo, event, __on_bt_sap_cmd_cb); + g_free(event); + + bt_cmds++; + + if (bt_cmds == SAP_PDU_BT_CMD_FX_MAX) + bt_cmds = SAP_PDU_BT_CMD_SET_PAN_CONFIGURATION_ON; + } + + bt_cmds = SAP_PDU_BT_CMD_GET_VERSION; + cmd_type++; + } + + g_free(cmd); +} + +static void _register_vconf_callback(WcObject *wo) +{ + int ret = VCONF_OK; + + if (!wo) + return; + + DBG(""); + + // TODO: use sap event callback + ret = vconf_notify_key_changed(VCONFKEY_SAP_CONNECTION_STATUS, + __sap_connection_status_changed_cb, wo); + if (ret != VCONF_OK) + ERR("Failed to register sap connection status callback(%d)", ret); + + ret = vconf_notify_key_changed(VCONFKEY_RFCOMM_READY_STATUS, + __rfcomm_ready_status_changed_cb, wo); + if (ret != VCONF_OK) + ERR("Failed to register sap connection status callback(%d)", ret); + + ret = vconf_notify_key_changed(VCONFKEY_SETUP_WIZARD_FIRST_BOOT, + __setup_wizard_state_cb, wo); + if (ret != VCONF_OK) + ERR("Failed to register setup wizard state callback(%d)", ret); + + ret = vconf_notify_key_changed(VCONFKEY_SETAPPL_PSMODE, __power_saving_mode_cb, wo); + if (ret != VCONF_OK) + ERR("Failed to register power saving mode callback (%d)", ret); + + ret = vconf_notify_key_changed(VCONFKEY_PM_KEY_IGNORE, __pm_key_ignore_cb, wo); + if (ret != VCONF_OK) + ERR("Failed to register pm key ignore callback (%d)", ret); +} + +static void _deregister_vconf_callback(void) +{ + int ret = VCONF_OK; + + DBG(""); + + ret = vconf_ignore_key_changed(VCONFKEY_SAP_CONNECTION_STATUS, + __sap_connection_status_changed_cb); + if (ret != VCONF_OK) + ERR("Failed to de-register sap connection status callback(%d)", ret); + + ret = vconf_ignore_key_changed(VCONFKEY_RFCOMM_READY_STATUS, + __rfcomm_ready_status_changed_cb); + if (ret != VCONF_OK) + ERR("Failed to register sap connection status callback(%d)", ret); + + ret = vconf_ignore_key_changed(VCONFKEY_SETUP_WIZARD_FIRST_BOOT, + __setup_wizard_state_cb); + if (ret != VCONF_OK) + ERR("Failed to de-register setup wizard state callback(%d)", ret); + + ret = vconf_ignore_key_changed(VCONFKEY_SETAPPL_PSMODE, + __power_saving_mode_cb); + if (ret != VCONF_OK) + ERR("Failed to de-register power saving mode callback (%d)", ret); + + ret = vconf_ignore_key_changed(VCONFKEY_PM_KEY_IGNORE, + __pm_key_ignore_cb); + if (ret != VCONF_OK) + ERR("Failed to de-register pm key ignore callback (%d)", ret); +} + +static void _register_bt_event_listener(WcObject *wo) +{ + if (!wo) + return; + + DBG(""); + + /* BT power event listener */ + bt_adapter_set_state_changed_cb( + __on_bt_adapter_state_changed, wo); + + /* BT HFP event listener */ + bt_audio_set_connection_state_changed_cb( + __on_bt_audio_connection_state_changed_cb, wo); + + /* BT SPP event listener */ + /* TODO: should register eSAP event listener */ + bt_socket_set_connection_state_changed_cb( + __on_bt_socket_connection_state_changed_cb, wo); + + /* TODO: use HFP rather than device state */ + bt_device_set_connection_state_changed_cb( + __on_bt_device_connection_state_changed_cb, wo); + + bt_device_set_bond_created_cb( + __on_bt_device_bond_created_cb, wo); + + bt_device_set_bond_destroyed_cb( + __on_bt_device_bond_destroy_cb, wo); +} + +static void _deregister_bt_event_listener(void) +{ + DBG(""); + + bt_adapter_unset_state_changed_cb(); + + bt_device_unset_bond_created_cb(); + + bt_device_unset_connection_state_changed_cb(); + + bt_audio_unset_connection_state_changed_cb(); + + bt_socket_unset_connection_state_changed_cb(); +} + +static int bt_bearer_init(WcObject *wo) +{ + int ret; + int sap_conn = SAP_DISCONNECTED; + const char *autoconnectable_bt_address = NULL; + bt_adapter_state_e state = BT_ADAPTER_DISABLED; + + ret = bt_initialize(); + if (ret != BT_ERROR_NONE) + return ret; + + ret = bt_audio_initialize(); + if (ret != BT_ERROR_NONE) + return ret; + + ret = alarmmgr_init("weconn"); + if (ret != ALARMMGR_RESULT_SUCCESS) + ERR("alarmmgr_init FAILED (%d)", ret); + + ret = bt_adapter_get_state(&state); + if (ret != BT_ERROR_NONE) + return ret; + + if (state == BT_ADAPTER_ENABLED) { + wc_object_emit_callback(wo, WC_OBJECT_EVENT_BEARER_ENABLED, NULL); + + /* Update SPP state */ + if (vconf_get_int(VCONFKEY_SAP_CONNECTION_STATUS, &sap_conn) < 0) { + ERR("Failed to get sap connection status, but go ahead."); + sap_connected = SAP_DISCONNECTED; + } else + sap_connected = sap_conn; + + autoconnectable_bt_address = + vconf_get_str(VCONFKEY_WECONN_AUTOCONNECTABLE_BT_ADDRESS); + DBG("last connected BT address(%s)", autoconnectable_bt_address); + if (autoconnectable_bt_address && + strlen(autoconnectable_bt_address) > 0) { + /* Update HFP status */ + hf_connected = __bt_check_connection_status( + autoconnectable_bt_address, __device_check_hfp_cb); + } else { + /* If not, clear white list, for example, factory reset */ + __bt_update_white_list(NULL, 0); + } + + g_free((gpointer)autoconnectable_bt_address); + + DBG("BT power(%d), rfcomm_ready(%d), SPP(%d) HFP(%d)", + state, sap_rfcomm_ready, sap_connected, hf_connected); + if (sap_rfcomm_ready == 1 && + sap_connected != SAP_CONNECTED && hf_connected != true) + __bt_start_advertising(true); + + wc_object_set_property(wo, XSTR(W_DEVICE_TYPE_BT), + XSTR(W_DEVICE_STATE_ENABLED)); + } + + _register_vconf_callback(wo); + + _register_bt_event_listener(wo); + + _bt_bearer_register_events(wo); + + return ret; +} + +static int bt_bearer_activate(WcObject *wo) +{ + int ret; + bt_adapter_state_e state = BT_ADAPTER_DISABLED; + + ret = bt_adapter_get_state(&state); + if (ret != BT_ERROR_NONE) + return ret; + + DBG("BT state = %d", state); + if (state == BT_ADAPTER_DISABLED) { + ret = bt_adapter_enable(); + if (ret != BT_ERROR_NONE) { + ERR("Failed to enable bluetooth device"); + return ret; + } + } + + DBG("Activated"); + + return 0; +} + +static int bt_bearer_deactivate(WcObject *wo) +{ + int ret; + bt_adapter_state_e state = BT_ADAPTER_DISABLED; + + DBG("Deactivated"); + + ret = bt_adapter_get_state(&state); + if (ret != BT_ERROR_NONE) + return ret; + + DBG("BT state = %d", state); + if (state == BT_ADAPTER_ENABLED) + bt_adapter_disable(); + + return 0; +} + +static void bt_bearer_exit(WcObject *wo) +{ + DBG("Unloaded"); + + _bt_bearer_deregister_events(wo); + + _deregister_vconf_callback(); + + _deregister_bt_event_listener(); + + bt_deinitialize(); + + alarmmgr_fini(); +} + +EXPORT_API struct bearer_driver_desc bearer_driver_desc = { + .name = "bluetooth", + .technology = WC_TECHNOLOGY_BLUETOOTH, + + .init = bt_bearer_init, + .activate = bt_bearer_activate, + .deactivate = bt_bearer_deactivate, + .exit = bt_bearer_exit, +}; diff --git a/plugins/esap.c b/plugins/esap.c new file mode 100644 index 0000000..c030061 --- /dev/null +++ b/plugins/esap.c @@ -0,0 +1,866 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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. + * + */ + +#include +#include +#include +#include + +#include "esap.h" +#include "control.h" +#include "plugin.h" + + +#define BUF_SIZE 1024 +#define SAP_CHANNEL_ID 222 + +#define SAP_CONNECTION_VCONF_NOTIFICATION "memory/private/sap/conn_status" + +struct esap_private_data { + WcObject *wo_esap; + unsigned char role; +}; + +static unsigned int sap_service_handle; +static unsigned int sap_local_agentId; +static struct esap_private_data *gpd; +static GSList *send_pdu_list = NULL; + +static int __util_send_sap_pdu_data(GSList *pdu_list); +static int __util_process_sap_pdu_data(WcObject *wo, GSList **pdu_list); +static unsigned int __util_encode_sap_pdu_data(GSList *pdu_list, char **dst); +static int __util_decode_sap_pdu_data(char *src, unsigned int src_len, GSList **pdu_list); + +static int __util_process_sap_pdu_data(WcObject *wo, GSList **pdu_list) +{ + unsigned int index = 0; + int count = 0; + char *event; + GSList *list; + struct SAP_PDU_data *pdu_data = NULL; + struct SAP_event_data *evt_data = NULL; + + index = g_slist_length(send_pdu_list); + for (list = *pdu_list; list; list = list->next) { + pdu_data = (struct SAP_PDU_data *)list->data; + if (pdu_data != NULL) { + evt_data = g_try_malloc0(sizeof(struct SAP_event_data)); + if (evt_data == NULL) + return -1; + + evt_data->index = index; + evt_data->count = g_slist_length(*pdu_list); + evt_data->data_len = (unsigned int)pdu_data->length; + memcpy(evt_data->data, pdu_data->data, evt_data->data_len); + + event = g_strdup_printf("%s[%d][%d][%d]", WC_OBJECT_EVENT_SAP_RECEIVE_DATA, + evt_data->data[1], evt_data->data[2], evt_data->data[3]); + + wc_object_emit_callback(wo, event, evt_data); + g_free(evt_data); + g_free(event); + count++; + } + } + + return count; +} + +static int __util_send_sap_pdu_data(GSList *pdu_list) +{ + char *buf; + unsigned int len = 0; + int res = -1; + + if (g_slist_length(pdu_list) <= 0) { + ERR("No PDU data to send"); + return -1; + } + + len = __util_encode_sap_pdu_data(pdu_list, &buf); + if (len > 0) { + res = sendData(sap_service_handle, SAP_CHANNEL_ID, (unsigned short int)len, (void*)buf); + if (res == 0) + DBG("Completed to send data %d byte", len); + else + ERR("Failed to send data, error %d", res); + + g_free(buf); + } + + return res; +} + +static unsigned int __util_encode_sap_pdu_data(GSList *pdu_list, char **dst) +{ + GSList *list; + struct SAP_PDU_data *pdu_data = NULL; + char buf[SAP_DATA_LEN_MAX] = {0}; + unsigned int length = 0; + unsigned int index = 0; + + for (list = pdu_list; list != NULL; list = list->next) { + pdu_data = (struct SAP_PDU_data *)list->data; + if (pdu_data != NULL) { + if (pdu_data->main_cmd == 0 || pdu_data->sub_cmd == 0 || + pdu_data->type_cmd == 0) { + ERR("Invalid PDU data: main_cmd[%d], sub_cmd[%d], type_cmd[%d]", + pdu_data->main_cmd, pdu_data->sub_cmd, pdu_data->type_cmd); + continue; + } + + length = (unsigned int)pdu_data->length + (unsigned int)SAP_DATA_LEN_MIN; + buf[index++] = (char)length; + buf[index++] = pdu_data->main_cmd; + buf[index++] = pdu_data->sub_cmd; + buf[index++] = pdu_data->type_cmd; + memcpy(&buf[index], pdu_data->data, (unsigned int)pdu_data->length); + + index += (unsigned int)pdu_data->length; + } + } + + *dst = g_try_malloc0(index + 1); + g_strlcat(*dst, buf, index + 1); + + DBG("Completed to encode data %d byte", index); + return index; +} + +static int __util_decode_sap_pdu_data(char *src, unsigned int src_len, GSList **pdu_list) +{ + int res = -1; + unsigned int pos = 0; + unsigned int data_len = 0; + struct SAP_PDU_data *pdu_data = NULL; + + if (src_len < SAP_DATA_LEN_MIN) { + ERR("invalid data length, %d", src_len); + return res; + } + + while (pos < src_len) { + pdu_data = g_try_malloc0(sizeof(struct SAP_PDU_data)); + if (!pdu_data || !src) { + ERR("invalid input parameter !!!"); + return res; + } + + data_len = (unsigned int)src[pos]; + if (data_len < SAP_DATA_LEN_MIN) { + ERR("invalid pdu data length, %d", data_len); + goto error; + } + + memcpy(pdu_data->data, &src[pos], data_len); + pdu_data->length = (char)data_len; + + *pdu_list = g_slist_append(*pdu_list, pdu_data); + pos += data_len; + } + + if (*pdu_list) { + res = (int)g_slist_length(*pdu_list); + DBG("Done decode data counts = %d", res); + } + + return res; + +error: + if (g_slist_length(*pdu_list) > 0) + g_slist_free_full(*pdu_list, g_free); + else { + if (pdu_data != NULL) + g_free(pdu_data); + } + + return res; +} + +static int _request_feature_exchange(GSList *feat_list) +{ + GSList *list; + GSList *pdu_list = NULL; + struct SAP_PDU_data *sap_data = NULL; + + if (feat_list == NULL) + return 0; + + for (list = feat_list; list != NULL; list = list->next) { + if (g_strcmp0(list->data, XSTR(SAP_PDU_BT_CMD_GET_VERSION)) == 0) { + sap_data = g_try_malloc0(sizeof(struct SAP_PDU_data)); + if (!sap_data) { + ERR("invalid input parameter !!!"); + return -1; + } + + sap_data->main_cmd = SAP_PDU_CMD_BLUETOOTH; + sap_data->sub_cmd = SAP_PDU_BT_CMD_GET_VERSION; + sap_data->type_cmd = SAP_PDU_CMD_TYPE_REQ; + pdu_list = g_slist_append(pdu_list, sap_data); + } else if (g_strcmp0(list->data, XSTR(SAP_PDU_BT_CMD_GET_ADDRESS)) == 0) { + sap_data = g_try_malloc0(sizeof(struct SAP_PDU_data)); + if (!sap_data) { + ERR("invalid input parameter !!!"); + return -1; + } + + sap_data->main_cmd = SAP_PDU_CMD_BLUETOOTH; + sap_data->sub_cmd = SAP_PDU_BT_CMD_GET_ADDRESS; + sap_data->type_cmd = SAP_PDU_CMD_TYPE_REQ; + pdu_list = g_slist_append(pdu_list, sap_data); + } else if (g_strcmp0(list->data, XSTR(SAP_PDU_BT_CMD_GET_PROFILES)) == 0) { + sap_data = g_try_malloc0(sizeof(struct SAP_PDU_data)); + if (!sap_data) { + ERR("invalid input parameter !!!"); + return -1; + } + + sap_data->main_cmd = SAP_PDU_CMD_BLUETOOTH; + sap_data->sub_cmd = SAP_PDU_BT_CMD_GET_PROFILES; + sap_data->type_cmd = SAP_PDU_CMD_TYPE_REQ; + pdu_list = g_slist_append(pdu_list, sap_data); + } else if (g_strcmp0(list->data, XSTR(SAP_PDU_GENERIC_CMD_GET_MANUFACTURER)) == 0) { + sap_data = g_try_malloc0(sizeof(struct SAP_PDU_data)); + if (!sap_data) { + ERR("invalid input parameter !!!"); + return -1; + } + + sap_data->main_cmd = SAP_PDU_CMD_GENERIC; + sap_data->sub_cmd = SAP_PDU_GENERIC_CMD_GET_MANUFACTURER; + sap_data->type_cmd = SAP_PDU_CMD_TYPE_REQ; + pdu_list = g_slist_append(pdu_list, sap_data); + } else { + ERR("Not support feature : %s", (char *)list->data); + } + } + + __util_send_sap_pdu_data(pdu_list); + + g_slist_free_full(pdu_list, g_free); + + return 0; +} + +static void on_register_service_agent_confirm(int wStatusCode, unsigned int uwLocalAgentId) +{ + DBG("wStatusCode: %d, uwLocalAgentId: %d", wStatusCode, uwLocalAgentId); + + if (wStatusCode != RESULT_SUCCESS) { + DBG("Register service agent failed.\n"); + return; + } + + sap_local_agentId = uwLocalAgentId; + + if (gpd->role == SAP_ROLE_PROVIDER) { + DBG("No need to call findPeerAgent!!"); + return; + } + + if (findPeerAgent(uwLocalAgentId) != RESULT_SUCCESS) { + DBG("fail to call findPeerAgent.\n"); + // TBD: error handling + return; + } + DBG("success to call findPeerAgent\n"); +} + +static void on_deregister_service_agent_confirm(int wStatusCode, unsigned int uwLocalAgentId) +{ + DBG("wStatusCode: %d, uwLocalAgentId: %d", wStatusCode, uwLocalAgentId); +} + +static void on_peer_agent(unsigned int uwLocalAgentId, PeerAgent* pPeerAgent, int wStatusCode) +{ + DBG("wStatusCode: %d, uwLocalAgentId: %d", wStatusCode, uwLocalAgentId); + + if (wStatusCode != RESULT_SUCCESS) { + DBG("find peer agent failed."); + //TBD: error handling + return; + } else if (wStatusCode == RESULT_SUCCESS) { + DBG("find peer agent success: ALE Name %s, Id %s", pPeerAgent->pchALEName, pPeerAgent->pchASPID); + + if(createServiceConnection(uwLocalAgentId, pPeerAgent) != RESULT_SUCCESS) { + ERR("fail to create service connection"); + // TBD: error handling + return; + } + } +} + +static void on_service_connection_confirm(unsigned int uwLocalAgentId, unsigned int uwServiceHandle, PeerAgent* pPeerAgent, int wStatusCode) +{ + DBG("uwLocalAgentId: %d, uwServiceHandle: %d, wStatusCode: %d", uwLocalAgentId, uwServiceHandle, wStatusCode); + + if (wStatusCode != RESULT_SUCCESS) { + DBG("service connection fail.\n"); + // TBD: retry logic + } else { + DBG("service connection success.\n"); + sap_service_handle = uwServiceHandle; + } +} + +static void on_service_connection_indication(unsigned int uwLocalAgentId, unsigned int uwServiceHandle, PeerAgent* pPeerAgent) +{ + WcObject *wo = NULL; + + DBG("uwLocalAgentId: %d, uwServiceHandle: %d", + uwLocalAgentId, uwServiceHandle); + + if (acceptServiceConnection(uwLocalAgentId, uwServiceHandle, + pPeerAgent) != RESULT_SUCCESS) { + ERR("fail to accept service connection"); + // TBD: error handling + return; + } else { + GSList *list = NULL; + + DBG("success to accept service connection"); + // wait for service connection confirm? - Need to check. + sap_service_handle = uwServiceHandle; + + wo = wc_object_find(WC_TECHNOLOGY_BLUETOOTH); + if (wo) { + wc_object_set_property(wo, WC_OBJECT_EVENT_SAP_BCMSERVICE_STATUS, + XSTR(W_SERVICE_STATE_CONNECTED)); + } + + list = g_slist_append(list, XSTR(SAP_PDU_BT_CMD_GET_VERSION)); + list = g_slist_append(list, XSTR(SAP_PDU_GENERIC_CMD_GET_MANUFACTURER)); + + _request_feature_exchange(list); + } +} + +static void on_service_termination_indication(unsigned int uwServiceHandle, int wStatusCode) +{ + WcObject *wo = NULL; + + DBG("uwServiceHandle: %d, wStatusCode: %d", uwServiceHandle, wStatusCode); + + wo = wc_object_find(WC_TECHNOLOGY_BLUETOOTH); + if (!wo) { + ERR("No technology"); + return; + } + + wc_object_set_property(wo, WC_OBJECT_EVENT_SAP_BCMSERVICE_STATUS, + XSTR(W_SERVICE_STATE_DISCONNECTED)); +} + +static void on_receive(unsigned int uwServiceHandle, + unsigned short int uwChannelId, + unsigned int uwPayloadLength, void *pvBuffer) +{ + int res = 0; + WcObject *wo = NULL; + GSList *pdu_list = NULL; + char payload[BUF_SIZE] = { '\0', }; + + if (pvBuffer == NULL) + return; + + DBG("uwServiceHandle:%d, uwChannelId:%d, uwPayloadLength:%d\n", + uwServiceHandle, uwChannelId, uwPayloadLength); + + wo = wc_object_find(WC_TECHNOLOGY_BLUETOOTH); + if (!wo) { + ERR("No technology"); + return; + } + + memset(payload, 0, BUF_SIZE); + memcpy(payload, pvBuffer, uwPayloadLength); + + res = __util_decode_sap_pdu_data(payload, uwPayloadLength, &pdu_list); + if (res > 0) { + res = __util_process_sap_pdu_data(wo, &pdu_list); + DBG("processed %d data", res); + } else { + ERR("Mal-formed data"); + } +} + +static void on_space_available(unsigned int uwServiceHandle, + unsigned short int uwChannelId, unsigned int ulSpaceAvailable) +{ + DBG("uwChannelId: %d, ulSpaceAvailable: %d", uwChannelId, ulSpaceAvailable); +} + +static void on_error(unsigned int uwServiceHandle, unsigned int dwErrorCode) +{ + DBG("uwServiceHandle: %d, dwErrorCode: %d\n", uwServiceHandle, dwErrorCode); +} + +static SAPNotification sap_noti_funcs = { + on_register_service_agent_confirm, + on_receive, + on_service_connection_indication, + on_service_termination_indication, + on_service_connection_confirm, + on_peer_agent, + on_space_available, + on_error, + on_deregister_service_agent_confirm, +}; + +static gboolean _esap_service_init(WcObject *wo) +{ + ServiceDesc *service; + ChannelDesc *channel; + int ret; + + if (!gpd || !wo) + return FALSE; + + // register callbacks + ret = registerNotifications(sap_noti_funcs); + if (ret != RESULT_SUCCESS) { + ERR("Fail to register notification"); + return FALSE; + } + + DBG("registerNotifications success: sap_service_handle %d", sap_service_handle); + // Define service description + service = (ServiceDesc *)g_try_malloc0(sizeof(ServiceDesc)); + if (service == NULL) { + ERR("Memory allocation fail for ServiceDesc"); + return FALSE; + } + + channel = (ChannelDesc *)g_try_malloc0(sizeof(ChannelDesc)); + if (channel == NULL) { + ERR("Memory allocation fail for ChannelDesc"); + g_free(service); + return FALSE; + } + + channel->wChannelID = SAP_CHANNEL_ID; + channel->ubNoOfPayloadType = 1; // which value is to be set??? + channel->payloadTypeList = SAP_ALL_PAYLOAD; // Json or binary + + service->pchASPID = "/system/bcmservice"; + service->pchSPFName = "/system/bcmservice"; + service->wConnectionTypeMask = SAP_ALL; // all connectivity + service->ubRole = gpd->role; + service->pChDescArray = channel; + service->numChannels = 1; + + // register service + // ret = getRegisteredServiceAgent(service->pchASPID, SERVICE_AGENT_ROLE_PROVIDER); + ret = registerServiceAgent(service); + if (ret != RESULT_SUCCESS) { + ERR("registerServiceAgent Fail with reason: %d", ret); + g_free(service); + g_free(channel); + return FALSE; + } + + wc_object_set_property(wo, WC_OBJECT_EVENT_SAP_BCMSERVICE_STATUS, + XSTR(W_SERVICE_STATE_DISCONNECTED)); + + DBG("SAP service registered(%s)", (gpd->role == SAP_ROLE_PROVIDER) ? "PROVIDER" : "CONSUMER"); + return TRUE; +} + +static gboolean __on_esap_event_send_data_cb(WcObject *wo, + const void *event_info, void *user_data) +{ + struct SAP_event_data *evt_data = NULL; + struct SAP_send_data *send_data = NULL; + int res; + + evt_data = (struct SAP_event_data *)event_info; + if (evt_data == NULL) + return TRUE; + + if (evt_data->count > 1) { + send_data = g_slist_nth_data(send_pdu_list, evt_data->index); + if (send_data == NULL) { + send_data = g_try_malloc0(sizeof(struct SAP_send_data)); + if (send_data == NULL) + return TRUE; + + send_data->count = 1; + send_data->data_len = evt_data->data_len; + memcpy(send_data->data, evt_data->data, evt_data->data_len); + send_pdu_list = g_slist_append(send_pdu_list, send_data); + } else { + send_data->count++; + + if (send_data->count == evt_data->count) { + memcpy(&send_data->data[send_data->data_len], + evt_data->data, evt_data->data_len); + send_data->data_len += evt_data->data_len; + + res = sendData(sap_service_handle, SAP_CHANNEL_ID, + (unsigned short int)send_data->data_len, + (void*)send_data->data); + if (res == 0) + DBG("Completed to send data %d byte", send_data->data_len); + else + ERR("Failed to send data, error %d", res); + + g_free(send_data); + send_pdu_list = g_slist_remove(send_pdu_list, send_data); + } else { + memcpy(&send_data->data[send_data->data_len], + evt_data->data, evt_data->data_len); + send_data->data_len += evt_data->data_len; + } + } + } else { + res = sendData(sap_service_handle, SAP_CHANNEL_ID, + (unsigned short int)evt_data->data_len, + (void*)evt_data->data); + if (res == 0) + DBG("Completed to send data %d byte", evt_data->data_len); + else + ERR("Failed to send data, error %d", res); + } + + return TRUE; +} + +static gboolean _on_generic_sap_cmd_cb(WcObject *wo, + const void *event_info, void *user_data) +{ + struct SAP_event_data *evt_data = NULL; + struct SAP_event_data *new_data = NULL; + char buf[SAP_DATA_LEN_MAX] = { '\0', }; + char *value = NULL; + unsigned int len; + unsigned int pos = 0; + int mainCmd, subCmd, cmdType; + int ret; + + evt_data = (struct SAP_event_data *)event_info; + if (evt_data == NULL) + return TRUE; + + mainCmd = (int)evt_data->data[1]; + if (mainCmd != SAP_PDU_CMD_GENERIC) { + ERR("Invalid generic command %d", mainCmd); + return TRUE; + } + + subCmd = (int)evt_data->data[2]; + switch (subCmd) { + case SAP_PDU_GENERIC_CMD_GET_PLATFORM_NAME: + DBG("SAP_PDU_GENERIC_CMD_GET_PLATFORM_NAME"); + + cmdType = (int)evt_data->data[3]; + switch (cmdType) { + case SAP_PDU_CMD_TYPE_REQ: + ret = system_info_get_value_string(SYSTEM_INFO_KEY_PLATFORM_NAME, + &value); + if (ret == 0 && value != NULL) { + new_data = g_try_malloc0(sizeof(struct SAP_event_data)); + if (new_data == NULL) + return TRUE; + + new_data->count = evt_data->count; + new_data->index = evt_data->index; + new_data->data_len = SAP_DATA_LEN_MIN + strlen(value); + + buf[pos++] = (char)new_data->data_len; + buf[pos++] = (char)SAP_PDU_CMD_GENERIC; + buf[pos++] = (char)SAP_PDU_GENERIC_CMD_GET_PLATFORM_VERSION; + buf[pos++] = (char)SAP_PDU_CMD_TYPE_REQ_OK; + memcpy(&buf[pos], value, strlen(value)); + memcpy(new_data->data, buf, new_data->data_len); + + g_free(value); + + wc_object_emit_callback(wo, WC_OBJECT_EVENT_SAP_SEND_DATA, new_data); + g_free(new_data); + } + break; + case SAP_PDU_CMD_TYPE_REQ_OK: + break; + default: + break; + } + break; + case SAP_PDU_GENERIC_CMD_GET_PLATFORM_VERSION: + DBG("SAP_PDU_GENERIC_CMD_GET_PLATFORM_VERSION"); + + cmdType = (int)evt_data->data[3]; + switch (cmdType) { + case SAP_PDU_CMD_TYPE_REQ: + ret = system_info_get_value_string(SYSTEM_INFO_KEY_TIZEN_VERSION, + &value); + if (ret == 0 && value != NULL) { + new_data = g_try_malloc0(sizeof(struct SAP_event_data)); + if (new_data == NULL) + return TRUE; + + new_data->count = evt_data->count; + new_data->index = evt_data->index; + new_data->data_len = SAP_DATA_LEN_MIN + strlen(value); + + buf[pos++] = (char)new_data->data_len; + buf[pos++] = (char)SAP_PDU_CMD_GENERIC; + buf[pos++] = (char)SAP_PDU_GENERIC_CMD_GET_PLATFORM_VERSION; + buf[pos++] = (char)SAP_PDU_CMD_TYPE_REQ_OK; + memcpy(&buf[pos], value, strlen(value)); + memcpy(new_data->data, buf, new_data->data_len); + + g_free(value); + + wc_object_emit_callback(wo, WC_OBJECT_EVENT_SAP_SEND_DATA, new_data); + g_free(new_data); + } + break; + case SAP_PDU_CMD_TYPE_REQ_OK: + break; + default: + ERR("Invalid generic type command(%d)", cmdType); + break; + } + break; + case SAP_PDU_GENERIC_CMD_GET_MANUFACTURER: + DBG("SAP_PDU_GENERIC_CMD_GET_MANUFACTURER"); + + cmdType = (int)evt_data->data[3]; + switch (cmdType) { + case SAP_PDU_CMD_TYPE_REQ: + ret = system_info_get_value_string(SYSTEM_INFO_KEY_MANUFACTURER, + &value); + if (ret == 0 && value != NULL) { + new_data = g_try_malloc0(sizeof(struct SAP_event_data)); + if (new_data == NULL) + return TRUE; + + new_data->count = evt_data->count; + new_data->index = evt_data->index; + new_data->data_len = SAP_DATA_LEN_MIN + strlen(value); + + buf[pos++] = (char)new_data->data_len; + buf[pos++] = (char)SAP_PDU_CMD_GENERIC; + buf[pos++] = (char)SAP_PDU_GENERIC_CMD_GET_MANUFACTURER; + buf[pos++] = (char)SAP_PDU_CMD_TYPE_REQ_OK; + memcpy(&buf[pos], value, strlen(value)); + memcpy(new_data->data, buf, new_data->data_len); + + g_free(value); + + wc_object_emit_callback(wo, WC_OBJECT_EVENT_SAP_SEND_DATA, new_data); + g_free(new_data); + } + break; + case SAP_PDU_CMD_TYPE_REQ_OK: + len = evt_data->data_len - (unsigned int)SAP_DATA_LEN_MIN; + + value = g_strndup(&evt_data->data[SAP_DATA_LEN_MIN], len); + vconf_set_str(VCONFKEY_WECONN_CONNECTED_MANUFACTURER, value); + + g_free(value); + break; + case SAP_PDU_CMD_TYPE_RESP_ERR: + break; + case SAP_PDU_CMD_TYPE_NOTI: + break; + default: + ERR("Invalid generic type command(%d)", cmdType); + break; + } + break; + default: + ERR("Invalid generic sub command(%d)", subCmd); + break; + } + + return TRUE; +} + +static void _esap_bearer_register_events(WcObject *wo) +{ + enum SAP_PDU_commands_type cmd_type = SAP_PDU_CMD_TYPE_REQ; + enum SAP_PDU_GENERIC_commands generic_cmds = SAP_PDU_GENERIC_CMD_GET_PLATFORM_NAME; + char *cmd = NULL; + char *event = NULL; + + cmd = g_strdup_printf("%s[%d]", + WC_OBJECT_EVENT_SAP_RECEIVE_DATA, SAP_PDU_CMD_GENERIC); + + while (cmd_type < SAP_PDU_CMD_TYPE_MAX ) { + while (generic_cmds < SAP_PDU_GENERIC_CMD_MAX) { + event = g_strdup_printf("%s[%d][%d]", cmd, generic_cmds, cmd_type); + wc_object_add_callback(wo, event, _on_generic_sap_cmd_cb, NULL); + g_free(event); + + generic_cmds++; + } + + generic_cmds = SAP_PDU_GENERIC_CMD_GET_PLATFORM_NAME; + cmd_type++; + } + + wc_object_add_callback(wo, WC_OBJECT_EVENT_SAP_SEND_DATA, + __on_esap_event_send_data_cb, NULL); + + g_free(cmd); +} + +static void _esap_bearer_deregister_events(WcObject *wo) +{ + enum SAP_PDU_commands_type cmd_type = SAP_PDU_CMD_TYPE_REQ; + enum SAP_PDU_GENERIC_commands generic_cmds = SAP_PDU_GENERIC_CMD_GET_PLATFORM_NAME; + char *cmd = NULL; + char *event = NULL; + + cmd = g_strdup_printf("%s[%d]", + WC_OBJECT_EVENT_SAP_RECEIVE_DATA, SAP_PDU_CMD_GENERIC); + + while (cmd_type < SAP_PDU_CMD_TYPE_MAX ) { + while (generic_cmds < SAP_PDU_GENERIC_CMD_MAX) { + event = g_strdup_printf("%s[%d][%d]", cmd, generic_cmds, cmd_type); + wc_object_del_callback(wo, event, _on_generic_sap_cmd_cb); + g_free(event); + + generic_cmds++; + } + + generic_cmds = SAP_PDU_GENERIC_CMD_GET_PLATFORM_NAME; + cmd_type++; + } + + wc_object_del_callback(wo, WC_OBJECT_EVENT_SAP_SEND_DATA, + __on_esap_event_send_data_cb); + + g_free(cmd); +} + +static void __esap_connection_changed_cb(keynode_t *key, void *data) +{ + int status = 0; + WcObject *wo = (WcObject *)data; + + vconf_get_int(SAP_CONNECTION_VCONF_NOTIFICATION, &status); + DBG("status : %d", status); + + if (status == 1) { + _esap_service_init(wo); + + _esap_bearer_register_events(wo); + } else { + DBG("%s is disconnected", SAP_CONNECTION_VCONF_NOTIFICATION); + + _esap_bearer_deregister_events(wo); + + wc_object_set_property(wo, WC_OBJECT_EVENT_SAP_BCMSERVICE_STATUS, + XSTR(W_SERVICE_STATE_DISCONNECTED)); + } +} + +static int esap_bearer_init(WcObject *wo) +{ + int type; + unsigned char role; + + type = wc_control_get_device_type(); + if (type == PROP_CONTROL_TYPE_WEARABLE) { + role = SAP_ROLE_PROVIDER; + } else if (type == PROP_CONTROL_TYPE_HOST) { + role = SAP_ROLE_CONSUMER; + } else { + ERR("Unknown type"); + return -EINVAL; + } + + gpd = g_try_new0(struct esap_private_data, 1); + if (!gpd) + return -ENOMEM; + + /* FIXME: why WcObject -> wo.element -> WcObject (circular dependency) */ + gpd->wo_esap = wo; + gpd->role = role; + + /* FIXEME: test code */ + do { + int status = 0; + vconf_get_int(SAP_CONNECTION_VCONF_NOTIFICATION, &status); + + if (status == 1) + _esap_service_init(wo); + else + vconf_notify_key_changed(SAP_CONNECTION_VCONF_NOTIFICATION, + __esap_connection_changed_cb, wo); + } while(0); + + return 0; +} + +static int esap_bearer_activate(WcObject *wo) +{ + gboolean ret; + + ret = _esap_service_init(wo); + DBG("Activated: %d", ret); + return 0; +} + +static int esap_bearer_deactivate(WcObject *wo) +{ + DBG("Deactivated"); + + return 0; +} + +static int esap_bearer_connect(WcObject *wo, const char* address) +{ + DBG("Connected"); + // TBD: request connect() through SAP interface + return 0; +} + +static int esap_bearer_disconnect(WcObject *wo) +{ + DBG("Disconnected"); + // TBD: request disconnect() through SAP interface + return 0; +} + +static void esap_bearer_exit(WcObject *wo) +{ + vconf_ignore_key_changed(SAP_CONNECTION_VCONF_NOTIFICATION, + __esap_connection_changed_cb); + + if (gpd) { + g_free(gpd); + gpd = NULL; + } +} + +EXPORT_API struct bearer_driver_desc bearer_driver_desc = { + .name = "esap", + .technology = WC_TECHNOLOGY_BLUETOOTH, + + .init = esap_bearer_init, + .activate = esap_bearer_activate, + .deactivate = esap_bearer_deactivate, + .connect = esap_bearer_connect, + .disconnect = esap_bearer_disconnect, + .exit = esap_bearer_exit, +}; diff --git a/plugins/esap.h b/plugins/esap.h new file mode 100644 index 0000000..efa49ae --- /dev/null +++ b/plugins/esap.h @@ -0,0 +1,160 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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 __WECONN_ESAP_H__ +#define __WECONN_ESAP_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "log.h" +#include "events.h" +#include "object.h" + +#define SAP_ROLE_PROVIDER 0x00 +#define SAP_ROLE_CONSUMER 0x01 + +#define SAP_DATA_LEN_MIN 4 +#define SAP_DATA_LEN_MAX 255 +#define SAP_DATA_MAC_ADDR_LEN 17 + +#define VCONFKEY_WECONN_CONNECTED_BT_VERSION \ + "file/private/weconn/connected_bt_version" +#define VCONFKEY_WECONN_DISCONNECTED_MANUALLY \ + "file/private/weconn/disconnected_manually" +#define VCONFKEY_WECONN_CONNECTED_MANUFACTURER \ + "file/private/weconn/connected_manufacturer" +#define VCONFKEY_WECONN_AUTOCONNECTABLE_BT_ADDRESS \ + "file/private/weconn/autoconnectable_unique_bt_target_address" + +#define VCONFKEY_WECONN_ALL_CONNECTED "memory/private/weconn/all_connected" +#define VCONFKEY_RFCOMM_READY_STATUS "memory/private/sap/rfcomm_ready" +#define VCONFKEY_SAP_CONNECTION_STATUS "memory/private/sap/conn_status" +#define SAP_DISCONNECTED 0 +#define SAP_CONNECTED 1 + +enum SAP_PDU_commands_type { + SAP_PDU_CMD_TYPE_REQ = 0x01, + SAP_PDU_CMD_TYPE_REQ_OK = 0x02, + SAP_PDU_CMD_TYPE_RESP_ERR = 0x03, + SAP_PDU_CMD_TYPE_NOTI = 0x04, + SAP_PDU_CMD_TYPE_MAX = 0x05, +}; + +enum SAP_PDU_commands { + SAP_PDU_CMD_GENERIC = 0x01, + SAP_PDU_CMD_BLUETOOTH = 0x02, + SAP_PDU_CMD_WIFI = 0x03, + SAP_PDU_CMD_CELLULAR = 0x04, + SAP_PDU_CMD_ETHERNET = 0x05, +}; + +enum SAP_PDU_BT_commands { + SAP_PDU_BT_CMD_UNKNOWN = 0x00, + SAP_PDU_BT_CMD_GET_VERSION = 0x01, + SAP_PDU_BT_CMD_GET_ADDRESS = 0x02, + SAP_PDU_BT_CMD_GET_PROFILES = 0x03, + SAP_PDU_BT_CMD_GET_COD = 0x04, + SAP_PDU_BT_CMD_GET_SERVICES = 0x05, + SAP_PDU_BT_CMD_FX_MAX = 0x06, + + SAP_PDU_BT_CMD_SET_PAN_CONFIGURATION_ON = 0x11, + SAP_PDU_BT_CMD_SET_PAN_CONFIGURATION_OFF = 0x12, + SAP_PDU_BT_CMD_SET_PAN_CONNECTION = 0x13, + SAP_PDU_BT_CMD_DISCONNECTED_MANUALLY = 0x14, + SAP_PDU_BT_CMD_MAX = 0x15, +}; + +enum SAP_PDU_BT_version { + SAP_PDU_BT_VER_UNKOWN = 0x00, // unknown + SAP_PDU_BT_VER_30 = 0x01, // 3.0 + SAP_PDU_BT_VER_40_LE = 0x02, // 4.0 LE ONLY + SAP_PDU_BT_VER_40_DUAL = 0x03, // 4.0 DUAL + SAP_PDU_BT_VER_40_P = 0x04, // 4.0' + SAP_PDU_BT_VER_41 = 0x05, // 4.1 +}; + +enum SAP_PDU_BT_profile { + SAP_PDU_BT_PROFILE_UNKOWN = 0x00, + SAP_PDU_BT_PROFILE_HFP = 0x01, + SAP_PDU_BT_PROFILE_SPP = 0x02, + SAP_PDU_BT_PROFILE_GATT = 0x03, + SAP_PDU_BT_PROFILE_PAN = 0x04, + SAP_PDU_BT_PROFILE_MAX = 0x05, +}; + +enum SAP_PDU_WIFI_commands { + SAP_PDU_WIFI_CMD_UNKNOWN = 0x00, + SAP_PDU_WIFI_CMD_GET_VERSION = 0x01, + SAP_PDU_WIFI_CMD_GET_MAC_ADDR = 0x02, + SAP_PDU_WIFI_CMD_GET_IP_ADDR = 0x03, + SAP_PDU_WIFI_CMD_MAX = 0x04, +}; + +enum SAP_PDU_CELLULAR_commands { + SAP_PDU_CELLULAR_CMD_UNKNOWN = 0x00, + SAP_PDU_CELLULAR_CMD_GET_IP_ADDR = 0x01, + SAP_PDU_CELLULAR_CMD_MAX = 0x02, +}; + +enum SAP_PDU_ETHERNET_commands { + SAP_PDU_ETHERNET_CMD_UNKNOWN = 0x00, + SAP_PDU_ETHERNET_CMD_GET_MAC_ADDR = 0x01, + SAP_PDU_ETHERNET_CMD_GET_IP_ADDR = 0x02, + SAP_PDU_ETHERNET_CMD_MAX = 0x03, +}; + +enum SAP_PDU_GENERIC_commands { + SAP_PDU_GENERIC_CMD_UNKNOWN = 0x00, + SAP_PDU_GENERIC_CMD_GET_PLATFORM_NAME = 0x01, + SAP_PDU_GENERIC_CMD_GET_PLATFORM_VERSION = 0x02, + SAP_PDU_GENERIC_CMD_GET_MANUFACTURER = 0x03, + SAP_PDU_GENERIC_CMD_MAX = 0x04, +}; + +struct SAP_PDU_data { + char length; + char main_cmd; + char sub_cmd; + char type_cmd; + char data[SAP_DATA_LEN_MAX]; +}; + +struct SAP_event_data { + unsigned int index; + unsigned int count; + unsigned int data_len; + char data[SAP_DATA_LEN_MAX]; +}; + +struct SAP_send_data { + unsigned int index; + unsigned int count; + unsigned int data_len; + char data[SAP_DATA_LEN_MAX]; +}; + +typedef struct _SAP_PDU_CMD_Callbacks SAP_PDU_CMD_Callbacks; + +#ifdef __cplusplus +} +#endif + +#endif /* __WECONN_PLUGIN_H__ */ diff --git a/plugins/pan.c b/plugins/pan.c new file mode 100644 index 0000000..d31323b --- /dev/null +++ b/plugins/pan.c @@ -0,0 +1,923 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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. + * + */ + +#include "util.h" +#include "plugin.h" +#include "esap.h" +#include "technology.h" + +static __thread guint weconn_conn_subscribe_id_connman_state = 0; + +static int pan_bearer_connect(WcObject *wo, const char *address); +static int pan_bearer_disconnect(WcObject *wo); + +static const char **__get_pan_profile(GDBusConnection *connection) +{ + GVariant *reply; + unsigned int n = 0; + GError *error = NULL; + const char *obj_path = NULL; + const char **obj_array = NULL; + GVariantIter *iter = NULL, *next = NULL; + GSList *string_list = NULL, *slist; + + reply = g_dbus_connection_call_sync(connection, + "net.connman", + "/", + "net.connman.Manager", + "GetServices", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NONE, + DBUS_REPLY_TIMEOUT, + NULL, + &error); + + if (error) { + ERR("Failed to request (%s)", error->message); + g_error_free(error); + + if (reply) + g_variant_unref(reply); + + return NULL; + } + + if (!reply) { + ERR("Failed to request"); + return NULL; + } + + g_variant_get(reply, "(a(oa{sv}))", &iter); + while (g_variant_iter_loop(iter, "(oa{sv})", &obj_path, &next)) { + if (g_str_has_prefix(obj_path, "/net/connman/service/bluetooth_") + != TRUE) + continue; + + n++; + string_list = g_slist_prepend(string_list, g_strdup(obj_path)); + } + + if (iter) + g_variant_iter_free(iter); + + if (next) + g_variant_iter_free(next); + + g_variant_unref(reply); + + obj_array = g_try_new(const char *, n + 1); + if (!obj_array) { + ERR("No memory"); + return NULL; + } + + obj_array[n--] = NULL; + for (slist = string_list; slist; slist = slist->next) + obj_array[n--] = slist->data; + + g_slist_free(string_list); + + return obj_array; +} + +#ifdef USE_STATIC_IP +static int __request_pan_set_dns(GDBusConnection *connection, + const char *obj_path) +{ + GVariantBuilder *builder; + GVariant *params = NULL; + GVariant *reply = NULL; + GError *error = NULL; + int err = -EIO; + + const char *prop_nameserver_config = "Nameservers.Configuration"; + const char *dns = "192.168.44.1"; + + builder = g_variant_builder_new(G_VARIANT_TYPE ("as")); + g_variant_builder_add(builder, "s", dns); + params = g_variant_new("(sv)", + prop_nameserver_config, g_variant_builder_end(builder)); + g_variant_builder_unref(builder); + + reply = g_dbus_connection_call_sync(connection, + "net.connman", + obj_path, + "net.connman.Service", + "SetProperty", + params, + NULL, + G_DBUS_CALL_FLAGS_NONE, + DBUS_REPLY_TIMEOUT, + NULL, + &error); + + if (error) { + ERR("Failed to request (%s)", error->message); + if (g_strrstr(error->message, "net.connman.Error.AlreadyConnected")) + err = -EISCONN; + else if (g_strrstr(error->message, "net.connman.Error.NotConnected")) + err = -ENOTCONN; + + g_error_free(error); + + if (reply) + g_variant_unref(reply); + + return err; + } + + if (!reply) { + ERR("Failed to request"); + return -EIO; + } + + g_variant_unref(reply); + + return 0; +} + +static int __request_pan_set_static_ip(GDBusConnection *connection, + const char *obj_path) +{ + GVariantBuilder *builder; + GVariant *params = NULL; + GVariant *reply = NULL; + GError *error = NULL; + int err = -EIO; + + const char *prop_ipv4_config = "IPv4.Configuration"; + const char *prop_method = "Method"; + const char *prop_address = "Address"; + const char *prop_gateway = "Gateway"; + const char *prop_netmask = "Netmask"; + const char *manual_method = "manual"; + const char *ipaddress = "192.168.44.179"; + const char *netmask = "255.255.255.0"; + const char *gateway = "192.168.44.1"; + + builder = g_variant_builder_new(G_VARIANT_TYPE ("a{sv}")); + + g_variant_builder_add(builder, "{sv}", prop_method, g_variant_new_string(manual_method)); + g_variant_builder_add(builder, "{sv}", prop_address, g_variant_new_string(ipaddress)); + g_variant_builder_add(builder, "{sv}", prop_netmask, g_variant_new_string(netmask)); + g_variant_builder_add(builder, "{sv}", prop_gateway, g_variant_new_string(gateway)); + + params = g_variant_new("(sv)", + prop_ipv4_config, g_variant_builder_end(builder)); + g_variant_builder_unref(builder); + + reply = g_dbus_connection_call_sync(connection, + "net.connman", + obj_path, + "net.connman.Service", + "SetProperty", + params, + NULL, + G_DBUS_CALL_FLAGS_NONE, + DBUS_REPLY_TIMEOUT, + NULL, + &error); + + if (error) { + ERR("Failed to request (%s)", error->message); + if (g_strrstr(error->message, "net.connman.Error.AlreadyConnected")) + err = -EISCONN; + else if (g_strrstr(error->message, "net.connman.Error.NotConnected")) + err = -ENOTCONN; + + g_error_free(error); + + if (reply) + g_variant_unref(reply); + + return err; + } + + if (!reply) { + ERR("Failed to request"); + return -EIO; + } + + g_variant_unref(reply); + + return 0; +} +#endif + +static int __request_pan_command(GDBusConnection *connection, + const char *obj_path, const char *method) +{ + int err = -EIO; + GVariant *reply; + GError *error = NULL; + +#ifdef USE_STATIC_IP + err = __request_pan_set_static_ip(connection, obj_path); + if (err < 0) + SECURE_ERR("Failed to set static IP(%d)", err); + + err = __request_pan_set_dns(connection, obj_path); + if (err < 0) + SECURE_ERR("Failed to set DNS(%d)", err); +#endif + + reply = g_dbus_connection_call_sync(connection, + "net.connman", + obj_path, + "net.connman.Service", + method, + NULL, + NULL, + G_DBUS_CALL_FLAGS_NONE, + DBUS_REPLY_TIMEOUT, + NULL, + &error); + + if (error) { + ERR("Failed to request (%s)", error->message); + if (g_strrstr(error->message, "net.connman.Error.AlreadyConnected")) + err = -EISCONN; + else if (g_strrstr(error->message, "net.connman.Error.NotConnected")) + err = -ENOTCONN; + + g_error_free(error); + + if (reply) + g_variant_unref(reply); + + return err; + } + + if (!reply) { + ERR("Failed to request"); + return -EIO; + } + + g_variant_unref(reply); + + return 0; +} + +static int _pan_bearer_request_pan_configuration(WcObject *wo, gboolean enable) +{ + struct SAP_event_data *new_data = NULL; + char buf[SAP_DATA_LEN_MAX] = { '\0', }; + int pos = 0; + + new_data = g_try_malloc0(sizeof(struct SAP_event_data)); + if (new_data == NULL) + return -ENOMEM; + + new_data->count = 1; + new_data->index = 1; + new_data->data_len = (char)SAP_DATA_LEN_MIN; + + buf[pos++] = (char)new_data->data_len; + buf[pos++] = (char)SAP_PDU_CMD_BLUETOOTH; + + if (enable == TRUE) + buf[pos++] = (char)SAP_PDU_BT_CMD_SET_PAN_CONFIGURATION_ON; + else + buf[pos++] = (char)SAP_PDU_BT_CMD_SET_PAN_CONFIGURATION_OFF; + + buf[pos++] = (char)SAP_PDU_CMD_TYPE_REQ; + + memcpy(new_data->data, buf, new_data->data_len); + + wc_object_emit_callback(wo, WC_OBJECT_EVENT_SAP_SEND_DATA, new_data); + g_free(new_data); + + return 0; +} + +static GVariant *_get_properties(GDBusConnection *connection, + const char *obj_path) +{ + GVariant *reply; + GError *error = NULL; + + if (!connection || !obj_path) + return NULL; + + reply = g_dbus_connection_call_sync(connection, + "net.connman", + obj_path, + "net.connman.Service", + "GetProperties", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NONE, + DBUS_REPLY_TIMEOUT, + NULL, + &error); + + if (error) { + ERR("Failed to request (%s)", error->message); + g_error_free(error); + + if (reply) + g_variant_unref(reply); + + return NULL; + } + + if (!reply) { + ERR("Failed to request"); + return NULL; + } + + return reply; +} + +static int _get_pan_state(WcObject *wo) +{ + int ret = W_SERVICE_STATE_DISCONNECTED; + unsigned int i, num = 0; + GError *error = NULL; + const char **obj_array = NULL; + GDBusConnection *connection = NULL; + GVariantIter *iter = NULL; + GVariant *reply; + GVariant *value = NULL; + gchar *key = NULL; + + if (!wo) + return -EINVAL; + + DBG(""); + + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + if (!connection) { + ERR("Failed to get system bus[%s]", error->message); + g_error_free(error); + return -EIO; + } + + obj_array = __get_pan_profile(connection); + num = g_strv_length((char **)obj_array); + if (!num) { + ERR("Failed to get profiles"); + return -ENXIO; + } + + for (i=0; imessage); + g_error_free(error); + return -EIO; + } + + obj_array = __get_pan_profile(connection); + num = g_strv_length((char **)obj_array); + if (!num) { + ERR("Failed to get profiles"); + return -ENXIO; + } + + for (i=0; imessage); + g_error_free(error); + return -EIO; + } + + obj_array = __get_pan_profile(connection); + num = g_strv_length((char **)obj_array); + if (!num) { + ERR("Failed to get profiles"); + return -ENXIO; + } + + for (i=0; idata[1]; + if (mainCmd != SAP_PDU_CMD_BLUETOOTH) { + ERR("Invalid bluetooth command(%02x)", mainCmd); + return TRUE; + } + + subCmd = (int)evt_data->data[2]; + switch (subCmd) { + case SAP_PDU_BT_CMD_SET_PAN_CONFIGURATION_ON: + DBG("SAP_PDU_BT_CMD_SET_PAN_CONFIGURATION_ON"); + break; + case SAP_PDU_BT_CMD_SET_PAN_CONFIGURATION_OFF: + DBG("SAP_PDU_BT_CMD_SET_PAN_CONFIGURATION_OFF"); + break; + case SAP_PDU_BT_CMD_SET_PAN_CONNECTION: + DBG("SAP_PDU_BT_CMD_SET_PAN_CONNECTION"); + + cmdType = (int)evt_data->data[3]; + switch (cmdType) { + case SAP_PDU_CMD_TYPE_REQ: + res = _pan_connect(wo); + if (res < 0) { + ERR("Failed pan connect"); + + __pan_set_property(wo, XSTR(W_SERVICE_TYPE_BT_PAN), + XSTR(W_SERVICE_STATE_DISCONNECTED)); + } + + break; + default: + break; + } + + break; + case SAP_PDU_BT_CMD_DISCONNECTED_MANUALLY: + DBG("SAP_PDU_BT_CMD_DISCONNECTED_MANUALLY"); + + cmdType = (int)evt_data->data[3]; + switch (cmdType) { + case SAP_PDU_CMD_TYPE_NOTI: + pan_state = wc_object_get_property(wo, XSTR(W_SERVICE_TYPE_BT_PAN)); + if (g_strcmp0(pan_state, XSTR(W_SERVICE_STATE_CONNECTED)) == 0) { + res = _pan_disconnect(wo); + if (res < 0) + ERR("Failed pan connect"); + } + + g_free(pan_state); + break; + default: + break; + } + break; + default: + break; + } + + return TRUE; +} + +static void _pan_bearer_register_events(WcObject *wo) +{ + enum SAP_PDU_commands_type cmd_type = SAP_PDU_CMD_TYPE_REQ; + enum SAP_PDU_BT_commands pan_cmds = SAP_PDU_BT_CMD_SET_PAN_CONFIGURATION_ON; + char *cmd = NULL; + char *event = NULL; + + cmd = g_strdup_printf("%s[%d]", + WC_OBJECT_EVENT_SAP_RECEIVE_DATA, SAP_PDU_CMD_BLUETOOTH); + + while (cmd_type < SAP_PDU_CMD_TYPE_MAX ) { + while (pan_cmds < SAP_PDU_BT_CMD_MAX) { + event = g_strdup_printf("%s[%d][%d]", cmd, pan_cmds, cmd_type); + wc_object_add_callback(wo, event, _on_pan_sap_cmd_cb, NULL); + g_free(event); + + pan_cmds++; + } + + pan_cmds = SAP_PDU_BT_CMD_SET_PAN_CONFIGURATION_ON; + cmd_type++; + } + + g_free(cmd); +} + +static void _pan_connect_cb(service_h request, + service_h reply, service_result_e result, void *user_data) +{ + int ret; + char *resp = NULL; + const char *connected = NULL; + WcObject *wo = (WcObject *)user_data; + + if (!wo) { + ERR("WcObject is NULL"); + return; + } + + if (result != SERVICE_RESULT_SUCCEEDED) { + ERR("service reply failed = [%d]", result); + return; + } + + ret = service_get_extra_data(reply, "_WCPOPUP_RESP_", &resp); + if (ret != SERVICE_ERROR_NONE) { + ERR("failed to retrieve response = [%d]", ret); + return; + } + + if (g_strcmp0(resp, "OK") == 0) { + connected = (const char *)g_hash_table_lookup( + wc_object_peek_property_hash(wo), XSTR(W_SERVICE_TYPE_BT_SPP)); + if (g_strcmp0(connected, XSTR(W_SERVICE_STATE_CONNECTED)) != 0) { + ERR("CM does not connected correctly"); + __pan_set_property(wo, XSTR(W_SERVICE_TYPE_BT_PAN), + XSTR(W_SERVICE_STATE_DISCONNECTED)); + return; + } + + ret = _pan_bearer_request_pan_configuration(wo, TRUE); + if (ret < 0) + ERR("Failed pan configuration on"); + else + DBG("Requested pan configuration on"); + } else if (g_strcmp0(resp, "CANCEL") == 0) { + __pan_set_property(wo, XSTR(W_SERVICE_TYPE_BT_PAN), + XSTR(W_SERVICE_STATE_DISCONNECTED)); + } +} + +static void _pan_disconnect_cb(service_h request, + service_h reply, service_result_e result, void *user_data) +{ + int ret; + char *resp = NULL; + const char *connected = NULL; + WcObject *wo = (WcObject *)user_data; + + if (!wo) { + ERR("WcObject is NULL"); + return; + } + + if (result != SERVICE_RESULT_SUCCEEDED) { + ERR("service reply failed = [%d]", result); + return; + } + + ret = service_get_extra_data(reply, "_WCPOPUP_RESP_", &resp); + if (ret != SERVICE_ERROR_NONE) { + ERR("failed to retrieve response = [%d]", ret); + return; + } + + if (g_strcmp0(resp, "OK") == 0) { + ret = _pan_disconnect(wo); + if (ret < 0) + ERR("Failed pan connect"); + } else if (g_strcmp0(resp, "CANCEL") == 0) { + connected = (const char *)g_hash_table_lookup( + wc_object_peek_property_hash(wo), XSTR(W_SERVICE_TYPE_BT_SPP)); + if (g_strcmp0(connected, XSTR(W_SERVICE_STATE_CONNECTED)) == 0) { + __pan_set_property(wo, XSTR(W_SERVICE_TYPE_BT_PAN), + XSTR(W_SERVICE_STATE_CONNECTED)); + } + } +} + +static void __notify_bt_service_state_changed(const char *state, void *user_data) +{ + static wc_technology_service_state_e pan_service_state_old = 0; + + WcObject *wo = (WcObject *)user_data; + if (!wo) + return; + + if (!state) { + ERR("state is NULL"); + return; + } + + DBG("bt_service_state_changed : %s", state); + + if (g_strcmp0(state, "idle") == 0) { + pan_service_state_old = WC_SERVICE_STATE_IDLE; + } else if (g_strcmp0(state, "disconnect") == 0) { + if (pan_service_state_old > WC_SERVICE_STATE_DISCONNECTED) { + __pan_set_property(wo, XSTR(W_SERVICE_TYPE_BT_PAN), + XSTR(W_SERVICE_STATE_DISCONNECTED)); + } + pan_service_state_old = WC_SERVICE_STATE_DISCONNECTED; + } else if (g_strcmp0(state, "failure") == 0) { + if (pan_service_state_old > WC_SERVICE_STATE_DISCONNECTED) { + __pan_set_property(wo, XSTR(W_SERVICE_TYPE_BT_PAN), + XSTR(W_SERVICE_STATE_DISCONNECTED)); + } + pan_service_state_old = WC_SERVICE_STATE_FAILURE; + } else if (g_strcmp0(state, "association") == 0) { + pan_service_state_old = WC_SERVICE_STATE_ASSOCIATION; + } else if (g_strcmp0(state, "configuration") == 0) { + pan_service_state_old = WC_SERVICE_STATE_CONFIGURATION; + } else if (g_strcmp0(state, "ready") == 0) { + if (pan_service_state_old < WC_SERVICE_STATE_READY) { + __pan_set_property(wo, XSTR(W_SERVICE_TYPE_BT_PAN), + XSTR(W_SERVICE_STATE_CONNECTED)); + } + pan_service_state_old = WC_SERVICE_STATE_READY; + } else if (g_strcmp0(state, "online") == 0) { + if (pan_service_state_old < WC_SERVICE_STATE_READY) { + __pan_set_property(wo, XSTR(W_SERVICE_TYPE_BT_PAN), + XSTR(W_SERVICE_STATE_CONNECTED)); + } + pan_service_state_old = WC_SERVICE_STATE_ONLINE; + } +} + +static void __handle_service_state_changed(const gchar *path, + const char *key, const char *state, void *user_data) +{ + weconn_device_type_e device_type; + + if (path == NULL || key == NULL || state == NULL) + return; + + device_type = wc_device_get_type_from_path(path); + switch (device_type) { + case W_DEVICE_TYPE_BT: + __notify_bt_service_state_changed(state, user_data); + break; + case W_DEVICE_TYPE_CELLULAR: + break; + case W_DEVICE_TYPE_WIFI: + break; + case W_DEVICE_TYPE_WIFI_P2P: + break; + case W_DEVICE_TYPE_WIFI_ADHOC: + break; + case W_DEVICE_TYPE_ETHERNET: + break; + default: + break; + } +} + +static void __pan_connman_service_signal_filter(GDBusConnection *conn, + const gchar *name, const gchar *path, const gchar *interface, + const gchar *sig, GVariant *param, gpointer user_data) +{ + const char *key = NULL; + const char *value = NULL; + GVariant *var; + + if (g_strcmp0(sig, CONNMAN_SIGNAL_PROPERTY_CHANGED) == 0) { + g_variant_get(param, "(sv)", &key, &var); + + if (g_strcmp0(key, "State") == 0) { + g_variant_get(var, "s", &value); + __handle_service_state_changed(path, key, value, user_data); + } else if (g_strcmp0(key, "Error") == 0) { + g_variant_get(var, "s", &value); + ERR("Error (%s)", value); + } + + g_free((gchar *)value); + g_free((gchar *)key); + if (var != NULL) + g_variant_unref(var); + } else { + ERR("No handle signal(%s)", sig); + } +} + +static int pan_bearer_init(WcObject *wo) +{ + GError *error = NULL; + GDBusConnection *connection = NULL; + guint id; + + _pan_bearer_register_events(wo); + + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + if (!connection) { + ERR("Failed to get system bus %s", error->message); + g_error_free(error); + return -1; + } + + id = g_dbus_connection_signal_subscribe( + connection, + CONNMAN_SERVICE, + CONNMAN_SERVICE_INTERFACE, + CONNMAN_SIGNAL_PROPERTY_CHANGED, + NULL, + "State", + G_DBUS_SIGNAL_FLAGS_NONE, + __pan_connman_service_signal_filter, + wo, + NULL); + + if (id == 0) { + ERR("Failed register signals (%d)", id); + return -1; + } + + weconn_conn_subscribe_id_connman_state = id; + + return 0; +} + +static int pan_bearer_connect(WcObject *wo, const char *address) +{ + int err = -EIO; + char *connected = NULL; + weconn_service_state_e state; + + if (!wo) + return -EINVAL; + + connected = wc_object_get_property(wo, WC_OBJECT_EVENT_SAP_BCMSERVICE_STATUS); + if (connected != NULL) { + if (g_strcmp0(connected, XSTR(W_SERVICE_STATE_CONNECTED)) != 0) { + ERR("CM does not connected correctly"); + g_free(connected); + return -EIO; + } + g_free(connected); + } + + + state = _get_pan_state(wo); + if (state == W_SERVICE_STATE_CONNECTING) { + DBG("InProgress..."); + return -EINPROGRESS; + } else if (state == W_SERVICE_STATE_CONNECTED) { + DBG("already connected"); + return -EISCONN; + } + + err = wc_launch_popup(WC_POPUP_TYPE_PAN_CONNECT, _pan_connect_cb, wo); + if (err < 0) + ERR("failed launch pan popup"); + + return err; +} + +static int pan_bearer_disconnect(WcObject *wo) +{ + int err = -EIO; + weconn_service_state_e state; + + state = _get_pan_state(wo); + if (state == W_SERVICE_STATE_CONNECTING) { + DBG("InProgress..."); + return -EINPROGRESS; + } else if (state == W_SERVICE_STATE_DISCONNECTED) { + DBG("Not connected"); + return -ENOTCONN; + } + + err = wc_launch_popup(WC_POPUP_TYPE_PAN_DISCONNECT, _pan_disconnect_cb, wo); + if (err < 0) + ERR("failed launch pan popup"); + + return err; +} + +static void pan_bearer_exit(WcObject *wo) +{ + GError *error = NULL; + GDBusConnection *connection = NULL; + + DBG("Unloaded"); + + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + if (!connection) { + ERR("Failed to get system bus %s", error->message); + g_error_free(error); + return; + } + + g_dbus_connection_signal_unsubscribe(connection, + weconn_conn_subscribe_id_connman_state); +} + +EXPORT_API struct bearer_driver_desc bearer_driver_desc = { + .name = "pan", + .technology = WC_TECHNOLOGY_BLUETOOTH, + + .init = pan_bearer_init, + .connect = pan_bearer_connect, + .disconnect = pan_bearer_disconnect, + .exit = pan_bearer_exit, +}; diff --git a/plugins/plugin.h b/plugins/plugin.h new file mode 100644 index 0000000..35cb80a --- /dev/null +++ b/plugins/plugin.h @@ -0,0 +1,52 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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 __WECONN_PLUGIN_H__ +#define __WECONN_PLUGIN_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "log.h" +#include "dbus.h" +#include "util.h" +#include "events.h" +#include "object.h" +#include "technology.h" + +struct bearer_driver_desc { + const char *name; + const char *technology; + + int (*init)(WcObject *wcdrv); + int (*activate)(WcObject *wcdrv); + int (*deactivate)(WcObject *wcdrv); + int (*connect)(WcObject *wcdrv, const char *address); + int (*disconnect)(WcObject *wcdrv); + void (*exit)(WcObject *wcdrv); +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __WECONN_PLUGIN_H__ */ diff --git a/resources/etc/dbus-1/system.d/weconn.conf b/resources/etc/dbus-1/system.d/weconn.conf new file mode 100644 index 0000000..b7a275f --- /dev/null +++ b/resources/etc/dbus-1/system.d/weconn.conf @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/resources/etc/opt/upgrade/500.net.weconn.patch.sh b/resources/etc/opt/upgrade/500.net.weconn.patch.sh new file mode 100755 index 0000000..6e1fc6d --- /dev/null +++ b/resources/etc/opt/upgrade/500.net.weconn.patch.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +#vconf +if [ ! -e /opt/var/kdb/memory/private/weconn/all_connected ]; then +/usr/bin/vconftool set -tf int memory/private/weconn/all_connected 0 -s system::vconf_network -i +fi diff --git a/resources/mdbus2 b/resources/mdbus2 new file mode 100755 index 0000000..e36f4c7 Binary files /dev/null and b/resources/mdbus2 differ diff --git a/resources/usr/lib/systemd/system/weconn.service b/resources/usr/lib/systemd/system/weconn.service new file mode 100644 index 0000000..2ff8446 --- /dev/null +++ b/resources/usr/lib/systemd/system/weconn.service @@ -0,0 +1,11 @@ +[Unit] +Description=Wearable device connection controller + +[Service] +Type=forking +BusName=net.weconn +ExecStart=/usr/sbin/weconnd +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/resources/usr/share/appcessory/wearable-connection.xml b/resources/usr/share/appcessory/wearable-connection.xml new file mode 100644 index 0000000..4f3dffd --- /dev/null +++ b/resources/usr/share/appcessory/wearable-connection.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + +]> + + + + + + + + + + + + diff --git a/resources/usr/share/dbus-1/services/net.weconn.service b/resources/usr/share/dbus-1/services/net.weconn.service new file mode 100644 index 0000000..3b00181 --- /dev/null +++ b/resources/usr/share/dbus-1/services/net.weconn.service @@ -0,0 +1,5 @@ +[D-BUS Service] +Name=net.weconn +Exec=/bin/false +User=root +SystemdService=weconn.service diff --git a/src/control/control.c b/src/control/control.c new file mode 100644 index 0000000..2b2164a --- /dev/null +++ b/src/control/control.c @@ -0,0 +1,151 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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. + * + */ + +#include + +#include "control.h" +#include "driver.h" + +struct wc_control { + int device_type; + const GSList *driver_list; +}; + +static WcObject *wo_control; + +static gboolean on_spp_connected(WcObject *wo_control, const void *event_info, + void *user_data) +{ + int ret; + WcObject *wo_bearer = (WcObject*)user_data; + + /* FIXME: when spp is connected, what do you want to activate here? */ + ret = wc_bearer_driver_activate(wo_bearer, "correct name"); + DBG("activate request: %d", ret); + + return TRUE; +} + +static gboolean on_bearer_created(WcObject *wo_bearer, const void *event_info, + void *user_data) +{ + const char *name = NULL; + + if (!wo_bearer) { + DBG("no bearer object!!"); + return FALSE; + } + + name = wc_object_get_name(wo_bearer); + + if (!name) + return FALSE; + + /* FIXME: it doesn't work correctly */ + if (g_strcmp0(name, "eSAP") == 0) { + int ret; + ret = wc_object_add_callback(wo_control, + WC_BEARER_EVENT_BT_CONNECTED_SPP, on_spp_connected, wo_bearer); + DBG("add callback for SPP connection: %d", ret); + } else { + DBG("%s bearer added", name); + } + + return TRUE; +} + +int wc_control_add_bearer(WcObject *wo_bearer) +{ + /* FIXME: controller is a one of bearer?? what does that mean? */ + wc_object_add_callback(wo_bearer, WC_OBJECT_EVENT_BEARER_ENABLED, + on_bearer_created, NULL); + + return 0; +} + +WcObject *wc_control_get_object(void) +{ + if (!wo_control) { + ERR("control object is NULL !!"); + return NULL; + } + + return wo_control; +} + +EXPORT_API int wc_control_get_device_type(void) +{ + struct wc_control *cd = NULL; + + if (!wo_control) + return -EINVAL; + + cd = wc_object_get_element(wo_control, "control_driver"); + if (cd) + return cd->device_type; + + return -EINVAL; +} + +int control_init(void) +{ + struct wc_control *cd = NULL; + + wo_control = wc_object_new("control", WC_OBJECT_TYPE_CONTROL); + if (!wo_control) + return -ENOMEM; + + cd = g_try_new0 (struct wc_control, 1); + if (!cd) + return -ENOMEM; + + /* FIXME */ + /* TBD: determine current mode Host or Wearable */ + cd->device_type = PROP_CONTROL_TYPE_WEARABLE; + wc_object_append_element(wo_control, "control_driver", cd); + + /* + * TODO: According to the various wearable device scenarios, + * each control should be implemented. + * + * All of controls are based on wearable device scenarios. + */ + DBG("control init success."); + + return 0; +} + +static void __clean_control_elements(WcObject *wo, + const char *element_name, void *element, void *user_data) +{ + g_free((gpointer)element_name); + g_free(element); +} + +void control_cleanup(void) +{ + if (!wo_control) + return; + + WC_OBJECT_CHECK(wo_control, WC_OBJECT_TYPE_CONTROL); + + wc_object_foreach_get_elements(wo_control, __clean_control_elements, NULL); + + wc_object_free(wo_control); +} diff --git a/src/dbus.c b/src/dbus.c new file mode 100644 index 0000000..ebaf356 --- /dev/null +++ b/src/dbus.c @@ -0,0 +1,122 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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. + * + */ + +#include +#include +#include +#include + +#include "log.h" +#include "dbus.h" + +static GDBusConnection *__connection = NULL; +static GDBusObjectManagerServer *__object_manager_server = NULL; + + +static void __request_name_cb(GDBusConnection *conn, const gchar *name, + gpointer user_data) +{ + void (*__init_cb)(void) = user_data; + + /* Successfully register DBus name */ + __connection = conn; + + g_dbus_object_manager_server_set_connection(__object_manager_server, conn); + + if (__init_cb) + __init_cb(); +} + +static void __lost_name_cb(GDBusConnection *conn, const gchar *name, + gpointer user_data) +{ + /* May service name is already in use */ + ERR("Service name is already in use"); + + /* The result of DBus name request is only permitted, + * such as DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER. + */ + exit(1); +} + +GDBusConnection *dbus_get_connection(void) +{ + return __connection; +} + +const char *dbus_name2path(const char *name) +{ + size_t len, i, j; + char *buf; + char hex[3]; + + if (!name) + return NULL; + + len = strlen(name); + if (len == 0) + return NULL; + + buf = g_malloc0(len * 4); + if (!buf) + return NULL; + + for (i = 0, j = 0; i < len; i++) { + if (g_ascii_isalnum(name[i]) || name[i] == '_' || name[i] == '/') { + buf[j] = name[i]; + j++; + } else { + snprintf(hex, 3, "%02X", name[i]); + buf[j++] = '_'; + buf[j++] = hex[0]; + buf[j++] = hex[1]; + buf[j++] = '_'; + } + } + + return buf; +} + +int dbus_init(GBusType bus_type, const char *bus_name, const char *obj_path, + void (*__init_cb)(void)) +{ + guint id; + + __object_manager_server = g_dbus_object_manager_server_new(obj_path); + if (!__object_manager_server) + return -ENOMEM; + + id = g_bus_own_name(bus_type, bus_name, G_BUS_NAME_OWNER_FLAGS_NONE, NULL, + __request_name_cb, __lost_name_cb, __init_cb, NULL); + if (id == 0) { + ERR("Failed to get system bus"); + return -EIO; + } + + return 0; +} + +void dbus_cleanup(void) +{ + g_object_unref(__object_manager_server); + __object_manager_server = NULL; + + g_object_unref(__connection); + __connection = NULL; +} diff --git a/src/driver.c b/src/driver.c new file mode 100644 index 0000000..a734d6d --- /dev/null +++ b/src/driver.c @@ -0,0 +1,290 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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. + * + */ + +#include +#include +#include + +#include "dbus.h" +#include "driver.h" +#include "technology.h" +#include "plugins/plugin.h" + +#include "generated-code.h" + +struct bearer_driver { + const struct bearer_driver_desc *desc; + + const char *path; + void *user_data; + void *handle; +}; + +static GSList *driver_list; + +/* TIP: driver is an element of WcObject + * For example, there might be a bluetooth WcObject, + * which has bluetooth, esap and pan elements. + */ + +static WcObject *wc_bearer_driver_new(const char *filename, + const struct bearer_driver_desc *desc, void *dl_handle) +{ + int err; + WcObject *wo; + struct bearer_driver *bd; + + wo = wc_object_find(desc->technology); + if (!wo) { + wo = wc_object_new(desc->technology, WC_OBJECT_TYPE_BEARER_DRIVER); + if (!wo) + return NULL; + } + + bd = g_try_new0(struct bearer_driver, 1); + if (!bd) + return NULL; + + bd->desc = desc; + bd->handle = dl_handle; + bd->path = g_strdup(filename); + + err = wc_object_append_element(wo, desc->name, bd); + if (err < 0) + g_free(bd); + + return wo; +} + +static void wc_bearer_driver_free(struct bearer_driver *bd) +{ + if (!bd) + return; + + g_free((gpointer)bd->path); + dlclose(bd->handle); + g_free(bd); +} + +static int __add_bearer(const char *filename, gpointer handle) +{ + GSList *list; + WcObject *wo; + struct bearer_driver_desc *desc; + + desc = dlsym(handle, "bearer_driver_desc"); + if (!desc) + return -EIO; + + wo = wc_bearer_driver_new(filename, desc, handle); + if (!wo) + return -ENOMEM; + + wc_technology_add_bearer(wo); + + wc_bearer_driver_init(wo, desc->name); + + /* Check already registered */ + for (list = driver_list; list; list = list->next) { + if (wo == list->data) { + DBG("driver %s, %p, %p, %s(%s) registered", + filename, desc, handle, desc->technology, desc->name); + + return -EALREADY; + } + } + + driver_list = g_slist_append(driver_list, wo); + + DBG("driver %s, %p, %p, %s(%s) created", + filename, desc, handle, desc->technology, desc->name); + + return 0; +} + +int wc_bearer_driver_init(WcObject *wo, const char *desc_name) +{ + struct bearer_driver *bd; + + WC_OBJECT_CHECK_RETURN(wo, WC_OBJECT_TYPE_BEARER_DRIVER, -EINVAL); + + bd = wc_object_get_element(wo, desc_name); + if (!bd) + return -EINVAL; + + if (bd->desc->init) + return bd->desc->init(wo); + + return -ENXIO; +} + +int wc_bearer_driver_activate(WcObject *wo, const char *desc_name) +{ + struct bearer_driver *bd; + + WC_OBJECT_CHECK_RETURN(wo, WC_OBJECT_TYPE_BEARER_DRIVER, -EINVAL); + + bd = wc_object_get_element(wo, desc_name); + if (!bd) + return -EINVAL; + + if (bd->desc->activate) + return bd->desc->activate(wo); + + return -ENXIO; +} + +int wc_bearer_driver_deactivate(WcObject *wo, const char *desc_name) +{ + struct bearer_driver *bd; + + WC_OBJECT_CHECK_RETURN(wo, WC_OBJECT_TYPE_BEARER_DRIVER, -EINVAL); + + bd = wc_object_get_element(wo, desc_name); + if (!bd) + return -EINVAL; + + if (bd->desc->deactivate) + return bd->desc->deactivate(wo); + + return -ENXIO; +} + +int wc_bearer_driver_connect(WcObject *wo, const char *address, + const char *desc_name) +{ + struct bearer_driver *bd; + + WC_OBJECT_CHECK_RETURN(wo, WC_OBJECT_TYPE_BEARER_DRIVER, -EINVAL); + + bd = wc_object_get_element(wo, desc_name); + if (!bd) + return -EINVAL; + + if (bd->desc->connect) + return bd->desc->connect(wo, address); + + return -ENXIO; +} + +int wc_bearer_driver_disconnect(WcObject *wo, const char *desc_name) +{ + struct bearer_driver *bd; + + WC_OBJECT_CHECK_RETURN(wo, WC_OBJECT_TYPE_BEARER_DRIVER, -EINVAL); + + bd = wc_object_get_element(wo, desc_name); + if (!bd) + return -EINVAL; + + if (bd->desc->disconnect) + return bd->desc->disconnect(wo); + + return -ENXIO; +} + +void wc_bearer_driver_exit(WcObject *wo, const char *desc_name) +{ + struct bearer_driver *bd; + + WC_OBJECT_CHECK(wo, WC_OBJECT_TYPE_BEARER_DRIVER); + + bd = wc_object_get_element(wo, desc_name); + if (!bd) + return; + + if (bd->desc->exit) + bd->desc->exit(wo); +} + +int driver_init(const char *driver_path) +{ + int err; + void *h; + GDir *dir; + char *filename; + const gchar *file; + + if (!driver_path) + return -EINVAL; + + dir = g_dir_open(driver_path, 0, NULL); + if (!dir) { + ERR("Failed to open driver(%s)", driver_path); + return -EIO; + } + + while ((file = g_dir_read_name(dir)) != NULL) { + if (g_str_has_prefix(file, "lib") == TRUE || + g_str_has_suffix(file, ".so") == FALSE) + continue; + + filename = g_build_filename(driver_path, file, NULL); + + h = dlopen(filename, RTLD_NOW); + if (!h) { + ERR("Failed to open (%s) %s", filename, dlerror()); + g_free(filename); + continue; + } + + err = __add_bearer(filename, h); + if (err < 0 && err != -EALREADY) + dlclose(h); + + g_free(filename); + } + + g_dir_close(dir); + + return 0; +} + +static void __clean_bearer_driver_elements(WcObject *wo, + const char *element_name, void *element, void *user_data) +{ + struct bearer_driver *bd = (struct bearer_driver *)element; + + wc_bearer_driver_exit(wo, element_name); + + wc_bearer_driver_free(bd); + + g_free((gpointer)element_name); + g_free(element); +} + +static void __clean_bearer_drivers(gpointer data) +{ + WcObject *wo = data; + + if (!wo) + return; + + WC_OBJECT_CHECK(wo, WC_OBJECT_TYPE_BEARER_DRIVER); + + wc_object_foreach_get_elements(wo, __clean_bearer_driver_elements, NULL); + + wc_object_unexport(wo); + wc_object_free(wo); +} + +void driver_cleanup(void) +{ + g_slist_free_full(driver_list, __clean_bearer_drivers); +} diff --git a/src/error.c b/src/error.c new file mode 100644 index 0000000..783153d --- /dev/null +++ b/src/error.c @@ -0,0 +1,63 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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. + * + */ + +#include +#include + +#include "log.h" +#include "error.h" + +static char *__error_message; + +void wc_error_add(const char *msg, ...) +{ + va_list ap; + gchar buf[1024] = { 0, }; + char *new_message; + + va_start(ap, msg); + vsnprintf(buf, 1023, msg, ap); + va_end(ap); + + if (__error_message) { + new_message = g_strdup_printf("%s; %s", __error_message, buf); + + g_free(__error_message); + __error_message = new_message; + } else + __error_message = g_strdup(buf); + + ERR("%s", __error_message); +} + +void wc_error_return(GDBusMethodInvocation *invocation) +{ + if (!invocation) + return; + + if (!__error_message) + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, "No error message"); + else + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, __error_message); + + g_free(__error_message); + __error_message = NULL; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..3ed7d0a --- /dev/null +++ b/src/main.c @@ -0,0 +1,95 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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. + * + */ + +#include +#include +#include + +#include "log.h" +#include "dbus.h" +#include "driver.h" +#include "control.h" +#include "service.h" +#include "technology.h" + + +static void __on_log_glib(const gchar *log_domain, GLogLevelFlags log_level, + const gchar *msg, gpointer user_data) +{ + ERR("glib %s", msg); +} + +static void __init(void) +{ + technology_init(); + + service_init(); + + control_init(); + + driver_init(WECONN_PLUGIN_PATH); +} + +static void __cleanup(void) +{ + control_cleanup(); + + driver_cleanup(); + + service_cleanup(); + + technology_cleanup(); +} + +int main (int argc, char *argv[]) +{ + int err; + GMainLoop *main_loop; + + umask(0077); + + DBG("Wearable Connection Controller %s", VERSION); + + if (daemon(0, 0) != 0) + DBG("Cannot start daemon"); + + g_type_init(); + +#if 0 + g_log_set_always_fatal( + G_LOG_FATAL_MASK | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING); +#endif + + g_log_set_default_handler(__on_log_glib, NULL); + + main_loop = g_main_loop_new(NULL, FALSE); + + err = dbus_init(G_BUS_TYPE_SYSTEM, WECONN_SERVICE_DBUS, WECONN_PATH_DBUS, + __init); + if (err < 0) + return -1; + + g_main_loop_run(main_loop); + + __cleanup(); + + dbus_cleanup(); + + return 0; +} diff --git a/src/object.c b/src/object.c new file mode 100644 index 0000000..d828f42 --- /dev/null +++ b/src/object.c @@ -0,0 +1,582 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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. + * + */ + +#include +#include + +#include "log.h" +#include "dbus.h" +#include "error.h" +#include "events.h" +#include "object.h" + +#include "weconn_type.h" + +struct wc_callback_type { + const char *event; + WcObjectCallback callback; + void *user_data; +}; + +struct wc_element_type { + const char *element_name; + void *element; +}; + +struct wc_object { + const char *name; + unsigned int type; + + GSList *element_list; + + GSList *callbacks; + GHashTable *property; + void *user_data; + + char *service_connecting; + guint timeout_connecting; + char *service_disconnecting; + guint timeout_disconnecting; + + GDBusInterfaceSkeleton *di; +}; + +static GSList *object_list; + +EXPORT_API WcObject *wc_object_new(const char *name, unsigned int type) +{ + struct wc_object *wo; + + if (!name) + return NULL; + + wo = g_try_new0(struct wc_object, 1); + if (!wo) + return NULL; + + wo->name = g_strdup(name); + wo->type = type; + wo->property = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); + + object_list = g_slist_append(object_list, wo); + + return wo; +} + +EXPORT_API void wc_object_free(WcObject *wo) +{ + GSList *list = NULL; + struct wc_callback_type *wcb = NULL; + struct wc_element_type *we; + + if (!wo) + return; + + g_free((gpointer)wo->name); + g_object_unref(wo->di); + g_hash_table_destroy(wo->property); + + if (wo->callbacks) { + for (list = wo->callbacks; list; list = list->next) { + wcb = (struct wc_callback_type *)list->data; + if (!wcb) + continue; + + if (wcb->event) + g_free((gpointer)wcb->event); + + g_free(wcb); + } + + g_slist_free(wo->callbacks); + wo->callbacks = NULL; + } + + for (list = wo->element_list; list; list = list->next) { + we = (struct wc_element_type *)list->data; + + g_free((gpointer)we->element_name); + g_free(we->element); + } + + g_slist_free(wo->element_list); + + object_list = g_slist_remove(object_list, wo); + + g_free(wo); +} + +EXPORT_API WcObject *wc_object_find(const char *name) +{ + GSList *list = object_list; + WcObject *wo; + + if (!name) + return NULL; + + while (list) { + wo = (WcObject *)list->data; + if (g_strcmp0(wo->name, name) == 0) + return wo; + + list = list->next; + } + + return NULL; +} + +EXPORT_API const char *wc_object_get_name(WcObject *wo) +{ + if (!wo) + return NULL; + + return g_strdup(wo->name); +} + +EXPORT_API const char *wc_object_peek_name(WcObject *wo) +{ + if (!wo) + return NULL; + + return wo->name; +} + +EXPORT_API unsigned int wc_object_get_type(WcObject *wo) +{ + if (!wo) + return 0; + + return wo->type; +} + +EXPORT_API int wc_object_append_element(WcObject *wo, + const char *element_name, void *element) +{ + GSList *list; + struct wc_element_type *we; + + if (!wo || !element_name) + return -EINVAL; + + if (strlen(element_name) < 1) + return -EINVAL; + + /* Check already registered */ + for (list = wo->element_list; list; list = list->next) { + we = (struct wc_element_type *)list->data; + + if (we && g_strcmp0(element_name, we->element_name) == 0) + return -EALREADY; + } + + we = g_try_new0(struct wc_element_type, 1); + if (!we) + return -ENOMEM; + + we->element_name = g_strdup(element_name); + we->element = element; + + wo->element_list = g_slist_append(wo->element_list, we); + + return 0; +} + +EXPORT_API void *wc_object_get_element(WcObject *wo, const char *element_name) +{ + GSList *list; + struct wc_element_type *we; + + if (!wo || !element_name) + return NULL; + + if (strlen(element_name) < 1) + return NULL; + + for (list = wo->element_list; list; list = list->next) { + we = (struct wc_element_type *)list->data; + + if (we && g_strcmp0(element_name, we->element_name) == 0) + return we->element; + } + + return NULL; +} + +EXPORT_API int wc_object_foreach_get_elements(WcObject *wo, + wc_object_foreach_get_elements_cb callback, void *user_data) +{ + GSList *list; + struct wc_element_type *we; + + if (!wo || !callback) + return -EINVAL; + + for (list = wo->element_list; list; list = list->next) { + we = (struct wc_element_type *)list->data; + + callback(wo, we->element_name, we->element, user_data); + } + + return 0; +} + +EXPORT_API int wc_object_set_dbus_interface(WcObject *wo, + GDBusInterfaceSkeleton *di) +{ + if (!wo || !di) + return -EINVAL; + + wo->di = di; + return 0; +} + +EXPORT_API GDBusInterfaceSkeleton *wc_object_get_dbus_interface(WcObject *wo) +{ + if (!wo) + return NULL; + + return wo->di; +} + +EXPORT_API int wc_object_export(WcObject *wo, const char *path) +{ + GError *error = NULL; + gboolean ret; + const char *real_path = NULL; + + if (!wo || !path) + return -EINVAL; + + if (!wo->di) + return -EINVAL; + + real_path = dbus_name2path(path); + + if (g_variant_is_object_path(real_path) == FALSE) { + wc_error_add("InvalidName %s", real_path); + g_free((gpointer)real_path); + return -EINVAL; + } + + ret = g_dbus_interface_skeleton_export(wo->di, dbus_get_connection(), + real_path, &error); + if (ret == FALSE) { + if (error) { + wc_error_add("%s", error->message); + g_error_free(error); + } else + wc_error_add("DBusObjectPathFailed"); + + g_free((gpointer)real_path); + return -EIO; + } + + wc_object_set_property(wo, "object-path", real_path); + + g_free((gpointer)real_path); + + return 0; +} + +EXPORT_API int wc_object_unexport(WcObject *wo) +{ + if (!wo) + return -EINVAL; + + if (!wo->di) + return -EINVAL; + + g_dbus_interface_skeleton_unexport(wo->di); + + return 0; +} + +EXPORT_API int wc_object_add_callback(WcObject *wo, const char *event, + WcObjectCallback callback, void *user_data) +{ + GSList *list; + struct wc_callback_type *wcb = NULL; + + if (!wo || !event || !callback) + return -EINVAL; + + if (strlen(event) < 1) + return -EINVAL; + + /* Check already registered */ + list = wo->callbacks; + while (list) { + wcb = (struct wc_callback_type *)list->data; + if (wcb && wcb->callback == callback && g_strcmp0(wcb->event, event) == 0) + return -EALREADY; + + list = list->next; + } + + wcb = g_try_new0(struct wc_callback_type, 1); + if (!wcb) + return -ENOMEM; + + wcb->event = g_strdup(event); + wcb->callback = callback; + wcb->user_data = user_data; + + wo->callbacks = g_slist_append(wo->callbacks, wcb); + + return 0; +} + +EXPORT_API int wc_object_del_callback(WcObject *wo, const char *event, + WcObjectCallback callback) +{ + struct wc_callback_type *wcb = NULL; + GSList *l = NULL; + + if (!wo || !event || !callback || !wo->callbacks) + return -EINVAL; + + if (strlen(event) < 1) + return -EINVAL; + + l = wo->callbacks; + while (l) { + wcb = l->data; + if (!wcb) { + l = l->next; + continue; + } + + if (wcb->callback != callback) { + l = l->next; + continue; + } + + if (g_strcmp0(wcb->event, event) != 0) { + l = l->next; + continue; + } + + l = l->next; + wo->callbacks = g_slist_remove(wo->callbacks, wcb); + g_free((gpointer)wcb->event); + g_free(wcb); + } + + return 0; +} + +EXPORT_API int wc_object_emit_callback(WcObject *wo, const char *event, + const void *event_info) +{ + struct wc_callback_type *wcb = NULL; + GSList *l = NULL; + size_t event_len; + + if (!wo || !event) + return -EINVAL; + + event_len = strlen(event); + if (event_len < 1) + return -EINVAL; + + l = wo->callbacks; + while (l) { + wcb = l->data; + if (!wcb) { + l = l->next; + continue; + } + + if (g_strcmp0(wcb->event, event) != 0) { + l = l->next; + continue; + } + + if (wcb->callback) { + int ret = wcb->callback(wo, event_info, wcb->user_data); + if (ret == FALSE) { + l = l->next; + wo->callbacks = g_slist_remove(wo->callbacks, wcb); + continue; + } + } + + l = l->next; + } + + if (wo->di && event_len > 5 && g_str_has_prefix(event, "dbus-") == TRUE) { + /* + * if event is 'dbus-xxx', emit dbus signal 'xxx' + * but, only support one parameter. + * + * if you want use multiple parameters, + * use g_signal_emit_by_name() directly. + */ + g_signal_emit_by_name(wo->di, event + 5, event_info); + } + + return 0; +} + +static GSList *_set_property_real(WcObject *wo, const char *key, + const char *value, GSList *list) +{ + if (!wo || !key) + return list; + + if (!value) { + g_hash_table_remove(wo->property, key); + + return g_slist_append(list, (gpointer)key); + } + + g_hash_table_replace(wo->property, g_strdup(key), g_strdup(value)); + + return g_slist_append(list, (gpointer)key); +} + +EXPORT_API int wc_object_set_property_full(WcObject *wo, + const char *first_property, ...) +{ + va_list argptr; + GSList *list = NULL; + const char *k; + const char *v; + + if (!wo || !first_property) + return -EINVAL; + + va_start(argptr, first_property); + + k = first_property; + v = va_arg(argptr, char *); + list = _set_property_real(wo, k, v, list); + + while (1) { + k = va_arg(argptr, char *); + if (!k) + break; + + v = va_arg(argptr, char *); + list = _set_property_real(wo, k, v, list); + } + + va_end(argptr); + + if (!list) + return -ENODATA; + + if (g_slist_length(list) > 0) + wc_object_emit_callback(wo, WC_OBJECT_EVENT_PROPERTY_CHANGED, list); + + g_slist_free(list); + + return 0; +} + +EXPORT_API char *wc_object_get_property(WcObject *wo, const char *key) +{ + if (!wo || !key) + return NULL; + + return g_strdup(g_hash_table_lookup(wo->property, key)); +} + +EXPORT_API const char *wc_object_peek_property(WcObject *wo, const char *key) +{ + if (!wo || !key) + return NULL; + + return g_hash_table_lookup(wo->property, key); +} + +EXPORT_API GHashTable *wc_object_peek_property_hash(WcObject *wo) +{ + if (!wo) + return NULL; + + return wo->property; +} + +EXPORT_API void wc_object_set_timeout_connecting(WcObject *wo, + guint timeout, const char *service) +{ + if (!wo) + return; + + wo->timeout_connecting = timeout; + + if (wo->service_connecting != NULL) { + g_free(wo->service_connecting); + wo->service_connecting = NULL; + } + + if (service != NULL) + wo->service_connecting = g_strdup(service); +} + +EXPORT_API guint wc_object_get_timeout_connecting(WcObject *wo) +{ + if (!wo) + return 0; + + return wo->timeout_connecting; +} + +EXPORT_API char *wc_object_get_connecting_service(WcObject *wo) +{ + if (!wo) + return 0; + + return wo->service_connecting; +} + +EXPORT_API void wc_object_set_timeout_disconnecting(WcObject *wo, + guint timeout, const char *service) +{ + if (!wo) + return; + + wo->timeout_disconnecting = timeout; + + if (wo->service_disconnecting != NULL) { + g_free(wo->service_disconnecting); + wo->service_disconnecting = NULL; + } + + if (service != NULL) + wo->service_disconnecting = g_strdup(service); +} + +EXPORT_API guint wc_object_get_timeout_disconnecting(WcObject *wo) +{ + if (!wo) + return 0; + + return wo->timeout_disconnecting; +} + +EXPORT_API char *wc_object_get_disconnecting_service(WcObject *wo) +{ + if (!wo) + return 0; + + return wo->service_disconnecting; +} diff --git a/src/service.c b/src/service.c new file mode 100644 index 0000000..88ea4b6 --- /dev/null +++ b/src/service.c @@ -0,0 +1,56 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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. + * + */ + +#include +#include + +#include "log.h" +#include "dbus.h" +#include "object.h" +#include "service.h" + +#include "generated-code.h" + +static WcObject *wo_service; + +int service_init(void) +{ + GDBusInterfaceSkeleton *di; + + wo_service = wc_object_new("service", WC_OBJECT_TYPE_SERVICE); + if (!wo_service) + return -ENOMEM; + + di = G_DBUS_INTERFACE_SKELETON(weconn_service_skeleton_new()); + + /* TODO: extend technology to get bearers' properties */ + wc_object_append_element(wo_service, "service", NULL); + wc_object_set_dbus_interface(wo_service, di); + wc_object_export(wo_service, WECONN_SERVICE_PATH); + + return 0; +} + +void service_cleanup(void) +{ + /* TODO: clean all of services */ + + wc_object_unexport(wo_service); + wc_object_free(wo_service); +} diff --git a/src/technology.c b/src/technology.c new file mode 100644 index 0000000..0d3fed4 --- /dev/null +++ b/src/technology.c @@ -0,0 +1,640 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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. + * + */ + +#include +#include + +#include "log.h" +#include "dbus.h" +#include "util.h" +#include "error.h" +#include "driver.h" +#include "events.h" +#include "object.h" +#include "technology.h" + +#include "generated-code.h" + +#define CONNECT_TIMEOUT 120 + +static GSList *technology_list = NULL; + +static void __dbus_builder_add_sv(gpointer key, gpointer value, + gpointer user_data) +{ + GVariantBuilder *b = user_data; + if (!b) + return; + + g_variant_builder_add(b, "{sv}", key, g_variant_new_string(value)); +} + +static gboolean __get_properties(GDBusInterfaceSkeleton *di, + GDBusMethodInvocation *invocation, gpointer user_data) +{ + WcObject *wo = (WcObject *)user_data; + GVariant *result = NULL; + GVariantBuilder *b; + + DBG(""); + + if (!wo) { + wc_error_add("InvalidArguments"); + wc_error_return(invocation); + return TRUE; + } + + b = g_variant_builder_new(G_VARIANT_TYPE("a{sv}")); + + g_hash_table_foreach(wc_object_peek_property_hash(wo), + __dbus_builder_add_sv, b); + + result = g_variant_builder_end(b); + + g_variant_builder_unref(b); + + g_dbus_method_invocation_return_value(invocation, + g_variant_new("(@a{sv})", result)); + + return TRUE; +} + +static gboolean __get_property(GDBusInterfaceSkeleton *di, + GDBusMethodInvocation *invocation, + const gchar *name, gpointer user_data) +{ + gpointer value; + WcObject *wo = (WcObject *)user_data; + weconn_service_type_e service_type; + weconn_device_type_e device_type; + const char *default_property = NULL; + + DBG("%s", name); + + if (!wo || !name) { + wc_error_add("InvalidArguments"); + wc_error_return(invocation); + return TRUE; + } + + service_type = wc_service_type_string2enum(name); + if (service_type) + default_property = XSTR(W_SERVICE_STATE_DISCONNECTED); + else { + device_type = wc_device_type_string2enum(name); + + if (device_type) + default_property = XSTR(W_DEVICE_STATE_DISABLED); + else { + wc_error_add("InvalidArguments"); + wc_error_return(invocation); + return TRUE; + } + } + + value = g_hash_table_lookup(wc_object_peek_property_hash(wo), name); + if (value) { + g_dbus_method_invocation_return_value(invocation, + g_variant_new("(v)", g_variant_new_string(value))); + } else { + g_dbus_method_invocation_return_value(invocation, + g_variant_new("(v)", g_variant_new_string(default_property))); + } + + return TRUE; +} + +static gboolean __set_property(GDBusInterfaceSkeleton *di, + GDBusMethodInvocation *invocation, + const gchar *name, gpointer user_data) +{ + WcObject *wo = (WcObject *)user_data; + + DBG("%s", name); + + if (!wo || !name) { + wc_error_add("InvalidArguments"); + wc_error_return(invocation); + return TRUE; + } + + /* TODO: set property */ + + g_dbus_method_invocation_return_value(invocation, g_variant_new("()")); + + return TRUE; +} + +static gboolean __scan(GDBusInterfaceSkeleton *di, + GDBusMethodInvocation *invocation, gpointer user_data) +{ + WcObject *wo = (WcObject *)user_data; + + DBG(""); + + if (!wo) { + wc_error_add("InvalidArguments"); + wc_error_return(invocation); + return TRUE; + } + + g_dbus_method_invocation_return_value(invocation, g_variant_new("()")); + + return TRUE; +} + +static const char *__service_type_string2_driver_name(const char *service_type) +{ + if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_HFP), service_type) == 0) + return "bluetooth"; + else if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_SPP), service_type) == 0) + return "esap"; + else if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_PAN), service_type) == 0) + return "pan"; + else if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_GATT), service_type) == 0) + return "bluetooth"; + else if (g_strcmp0(XSTR(W_SERVICE_TYPE_CELLULAR), service_type) == 0) + return "cellular"; + else if (g_strcmp0(XSTR(W_SERVICE_TYPE_WIFI), service_type) == 0) + return "wifi"; + else if (g_strcmp0(XSTR(W_SERVICE_TYPE_WIFI_P2P), service_type) == 0) + return "p2p"; + else if (g_strcmp0(XSTR(W_SERVICE_TYPE_WIFI_ADHOC), service_type) == 0) + return "adhoc"; + else if (g_strcmp0(XSTR(W_SERVICE_TYPE_ETHERNET), service_type) == 0) + return "ethernet"; + + return NULL; +} + +static void __connect_result_emit_signal(WcObject *wo, + const gchar *service, const char *signal_name, int result) +{ + GDBusConnection *connection = NULL; + GError *error = NULL; + GVariant *variant = NULL; + + if (!wo) + return; + + if (!service) { + ERR("service is NULL"); + return; + } + + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + if (!connection) { + ERR("Failed to get system bus[%s]", error->message); + g_error_free(error); + return; + } + + variant = g_variant_new("(si)", service, result); + + g_dbus_connection_emit_signal(connection, NULL, + WECONN_PATH_DBUS, WECONN_TECHNOLOGY_INTERFACE, + signal_name, variant, NULL); + + g_object_unref(connection); +} + +static gboolean __connect_timeout_cb(gpointer user_data) +{ + char *service = NULL; + char *state = NULL; + WcObject *wo = (WcObject *)user_data; + if (!wo) + return FALSE; + + service = wc_object_get_connecting_service(wo); + if (!service) + return FALSE; + + state = wc_object_get_property(wo, service); + if (state != NULL) { + if (g_strcmp0(state, XSTR(W_SERVICE_STATE_DISCONNECTED)) != 0) + wc_object_set_property(wo, service, + XSTR(W_SERVICE_STATE_DISCONNECTED)); + + g_free(state); + } + + __connect_result_emit_signal(wo, service, + WECONN_TECHNOLOGY_SIGNAL_CONNECTION_RESULT, -ETIMEDOUT); + + wc_object_set_timeout_connecting(wo, 0, NULL); + + DBG("connect_timeout callback"); + return FALSE; +} + +static gboolean __disconnect_timeout_cb(gpointer user_data) +{ + char *service = NULL; + char *state = NULL; + WcObject *wo = (WcObject *)user_data; + if (!wo) + return FALSE; + + service = wc_object_get_disconnecting_service(wo); + if (!service) + return FALSE; + + state = wc_object_get_property(wo, service); + if (state != NULL) { + if (g_strcmp0(state, XSTR(W_SERVICE_STATE_CONNECTED)) != 0) + wc_object_set_property(wo, service, + XSTR(W_SERVICE_STATE_CONNECTED)); + + g_free(state); + } + + __connect_result_emit_signal(wo, service, + WECONN_TECHNOLOGY_SIGNAL_DISCONNECTION_RESULT, -ETIMEDOUT); + + wc_object_set_timeout_disconnecting(wo, 0, NULL); + + DBG("disconnect_timeout callback"); + return FALSE; +} + +static void __connect_timer_clear(WcObject *wo) +{ + guint timer = 0; + + if (!wo) + return; + + timer = wc_object_get_timeout_connecting(wo); + if (timer > 0) { + g_source_remove(timer); + wc_object_set_timeout_connecting(wo, 0, NULL); + } + + DBG("connect_timer cleared"); +} + +static void __disconnect_timer_clear(WcObject *wo) +{ + guint timer = 0; + + if (!wo) + return; + + timer = wc_object_get_timeout_disconnecting(wo); + if (timer > 0) { + g_source_remove(timer); + wc_object_set_timeout_disconnecting(wo, 0, NULL); + } + + DBG("disconnect_timer cleared"); +} + +static gboolean __connect(GDBusInterfaceSkeleton *di, + GDBusMethodInvocation *invocation, + const gchar *service, gpointer user_data) +{ + int err; + guint timer; + char *service_state; + WcObject *wo = (WcObject *)user_data; + + DBG(""); + + /* TODO: + * 1. If host CM, at first configure PAN, BT tethering on, + * and send PAN_CONNECTION command to wearable CM. + * 2. If wearable CM, send PAN_CONFIGURATION command to host CM. + */ + + // __request_pan_configuration(); + + /* TODO: + * After PAN_CONNECTION request from host CM, do following procedure. + */ + + if (!wo || wc_object_get_type(wo) != WC_OBJECT_TYPE_BEARER_DRIVER) { + wc_error_add("InvalidArguments"); + wc_error_return(invocation); + return TRUE; + } + + service_state = wc_object_get_property(wo, service); + if (service_state != NULL) { + if (g_strcmp0(service_state, XSTR(W_SERVICE_STATE_CONNECTING)) == 0) { + wc_error_add("InProgress"); + wc_error_return(invocation); + g_free(service_state); + return TRUE; + } + g_free(service_state); + } + + err = wc_bearer_driver_connect(wo, NULL, + __service_type_string2_driver_name(service)); + if (err == -EISCONN) { + wc_error_add("AlreadyConnected"); + wc_error_return(invocation); + return TRUE; + } else if (err == -EIO) { + wc_error_add("LostConnection"); + wc_error_return(invocation); + return TRUE; + } else if (err < 0) { + /* TODO: specific error code should be defined */ + wc_error_add("ConnectionFailed"); + wc_error_return(invocation); + return TRUE; + } + + if (wc_object_get_timeout_connecting(wo) == 0) { + wc_object_set_property(wo, service, XSTR(W_SERVICE_STATE_CONNECTING)); + timer = g_timeout_add_seconds(CONNECT_TIMEOUT, + __connect_timeout_cb, wo); + wc_object_set_timeout_connecting(wo, timer, service); + } + + g_dbus_method_invocation_return_value(invocation, g_variant_new("()")); + return TRUE; +} + +static gboolean __disconnect(GDBusInterfaceSkeleton *di, + GDBusMethodInvocation *invocation, + const gchar *service, gpointer user_data) +{ + int err; + guint timer; + char *service_state; + WcObject *wo = (WcObject *)user_data; + + DBG(""); + + if (!wo || wc_object_get_type(wo) != WC_OBJECT_TYPE_BEARER_DRIVER) { + wc_error_add("InvalidArguments"); + wc_error_return(invocation); + return TRUE; + } + + service_state = wc_object_get_property(wo, service); + if (service_state != NULL) { + if (g_strcmp0(service_state, XSTR(W_SERVICE_STATE_DISCONNECTING)) == 0) { + wc_error_add("InProgress"); + wc_error_return(invocation); + g_free(service_state); + return TRUE; + } + g_free(service_state); + } + + err = wc_bearer_driver_disconnect(wo, + __service_type_string2_driver_name(service)); + if (err == -ENOTCONN) { + wc_error_add("NotConnected"); + wc_error_return(invocation); + + return TRUE; + } else if (err < 0) { + /* TODO: specific error code should be defined */ + wc_error_add("DisconnectionFailed"); + wc_error_return(invocation); + return TRUE; + } + + if (wc_object_get_timeout_disconnecting(wo) == 0) { + wc_object_set_property(wo, service, XSTR(W_SERVICE_STATE_DISCONNECTING)); + timer = g_timeout_add_seconds(CONNECT_TIMEOUT, + __disconnect_timeout_cb, wo); + wc_object_set_timeout_disconnecting(wo, timer, service); + } + + g_dbus_method_invocation_return_value(invocation, g_variant_new("()")); + return TRUE; +} + +static gboolean __wc_technology_bt_property_changed_cb(WcObject *wo, + const void *event_info, void *user_data) +{ + const char *key = NULL; + char *state = NULL; + char *service = NULL; + char *bcmservice_state = NULL; + GSList *list_properies = NULL; + GSList *list = NULL; + + list_properies = (GSList *)event_info; + if (!list_properies) + return TRUE; + + if (g_slist_length(list_properies) < 1) { + ERR("No property"); + return TRUE; + } + + for (list = list_properies; list; list = list->next) { + key = (const char *)list->data; + DBG("property : %s", key); + if (g_strcmp0(key, XSTR(W_SERVICE_TYPE_BT_PAN)) == 0) { + state = wc_object_get_property(wo, key); + if (!state) + continue; + + DBG("state : %s", state); + + if (g_strcmp0(state, XSTR(W_SERVICE_STATE_CONNECTED)) == 0) { + + service = wc_object_get_connecting_service(wo); + if (g_strcmp0(service, key) == 0) { + __connect_result_emit_signal(wo, key, + WECONN_TECHNOLOGY_SIGNAL_CONNECTION_RESULT, 0); + __connect_timer_clear(wo); + } + + service = wc_object_get_disconnecting_service(wo); + if (g_strcmp0(service, key) == 0) { + __connect_result_emit_signal(wo, key, + WECONN_TECHNOLOGY_SIGNAL_DISCONNECTION_RESULT, -ECANCELED); + __disconnect_timer_clear(wo); + return TRUE; + } + + __connect_result_emit_signal(wo, key, + WECONN_TECHNOLOGY_SIGNAL_SERVICE_STATE_CHANGED, + W_SERVICE_STATE_CONNECTED); + + } else if (g_strcmp0(state, XSTR(W_SERVICE_STATE_DISCONNECTED)) == 0) { + + service = wc_object_get_connecting_service(wo); + if (g_strcmp0(service, key) == 0) { + bcmservice_state = wc_object_get_property(wo, + WC_OBJECT_EVENT_SAP_BCMSERVICE_STATUS); + if (g_strcmp0(bcmservice_state, XSTR(W_SERVICE_STATE_CONNECTED)) == 0) + __connect_result_emit_signal(wo, key, + WECONN_TECHNOLOGY_SIGNAL_CONNECTION_RESULT, -ECANCELED); + else + __connect_result_emit_signal(wo, key, + WECONN_TECHNOLOGY_SIGNAL_DISCONNECTION_RESULT, -EIO); + + __connect_result_emit_signal(wo, key, + WECONN_TECHNOLOGY_SIGNAL_CONNECTION_RESULT, -ECANCELED); + __connect_timer_clear(wo); + return TRUE; + } + + service = wc_object_get_disconnecting_service(wo); + if (g_strcmp0(service, key) == 0) { + bcmservice_state = wc_object_get_property(wo, + WC_OBJECT_EVENT_SAP_BCMSERVICE_STATUS); + if (g_strcmp0(bcmservice_state, XSTR(W_SERVICE_STATE_CONNECTED)) == 0) + __connect_result_emit_signal(wo, key, + WECONN_TECHNOLOGY_SIGNAL_DISCONNECTION_RESULT, 0); + else + __connect_result_emit_signal(wo, key, + WECONN_TECHNOLOGY_SIGNAL_DISCONNECTION_RESULT, -ENOTCONN); + __disconnect_timer_clear(wo); + } + + __connect_result_emit_signal(wo, key, + WECONN_TECHNOLOGY_SIGNAL_SERVICE_STATE_CHANGED, + W_SERVICE_STATE_DISCONNECTED); + } + + g_free(state); + } + } + + return TRUE; +} + +static void __wc_technology_register_service_state_changed(WcObject *wo) +{ + const char *technology; + + if (!wo) + return; + + technology = wc_object_peek_name(wo); + if (g_strcmp0(technology, WC_TECHNOLOGY_BLUETOOTH) == 0) { + wc_object_add_callback(wo, WC_OBJECT_EVENT_PROPERTY_CHANGED, + __wc_technology_bt_property_changed_cb, NULL); + } else if (g_strcmp0(technology, WC_TECHNOLOGY_CELLULAR) == 0) { + + } +} + +static gboolean __wc_technology_bearer_enabled_cb(WcObject *wo, + const void *event_info, void *user_data) +{ + const char *path; + GDBusInterfaceSkeleton *di; + + DBG("technology %s added", wc_object_peek_name(wo)); + + if (!wo) + return TRUE; + + di = G_DBUS_INTERFACE_SKELETON(weconn_technology_skeleton_new()); + + g_signal_connect(di, "handle-get-properties", + G_CALLBACK(__get_properties), wo); + g_signal_connect(di, "handle-get-property", + G_CALLBACK(__get_property), wo); + g_signal_connect(di, "handle-set-property", + G_CALLBACK(__set_property), wo); + g_signal_connect(di, "handle-get-properties", + G_CALLBACK(__scan), wo); + g_signal_connect(di, "handle-connect", + G_CALLBACK(__connect), wo); + g_signal_connect(di, "handle-disconnect", + G_CALLBACK(__disconnect), wo); + + wc_object_set_dbus_interface(wo, di); + + technology_list = g_slist_prepend(technology_list, wo); + + path = g_strdup_printf("%s/%s", + WECONN_TECHNOLOGY_PATH, wc_object_peek_name(wo)); + + wc_object_export(wo, path); + g_free((gpointer)path); + + __wc_technology_register_service_state_changed(wo); + + return TRUE; +} + +static gboolean __wc_technology_bearer_disabled_cb(WcObject *wo, + const void *event_info, void *user_data) +{ + if (!wo) + return TRUE; + + wc_object_unexport(wo); + + /* TODO: g_signal_connect_closure for each c_handler */ + + technology_list = g_slist_remove(technology_list, wo); + + return TRUE; +} + +int wc_technology_add_bearer(WcObject *wo) +{ + int err; + + err = wc_object_add_callback(wo, WC_OBJECT_EVENT_BEARER_ENABLED, + __wc_technology_bearer_enabled_cb, NULL); + if (err < 0 && err != -EALREADY) + return err; + + err = wc_object_add_callback(wo, WC_OBJECT_EVENT_BEARER_DISABLED, + __wc_technology_bearer_disabled_cb, NULL); + + return err; +} + +WcObject *wc_technology_get_bearer(const char *technology) +{ + GSList *list = technology_list; + WcObject *wo; + + if (!technology) + return NULL; + + while (list) { + wo = (WcObject *)list->data; + if (wo && g_strcmp0(technology, wc_object_peek_name(wo)) == 0) + return wo; + + list = list->next; + } + + return NULL; +} + +int technology_init(void) +{ + /* TODO: initialize default technologies */ + + return 0; +} + +void technology_cleanup(void) +{ + /* TODO: clean all of technologies */ +} diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..559816a --- /dev/null +++ b/src/util.c @@ -0,0 +1,236 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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. + * + */ + +#include + +#include "log.h" +#include "util.h" + +#define CONNMAN_PATH "/net/connman" +#define CONNMAN_CELLULAR_SERVICE_PROFILE_PREFIX \ + CONNMAN_PATH "/service/cellular_" +#define CONNMAN_WIFI_SERVICE_PROFILE_PREFIX \ + CONNMAN_PATH "/service/wifi_" +#define CONNMAN_ETHERNET_SERVICE_PROFILE_PREFIX \ + CONNMAN_PATH "/service/ethernet_" +#define CONNMAN_BLUETOOTH_SERVICE_PROFILE_PREFIX \ + CONNMAN_PATH "/service/bluetooth_" + +weconn_service_type_e wc_service_type_string2enum(const char *service_type) +{ + if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_HFP), service_type) == 0) + return W_SERVICE_TYPE_BT_HFP; + else if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_SPP), service_type) == 0) + return W_SERVICE_TYPE_BT_SPP; + else if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_PAN), service_type) == 0) + return W_SERVICE_TYPE_BT_PAN; + else if (g_strcmp0(XSTR(W_SERVICE_TYPE_BT_GATT), service_type) == 0) + return W_SERVICE_TYPE_BT_GATT; + else if (g_strcmp0(XSTR(W_SERVICE_TYPE_CELLULAR), service_type) == 0) + return W_SERVICE_TYPE_CELLULAR; + else if (g_strcmp0(XSTR(W_SERVICE_TYPE_WIFI), service_type) == 0) + return W_SERVICE_TYPE_WIFI; + else if (g_strcmp0(XSTR(W_SERVICE_TYPE_WIFI_P2P), service_type) == 0) + return W_SERVICE_TYPE_WIFI_P2P; + else if (g_strcmp0(XSTR(W_SERVICE_TYPE_WIFI_ADHOC), service_type) == 0) + return W_SERVICE_TYPE_WIFI_ADHOC; + else if (g_strcmp0(XSTR(W_SERVICE_TYPE_ETHERNET), service_type) == 0) + return W_SERVICE_TYPE_ETHERNET; + + return 0x00; +} + +const char *wc_service_type_enum2string(weconn_service_type_e service_type) +{ + switch (service_type) { + case W_SERVICE_TYPE_BT_HFP: + return XSTR(W_SERVICE_TYPE_BT_HFP); + case W_SERVICE_TYPE_BT_SPP: + return XSTR(W_SERVICE_TYPE_BT_SPP); + case W_SERVICE_TYPE_BT_PAN: + return XSTR(W_SERVICE_TYPE_BT_PAN); + case W_SERVICE_TYPE_BT_GATT: + return XSTR(W_SERVICE_TYPE_BT_GATT); + case W_SERVICE_TYPE_CELLULAR: + return XSTR(W_SERVICE_TYPE_CELLULAR); + case W_SERVICE_TYPE_WIFI: + return XSTR(W_SERVICE_TYPE_WIFI); + case W_SERVICE_TYPE_WIFI_P2P: + return XSTR(W_SERVICE_TYPE_WIFI_P2P); + case W_SERVICE_TYPE_WIFI_ADHOC: + return XSTR(W_SERVICE_TYPE_WIFI_ADHOC); + case W_SERVICE_TYPE_ETHERNET: + return XSTR(W_SERVICE_TYPE_ETHERNET); + default: + break; + } + + return NULL; +} + +weconn_service_state_e wc_service_state_string2enum(const char *service_state) +{ + if (g_strcmp0(XSTR(W_SERVICE_STATE_DISCONNECTED), service_state) == 0) + return W_SERVICE_STATE_DISCONNECTED; + else if (g_strcmp0(XSTR(W_SERVICE_STATE_CONNECTING), service_state) == 0) + return W_SERVICE_STATE_CONNECTING; + else if (g_strcmp0(XSTR(W_SERVICE_STATE_CONNECTED), service_state) == 0) + return W_SERVICE_STATE_CONNECTED; + else if (g_strcmp0(XSTR(W_SERVICE_STATE_DISCONNECTING), service_state) == 0) + return W_SERVICE_STATE_DISCONNECTING; + + return 0x00; +} + +const char *wc_service_state_enum2string(weconn_service_state_e service_state) +{ + switch (service_state) { + case W_SERVICE_STATE_DISCONNECTED: + return XSTR(W_SERVICE_STATE_DISCONNECTED); + case W_SERVICE_STATE_CONNECTING: + return XSTR(W_SERVICE_STATE_CONNECTING); + case W_SERVICE_STATE_CONNECTED: + return XSTR(W_SERVICE_STATE_CONNECTED); + case W_SERVICE_STATE_DISCONNECTING: + return XSTR(W_SERVICE_STATE_DISCONNECTING); + default: + break; + } + + return NULL; +} + +weconn_device_type_e wc_device_type_string2enum(const char *device_type) +{ + if (g_strcmp0(XSTR(W_DEVICE_TYPE_BT), device_type) == 0) + return W_DEVICE_TYPE_BT; + else if (g_strcmp0(XSTR(W_DEVICE_TYPE_CELLULAR), device_type) == 0) + return W_DEVICE_TYPE_CELLULAR; + else if (g_strcmp0(XSTR(W_DEVICE_TYPE_WIFI), device_type) == 0) + return W_DEVICE_TYPE_WIFI; + else if (g_strcmp0(XSTR(W_DEVICE_TYPE_WIFI_P2P), device_type) == 0) + return W_DEVICE_TYPE_WIFI_P2P; + else if (g_strcmp0(XSTR(W_DEVICE_TYPE_WIFI_ADHOC), device_type) == 0) + return W_DEVICE_TYPE_WIFI_ADHOC; + else if (g_strcmp0(XSTR(W_DEVICE_TYPE_ETHERNET), device_type) == 0) + return W_DEVICE_TYPE_ETHERNET; + + return 0x00; +} + +const char *wc_device_type_enum2string(weconn_device_type_e device_type) +{ + switch (device_type) { + case W_DEVICE_TYPE_BT: + return XSTR(W_DEVICE_TYPE_BT); + case W_DEVICE_TYPE_CELLULAR: + return XSTR(W_DEVICE_TYPE_CELLULAR); + case W_DEVICE_TYPE_WIFI: + return XSTR(W_DEVICE_TYPE_WIFI); + case W_DEVICE_TYPE_WIFI_P2P: + return XSTR(W_DEVICE_TYPE_WIFI_P2P); + case W_DEVICE_TYPE_WIFI_ADHOC: + return XSTR(W_DEVICE_TYPE_WIFI_ADHOC); + case W_DEVICE_TYPE_ETHERNET: + return XSTR(W_DEVICE_TYPE_ETHERNET); + default: + break; + } + + return NULL; +} + +weconn_device_state_e wc_device_state_string2enum(const char *device_state) +{ + if (g_strcmp0(XSTR(W_DEVICE_STATE_DISABLED), device_state) == 0) + return W_DEVICE_STATE_DISABLED; + else if (g_strcmp0(XSTR(W_DEVICE_STATE_ENABLED), device_state) == 0) + return W_DEVICE_STATE_ENABLED; + + return 0x00; +} + +const char *wc_device_state_enum2string(weconn_device_state_e device_state) +{ + switch (device_state) { + case W_DEVICE_STATE_DISABLED: + return XSTR(W_DEVICE_STATE_DISABLED); + case W_DEVICE_STATE_ENABLED: + return XSTR(W_DEVICE_STATE_ENABLED); + default: + break; + } + + return NULL; +} + +weconn_device_type_e wc_device_get_type_from_path(const char *path) +{ + if (g_str_has_prefix(path, + CONNMAN_WIFI_SERVICE_PROFILE_PREFIX) == TRUE) + return W_DEVICE_TYPE_WIFI; + else if (g_str_has_prefix(path, + CONNMAN_CELLULAR_SERVICE_PROFILE_PREFIX) == TRUE) + return W_DEVICE_TYPE_CELLULAR; + else if (g_str_has_prefix(path, + CONNMAN_ETHERNET_SERVICE_PROFILE_PREFIX) == TRUE) + return W_DEVICE_TYPE_ETHERNET; + else if (g_str_has_prefix(path, + CONNMAN_BLUETOOTH_SERVICE_PROFILE_PREFIX) == TRUE) + return W_DEVICE_TYPE_BT; + + return 0x00; +} + +int wc_launch_popup(weconn_popup_type_e type, service_reply_cb callback, + void *user_data) +{ + int ret; + service_h service = NULL; + + ret = service_create(&service); + if (ret != SERVICE_ERROR_NONE) { + DBG("failed to create service [%d]", ret); + return -1; + } + + switch (type) { + case WC_POPUP_TYPE_PAN_CONNECT: + service_add_extra_data(service, "_WCPOPUP_TYPE_", "pan_connect"); + break; + case WC_POPUP_TYPE_PAN_DISCONNECT: + service_add_extra_data(service, "_WCPOPUP_TYPE_", "pan_disconnect"); + break; + default: + ERR("Not support popup type [%d]", type); + service_destroy(service); + return -1; + } + + service_set_package(service, "net.wc-popup"); + ret = service_send_launch_request(service, callback, user_data); + if (ret != SERVICE_ERROR_NONE) { + DBG("failed service launch request [%d]", ret); + return -1; + } + + service_destroy(service); + + return 0; +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..52dee07 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,22 @@ +SET(weconn_test "${PROJECT_NAME}-test") + +SET(dependents "capi-base-common gio-2.0 glib-2.0") +SET(pc_dependents "capi-base-common") + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/client/include/) + +INCLUDE(FindPkgConfig) +pkg_check_modules(${weconn_test} REQUIRED ${dependents}) +FOREACH(flag ${${weconn_test}_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wall") + +aux_source_directory(. sources) +FOREACH(src ${sources}) + GET_FILENAME_COMPONENT(src_name ${src} NAME_WE) + MESSAGE("${src_name}") + ADD_EXECUTABLE(${src_name} ${src}) + TARGET_LINK_LIBRARIES(${src_name} weconn ${${weconn_test}_LDFLAGS}) +ENDFOREACH() diff --git a/test/w_connection_manager_test.c b/test/w_connection_manager_test.c new file mode 100644 index 0000000..700d64a --- /dev/null +++ b/test/w_connection_manager_test.c @@ -0,0 +1,414 @@ +/* + * Wearable device Connection Controller Framework + * + * Copyright (c) 2013 - 2014 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. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +gboolean test_thread(GIOChannel *source, GIOCondition condition, gpointer data); + +static weconn_h connection = NULL; + +static int __get_service_type(void) +{ + int input, rv; + + printf("Input service type:\n"); + printf("1: W_SERVICE_TYPE_BT_HFP\n"); + printf("2: W_SERVICE_TYPE_BT_SPP\n"); + printf("3: W_SERVICE_TYPE_BT_PAN\n"); + printf("4: W_SERVICE_TYPE_BT_GATT\n"); + printf("5: W_SERVICE_TYPE_CELLULAR\n"); + printf("6: W_SERVICE_TYPE_WIFI\n"); + printf("7: W_SERVICE_TYPE_WIFI_P2P\n"); + printf("8: W_SERVICE_TYPE_WIFI_ADHOC\n"); + printf("9: W_SERVICE_TYPE_ETHERNET\n"); + printf("11: W_SERVICE_TYPE_HOST_TO_WEARABLE_CONNECTIVITY\n"); + printf("12: W_SERVICE_TYPE_INTERNET_CONNECTIVITY\n"); + + rv = scanf("%d", &input); + if (rv < 0) + return -EINVAL; + + /* convert the real enum of service type */ + printf("service type %02X\n", input); + if (input < 10) { + input = input + 0x10; + if (W_SERVICE_TYPE_BT_HFP <= input && input <= W_SERVICE_TYPE_ETHERNET) + return input; + } else { + input = input - 10; + if (W_SERVICE_TYPE_HOST_TO_WEARABLE_CONNECTIVITY == input || + W_SERVICE_TYPE_INTERNET_CONNECTIVITY == input) + return input; + } + + return -EINVAL; +} + +static void __print_service_state(weconn_service_state_e state) +{ + switch (state) { + case W_SERVICE_STATE_DISCONNECTED: + printf("Disconnected\n"); + break; + case W_SERVICE_STATE_CONNECTING: + printf("Connecting\n"); + break; + case W_SERVICE_STATE_CONNECTED: + printf("Connected\n"); + break; + case W_SERVICE_STATE_DISCONNECTING: + printf("Disconnected\n"); + break; + default: + printf("Unknown\n"); + } +} + +static int __get_device_type(void) +{ + int input, rv; + + printf("Input device type:\n"); + printf("1: W_DEVICE_TYPE_BT\n"); + printf("2: W_DEVICE_TYPE_CELLULAR\n"); + printf("3: W_DEVICE_TYPE_WIFI\n"); + printf("4: W_DEVICE_TYPE_WIFI_P2P\n"); + printf("5: W_DEVICE_TYPE_WIFI_ADHOC\n"); + printf("6: W_DEVICE_TYPE_ETHERNET\n"); + + rv = scanf("%d", &input); + if (rv < 0) + return -EINVAL; + + printf("device type %d\n", input); + if (W_DEVICE_TYPE_BT <= input && input <= W_DEVICE_TYPE_ETHERNET) + return input; + + return -EINVAL; +} + +static void __print_device_state(weconn_service_state_e state) +{ + switch (state) { + case W_DEVICE_STATE_DISABLED: + printf("Disabled\n"); + break; + case W_DEVICE_STATE_ENABLED: + printf("Enabled\n"); + break; + default: + printf("Unknown\n"); + } +} + +static int test_register_client(void) +{ + int err = weconn_create(&connection); + + if (err < 0) { + printf("Client registration failed [%d]\n", err); + return -EIO; + } + + printf("Client registered successfully\n"); + + return 0; +} + +static int test_deregister_client(void) +{ + int err = 0; + + if (connection != NULL) + err = weconn_destroy(connection); + else { + printf("Cannot de-register: handle is NULL\n"); + err = -EINVAL; + } + + if (err < 0) { + printf("Client de-registration fail [%d]\n", err); + return err; + } + + connection = NULL; + + printf("Client de-registration success\n"); + + return 0; +} + +static int test_get_service_state(void) +{ + int type, err; + weconn_service_state_e state; + + type = __get_service_type(); + + err = weconn_get_service_state(connection, type, &state); + if (err < 0) { + printf("Failed to get service state [%d]", err); + return err; + } + + __print_service_state(state); + return 0; +} + +static int test_get_device_state(void) +{ + int type, err; + weconn_device_state_e state; + + type = __get_device_type(); + + err = weconn_get_device_state(connection, type, &state); + if (err < 0) { + printf("Failed to get device state [%d]", err); + return err; + } + + __print_device_state(state); + return 0; +} + +static void __test_connect_service_callback(int result, void *user_data) +{ + if (result == 0) + printf("Succeed to connect\n"); + else + printf("Failed to connect [%d]\n", result); +} + +static void __test_disconnect_service_callback(int result, void *user_data) +{ + if (result == 0) + printf("Succeed to disconnect\n"); + else + printf("Failed to disconnect [%d]\n", result); +} + +static int test_connect_service(void) +{ + int err = 0; + weconn_service_type_e type; + + type = __get_service_type(); + + err = weconn_connect_service(connection, type, + __test_connect_service_callback, NULL); + if (err < 0) { + printf("Failed to connect service [%d]", err); + return err; + } + + return err; +} + +static int test_disconnect_service(void) +{ + int err = 0; + weconn_service_type_e type; + + type = __get_service_type(); + + err = weconn_disconnect_service(connection, type, + __test_disconnect_service_callback, NULL); + if (err < 0) { + printf("Failed to get device state [%d]", err); + return err; + } + + return err; +} + +static void _test_wearable_service_state_changed_cb( + weconn_service_state_e state, void *user_data) +{ + printf("wearable service state changed = %d\n", state); +} + +static void _test_internet_service_state_changed_cb( + weconn_service_state_e state, void *user_data) +{ + printf("internet service state changed = %d\n", state); +} + +static int test_register_service_connection_cb(void) +{ + int err = 0; + weconn_service_type_e type; + + type = __get_service_type(); + + switch (type) { + case W_SERVICE_TYPE_HOST_TO_WEARABLE_CONNECTIVITY: + err = weconn_set_service_state_change_cb(connection, + _test_wearable_service_state_changed_cb, type, NULL); + if (err < 0) { + printf("Register_service_connectionn failed [%d]\n", err); + return -EIO; + } + break; + case W_SERVICE_TYPE_INTERNET_CONNECTIVITY : + err = weconn_set_service_state_change_cb(connection, + _test_internet_service_state_changed_cb, type, NULL); + if (err < 0) { + printf("Register_service_connectionn failed [%d]\n", err); + return -EIO; + } + break; + case W_SERVICE_TYPE_BT_HFP: + case W_SERVICE_TYPE_BT_SPP: + case W_SERVICE_TYPE_BT_PAN: + case W_SERVICE_TYPE_BT_GATT: + case W_SERVICE_TYPE_CELLULAR: + case W_SERVICE_TYPE_WIFI: + case W_SERVICE_TYPE_WIFI_P2P: + case W_SERVICE_TYPE_WIFI_ADHOC: + case W_SERVICE_TYPE_ETHERNET: + printf("Not support service type [%d]\n", type); + return -EINVAL; + } + + printf("Register_service_connectionn successfully\n"); + + return 0; +} + +static int test_unregister_service_connection_cb(void) +{ + int err = 0; + weconn_service_type_e type; + + type = __get_service_type(); + + if (type > W_SERVICE_TYPE_INTERNET_CONNECTIVITY) + printf("Not support service type [%d]\n", type); + + err = weconn_unset_service_state_change_cb(connection, type); + if (err < 0) { + printf("Unregister_service_connectionn failed [%d]\n", err); + return -EIO; + } + + printf("Unregister_service_connectionn successfully\n"); + + return 0; +} + +static void __print_menu(void) +{ + printf("\nWearable Connection API Test App\n\n"); + printf("Options..\n"); + printf("1 - Create Handle\n"); + printf("2 - Destroy Handle(unset callbacks automatically)\n"); + printf("3 - Get wearable service state\n"); + printf("4 - Get wearable device state\n"); + printf("5 - Connect wearable service\n"); + printf("6 - Disconnect wearable service\n"); + printf("7 - Register wearable service connection callback\n"); + printf("8 - Unregister wearable service connection callback\n"); + printf("0 - Exit \n"); + printf("ENTER - Show options menu.......\n"); +} + +int main(int argc, char **argv) +{ + GMainLoop *mainloop; + GIOChannel *channel; + mainloop = g_main_loop_new (NULL, FALSE); + + channel = g_io_channel_unix_new(0); + g_io_add_watch(channel, (G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL), + test_thread,NULL ); + + __print_menu(); + + g_main_loop_run (mainloop); + + return 0; +} + +gboolean test_thread(GIOChannel *source, GIOCondition condition, gpointer data) +{ + int rv = 0; + char cmds[100]; + char cmd; + + memset(cmds, '\0', 100); + rv = read(0, cmds, 100); + + cmd = *cmds; + + if (cmd == '\n' || cmd == '\r'){ + __print_menu(); + + return TRUE; + } + + switch (cmd) { + case '0': + if (connection != NULL) + test_deregister_client(); + + exit(1); + case '1': + rv = test_register_client(); + break; + case '2': + rv = test_deregister_client(); + break; + case '3': + rv = test_get_service_state(); + break; + case '4': + rv = test_get_device_state(); + break; + case '5': + rv = test_connect_service(); + break; + case '6': + rv = test_disconnect_service(); + break; + case '7': + rv = test_register_service_connection_cb(); + break; + case '8': + rv = test_unregister_service_connection_cb(); + break; + default: + break; + } + + if (rv == 0) + printf("Operation succeeded!\n\n"); + else + printf("Operation failed!\n\n"); + + return TRUE; +} diff --git a/weconn.manifest b/weconn.manifest new file mode 100644 index 0000000..09c7da4 --- /dev/null +++ b/weconn.manifest @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +