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