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