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