1 /*
2 *
3 * BlueZ - Bluetooth protocol stack for Linux
4 *
5 * Copyright (C) 2006-2010 Nokia Corporation
6 * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
7 *
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <stdint.h>
32 #include <glib.h>
33 #include <dbus/dbus.h>
34 #include <gdbus.h>
35
36 #include "log.h"
37 #include "telephony.h"
38 #include "error.h"
39
40 #define TELEPHONY_DUMMY_IFACE "org.bluez.TelephonyTest"
41 #define TELEPHONY_DUMMY_PATH "/org/bluez/test"
42
43 static DBusConnection *connection = NULL;
44
45 static const char *chld_str = "0,1,1x,2,2x,3,4";
46 static char *subscriber_number = NULL;
47 static char *active_call_number = NULL;
48 static int active_call_status = 0;
49 static int active_call_dir = 0;
50
51 static gboolean events_enabled = FALSE;
52
53 static struct indicator dummy_indicators[] =
54 {
55 { "battchg", "0-5", 5, TRUE },
56 { "signal", "0-5", 5, TRUE },
57 { "service", "0,1", 1, TRUE },
58 { "call", "0,1", 0, TRUE },
59 { "callsetup", "0-3", 0, TRUE },
60 { "callheld", "0-2", 0, FALSE },
61 { "roam", "0,1", 0, TRUE },
62 { NULL }
63 };
64
telephony_device_connected(void * telephony_device)65 void telephony_device_connected(void *telephony_device)
66 {
67 DBG("telephony-dummy: device %p connected", telephony_device);
68 }
69
telephony_device_disconnected(void * telephony_device)70 void telephony_device_disconnected(void *telephony_device)
71 {
72 DBG("telephony-dummy: device %p disconnected", telephony_device);
73 events_enabled = FALSE;
74 }
75
telephony_event_reporting_req(void * telephony_device,int ind)76 void telephony_event_reporting_req(void *telephony_device, int ind)
77 {
78 events_enabled = ind == 1 ? TRUE : FALSE;
79
80 telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
81 }
82
telephony_response_and_hold_req(void * telephony_device,int rh)83 void telephony_response_and_hold_req(void *telephony_device, int rh)
84 {
85 telephony_response_and_hold_rsp(telephony_device,
86 CME_ERROR_NOT_SUPPORTED);
87 }
88
telephony_last_dialed_number_req(void * telephony_device)89 void telephony_last_dialed_number_req(void *telephony_device)
90 {
91 telephony_last_dialed_number_rsp(telephony_device, CME_ERROR_NONE);
92
93 /* Notify outgoing call set-up successfully initiated */
94 telephony_update_indicator(dummy_indicators, "callsetup",
95 EV_CALLSETUP_OUTGOING);
96 telephony_update_indicator(dummy_indicators, "callsetup",
97 EV_CALLSETUP_ALERTING);
98
99 active_call_status = CALL_STATUS_ALERTING;
100 active_call_dir = CALL_DIR_OUTGOING;
101 }
102
telephony_terminate_call_req(void * telephony_device)103 void telephony_terminate_call_req(void *telephony_device)
104 {
105 g_free(active_call_number);
106 active_call_number = NULL;
107
108 telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
109
110 if (telephony_get_indicator(dummy_indicators, "callsetup") > 0)
111 telephony_update_indicator(dummy_indicators, "callsetup",
112 EV_CALLSETUP_INACTIVE);
113 else
114 telephony_update_indicator(dummy_indicators, "call",
115 EV_CALL_INACTIVE);
116 }
117
telephony_answer_call_req(void * telephony_device)118 void telephony_answer_call_req(void *telephony_device)
119 {
120 telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
121
122 telephony_update_indicator(dummy_indicators, "call", EV_CALL_ACTIVE);
123 telephony_update_indicator(dummy_indicators, "callsetup",
124 EV_CALLSETUP_INACTIVE);
125
126 active_call_status = CALL_STATUS_ACTIVE;
127 }
128
telephony_dial_number_req(void * telephony_device,const char * number)129 void telephony_dial_number_req(void *telephony_device, const char *number)
130 {
131 g_free(active_call_number);
132 active_call_number = g_strdup(number);
133
134 DBG("telephony-dummy: dial request to %s", active_call_number);
135
136 telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
137
138 /* Notify outgoing call set-up successfully initiated */
139 telephony_update_indicator(dummy_indicators, "callsetup",
140 EV_CALLSETUP_OUTGOING);
141 telephony_update_indicator(dummy_indicators, "callsetup",
142 EV_CALLSETUP_ALERTING);
143
144 active_call_status = CALL_STATUS_ALERTING;
145 active_call_dir = CALL_DIR_OUTGOING;
146 }
147
telephony_transmit_dtmf_req(void * telephony_device,char tone)148 void telephony_transmit_dtmf_req(void *telephony_device, char tone)
149 {
150 DBG("telephony-dummy: transmit dtmf: %c", tone);
151 telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
152 }
153
telephony_subscriber_number_req(void * telephony_device)154 void telephony_subscriber_number_req(void *telephony_device)
155 {
156 DBG("telephony-dummy: subscriber number request");
157 if (subscriber_number)
158 telephony_subscriber_number_ind(subscriber_number,
159 NUMBER_TYPE_TELEPHONY,
160 SUBSCRIBER_SERVICE_VOICE);
161 telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
162 }
163
telephony_list_current_calls_req(void * telephony_device)164 void telephony_list_current_calls_req(void *telephony_device)
165 {
166 DBG("telephony-dummy: list current calls request");
167 if (active_call_number)
168 telephony_list_current_call_ind(1, active_call_dir,
169 active_call_status,
170 CALL_MODE_VOICE,
171 CALL_MULTIPARTY_NO,
172 active_call_number,
173 NUMBER_TYPE_TELEPHONY);
174 telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
175 }
176
telephony_operator_selection_req(void * telephony_device)177 void telephony_operator_selection_req(void *telephony_device)
178 {
179 telephony_operator_selection_ind(OPERATOR_MODE_AUTO, "DummyOperator");
180 telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
181 }
182
telephony_call_hold_req(void * telephony_device,const char * cmd)183 void telephony_call_hold_req(void *telephony_device, const char *cmd)
184 {
185 DBG("telephony-dymmy: got call hold request %s", cmd);
186 telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
187 }
188
telephony_nr_and_ec_req(void * telephony_device,gboolean enable)189 void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
190 {
191 DBG("telephony-dummy: got %s NR and EC request",
192 enable ? "enable" : "disable");
193
194 telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
195 }
196
telephony_voice_dial_req(void * telephony_device,gboolean enable)197 void telephony_voice_dial_req(void *telephony_device, gboolean enable)
198 {
199 DBG("telephony-dummy: got %s voice dial request",
200 enable ? "enable" : "disable");
201
202 g_dbus_emit_signal(connection, TELEPHONY_DUMMY_PATH,
203 TELEPHONY_DUMMY_IFACE, "VoiceDial",
204 DBUS_TYPE_INVALID);
205
206 telephony_voice_dial_rsp(telephony_device, CME_ERROR_NONE);
207 }
208
telephony_key_press_req(void * telephony_device,const char * keys)209 void telephony_key_press_req(void *telephony_device, const char *keys)
210 {
211 DBG("telephony-dummy: got key press request for %s", keys);
212 telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
213 }
214
215 /* D-Bus method handlers */
outgoing_call(DBusConnection * conn,DBusMessage * msg,void * data)216 static DBusMessage *outgoing_call(DBusConnection *conn, DBusMessage *msg,
217 void *data)
218 {
219 const char *number;
220
221 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
222 DBUS_TYPE_INVALID))
223 return btd_error_invalid_args(msg);
224
225 DBG("telephony-dummy: outgoing call to %s", number);
226
227 g_free(active_call_number);
228 active_call_number = g_strdup(number);
229
230 telephony_update_indicator(dummy_indicators, "callsetup",
231 EV_CALLSETUP_OUTGOING);
232 telephony_update_indicator(dummy_indicators, "callsetup",
233 EV_CALLSETUP_ALERTING);
234
235 active_call_status = CALL_STATUS_ALERTING;
236 active_call_dir = CALL_DIR_OUTGOING;
237
238 return dbus_message_new_method_return(msg);
239 }
240
incoming_call(DBusConnection * conn,DBusMessage * msg,void * data)241 static DBusMessage *incoming_call(DBusConnection *conn, DBusMessage *msg,
242 void *data)
243 {
244 const char *number;
245
246 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
247 DBUS_TYPE_INVALID))
248 return btd_error_invalid_args(msg);
249
250 DBG("telephony-dummy: incoming call to %s", number);
251
252 g_free(active_call_number);
253 active_call_number = g_strdup(number);
254
255 telephony_update_indicator(dummy_indicators, "callsetup",
256 EV_CALLSETUP_INCOMING);
257
258 active_call_status = CALL_STATUS_INCOMING;
259 active_call_dir = CALL_DIR_INCOMING;
260
261 telephony_incoming_call_ind(number, NUMBER_TYPE_TELEPHONY);
262
263 return dbus_message_new_method_return(msg);
264 }
265
cancel_call(DBusConnection * conn,DBusMessage * msg,void * data)266 static DBusMessage *cancel_call(DBusConnection *conn, DBusMessage *msg,
267 void *data)
268 {
269 DBG("telephony-dummy: cancel call");
270
271 g_free(active_call_number);
272 active_call_number = NULL;
273
274 if (telephony_get_indicator(dummy_indicators, "callsetup") > 0) {
275 telephony_update_indicator(dummy_indicators, "callsetup",
276 EV_CALLSETUP_INACTIVE);
277 telephony_calling_stopped_ind();
278 }
279
280 if (telephony_get_indicator(dummy_indicators, "call") > 0)
281 telephony_update_indicator(dummy_indicators, "call",
282 EV_CALL_INACTIVE);
283
284 return dbus_message_new_method_return(msg);
285 }
286
signal_strength(DBusConnection * conn,DBusMessage * msg,void * data)287 static DBusMessage *signal_strength(DBusConnection *conn, DBusMessage *msg,
288 void *data)
289 {
290 dbus_uint32_t strength;
291
292 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &strength,
293 DBUS_TYPE_INVALID))
294 return btd_error_invalid_args(msg);
295
296 if (strength > 5)
297 return btd_error_invalid_args(msg);
298
299 telephony_update_indicator(dummy_indicators, "signal", strength);
300
301 DBG("telephony-dummy: signal strength set to %u", strength);
302
303 return dbus_message_new_method_return(msg);
304 }
305
battery_level(DBusConnection * conn,DBusMessage * msg,void * data)306 static DBusMessage *battery_level(DBusConnection *conn, DBusMessage *msg,
307 void *data)
308 {
309 dbus_uint32_t level;
310
311 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &level,
312 DBUS_TYPE_INVALID))
313 return btd_error_invalid_args(msg);
314
315 if (level > 5)
316 return btd_error_invalid_args(msg);
317
318 telephony_update_indicator(dummy_indicators, "battchg", level);
319
320 DBG("telephony-dummy: battery level set to %u", level);
321
322 return dbus_message_new_method_return(msg);
323 }
324
roaming_status(DBusConnection * conn,DBusMessage * msg,void * data)325 static DBusMessage *roaming_status(DBusConnection *conn, DBusMessage *msg,
326 void *data)
327 {
328 dbus_bool_t roaming;
329 int val;
330
331 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &roaming,
332 DBUS_TYPE_INVALID))
333 return btd_error_invalid_args(msg);
334
335 val = roaming ? EV_ROAM_ACTIVE : EV_ROAM_INACTIVE;
336
337 telephony_update_indicator(dummy_indicators, "roam", val);
338
339 DBG("telephony-dummy: roaming status set to %d", val);
340
341 return dbus_message_new_method_return(msg);
342 }
343
registration_status(DBusConnection * conn,DBusMessage * msg,void * data)344 static DBusMessage *registration_status(DBusConnection *conn, DBusMessage *msg,
345 void *data)
346 {
347 dbus_bool_t registration;
348 int val;
349
350 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, ®istration,
351 DBUS_TYPE_INVALID))
352 return btd_error_invalid_args(msg);
353
354 val = registration ? EV_SERVICE_PRESENT : EV_SERVICE_NONE;
355
356 telephony_update_indicator(dummy_indicators, "service", val);
357
358 DBG("telephony-dummy: registration status set to %d", val);
359
360 return dbus_message_new_method_return(msg);
361 }
362
set_subscriber_number(DBusConnection * conn,DBusMessage * msg,void * data)363 static DBusMessage *set_subscriber_number(DBusConnection *conn,
364 DBusMessage *msg,
365 void *data)
366 {
367 const char *number;
368
369 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
370 DBUS_TYPE_INVALID))
371 return btd_error_invalid_args(msg);
372
373 g_free(subscriber_number);
374 subscriber_number = g_strdup(number);
375
376 DBG("telephony-dummy: subscriber number set to %s", number);
377
378 return dbus_message_new_method_return(msg);
379 }
380
381 static GDBusMethodTable dummy_methods[] = {
382 { "OutgoingCall", "s", "", outgoing_call },
383 { "IncomingCall", "s", "", incoming_call },
384 { "CancelCall", "", "", cancel_call },
385 { "SignalStrength", "u", "", signal_strength },
386 { "BatteryLevel", "u", "", battery_level },
387 { "RoamingStatus", "b", "", roaming_status },
388 { "RegistrationStatus", "b", "", registration_status },
389 { "SetSubscriberNumber","s", "", set_subscriber_number },
390 { }
391 };
392
393 static GDBusSignalTable dummy_signals[] = {
394 { "VoiceDial", "" },
395 { }
396 };
397
telephony_init(void)398 int telephony_init(void)
399 {
400 uint32_t features = AG_FEATURE_REJECT_A_CALL |
401 AG_FEATURE_ENHANCED_CALL_STATUS |
402 AG_FEATURE_EXTENDED_ERROR_RESULT_CODES;
403
404 DBG("");
405
406 connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
407
408 if (g_dbus_register_interface(connection, TELEPHONY_DUMMY_PATH,
409 TELEPHONY_DUMMY_IFACE,
410 dummy_methods, dummy_signals,
411 NULL, NULL, NULL) == FALSE) {
412 error("telephony-dummy interface %s init failed on path %s",
413 TELEPHONY_DUMMY_IFACE, TELEPHONY_DUMMY_PATH);
414 return -1;
415 }
416
417 telephony_ready_ind(features, dummy_indicators, BTRH_NOT_SUPPORTED,
418 chld_str);
419
420 return 0;
421 }
422
telephony_exit(void)423 void telephony_exit(void)
424 {
425 DBG("");
426
427 g_dbus_unregister_interface(connection, TELEPHONY_DUMMY_PATH,
428 TELEPHONY_DUMMY_IFACE);
429 dbus_connection_unref(connection);
430 connection = NULL;
431
432 telephony_deinit();
433 }
434