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 39#define TELEPHONY_DUMMY_IFACE "org.bluez.TelephonyTest" 40#define TELEPHONY_DUMMY_PATH "/org/bluez/test" 41 42static DBusConnection *connection = NULL; 43 44static const char *chld_str = "0,1,1x,2,2x,3,4"; 45static char *subscriber_number = NULL; 46static char *active_call_number = NULL; 47static int active_call_status = 0; 48static int active_call_dir = 0; 49 50static gboolean events_enabled = FALSE; 51 52/* Response and hold state 53 * -1 = none 54 * 0 = incoming call is put on hold in the AG 55 * 1 = held incoming call is accepted in the AG 56 * 2 = held incoming call is rejected in the AG 57 */ 58static int response_and_hold = -1; 59 60static struct indicator dummy_indicators[] = 61{ 62 { "battchg", "0-5", 5, TRUE }, 63 { "signal", "0-5", 5, TRUE }, 64 { "service", "0,1", 1, TRUE }, 65 { "call", "0,1", 0, TRUE }, 66 { "callsetup", "0-3", 0, TRUE }, 67 { "callheld", "0-2", 0, FALSE }, 68 { "roam", "0,1", 0, TRUE }, 69 { NULL } 70}; 71 72static inline DBusMessage *invalid_args(DBusMessage *msg) 73{ 74 return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments", 75 "Invalid arguments in method call"); 76} 77 78void telephony_device_connected(void *telephony_device) 79{ 80 DBG("telephony-dummy: device %p connected", telephony_device); 81} 82 83void telephony_device_disconnected(void *telephony_device) 84{ 85 DBG("telephony-dummy: device %p disconnected", telephony_device); 86 events_enabled = FALSE; 87} 88 89void telephony_event_reporting_req(void *telephony_device, int ind) 90{ 91 events_enabled = ind == 1 ? TRUE : FALSE; 92 93 telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE); 94} 95 96void telephony_response_and_hold_req(void *telephony_device, int rh) 97{ 98 response_and_hold = rh; 99 100 telephony_response_and_hold_ind(response_and_hold); 101 102 telephony_response_and_hold_rsp(telephony_device, CME_ERROR_NONE); 103} 104 105void telephony_last_dialed_number_req(void *telephony_device) 106{ 107 telephony_last_dialed_number_rsp(telephony_device, CME_ERROR_NONE); 108 109 /* Notify outgoing call set-up successfully initiated */ 110 telephony_update_indicator(dummy_indicators, "callsetup", 111 EV_CALLSETUP_OUTGOING); 112 telephony_update_indicator(dummy_indicators, "callsetup", 113 EV_CALLSETUP_ALERTING); 114 115 active_call_status = CALL_STATUS_ALERTING; 116 active_call_dir = CALL_DIR_OUTGOING; 117} 118 119void telephony_terminate_call_req(void *telephony_device) 120{ 121 g_free(active_call_number); 122 active_call_number = NULL; 123 124 telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE); 125 126 if (telephony_get_indicator(dummy_indicators, "callsetup") > 0) 127 telephony_update_indicator(dummy_indicators, "callsetup", 128 EV_CALLSETUP_INACTIVE); 129 else 130 telephony_update_indicator(dummy_indicators, "call", 131 EV_CALL_INACTIVE); 132} 133 134void telephony_answer_call_req(void *telephony_device) 135{ 136 telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE); 137 138 telephony_update_indicator(dummy_indicators, "call", EV_CALL_ACTIVE); 139 telephony_update_indicator(dummy_indicators, "callsetup", 140 EV_CALLSETUP_INACTIVE); 141 142 active_call_status = CALL_STATUS_ACTIVE; 143} 144 145void telephony_dial_number_req(void *telephony_device, const char *number) 146{ 147 g_free(active_call_number); 148 active_call_number = g_strdup(number); 149 150 DBG("telephony-dummy: dial request to %s", active_call_number); 151 152 telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE); 153 154 /* Notify outgoing call set-up successfully initiated */ 155 telephony_update_indicator(dummy_indicators, "callsetup", 156 EV_CALLSETUP_OUTGOING); 157 telephony_update_indicator(dummy_indicators, "callsetup", 158 EV_CALLSETUP_ALERTING); 159 160 active_call_status = CALL_STATUS_ALERTING; 161 active_call_dir = CALL_DIR_OUTGOING; 162} 163 164void telephony_transmit_dtmf_req(void *telephony_device, char tone) 165{ 166 DBG("telephony-dummy: transmit dtmf: %c", tone); 167 telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE); 168} 169 170void telephony_subscriber_number_req(void *telephony_device) 171{ 172 DBG("telephony-dummy: subscriber number request"); 173 if (subscriber_number) 174 telephony_subscriber_number_ind(subscriber_number, 175 NUMBER_TYPE_TELEPHONY, 176 SUBSCRIBER_SERVICE_VOICE); 177 telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE); 178} 179 180void telephony_list_current_calls_req(void *telephony_device) 181{ 182 DBG("telephony-dummy: list current calls request"); 183 if (active_call_number) 184 telephony_list_current_call_ind(1, active_call_dir, 185 active_call_status, 186 CALL_MODE_VOICE, 187 CALL_MULTIPARTY_NO, 188 active_call_number, 189 NUMBER_TYPE_TELEPHONY); 190 telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE); 191} 192 193void telephony_operator_selection_req(void *telephony_device) 194{ 195 telephony_operator_selection_ind(OPERATOR_MODE_AUTO, "DummyOperator"); 196 telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE); 197} 198 199void telephony_call_hold_req(void *telephony_device, const char *cmd) 200{ 201 DBG("telephony-dymmy: got call hold request %s", cmd); 202 telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE); 203} 204 205void telephony_nr_and_ec_req(void *telephony_device, gboolean enable) 206{ 207 DBG("telephony-dummy: got %s NR and EC request", 208 enable ? "enable" : "disable"); 209 210 telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE); 211} 212 213void telephony_voice_dial_req(void *telephony_device, gboolean enable) 214{ 215 DBG("telephony-dummy: got %s voice dial request", 216 enable ? "enable" : "disable"); 217 218 g_dbus_emit_signal(connection, TELEPHONY_DUMMY_PATH, 219 TELEPHONY_DUMMY_IFACE, "VoiceDial", 220 DBUS_TYPE_INVALID); 221 222 telephony_voice_dial_rsp(telephony_device, CME_ERROR_NONE); 223} 224 225void telephony_key_press_req(void *telephony_device, const char *keys) 226{ 227 DBG("telephony-dummy: got key press request for %s", keys); 228 telephony_key_press_rsp(telephony_device, CME_ERROR_NONE); 229} 230 231/* D-Bus method handlers */ 232static DBusMessage *outgoing_call(DBusConnection *conn, DBusMessage *msg, 233 void *data) 234{ 235 const char *number; 236 237 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number, 238 DBUS_TYPE_INVALID)) 239 return invalid_args(msg); 240 241 DBG("telephony-dummy: outgoing call to %s", number); 242 243 g_free(active_call_number); 244 active_call_number = g_strdup(number); 245 246 telephony_update_indicator(dummy_indicators, "callsetup", 247 EV_CALLSETUP_OUTGOING); 248 telephony_update_indicator(dummy_indicators, "callsetup", 249 EV_CALLSETUP_ALERTING); 250 251 active_call_status = CALL_STATUS_ALERTING; 252 active_call_dir = CALL_DIR_OUTGOING; 253 254 return dbus_message_new_method_return(msg); 255} 256 257static DBusMessage *incoming_call(DBusConnection *conn, DBusMessage *msg, 258 void *data) 259{ 260 const char *number; 261 262 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number, 263 DBUS_TYPE_INVALID)) 264 return invalid_args(msg); 265 266 DBG("telephony-dummy: incoming call to %s", number); 267 268 g_free(active_call_number); 269 active_call_number = g_strdup(number); 270 271 telephony_update_indicator(dummy_indicators, "callsetup", 272 EV_CALLSETUP_INCOMING); 273 274 active_call_status = CALL_STATUS_INCOMING; 275 active_call_dir = CALL_DIR_INCOMING; 276 277 telephony_incoming_call_ind(number, NUMBER_TYPE_TELEPHONY); 278 279 return dbus_message_new_method_return(msg); 280} 281 282static DBusMessage *cancel_call(DBusConnection *conn, DBusMessage *msg, 283 void *data) 284{ 285 DBG("telephony-dummy: cancel call"); 286 287 g_free(active_call_number); 288 active_call_number = NULL; 289 290 if (telephony_get_indicator(dummy_indicators, "callsetup") > 0) { 291 telephony_update_indicator(dummy_indicators, "callsetup", 292 EV_CALLSETUP_INACTIVE); 293 telephony_calling_stopped_ind(); 294 } 295 296 if (telephony_get_indicator(dummy_indicators, "call") > 0) 297 telephony_update_indicator(dummy_indicators, "call", 298 EV_CALL_INACTIVE); 299 300 return dbus_message_new_method_return(msg); 301} 302 303static DBusMessage *signal_strength(DBusConnection *conn, DBusMessage *msg, 304 void *data) 305{ 306 dbus_uint32_t strength; 307 308 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &strength, 309 DBUS_TYPE_INVALID)) 310 return invalid_args(msg); 311 312 if (strength > 5) 313 return invalid_args(msg); 314 315 telephony_update_indicator(dummy_indicators, "signal", strength); 316 317 DBG("telephony-dummy: signal strength set to %u", strength); 318 319 return dbus_message_new_method_return(msg); 320} 321 322static DBusMessage *battery_level(DBusConnection *conn, DBusMessage *msg, 323 void *data) 324{ 325 dbus_uint32_t level; 326 327 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &level, 328 DBUS_TYPE_INVALID)) 329 return invalid_args(msg); 330 331 if (level > 5) 332 return invalid_args(msg); 333 334 telephony_update_indicator(dummy_indicators, "battchg", level); 335 336 DBG("telephony-dummy: battery level set to %u", level); 337 338 return dbus_message_new_method_return(msg); 339} 340 341static DBusMessage *roaming_status(DBusConnection *conn, DBusMessage *msg, 342 void *data) 343{ 344 dbus_bool_t roaming; 345 int val; 346 347 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &roaming, 348 DBUS_TYPE_INVALID)) 349 return invalid_args(msg); 350 351 val = roaming ? EV_ROAM_ACTIVE : EV_ROAM_INACTIVE; 352 353 telephony_update_indicator(dummy_indicators, "roam", val); 354 355 DBG("telephony-dummy: roaming status set to %d", val); 356 357 return dbus_message_new_method_return(msg); 358} 359 360static DBusMessage *registration_status(DBusConnection *conn, DBusMessage *msg, 361 void *data) 362{ 363 dbus_bool_t registration; 364 int val; 365 366 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, ®istration, 367 DBUS_TYPE_INVALID)) 368 return invalid_args(msg); 369 370 val = registration ? EV_SERVICE_PRESENT : EV_SERVICE_NONE; 371 372 telephony_update_indicator(dummy_indicators, "service", val); 373 374 DBG("telephony-dummy: registration status set to %d", val); 375 376 return dbus_message_new_method_return(msg); 377} 378 379static DBusMessage *set_subscriber_number(DBusConnection *conn, 380 DBusMessage *msg, 381 void *data) 382{ 383 const char *number; 384 385 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number, 386 DBUS_TYPE_INVALID)) 387 return invalid_args(msg); 388 389 g_free(subscriber_number); 390 subscriber_number = g_strdup(number); 391 392 DBG("telephony-dummy: subscriber number set to %s", number); 393 394 return dbus_message_new_method_return(msg); 395} 396 397static GDBusMethodTable dummy_methods[] = { 398 { "OutgoingCall", "s", "", outgoing_call }, 399 { "IncomingCall", "s", "", incoming_call }, 400 { "CancelCall", "", "", cancel_call }, 401 { "SignalStrength", "u", "", signal_strength }, 402 { "BatteryLevel", "u", "", battery_level }, 403 { "RoamingStatus", "b", "", roaming_status }, 404 { "RegistrationStatus", "b", "", registration_status }, 405 { "SetSubscriberNumber","s", "", set_subscriber_number }, 406 { } 407}; 408 409static GDBusSignalTable dummy_signals[] = { 410 { "VoiceDial", "" }, 411 { } 412}; 413 414int telephony_init(void) 415{ 416 uint32_t features = AG_FEATURE_REJECT_A_CALL | 417 AG_FEATURE_ENHANCED_CALL_STATUS | 418 AG_FEATURE_EXTENDED_ERROR_RESULT_CODES; 419 420 connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); 421 422 if (g_dbus_register_interface(connection, TELEPHONY_DUMMY_PATH, 423 TELEPHONY_DUMMY_IFACE, 424 dummy_methods, dummy_signals, 425 NULL, NULL, NULL) == FALSE) { 426 error("telephony-dummy interface %s init failed on path %s", 427 TELEPHONY_DUMMY_IFACE, TELEPHONY_DUMMY_PATH); 428 return -1; 429 } 430 431 telephony_ready_ind(features, dummy_indicators, response_and_hold, 432 chld_str); 433 434 return 0; 435} 436 437void telephony_exit(void) 438{ 439 dbus_connection_unref(connection); 440 connection = NULL; 441} 442