Git init
[framework/connectivity/bluetooth-frwk.git] / bluetooth-frwk-agent / bluetooth-agent-cb.c
1 /*
2  * Bluetooth-frwk
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact:  Hocheol Seo <hocheol.seo@samsung.com>
7  *               Girishashok Joshi <girish.joshi@samsung.com>
8  *               Chanyeol Park <chanyeol.park@samsung.com>
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *              http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  *
22  */
23
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <string.h>
27 #include <malloc.h>
28 #include <stacktrim.h>
29 #include <syspopup_caller.h>
30
31 #include "bluetooth-agent.h"
32 #include "sc_core_agent.h"
33
34 extern struct bt_agent_appdata *app_data;
35
36 #define BT_APP_AUTHENTICATION_TIMEOUT           35
37 #define BT_APP_AUTHORIZATION_TIMEOUT            15
38
39 #define HFP_AUDIO_GATEWAY_UUID "0000111f-0000-1000-8000-00805f9b34fb"
40 #define A2DP_UUID "0000110D-0000-1000-8000-00805F9B34FB"
41
42 #define BT_PIN_MAX_LENGTH 16
43 #define BT_PASSKEY_MAX_LENGTH 6
44
45 #define BT_AGENT_SYSPOPUP_TIMEOUT_FOR_MULTIPLE_POPUPS 200
46
47 static int __bt_agent_is_auto_response(uint32_t dev_class, const gchar *address);
48 static const int __bt_agent_is_hid_keyboard(uint32_t dev_class);
49
50 static void __bt_agent_release_memory(void)
51 {
52         /* Release Malloc Memory*/
53         malloc_trim(0);
54
55         /* Release Stack Memory*/
56         stack_trim();
57 }
58
59 static void __bt_agent_show_confirm_mode_request(const char *mode, const char *sender,
60                                                gboolean need_popup, void *data)
61 {
62         bt_agent_changed_mode_type_t changed_mode = 0;
63
64         if (strcasecmp(mode, "enable") == 0) {
65                 changed_mode = BT_AGENT_CHANGED_MODE_ENABLE;
66         } else if (strcasecmp(mode, "disable") == 0) {
67                 changed_mode = BT_AGENT_CHANGED_MODE_DISABLE;
68         } else {
69                 sc_core_agent_reply_adapter_enable(_sc_core_agent_get_proxy(), changed_mode,
70                                                    SC_CORE_AGENT_REJECT, NULL);
71                 return;
72         }
73
74         sc_core_agent_reply_adapter_enable(_sc_core_agent_get_proxy(), changed_mode,
75                                            SC_CORE_AGENT_ACCEPT, NULL);
76 }
77
78 static gboolean __bt_agent_system_popup_timer_cb(gpointer user_data)
79 {
80         int ret = 0;
81         bundle *b = (bundle *) user_data;
82
83         if (NULL == b) {
84                 DBG("There is some problem with the user data..popup can not be created\n");
85                 return FALSE;
86         }
87         ret = syspopup_launch("bt-syspopup", b);
88
89         if (0 > ret) {
90                 DBG("Sorry Can not launch popup\n");
91                 return TRUE;
92         } else {
93                 DBG("Hurray Popup launched \n");
94                 bundle_free(b);
95                 return FALSE;
96         }
97 }
98
99 int _bt_agent_launch_system_popup(bt_agent_event_type_t event_type, const char *device_name,
100                                  char *passkey, const char *filename)
101 {
102         int ret = 0;
103         bundle *b = NULL;
104         char event_str[BT_MAX_EVENT_STR_LENGTH + 1] = { 0 };
105
106         DBG("_bt_agent_launch_system_popup +");
107
108         b = bundle_create();
109
110         bundle_add(b, "device-name", device_name);
111         bundle_add(b, "passkey", passkey);
112         bundle_add(b, "file", filename);
113
114         switch (event_type) {
115         case BT_AGENT_EVENT_PIN_REQUEST:
116                 strncpy(event_str, "pin-request", BT_MAX_EVENT_STR_LENGTH);
117                 break;
118
119         case BT_AGENT_EVENT_PASSKEY_CONFIRM_REQUEST:
120                 strncpy(event_str, "passkey-confirm-request", BT_MAX_EVENT_STR_LENGTH);
121                 break;
122
123         case BT_AGENT_EVENT_PASSKEY_REQUEST:
124                 strncpy(event_str, "passkey-request", BT_MAX_EVENT_STR_LENGTH);
125                 break;
126
127         case BT_AGENT_EVENT_PASSKEY_DISPLAY_REQUEST:
128                 strncpy(event_str, "passkey-display-request", BT_MAX_EVENT_STR_LENGTH);
129                 break;
130
131         case BT_AGENT_EVENT_AUTHORIZE_REQUEST:
132                 strncpy(event_str, "authorize-request", BT_MAX_EVENT_STR_LENGTH);
133                 break;
134
135         case BT_AGENT_EVENT_CONFIRM_MODE_REQUEST:
136                 strncpy(event_str, "confirm-mode-request", BT_MAX_EVENT_STR_LENGTH);
137                 break;
138
139         case BT_AGENT_EVENT_FILE_RECIEVED:
140                 strncpy(event_str, "file-recieved", BT_MAX_EVENT_STR_LENGTH);
141                 break;
142
143         case BT_AGENT_EVENT_KEYBOARD_PASSKEY_REQUEST:
144                 strncpy(event_str, "keyboard-passkey-request", BT_MAX_EVENT_STR_LENGTH);
145                 break;
146
147         case BT_AGENT_EVENT_TERMINATE:
148                 strncpy(event_str, "terminate", BT_MAX_EVENT_STR_LENGTH);
149                 break;
150
151         default:
152
153                 break;
154
155         }
156
157         bundle_add(b, "event-type", event_str);
158
159         ret = syspopup_launch("bt-syspopup", b);
160         if (0 > ret) {
161                 DBG("Popup launch failed...retry %d\n", ret);
162                 g_timeout_add(BT_AGENT_SYSPOPUP_TIMEOUT_FOR_MULTIPLE_POPUPS,
163                               (GSourceFunc) __bt_agent_system_popup_timer_cb, b);
164         } else {
165                 bundle_free(b);
166         }
167
168         DBG("_bt_agent_launch_system_popup -%d", ret);
169         return 0;
170 }
171
172 static gboolean __pincode_request(DBusGProxy *device)
173 {
174         uint32_t device_class;
175         GHashTable *hash = NULL;
176         GValue *value;
177         const gchar *address, *name;
178         GError *error = NULL;
179
180         DBG("+\n");
181
182         dbus_g_proxy_call(device, "GetProperties", &error,
183                                 G_TYPE_INVALID,
184                                 dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
185                                 &hash, G_TYPE_INVALID);
186
187         if (hash != NULL) {
188                 value = g_hash_table_lookup(hash, "Class");
189                 device_class = value ? g_value_get_uint(value) : 0;
190
191                 value = g_hash_table_lookup(hash, "Address");
192                 address = value ? g_value_get_string(value) : NULL;
193
194                 value = g_hash_table_lookup(hash, "Name");
195                 name = value ? g_value_get_string(value) : NULL;
196
197                 if (__bt_agent_is_auto_response(device_class, address)) {
198                         /* Use Fixed PIN "0000" for basic pairing*/
199                         sc_core_agent_reply_pin_code(_sc_core_agent_get_proxy(),
200                                                      SC_CORE_AGENT_ACCEPT, "0000", NULL);
201                 } else if (__bt_agent_is_hid_keyboard(device_class)) {
202                         char str_passkey[7] = { 0 };
203
204                         bt_agent_generate_passkey(str_passkey, sizeof(str_passkey));
205
206                         if (name)
207                                 _bt_agent_launch_system_popup(BT_AGENT_EVENT_KEYBOARD_PASSKEY_REQUEST,
208                                                         (const char *)name, str_passkey, NULL);
209                         else
210                                 _bt_agent_launch_system_popup(BT_AGENT_EVENT_KEYBOARD_PASSKEY_REQUEST,
211                                                         (const char *)address, str_passkey, NULL);
212                 } else {
213                         value = g_hash_table_lookup(hash, "Name");
214                         name = value ? g_value_get_string(value) : NULL;
215
216                         if (!name && !address)
217                                 sc_core_agent_reply_pin_code(_sc_core_agent_get_proxy(),
218                                                              SC_CORE_AGENT_REJECT, "", NULL);
219
220                         if (name)
221                                 _bt_agent_launch_system_popup(BT_AGENT_EVENT_PIN_REQUEST,
222                                                              (const char *)name, NULL, NULL);
223                         else
224                                 _bt_agent_launch_system_popup(BT_AGENT_EVENT_PIN_REQUEST,
225                                                              (const char *)address, NULL, NULL);
226
227                 }
228         } else {
229                 DBG("error in GetBasicProperties [%s]\n", error->message);
230                 g_error_free(error);
231                 error = NULL;
232                 sc_core_agent_reply_pin_code(_sc_core_agent_get_proxy(), SC_CORE_AGENT_REJECT, "",
233                                              NULL);
234         }
235
236         __bt_agent_release_memory();
237
238         DBG("-\n");
239
240         return TRUE;
241 }
242
243 static gboolean __passkey_request(DBusGProxy *device)
244 {
245         GHashTable *hash = NULL;
246         GValue *value;
247         const gchar *address, *name;
248         GError *error = NULL;
249
250         DBG("+\n");
251
252         dbus_g_proxy_call(device, "GetProperties", &error,
253                                 G_TYPE_INVALID,
254                                 dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
255                                 &hash, G_TYPE_INVALID);
256
257         if (hash != NULL) {
258                 value = g_hash_table_lookup(hash, "Address");
259                 address = value ? g_value_get_string(value) : NULL;
260
261                 value = g_hash_table_lookup(hash, "Name");
262                 name = value ? g_value_get_string(value) : NULL;
263
264                 if (!name && !address)
265                         sc_core_agent_reply_passkey(_sc_core_agent_get_proxy(),
266                                                     SC_CORE_AGENT_REJECT, "", NULL);
267
268                 if (name)
269                         _bt_agent_launch_system_popup(BT_AGENT_EVENT_PASSKEY_REQUEST,
270                                                      (const char *)name, NULL, NULL);
271                 else
272                         _bt_agent_launch_system_popup(BT_AGENT_EVENT_PASSKEY_REQUEST,
273                                                      (const char *)address, NULL, NULL);
274
275         } else {
276                 DBG("error in GetBasicProperties [%s]\n", error->message);
277                 g_error_free(error);
278                 error = NULL;
279                 sc_core_agent_reply_passkey(_sc_core_agent_get_proxy(), SC_CORE_AGENT_REJECT, "",
280                                             NULL);
281         }
282
283         __bt_agent_release_memory();
284
285         DBG("-\n");
286
287         return TRUE;
288 }
289
290 static gboolean __display_request(DBusGProxy *device, guint passkey, guint entered)
291 {
292         GHashTable *hash = NULL;
293         GValue *value;
294         const gchar *address, *name;
295         GError *error = NULL;
296
297         DBG("+\n");
298
299         dbus_g_proxy_call(device, "GetProperties", &error,
300                                 G_TYPE_INVALID,
301                                 dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
302                                 &hash, G_TYPE_INVALID);
303
304         if (hash != NULL) {
305                 value = g_hash_table_lookup(hash, "Address");
306                 address = value ? g_value_get_string(value) : NULL;
307
308                 value = g_hash_table_lookup(hash, "Name");
309                 name = value ? g_value_get_string(value) : NULL;
310         } else {
311                 DBG("error in GetBasicProperties [%s]\n", error->message);
312                 g_error_free(error);
313                 error = NULL;
314         }
315
316         __bt_agent_release_memory();
317
318         DBG("-\n");
319
320         return TRUE;
321 }
322
323 static gboolean __confirm_request(DBusGProxy *device, guint passkey)
324 {
325         GHashTable *hash = NULL;
326         GValue *value;
327         const gchar *address, *name;
328         GError *error = NULL;
329         char str_passkey[7] = { 0 };
330
331         DBG("+ passkey[%.6d]\n", passkey);
332
333         snprintf(str_passkey, sizeof(str_passkey), "%.6d", passkey);
334
335         dbus_g_proxy_call(device, "GetProperties", &error,
336                                 G_TYPE_INVALID,
337                                 dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
338                                 &hash, G_TYPE_INVALID);
339
340         if (hash != NULL) {
341                 value = g_hash_table_lookup(hash, "Address");
342                 address = value ? g_value_get_string(value) : NULL;
343
344                 value = g_hash_table_lookup(hash, "Name");
345                 name = value ? g_value_get_string(value) : NULL;
346
347                 if (name != NULL)
348                         _bt_agent_launch_system_popup(BT_AGENT_EVENT_PASSKEY_CONFIRM_REQUEST,
349                                                      (const char *)name, str_passkey, NULL);
350                 else if (address != NULL)
351                         _bt_agent_launch_system_popup(BT_AGENT_EVENT_PASSKEY_CONFIRM_REQUEST,
352                                                      (const char *)address, str_passkey, NULL);
353                 else
354                         sc_core_agent_reply_confirmation(_sc_core_agent_get_proxy(),
355                                                          SC_CORE_AGENT_REJECT, NULL);
356         } else {
357                 DBG("error in GetBasicProperties [%s]\n", error->message);
358                 g_error_free(error);
359                 error = NULL;
360                 sc_core_agent_reply_confirmation(_sc_core_agent_get_proxy(), SC_CORE_AGENT_REJECT,
361                                                  NULL);
362         }
363
364         __bt_agent_release_memory();
365
366         DBG("-\n");
367
368         return TRUE;
369 }
370
371 static gboolean __pairing_cancel_request(const char *address)
372 {
373         DBG("On Going Pairing is cancelled by remote\n");
374
375         sc_core_agent_reply_pin_code(_sc_core_agent_get_proxy(), SC_CORE_AGENT_CANCEL, "", NULL);
376
377         _bt_agent_launch_system_popup(BT_AGENT_EVENT_TERMINATE, NULL, NULL, NULL);
378
379         __bt_agent_release_memory();
380
381         return TRUE;
382 }
383
384 static gboolean __authorize_request(DBusGProxy *device, const char *uuid)
385 {
386         GHashTable *hash = NULL;
387         GValue *value;
388         const gchar *address, *name;
389         gboolean trust = FALSE;
390         GError *error = NULL;
391
392         DBG("+\n");
393
394         if (!strcasecmp(uuid, HFP_AUDIO_GATEWAY_UUID) || !strcasecmp(uuid, A2DP_UUID)) {
395                 DBG("In case of audio device(HFP,A2DP), we authorize the request [%s]\n", uuid);
396                 sc_core_agent_reply_authorize(_sc_core_agent_get_proxy(), SC_CORE_AGENT_ACCEPT,
397                                               NULL);
398
399                 return TRUE;
400         }
401
402         dbus_g_proxy_call(device, "GetProperties", &error, G_TYPE_INVALID,
403                                 dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
404                                 &hash, G_TYPE_INVALID);
405
406         if (hash != NULL) {
407                 value = g_hash_table_lookup(hash, "Address");
408                 address = value ? g_value_get_string(value) : NULL;
409
410                 value = g_hash_table_lookup(hash, "Name");
411                 name = value ? g_value_get_string(value) : NULL;
412
413                 value = g_hash_table_lookup(hash, "Trusted");
414                 trust = value ? g_value_get_boolean(value) : 0;
415
416                 DBG("Authorization request for device [%s] Service:[%s]\n", address, uuid);
417
418                 if (trust) {
419                         DBG("Trusted device, so authorize\n");
420                         sc_core_agent_reply_authorize(_sc_core_agent_get_proxy(),
421                                                       SC_CORE_AGENT_ACCEPT, NULL);
422                 } else if (name != NULL)
423                         _bt_agent_launch_system_popup(BT_AGENT_EVENT_AUTHORIZE_REQUEST,
424                                                      (const char *)name, NULL, NULL);
425                 else if (address != NULL)
426                         _bt_agent_launch_system_popup(BT_AGENT_EVENT_AUTHORIZE_REQUEST,
427                                                      (const char *)address, NULL, NULL);
428                 else
429                         sc_core_agent_reply_authorize(_sc_core_agent_get_proxy(),
430                                                       SC_CORE_AGENT_REJECT, NULL);
431         } else {
432                 DBG("error in GetBasicProperties [%s]\n", error->message);
433                 g_error_free(error);
434                 error = NULL;
435                 sc_core_agent_reply_authorize(_sc_core_agent_get_proxy(), SC_CORE_AGENT_REJECT,
436                                               NULL);
437         }
438
439         __bt_agent_release_memory();
440
441         DBG("-\n");
442
443         return TRUE;
444 }
445
446 static gboolean __authorization_cancel_request(const char *address)
447 {
448         DBG("On Going Authorization is cancelled by remote\n");
449
450         sc_core_agent_reply_authorize(_sc_core_agent_get_proxy(), SC_CORE_AGENT_CANCEL, NULL);
451
452         _bt_agent_launch_system_popup(BT_AGENT_EVENT_TERMINATE, NULL, NULL, NULL);
453
454         __bt_agent_release_memory();
455
456         return TRUE;
457 }
458
459 static gboolean __confirm_mode_request(const char *mode, const char *sender, gboolean need_popup,
460                                      void *data)
461 {
462         DBG("+\n");
463
464         if (mode != NULL) {
465                 __bt_agent_show_confirm_mode_request(mode, sender, need_popup, data);
466         } else {
467                 DBG("Wrong mode requested [%s]\n", mode);
468                 sc_core_agent_reply_adapter_enable(_sc_core_agent_get_proxy(),
469                                                    BT_AGENT_CHANGED_MODE_ENABLE,
470                                                    SC_CORE_AGENT_REJECT, NULL);
471         }
472
473         __bt_agent_release_memory();
474
475         DBG("-\n");
476
477         return TRUE;
478 }
479
480 static gboolean __ignore_auto_pairing_request(const char *address)
481 {
482         DBG("+\n");
483
484         struct bt_agent_appdata *ad = (struct bt_agent_appdata *)app_data;
485
486         if (address == NULL)
487                 return FALSE;
488
489         /* To input the pin code, if headset does not have '0000' pin code */
490         ad->ignore_auto_pairing = 1;
491         memset(ad->bonding_addr, 0x00, BT_AGENT_ADDR_SIZE + 1);
492         strncpy(ad->bonding_addr, address, BT_AGENT_ADDR_SIZE);
493
494         DBG("-\n");
495
496         return TRUE;
497 }
498
499 void _bt_agent_register(DBusGProxy *adapter_proxy)
500 {
501         SC_CORE_AGENT_FUNC_CB func_cb = { 0 };
502
503         func_cb.pincode_func = __pincode_request;
504         func_cb.display_func = __display_request;
505         func_cb.passkey_func = __passkey_request;
506         func_cb.confirm_func = __confirm_request;
507         func_cb.authorize_func = __authorize_request;
508         func_cb.pairing_cancel_func = __pairing_cancel_request;
509         func_cb.authorization_cancel_func = __authorization_cancel_request;
510         func_cb.confirm_mode_func = __confirm_mode_request;
511         func_cb.ignore_auto_pairing_func = __ignore_auto_pairing_request;
512
513         if (_sc_core_agent_add(adapter_proxy, &func_cb) < 0) {
514                 if (adapter_proxy == NULL) {
515                         return;
516                 }
517                 ERR("Agent register failed, Agent finish.\n");
518         }
519
520         DBG("Agent registered.\n");
521 }
522
523 static const int __bt_agent_is_hid_keyboard(uint32_t dev_class)
524 {
525         int is_keyboard = 0;
526
527         switch ((dev_class & 0x1f00) >> 8) {
528         case 0x05:
529                 switch ((dev_class & 0xc0) >> 6) {
530                 case 0x01:
531                         /* input-keyboard";*/
532                         is_keyboard = 1;
533                         break;
534                 }
535                 break;
536         }
537
538         DBG("is_keyboard: %d\n", is_keyboard);
539
540         return is_keyboard;
541 }
542
543 static int __bt_agent_is_auto_response(uint32_t dev_class, const gchar *address)
544 {
545         DBG("bt_agent_is_headset_class, %d +", dev_class);
546
547         int is_headset = 0, ret = 0;
548         struct bt_agent_appdata *ad = (struct bt_agent_appdata *)app_data;
549
550         if (address == NULL)
551                 return 0;
552
553         switch ((dev_class & 0x1f00) >> 8) {
554         case 0x04:
555                 switch ((dev_class & 0xfc) >> 2) {
556                 case 0x01:
557                 case 0x02:
558                         /* Headset */
559                         is_headset = 1;
560                         break;
561                 case 0x06:
562                         /* Headphone */
563                         is_headset = 1;
564                         break;
565                 case 0x0b:      /* VCR */
566                 case 0x0c:      /* Video Camera */
567                 case 0x0d:      /* Camcorder */
568                         break;
569                 default:
570                         /* Other audio device */
571                         is_headset = 1;
572                         break;
573                 }
574                 break;
575         }
576
577         if (is_headset) {
578                 if (ad->ignore_auto_pairing == 0 || (strcmp(address, ad->bonding_addr) != 0))
579                         ret = 1;
580         }
581
582         ad->ignore_auto_pairing = 0;
583         memset(ad->bonding_addr, 0x00, BT_AGENT_ADDR_SIZE + 1);
584
585         return ret;
586 }
587
588 int bt_agent_generate_passkey(char *passkey, int size)
589 {
590         int i = 0;
591         int random_fd = 0;
592         unsigned int value = 0;
593
594         if (passkey == NULL)
595                 return -1;
596
597         if (size <= 0)
598                 return -1;
599
600         random_fd = open("/dev/urandom", O_RDONLY);
601
602         if (random_fd < 0)
603                 return -1;
604
605         for (i = 0; i < size - 1; i++) {
606                 read(random_fd, &value, sizeof(value));
607                 passkey[i] = '0' + (value % 10);
608         }
609
610         close(random_fd);
611
612         passkey[size - 1] = '\0';
613
614         DBG("passkey: %s", passkey);
615
616         return 0;
617 }