Sync with Tizen 2.4
[platform/core/context/device-context-provider.git] / src / device_status / headphone.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <context_mgr.h>
18 #include "device_status_types.h"
19 #include "headphone.h"
20
21 #define HANDLING_DELAY 2000
22 #define MAX_HANDLING_COUNT 3
23
24 GENERATE_PROVIDER_COMMON_IMPL(device_status_headphone);
25
26 ctx::device_status_headphone::device_status_headphone()
27         : connected(false)
28         , audio_jack_state(RUNTIME_INFO_AUDIO_JACK_STATUS_UNCONNECTED)
29         , bt_audio_state(false)
30         , bt_audio_callback_on(false)
31         , bt_event_handler_added(false)
32         , bt_event_handling_count(0)
33 {
34 }
35
36 ctx::device_status_headphone::~device_status_headphone()
37 {
38 }
39
40 bool ctx::device_status_headphone::is_supported()
41 {
42         return true;
43 }
44
45 void ctx::device_status_headphone::submit_trigger_item()
46 {
47         context_manager::register_trigger_item(DEVICE_ST_SUBJ_HEADPHONE, OPS_SUBSCRIBE | OPS_READ,
48                         "{"
49                                 TRIG_BOOL_ITEM_DEF("IsConnected") ","
50                                 "\"Type\":{\"type\":\"string\",\"values\":[\"Normal\",\"Headset\",\"Bluetooth\"]}"
51                         "}",
52                         NULL);
53 }
54
55 int ctx::device_status_headphone::subscribe()
56 {
57         connected = get_current_status();
58
59         // Wired headphone
60         int ret = runtime_info_set_changed_cb(RUNTIME_INFO_KEY_AUDIO_JACK_STATUS, on_audio_jack_state_changed, this);
61         IF_FAIL_RETURN(ret == RUNTIME_INFO_ERROR_NONE, ERR_OPERATION_FAILED);
62
63         // Bluetooth headphone
64         set_bt_audio_callback();
65
66         return ERR_NONE;
67 }
68
69 int ctx::device_status_headphone::unsubscribe()
70 {
71         runtime_info_unset_changed_cb(RUNTIME_INFO_KEY_AUDIO_JACK_STATUS);
72         unset_bt_audio_callback();
73
74         return ERR_NONE;
75 }
76
77 int ctx::device_status_headphone::read()
78 {
79         if (!being_subscribed)
80                 connected = get_current_status();
81
82         json data;
83         generate_data_packet(data);
84         ctx::context_manager::reply_to_read(DEVICE_ST_SUBJ_HEADPHONE, NULL, ERR_NONE, data);
85
86         return ERR_NONE;
87 }
88
89 void ctx::device_status_headphone::set_bt_audio_callback()
90 {
91         IF_FAIL_VOID(!bt_audio_callback_on);
92         int ret;
93
94         ret = bt_initialize();
95         if (ret != BT_ERROR_NONE) {
96                 _W("Bluetooth initialization failed");
97                 return;
98         }
99
100         ret = bt_device_set_connection_state_changed_cb(on_bt_connection_changed, this);
101         if (ret != BT_ERROR_NONE) {
102                 bt_deinitialize();
103                 return;
104         }
105
106         bt_audio_callback_on = true;
107 }
108
109 void ctx::device_status_headphone::unset_bt_audio_callback()
110 {
111         IF_FAIL_VOID(bt_audio_callback_on);
112
113         bt_device_unset_connection_state_changed_cb();
114         bt_deinitialize();
115
116         bt_audio_callback_on = false;
117 }
118
119 void ctx::device_status_headphone::set_bt_audio_state(bool state)
120 {
121         bt_audio_state = state;
122 }
123
124 bool ctx::device_status_headphone::get_current_status()
125 {
126         int ret;
127
128         // Wired audio
129         ret = runtime_info_get_value_int(RUNTIME_INFO_KEY_AUDIO_JACK_STATUS, &audio_jack_state);
130         IF_FAIL_RETURN(ret == ERR_NONE, connected);
131
132         // Bluetooth audio
133         bt_audio_state = false;
134         ret = bt_initialize();
135         if (ret == BT_ERROR_NONE) {
136                 bt_adapter_foreach_bonded_device(on_bt_bond, this);
137                 bt_deinitialize();
138         }
139
140         return ((audio_jack_state != RUNTIME_INFO_AUDIO_JACK_STATUS_UNCONNECTED) || bt_audio_state);
141 }
142
143 void ctx::device_status_headphone::generate_data_packet(ctx::json &data)
144 {
145         data.set(NULL, DEVICE_ST_IS_CONNECTED, connected ? DEVICE_ST_TRUE : DEVICE_ST_FALSE);
146
147         switch (audio_jack_state) {
148         case RUNTIME_INFO_AUDIO_JACK_STATUS_CONNECTED_3WIRE:
149                 data.set(NULL, DEVICE_ST_TYPE, DEVICE_ST_NORMAL);
150                 break;
151         case RUNTIME_INFO_AUDIO_JACK_STATUS_CONNECTED_4WIRE:
152                 data.set(NULL, DEVICE_ST_TYPE, DEVICE_ST_HEADSET);
153                 break;
154         default:
155                 if (bt_audio_state)
156                         data.set(NULL, DEVICE_ST_TYPE, DEVICE_ST_BLUETOOTH);
157                 break;
158         }
159 }
160
161 bool ctx::device_status_headphone::handle_event()
162 {
163         bool prev_state = connected;
164         connected = ((audio_jack_state != RUNTIME_INFO_AUDIO_JACK_STATUS_UNCONNECTED) || bt_audio_state);
165
166         IF_FAIL_RETURN(prev_state != connected, false);
167
168         ctx::json data;
169         generate_data_packet(data);
170         ctx::context_manager::publish(DEVICE_ST_SUBJ_HEADPHONE, NULL, ERR_NONE, data);
171         return true;
172 }
173
174 void ctx::device_status_headphone::handle_audio_jack_event()
175 {
176         int ret = runtime_info_get_value_int(RUNTIME_INFO_KEY_AUDIO_JACK_STATUS, &audio_jack_state);
177         IF_FAIL_VOID_TAG(ret == ERR_NONE, _E, "Getting runtime info failed");
178         handle_event();
179 }
180
181 void ctx::device_status_headphone::on_audio_jack_state_changed(runtime_info_key_e runtime_key, void* user_data)
182 {
183         _D("EarJack");
184         ctx::device_status_headphone *instance = static_cast<ctx::device_status_headphone*>(user_data);
185         instance->handle_audio_jack_event();
186 }
187
188 void ctx::device_status_headphone::on_bt_connection_changed(bool connected, bt_device_connection_info_s *conn_info, void *user_data)
189 {
190         ctx::device_status_headphone *instance = static_cast<ctx::device_status_headphone*>(user_data);
191         IF_FAIL_VOID(connected != instance->bt_audio_state);
192         IF_FAIL_VOID(!instance->bt_event_handler_added);
193
194         if (connected) {
195                 _D("BT state checking scheduled");
196                 instance->bt_event_handler_added = true;
197                 instance->bt_event_handling_count = 0;
198                 g_timeout_add(HANDLING_DELAY, handle_bt_event, user_data);
199         } else {
200                 handle_bt_event(user_data);
201         }
202 }
203
204 gboolean ctx::device_status_headphone::handle_bt_event(gpointer data)
205 {
206         _D("BT state checking started");
207         ctx::device_status_headphone *instance = static_cast<ctx::device_status_headphone*>(data);
208         instance->bt_event_handler_added = false;
209
210         instance->set_bt_audio_state(false);
211         int err = bt_adapter_foreach_bonded_device(on_bt_bond, data);
212         IF_FAIL_RETURN_TAG(err == BT_ERROR_NONE, FALSE, _E, "bt_adapter_foreach_bonded_device() failed");
213
214         instance->bt_event_handling_count++;
215
216         if (instance->handle_event() || instance->bt_event_handling_count >= MAX_HANDLING_COUNT)
217                 return FALSE;
218
219         return TRUE;
220 }
221
222 bool ctx::device_status_headphone::on_bt_bond(bt_device_info_s *device_info, void* user_data)
223 {
224         if (device_info->bt_class.major_device_class != BT_MAJOR_DEVICE_CLASS_AUDIO_VIDEO)
225                 return true;
226
227         bool st = false;
228         int err = bt_device_is_profile_connected(device_info->remote_address, BT_PROFILE_A2DP, &st);
229         IF_FAIL_RETURN_TAG(err == BT_ERROR_NONE, false, _E, "bt_device_is_profile_connected() failed");
230
231         if (st) {
232                 ctx::device_status_headphone *instance = static_cast<ctx::device_status_headphone*>(user_data);
233                 instance->set_bt_audio_state(true);
234                 return false;
235         }
236
237         return true;
238 }