From patchwork Wed Nov 11 01:17:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sonny Sasaka X-Patchwork-Id: 11896075 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E639AC388F7 for ; Wed, 11 Nov 2020 01:17:57 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8C305221F1 for ; Wed, 11 Nov 2020 01:17:57 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="DFRjgjtk" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732763AbgKKBRx (ORCPT ); Tue, 10 Nov 2020 20:17:53 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49152 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731610AbgKKBRw (ORCPT ); Tue, 10 Nov 2020 20:17:52 -0500 Received: from mail-pg1-x543.google.com (mail-pg1-x543.google.com [IPv6:2607:f8b0:4864:20::543]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5CC4CC0613D1 for ; Tue, 10 Nov 2020 17:17:52 -0800 (PST) Received: by mail-pg1-x543.google.com with SMTP id h6so306084pgk.4 for ; Tue, 10 Nov 2020 17:17:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=K3Nmj2JHnNTqlDSX6ZLPOU+slg6cs7/sMIsppeUcvMw=; b=DFRjgjtkRRFlwvOnxnTA2Bo0Uq7BekdlE6Lr4yUOLFQgo2fLhbND9PlbO9H5FODKhb zpMYLkIWhIlAV0XIKWBc/q1k0pLi1RFy7XPAVzHo1IjEKJflSG6J3yqRZQslvCffwFF0 Ifrjc0pgXDjSTmB3ZKvYtimiypDkwDUHY0BkI= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=K3Nmj2JHnNTqlDSX6ZLPOU+slg6cs7/sMIsppeUcvMw=; b=mDupg78r8uZC/I5sJab2OVnmw7wiM4kp60U2h6zOJCEVAu9Ic1it07rIz1PZVoBoLt NLP+wN5E1ko5UpVbXdCIxce3jGgVv1Cloptuvei8upLj1TYy+JgLkRpvaJ6BxWKGc5ay 1PdPlsQjzmAJsdf3La72NWxSfejEUtEannMcxz/tQfLVX/ai3I4pfeqMx/yjB6T9xd4M m9RZtxUtg3mW/lBkwlf/H74aNTMw33hnuf7HU0G13jXvV52woFC2ji9OZcYRA3x76dxw W5lFO0OclAP+DV78gv5PNlnVISbxYHGJJmV1YL4Wa1/jgDHQL3SvsE2eyQICpUxTposX of/w== X-Gm-Message-State: AOAM531YSepGeEuD4bCADLqQPqx3EVXXyQ+cmAk5EcJoV5j71wCqYc7P M740daLtfZ0fwuUhT0I2K4yKiLlDZKF8JA== X-Google-Smtp-Source: ABdhPJyonuiJxdTALUMXBYqkf3qKi37GORZbW6/pJ9pNSXxuGw955a8X6iqJNorEqGNyctc6llgWIQ== X-Received: by 2002:a17:90a:fd08:: with SMTP id cv8mr1035916pjb.203.1605057471549; Tue, 10 Nov 2020 17:17:51 -0800 (PST) Received: from sonnysasaka-chrome.mtv.corp.google.com ([2620:15c:202:201:4a0f:cfff:fe66:e60c]) by smtp.gmail.com with ESMTPSA id y19sm331695pfn.147.2020.11.10.17.17.50 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 10 Nov 2020 17:17:50 -0800 (PST) From: Sonny Sasaka To: linux-bluetooth@vger.kernel.org Cc: Sonny Sasaka , Daniel Winkler Subject: [PATCH BlueZ v2 1/7] battery: Add the internal Battery API Date: Tue, 10 Nov 2020 17:17:39 -0800 Message-Id: <20201111011745.2016-1-sonnysasaka@chromium.org> X-Mailer: git-send-email 2.26.2 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org This patch adds an API for internal BlueZ code to expose battery information to org.bluez.Battery1 interface. The motivation behind this is that there is going to be other places than GATT BAS handler that exposes battery information, for example internal plugins and the BatteryProvider1 D-Bus API for external clients. Reviewed-by: Daniel Winkler --- Makefile.am | 3 +- src/battery.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/battery.h | 15 ++++ 3 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 src/battery.c create mode 100644 src/battery.h diff --git a/Makefile.am b/Makefile.am index 56279c4ba..b19c1d40f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -294,7 +294,8 @@ src_bluetoothd_SOURCES = $(builtin_sources) \ src/device.h src/device.c \ src/dbus-common.c src/dbus-common.h \ src/eir.h src/eir.c \ - src/adv_monitor.h src/adv_monitor.c + src/adv_monitor.h src/adv_monitor.c \ + src/battery.h src/battery.c src_bluetoothd_LDADD = lib/libbluetooth-internal.la \ gdbus/libgdbus-internal.la \ src/libshared-glib.la \ diff --git a/src/battery.c b/src/battery.c new file mode 100644 index 000000000..87a6b91fb --- /dev/null +++ b/src/battery.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2020 Google LLC + * + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define _GNU_SOURCE +#include +#include +#include + +#include "gdbus/gdbus.h" +#include "lib/bluetooth.h" +#include "src/shared/queue.h" +#include "src/shared/util.h" +#include "battery.h" +#include "dbus-common.h" +#include "adapter.h" +#include "log.h" + +#define BATTERY_INTERFACE "org.bluez.Battery1" + +#define BATTERY_MAX_PERCENTAGE 100 + +struct btd_battery { + char *path; /* D-Bus object path, owns pointer */ + uint8_t percentage; /* valid between 0 to 100 inclusively */ +}; + +static struct queue *batteries = NULL; + +static void battery_add(struct btd_battery *battery) +{ + if (!batteries) + batteries = queue_new(); + + queue_push_head(batteries, battery); +} + +static void battery_remove(struct btd_battery *battery) +{ + queue_remove(batteries, battery); + if (queue_isempty(batteries)) { + queue_destroy(batteries, NULL); + batteries = NULL; + } +} + +static bool match_path(const void *data, const void *user_data) +{ + const struct btd_battery *battery = data; + const char *path = user_data; + + return g_strcmp0(battery->path, path) == 0; +} + +static struct btd_battery *battery_new(const char *path) +{ + struct btd_battery *battery; + + battery = new0(struct btd_battery, 1); + battery->path = g_strdup(path); + battery->percentage = UINT8_MAX; + + return battery; +} + +static void battery_free(struct btd_battery *battery) +{ + if (battery->path) + g_free(battery->path); + + free(battery); +} + +static gboolean property_percentage_get(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct btd_battery *battery = data; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, + &battery->percentage); + + return TRUE; +} + +static gboolean property_percentage_exists(const GDBusPropertyTable *property, + void *data) +{ + struct btd_battery *battery = data; + + return battery->percentage <= BATTERY_MAX_PERCENTAGE; +} + +static const GDBusPropertyTable battery_properties[] = { + { "Percentage", "y", property_percentage_get, NULL, + property_percentage_exists }, + {} +}; + +struct btd_battery *btd_battery_register(const char *path) +{ + struct btd_battery *battery; + + DBG("path = %s", path); + + if (queue_find(batteries, match_path, path)) { + error("error registering battery: path exists"); + return NULL; + } + + if (!g_str_has_prefix(path, "/")) { + error("error registering battery: invalid D-Bus object path"); + return NULL; + } + + battery = battery_new(path); + battery_add(battery); + + if (!g_dbus_register_interface(btd_get_dbus_connection(), battery->path, + BATTERY_INTERFACE, NULL, NULL, + battery_properties, battery, NULL)) { + error("error registering D-Bus interface for %s", + battery->path); + + battery_remove(battery); + battery_free(battery); + + return NULL; + } + + DBG("registered Battery object: %s", battery->path); + + return battery; +} + +bool btd_battery_unregister(struct btd_battery *battery) +{ + DBG("path = %s", battery->path); + + if (!queue_find(batteries, NULL, battery)) { + error("error unregistering battery: " + "battery %s is not registered", + battery->path); + return false; + } + + if (!g_dbus_unregister_interface(btd_get_dbus_connection(), + battery->path, BATTERY_INTERFACE)) { + error("error unregistering battery %s from D-Bus interface", + battery->path); + return false; + } + + battery_remove(battery); + battery_free(battery); + + return true; +} + +bool btd_battery_update(struct btd_battery *battery, uint8_t percentage) +{ + DBG("path = %s", battery->path); + + if (!queue_find(batteries, NULL, battery)) { + error("error updating battery: battery is not registered"); + return false; + } + + if (percentage > BATTERY_MAX_PERCENTAGE) { + error("error updating battery: percentage is not valid"); + return false; + } + + if (battery->percentage == percentage) + return true; + + battery->percentage = percentage; + g_dbus_emit_property_changed(btd_get_dbus_connection(), battery->path, + BATTERY_INTERFACE, "Percentage"); + + return true; +} diff --git a/src/battery.h b/src/battery.h new file mode 100644 index 000000000..9c69b7afa --- /dev/null +++ b/src/battery.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2020 Google LLC + * + * + */ + +struct btd_battery; + +struct btd_battery *btd_battery_register(const char *path); +bool btd_battery_unregister(struct btd_battery *battery); +bool btd_battery_update(struct btd_battery *battery, uint8_t percentage); From patchwork Wed Nov 11 01:17:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sonny Sasaka X-Patchwork-Id: 11896077 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.1 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, UNWANTED_LANGUAGE_BODY,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B5A0BC5517A for ; Wed, 11 Nov 2020 01:18:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6D805221E9 for ; Wed, 11 Nov 2020 01:18:03 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="TKL7UCOg" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1733054AbgKKBSB (ORCPT ); Tue, 10 Nov 2020 20:18:01 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49158 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732819AbgKKBRy (ORCPT ); Tue, 10 Nov 2020 20:17:54 -0500 Received: from mail-pf1-x443.google.com (mail-pf1-x443.google.com [IPv6:2607:f8b0:4864:20::443]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5F0E8C0613D1 for ; Tue, 10 Nov 2020 17:17:53 -0800 (PST) Received: by mail-pf1-x443.google.com with SMTP id q5so516807pfk.6 for ; Tue, 10 Nov 2020 17:17:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=FTvIzX7GCozO99X7SEFRSY0nTaCgNWs0wH/hjeI4kek=; b=TKL7UCOgVEhGeAcTYwRwFHWU9JzKd9Fqs2i7gLtrJ+0Ab2tV8TU0PeecPfLHN/sTmz nzPLgu6RVJta0RQnUjSX6IX2xZElfdLEK4iDy8yIQuz8EgnMs0s8L+uA6c3fhRcx49Bg 2+/4RNOuR16eP2u+Dzq4yjOk+Pa5DGVwgkVIg= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=FTvIzX7GCozO99X7SEFRSY0nTaCgNWs0wH/hjeI4kek=; b=hw6paIHpw0YqKV6c3fmHrVPw/+6F7EZyENT92sqK4EdHGjfHUKYKRB5WqnoG8rVzMk 4jVWb9bd2t9SuMUlEmS4MhN7FgCcLY9MOjGDAAoM3TW+ZSGQspR1qBzEtFfA2Oqqkc1v h1qpvwqIBzxhBQQIw3AMULvDm+ukEcQqetHJPd1pdPZ40dRaSvpZ7YkCeuAbWVyyZ+q7 UmZxVax78w7EaVMvpyZHUydvZGP/bD7yt0MDI7SYSTsG4ETvNUctXrQEFrM/TPlb6AoG 1Ln2Xc5oknFAjR/QwPUTK4Q759b9KQoDz7mw0vqYmD4JjMU/fA4ZkHDors1XPm2KSN9l mD3Q== X-Gm-Message-State: AOAM533TLglcB9rN6imFoSYH/WVorYTDjR5tuCSUWC+NvKT+arCdaqjZ tZkOtMD3zW1G5NXxLi+KqOqcur8X2ndcnw== X-Google-Smtp-Source: ABdhPJxkugObQSUWD/Z1NdzN6KtSF/SPHJCgmixamaxlaXWAk0Ia7BbiAmiJ01kUvPJqA0KkSxKwmg== X-Received: by 2002:a17:90a:7503:: with SMTP id q3mr1124887pjk.6.1605057472723; Tue, 10 Nov 2020 17:17:52 -0800 (PST) Received: from sonnysasaka-chrome.mtv.corp.google.com ([2620:15c:202:201:4a0f:cfff:fe66:e60c]) by smtp.gmail.com with ESMTPSA id y19sm331695pfn.147.2020.11.10.17.17.51 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 10 Nov 2020 17:17:52 -0800 (PST) From: Sonny Sasaka To: linux-bluetooth@vger.kernel.org Cc: Sonny Sasaka , Daniel Winkler Subject: [PATCH BlueZ v2 2/7] profiles/battery: Refactor to use battery library Date: Tue, 10 Nov 2020 17:17:40 -0800 Message-Id: <20201111011745.2016-2-sonnysasaka@chromium.org> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201111011745.2016-1-sonnysasaka@chromium.org> References: <20201111011745.2016-1-sonnysasaka@chromium.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org This refactors profiles/battery to use the internal battery library that handles the D-Bus intricacies so that profiles/battery only handles the GATT BAS concerns. Reviewed-by: Daniel Winkler --- profiles/battery/battery.c | 51 +++++++++++--------------------------- 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c index 13c80d05c..e1a61dd0b 100644 --- a/profiles/battery/battery.c +++ b/profiles/battery/battery.c @@ -23,14 +23,11 @@ #include -#include "gdbus/gdbus.h" - #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/sdp.h" #include "lib/uuid.h" -#include "src/dbus-common.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/queue.h" @@ -42,6 +39,7 @@ #include "src/profile.h" #include "src/service.h" #include "src/log.h" +#include "src/battery.h" #include "attrib/att.h" #define BATTERY_INTERFACE "org.bluez.Battery1" @@ -50,7 +48,7 @@ /* Generic Attribute/Access Service */ struct batt { - char *path; /* D-Bus path of device */ + struct btd_battery *battery; struct btd_device *device; struct gatt_db *db; struct bt_gatt_client *client; @@ -69,6 +67,8 @@ static void batt_free(struct batt *batt) bt_gatt_client_unref(batt->client); btd_device_unref(batt->device); g_free (batt->initial_value); + if (batt->battery) + btd_battery_unregister(batt->battery); g_free(batt); } @@ -81,11 +81,9 @@ static void batt_reset(struct batt *batt) batt->client = NULL; g_free (batt->initial_value); batt->initial_value = NULL; - if (batt->path) { - g_dbus_unregister_interface(btd_get_dbus_connection(), - batt->path, BATTERY_INTERFACE); - g_free(batt->path); - batt->path = NULL; + if (batt->battery) { + btd_battery_unregister(batt->battery); + batt->battery = NULL; } } @@ -98,8 +96,11 @@ static void parse_battery_level(struct batt *batt, if (batt->percentage != percentage) { batt->percentage = percentage; DBG("Battery Level updated: %d%%", percentage); - g_dbus_emit_property_changed(btd_get_dbus_connection(), batt->path, - BATTERY_INTERFACE, "Percentage"); + if (!batt->battery) { + warn("Trying to update an unregistered battery"); + return; + } + btd_battery_update(batt->battery, batt->percentage); } } @@ -115,22 +116,6 @@ static void batt_io_value_cb(uint16_t value_handle, const uint8_t *value, } } -static gboolean property_get_percentage( - const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - struct batt *batt = data; - - dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &batt->percentage); - - return TRUE; -} - -static const GDBusPropertyTable battery_properties[] = { - { "Percentage", "y", property_get_percentage }, - { } -}; - static void batt_io_ccc_written_cb(uint16_t att_ecode, void *user_data) { struct batt *batt = user_data; @@ -141,13 +126,9 @@ static void batt_io_ccc_written_cb(uint16_t att_ecode, void *user_data) return; } - if (g_dbus_register_interface(btd_get_dbus_connection(), - batt->path, BATTERY_INTERFACE, - NULL, NULL, - battery_properties, batt, - NULL) == FALSE) { - error("Unable to register %s interface for %s", - BATTERY_INTERFACE, batt->path); + batt->battery = btd_battery_register(device_get_path(batt->device)); + + if (!batt->battery) { batt_reset(batt); return; } @@ -321,8 +302,6 @@ static int batt_accept(struct btd_service *service) return -1; } - batt->path = g_strdup (device_get_path(device)); - btd_service_connecting_complete(service, 0); return 0; From patchwork Wed Nov 11 01:17:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sonny Sasaka X-Patchwork-Id: 11896083 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id EBFFCC388F7 for ; Wed, 11 Nov 2020 01:18:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9BFA9221F9 for ; Wed, 11 Nov 2020 01:18:03 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="BOMtsVSo" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1733001AbgKKBSA (ORCPT ); Tue, 10 Nov 2020 20:18:00 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49162 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732835AbgKKBRy (ORCPT ); Tue, 10 Nov 2020 20:17:54 -0500 Received: from mail-pg1-x543.google.com (mail-pg1-x543.google.com [IPv6:2607:f8b0:4864:20::543]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id ACD02C0613D3 for ; Tue, 10 Nov 2020 17:17:54 -0800 (PST) Received: by mail-pg1-x543.google.com with SMTP id i26so302324pgl.5 for ; Tue, 10 Nov 2020 17:17:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=qnjOyIqD02WVy6ZZ2x5bkuCTpr4hjumX5jR/HYlXCOk=; b=BOMtsVSoSPnADSKJwzb1eP0Vwq39Fkd9SuyQT/fbeY9TAtnbDONIODZ8jwWBsEVt5d lpZJnC0w7K7A0oNyD6CP+UkPIiyX+66Cqgp2/SVtORW6pi6gJKl25WWhFY4NG7z1bsOj bfBO60uh5F3/7IVfDtQsvl8akXKcty+46xtdw= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=qnjOyIqD02WVy6ZZ2x5bkuCTpr4hjumX5jR/HYlXCOk=; b=iLNu+Ty1hgPuWtfkfZKR92AH1TS8H3L8JCV0eTINN2fLjmLE2ZhU2NQqTWfE1wnvty zpkRFvGVF9dLIvQD0koergT7jcw6chQI1TLrag3aVLWCyC+GovYk4QTLF9UhZW14J3Nh ysENBCZJ8t4Expt/NoBq8qmcwh36EWfOF6VL0veAyZ4kbTAlTlPAhF3bEulYI3juXybQ jZOyaKC8ECjoydw8Z8BTfwTJAtNmN3VA+JqQvKQcqAiVmonacA0F18/iRiieFy37Gffi WaioDU3Uf7EHQnR/RmDQtI1b23als463awMLTmp8nd6XKZrLVsOfmYsSOSp0ULr+Gtpq zpOA== X-Gm-Message-State: AOAM530YAJk0uMc2kn26mXqq31upDecAi8v4COLwUmNMQizAU04BKSF6 gITy+mYqUNHOTWkipPCHd50Gqs10r1H8ZA== X-Google-Smtp-Source: ABdhPJwgafqx6FwTLdAYDbaZrTTrY/mMKKaUKAgMlPby5N0cZluNlNt7c88bGZa+sL+FFaivHDpJUw== X-Received: by 2002:a63:c43:: with SMTP id 3mr20231994pgm.222.1605057474034; Tue, 10 Nov 2020 17:17:54 -0800 (PST) Received: from sonnysasaka-chrome.mtv.corp.google.com ([2620:15c:202:201:4a0f:cfff:fe66:e60c]) by smtp.gmail.com with ESMTPSA id y19sm331695pfn.147.2020.11.10.17.17.52 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 10 Nov 2020 17:17:53 -0800 (PST) From: Sonny Sasaka To: linux-bluetooth@vger.kernel.org Cc: Sonny Sasaka , Miao-chen Chou Subject: [PATCH BlueZ v2 3/7] battery: Add Source property to Battery API Date: Tue, 10 Nov 2020 17:17:41 -0800 Message-Id: <20201111011745.2016-3-sonnysasaka@chromium.org> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201111011745.2016-1-sonnysasaka@chromium.org> References: <20201111011745.2016-1-sonnysasaka@chromium.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org As Battery API will be generalized for other battery reporting protocols, the Source property is useful for diagnostics purposes. Reviewed-by: Miao-chen Chou --- profiles/battery/battery.c | 3 ++- src/battery.c | 35 +++++++++++++++++++++++++++++++---- src/battery.h | 2 +- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c index e1a61dd0b..2478816a4 100644 --- a/profiles/battery/battery.c +++ b/profiles/battery/battery.c @@ -126,7 +126,8 @@ static void batt_io_ccc_written_cb(uint16_t att_ecode, void *user_data) return; } - batt->battery = btd_battery_register(device_get_path(batt->device)); + batt->battery = btd_battery_register(device_get_path(batt->device), + "GATT Battery Service"); if (!batt->battery) { batt_reset(batt); diff --git a/src/battery.c b/src/battery.c index 87a6b91fb..8613d6e23 100644 --- a/src/battery.c +++ b/src/battery.c @@ -31,8 +31,9 @@ #define BATTERY_MAX_PERCENTAGE 100 struct btd_battery { - char *path; /* D-Bus object path, owns pointer */ + char *path; /* D-Bus object path */ uint8_t percentage; /* valid between 0 to 100 inclusively */ + char *source; /* Descriptive source of the battery info */ }; static struct queue *batteries = NULL; @@ -62,13 +63,15 @@ static bool match_path(const void *data, const void *user_data) return g_strcmp0(battery->path, path) == 0; } -static struct btd_battery *battery_new(const char *path) +static struct btd_battery *battery_new(const char *path, const char *source) { struct btd_battery *battery; battery = new0(struct btd_battery, 1); battery->path = g_strdup(path); battery->percentage = UINT8_MAX; + if (source) + battery->source = g_strdup(source); return battery; } @@ -78,6 +81,9 @@ static void battery_free(struct btd_battery *battery) if (battery->path) g_free(battery->path); + if (battery->source) + g_free(battery->source); + free(battery); } @@ -100,13 +106,34 @@ static gboolean property_percentage_exists(const GDBusPropertyTable *property, return battery->percentage <= BATTERY_MAX_PERCENTAGE; } +static gboolean property_source_get(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct btd_battery *battery = data; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, + &battery->source); + + return TRUE; +} + +static gboolean property_source_exists(const GDBusPropertyTable *property, + void *data) +{ + struct btd_battery *battery = data; + + return battery->source != NULL; +} + static const GDBusPropertyTable battery_properties[] = { { "Percentage", "y", property_percentage_get, NULL, property_percentage_exists }, + { "Source", "s", property_source_get, NULL, property_source_exists, + G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, {} }; -struct btd_battery *btd_battery_register(const char *path) +struct btd_battery *btd_battery_register(const char *path, const char *source) { struct btd_battery *battery; @@ -122,7 +149,7 @@ struct btd_battery *btd_battery_register(const char *path) return NULL; } - battery = battery_new(path); + battery = battery_new(path, source); battery_add(battery); if (!g_dbus_register_interface(btd_get_dbus_connection(), battery->path, diff --git a/src/battery.h b/src/battery.h index 9c69b7afa..ff63454cd 100644 --- a/src/battery.h +++ b/src/battery.h @@ -10,6 +10,6 @@ struct btd_battery; -struct btd_battery *btd_battery_register(const char *path); +struct btd_battery *btd_battery_register(const char *path, const char *source); bool btd_battery_unregister(struct btd_battery *battery); bool btd_battery_update(struct btd_battery *battery, uint8_t percentage); From patchwork Wed Nov 11 01:17:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sonny Sasaka X-Patchwork-Id: 11896081 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2EFBCC5517A for ; Wed, 11 Nov 2020 01:18:01 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CCC7C221F1 for ; Wed, 11 Nov 2020 01:18:00 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="XLZHS02o" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732933AbgKKBR7 (ORCPT ); Tue, 10 Nov 2020 20:17:59 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49166 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731610AbgKKBR4 (ORCPT ); Tue, 10 Nov 2020 20:17:56 -0500 Received: from mail-pf1-x431.google.com (mail-pf1-x431.google.com [IPv6:2607:f8b0:4864:20::431]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E4856C0613D4 for ; Tue, 10 Nov 2020 17:17:55 -0800 (PST) Received: by mail-pf1-x431.google.com with SMTP id x13so506298pfa.9 for ; Tue, 10 Nov 2020 17:17:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=dQ+EnXZ2kdnBZmC8x3MiuHzZ0854bTxxkZnBN1oy2g0=; b=XLZHS02oUWYhs8LxNQ5yMnT0sxOSuGOSUaw3DSncZGj34Q0BCRuwkPm+TgA1RJDRma kq6RW0zJ1/hnLwDFVvCyRPv8c+1O2GG68dPSxDyJGcycg/pIPyY1ou2IRyUkSNBkJGPp JxFqU/3wbjYeQqHXeeWMtzz+ZGR0zYbVZeCCY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=dQ+EnXZ2kdnBZmC8x3MiuHzZ0854bTxxkZnBN1oy2g0=; b=iVnBLKT6Hd50567QtBT+gOum9GEiVwh9fru+RIxKh3ExfmQnz4/X3hG8pgNzy1PDzj K+h78J2QnVahIVTUiS8Q8ILY9FYFIW6Ol0KI3H4OmMFQCiptOzicPC+OJDNvoNqVL2U9 bgCaF+IAHAhwjZ60uTnuFZDnb8/4Svf5RXR+OyD6gBY/7Rxx58/awfQl1dapiT6wx3IH W6tiv6rSuEmJbd6K3YY5/vSJX91401yb3wW/qX6CtU7l1PcC3izR5iWG2LErkhBTYY51 pNVuKcBO8GEPMt26Zyf9ROz6igqK6BT0ePF1YYihziRgBIh95KX0CfwyG9ccDv+CnbS5 UuGw== X-Gm-Message-State: AOAM530k3Tjfl2NoAYTrsvmTjMrcMbpOucC+pgjx0AZvyRoUS5GXCmHU KzEeY24DQLsdnavSFFjASNhuXsmNCmgXYQ== X-Google-Smtp-Source: ABdhPJyArm2VZBB8lvXY4u5nWyeOtZgdpOWjTktTrmDmbBw1cvrEFP/UiYSqymtwqaKmEFHFrSjiuQ== X-Received: by 2002:a65:4305:: with SMTP id j5mr19067813pgq.249.1605057475085; Tue, 10 Nov 2020 17:17:55 -0800 (PST) Received: from sonnysasaka-chrome.mtv.corp.google.com ([2620:15c:202:201:4a0f:cfff:fe66:e60c]) by smtp.gmail.com with ESMTPSA id y19sm331695pfn.147.2020.11.10.17.17.54 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 10 Nov 2020 17:17:54 -0800 (PST) From: Sonny Sasaka To: linux-bluetooth@vger.kernel.org Cc: Sonny Sasaka , Miao-chen Chou Subject: [PATCH BlueZ v2 4/7] doc: Add Battery Provider API doc Date: Tue, 10 Nov 2020 17:17:42 -0800 Message-Id: <20201111011745.2016-4-sonnysasaka@chromium.org> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201111011745.2016-1-sonnysasaka@chromium.org> References: <20201111011745.2016-1-sonnysasaka@chromium.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org This patch add the documentation of the Battery Provider which lets external clients feed battery information to BlueZ if they are able to decode battery reporting via any profile. BlueZ UI clients can then use the org.bluez.Battery1 API as a single source of battery information coming from many different profiles. Reviewed-by: Miao-chen Chou --- doc/battery-api.txt | 55 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/doc/battery-api.txt b/doc/battery-api.txt index dc7dbeda2..058bf0c6e 100644 --- a/doc/battery-api.txt +++ b/doc/battery-api.txt @@ -12,3 +12,58 @@ Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX Properties byte Percentage [readonly] The percentage of battery left as an unsigned 8-bit integer. + + string Source [readonly, optional, experimental] + + Describes where the battery information comes from + (e.g. "HFP 1.7", "HID"). + This property is informational only and may be useful + for debugging purposes. + + +Battery Provider Manager hierarchy +================================== +A battery provider starts by registering itself as a battery provider with the +RegisterBatteryProvider method passing an object path as the provider ID. Then, +it can start exposing org.bluez.BatteryProvider1 objects having the path +starting with the given provider ID. It can also remove objects at any time. +BlueZ will stop monitoring these exposed and removed objects after +UnregisterBatteryProvider is called for that provider ID. + +Service org.bluez +Interface org.bluez.BatteryProviderManager1 [experimental] +Object path /org/bluez/{hci0,hci1,...} + +Methods void RegisterBatteryProvider(object provider) + + This registers a battery provider. A registered + battery provider can then expose objects with + org.bluez.BatteryProvider1 interface described below. + + void UnregisterBatteryProvider(object provider) + + This unregisters a battery provider. After + unregistration, the BatteryProvider1 objects provided + by this client are ignored by BlueZ. + + +Battery Provider hierarchy +========================== + +Service +Interface org.bluez.BatteryProvider1 [experimental] +Object path {provider_root}/org/bluez/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX + +Properties byte Percentage [readonly] + + The percentage of battery left as an unsigned 8-bit + integer. + + string Source [readonly, optional] + + Describes where the battery information comes from + (e.g. "HFP 1.7", "HID"). + This property is informational only and may be useful + for debugging purposes. The content of this property is + a free string, but it is recommended to include the + profile name and version to be useful. From patchwork Wed Nov 11 01:17:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sonny Sasaka X-Patchwork-Id: 11896079 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1927EC56202 for ; Wed, 11 Nov 2020 01:18:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AB7A5221FE for ; Wed, 11 Nov 2020 01:17:59 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="NfLwFxol" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732982AbgKKBR6 (ORCPT ); Tue, 10 Nov 2020 20:17:58 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49174 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732962AbgKKBR5 (ORCPT ); Tue, 10 Nov 2020 20:17:57 -0500 Received: from mail-pf1-x434.google.com (mail-pf1-x434.google.com [IPv6:2607:f8b0:4864:20::434]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0EE18C0613D6 for ; Tue, 10 Nov 2020 17:17:57 -0800 (PST) Received: by mail-pf1-x434.google.com with SMTP id g7so539634pfc.2 for ; Tue, 10 Nov 2020 17:17:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=SALKOqvduBW19p97zuVc5XoKlMwFmjr1u1CvAIAAJ9A=; b=NfLwFxolhCwG2+rFQ5PNxdYs+rvkmno1b5SwXhpC8lERMS5D2VY7C0Z8tIy5i5tOBe NFsiZb/PONbwq/J35AVCFrNHKOOA2Xqtk2wTUwFmntI76jmJJIhnfe6+RjXR+WTm8dt8 aFeKGllCB+6Ps3k50RfTMn1TyPG0TXVasejow= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=SALKOqvduBW19p97zuVc5XoKlMwFmjr1u1CvAIAAJ9A=; b=Fa4vy7o4BHRZ5ZkLdV22qzDxvi0R2zTf1tgzWmUidOL5z9wo3E+UGueq6ZXtbq6N7l 6nxq+c5oEpzZBw61tjn9kw2IP6xtmiWv0wIgV5zgFIHYr4eCXJ0Mquv2mFsZEWhZbplX i8HuaUYeu+B61YbEChOnLmKp4TsvxLH6BIf2YoyzRS7JkUGd0KfWtpzk7rb0pOlVbJGY v02lsNWOnDiyYMyYPByqPJRlwsef1ZvVyyhH8sJwMBo/maq+0BMuwuiRSUZ2C5If8GNf XEBW3yZf+2IluBckP6zz2hSUGVJ6nGrfzx3TXCzRQVtimN/T1S0QNzefnityy7xG/b/t 6LZQ== X-Gm-Message-State: AOAM531FXrg4V2/BVLpe8vwq20n4j4gtj9kx9uJvzmKEYV6yl/+q7Spd gW4DRDimvSZWJDoV7FM70LUEAfJcAaHXDQ== X-Google-Smtp-Source: ABdhPJyscjxdb06+AcOp02lfjVsCAg9Iazi2/3ijinQpLFIqhUA68ghKLEbU9jqO8yS47wsmPR7cag== X-Received: by 2002:a63:5a07:: with SMTP id o7mr18850552pgb.77.1605057476334; Tue, 10 Nov 2020 17:17:56 -0800 (PST) Received: from sonnysasaka-chrome.mtv.corp.google.com ([2620:15c:202:201:4a0f:cfff:fe66:e60c]) by smtp.gmail.com with ESMTPSA id y19sm331695pfn.147.2020.11.10.17.17.55 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 10 Nov 2020 17:17:55 -0800 (PST) From: Sonny Sasaka To: linux-bluetooth@vger.kernel.org Cc: Sonny Sasaka , Miao-chen Chou Subject: [PATCH BlueZ v2 5/7] test: Add test app for Battery Provider API Date: Tue, 10 Nov 2020 17:17:43 -0800 Message-Id: <20201111011745.2016-5-sonnysasaka@chromium.org> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201111011745.2016-1-sonnysasaka@chromium.org> References: <20201111011745.2016-1-sonnysasaka@chromium.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org The python test app simulates an application registering to BlueZ as a Battery Provider providing three fake batteries drained periodically. Reviewed-by: Miao-chen Chou --- test/example-battery-provider | 230 ++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100755 test/example-battery-provider diff --git a/test/example-battery-provider b/test/example-battery-provider new file mode 100755 index 000000000..3dbb08563 --- /dev/null +++ b/test/example-battery-provider @@ -0,0 +1,230 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +import dbus +import dbus.exceptions +import dbus.mainloop.glib +import dbus.service + +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject +import sys + +mainloop = None +app = None +bus = None + +BLUEZ_SERVICE_NAME = 'org.bluez' +DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager' +DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties' + +BATTERY_PROVIDER_MANAGER_IFACE = 'org.bluez.BatteryProviderManager1' +BATTERY_PROVIDER_IFACE = 'org.bluez.BatteryProvider1' +BATTERY_PROVIDER_PATH = '/path/to/provider' + +BATTERY_PATH1 = '11_11_11_11_11_11' +BATTERY_PATH2 = '22_22_22_22_22_22' +BATTERY_PATH3 = '33_33_33_33_33_33' + +class InvalidArgsException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs' + + +class Application(dbus.service.Object): + def __init__(self, bus): + self.path = BATTERY_PROVIDER_PATH + self.services = [] + self.batteries = [] + dbus.service.Object.__init__(self, bus, self.path) + + def get_path(self): + return dbus.ObjectPath(self.path) + + def add_battery(self, battery): + self.batteries.append(battery) + self.InterfacesAdded(battery.get_path(), battery.get_properties()) + GObject.timeout_add(1000, drain_battery, battery) + + def remove_battery(self, battery): + self.batteries.remove(battery) + self.InterfacesRemoved(battery.get_path(), [BATTERY_PROVIDER_IFACE]) + + @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}') + def GetManagedObjects(self): + response = {} + print('GetManagedObjects called') + + for battery in self.batteries: + response[battery.get_path()] = battery.get_properties() + + return response + + @dbus.service.signal(DBUS_OM_IFACE, signature='oa{sa{sv}}') + def InterfacesAdded(self, object_path, interfaces_and_properties): + return + + @dbus.service.signal(DBUS_OM_IFACE, signature='oas') + def InterfacesRemoved(self, object_path, interfaces): + return + + +class Battery(dbus.service.Object): + """ + org.bluez.BatteryProvider1 interface implementation + """ + def __init__(self, bus, dev, percentage, source = None): + self.path = BATTERY_PROVIDER_PATH + '/org/bluez/hci0/dev_' + dev + self.bus = bus + self.percentage = percentage + self.source = source + dbus.service.Object.__init__(self, bus, self.path) + + def get_battery_properties(self): + properties = {} + if self.percentage != None: + properties['Percentage'] = dbus.Byte(self.percentage) + if self.source != None: + properties['Source'] = self.source + return properties + + def get_properties(self): + return { BATTERY_PROVIDER_IFACE: self.get_battery_properties() } + + def get_path(self): + return dbus.ObjectPath(self.path) + + def set_percentage(self, percentage): + if percentage < 0 or percentage > 100: + print('percentage not valid') + return + + self.percentage = percentage + print('battery %s percentage %d' % (self.path, self.percentage)) + self.PropertiesChanged( + BATTERY_PROVIDER_IFACE, self.get_battery_properties()) + + @dbus.service.method(DBUS_PROP_IFACE, + in_signature='s', + out_signature='a{sv}') + def GetAll(self, interface): + if interface != BATTERY_PROVIDER_IFACE: + raise InvalidArgsException() + + return self.get_properties()[BATTERY_PROVIDER_IFACE] + + @dbus.service.signal(DBUS_PROP_IFACE, signature='sa{sv}') + def PropertiesChanged(self, interface, properties): + return + + +def add_late_battery(): + app.add_battery(Battery(bus, BATTERY_PATH3, 70, 'Protocol 2')) + + +def drain_battery(battery): + new_percentage = 100 + if battery.percentage != None: + new_percentage = battery.percentage - 5 + if new_percentage < 0: + new_percentage = 0 + + battery.set_percentage(new_percentage) + + if new_percentage <= 0: + return False + + return True + +def register_provider_cb(): + print('Battery Provider registered') + + # Battery added early right after RegisterBatteryProvider succeeds + app.add_battery(Battery(bus, BATTERY_PATH2, None)) + # Battery added later + GObject.timeout_add(5000, add_late_battery) + + +def register_provider_error_cb(error): + print('Failed to register Battery Provider: ' + str(error)) + mainloop.quit() + + +def find_manager(bus): + remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'), + DBUS_OM_IFACE) + objects = remote_om.GetManagedObjects() + + for o, props in objects.items(): + if BATTERY_PROVIDER_MANAGER_IFACE in props.keys(): + return o + + return None + + +def unregister_provider_cb(): + print('Battery Provider unregistered') + + +def unregister_provider_error_cb(error): + print('Failed to unregister Battery Provider: ' + str(error)) + + +def unregister_battery_provider(battery_provider_manager): + battery_provider_manager.UnregisterBatteryProvider(BATTERY_PROVIDER_PATH, + reply_handler=unregister_provider_cb, + error_handler=unregister_provider_error_cb) + + +def remove_battery(app, battery): + app.remove_battery(battery) + + +""" +Simulates an application registering to BlueZ as a Battery Provider providing +fake batteries drained periodically. +""" +def main(): + global mainloop, bus, app + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + bus = dbus.SystemBus() + + manager_path = find_manager(bus) + if not manager_path: + print('BatteryProviderManager1 interface not found') + return + + print('BatteryProviderManager1 path = ', manager_path) + + battery_provider_manager = dbus.Interface( + bus.get_object(BLUEZ_SERVICE_NAME, manager_path), + BATTERY_PROVIDER_MANAGER_IFACE) + + app = Application(bus) + + # Battery pre-added before RegisterBatteryProvider + battery1 = Battery(bus, BATTERY_PATH1, 87, 'Protocol 1') + app.add_battery(battery1) + + mainloop = GObject.MainLoop() + + print('Registering Battery Provider...') + + battery_provider_manager.RegisterBatteryProvider(BATTERY_PROVIDER_PATH, + reply_handler=register_provider_cb, + error_handler=register_provider_error_cb) + + # Unregister the Battery Provider after an arbitrary amount of time + GObject.timeout_add( + 12000, unregister_battery_provider, battery_provider_manager) + # Simulate battery removal by a provider + GObject.timeout_add(8000, remove_battery, app, battery1) + + mainloop.run() + + +if __name__ == '__main__': + main() From patchwork Wed Nov 11 01:17:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sonny Sasaka X-Patchwork-Id: 11896085 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 420F9C61DD8 for ; Wed, 11 Nov 2020 01:18:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id F33EB221F1 for ; Wed, 11 Nov 2020 01:18:04 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="ZRHeaN0J" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1733048AbgKKBSA (ORCPT ); Tue, 10 Nov 2020 20:18:00 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49176 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732586AbgKKBR6 (ORCPT ); Tue, 10 Nov 2020 20:17:58 -0500 Received: from mail-pl1-x642.google.com (mail-pl1-x642.google.com [IPv6:2607:f8b0:4864:20::642]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2054EC0613D1 for ; Tue, 10 Nov 2020 17:17:58 -0800 (PST) Received: by mail-pl1-x642.google.com with SMTP id g11so89235pll.13 for ; Tue, 10 Nov 2020 17:17:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=2hhs0cimnIAGDXquUQAWrxX/tnrV59lDfBqhqkbcuJM=; b=ZRHeaN0J0ZhyTHVueA0FXCBSpj/l2dTjM6YarY42ve3C0Fo1BbzWA7bjCk/V0326rJ ykqbX5sJoMY7cL0gTHAKQt+c0VaeSLluqtt/W19FpkGunE3L4kQ5uYja4dahGzUBNxry j0VTxOlamTchrtswV5ohancalGmg5jya/GRiQ= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=2hhs0cimnIAGDXquUQAWrxX/tnrV59lDfBqhqkbcuJM=; b=ngOO7c/2+QxhhtcqYmvPOPsMwN+F4dTxSYZRcifYX7C3H1JUezoQb0tuap0F6dAYgc vQUPZqVPyQNjfAyeTK353qdy9l3FFl2Tq/WSFJGXi77BTa4NQvqTOaqRe10bfHVIoiAv 1qWbhYA0mMThbSeC/tJJvjGn4pKFIicQkjl/w8iR3iIQimfb9Jl/lwHanY3MhnhBAKdu IhjL6+UIs4fBB3l0T32uwi9zW3PeZoWYqlouAWKrWLsBilRo0h5bWkfL8JKSLHwF8dzW ABjSY5GQBgYb/2dS/H9/tB4gyiLjk849Jrj+kGNH4NsT8wMwUdsvlQC2gcbxjQEK8zbb IYvw== X-Gm-Message-State: AOAM5308m/XRSnUImMc8Qnz3TKSxmlTArud2M+FjmGbkUts7qx9TqzxL g3Grc3Giipjq0/D9h3Fe7NU/fLFwTB5zkg== X-Google-Smtp-Source: ABdhPJwiqAKeWrnaprYS+WB+1JTjiyzAoC+5mnAAOH1Nm6e7Syxyd/7LomdbDG948TSMJOqDoR95xg== X-Received: by 2002:a17:902:7681:b029:d6:42d5:6af3 with SMTP id m1-20020a1709027681b02900d642d56af3mr19353876pll.12.1605057477475; Tue, 10 Nov 2020 17:17:57 -0800 (PST) Received: from sonnysasaka-chrome.mtv.corp.google.com ([2620:15c:202:201:4a0f:cfff:fe66:e60c]) by smtp.gmail.com with ESMTPSA id y19sm331695pfn.147.2020.11.10.17.17.56 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 10 Nov 2020 17:17:56 -0800 (PST) From: Sonny Sasaka To: linux-bluetooth@vger.kernel.org Cc: Sonny Sasaka , Miao-chen Chou Subject: [PATCH BlueZ v2 6/7] adapter: Add a public function to find a device by path Date: Tue, 10 Nov 2020 17:17:44 -0800 Message-Id: <20201111011745.2016-6-sonnysasaka@chromium.org> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201111011745.2016-1-sonnysasaka@chromium.org> References: <20201111011745.2016-1-sonnysasaka@chromium.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org The public function is motivated by the Battery Provider API code which needs to probe whether a device exists. Reviewed-by: Miao-chen Chou --- src/adapter.c | 33 ++++++++++++++++++++++++--------- src/adapter.h | 2 ++ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/adapter.c b/src/adapter.c index c0053000a..d27faaaa3 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -872,6 +872,30 @@ struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter, return device; } +static int device_path_cmp(gconstpointer a, gconstpointer b) +{ + const struct btd_device *device = a; + const char *path = b; + const char *dev_path = device_get_path(device); + + return strcasecmp(dev_path, path); +} + +struct btd_device *btd_adapter_find_device_by_path(struct btd_adapter *adapter, + const char *path) +{ + GSList *list; + + if (!adapter) + return NULL; + + list = g_slist_find_custom(adapter->devices, path, device_path_cmp); + if (!list) + return NULL; + + return list->data; +} + static void uuid_to_uuid128(uuid_t *uuid128, const uuid_t *uuid) { if (uuid->type == SDP_UUID16) @@ -3184,15 +3208,6 @@ static gboolean property_get_roles(const GDBusPropertyTable *property, return TRUE; } -static int device_path_cmp(gconstpointer a, gconstpointer b) -{ - const struct btd_device *device = a; - const char *path = b; - const char *dev_path = device_get_path(device); - - return strcasecmp(dev_path, path); -} - static DBusMessage *remove_device(DBusConnection *conn, DBusMessage *msg, void *user_data) { diff --git a/src/adapter.h b/src/adapter.h index dcc574857..a77c7a61c 100644 --- a/src/adapter.h +++ b/src/adapter.h @@ -83,6 +83,8 @@ sdp_list_t *btd_adapter_get_services(struct btd_adapter *adapter); struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter, const bdaddr_t *dst, uint8_t dst_type); +struct btd_device *btd_adapter_find_device_by_path(struct btd_adapter *adapter, + const char *path); const char *adapter_get_path(struct btd_adapter *adapter); const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter); From patchwork Wed Nov 11 01:17:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sonny Sasaka X-Patchwork-Id: 11896087 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 383B0C55ABD for ; Wed, 11 Nov 2020 01:18:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D3ACC221F1 for ; Wed, 11 Nov 2020 01:18:03 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="ifSjF+LV" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1733064AbgKKBSC (ORCPT ); Tue, 10 Nov 2020 20:18:02 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49182 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732427AbgKKBR7 (ORCPT ); Tue, 10 Nov 2020 20:17:59 -0500 Received: from mail-pf1-x429.google.com (mail-pf1-x429.google.com [IPv6:2607:f8b0:4864:20::429]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9F120C0613D1 for ; Tue, 10 Nov 2020 17:17:59 -0800 (PST) Received: by mail-pf1-x429.google.com with SMTP id c66so529018pfa.4 for ; Tue, 10 Nov 2020 17:17:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=r6ulbDU/qHtPa8uOGjOuIGnHeKT1XK2hEQHAcI9dgwY=; b=ifSjF+LV+rovtO6Zrg4u8oSXG7pSSw27TSQ+HzEhgITC3vDDpmDPz9eQ/eOnKl699u /ZhGcyiaMAuJ98UYHOKjNUgizmOMf4hwS8V6H9EPmwDutBLVCPSXeDfiruZFnpiwXfTl JjdqnJzSmekz0gnXOe1LnVoUnQhKa5q47T4sM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=r6ulbDU/qHtPa8uOGjOuIGnHeKT1XK2hEQHAcI9dgwY=; b=OJfklHVRWQpqsrGv9FJ4ZH7oxgxUcLg8g0r2x6j5tKfsrHGcni5ANIJVC5F7GTBmds URq4Watmkrms5rU+PWMCEJGnp8ztyxYxK6SWlCmVMAIc2ZzeiYnBzHoVYTRvZctcHAkt k3RUSMRMwR73dsppdqP6X5FmTipDkzBYPnf6EkOf36YsUENCStZYamEe3hv83+hrbfjS Gotgf1JHSMvy8pZJzXEC6hpL+gN4fx2ZPiYzcMwRwVB2RedwpFjwxgFhPy1RH+ODapa6 3L6yBj4SeFdwAPS7dUAo94HZsMLm0jkwGZsieZUBabvoGxYWMrc7vPzOdeJhX/YwmfxQ kBUw== X-Gm-Message-State: AOAM532hxZCkiiaS+TL292lcPKEgiO6wIpMG6Jd8YhldYzxdPUpgGuNa AlGqZTZ9S8wPLfMQEfnODBCIIPUEJ1iVKQ== X-Google-Smtp-Source: ABdhPJy0F9w6Fw1d6jCn+Zdtb9hpfH56sWiI5tuIkcepm37/j3JTiSRSiRuSEDuejiAlNK2e43637w== X-Received: by 2002:a63:1e64:: with SMTP id p36mr19120266pgm.126.1605057478769; Tue, 10 Nov 2020 17:17:58 -0800 (PST) Received: from sonnysasaka-chrome.mtv.corp.google.com ([2620:15c:202:201:4a0f:cfff:fe66:e60c]) by smtp.gmail.com with ESMTPSA id y19sm331695pfn.147.2020.11.10.17.17.57 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 10 Nov 2020 17:17:58 -0800 (PST) From: Sonny Sasaka To: linux-bluetooth@vger.kernel.org Cc: Sonny Sasaka , Miao-chen Chou Subject: [PATCH BlueZ v2 7/7] battery: Implement Battery Provider API Date: Tue, 10 Nov 2020 17:17:45 -0800 Message-Id: <20201111011745.2016-7-sonnysasaka@chromium.org> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201111011745.2016-1-sonnysasaka@chromium.org> References: <20201111011745.2016-1-sonnysasaka@chromium.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org This patch implements the BatteryProvider1 and BatteryProviderManager1 API. This is a means for external clients to feed battery information to BlueZ if they handle some profile and can decode battery reporting. The battery information is then exposed externally via the existing Battery1 interface. UI components can consume this API to display Bluetooth peripherals' battery via a unified BlueZ API. Reviewed-by: Miao-chen Chou --- profiles/battery/battery.c | 2 +- src/adapter.c | 11 ++ src/battery.c | 371 ++++++++++++++++++++++++++++++++++++- src/battery.h | 10 +- 4 files changed, 389 insertions(+), 5 deletions(-) diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c index 2478816a4..81f849d57 100644 --- a/profiles/battery/battery.c +++ b/profiles/battery/battery.c @@ -127,7 +127,7 @@ static void batt_io_ccc_written_cb(uint16_t att_ecode, void *user_data) } batt->battery = btd_battery_register(device_get_path(batt->device), - "GATT Battery Service"); + "GATT Battery Service", NULL); if (!batt->battery) { batt_reset(batt); diff --git a/src/adapter.c b/src/adapter.c index d27faaaa3..b791cdad2 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -66,6 +66,7 @@ #include "advertising.h" #include "adv_monitor.h" #include "eir.h" +#include "battery.h" #define MODE_OFF 0x00 #define MODE_CONNECTABLE 0x01 @@ -254,6 +255,8 @@ struct btd_adapter { struct btd_adv_monitor_manager *adv_monitor_manager; + struct btd_battery_provider_manager *battery_provider_manager; + gboolean initialized; GSList *pin_callbacks; @@ -6361,6 +6364,9 @@ static void adapter_remove(struct btd_adapter *adapter) btd_adv_monitor_manager_destroy(adapter->adv_monitor_manager); adapter->adv_monitor_manager = NULL; + btd_battery_provider_manager_destroy(adapter->battery_provider_manager); + adapter->battery_provider_manager = NULL; + g_slist_free(adapter->pin_callbacks); adapter->pin_callbacks = NULL; @@ -8651,6 +8657,11 @@ static int adapter_register(struct btd_adapter *adapter) } } + if (g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL) { + adapter->battery_provider_manager = + btd_battery_provider_manager_create(adapter); + } + db = btd_gatt_database_get_db(adapter->database); adapter->db_id = gatt_db_register(db, services_modified, services_modified, diff --git a/src/battery.c b/src/battery.c index 8613d6e23..8594c8d77 100644 --- a/src/battery.c +++ b/src/battery.c @@ -25,8 +25,11 @@ #include "dbus-common.h" #include "adapter.h" #include "log.h" +#include "error.h" #define BATTERY_INTERFACE "org.bluez.Battery1" +#define BATTERY_PROVIDER_INTERFACE "org.bluez.BatteryProvider1" +#define BATTERY_PROVIDER_MANAGER_INTERFACE "org.bluez.BatteryProviderManager1" #define BATTERY_MAX_PERCENTAGE 100 @@ -34,10 +37,27 @@ struct btd_battery { char *path; /* D-Bus object path */ uint8_t percentage; /* valid between 0 to 100 inclusively */ char *source; /* Descriptive source of the battery info */ + char *provider_path; /* The provider root path, if any */ +}; + +struct btd_battery_provider_manager { + struct btd_adapter *adapter; /* Does not own pointer */ + struct queue *battery_providers; +}; + +struct battery_provider { + struct btd_battery_provider_manager *manager; /* Does not own pointer */ + + char *owner; /* Owner D-Bus address */ + char *path; /* D-Bus object path */ + + GDBusClient *client; }; static struct queue *batteries = NULL; +static void provider_disconnect_cb(DBusConnection *conn, void *user_data); + static void battery_add(struct btd_battery *battery) { if (!batteries) @@ -63,7 +83,8 @@ static bool match_path(const void *data, const void *user_data) return g_strcmp0(battery->path, path) == 0; } -static struct btd_battery *battery_new(const char *path, const char *source) +static struct btd_battery *battery_new(const char *path, const char *source, + const char *provider_path) { struct btd_battery *battery; @@ -72,6 +93,8 @@ static struct btd_battery *battery_new(const char *path, const char *source) battery->percentage = UINT8_MAX; if (source) battery->source = g_strdup(source); + if (provider_path) + battery->provider_path = g_strdup(provider_path); return battery; } @@ -133,7 +156,8 @@ static const GDBusPropertyTable battery_properties[] = { {} }; -struct btd_battery *btd_battery_register(const char *path, const char *source) +struct btd_battery *btd_battery_register(const char *path, const char *source, + const char *provider_path) { struct btd_battery *battery; @@ -149,7 +173,7 @@ struct btd_battery *btd_battery_register(const char *path, const char *source) return NULL; } - battery = battery_new(path, source); + battery = battery_new(path, source, provider_path); battery_add(battery); if (!g_dbus_register_interface(btd_get_dbus_connection(), battery->path, @@ -216,3 +240,344 @@ bool btd_battery_update(struct btd_battery *battery, uint8_t percentage) return true; } + +static struct btd_battery *find_battery_by_path(const char *path) +{ + return queue_find(batteries, match_path, path); +} + +static void provided_battery_property_changed_cb(GDBusProxy *proxy, + const char *name, + DBusMessageIter *iter, + void *user_data) +{ + uint8_t percentage; + struct battery_provider *provider = user_data; + const char *path = g_dbus_proxy_get_path(proxy); + const char *export_path; + + export_path = &path[strlen(provider->path)]; + + if (strcmp(name, "Percentage") != 0) + return; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BYTE) + return; + + dbus_message_iter_get_basic(iter, &percentage); + + DBG("battery percentage changed on %s, percentage = %d", + g_dbus_proxy_get_path(proxy), percentage); + + btd_battery_update(find_battery_by_path(export_path), percentage); +} + +static void provided_battery_added_cb(GDBusProxy *proxy, void *user_data) +{ + struct battery_provider *provider = user_data; + struct btd_battery *battery; + const char *path = g_dbus_proxy_get_path(proxy); + const char *export_path; + const char *source = NULL; + uint8_t percentage; + DBusMessageIter iter; + + export_path = &path[strlen(provider->path)]; + + if (strcmp(g_dbus_proxy_get_interface(proxy), + BATTERY_PROVIDER_INTERFACE) != 0) + return; + + if (!btd_adapter_find_device_by_path(provider->manager->adapter, + export_path)) { + warn("Ignoring non-existent device path for battery %s", + export_path); + return; + } + + if (find_battery_by_path(export_path)) + return; + + g_dbus_proxy_set_property_watch( + proxy, provided_battery_property_changed_cb, provider); + + if (g_dbus_proxy_get_property(proxy, "Source", &iter) == TRUE) + dbus_message_iter_get_basic(&iter, &source); + + battery = btd_battery_register(export_path, source, provider->path); + + DBG("provided battery added %s", path); + + /* Percentage property may not be immediately available, that's okay + * since we monitor changes to this property. + */ + if (g_dbus_proxy_get_property(proxy, "Percentage", &iter) == FALSE) + return; + + dbus_message_iter_get_basic(&iter, &percentage); + + btd_battery_update(battery, percentage); +} + +static void provided_battery_removed_cb(GDBusProxy *proxy, void *user_data) +{ + struct battery_provider *provider = user_data; + struct btd_battery *battery; + const char *path = g_dbus_proxy_get_path(proxy); + const char *export_path; + + export_path = &path[strlen(provider->path)]; + + if (strcmp(g_dbus_proxy_get_interface(proxy), + BATTERY_PROVIDER_INTERFACE) != 0) + return; + + DBG("provided battery removed %s", g_dbus_proxy_get_path(proxy)); + + battery = find_battery_by_path(export_path); + if (!battery) + return; + + if (g_strcmp0(battery->provider_path, provider->path) != 0) + return; + + g_dbus_proxy_set_property_watch(proxy, NULL, NULL); + + btd_battery_unregister(battery); +} + +static bool match_provider_path(const void *data, const void *user_data) +{ + const struct battery_provider *provider = data; + const char *path = user_data; + + return strcmp(provider->path, path) == 0; +} + +static void unregister_if_path_has_prefix(void *data, void *user_data) +{ + struct btd_battery *battery = data; + struct battery_provider *provider = user_data; + + if (g_strcmp0(battery->provider_path, provider->path) == 0) + btd_battery_unregister(battery); +} + +static void battery_provider_free(gpointer data) +{ + struct battery_provider *provider = data; + + /* Unregister batteries under the root path of provider->path */ + queue_foreach(batteries, unregister_if_path_has_prefix, provider); + + if (provider->owner) + g_free(provider->owner); + + if (provider->path) + g_free(provider->path); + + if (provider->client) { + g_dbus_client_set_disconnect_watch(provider->client, NULL, + NULL); + g_dbus_client_set_proxy_handlers(provider->client, NULL, NULL, + NULL, NULL); + g_dbus_client_unref(provider->client); + } + + free(provider); +} + +static struct battery_provider * +battery_provider_new(DBusConnection *conn, + struct btd_battery_provider_manager *manager, + const char *path, const char *sender) +{ + struct battery_provider *provider; + + provider = new0(struct battery_provider, 1); + provider->manager = manager; + provider->owner = g_strdup(sender); + provider->path = g_strdup(path); + + provider->client = g_dbus_client_new_full(conn, sender, path, path); + + if (!provider->client) { + error("error creating D-Bus client %s", path); + battery_provider_free(provider); + return NULL; + } + + g_dbus_client_set_disconnect_watch(provider->client, + provider_disconnect_cb, provider); + + g_dbus_client_set_proxy_handlers(provider->client, + provided_battery_added_cb, + provided_battery_removed_cb, NULL, + provider); + + return provider; +} + +static void provider_disconnect_cb(DBusConnection *conn, void *user_data) +{ + struct battery_provider *provider = user_data; + struct btd_battery_provider_manager *manager = provider->manager; + + DBG("battery provider client disconnected %s root path %s", + provider->owner, provider->path); + + if (!queue_find(manager->battery_providers, NULL, provider)) { + warn("Disconnection on a non-existing provider %s", + provider->path); + return; + } + + queue_remove(manager->battery_providers, provider); + battery_provider_free(provider); +} + +static DBusMessage *register_battery_provider(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + struct btd_battery_provider_manager *manager = user_data; + const char *sender = dbus_message_get_sender(msg); + DBusMessageIter args; + const char *path; + struct battery_provider *provider; + + if (!dbus_message_iter_init(msg, &args)) + return btd_error_invalid_args(msg); + + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) + return btd_error_invalid_args(msg); + + dbus_message_iter_get_basic(&args, &path); + + DBG("register battery provider path = %s", path); + + if (!g_str_has_prefix(path, "/")) + return btd_error_invalid_args(msg); + + if (queue_find(manager->battery_providers, match_provider_path, path)) { + return dbus_message_new_error(msg, + ERROR_INTERFACE ".AlreadyExists", + "Provider already exists"); + } + + provider = battery_provider_new(conn, manager, path, sender); + queue_push_head(manager->battery_providers, provider); + + return dbus_message_new_method_return(msg); +} + +static DBusMessage *unregister_battery_provider(DBusConnection *conn, + DBusMessage *msg, + void *user_data) +{ + struct btd_battery_provider_manager *manager = user_data; + const char *sender = dbus_message_get_sender(msg); + DBusMessageIter args; + const char *path; + struct battery_provider *provider; + + if (!dbus_message_iter_init(msg, &args)) + return btd_error_invalid_args(msg); + + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) + return btd_error_invalid_args(msg); + + dbus_message_iter_get_basic(&args, &path); + + DBG("unregister battery provider path = %s", path); + + provider = queue_find(manager->battery_providers, match_provider_path, + path); + if (!provider || strcmp(provider->owner, sender) != 0) { + return dbus_message_new_error(msg, + ERROR_INTERFACE ".DoesNotExist", + "Provider does not exist"); + } + + queue_remove(manager->battery_providers, provider); + battery_provider_free(provider); + + return dbus_message_new_method_return(msg); +} + +static const GDBusMethodTable methods[] = { + { GDBUS_EXPERIMENTAL_METHOD("RegisterBatteryProvider", + GDBUS_ARGS({ "provider", "o" }), NULL, + register_battery_provider) }, + { GDBUS_EXPERIMENTAL_METHOD("UnregisterBatteryProvider", + GDBUS_ARGS({ "provider", "o" }), NULL, + unregister_battery_provider) }, + {} +}; + +static struct btd_battery_provider_manager * +manager_new(struct btd_adapter *adapter) +{ + struct btd_battery_provider_manager *manager; + + DBG(""); + + manager = new0(struct btd_battery_provider_manager, 1); + manager->adapter = adapter; + manager->battery_providers = queue_new(); + + return manager; +} + +static void manager_free(struct btd_battery_provider_manager *manager) +{ + if (!manager) + return; + + DBG(""); + + queue_destroy(manager->battery_providers, battery_provider_free); + + free(manager); +} + +struct btd_battery_provider_manager * +btd_battery_provider_manager_create(struct btd_adapter *adapter) +{ + struct btd_battery_provider_manager *manager; + + if (!adapter) + return NULL; + + manager = manager_new(adapter); + if (!manager) + return NULL; + + if (!g_dbus_register_interface(btd_get_dbus_connection(), + adapter_get_path(manager->adapter), + BATTERY_PROVIDER_MANAGER_INTERFACE, + methods, NULL, NULL, manager, NULL)) { + error("error registering " BATTERY_PROVIDER_MANAGER_INTERFACE + " interface"); + manager_free(manager); + return NULL; + } + + info("Battery Provider Manager created"); + + return manager; +} + +void btd_battery_provider_manager_destroy( + struct btd_battery_provider_manager *manager) +{ + if (!manager) + return; + + g_dbus_unregister_interface(btd_get_dbus_connection(), + adapter_get_path(manager->adapter), + BATTERY_PROVIDER_MANAGER_INTERFACE); + + info("Battery Provider Manager destroyed"); + + manager_free(manager); +} diff --git a/src/battery.h b/src/battery.h index ff63454cd..271659474 100644 --- a/src/battery.h +++ b/src/battery.h @@ -8,8 +8,16 @@ * */ +struct btd_adapter; struct btd_battery; +struct btd_battery_provider_manager; -struct btd_battery *btd_battery_register(const char *path, const char *source); +struct btd_battery *btd_battery_register(const char *path, const char *source, + const char *provider_path); bool btd_battery_unregister(struct btd_battery *battery); bool btd_battery_update(struct btd_battery *battery, uint8_t percentage); + +struct btd_battery_provider_manager * +btd_battery_provider_manager_create(struct btd_adapter *adapter); +void btd_battery_provider_manager_destroy( + struct btd_battery_provider_manager *manager);