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 (at) 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 65 void telephony_device_connected(void *telephony_device) 66 { 67 DBG("telephony-dummy: device %p connected", telephony_device); 68 } 69 70 void telephony_device_disconnected(void *telephony_device) 71 { 72 DBG("telephony-dummy: device %p disconnected", telephony_device); 73 events_enabled = FALSE; 74 } 75 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 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 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 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 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 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 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 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 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 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 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 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 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 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 */ 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 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 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 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 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 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 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 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 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 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