• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2020 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 #include <dbus/dbus.h>
6 #include <errno.h>
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <syslog.h>
11 
12 #include "cras_bt_adapter.h"
13 #include "cras_bt_battery_provider.h"
14 #include "cras_bt_constants.h"
15 #include "cras_dbus_util.h"
16 #include "cras_observer.h"
17 #include "utlist.h"
18 
19 /* CRAS registers one battery provider to BlueZ, so we use a singleton. */
20 static struct cras_bt_battery_provider battery_provider = {
21 	.object_path = CRAS_DEFAULT_BATTERY_PROVIDER,
22 	.interface = BLUEZ_INTERFACE_BATTERY_PROVIDER,
23 	.conn = NULL,
24 	.is_registered = false,
25 	.observer = NULL,
26 	.batteries = NULL,
27 };
28 
cmp_battery_address(const struct cras_bt_battery * battery,const char * address)29 static int cmp_battery_address(const struct cras_bt_battery *battery,
30 			       const char *address)
31 {
32 	return strcmp(battery->address, address);
33 }
34 
replace_colon_with_underscore(char * str)35 static void replace_colon_with_underscore(char *str)
36 {
37 	for (int i = 0; str[i]; i++) {
38 		if (str[i] == ':')
39 			str[i] = '_';
40 	}
41 }
42 
43 /* Converts address XX:XX:XX:XX:XX:XX to Battery Provider object path:
44  * /org/chromium/Cras/Bluetooth/BatteryProvider/XX_XX_XX_XX_XX_XX
45  */
address_to_battery_path(const char * address)46 static char *address_to_battery_path(const char *address)
47 {
48 	char *object_path = malloc(strlen(CRAS_DEFAULT_BATTERY_PROVIDER) +
49 				   strlen(address) + 2);
50 
51 	sprintf(object_path, "%s/%s", CRAS_DEFAULT_BATTERY_PROVIDER, address);
52 	replace_colon_with_underscore(object_path);
53 
54 	return object_path;
55 }
56 
57 /* Converts address XX:XX:XX:XX:XX:XX to device object path:
58  * /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX
59  */
address_to_device_path(const char * address)60 static char *address_to_device_path(const char *address)
61 {
62 	char *object_path = malloc(strlen(CRAS_DEFAULT_BATTERY_PREFIX) +
63 				   strlen(address) + 1);
64 
65 	sprintf(object_path, "%s%s", CRAS_DEFAULT_BATTERY_PREFIX, address);
66 	replace_colon_with_underscore(object_path);
67 
68 	return object_path;
69 }
70 
battery_new(const char * address,uint32_t level)71 static struct cras_bt_battery *battery_new(const char *address, uint32_t level)
72 {
73 	struct cras_bt_battery *battery;
74 
75 	battery = calloc(1, sizeof(struct cras_bt_battery));
76 	battery->address = strdup(address);
77 	battery->object_path = address_to_battery_path(address);
78 	battery->device_path = address_to_device_path(address);
79 	battery->level = level;
80 
81 	return battery;
82 }
83 
battery_free(struct cras_bt_battery * battery)84 static void battery_free(struct cras_bt_battery *battery)
85 {
86 	if (battery->address)
87 		free(battery->address);
88 	if (battery->object_path)
89 		free(battery->object_path);
90 	if (battery->device_path)
91 		free(battery->device_path);
92 	free(battery);
93 }
94 
populate_battery_properties(DBusMessageIter * iter,const struct cras_bt_battery * battery)95 static void populate_battery_properties(DBusMessageIter *iter,
96 					const struct cras_bt_battery *battery)
97 {
98 	DBusMessageIter dict, entry, variant;
99 	const char *property_percentage = "Percentage";
100 	const char *property_device = "Device";
101 	uint8_t level = battery->level;
102 
103 	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
104 
105 	dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL,
106 					 &entry);
107 	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
108 				       &property_percentage);
109 	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
110 					 DBUS_TYPE_BYTE_AS_STRING, &variant);
111 	dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &level);
112 	dbus_message_iter_close_container(&entry, &variant);
113 	dbus_message_iter_close_container(&dict, &entry);
114 
115 	dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL,
116 					 &entry);
117 	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
118 				       &property_device);
119 	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
120 					 DBUS_TYPE_OBJECT_PATH_AS_STRING,
121 					 &variant);
122 	dbus_message_iter_append_basic(&variant, DBUS_TYPE_OBJECT_PATH,
123 				       &battery->device_path);
124 	dbus_message_iter_close_container(&entry, &variant);
125 	dbus_message_iter_close_container(&dict, &entry);
126 
127 	dbus_message_iter_close_container(iter, &dict);
128 }
129 
130 /* Creates a new battery object and exposes it on D-Bus. */
131 static struct cras_bt_battery *
get_or_create_battery(struct cras_bt_battery_provider * provider,const char * address,uint32_t level)132 get_or_create_battery(struct cras_bt_battery_provider *provider,
133 		      const char *address, uint32_t level)
134 {
135 	struct cras_bt_battery *battery;
136 	DBusMessage *msg;
137 	DBusMessageIter iter, dict, entry;
138 
139 	LL_SEARCH(provider->batteries, battery, address, cmp_battery_address);
140 
141 	if (battery)
142 		return battery;
143 
144 	syslog(LOG_DEBUG, "Creating new battery for %s", address);
145 
146 	battery = battery_new(address, level);
147 	LL_APPEND(provider->batteries, battery);
148 
149 	msg = dbus_message_new_signal(CRAS_DEFAULT_BATTERY_PROVIDER,
150 				      DBUS_INTERFACE_OBJECT_MANAGER,
151 				      DBUS_SIGNAL_INTERFACES_ADDED);
152 
153 	dbus_message_iter_init_append(msg, &iter);
154 	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
155 				       &battery->object_path);
156 	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sa{sv}}",
157 					 &dict);
158 	dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL,
159 					 &entry);
160 	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
161 				       &provider->interface);
162 	populate_battery_properties(&entry, battery);
163 	dbus_message_iter_close_container(&dict, &entry);
164 	dbus_message_iter_close_container(&iter, &dict);
165 
166 	if (!dbus_connection_send(provider->conn, msg, NULL)) {
167 		syslog(LOG_ERR,
168 		       "Error sending " DBUS_SIGNAL_INTERFACES_ADDED " signal");
169 	}
170 
171 	dbus_message_unref(msg);
172 
173 	return battery;
174 }
175 
176 /* Updates the level of a battery object and signals it on D-Bus. */
177 static void
update_battery_level(const struct cras_bt_battery_provider * provider,struct cras_bt_battery * battery,uint32_t level)178 update_battery_level(const struct cras_bt_battery_provider *provider,
179 		     struct cras_bt_battery *battery, uint32_t level)
180 {
181 	DBusMessage *msg;
182 	DBusMessageIter iter;
183 
184 	if (battery->level == level)
185 		return;
186 
187 	battery->level = level;
188 
189 	msg = dbus_message_new_signal(battery->object_path,
190 				      DBUS_INTERFACE_PROPERTIES,
191 				      DBUS_SIGNAL_PROPERTIES_CHANGED);
192 
193 	dbus_message_iter_init_append(msg, &iter);
194 	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
195 				       &provider->interface);
196 	populate_battery_properties(&iter, battery);
197 
198 	if (!dbus_connection_send(provider->conn, msg, NULL)) {
199 		syslog(LOG_ERR, "Error sending " DBUS_SIGNAL_PROPERTIES_CHANGED
200 				" signal");
201 	}
202 
203 	dbus_message_unref(msg);
204 }
205 
206 /* Invoked when HFP sends an alert about a battery value change. */
on_bt_battery_changed(void * context,const char * address,uint32_t level)207 static void on_bt_battery_changed(void *context, const char *address,
208 				  uint32_t level)
209 {
210 	struct cras_bt_battery_provider *provider = context;
211 
212 	syslog(LOG_DEBUG, "Battery changed for address %s, level %d", address,
213 	       level);
214 
215 	if (!provider->is_registered) {
216 		syslog(LOG_WARNING, "Received battery level update while "
217 				    "battery provider is not registered");
218 		return;
219 	}
220 
221 	struct cras_bt_battery *battery =
222 		get_or_create_battery(provider, address, level);
223 
224 	update_battery_level(provider, battery, level);
225 }
226 
227 /* Invoked when we receive a D-Bus return of RegisterBatteryProvider from
228  * BlueZ.
229  */
230 static void
cras_bt_on_battery_provider_registered(DBusPendingCall * pending_call,void * data)231 cras_bt_on_battery_provider_registered(DBusPendingCall *pending_call,
232 				       void *data)
233 {
234 	DBusMessage *reply;
235 	struct cras_bt_battery_provider *provider = data;
236 	struct cras_observer_ops observer_ops;
237 
238 	reply = dbus_pending_call_steal_reply(pending_call);
239 	dbus_pending_call_unref(pending_call);
240 
241 	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
242 		syslog(LOG_ERR, "RegisterBatteryProvider returned error: %s",
243 		       dbus_message_get_error_name(reply));
244 		dbus_message_unref(reply);
245 		return;
246 	}
247 
248 	syslog(LOG_INFO, "RegisterBatteryProvider succeeded");
249 
250 	provider->is_registered = true;
251 
252 	memset(&observer_ops, 0, sizeof(observer_ops));
253 	observer_ops.bt_battery_changed = on_bt_battery_changed;
254 	provider->observer = cras_observer_add(&observer_ops, provider);
255 
256 	dbus_message_unref(reply);
257 }
258 
cras_bt_register_battery_provider(DBusConnection * conn,const struct cras_bt_adapter * adapter)259 int cras_bt_register_battery_provider(DBusConnection *conn,
260 				      const struct cras_bt_adapter *adapter)
261 {
262 	const char *adapter_path;
263 	DBusMessage *method_call;
264 	DBusMessageIter message_iter;
265 	DBusPendingCall *pending_call;
266 
267 	if (battery_provider.is_registered) {
268 		syslog(LOG_ERR, "Battery Provider already registered");
269 		return -EBUSY;
270 	}
271 
272 	if (battery_provider.conn)
273 		dbus_connection_unref(battery_provider.conn);
274 
275 	battery_provider.conn = conn;
276 	dbus_connection_ref(battery_provider.conn);
277 
278 	adapter_path = cras_bt_adapter_object_path(adapter);
279 	method_call = dbus_message_new_method_call(
280 		BLUEZ_SERVICE, adapter_path,
281 		BLUEZ_INTERFACE_BATTERY_PROVIDER_MANAGER,
282 		"RegisterBatteryProvider");
283 	if (!method_call)
284 		return -ENOMEM;
285 
286 	dbus_message_iter_init_append(method_call, &message_iter);
287 	dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_OBJECT_PATH,
288 				       &battery_provider.object_path);
289 
290 	if (!dbus_connection_send_with_reply(conn, method_call, &pending_call,
291 					     DBUS_TIMEOUT_USE_DEFAULT)) {
292 		dbus_message_unref(method_call);
293 		return -ENOMEM;
294 	}
295 
296 	dbus_message_unref(method_call);
297 
298 	if (!pending_call)
299 		return -EIO;
300 
301 	if (!dbus_pending_call_set_notify(
302 		    pending_call, cras_bt_on_battery_provider_registered,
303 		    &battery_provider, NULL)) {
304 		dbus_pending_call_cancel(pending_call);
305 		dbus_pending_call_unref(pending_call);
306 		return -ENOMEM;
307 	}
308 
309 	return 0;
310 }
311 
312 /* Removes a battery object and signals the removal on D-Bus as well. */
cleanup_battery(struct cras_bt_battery_provider * provider,struct cras_bt_battery * battery)313 static void cleanup_battery(struct cras_bt_battery_provider *provider,
314 			    struct cras_bt_battery *battery)
315 {
316 	DBusMessage *msg;
317 	DBusMessageIter iter, entry;
318 
319 	if (!battery)
320 		return;
321 
322 	LL_DELETE(provider->batteries, battery);
323 
324 	msg = dbus_message_new_signal(CRAS_DEFAULT_BATTERY_PROVIDER,
325 				      DBUS_INTERFACE_OBJECT_MANAGER,
326 				      DBUS_SIGNAL_INTERFACES_REMOVED);
327 
328 	dbus_message_iter_init_append(msg, &iter);
329 	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
330 				       &battery->object_path);
331 	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
332 					 DBUS_TYPE_STRING_AS_STRING, &entry);
333 	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
334 				       &provider->interface);
335 	dbus_message_iter_close_container(&iter, &entry);
336 
337 	if (!dbus_connection_send(provider->conn, msg, NULL)) {
338 		syslog(LOG_ERR, "Error sending " DBUS_SIGNAL_INTERFACES_REMOVED
339 				" signal");
340 	}
341 
342 	dbus_message_unref(msg);
343 
344 	battery_free(battery);
345 }
346 
cras_bt_battery_provider_reset()347 void cras_bt_battery_provider_reset()
348 {
349 	struct cras_bt_battery *battery;
350 
351 	syslog(LOG_INFO, "Resetting battery provider");
352 
353 	if (!battery_provider.is_registered)
354 		return;
355 
356 	battery_provider.is_registered = false;
357 
358 	LL_FOREACH (battery_provider.batteries, battery) {
359 		cleanup_battery(&battery_provider, battery);
360 	}
361 
362 	if (battery_provider.conn) {
363 		dbus_connection_unref(battery_provider.conn);
364 		battery_provider.conn = NULL;
365 	}
366 
367 	if (battery_provider.observer) {
368 		cras_observer_remove(battery_provider.observer);
369 		battery_provider.observer = NULL;
370 	}
371 }
372