/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include #include #include #include #include "cras_telephony.h" #include "cras_hfp_ag_profile.h" #include "cras_hfp_slc.h" #define CRAS_TELEPHONY_INTERFACE "org.chromium.cras.Telephony" #define CRAS_TELEPHONY_OBJECT_PATH "/org/chromium/cras/telephony" #define TELEPHONY_INTROSPECT_XML \ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ "\n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ "\n" static struct cras_telephony_handle telephony_handle; /* Helper to extract a single argument from a DBus message. */ static DBusHandlerResult get_single_arg(DBusMessage *message, int dbus_type, void *arg) { DBusError dbus_error; dbus_error_init(&dbus_error); if (!dbus_message_get_args(message, &dbus_error, dbus_type, arg, DBUS_TYPE_INVALID)) { syslog(LOG_WARNING, "Bad method received: %s", dbus_error.message); dbus_error_free(&dbus_error); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } return DBUS_HANDLER_RESULT_HANDLED; } /* Helper to send an empty reply. */ static void send_empty_reply(DBusConnection *conn, DBusMessage *message) { DBusMessage *reply; dbus_uint32_t serial = 0; reply = dbus_message_new_method_return(message); if (!reply) return; dbus_connection_send(conn, reply, &serial); dbus_message_unref(reply); } static DBusHandlerResult handle_incoming_call(DBusConnection *conn, DBusMessage *message, void *arg) { struct hfp_slc_handle *handle; DBusHandlerResult rc; const char *number; rc = get_single_arg(message, DBUS_TYPE_STRING, &number); if (rc != DBUS_HANDLER_RESULT_HANDLED) return rc; handle = cras_hfp_ag_get_active_handle(); telephony_handle.callsetup = 1; if (handle) { hfp_event_update_callsetup(handle); hfp_event_incoming_call(handle, number, 129); } send_empty_reply(conn, message); return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult handle_terminate_call(DBusConnection *conn, DBusMessage *message, void *arg) { cras_telephony_event_terminate_call(); send_empty_reply(conn, message); return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult handle_answer_call(DBusConnection *conn, DBusMessage *message, void *arg) { cras_telephony_event_answer_call(); send_empty_reply(conn, message); return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult handle_set_dial_number(DBusConnection *conn, DBusMessage *message, void *arg) { DBusHandlerResult rc; const char *number; rc = get_single_arg(message, DBUS_TYPE_STRING, &number); if (rc != DBUS_HANDLER_RESULT_HANDLED) return rc; cras_telephony_store_dial_number(strlen(number), number); send_empty_reply(conn, message); return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult handle_set_battery(DBusConnection *conn, DBusMessage *message, void *arg) { struct hfp_slc_handle *handle; DBusHandlerResult rc; int value; rc = get_single_arg(message, DBUS_TYPE_INT32, &value); if (rc != DBUS_HANDLER_RESULT_HANDLED) return rc; handle = cras_hfp_ag_get_active_handle(); if (handle) hfp_event_set_battery(handle, value); send_empty_reply(conn, message); return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult handle_set_signal(DBusConnection *conn, DBusMessage *message, void *arg) { struct hfp_slc_handle *handle; DBusHandlerResult rc; int value; rc = get_single_arg(message, DBUS_TYPE_INT32, &value); if (rc != DBUS_HANDLER_RESULT_HANDLED) return rc; handle = cras_hfp_ag_get_active_handle(); if (handle) hfp_event_set_signal(handle, value); send_empty_reply(conn, message); return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult handle_set_service(DBusConnection *conn, DBusMessage *message, void *arg) { struct hfp_slc_handle *handle; DBusHandlerResult rc; int value; rc = get_single_arg(message, DBUS_TYPE_INT32, &value); if (rc != DBUS_HANDLER_RESULT_HANDLED) return rc; handle = cras_hfp_ag_get_active_handle(); if (handle) hfp_event_set_service(handle, value); send_empty_reply(conn, message); return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult handle_set_callheld(DBusConnection *conn, DBusMessage *message, void *arg) { struct hfp_slc_handle *handle; DBusHandlerResult rc; int value; rc = get_single_arg(message, DBUS_TYPE_INT32, &value); if (rc != DBUS_HANDLER_RESULT_HANDLED) return rc; telephony_handle.callheld = value; handle = cras_hfp_ag_get_active_handle(); if (handle) hfp_event_update_callheld(handle); send_empty_reply(conn, message); return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult handle_set_callsetup(DBusConnection *conn, DBusMessage *message, void *arg) { struct hfp_slc_handle *handle; DBusHandlerResult rc; int value; rc = get_single_arg(message, DBUS_TYPE_INT32, &value); if (rc != DBUS_HANDLER_RESULT_HANDLED) return rc; telephony_handle.callsetup = value; handle = cras_hfp_ag_get_active_handle(); if (handle) hfp_event_update_callsetup(handle); send_empty_reply(conn, message); return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult handle_set_call(DBusConnection *conn, DBusMessage *message, void *arg) { struct hfp_slc_handle *handle; DBusHandlerResult rc; int value; rc = get_single_arg(message, DBUS_TYPE_INT32, &value); if (rc != DBUS_HANDLER_RESULT_HANDLED) return rc; telephony_handle.call = value; handle = cras_hfp_ag_get_active_handle(); if (handle) hfp_event_update_call(handle); send_empty_reply(conn, message); return DBUS_HANDLER_RESULT_HANDLED; } /* Handle incoming messages. */ static DBusHandlerResult handle_telephony_message(DBusConnection *conn, DBusMessage *message, void *arg) { syslog(LOG_ERR, "Telephony message: %s %s %s", dbus_message_get_path(message), dbus_message_get_interface(message), dbus_message_get_member(message)); if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) { DBusMessage *reply; const char *xml = TELEPHONY_INTROSPECT_XML; reply = dbus_message_new_method_return(message); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID)) return DBUS_HANDLER_RESULT_NEED_MEMORY; if (!dbus_connection_send(conn, reply, NULL)) return DBUS_HANDLER_RESULT_NEED_MEMORY; dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_method_call( message, CRAS_TELEPHONY_INTERFACE, "IncomingCall")) { return handle_incoming_call(conn, message, arg); } else if (dbus_message_is_method_call(message, CRAS_TELEPHONY_INTERFACE, "TerminateCall")) { return handle_terminate_call(conn, message, arg); } else if (dbus_message_is_method_call( message, CRAS_TELEPHONY_INTERFACE, "AnswerCall")) { return handle_answer_call(conn, message, arg); } else if (dbus_message_is_method_call(message, CRAS_TELEPHONY_INTERFACE, "SetDialNumber")) { return handle_set_dial_number(conn, message, arg); } else if (dbus_message_is_method_call(message, CRAS_TELEPHONY_INTERFACE, "SetBatteryLevel")) { return handle_set_battery(conn, message, arg); } else if (dbus_message_is_method_call(message, CRAS_TELEPHONY_INTERFACE, "SetSignalStrength")) { return handle_set_signal(conn, message, arg); } else if (dbus_message_is_method_call(message, CRAS_TELEPHONY_INTERFACE, "SetServiceAvailability")) { return handle_set_service(conn, message, arg); } else if (dbus_message_is_method_call( message, CRAS_TELEPHONY_INTERFACE, "SetCallheld")) { return handle_set_callheld(conn, message, arg); } else if (dbus_message_is_method_call( message, CRAS_TELEPHONY_INTERFACE, "SetCallsetup")) { return handle_set_callsetup(conn, message, arg); } else if (dbus_message_is_method_call( message, CRAS_TELEPHONY_INTERFACE, "SetCall")) { return handle_set_call(conn, message, arg); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } /* Exported Interface */ void cras_telephony_start(DBusConnection *conn) { static const DBusObjectPathVTable control_vtable = { .message_function = handle_telephony_message, }; DBusError dbus_error; telephony_handle.dbus_conn = conn; dbus_connection_ref(telephony_handle.dbus_conn); if (!dbus_connection_register_object_path( conn, CRAS_TELEPHONY_OBJECT_PATH, &control_vtable, &dbus_error)) { syslog(LOG_ERR, "Couldn't register telephony control: %s: %s", CRAS_TELEPHONY_OBJECT_PATH, dbus_error.message); dbus_error_free(&dbus_error); return; } } void cras_telephony_stop() { if (!telephony_handle.dbus_conn) return; dbus_connection_unregister_object_path(telephony_handle.dbus_conn, CRAS_TELEPHONY_OBJECT_PATH); dbus_connection_unref(telephony_handle.dbus_conn); telephony_handle.dbus_conn = NULL; } struct cras_telephony_handle *cras_telephony_get() { return &telephony_handle; } /* Procedure to answer a call from AG. * * HF(hands-free) AG(audio gateway) * <-- Call answered * <-- +CIEV: (call = 1) * <-- +CIEV: (callsetup = 0) */ int cras_telephony_event_answer_call() { int rc; struct hfp_slc_handle *handle; handle = cras_hfp_ag_get_active_handle(); if (telephony_handle.call == 0) { telephony_handle.call = 1; if (handle) { rc = hfp_event_update_call(handle); if (rc) return rc; } } telephony_handle.callsetup = 0; if (handle) { rc = hfp_event_update_callsetup(handle); if (rc) return rc; } return 0; } /* Procedure to terminate a call from AG. * * HF(hands-free) AG(audio gateway) * <-- Call dropped * <-- +CIEV: (call = 0) */ int cras_telephony_event_terminate_call() { int rc; struct hfp_slc_handle *handle; handle = cras_hfp_ag_get_active_handle(); if (telephony_handle.call) { telephony_handle.call = 0; if (handle) { rc = hfp_event_update_call(handle); if (rc) return rc; } } if (telephony_handle.callsetup) { telephony_handle.callsetup = 0; if (handle) { rc = hfp_event_update_callsetup(handle); if (rc) return rc; } } return 0; } void cras_telephony_store_dial_number(int len, const char *number) { if (telephony_handle.dial_number != NULL) { free(telephony_handle.dial_number); telephony_handle.dial_number = NULL; } if (len == 0) return; telephony_handle.dial_number = (char *)calloc(len + 1, sizeof(*telephony_handle.dial_number)); strncpy(telephony_handle.dial_number, number, len); syslog(LOG_ERR, "store dial_number: \"%s\"", telephony_handle.dial_number); }