/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <glib.h> #include <dbus/dbus.h> #include <gdbus.h> #include "log.h" #include "telephony.h" #define TELEPHONY_DUMMY_IFACE "org.bluez.TelephonyTest" #define TELEPHONY_DUMMY_PATH "/org/bluez/test" static DBusConnection *connection = NULL; static const char *chld_str = "0,1,1x,2,2x,3,4"; static char *subscriber_number = NULL; static char *active_call_number = NULL; static int active_call_status = 0; static int active_call_dir = 0; static gboolean events_enabled = FALSE; /* Response and hold state * -1 = none * 0 = incoming call is put on hold in the AG * 1 = held incoming call is accepted in the AG * 2 = held incoming call is rejected in the AG */ static int response_and_hold = -1; static struct indicator dummy_indicators[] = { { "battchg", "0-5", 5, TRUE }, { "signal", "0-5", 5, TRUE }, { "service", "0,1", 1, TRUE }, { "call", "0,1", 0, TRUE }, { "callsetup", "0-3", 0, TRUE }, { "callheld", "0-2", 0, FALSE }, { "roam", "0,1", 0, TRUE }, { NULL } }; static inline DBusMessage *invalid_args(DBusMessage *msg) { return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments", "Invalid arguments in method call"); } void telephony_device_connected(void *telephony_device) { DBG("telephony-dummy: device %p connected", telephony_device); } void telephony_device_disconnected(void *telephony_device) { DBG("telephony-dummy: device %p disconnected", telephony_device); events_enabled = FALSE; } void telephony_event_reporting_req(void *telephony_device, int ind) { events_enabled = ind == 1 ? TRUE : FALSE; telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE); } void telephony_response_and_hold_req(void *telephony_device, int rh) { response_and_hold = rh; telephony_response_and_hold_ind(response_and_hold); telephony_response_and_hold_rsp(telephony_device, CME_ERROR_NONE); } void telephony_last_dialed_number_req(void *telephony_device) { telephony_last_dialed_number_rsp(telephony_device, CME_ERROR_NONE); /* Notify outgoing call set-up successfully initiated */ telephony_update_indicator(dummy_indicators, "callsetup", EV_CALLSETUP_OUTGOING); telephony_update_indicator(dummy_indicators, "callsetup", EV_CALLSETUP_ALERTING); active_call_status = CALL_STATUS_ALERTING; active_call_dir = CALL_DIR_OUTGOING; } void telephony_terminate_call_req(void *telephony_device) { g_free(active_call_number); active_call_number = NULL; telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE); if (telephony_get_indicator(dummy_indicators, "callsetup") > 0) telephony_update_indicator(dummy_indicators, "callsetup", EV_CALLSETUP_INACTIVE); else telephony_update_indicator(dummy_indicators, "call", EV_CALL_INACTIVE); } void telephony_answer_call_req(void *telephony_device) { telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE); telephony_update_indicator(dummy_indicators, "call", EV_CALL_ACTIVE); telephony_update_indicator(dummy_indicators, "callsetup", EV_CALLSETUP_INACTIVE); active_call_status = CALL_STATUS_ACTIVE; } void telephony_dial_number_req(void *telephony_device, const char *number) { g_free(active_call_number); active_call_number = g_strdup(number); DBG("telephony-dummy: dial request to %s", active_call_number); telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE); /* Notify outgoing call set-up successfully initiated */ telephony_update_indicator(dummy_indicators, "callsetup", EV_CALLSETUP_OUTGOING); telephony_update_indicator(dummy_indicators, "callsetup", EV_CALLSETUP_ALERTING); active_call_status = CALL_STATUS_ALERTING; active_call_dir = CALL_DIR_OUTGOING; } void telephony_transmit_dtmf_req(void *telephony_device, char tone) { DBG("telephony-dummy: transmit dtmf: %c", tone); telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE); } void telephony_subscriber_number_req(void *telephony_device) { DBG("telephony-dummy: subscriber number request"); if (subscriber_number) telephony_subscriber_number_ind(subscriber_number, NUMBER_TYPE_TELEPHONY, SUBSCRIBER_SERVICE_VOICE); telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE); } void telephony_list_current_calls_req(void *telephony_device) { DBG("telephony-dummy: list current calls request"); if (active_call_number) telephony_list_current_call_ind(1, active_call_dir, active_call_status, CALL_MODE_VOICE, CALL_MULTIPARTY_NO, active_call_number, NUMBER_TYPE_TELEPHONY); telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE); } void telephony_operator_selection_req(void *telephony_device) { telephony_operator_selection_ind(OPERATOR_MODE_AUTO, "DummyOperator"); telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE); } void telephony_call_hold_req(void *telephony_device, const char *cmd) { DBG("telephony-dymmy: got call hold request %s", cmd); telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE); } void telephony_nr_and_ec_req(void *telephony_device, gboolean enable) { DBG("telephony-dummy: got %s NR and EC request", enable ? "enable" : "disable"); telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE); } void telephony_voice_dial_req(void *telephony_device, gboolean enable) { DBG("telephony-dummy: got %s voice dial request", enable ? "enable" : "disable"); g_dbus_emit_signal(connection, TELEPHONY_DUMMY_PATH, TELEPHONY_DUMMY_IFACE, "VoiceDial", DBUS_TYPE_INVALID); telephony_voice_dial_rsp(telephony_device, CME_ERROR_NONE); } void telephony_key_press_req(void *telephony_device, const char *keys) { DBG("telephony-dummy: got key press request for %s", keys); telephony_key_press_rsp(telephony_device, CME_ERROR_NONE); } /* D-Bus method handlers */ static DBusMessage *outgoing_call(DBusConnection *conn, DBusMessage *msg, void *data) { const char *number; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number, DBUS_TYPE_INVALID)) return invalid_args(msg); DBG("telephony-dummy: outgoing call to %s", number); g_free(active_call_number); active_call_number = g_strdup(number); telephony_update_indicator(dummy_indicators, "callsetup", EV_CALLSETUP_OUTGOING); telephony_update_indicator(dummy_indicators, "callsetup", EV_CALLSETUP_ALERTING); active_call_status = CALL_STATUS_ALERTING; active_call_dir = CALL_DIR_OUTGOING; return dbus_message_new_method_return(msg); } static DBusMessage *incoming_call(DBusConnection *conn, DBusMessage *msg, void *data) { const char *number; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number, DBUS_TYPE_INVALID)) return invalid_args(msg); DBG("telephony-dummy: incoming call to %s", number); g_free(active_call_number); active_call_number = g_strdup(number); telephony_update_indicator(dummy_indicators, "callsetup", EV_CALLSETUP_INCOMING); active_call_status = CALL_STATUS_INCOMING; active_call_dir = CALL_DIR_INCOMING; telephony_incoming_call_ind(number, NUMBER_TYPE_TELEPHONY); return dbus_message_new_method_return(msg); } static DBusMessage *cancel_call(DBusConnection *conn, DBusMessage *msg, void *data) { DBG("telephony-dummy: cancel call"); g_free(active_call_number); active_call_number = NULL; if (telephony_get_indicator(dummy_indicators, "callsetup") > 0) { telephony_update_indicator(dummy_indicators, "callsetup", EV_CALLSETUP_INACTIVE); telephony_calling_stopped_ind(); } if (telephony_get_indicator(dummy_indicators, "call") > 0) telephony_update_indicator(dummy_indicators, "call", EV_CALL_INACTIVE); return dbus_message_new_method_return(msg); } static DBusMessage *signal_strength(DBusConnection *conn, DBusMessage *msg, void *data) { dbus_uint32_t strength; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &strength, DBUS_TYPE_INVALID)) return invalid_args(msg); if (strength > 5) return invalid_args(msg); telephony_update_indicator(dummy_indicators, "signal", strength); DBG("telephony-dummy: signal strength set to %u", strength); return dbus_message_new_method_return(msg); } static DBusMessage *battery_level(DBusConnection *conn, DBusMessage *msg, void *data) { dbus_uint32_t level; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &level, DBUS_TYPE_INVALID)) return invalid_args(msg); if (level > 5) return invalid_args(msg); telephony_update_indicator(dummy_indicators, "battchg", level); DBG("telephony-dummy: battery level set to %u", level); return dbus_message_new_method_return(msg); } static DBusMessage *roaming_status(DBusConnection *conn, DBusMessage *msg, void *data) { dbus_bool_t roaming; int val; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &roaming, DBUS_TYPE_INVALID)) return invalid_args(msg); val = roaming ? EV_ROAM_ACTIVE : EV_ROAM_INACTIVE; telephony_update_indicator(dummy_indicators, "roam", val); DBG("telephony-dummy: roaming status set to %d", val); return dbus_message_new_method_return(msg); } static DBusMessage *registration_status(DBusConnection *conn, DBusMessage *msg, void *data) { dbus_bool_t registration; int val; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, ®istration, DBUS_TYPE_INVALID)) return invalid_args(msg); val = registration ? EV_SERVICE_PRESENT : EV_SERVICE_NONE; telephony_update_indicator(dummy_indicators, "service", val); DBG("telephony-dummy: registration status set to %d", val); return dbus_message_new_method_return(msg); } static DBusMessage *set_subscriber_number(DBusConnection *conn, DBusMessage *msg, void *data) { const char *number; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number, DBUS_TYPE_INVALID)) return invalid_args(msg); g_free(subscriber_number); subscriber_number = g_strdup(number); DBG("telephony-dummy: subscriber number set to %s", number); return dbus_message_new_method_return(msg); } static GDBusMethodTable dummy_methods[] = { { "OutgoingCall", "s", "", outgoing_call }, { "IncomingCall", "s", "", incoming_call }, { "CancelCall", "", "", cancel_call }, { "SignalStrength", "u", "", signal_strength }, { "BatteryLevel", "u", "", battery_level }, { "RoamingStatus", "b", "", roaming_status }, { "RegistrationStatus", "b", "", registration_status }, { "SetSubscriberNumber","s", "", set_subscriber_number }, { } }; static GDBusSignalTable dummy_signals[] = { { "VoiceDial", "" }, { } }; int telephony_init(void) { uint32_t features = AG_FEATURE_REJECT_A_CALL | AG_FEATURE_ENHANCED_CALL_STATUS | AG_FEATURE_EXTENDED_ERROR_RESULT_CODES; connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); if (g_dbus_register_interface(connection, TELEPHONY_DUMMY_PATH, TELEPHONY_DUMMY_IFACE, dummy_methods, dummy_signals, NULL, NULL, NULL) == FALSE) { error("telephony-dummy interface %s init failed on path %s", TELEPHONY_DUMMY_IFACE, TELEPHONY_DUMMY_PATH); return -1; } telephony_ready_ind(features, dummy_indicators, response_and_hold, chld_str); return 0; } void telephony_exit(void) { dbus_connection_unref(connection); connection = NULL; }