tizenaudio-policy: Support to open Bluetooth SCO link with voice-recognition role
[platform/core/multimedia/pulseaudio-modules-tizen.git] / src / tizen-device.c
1 #ifdef HAVE_CONFIG_H
2 #include <config.h>
3 #endif
4
5 #include <ctype.h>
6 #include <pulse/proplist.h>
7 #include <pulse/util.h>
8 #include <pulse/rtclock.h>
9 #include <pulsecore/log.h>
10 #include <pulsecore/core-util.h>
11 #include <pulsecore/strbuf.h>
12
13 #include "tizen-device.h"
14
15 #define DBUS_SERVICE_HFP_AGENT "org.bluez.ag_agent"
16 #define DBUS_OBJECT_HFP_AGENT "/org/bluez/hfp_agent"
17 #define DBUS_INTERFACE_HFP_AGENT "Org.Hfp.App.Interface"
18
19 #define BT_CVSD_CODEC_ID 1 // narrow-band
20 #define BT_MSBC_CODEC_ID 2 // wide-band
21
22 struct pa_intset {
23     int values[MAX_INTSET_NUM];
24     uint32_t write_index;
25     uint32_t read_index;
26 };
27
28 int device_id_max_g = 1;
29 uint32_t event_id_max_g = 1;
30
31 static uint32_t _new_event_id() {
32     return event_id_max_g++;
33 }
34
35 int pa_intset_put(pa_intset *s, int val) {
36     int i;
37
38     pa_assert(s);
39
40     if (s->write_index >= MAX_INTSET_NUM)
41         return -1;
42
43     for(i = 0; i < s->write_index; i++) {
44         if (s->values[i] == val)
45             return -1;
46     }
47
48     s->values[s->write_index++] = val;
49
50     return 0;
51 }
52
53 pa_intset* pa_intset_new() {
54     pa_intset *s;
55
56     s = pa_xmalloc0(sizeof(pa_intset));
57     s->write_index = 0;
58     s->read_index = 0;
59
60     return s;
61 }
62
63 void pa_intset_free(pa_intset *s) {
64     pa_assert(s);
65     pa_xfree(s);
66 }
67
68 int pa_intset_first(pa_intset *s, int *val) {
69     pa_assert(s);
70     pa_assert(val);
71
72     if (s->write_index == 0)
73         return -1;
74
75     s->read_index = 0;
76     *val = s->values[s->read_index++];
77
78     return 0;
79 }
80
81 int pa_intset_next(pa_intset *s, int *val) {
82     pa_assert(s);
83     pa_assert(val);
84
85     if (s->read_index >= s->write_index)
86         return -1;
87
88     *val = s->values[s->read_index++];
89
90     return 0;
91 }
92
93 static char* get_playback_list_str(pa_hashmap *playback_devices) {
94     pa_sink *sink = NULL;
95     void *state;
96     const char *role;
97     pa_strbuf *buf;
98
99     if (!playback_devices || !pa_hashmap_size(playback_devices))
100         return NULL;
101
102     buf = pa_strbuf_new();
103     pa_strbuf_printf(buf, "    Playback device list\n");
104     PA_HASHMAP_FOREACH_KV(role, sink, playback_devices, state)
105         pa_strbuf_printf(buf, "        %-13s -> %s\n", role, sink->name);
106
107     return pa_strbuf_tostring_free(buf);
108 }
109
110 static char* get_capture_list_str(pa_hashmap *capture_devices) {
111     pa_source *source = NULL;
112     void *state;
113     const char *role;
114     pa_strbuf *buf;
115
116     if (!capture_devices || !pa_hashmap_size(capture_devices))
117         return NULL;
118
119     buf = pa_strbuf_new();
120     pa_strbuf_printf(buf, "    Capture device list\n");
121     PA_HASHMAP_FOREACH_KV(role, source, capture_devices, state)
122         pa_strbuf_printf(buf, "        %-13s -> %s\n", role, source->name);
123
124     return pa_strbuf_tostring_free(buf);
125 }
126
127 static char* _device_get_info_str(pa_tz_device *device) {
128     pa_strbuf *buf;
129     char *playback_str, *capture_str;
130
131     if (!device)
132         return NULL;
133
134     buf = pa_strbuf_new();
135     pa_strbuf_printf(buf, "[Device #%u]\n", device->id);
136     pa_strbuf_printf(buf, "  ID           : %u\n", device->id);
137     pa_strbuf_printf(buf, "  Type         : %s\n", device->type);
138     pa_strbuf_printf(buf, "  Name         : %s\n", device->name);
139     pa_strbuf_printf(buf, "  System ID    : %s\n", device->system_id);
140     pa_strbuf_printf(buf, "  Direction    : %s\n", device_direction_to_string(device->direction));
141     pa_strbuf_printf(buf, "  Is activated : %s\n", pa_yes_no(device->state == DM_DEVICE_STATE_ACTIVATED));
142     pa_strbuf_printf(buf, "  Internal     : %s\n", pa_yes_no(device->use_internal_codec));
143     if (device_type_is_equal(device->type, DEVICE_TYPE_USB_AUDIO)) {
144         pa_strbuf_printf(buf, "  Vendor ID    : %04x\n", device->vendor_id);
145         pa_strbuf_printf(buf, "  Product ID   : %04x\n", device->product_id);
146     }
147     if (device_type_is_equal(device->type, DEVICE_TYPE_BT_SCO))
148         pa_strbuf_printf(buf, "    SCO opened   : %s\n", pa_yes_no(device->sco_opened));
149     playback_str = get_playback_list_str(device->playback_devices);
150     capture_str = get_capture_list_str(device->capture_devices);
151
152     if (playback_str)
153         pa_strbuf_puts(buf, playback_str);
154     if (capture_str)
155         pa_strbuf_puts(buf, capture_str);
156
157     pa_xfree(playback_str);
158     pa_xfree(capture_str);
159
160     return pa_strbuf_tostring_free(buf);
161 }
162
163
164 void pa_tz_device_dump_info(pa_tz_device *device, pa_log_level_t log_level) {
165     char *info;
166
167     if (!device)
168         return;
169
170     if ((info = _device_get_info_str(device))) {
171         pa_logl(log_level, "%s", info);
172         pa_xfree(info);
173     }
174 }
175
176 static void notify_device_connection_changed(pa_tz_device *device, bool connected) {
177     pa_tz_device_hook_data_for_conn_changed hook_data;
178
179     hook_data.event_id = _new_event_id();
180     hook_data.is_connected = connected;
181     hook_data.device = device;
182
183     pa_log_info("Fire hook for device connection changed, device(%s/%u) %s",
184             device->type, device->id, connected ? "connected" : "disconnected");
185     pa_hook_fire(pa_communicator_hook(device->comm, PA_COMMUNICATOR_HOOK_DEVICE_CONNECTION_CHANGED), &hook_data);
186 }
187
188 static void notify_device_state_changed(pa_tz_device *device, dm_device_state_t state) {
189     pa_tz_device_hook_data_for_state_changed hook_data;
190
191     hook_data.event_id = _new_event_id();
192     hook_data.activated = (state == DM_DEVICE_STATE_ACTIVATED) ? true : false;
193     hook_data.device = device;
194
195     pa_log_info("Fire hook for device state changed, device(%s/%u) %s",
196             device->type, device->id, hook_data.activated ? "Activated" : "De-activated");
197     pa_hook_fire(pa_communicator_hook(device->comm, PA_COMMUNICATOR_HOOK_DEVICE_STATE_CHANGED), &hook_data);
198 }
199
200 /* pa_tz_device_new_data */
201 void pa_tz_device_new_data_init(pa_tz_device_new_data *data, pa_idxset *list,
202         pa_communicator *comm, pa_dbus_connection *conn) {
203     pa_assert(data);
204     pa_assert(list);
205     pa_assert(comm);
206
207     data->list = list;
208     data->comm = comm;
209     data->dbus_conn = conn;
210
211     data->type = NULL;
212     data->name = NULL;
213     data->system_id = NULL;
214     data->vendor_id = -1;
215     data->product_id = -1;
216     data->direction = DM_DEVICE_DIRECTION_NONE;
217
218     data->playback_pcms= pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
219     data->capture_pcms = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
220 }
221
222 void pa_tz_device_new_data_set_type(pa_tz_device_new_data *data, const char *type) {
223     pa_assert(data);
224
225     data->type = pa_xstrdup(type);
226 }
227
228 void pa_tz_device_new_data_set_name(pa_tz_device_new_data *data, const char *name) {
229     pa_assert(data);
230
231     data->name = pa_xstrdup(name);
232 }
233
234 void pa_tz_device_new_data_set_direction(pa_tz_device_new_data *data, dm_device_direction_t direction) {
235     pa_assert(data);
236
237     data->direction = direction;
238 }
239 /* only for external? */
240 void pa_tz_device_new_data_set_system_id(pa_tz_device_new_data *data, const char *system_id) {
241     pa_assert(data);
242
243     data->system_id = pa_xstrdup(system_id);
244 }
245
246 void pa_tz_device_new_data_set_vendor_id(pa_tz_device_new_data *data, int vendor_id) {
247     pa_assert(data);
248
249     data->vendor_id = vendor_id;
250 }
251
252 void pa_tz_device_new_data_set_product_id(pa_tz_device_new_data *data, int product_id) {
253     pa_assert(data);
254
255     data->product_id = product_id;
256 }
257
258 void pa_tz_device_new_data_set_use_internal_codec(pa_tz_device_new_data *data, bool use_internal_codec) {
259     pa_assert(data);
260
261     data->use_internal_codec = use_internal_codec;
262 }
263
264 void pa_tz_device_new_data_add_sink(pa_tz_device_new_data *data, const char *role, pa_sink *sink) {
265     pa_assert(data);
266
267     pa_hashmap_put(data->playback_pcms, (void*)role, sink);
268 }
269
270 void pa_tz_device_new_data_add_source(pa_tz_device_new_data *data, const char *role, pa_source *source) {
271     pa_assert(data);
272
273     pa_hashmap_put(data->capture_pcms, (void*)role, source);
274 }
275
276 void pa_tz_device_new_data_done(pa_tz_device_new_data *data) {
277     pa_assert(data);
278
279     pa_xfree(data->type);
280     pa_xfree(data->name);
281 }
282
283 static int _check_valid_device_new_data(pa_tz_device_new_data *data) {
284     pa_assert(data);
285
286     if (data->type == NULL) {
287         pa_log_error("new data type is null");
288         return -1;
289     }
290
291     if (data->direction == DM_DEVICE_DIRECTION_NONE) {
292         pa_log_error("new data direction is none");
293         return -1;
294     }
295
296     return 0;
297 }
298
299 static int device_add_sink(pa_tz_device *device, const char *role, pa_sink *sink) {
300     pa_assert(device);
301     pa_assert(device->playback_devices);
302
303     if (pa_hashmap_put(device->playback_devices, (void*)role, sink) < 0) {
304         pa_log_error("Failed to add sink : put sink failed");
305         return -1;
306     }
307     return 0;
308 }
309
310 static int device_add_source(pa_tz_device *device, const char *role, pa_source *source) {
311     pa_assert(device);
312     pa_assert(device->capture_devices);
313
314     if (pa_hashmap_put(device->capture_devices, (void*)role, source) < 0) {
315         pa_log_error("Failed to add source : put source failed");
316         return -1;
317     }
318     return 0;
319 }
320
321 static pa_sink* device_get_sink(pa_tz_device *device, const char *role) {
322     pa_sink *sink;
323     pa_assert(device);
324     pa_assert(device->playback_devices);
325
326     if ((sink = pa_hashmap_get(device->playback_devices, role)) == NULL) {
327         pa_log_warn("Failed to get sink for %s", role);
328         return NULL;
329     }
330
331     return sink;
332 }
333
334 static pa_source* device_get_source(pa_tz_device *device, const char *role) {
335     pa_source *source;
336     pa_assert(device);
337     pa_assert(device->capture_devices);
338
339     if ((source = pa_hashmap_get(device->capture_devices, role)) == NULL) {
340         pa_log_warn("Failed to get source for %s", role);
341         return NULL;
342     }
343
344     return source;
345 }
346
347 static void device_set_state(pa_tz_device *device, dm_device_state_t new_state) {
348     pa_assert(device);
349
350     if (device->state != new_state) {
351         pa_log_debug("change state %d -> %d", device->state, new_state);
352         device->state = new_state;
353         notify_device_state_changed(device, new_state);
354         pa_tz_device_dump_info(device, PA_LOG_DEBUG);
355     } else {
356         pa_log_debug("same state, not change it");
357     }
358 }
359
360 static dm_device_state_t device_get_state(pa_tz_device *device) {
361     pa_assert(device);
362     return device->state;
363 }
364
365 static int device_remove_sink_with_role(pa_tz_device *device, const char *role) {
366     pa_sink *sink;
367     pa_assert(device);
368
369     sink = pa_hashmap_remove(device->playback_devices, role);
370     return sink ? 0 : -1;
371 }
372
373 static int device_remove_source_with_role(pa_tz_device *device, const char *role) {
374     pa_source *source;
375     pa_assert(device);
376
377     source = pa_hashmap_remove(device->capture_devices, role);
378     return source ? 0 : -1;
379 }
380
381 static int device_remove_sink(pa_tz_device *device, pa_sink *sink) {
382     pa_sink *_sink;
383     void *state;
384     char *role;
385
386     pa_assert(device);
387
388     PA_HASHMAP_FOREACH_KV(role, _sink, device->playback_devices, state) {
389         if (sink == _sink)
390             return device_remove_sink_with_role(device, role);
391     }
392
393     return -1;
394 }
395
396 static int device_remove_source(pa_tz_device *device, pa_source *source) {
397     pa_source *_source;
398     void *state;
399     char *role;
400
401     pa_assert(device);
402
403     PA_HASHMAP_FOREACH_KV(role, _source, device->capture_devices, state) {
404         if (source == _source)
405             return device_remove_source_with_role(device, role);
406     }
407
408     return -1;
409 }
410
411 /* pa_tz_device */
412 pa_tz_device* pa_tz_device_new(pa_tz_device_new_data *data) {
413     pa_tz_device *device;
414     pa_sink *sink;
415     pa_source *source;
416
417     pa_assert(data);
418
419     if (_check_valid_device_new_data(data) < 0) {
420         pa_log_error("Invalid device_new_data");
421         return NULL;
422     }
423
424     device = pa_xmalloc(sizeof(pa_tz_device));
425     device->list = data->list;
426     device->comm = data->comm;
427     device->dbus_conn = data->dbus_conn;
428
429     device->id = device_id_max_g++;
430     device->type = pa_xstrdup(data->type);
431     if (data->name)
432         device->name = pa_xstrdup(data->name);
433     else
434         device->name = pa_xstrdup(data->type);
435     device->system_id = pa_xstrdup(data->system_id);
436     device->vendor_id = data->vendor_id;
437     device->product_id = data->product_id;
438
439     pa_log_info("New device type(%s) id(%u) name(%s) system_id(%s) vendor_id(%04x) product_id(%04x)",
440             device->type, device->id, device->name, pa_strempty(device->system_id),
441             device->vendor_id, device->product_id);
442
443     device->playback_devices = data->playback_pcms;
444     device->capture_devices = data->capture_pcms;
445     device->direction = data->direction;
446     device->state = DM_DEVICE_STATE_DEACTIVATED;
447     device->creation_time = pa_rtclock_now();
448     device->use_internal_codec = data->use_internal_codec;
449     device->sco_opened = false;
450
451     if (device_type_is_use_external_card(device->type)) {
452         if ((sink = device_get_sink(device, DEVICE_ROLE_NORMAL)))
453             sink->device_item = device;
454         if ((source = device_get_source(device, DEVICE_ROLE_NORMAL)))
455             source->device_item = device;
456     }
457
458     pa_idxset_put(device->list, device, NULL);
459     notify_device_connection_changed(device, true);
460
461     pa_tz_device_dump_info(device, PA_LOG_INFO);
462
463     return device;
464 }
465
466 int pa_tz_device_add_sink(pa_tz_device *device, const char *role, pa_sink *sink) {
467     pa_assert(device);
468     pa_assert(device_role_is_valid(role));
469     pa_assert(sink);
470
471     pa_log_info("device add sink, device(%u) role(%s) sink(%s)",
472             device->id, role, sink->name);
473
474     if (device_add_sink(device, role, sink) < 0) {
475         pa_log_error("Failed to add sink : Can't add to device");
476         return -1;
477     }
478
479     return 0;
480 }
481
482 int pa_tz_device_add_source(pa_tz_device *device, const char *role, pa_source *source) {
483     pa_assert(device);
484     pa_assert(device_role_is_valid(role));
485     pa_assert(source);
486
487     pa_log_info("device add source, device(%u) role(%s) source(%s)",
488             device->id, role, source->name);
489
490     if (device_add_source(device, role, source) < 0) {
491         pa_log_error("Failed to add source : Can't add to device");
492         return -1;
493     }
494
495     return 0;
496 }
497
498 int pa_tz_device_remove_sink(pa_tz_device *device, pa_sink *sink) {
499     pa_assert(device);
500     pa_assert(sink);
501
502     pa_log_info("device remove sink, device(%u) sink(%s)",
503             device->id, sink->name);
504
505     return device_remove_sink(device, sink);
506 }
507
508 int pa_tz_device_remove_source(pa_tz_device *device, pa_source *source) {
509     pa_assert(device);
510     pa_assert(source);
511
512     pa_log_info("device remove source, device(%u) source(%s)",
513             device->id, source->name);
514
515     return device_remove_source(device, source);
516 }
517
518 int pa_tz_device_remove_sink_with_role(pa_tz_device *device, const char *role) {
519     pa_assert(device);
520     pa_assert(device_role_is_valid(role));
521
522     pa_log_info("device remove sink with role, device(%u) role(%s)",
523             device->id, role);
524
525     return device_remove_sink_with_role(device, role);
526 }
527
528 int pa_tz_device_remove_source_with_role(pa_tz_device *device, const char *role) {
529     pa_assert(device);
530     pa_assert(device_role_is_valid(role));
531
532     pa_log_info("device remove source with role, device(%u) role(%s)",
533             device->id, role);
534
535     return device_remove_source_with_role(device, role);
536 }
537
538 void pa_tz_device_free(pa_tz_device *device) {
539     pa_assert(device);
540
541     pa_log_info("Free device type(%s) id(%u) name(%s) system_id(%s)",
542             device->type, device->id, device->name, device->system_id);
543
544     pa_tz_device_dump_info(device, PA_LOG_INFO);
545
546     pa_idxset_remove_by_data(device->list, device, NULL);
547     notify_device_connection_changed(device, false);
548
549     pa_xfree(device->type);
550     pa_xfree(device->name);
551     pa_xfree(device->system_id);
552
553     pa_xfree(device);
554 }
555
556 /* exported api */
557 pa_sink* pa_tz_device_get_sink(pa_tz_device *device, const char *role) {
558     pa_sink *sink;
559
560     pa_assert(device);
561
562     pa_log_info("device get sink, device(%u) role(%s)", device->id, role);
563
564     if ((sink = device_get_sink(device, role)) == NULL) {
565         pa_log_error("Failed to get sink from device");
566         return NULL;
567     }
568
569     return sink;
570 }
571
572 pa_source* pa_tz_device_get_source(pa_tz_device *device, const char *role) {
573     pa_source *source;
574
575     pa_assert(device);
576
577     pa_log_info("device get source, device(%u) role(%s)",
578             device->id, role);
579
580     if ((source = device_get_source(device, role)) == NULL) {
581         pa_log_error("Failed to get source from device");
582         return NULL;
583     }
584
585     return source;
586 }
587
588 /* TODO : Change param dm_device_state_t to bool or pa_tz_device_state_t,
589  * Because this state represent pa_tz_device's own state */
590 void pa_tz_device_set_state(pa_tz_device *device, dm_device_state_t state) {
591     pa_assert(device);
592
593     pa_log_info("device set state, device(%u) type(%s) -> %d",
594             device->id, device->type, state);
595     device_set_state(device, state);
596 }
597
598 dm_device_state_t pa_tz_device_get_state(pa_tz_device *device) {
599     pa_assert(device);
600
601     return device_get_state(device);
602 }
603
604 uint32_t pa_tz_device_get_id(pa_tz_device *device) {
605     pa_assert(device);
606
607     return device->id;
608 }
609
610 char* pa_tz_device_get_type(pa_tz_device *device) {
611     pa_assert(device);
612
613     return device->type;
614 }
615
616 char* pa_tz_device_get_name(pa_tz_device *device) {
617     pa_assert(device);
618
619     return device->name;
620 }
621
622 char* pa_tz_device_get_system_id(pa_tz_device *device) {
623     pa_assert(device);
624
625     return device->system_id;
626 }
627
628 int pa_tz_device_get_vendor_id(pa_tz_device *device) {
629     pa_assert(device);
630
631     return device->vendor_id;
632 }
633
634 int pa_tz_device_get_product_id(pa_tz_device *device) {
635     pa_assert(device);
636
637     return device->product_id;
638 }
639
640 dm_device_direction_t pa_tz_device_get_direction(pa_tz_device *device) {
641     pa_assert(device);
642
643     return device->direction;
644 }
645
646 pa_usec_t pa_tz_device_get_creation_time(pa_tz_device *device) {
647     pa_assert(device);
648
649     return device->creation_time;
650 }
651
652 bool pa_tz_device_is_use_internal_codec(pa_tz_device *device) {
653     pa_assert(device);
654
655     return device->use_internal_codec;
656 }
657
658 bool pa_tz_device_is_all_suspended(pa_tz_device *device) {
659     pa_sink *sink;
660     pa_source *source;
661     void *state;
662
663     PA_HASHMAP_FOREACH(sink, device->playback_devices, state) {
664         if (pa_sink_get_state(sink) != PA_SINK_SUSPENDED)
665             return false;
666     }
667
668     PA_HASHMAP_FOREACH(source, device->capture_devices, state) {
669         if (pa_source_get_state(source) != PA_SOURCE_SUSPENDED)
670             return false;
671     }
672
673     return true;
674 }
675
676 pa_intset* pa_tz_device_get_stream_list(pa_tz_device *device) {
677     pa_sink *sink;
678     pa_source *source;
679     pa_sink_input *input;
680     pa_source_output *output;
681     void *state;
682     const char *p_id_str;
683     int32_t p_id;
684     uint32_t idx;
685     pa_intset *stream_id_set;
686
687     pa_assert(device);
688
689     stream_id_set = pa_intset_new();
690     PA_HASHMAP_FOREACH(sink, device->playback_devices, state) {
691         PA_IDXSET_FOREACH(input, sink->inputs, idx) {
692             p_id_str = pa_proplist_gets(input->proplist, PA_PROP_MEDIA_PARENT_ID);
693             if (p_id_str && !pa_atoi(p_id_str, &p_id)) {
694                 pa_intset_put(stream_id_set, p_id);
695             } else {
696                 pa_log_warn("Invalid Parent ID : '%s'", pa_strnull(p_id_str));
697             }
698         }
699     }
700     PA_HASHMAP_FOREACH(source, device->capture_devices, state) {
701         PA_IDXSET_FOREACH(output, source->outputs, idx) {
702             p_id_str = pa_proplist_gets(output->proplist, PA_PROP_MEDIA_PARENT_ID);
703             if (p_id_str && !pa_atoi(p_id_str, &p_id)) {
704                 pa_intset_put(stream_id_set, p_id);
705             } else {
706                 pa_log_warn("Invalid Parent ID : '%s'", pa_strnull(p_id_str));
707             }
708         }
709     }
710
711     return stream_id_set;
712 }
713
714 static int method_call_bt_sco(pa_dbus_connection *conn, bool onoff) {
715     DBusMessage *msg, *reply;
716     DBusError err;
717     const char *method;
718
719     pa_assert(conn);
720
721     method = onoff ? "Play" : "Stop";
722     if (!(msg = dbus_message_new_method_call(DBUS_SERVICE_HFP_AGENT, DBUS_OBJECT_HFP_AGENT, DBUS_INTERFACE_HFP_AGENT, method))) {
723         pa_log_error("dbus method call failed");
724         return -1;
725     }
726
727     dbus_error_init(&err);
728     if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(conn), msg, -1, &err))) {
729         pa_log_error("Failed to method call %s.%s, %s", DBUS_INTERFACE_HFP_AGENT, method, err.message);
730         dbus_error_free(&err);
731         return -1;
732     }
733
734     dbus_message_unref(reply);
735     return 0;
736 }
737
738 static int method_call_bt_sco_get_property(pa_dbus_connection *conn, bool *is_wide_band, bool *is_nrec) {
739     DBusMessage *msg, *reply;
740     DBusMessageIter reply_iter, reply_iter_entry;
741     DBusError err;
742     unsigned int codec;
743     unsigned int nrec;
744     const char *property;
745
746     pa_assert(conn);
747
748     if (!is_wide_band && !is_nrec) {
749         return -1;
750     }
751
752     if (!(msg = dbus_message_new_method_call(DBUS_SERVICE_HFP_AGENT, DBUS_OBJECT_HFP_AGENT, DBUS_INTERFACE_HFP_AGENT, "GetProperties"))) {
753         pa_log_error("dbus method call failed");
754         return -1;
755     }
756
757     dbus_error_init(&err);
758     if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(conn), msg, -1, &err))) {
759         pa_log_error("Failed to method call %s.%s, %s", DBUS_INTERFACE_HFP_AGENT, "GetProperties", err.message);
760         dbus_error_free(&err);
761         return -1;
762     }
763
764     dbus_message_iter_init(reply,  &reply_iter);
765
766     if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) {
767         pa_log_error("Cannot get reply argument");
768         return -1;
769     }
770
771     dbus_message_iter_recurse(&reply_iter,  &reply_iter_entry);
772
773     while (dbus_message_iter_get_arg_type(&reply_iter_entry) == DBUS_TYPE_DICT_ENTRY) {
774         DBusMessageIter dict_entry, dict_entry_val;
775         dbus_message_iter_recurse(&reply_iter_entry, &dict_entry);
776         dbus_message_iter_get_basic(&dict_entry, &property);
777         pa_log_debug("String received = %s", property);
778         if (property) {
779             if (pa_streq("codec", property) && is_wide_band) {
780                 dbus_message_iter_next(&dict_entry);
781                 dbus_message_iter_recurse(&dict_entry, &dict_entry_val);
782                 if (dbus_message_iter_get_arg_type(&dict_entry_val) != DBUS_TYPE_UINT32)
783                     continue;
784                 dbus_message_iter_get_basic(&dict_entry_val, &codec);
785                 pa_log_debug("Codec = [%d]", codec);
786                 *is_wide_band = (codec == BT_MSBC_CODEC_ID) ? true : false;
787             } else if (pa_streq("nrec", property) && is_nrec) {
788                 dbus_message_iter_next(&dict_entry);
789                 dbus_message_iter_recurse(&dict_entry, &dict_entry_val);
790                 if (dbus_message_iter_get_arg_type(&dict_entry_val) != DBUS_TYPE_BOOLEAN)
791                     continue;
792                 dbus_message_iter_get_basic(&dict_entry_val, &nrec);
793                 pa_log_debug("nrec= [%d]", nrec);
794                 *is_nrec = nrec;
795             }
796         }
797         dbus_message_iter_next(&reply_iter_entry);
798     }
799
800
801     dbus_message_unref(reply);
802     return 0;
803 }
804
805 /* only for bt sco device */
806 int pa_tz_device_sco_open(pa_tz_device *device) {
807     pa_assert(device);
808     pa_assert(device->dbus_conn);
809
810     pa_log_info("BT SCO Open for device(%u)", device->id);
811     if (device_type_is_equal(device->type, DEVICE_TYPE_BT_SCO) == false) {
812         pa_log_error("Not BT SCO device");
813         return -1;
814     }
815
816     if (device->sco_opened) {
817         pa_log_warn("SCO already opened");
818         return -1;
819     }
820
821     if (method_call_bt_sco(device->dbus_conn, true) < 0) {
822         pa_log_error("Failed to bt sco on");
823         return -1;
824     }
825
826     device->sco_opened = true;
827     device->creation_time = pa_rtclock_now();
828     pa_tz_device_dump_info(device, PA_LOG_DEBUG);
829     pa_log_info("BT SCO Open - SUCCESS");
830
831     return 0;
832 }
833
834 /* only for bt sco device */
835 int pa_tz_device_sco_close(pa_tz_device *device) {
836     pa_assert(device);
837
838     pa_log_info("BT SCO Close for device(%u)", device->id);
839
840     if (device_type_is_equal(device->type, DEVICE_TYPE_BT_SCO) == false) {
841         pa_log_error("Not BT SCO device");
842         return -1;
843     }
844
845     if (device->sco_opened == false) {
846         pa_log_warn("SCO not opened");
847         return -1;
848     }
849
850     if (method_call_bt_sco(device->dbus_conn, false) < 0) {
851         pa_log_error("Failed to bt sco on");
852         return -1;
853     }
854
855     device->sco_opened = false;
856     pa_tz_device_dump_info(device, PA_LOG_DEBUG);
857     pa_log_info("BT SCO Close - Success");
858
859     return 0;
860 }
861
862 /* only for bt sco device */
863 int pa_tz_device_sco_get_property(pa_tz_device *device, bool *is_wide_band, bool *nrec) {
864     pa_assert(device);
865
866     pa_log_info("BT SCO get property for device(%u)", device->id);
867     if (device_type_is_equal(device->type, DEVICE_TYPE_BT_SCO) == false) {
868         pa_log_error("Not BT device");
869         return -1;
870     }
871
872     if (method_call_bt_sco_get_property(device->dbus_conn, is_wide_band, nrec) < 0) {
873         pa_log_error("Failed to get BT SCO Property");
874         return -1;
875     }
876
877     pa_log_info("BT SCO Get Property - Success, is wide band : %s, nrec : %s", pa_yes_no(*is_wide_band), pa_yes_no(*nrec));
878
879     return 0;
880 }
881
882 /* only for bt sco device */
883 int pa_tz_device_sco_get_status(pa_tz_device *device, dm_device_bt_sco_status_t *status) {
884     pa_assert(device);
885
886     pa_log_debug("BT SCO get status for device(%u)", device->id);
887     if (device_type_is_equal(device->type, DEVICE_TYPE_BT_SCO) == false) {
888         pa_log_error("Not BT device");
889         return -1;
890     }
891     if (status == NULL) {
892         pa_log_error("invalid parameter");
893         return -1;
894     }
895
896     if (device->sco_opened == false)
897         *status = DM_DEVICE_BT_SCO_STATUS_CONNECTED;
898     else
899         *status = DM_DEVICE_BT_SCO_STATUS_OPENED;
900
901     pa_log_info("BT SCO (%u) Get Status, %d", device->id, *status);
902     return 0;
903 }
904