bluetooth-client: don't try to dereference potential NULL while checking for the...
[profile/ivi/speech-recognition.git] / src / plugins / bluetooth-client / dbusif.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #include <murphy/common/debug.h>
6 #include <murphy/common/dbus-libdbus.h>
7 #include <murphy/common/hashtbl.h>
8 #include <murphy/common/list.h>
9 #include <murphy/common/utils.h>
10
11 #include "dbusif.h"
12 #include "clients.h"
13 #include "pulseif.h"
14
15 #define MAKE_DBUS_VERSION(major, minor, patch)  \
16     (((major) << 16) | ((minor) << 8) | (patch))
17
18 struct dbusif_s {
19     const char *bustype;
20     mrp_dbus_t *dbus;
21     mrp_list_hook_t modems;
22 };
23
24 static int mrp_dbus_is_basic_type(mrp_dbus_type_t t)
25 {
26     return t != MRP_DBUS_TYPE_ARRAY && t != MRP_DBUS_TYPE_VARIANT &&
27            t != MRP_DBUS_TYPE_STRUCT && t != MRP_DBUS_TYPE_DICT_ENTRY &&
28            t != MRP_DBUS_TYPE_INVALID;
29 }
30
31 static const char * mrp_dbus_type_to_string(mrp_dbus_type_t t)
32 {
33     static char type_str[2] = "";
34
35     type_str[0] = t;
36
37     return type_str;
38 }
39
40 static modem_t *reference_modem(modem_t *modem)
41 {
42     if (modem && modem->refcnt >= 0)
43         modem->refcnt++;
44
45     return modem;
46 }
47
48 static void unreference_modem(modem_t *modem)
49 {
50     device_t *dev = NULL;
51
52     if (modem) {
53         if (modem->refcnt > 1)
54             modem->refcnt--;
55         else {
56             mrp_log_info("remove bluetooth modem '%s' @ %s (paths %s)",
57                          modem->name, modem->addr, modem->path);
58
59             if ((dev = modem->device)) {
60                 modem->device = NULL;
61                 clients_remove_device(dev);
62             }
63
64             mrp_list_delete(&modem->link);
65
66             mrp_free((void *)modem->path);
67             mrp_free((void *)modem->name);
68             mrp_free((void *)modem->addr);
69
70             mrp_free((void *)modem);
71         }
72     }
73 }
74
75 static modem_t *find_modem_by_path(context_t *ctx, const char *path)
76 {
77     modem_t *modem = NULL;
78     dbusif_t *dbusif = NULL;
79     mrp_list_hook_t *entry, *n;
80
81     if (!ctx || !path || !(dbusif = ctx->dbusif))
82         return NULL;
83
84     mrp_list_foreach(&dbusif->modems, entry, n) {
85         modem = mrp_list_entry(entry, modem_t, link);
86         if (!strcmp(path, modem->path))
87             return modem;
88     }
89
90     return NULL;
91 }
92
93 static modem_t *create_modem(context_t *ctx,
94                              const char *path,
95                              const char *name,
96                              const char *addr)
97 {
98     modem_t *modem = NULL;
99     dbusif_t *dbusif = NULL;
100
101     if (!ctx || !path || !addr || !(dbusif = ctx->dbusif))
102         return NULL;
103
104     if (find_modem_by_path(ctx, path))
105         return NULL;
106
107     if (!(modem = mrp_allocz(sizeof(modem_t))))
108         return NULL;
109
110     modem->path = mrp_strdup(path);
111     modem->name = mrp_strdup(name ? name : "<unknown>");
112     modem->addr = mrp_strdup(addr);
113     modem->ctx = ctx;
114
115     reference_modem(modem);
116
117     mrp_list_prepend(&dbusif->modems, &modem->link);
118
119     return modem;
120 }
121
122 static void destroy_modem(modem_t *modem)
123 {
124     context_t *ctx;
125     dbusif_t *dbusif;
126
127     if (modem && (ctx = modem->ctx) && (dbusif = ctx->dbusif)) {
128         mrp_list_delete(&modem->link);
129         unreference_modem(modem);
130     }
131 }
132
133 typedef struct {
134   mrp_dbus_type_t type;
135   void *value;
136   size_t n_values; /* for arrays */
137 } property_info_t;
138
139 static property_info_t * property_info_new(mrp_dbus_type_t type)
140 {
141   property_info_t *p = NULL;
142
143   if ((p = mrp_allocz(sizeof(*p)))) {
144     p->type = type;
145     p->value = 0;
146     p->n_values = 0;
147   }
148
149   return p;
150 }
151
152 static void property_hash_free(void *key, void *object)
153 {
154   property_info_t *p = object;
155
156   MRP_UNUSED(key);
157
158   if (p)
159     mrp_free(p);
160 }
161
162 static int parse_property_value_basic(mrp_dbus_msg_t *msg,
163                                       const char *name,
164                                       mrp_dbus_type_t type,
165                                       void *value_out)
166 {
167     const char *n = NULL;
168
169     if (!name || !value_out || !mrp_dbus_is_basic_type(type))
170       return FALSE;
171
172     if (mrp_dbus_msg_arg_type(msg, NULL) != MRP_DBUS_TYPE_STRING)
173         return FALSE;
174
175     mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &n);
176
177     if (strcmp(n, name))
178       return FALSE;
179
180     if (mrp_dbus_msg_arg_type(msg, NULL) != MRP_DBUS_TYPE_VARIANT)
181         return FALSE;
182
183     mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_VARIANT,
184                                  mrp_dbus_type_to_string(type));
185
186     mrp_dbus_msg_read_basic(msg, type, value_out);
187
188     mrp_dbus_msg_exit_container(msg); /* v */
189
190     return TRUE;
191 }
192
193 static void parse_propertites(mrp_dbus_msg_t *msg,
194                               mrp_htbl_t *prop_tbl,
195                               size_t size)
196 {
197     size_t n_found = 0;
198
199     if (mrp_dbus_msg_arg_type(msg, NULL) != MRP_DBUS_TYPE_ARRAY)
200         return;
201
202     if (!mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_ARRAY, "{sv}"))
203         return;
204
205     while (mrp_dbus_msg_enter_container(msg,  MRP_DBUS_TYPE_DICT_ENTRY, NULL)) {
206         const char *prop = NULL;
207         property_info_t *p_info = NULL;
208
209         mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &prop);
210
211         p_info = (property_info_t *)mrp_htbl_lookup(prop_tbl, (void*)prop);
212         if (p_info) {
213             mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_VARIANT, NULL);
214
215             /* NOTE: Currently we handle only string arrays, should modify
216              *       based on need
217              */
218             if (p_info->type == MRP_DBUS_TYPE_ARRAY)
219                 mrp_dbus_msg_read_array(msg, MRP_DBUS_TYPE_STRING,
220                                         &p_info->value, &p_info->n_values);
221             else if (mrp_dbus_is_basic_type(p_info->type))
222                 mrp_dbus_msg_read_basic(msg, p_info->type, &p_info->value);
223
224             mrp_dbus_msg_exit_container(msg); /* v */
225             n_found++;
226         }
227
228         mrp_dbus_msg_exit_container(msg); /* "{sv}" */
229
230         /* check if found all requested properties */
231         if (n_found == size)
232           break;
233     }
234
235     mrp_dbus_msg_exit_container(msg); /* a{sv} */
236 }
237
238 static void parse_modem_properties(mrp_dbus_msg_t *msg,
239                                    const char **btaddr,
240                                    const char **btname,
241                                    uint32_t *online)
242 {
243     bool has_handsfree_interface = FALSE;
244
245     if (!msg || !btaddr || !btname || !online)
246         return;
247
248     const char **interfaces = NULL;
249     mrp_htbl_config_t conf = {
250       .nbucket = 0,
251       .nentry = 4,
252       .comp = mrp_string_comp,
253       .hash = mrp_string_hash,
254       .free = property_hash_free
255     };
256     property_info_t *info = NULL;
257     mrp_htbl_t *prop_tbl = mrp_htbl_create(&conf);
258
259     info = property_info_new(MRP_DBUS_TYPE_STRING);
260     mrp_htbl_insert(prop_tbl, "Name", info);
261     info = property_info_new(MRP_DBUS_TYPE_STRING);
262     mrp_htbl_insert(prop_tbl, "Serial", info);
263     info = property_info_new(MRP_DBUS_TYPE_BOOLEAN);
264     mrp_htbl_insert(prop_tbl, "Online", info);
265     info = property_info_new(MRP_DBUS_TYPE_ARRAY);
266     mrp_htbl_insert(prop_tbl, "Interfaces", info);
267
268     parse_propertites(msg, prop_tbl, 4);
269
270     info = (property_info_t *)mrp_htbl_lookup(prop_tbl, (void*)"Name");
271     *btname = (const char *)info->value;
272     info = (property_info_t *)mrp_htbl_lookup(prop_tbl, (void*)"Serial");
273     *btaddr = (const char *)info->value;
274     info = (property_info_t *)mrp_htbl_lookup(prop_tbl, (void*)"Online");
275     *online = *(uint32_t *)info->value;
276     info = (property_info_t *)mrp_htbl_lookup(prop_tbl, (void*)"Interfaces");
277     interfaces = (const char **)info->value;
278     if (interfaces) {
279       size_t i;
280
281       for (i = 0; i < info->n_values; i++) {
282         if (!strcmp(interfaces[i], "org.ofono.Handsfree")) {
283           has_handsfree_interface = TRUE;
284           break;
285         }
286       }
287     }
288
289     mrp_htbl_destroy(prop_tbl, TRUE);
290
291     if (!has_handsfree_interface) {
292      //   *btname = "<unknown>";
293      //   *btaddr = "<unknown>";
294         *online = FALSE;
295     }
296 }
297
298 static void modem_query_cb(mrp_dbus_t *dbus,
299                            mrp_dbus_msg_t *msg,
300                            void *user_data)
301 {
302     modem_t *modem = (modem_t *)user_data;
303     context_t *ctx = NULL;
304
305     MRP_UNUSED(dbus);
306
307     if (modem && (ctx = modem->ctx)) {
308         if (mrp_dbus_msg_type(msg) != MRP_DBUS_MESSAGE_TYPE_ERROR) {
309             const char *addr = NULL;
310             const char *name = NULL;
311             uint32_t online;
312             device_t *dev = NULL;
313
314             parse_modem_properties(msg, &addr, &name, &online);
315
316             if (!online || !name || !(dev = clients_add_device(ctx, addr)))
317                 destroy_modem(modem);
318             else {
319                 mrp_free((void *)modem->addr);
320                 mrp_free((void *)modem->name);
321
322                 modem->addr = mrp_strdup(addr);
323                 modem->name = mrp_strdup(name);
324                 modem->device = dev;
325
326                 dev->modem = modem;
327
328                 mrp_log_info("created bluetooth modem '%s' @ %s (path %s)",
329                              modem->name, modem->addr, modem->path);
330             }
331         }
332
333         unreference_modem(modem);
334     }
335 }
336
337 static void query_modem(modem_t *modem)
338 {
339     context_t *ctx = NULL;
340     dbusif_t *dbusif = NULL;
341     modem_t *ref = NULL;
342
343     if (!modem || !modem->path || !(ctx = modem->ctx) ||
344         !(dbusif = ctx->dbusif))
345         return;
346
347     if ((ref = reference_modem(modem))) {
348         mrp_dbus_msg_t *msg = mrp_dbus_msg_method_call(dbusif->dbus,
349                                                        "org.ofono",
350                                                        modem->path,
351                                                        "org.ofono.Modem",
352                                                        "GetProperties");
353         mrp_dbus_send(dbusif->dbus, "org.ofono", modem->path, "org.ofono.Modem",
354                       "GetProperties", 1000, modem_query_cb, ref, msg);
355         mrp_dbus_msg_unref(msg);
356     }
357 }
358
359 static void parse_handsfree_properties(modem_t *modem,
360                                        mrp_dbus_msg_t *msg,
361                                        hfp_state_t *state)
362 {
363     mrp_htbl_config_t conf = {
364       .nbucket = 0,
365       .nentry = 1,
366       .comp = mrp_string_comp,
367       .hash = mrp_string_hash,
368       .free = property_hash_free
369     };
370     property_info_t *info = NULL;
371     mrp_htbl_t *prop_tbl = NULL;
372
373     if (!modem || !msg || !state)
374         return;
375
376     *state = VOICE_RECOGNITION_UNKNOWN;
377
378     prop_tbl = mrp_htbl_create(&conf);
379     info = property_info_new(MRP_DBUS_TYPE_BOOLEAN);
380     mrp_htbl_insert(prop_tbl, "VoiceRecognition", info);
381
382     parse_propertites(msg, prop_tbl, 1);
383
384     *state = (uint32_t)(ptrdiff_t)((property_info_t *)mrp_htbl_lookup(
385         prop_tbl, (void*)"VoiceRecognition"))->value ?
386                   VOICE_RECOGNITION_ON : VOICE_RECOGNITION_OFF;
387
388     mrp_htbl_destroy(prop_tbl, TRUE);
389 }
390
391 static void set_modem_state(modem_t *modem, hfp_state_t state)
392 {
393     device_t *device;
394     card_t *card;
395
396     if (state == modem->state)
397         return;
398
399     modem->state = state;
400
401     if (!(device = modem->device))
402         return;
403
404     switch (state) {
405
406     case VOICE_RECOGNITION_ON:
407         mrp_log_info("bluetooth modem: setting voicerecognition on "
408                      "for modem %s", modem->addr);
409         if ((card = device->card)) {
410             pulseif_set_card_profile(card, "hfgw");
411         }
412         break;
413
414     case VOICE_RECOGNITION_OFF:
415         mrp_log_info("bluetooth modem: setting voicerecognition off "
416                      "for modem %s", modem->addr);
417         clients_stop_recognising_voice(device);
418         break;
419
420     default:
421         mrp_log_error("bluetooth plugin: attempt to set invalid stte "
422                       "for modem %s", modem->addr);
423         break;
424     }
425 }
426
427 static void handsfree_query_cb(mrp_dbus_t *dbus,
428                                mrp_dbus_msg_t *msg,
429                                void *user_data)
430 {
431     modem_t *modem = (modem_t *)user_data;
432     hfp_state_t state = VOICE_RECOGNITION_UNKNOWN;
433
434     MRP_UNUSED(dbus);
435
436     if (!modem) return;
437
438     if (mrp_dbus_msg_type(msg) != MRP_DBUS_MESSAGE_TYPE_ERROR) {
439         parse_handsfree_properties(modem, msg, &state);
440         set_modem_state(modem, state);
441      }
442
443      unreference_modem(modem);
444 }
445
446 static void query_handsfree(modem_t *modem)
447 {
448     context_t *ctx = NULL;;
449     dbusif_t *dbusif = NULL;
450     modem_t *ref = NULL;
451
452     if (!modem || !modem->path || !(ctx = modem->ctx) ||
453         !(dbusif = ctx->dbusif))
454         return;
455
456     if ((ref = reference_modem(modem))) {
457         mrp_dbus_msg_t *msg = mrp_dbus_msg_method_call(dbusif->dbus,
458                                                        "org.ofono",
459                                                        modem->path,
460                                                        "org.ofono.Handsfree",
461                                                        "GetProperties");
462         mrp_dbus_send(dbusif->dbus, "org.ofono", modem->path,
463                       "org.ofono.Handsfree", "GetProperties", 1000,
464                       handsfree_query_cb, ref, msg);
465         mrp_dbus_msg_unref(msg);
466     }
467 }
468
469 static int modem_property_changed_cb(mrp_dbus_t *dbus,
470                                      mrp_dbus_msg_t *msg,
471                                      void *user_data)
472 {
473     context_t *ctx = (context_t *)user_data;
474     const char *path = NULL;
475     uint32_t is_online = FALSE;
476
477     MRP_UNUSED(dbus);
478
479     if (!ctx || !ctx->dbusif ||
480         !(path = mrp_dbus_msg_path(msg)))
481         return FALSE;
482
483     if (parse_property_value_basic(msg, "Online", MRP_DBUS_TYPE_BOOLEAN,
484                                    &is_online)) {
485         modem_t *modem = find_modem_by_path(ctx, path);
486
487         if (is_online) {
488             if (!modem) {
489                 if ((modem = create_modem(ctx, path, "", ""))) {
490                     query_modem(modem);
491                     query_handsfree(modem);
492                 }
493             }
494         } else if (modem) {
495             destroy_modem(modem);
496         }
497     }
498
499     return FALSE;
500 }
501
502
503 static int handsfree_property_changed_cb(mrp_dbus_t *dbus,
504                                          mrp_dbus_msg_t *msg,
505                                          void *user_data)
506 {
507     context_t *ctx = (context_t *)user_data;
508     const char *path = mrp_dbus_msg_path(msg);
509     modem_t *modem = NULL;
510     uint32_t state = FALSE;
511
512     MRP_UNUSED(dbus);
513
514     if (!ctx || !ctx->dbusif || !path)
515         return FALSE;
516
517     if (!(modem = find_modem_by_path(ctx, path)))
518         return FALSE;
519
520     if (parse_property_value_basic(msg, "VoiceRecognition",
521                                    MRP_DBUS_TYPE_BOOLEAN, &state)) {
522         set_modem_state(modem,
523                         state ? VOICE_RECOGNITION_ON : VOICE_RECOGNITION_OFF);
524     }
525
526     return FALSE;
527 }
528
529 static void modem_query_all_cb(mrp_dbus_t *dbus,
530                                mrp_dbus_msg_t *msg,
531                                void *user_data)
532 {
533     context_t *ctx = (context_t *)user_data;
534     modem_t *modem;
535
536     MRP_UNUSED(dbus);
537
538     if (!ctx || !ctx->dbusif)
539         return;
540
541     if (mrp_dbus_msg_arg_type(msg, NULL) != MRP_DBUS_TYPE_ARRAY)
542         return;
543
544     mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_ARRAY, "(oa{sv})");
545
546     while (mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_STRUCT, NULL)) {
547         device_t *dev;
548         const char *path = NULL;
549         const char *addr;
550         const char *name;
551         uint32_t online;
552
553         mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_OBJECT_PATH, &path);
554
555         parse_modem_properties(msg, &addr, &name, &online);
556
557         mrp_log_info("Modem details: %s %s %s %d", path, addr, name, online);
558
559         if (path && online) {
560             if ((dev = clients_add_device(ctx, addr)) &&
561                 (modem = create_modem(ctx, path, name, addr)))
562             {
563                 modem->device = dev;
564                 dev->modem = modem;
565
566                 mrp_log_info("created bluetooth modem '%s' @ %s "
567                              "(path %s)", modem->name, modem->addr,
568                              modem->path);
569                 query_handsfree(modem);
570             }
571         }
572
573         mrp_dbus_msg_exit_container(msg); /* (oa{sv}) */
574     }
575
576     mrp_dbus_msg_exit_container(msg); /* a(oa{sv}) */
577 }
578
579 static void track_modems(context_t *ctx, bool track)
580 {
581     static const char *modem_interface = "org.ofono.Modem";
582     static const char *handsfree_interface = "org.ofono.Handsfree";
583     static const char *member = "PropertyChanged";
584     dbusif_t *dbusif = NULL;
585
586     if (!ctx || !(dbusif = ctx->dbusif))
587         return;
588
589     if (track) {
590         mrp_dbus_add_signal_handler(dbusif->dbus, NULL, NULL,
591                                     modem_interface, member,
592                                     modem_property_changed_cb, ctx);
593         mrp_dbus_add_signal_handler(dbusif->dbus, NULL, NULL,
594                                     handsfree_interface, member,
595                                     handsfree_property_changed_cb, ctx);
596         mrp_dbus_install_filter(dbusif->dbus, NULL, NULL,
597                                 modem_interface, member, NULL);
598         mrp_dbus_install_filter(dbusif->dbus, NULL, NULL,
599                                 handsfree_interface, member, NULL);
600     }
601     else {
602         mrp_dbus_del_signal_handler(dbusif->dbus, NULL, NULL,
603                                     modem_interface, member,
604                                     modem_property_changed_cb, ctx);
605         mrp_dbus_del_signal_handler(dbusif->dbus, NULL, NULL,
606                                     handsfree_interface, member,
607                                     handsfree_property_changed_cb, ctx);
608         mrp_dbus_remove_filter(dbusif->dbus, NULL, NULL,
609                                modem_interface, member, NULL);
610         mrp_dbus_remove_filter(dbusif->dbus, NULL, NULL,
611                                handsfree_interface, member, NULL);
612     }
613 }
614
615 static void query_all_modems(context_t *ctx)
616 {
617     uint32_t query_id;
618     dbusif_t *dbusif = NULL;
619
620     if (!ctx || !(dbusif = ctx->dbusif))
621         return;
622
623     query_id = mrp_dbus_call(dbusif->dbus,
624                              "org.ofono", "/", "org.ofono.Manager",
625                              "GetModems", 1000, modem_query_all_cb, ctx,
626                              MRP_DBUS_TYPE_INVALID);
627     if (query_id == 0) {
628       /* query modems failed */
629     }
630 }
631
632 static void set_property(mrp_dbus_msg_t *msg,
633                          const char *name,
634                          mrp_dbus_type_t type,
635                          void *value)
636 {
637     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_DICT_ENTRY, NULL);
638
639     mrp_dbus_msg_append_basic(msg, DBUS_TYPE_STRING, (void*)name);
640
641     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_VARIANT, NULL);
642     mrp_dbus_msg_append_basic(msg, (char)type, value);
643     mrp_dbus_msg_close_container(msg); /* variant */
644
645     mrp_dbus_msg_close_container(msg); /* dictionary */
646 }
647
648 int dbusif_create(context_t *ctx, mrp_mainloop_t *ml)
649 {
650     dbusif_t *dbusif;
651
652     if (!(dbusif = mrp_allocz(sizeof(dbusif_t))))
653         return -1;
654
655     dbusif->bustype = mrp_strdup("system");
656     dbusif->dbus = mrp_dbus_get(ml, dbusif->bustype, NULL);
657
658     if (!dbusif->dbus) {
659         mrp_log_error("bluetooth voice recognition plugin: "
660                       "failed to obtain DBus");
661         mrp_free(dbusif);
662         return -1;
663     }
664
665     mrp_list_init(&dbusif->modems);
666
667     ctx->dbusif = dbusif;
668
669     return 0;
670 }
671
672 void dbusif_destroy(context_t *ctx)
673 {
674     dbusif_t *dbusif;
675
676     if (ctx && (dbusif = ctx->dbusif)) {
677         ctx->dbusif = NULL;
678
679         mrp_dbus_unref(dbusif->dbus);
680
681         mrp_free((void *)dbusif->bustype);
682         mrp_free((void *)dbusif);
683     }
684 }
685
686 int dbusif_start(context_t *ctx)
687 {
688     track_modems(ctx, TRUE);
689     query_all_modems(ctx);
690     return 0;
691 }
692
693 void dbusif_stop(context_t *ctx)
694 {
695     dbusif_t *dbusif;
696     modem_t *modem;
697     mrp_list_hook_t *entry, *n;
698
699     track_modems(ctx, FALSE);
700
701     if (ctx && (dbusif = ctx->dbusif)) {
702         mrp_list_foreach(&dbusif->modems, entry, n) {
703             modem = mrp_list_entry(entry, modem_t, link);
704             destroy_modem(modem);
705         }
706     }
707 }
708
709 int dbusif_set_voice_recognition(modem_t *modem, hfp_state_t state)
710 {
711     context_t *ctx = NULL;
712     dbusif_t *dbusif = NULL;;
713     mrp_dbus_msg_t *msg = NULL;
714     uint32_t value;
715
716     if (!modem || !modem->path || !(ctx = modem->ctx) ||
717         !(dbusif = ctx->dbusif))
718         return -1;
719
720     switch (state) {
721     case VOICE_RECOGNITION_ON:   value = TRUE;    break;
722     case VOICE_RECOGNITION_OFF:  value = FALSE;   break;
723     default:                     /* invalid */    return -1;
724     }
725
726     msg = mrp_dbus_msg_method_call(dbusif->dbus, "org.ofono",
727                                    modem->path,
728                                    "org.ofono.Handsfree",
729                                    "SetProperty");
730
731     if (!msg)
732         return -1;
733
734     set_property(msg, "VoiceRecognition", MRP_DBUS_TYPE_BOOLEAN, &value);
735
736     mrp_dbus_send_msg(dbusif->dbus, msg);
737
738     mrp_dbus_msg_unref(msg);
739
740     return 0;
741 }
742
743
744 /*
745  * Local Variables:
746  * c-basic-offset: 4
747  * indent-tabs-mode: nil
748  * End:
749  *
750  */