1 /* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6 #include <string.h>
7 #include <stdlib.h>
8 #include <syslog.h>
9
10 #include <dbus/dbus.h>
11
12 #include "cras_telephony.h"
13 #include "cras_hfp_ag_profile.h"
14 #include "cras_hfp_slc.h"
15
16 #define CRAS_TELEPHONY_INTERFACE "org.chromium.cras.Telephony"
17 #define CRAS_TELEPHONY_OBJECT_PATH "/org/chromium/cras/telephony"
18 #define TELEPHONY_INTROSPECT_XML \
19 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
20 "<node>\n" \
21 " <interface name=\"" CRAS_TELEPHONY_INTERFACE "\">\n" \
22 " <method name=\"AnswerCall\">\n" \
23 " </method>\n" \
24 " <method name=\"IncomingCall\">\n" \
25 " <arg name=\"value\" type=\"s\" direction=\"in\"/>\n" \
26 " </method>\n" \
27 " <method name=\"TerminateCall\">\n" \
28 " </method>\n" \
29 " <method name=\"SetBatteryLevel\">\n" \
30 " <arg name=\"value\" type=\"i\" direction=\"in\"/>\n" \
31 " </method>\n" \
32 " <method name=\"SetSignalStrength\">\n" \
33 " <arg name=\"value\" type=\"i\" direction=\"in\"/>\n" \
34 " </method>\n" \
35 " <method name=\"SetServiceAvailability\">\n" \
36 " <arg name=\"value\" type=\"i\" direction=\"in\"/>\n" \
37 " </method>\n" \
38 " <method name=\"SetDialNumber\">\n" \
39 " <arg name=\"value\" type=\"s\" direction=\"in\"/>\n" \
40 " </method>\n" \
41 " <method name=\"SetCallheld\">\n" \
42 " <arg name=\"value\" type=\"i\" direction=\"in\"/>\n" \
43 " </method>\n" \
44 " <method name=\"SetCallsetup\">\n" \
45 " <arg name=\"value\" type=\"i\" direction=\"in\"/>\n" \
46 " </method>\n" \
47 " <method name=\"SetCall\">\n" \
48 " <arg name=\"value\" type=\"i\" direction=\"in\"/>\n" \
49 " </method>\n" \
50 " </interface>\n" \
51 " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n" \
52 " <method name=\"Introspect\">\n" \
53 " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n" \
54 " </method>\n" \
55 " </interface>\n" \
56 "</node>\n"
57
58 static struct cras_telephony_handle telephony_handle;
59
60 /* Helper to extract a single argument from a DBus message. */
get_single_arg(DBusMessage * message,int dbus_type,void * arg)61 static DBusHandlerResult get_single_arg(DBusMessage *message, int dbus_type,
62 void *arg)
63 {
64 DBusError dbus_error;
65
66 dbus_error_init(&dbus_error);
67
68 if (!dbus_message_get_args(message, &dbus_error, dbus_type, arg,
69 DBUS_TYPE_INVALID)) {
70 syslog(LOG_WARNING, "Bad method received: %s",
71 dbus_error.message);
72 dbus_error_free(&dbus_error);
73 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
74 }
75
76 return DBUS_HANDLER_RESULT_HANDLED;
77 }
78
79 /* Helper to send an empty reply. */
send_empty_reply(DBusConnection * conn,DBusMessage * message)80 static void send_empty_reply(DBusConnection *conn, DBusMessage *message)
81 {
82 DBusMessage *reply;
83 dbus_uint32_t serial = 0;
84
85 reply = dbus_message_new_method_return(message);
86 if (!reply)
87 return;
88
89 dbus_connection_send(conn, reply, &serial);
90
91 dbus_message_unref(reply);
92 }
93
handle_incoming_call(DBusConnection * conn,DBusMessage * message,void * arg)94 static DBusHandlerResult handle_incoming_call(DBusConnection *conn,
95 DBusMessage *message, void *arg)
96 {
97 struct hfp_slc_handle *handle;
98 DBusHandlerResult rc;
99 const char *number;
100
101 rc = get_single_arg(message, DBUS_TYPE_STRING, &number);
102 if (rc != DBUS_HANDLER_RESULT_HANDLED)
103 return rc;
104
105 handle = cras_hfp_ag_get_active_handle();
106
107 telephony_handle.callsetup = 1;
108
109 if (handle) {
110 hfp_event_update_callsetup(handle);
111 hfp_event_incoming_call(handle, number, 129);
112 }
113
114 send_empty_reply(conn, message);
115 return DBUS_HANDLER_RESULT_HANDLED;
116 }
117
handle_terminate_call(DBusConnection * conn,DBusMessage * message,void * arg)118 static DBusHandlerResult handle_terminate_call(DBusConnection *conn,
119 DBusMessage *message, void *arg)
120 {
121 cras_telephony_event_terminate_call();
122
123 send_empty_reply(conn, message);
124 return DBUS_HANDLER_RESULT_HANDLED;
125 }
126
handle_answer_call(DBusConnection * conn,DBusMessage * message,void * arg)127 static DBusHandlerResult handle_answer_call(DBusConnection *conn,
128 DBusMessage *message, void *arg)
129 {
130 cras_telephony_event_answer_call();
131
132 send_empty_reply(conn, message);
133 return DBUS_HANDLER_RESULT_HANDLED;
134 }
135
handle_set_dial_number(DBusConnection * conn,DBusMessage * message,void * arg)136 static DBusHandlerResult handle_set_dial_number(DBusConnection *conn,
137 DBusMessage *message, void *arg)
138 {
139 DBusHandlerResult rc;
140 const char *number;
141
142 rc = get_single_arg(message, DBUS_TYPE_STRING, &number);
143 if (rc != DBUS_HANDLER_RESULT_HANDLED)
144 return rc;
145
146 cras_telephony_store_dial_number(strlen(number), number);
147
148 send_empty_reply(conn, message);
149 return DBUS_HANDLER_RESULT_HANDLED;
150 }
151
handle_set_battery(DBusConnection * conn,DBusMessage * message,void * arg)152 static DBusHandlerResult handle_set_battery(DBusConnection *conn,
153 DBusMessage *message, void *arg)
154 {
155 struct hfp_slc_handle *handle;
156 DBusHandlerResult rc;
157 int value;
158
159 rc = get_single_arg(message, DBUS_TYPE_INT32, &value);
160 if (rc != DBUS_HANDLER_RESULT_HANDLED)
161 return rc;
162
163 handle = cras_hfp_ag_get_active_handle();
164 if (handle)
165 hfp_event_set_battery(handle, value);
166
167 send_empty_reply(conn, message);
168 return DBUS_HANDLER_RESULT_HANDLED;
169 }
170
handle_set_signal(DBusConnection * conn,DBusMessage * message,void * arg)171 static DBusHandlerResult handle_set_signal(DBusConnection *conn,
172 DBusMessage *message, void *arg)
173 {
174 struct hfp_slc_handle *handle;
175 DBusHandlerResult rc;
176 int value;
177
178 rc = get_single_arg(message, DBUS_TYPE_INT32, &value);
179 if (rc != DBUS_HANDLER_RESULT_HANDLED)
180 return rc;
181
182 handle = cras_hfp_ag_get_active_handle();
183 if (handle)
184 hfp_event_set_signal(handle, value);
185
186 send_empty_reply(conn, message);
187 return DBUS_HANDLER_RESULT_HANDLED;
188 }
189
handle_set_service(DBusConnection * conn,DBusMessage * message,void * arg)190 static DBusHandlerResult handle_set_service(DBusConnection *conn,
191 DBusMessage *message, void *arg)
192 {
193 struct hfp_slc_handle *handle;
194 DBusHandlerResult rc;
195 int value;
196
197 rc = get_single_arg(message, DBUS_TYPE_INT32, &value);
198 if (rc != DBUS_HANDLER_RESULT_HANDLED)
199 return rc;
200
201 handle = cras_hfp_ag_get_active_handle();
202 if (handle)
203 hfp_event_set_service(handle, value);
204
205 send_empty_reply(conn, message);
206 return DBUS_HANDLER_RESULT_HANDLED;
207 }
208
handle_set_callheld(DBusConnection * conn,DBusMessage * message,void * arg)209 static DBusHandlerResult handle_set_callheld(DBusConnection *conn,
210 DBusMessage *message, void *arg)
211 {
212 struct hfp_slc_handle *handle;
213 DBusHandlerResult rc;
214 int value;
215
216 rc = get_single_arg(message, DBUS_TYPE_INT32, &value);
217 if (rc != DBUS_HANDLER_RESULT_HANDLED)
218 return rc;
219
220 telephony_handle.callheld = value;
221 handle = cras_hfp_ag_get_active_handle();
222 if (handle)
223 hfp_event_update_callheld(handle);
224
225 send_empty_reply(conn, message);
226 return DBUS_HANDLER_RESULT_HANDLED;
227 }
228
handle_set_callsetup(DBusConnection * conn,DBusMessage * message,void * arg)229 static DBusHandlerResult handle_set_callsetup(DBusConnection *conn,
230 DBusMessage *message, void *arg)
231 {
232 struct hfp_slc_handle *handle;
233 DBusHandlerResult rc;
234 int value;
235
236 rc = get_single_arg(message, DBUS_TYPE_INT32, &value);
237 if (rc != DBUS_HANDLER_RESULT_HANDLED)
238 return rc;
239
240 telephony_handle.callsetup = value;
241 handle = cras_hfp_ag_get_active_handle();
242 if (handle)
243 hfp_event_update_callsetup(handle);
244
245 send_empty_reply(conn, message);
246 return DBUS_HANDLER_RESULT_HANDLED;
247 }
248
handle_set_call(DBusConnection * conn,DBusMessage * message,void * arg)249 static DBusHandlerResult handle_set_call(DBusConnection *conn,
250 DBusMessage *message, void *arg)
251 {
252 struct hfp_slc_handle *handle;
253 DBusHandlerResult rc;
254 int value;
255
256 rc = get_single_arg(message, DBUS_TYPE_INT32, &value);
257 if (rc != DBUS_HANDLER_RESULT_HANDLED)
258 return rc;
259
260 telephony_handle.call = value;
261 handle = cras_hfp_ag_get_active_handle();
262 if (handle)
263 hfp_event_update_call(handle);
264
265 send_empty_reply(conn, message);
266 return DBUS_HANDLER_RESULT_HANDLED;
267 }
268
269 /* Handle incoming messages. */
270 static DBusHandlerResult
handle_telephony_message(DBusConnection * conn,DBusMessage * message,void * arg)271 handle_telephony_message(DBusConnection *conn, DBusMessage *message, void *arg)
272 {
273 syslog(LOG_ERR, "Telephony message: %s %s %s",
274 dbus_message_get_path(message),
275 dbus_message_get_interface(message),
276 dbus_message_get_member(message));
277
278 if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE,
279 "Introspect")) {
280 DBusMessage *reply;
281 const char *xml = TELEPHONY_INTROSPECT_XML;
282
283 reply = dbus_message_new_method_return(message);
284 if (!reply)
285 return DBUS_HANDLER_RESULT_NEED_MEMORY;
286 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &xml,
287 DBUS_TYPE_INVALID))
288 return DBUS_HANDLER_RESULT_NEED_MEMORY;
289 if (!dbus_connection_send(conn, reply, NULL))
290 return DBUS_HANDLER_RESULT_NEED_MEMORY;
291
292 dbus_message_unref(reply);
293 return DBUS_HANDLER_RESULT_HANDLED;
294 } else if (dbus_message_is_method_call(
295 message, CRAS_TELEPHONY_INTERFACE, "IncomingCall")) {
296 return handle_incoming_call(conn, message, arg);
297 } else if (dbus_message_is_method_call(message,
298 CRAS_TELEPHONY_INTERFACE,
299 "TerminateCall")) {
300 return handle_terminate_call(conn, message, arg);
301 } else if (dbus_message_is_method_call(
302 message, CRAS_TELEPHONY_INTERFACE, "AnswerCall")) {
303 return handle_answer_call(conn, message, arg);
304 } else if (dbus_message_is_method_call(message,
305 CRAS_TELEPHONY_INTERFACE,
306 "SetDialNumber")) {
307 return handle_set_dial_number(conn, message, arg);
308 } else if (dbus_message_is_method_call(message,
309 CRAS_TELEPHONY_INTERFACE,
310 "SetBatteryLevel")) {
311 return handle_set_battery(conn, message, arg);
312 } else if (dbus_message_is_method_call(message,
313 CRAS_TELEPHONY_INTERFACE,
314 "SetSignalStrength")) {
315 return handle_set_signal(conn, message, arg);
316 } else if (dbus_message_is_method_call(message,
317 CRAS_TELEPHONY_INTERFACE,
318 "SetServiceAvailability")) {
319 return handle_set_service(conn, message, arg);
320 } else if (dbus_message_is_method_call(
321 message, CRAS_TELEPHONY_INTERFACE, "SetCallheld")) {
322 return handle_set_callheld(conn, message, arg);
323 } else if (dbus_message_is_method_call(
324 message, CRAS_TELEPHONY_INTERFACE, "SetCallsetup")) {
325 return handle_set_callsetup(conn, message, arg);
326 } else if (dbus_message_is_method_call(
327 message, CRAS_TELEPHONY_INTERFACE, "SetCall")) {
328 return handle_set_call(conn, message, arg);
329 }
330
331 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
332 }
333
334 /* Exported Interface */
335
cras_telephony_start(DBusConnection * conn)336 void cras_telephony_start(DBusConnection *conn)
337 {
338 static const DBusObjectPathVTable control_vtable = {
339 .message_function = handle_telephony_message,
340 };
341
342 DBusError dbus_error;
343
344 telephony_handle.dbus_conn = conn;
345 dbus_connection_ref(telephony_handle.dbus_conn);
346
347 if (!dbus_connection_register_object_path(
348 conn, CRAS_TELEPHONY_OBJECT_PATH, &control_vtable,
349 &dbus_error)) {
350 syslog(LOG_ERR, "Couldn't register telephony control: %s: %s",
351 CRAS_TELEPHONY_OBJECT_PATH, dbus_error.message);
352 dbus_error_free(&dbus_error);
353 return;
354 }
355 }
356
cras_telephony_stop()357 void cras_telephony_stop()
358 {
359 if (!telephony_handle.dbus_conn)
360 return;
361
362 dbus_connection_unregister_object_path(telephony_handle.dbus_conn,
363 CRAS_TELEPHONY_OBJECT_PATH);
364 dbus_connection_unref(telephony_handle.dbus_conn);
365 telephony_handle.dbus_conn = NULL;
366 }
367
cras_telephony_get()368 struct cras_telephony_handle *cras_telephony_get()
369 {
370 return &telephony_handle;
371 }
372
373 /* Procedure to answer a call from AG.
374 *
375 * HF(hands-free) AG(audio gateway)
376 * <-- Call answered
377 * <-- +CIEV: (call = 1)
378 * <-- +CIEV: (callsetup = 0)
379 */
cras_telephony_event_answer_call()380 int cras_telephony_event_answer_call()
381 {
382 int rc;
383
384 struct hfp_slc_handle *handle;
385
386 handle = cras_hfp_ag_get_active_handle();
387
388 if (telephony_handle.call == 0) {
389 telephony_handle.call = 1;
390 if (handle) {
391 rc = hfp_event_update_call(handle);
392 if (rc)
393 return rc;
394 }
395 }
396
397 telephony_handle.callsetup = 0;
398 if (handle) {
399 rc = hfp_event_update_callsetup(handle);
400 if (rc)
401 return rc;
402 }
403
404 return 0;
405 }
406
407 /* Procedure to terminate a call from AG.
408 *
409 * HF(hands-free) AG(audio gateway)
410 * <-- Call dropped
411 * <-- +CIEV: (call = 0)
412 */
cras_telephony_event_terminate_call()413 int cras_telephony_event_terminate_call()
414 {
415 int rc;
416 struct hfp_slc_handle *handle;
417
418 handle = cras_hfp_ag_get_active_handle();
419
420 if (telephony_handle.call) {
421 telephony_handle.call = 0;
422 if (handle) {
423 rc = hfp_event_update_call(handle);
424 if (rc)
425 return rc;
426 }
427 }
428 if (telephony_handle.callsetup) {
429 telephony_handle.callsetup = 0;
430 if (handle) {
431 rc = hfp_event_update_callsetup(handle);
432 if (rc)
433 return rc;
434 }
435 }
436 return 0;
437 }
438
cras_telephony_store_dial_number(int len,const char * number)439 void cras_telephony_store_dial_number(int len, const char *number)
440 {
441 if (telephony_handle.dial_number != NULL) {
442 free(telephony_handle.dial_number);
443 telephony_handle.dial_number = NULL;
444 }
445
446 if (len == 0)
447 return;
448
449 telephony_handle.dial_number =
450 (char *)calloc(len + 1, sizeof(*telephony_handle.dial_number));
451 strncpy(telephony_handle.dial_number, number, len);
452
453 syslog(LOG_ERR, "store dial_number: \"%s\"",
454 telephony_handle.dial_number);
455 }
456