• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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