/* Copyright 2016 ALPS ELECTRIC CO., LTD. * * 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 "bluetooth-agent.h" #ifdef AGENT_THREAD #include static GMainLoop *agentLoop = NULL; #endif static GDBusConnection *system_conn = NULL; static gboolean agent_registered = FALSE; static const char *agent_capability = NULL; static Agent_RegisterCallback_t agent_RegisterCallback = { 0 }; static AGENTOrgBluezAgent1 * agnet_interface = NULL; static struct Agent_Node *agent_event = NULL; static void agent_event_auto_release(struct Agent_Node *event) { if (NULL == event ){ return; } g_dbus_method_invocation_return_dbus_error (event->invocation, ERROR_BLUEZ_REJECT, ""); g_free(event); } /* * Store the agent event */ static void agent_event_new(AGENTOrgBluezAgent1 *object, GDBusMethodInvocation *invocation, int type) { LOGD("\n"); if(NULL !=agent_event ) { agent_event_auto_release(agent_event); } agent_event = g_malloc0(sizeof(struct Agent_Node)); agent_event->type = type; agent_event->object = object; agent_event->invocation = invocation; } /* * agent api * * Methods : void Release() * This method gets called when the service daemon * unregisters the agent. An agent can use it to do * cleanup tasks. There is no need to unregister the * agent, because when this method gets called it has * already been unregistered. */ static gboolean on_handle_Release (AGENTOrgBluezAgent1 *object, GDBusMethodInvocation *invocation, gpointer user_data) { LOGD("\n"); //gboolean ret = FALSE; if (NULL != agent_RegisterCallback.agent_Release) { agent_RegisterCallback.agent_Release(); } agent_org_bluez_agent1_complete_release(object, invocation); g_dbus_interface_skeleton_unexport( G_DBUS_INTERFACE_SKELETON(agnet_interface)); g_object_unref(agnet_interface); agent_registered = FALSE; #ifdef AGENT_THREAD if(agentLoop){ g_main_loop_quit(agentLoop); } #endif return TRUE; } /* * agent api * Methods : string RequestPinCode(object device) * * This method gets called when the service daemon * needs to get the passkey for an authentication. * * The return value should be a string of 1-16 characters * length. The string can be alphanumeric. */ static gboolean on_handle_RequestPinCode (AGENTOrgBluezAgent1 *object, GDBusMethodInvocation *invocation, const gchar *device, gpointer user_data) { LOGD("device:%s.\n",device); gboolean ret = FALSE; const gchar *error = ERROR_BLUEZ_REJECT; gchar *response; if (NULL != agent_RegisterCallback.agent_RequestPinCode) { ret = agent_RegisterCallback.agent_RequestPinCode (device, &response, &error); } if (TRUE == ret) { agent_org_bluez_agent1_complete_request_pin_code(object, invocation, response); g_free(response); } else { g_dbus_method_invocation_return_dbus_error (invocation, error, ""); } return TRUE; } /* * agent api * Methods : void DisplayPinCode(object device, string pincode) * * This method gets called when the service daemon * needs to display a pincode for an authentication. */ static gboolean on_handle_DisplayPinCode (AGENTOrgBluezAgent1 *object, GDBusMethodInvocation *invocation, const gchar *device, const gchar *pincode, gpointer user_data) { LOGD("device:%s,pincode:%s.\n",device, pincode); gboolean ret = FALSE; const gchar *error = ERROR_BLUEZ_REJECT; if (NULL != agent_RegisterCallback.agent_DisplayPinCode) { ret = agent_RegisterCallback.agent_DisplayPinCode(device, pincode, &error); } if (TRUE == ret) { agent_org_bluez_agent1_complete_display_pin_code(object, invocation); } else { g_dbus_method_invocation_return_dbus_error (invocation, error, ""); } return TRUE; } /* * agent api * Methods : uint32 RequestPasskey(object device) * * This method gets called when the service daemon * needs to get the passkey for an authentication. * * The return value should be a numeric value * between 0-999999. */ static gboolean on_handle_RequestPasskey (AGENTOrgBluezAgent1 *object, GDBusMethodInvocation *invocation, const gchar *device, gpointer user_data) { LOGD("device:%s.\n",device); gboolean ret = FALSE; const gchar *error = ERROR_BLUEZ_REJECT; guint passkey; if (NULL != agent_RegisterCallback.agent_RequestPasskey) { ret = agent_RegisterCallback.agent_RequestPasskey(device, &passkey, &error); } if (TRUE == ret) { agent_org_bluez_agent1_complete_request_passkey(object, invocation, passkey); } else { g_dbus_method_invocation_return_dbus_error (invocation, error, ""); } return TRUE; } /* * agent api * Methods : void DisplayPasskey(object device, uint32 passkey, uint16 entered) * * This method gets called when the service daemon * needs to display a passkey for an authentication. * * The entered parameter indicates the number of already * typed keys on the remote side. */ static gboolean on_handle_DisplayPasskey (AGENTOrgBluezAgent1 *object, GDBusMethodInvocation *invocation, const gchar *device, guint passkey, guint16 entered, gpointer user_data) { LOGD("device:%s,passkey:%d,entered:%d.\n",device, passkey, entered); //gboolean ret = FALSE; if (NULL != agent_RegisterCallback.agent_DisplayPasskey) { agent_RegisterCallback.agent_DisplayPasskey(device, passkey, entered); } agent_org_bluez_agent1_complete_display_passkey(object, invocation); return TRUE; } /* * agent api * Methods : void RequestConfirmation(object device, uint32 passkey) * * This method gets called when the service daemon * needs to confirm a passkey for an authentication. * * To confirm the value it should return an empty reply * or an error in case the passkey is invalid. */ static gboolean on_handle_RequestConfirmation (AGENTOrgBluezAgent1 *object, GDBusMethodInvocation *invocation, const gchar *device, guint passkey, gpointer user_data) { LOGD("device:%s,passkey:%d.\n", device, passkey); gboolean ret = FALSE; const gchar *error = ERROR_BLUEZ_REJECT; if (NULL != agent_RegisterCallback.agent_RequestConfirmation) { ret = agent_RegisterCallback.agent_RequestConfirmation(device, passkey, &error); } LOGD("ret %d\n",ret); if (TRUE == ret) { agent_event_new(object, invocation,REQUEST_CONFIRMATION); //agent_org_bluez_agent1_complete_request_confirmation(object, // invocation); } else { g_dbus_method_invocation_return_dbus_error (invocation, error, ""); } return TRUE; } /* * agent api * Methods : void RequestAuthorization(object device) * * This method gets called to request the user to * authorize an incoming pairing attempt which * would in other circumstances trigger the just-works * model. */ static gboolean on_handle_RequestAuthorization (AGENTOrgBluezAgent1 *object, GDBusMethodInvocation *invocation, const gchar *device, gpointer user_data) { LOGD("device:%s.\n",device); gboolean ret = FALSE; const gchar *error = ERROR_BLUEZ_REJECT; if (NULL != agent_RegisterCallback.agent_RequestAuthorization) { ret = agent_RegisterCallback.agent_RequestAuthorization(device,&error); } if (TRUE == ret) { agent_org_bluez_agent1_complete_request_authorization(object, invocation); } else { g_dbus_method_invocation_return_dbus_error (invocation, error, ""); } return TRUE; } /* * agent api * Methods : void AuthorizeService(object device, string uuid) * * This method gets called when the service daemon * needs to authorize a connection/service request. */ static gboolean on_handle_AuthorizeService (AGENTOrgBluezAgent1 *object, GDBusMethodInvocation *invocation, const gchar *device, const gchar *uuid, gpointer user_data) { LOGD("device:%s.uuid:%s\n",device,uuid); gboolean ret = FALSE; const gchar *error = ERROR_BLUEZ_REJECT; if (NULL != agent_RegisterCallback.agent_AuthorizeService) { ret = agent_RegisterCallback.agent_AuthorizeService(device, uuid, &error); } if (TRUE == ret) { agent_org_bluez_agent1_complete_authorize_service(object, invocation); } else { g_dbus_method_invocation_return_dbus_error (invocation, error, ""); } return TRUE; } /* * agent api * Methods : void Cancel() * * This method gets called to indicate that the agent * request failed before a reply was returned. */ static gboolean on_handle_Cancel (AGENTOrgBluezAgent1 *object, GDBusMethodInvocation *invocation, gpointer user_data) { LOGD("\n"); if (NULL != agent_RegisterCallback.agent_Cancel) { agent_RegisterCallback.agent_Cancel(); } agent_org_bluez_agent1_complete_cancel(object, invocation); return TRUE; } /* * agent init * init the dbus and register the agent to bluez */ static int create_and_register_agent(const char *capability) { GError *error = NULL; gboolean ret; GVariant *value; LOGD("%s\n",capability); if (agent_registered == TRUE) { LOGD("Agent is already registered\n"); return -1; } system_conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); if (error) { LOGE("errr:%s",error->message); g_error_free(error); return -1; } agent_capability = capability; agnet_interface = agent_org_bluez_agent1_skeleton_new (); g_signal_connect (agnet_interface, "handle-release", G_CALLBACK (on_handle_Release), NULL); g_signal_connect (agnet_interface, "handle-request-pin-code", G_CALLBACK (on_handle_RequestPinCode), NULL); g_signal_connect (agnet_interface, "handle-display-pin-code", G_CALLBACK (on_handle_DisplayPinCode), NULL); g_signal_connect (agnet_interface, "handle-request-passkey", G_CALLBACK (on_handle_RequestPasskey), NULL); g_signal_connect (agnet_interface, "handle-display-passkey", G_CALLBACK (on_handle_DisplayPasskey), NULL); g_signal_connect (agnet_interface, "handle-request-confirmation", G_CALLBACK (on_handle_RequestConfirmation), NULL); g_signal_connect (agnet_interface, "handle-request-authorization", G_CALLBACK (on_handle_RequestAuthorization), NULL); g_signal_connect (agnet_interface, "handle-authorize-service", G_CALLBACK (on_handle_AuthorizeService), NULL); g_signal_connect (agnet_interface, "handle-cancel", G_CALLBACK (on_handle_Cancel), NULL); ret = g_dbus_interface_skeleton_export ( G_DBUS_INTERFACE_SKELETON (agnet_interface), system_conn, AGENT_PATH, &error); if (FALSE == ret) { LOGE("errr:%s",error->message); g_error_free(error); g_object_unref(system_conn); return -1; } value = g_dbus_connection_call_sync(system_conn, BLUEZ_SERVICE, AGENT_PATH, AGENT_MANAGER_INTERFACE, "RegisterAgent", g_variant_new("(os)", AGENT_PATH, agent_capability), NULL, G_DBUS_CALL_FLAGS_NONE, DBUS_REPLY_TIMEOUT, NULL, &error); if (NULL == value) { LOGE ("RegisterAgent Err: %s", error->message); g_error_free(error); g_dbus_interface_skeleton_unexport( G_DBUS_INTERFACE_SKELETON(agnet_interface)); g_object_unref(system_conn); return -1; } g_variant_unref(value); agent_capability = NULL; agent_registered = TRUE; return 0; } #ifdef AGENT_THREAD /* * agent thread function */ static void *agent_event_loop_thread(const char *capability) { agentLoop = g_main_loop_new(NULL, FALSE); guint id; GError *error = NULL; gboolean ret; ret = create_and_register_agent(capability); if (0 == ret) { LOGD("g_main_loop_run\n"); g_main_loop_run(agentLoop); } g_main_loop_unref(agentLoop); agentLoop = NULL; LOGD("exit...\n"); } #endif /* --- PUBLIC FUNCTIONS --- */ /* * start agent. * Returns: 0 - success or other errors */ int agent_register(const char *capability) { int ret = 0; LOGD("\n"); if (agent_registered == TRUE) { LOGD("Agent is already registered\n"); return -1; } #ifdef AGENT_THREAD pthread_t thread_id; pthread_create(&thread_id, NULL, agent_event_loop_thread, capability); pthread_setname_np(thread_id, "agent"); #else ret = create_and_register_agent(capability); #endif return ret; } /* * stop agent. * Returns: 0 - success or other errors */ int stop_agent() { GError *error = NULL; GVariant *value; LOGD("\n"); if (agent_registered == FALSE) { LOGD("No agent is registered\n"); return -1; } value = g_dbus_connection_call_sync(system_conn, BLUEZ_SERVICE, AGENT_PATH, AGENT_MANAGER_INTERFACE, "UnregisterAgent", g_variant_new("(o)", AGENT_PATH ), NULL, G_DBUS_CALL_FLAGS_NONE, DBUS_REPLY_TIMEOUT, NULL, &error); if (NULL == value) { LOGE ("Error UnregisterAgent: %s", error->message); g_error_free(error); return -1; } g_dbus_interface_skeleton_unexport( G_DBUS_INTERFACE_SKELETON(agnet_interface)); g_object_unref(agnet_interface); g_object_unref(system_conn); agent_registered = FALSE; memset(&agent_RegisterCallback, 0, sizeof(Agent_RegisterCallback_t)); #ifdef AGENT_THREAD if(agentLoop){ g_main_loop_quit(agentLoop); } #endif return 0; } /* * Register Agent Callback function */ void agent_API_register(const Agent_RegisterCallback_t* pstRegisterCallback) { LOGD("\n"); if (NULL != pstRegisterCallback) { if (NULL != pstRegisterCallback->agent_Release) { agent_RegisterCallback.agent_Release = pstRegisterCallback->agent_Release; } if (NULL != pstRegisterCallback->agent_RequestPinCode) { agent_RegisterCallback.agent_RequestPinCode = pstRegisterCallback->agent_RequestPinCode; } if (NULL != pstRegisterCallback->agent_DisplayPinCode) { agent_RegisterCallback.agent_DisplayPinCode = pstRegisterCallback->agent_DisplayPinCode; } if (NULL != pstRegisterCallback->agent_RequestPasskey) { agent_RegisterCallback.agent_RequestPasskey = pstRegisterCallback->agent_RequestPasskey; } if (NULL != pstRegisterCallback->agent_DisplayPasskey) { agent_RegisterCallback.agent_DisplayPasskey = pstRegisterCallback->agent_DisplayPasskey; } if (NULL != pstRegisterCallback->agent_RequestConfirmation) { agent_RegisterCallback.agent_RequestConfirmation = pstRegisterCallback->agent_RequestConfirmation; } if (NULL != pstRegisterCallback->agent_RequestAuthorization) { agent_RegisterCallback.agent_RequestAuthorization = pstRegisterCallback->agent_RequestAuthorization; } if (NULL != pstRegisterCallback->agent_AuthorizeService) { agent_RegisterCallback.agent_AuthorizeService = pstRegisterCallback->agent_AuthorizeService; } if (NULL != pstRegisterCallback->agent_Cancel) { agent_RegisterCallback.agent_Cancel = pstRegisterCallback->agent_Cancel; } } } /* * Send the agent event "RequestConfirmation" reply */ int agent_send_confirmation(gboolean confirmation) { if (NULL == agent_event) { LOGW("Not agent event"); return -1; } LOGD("%d-%d\n", confirmation, agent_event->type); if (REQUEST_CONFIRMATION != agent_event->type) { return -1; } if (TRUE == confirmation){ agent_org_bluez_agent1_complete_request_confirmation(agent_event->object, agent_event->invocation); }else{ g_dbus_method_invocation_return_dbus_error (agent_event->invocation, ERROR_BLUEZ_REJECT, ""); } g_free(agent_event); agent_event = NULL; return 0; } /****************************** The End Of File ******************************/