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 " </interface>\n" \
45 " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n" \
46 " <method name=\"Introspect\">\n" \
47 " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n" \
48 " </method>\n" \
49 " </interface>\n" \
50 "</node>\n"
51
52 static struct cras_telephony_handle telephony_handle;
53
54 /* Helper to extract a single argument from a DBus message. */
get_single_arg(DBusMessage * message,int dbus_type,void * arg)55 static DBusHandlerResult get_single_arg(DBusMessage *message, int dbus_type,
56 void *arg)
57 {
58 DBusError dbus_error;
59
60 dbus_error_init(&dbus_error);
61
62 if (!dbus_message_get_args(message, &dbus_error, dbus_type, arg,
63 DBUS_TYPE_INVALID)) {
64 syslog(LOG_WARNING, "Bad method received: %s",
65 dbus_error.message);
66 dbus_error_free(&dbus_error);
67 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
68 }
69
70 return DBUS_HANDLER_RESULT_HANDLED;
71 }
72
73 /* Helper to send an empty reply. */
send_empty_reply(DBusConnection * conn,DBusMessage * message)74 static void send_empty_reply(DBusConnection *conn, DBusMessage *message)
75 {
76 DBusMessage *reply;
77 dbus_uint32_t serial = 0;
78
79 reply = dbus_message_new_method_return(message);
80 if (!reply)
81 return;
82
83 dbus_connection_send(conn, reply, &serial);
84
85 dbus_message_unref(reply);
86 }
87
handle_incoming_call(DBusConnection * conn,DBusMessage * message,void * arg)88 static DBusHandlerResult handle_incoming_call(DBusConnection *conn,
89 DBusMessage *message, void *arg)
90 {
91 struct hfp_slc_handle *handle;
92 DBusHandlerResult rc;
93 const char *number;
94
95 rc = get_single_arg(message, DBUS_TYPE_STRING, &number);
96 if (rc != DBUS_HANDLER_RESULT_HANDLED)
97 return rc;
98
99 handle = cras_hfp_ag_get_active_handle();
100
101 telephony_handle.callsetup = 1;
102
103 if (handle) {
104 hfp_event_update_callsetup(handle);
105 hfp_event_incoming_call(handle, number, 129);
106 }
107
108 send_empty_reply(conn, message);
109 return DBUS_HANDLER_RESULT_HANDLED;
110 }
111
handle_terminate_call(DBusConnection * conn,DBusMessage * message,void * arg)112 static DBusHandlerResult handle_terminate_call(DBusConnection *conn,
113 DBusMessage *message, void *arg)
114 {
115 cras_telephony_event_terminate_call();
116
117 send_empty_reply(conn, message);
118 return DBUS_HANDLER_RESULT_HANDLED;
119 }
120
handle_answer_call(DBusConnection * conn,DBusMessage * message,void * arg)121 static DBusHandlerResult handle_answer_call(DBusConnection *conn,
122 DBusMessage *message, void *arg)
123 {
124 cras_telephony_event_answer_call();
125
126 send_empty_reply(conn, message);
127 return DBUS_HANDLER_RESULT_HANDLED;
128 }
129
handle_set_dial_number(DBusConnection * conn,DBusMessage * message,void * arg)130 static DBusHandlerResult handle_set_dial_number(DBusConnection *conn,
131 DBusMessage *message, void *arg)
132 {
133 DBusHandlerResult rc;
134 const char *number;
135
136 rc = get_single_arg(message, DBUS_TYPE_STRING, &number);
137 if (rc != DBUS_HANDLER_RESULT_HANDLED)
138 return rc;
139
140 cras_telephony_store_dial_number(strlen(number), number);
141
142 send_empty_reply(conn, message);
143 return DBUS_HANDLER_RESULT_HANDLED;
144 }
145
handle_set_battery(DBusConnection * conn,DBusMessage * message,void * arg)146 static DBusHandlerResult handle_set_battery(DBusConnection *conn,
147 DBusMessage *message, void *arg)
148 {
149 struct hfp_slc_handle *handle;
150 DBusHandlerResult rc;
151 int value;
152
153 rc = get_single_arg(message, DBUS_TYPE_INT32, &value);
154 if (rc != DBUS_HANDLER_RESULT_HANDLED)
155 return rc;
156
157 handle = cras_hfp_ag_get_active_handle();
158 if (handle)
159 hfp_event_set_battery(handle, value);
160
161 send_empty_reply(conn, message);
162 return DBUS_HANDLER_RESULT_HANDLED;
163 }
164
handle_set_signal(DBusConnection * conn,DBusMessage * message,void * arg)165 static DBusHandlerResult handle_set_signal(DBusConnection *conn,
166 DBusMessage *message, void *arg)
167 {
168 struct hfp_slc_handle *handle;
169 DBusHandlerResult rc;
170 int value;
171
172 rc = get_single_arg(message, DBUS_TYPE_INT32, &value);
173 if (rc != DBUS_HANDLER_RESULT_HANDLED)
174 return rc;
175
176 handle = cras_hfp_ag_get_active_handle();
177 if (handle)
178 hfp_event_set_signal(handle, value);
179
180 send_empty_reply(conn, message);
181 return DBUS_HANDLER_RESULT_HANDLED;
182 }
183
handle_set_service(DBusConnection * conn,DBusMessage * message,void * arg)184 static DBusHandlerResult handle_set_service(DBusConnection *conn,
185 DBusMessage *message, void *arg)
186 {
187 struct hfp_slc_handle *handle;
188 DBusHandlerResult rc;
189 int value;
190
191 rc = get_single_arg(message, DBUS_TYPE_INT32, &value);
192 if (rc != DBUS_HANDLER_RESULT_HANDLED)
193 return rc;
194
195 handle = cras_hfp_ag_get_active_handle();
196 if (handle)
197 hfp_event_set_service(handle, value);
198
199 send_empty_reply(conn, message);
200 return DBUS_HANDLER_RESULT_HANDLED;
201 }
202
handle_set_callheld(DBusConnection * conn,DBusMessage * message,void * arg)203 static DBusHandlerResult handle_set_callheld(DBusConnection *conn,
204 DBusMessage *message, void *arg)
205 {
206 struct hfp_slc_handle *handle;
207 DBusHandlerResult rc;
208 int value;
209
210 rc = get_single_arg(message, DBUS_TYPE_INT32, &value);
211 if (rc != DBUS_HANDLER_RESULT_HANDLED)
212 return rc;
213
214 telephony_handle.callheld = value;
215 handle = cras_hfp_ag_get_active_handle();
216 if (handle)
217 hfp_event_update_callheld(handle);
218
219 send_empty_reply(conn, message);
220 return DBUS_HANDLER_RESULT_HANDLED;
221 }
222
223 /* Handle incoming messages. */
224 static DBusHandlerResult
handle_telephony_message(DBusConnection * conn,DBusMessage * message,void * arg)225 handle_telephony_message(DBusConnection *conn, DBusMessage *message, void *arg)
226 {
227 syslog(LOG_ERR, "Telephony message: %s %s %s",
228 dbus_message_get_path(message),
229 dbus_message_get_interface(message),
230 dbus_message_get_member(message));
231
232 if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE,
233 "Introspect")) {
234 DBusMessage *reply;
235 const char *xml = TELEPHONY_INTROSPECT_XML;
236
237 reply = dbus_message_new_method_return(message);
238 if (!reply)
239 return DBUS_HANDLER_RESULT_NEED_MEMORY;
240 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &xml,
241 DBUS_TYPE_INVALID))
242 return DBUS_HANDLER_RESULT_NEED_MEMORY;
243 if (!dbus_connection_send(conn, reply, NULL))
244 return DBUS_HANDLER_RESULT_NEED_MEMORY;
245
246 dbus_message_unref(reply);
247 return DBUS_HANDLER_RESULT_HANDLED;
248 } else if (dbus_message_is_method_call(
249 message, CRAS_TELEPHONY_INTERFACE, "IncomingCall")) {
250 return handle_incoming_call(conn, message, arg);
251 } else if (dbus_message_is_method_call(message,
252 CRAS_TELEPHONY_INTERFACE,
253 "TerminateCall")) {
254 return handle_terminate_call(conn, message, arg);
255 } else if (dbus_message_is_method_call(
256 message, CRAS_TELEPHONY_INTERFACE, "AnswerCall")) {
257 return handle_answer_call(conn, message, arg);
258 } else if (dbus_message_is_method_call(message,
259 CRAS_TELEPHONY_INTERFACE,
260 "SetDialNumber")) {
261 return handle_set_dial_number(conn, message, arg);
262 } else if (dbus_message_is_method_call(message,
263 CRAS_TELEPHONY_INTERFACE,
264 "SetBatteryLevel")) {
265 return handle_set_battery(conn, message, arg);
266 } else if (dbus_message_is_method_call(message,
267 CRAS_TELEPHONY_INTERFACE,
268 "SetSignalStrength")) {
269 return handle_set_signal(conn, message, arg);
270 } else if (dbus_message_is_method_call(message,
271 CRAS_TELEPHONY_INTERFACE,
272 "SetServiceAvailability")) {
273 return handle_set_service(conn, message, arg);
274 } else if (dbus_message_is_method_call(
275 message, CRAS_TELEPHONY_INTERFACE, "SetCallheld")) {
276 return handle_set_callheld(conn, message, arg);
277 }
278
279 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
280 }
281
282 /* Exported Interface */
283
cras_telephony_start(DBusConnection * conn)284 void cras_telephony_start(DBusConnection *conn)
285 {
286 static const DBusObjectPathVTable control_vtable = {
287 .message_function = handle_telephony_message,
288 };
289
290 DBusError dbus_error;
291
292 telephony_handle.dbus_conn = conn;
293 dbus_connection_ref(telephony_handle.dbus_conn);
294
295 if (!dbus_connection_register_object_path(
296 conn, CRAS_TELEPHONY_OBJECT_PATH, &control_vtable,
297 &dbus_error)) {
298 syslog(LOG_ERR, "Couldn't register telephony control: %s: %s",
299 CRAS_TELEPHONY_OBJECT_PATH, dbus_error.message);
300 dbus_error_free(&dbus_error);
301 return;
302 }
303 }
304
cras_telephony_stop()305 void cras_telephony_stop()
306 {
307 if (!telephony_handle.dbus_conn)
308 return;
309
310 dbus_connection_unregister_object_path(telephony_handle.dbus_conn,
311 CRAS_TELEPHONY_OBJECT_PATH);
312 dbus_connection_unref(telephony_handle.dbus_conn);
313 telephony_handle.dbus_conn = NULL;
314 }
315
cras_telephony_get()316 struct cras_telephony_handle *cras_telephony_get()
317 {
318 return &telephony_handle;
319 }
320
321 /* Procedure to answer a call from AG.
322 *
323 * HF(hands-free) AG(audio gateway)
324 * <-- Call answered
325 * <-- +CIEV: (call = 1)
326 * <-- +CIEV: (callsetup = 0)
327 */
cras_telephony_event_answer_call()328 int cras_telephony_event_answer_call()
329 {
330 int rc;
331
332 struct hfp_slc_handle *handle;
333
334 handle = cras_hfp_ag_get_active_handle();
335
336 if (telephony_handle.call == 0) {
337 telephony_handle.call = 1;
338 if (handle) {
339 rc = hfp_event_update_call(handle);
340 if (rc)
341 return rc;
342 }
343 }
344
345 telephony_handle.callsetup = 0;
346 if (handle) {
347 rc = hfp_event_update_callsetup(handle);
348 if (rc)
349 return rc;
350 }
351
352 return 0;
353 }
354
355 /* Procedure to terminate a call from AG.
356 *
357 * HF(hands-free) AG(audio gateway)
358 * <-- Call dropped
359 * <-- +CIEV: (call = 0)
360 */
cras_telephony_event_terminate_call()361 int cras_telephony_event_terminate_call()
362 {
363 int rc;
364 struct hfp_slc_handle *handle;
365
366 handle = cras_hfp_ag_get_active_handle();
367
368 if (telephony_handle.call) {
369 telephony_handle.call = 0;
370 if (handle) {
371 rc = hfp_event_update_call(handle);
372 if (rc)
373 return rc;
374 }
375 }
376 if (telephony_handle.callsetup) {
377 telephony_handle.callsetup = 0;
378 if (handle) {
379 rc = hfp_event_update_callsetup(handle);
380 if (rc)
381 return rc;
382 }
383 }
384 return 0;
385 }
386
cras_telephony_store_dial_number(int len,const char * number)387 void cras_telephony_store_dial_number(int len, const char *number)
388 {
389 if (telephony_handle.dial_number != NULL) {
390 free(telephony_handle.dial_number);
391 telephony_handle.dial_number = NULL;
392 }
393
394 if (len == 0)
395 return;
396
397 telephony_handle.dial_number =
398 (char *)calloc(len + 1, sizeof(*telephony_handle.dial_number));
399 strncpy(telephony_handle.dial_number, number, len);
400
401 syslog(LOG_ERR, "store dial_number: \"%s\"",
402 telephony_handle.dial_number);
403 }
404