Commit 18b2a915 authored by Alberto Herrera's avatar Alberto Herrera Committed by Commit Bot

[Bluetooth] Show low battery notifications for GATT Bluetooth Devices.

Use the existing PeripheralBatteryNotifier to show low battery
notifications of connected device::BluetoothDevice objects. This CL
adds functionality to trigger for GATT connections that support the
Battery Service.

Also added DeviceBatteryChanged() to device::BluetoothAdapter::Observer
so device::BluetoothDevice can notify about battery level changes and
PeripheralBatteryNotifier gets notified about those changes.

Also modified BluetoothDevice to make the battery percentage property
OS_CHROMEOS-only.

Design doc at go/cros-bt-battery.

Bug: 785758
Change-Id: I325e6bf47032fe5fc2a7471d2a1be9850fd89030
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1703341
Commit-Queue: Alberto Herrera <ahrfgb@google.com>
Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarRyan Hansberry <hansberry@chromium.org>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Cr-Commit-Position: refs/heads/master@{#682791}
parent e0d76169
......@@ -76,7 +76,7 @@ void GattBatteryPoller::OnBatteryPercentageFetched(
if (battery_percentage) {
device::BluetoothDevice* device = adapter_->GetDevice(device_address_);
if (device)
device->set_battery_percentage(*battery_percentage);
device->SetBatteryPercentage(*battery_percentage);
}
ScheduleNextAttempt(battery_percentage.has_value());
......@@ -100,7 +100,7 @@ void GattBatteryPoller::ScheduleNextAttempt(bool was_last_attempt_successful) {
StartNextAttemptTimer();
} else {
// Reset battery field after exceeding the retry count.
device->set_battery_percentage(base::nullopt);
device->SetBatteryPercentage(base::nullopt);
}
}
......
......@@ -165,7 +165,7 @@ TEST_F(GattBatteryPollerTest, RetryPollingAfterAnError) {
}
TEST_F(GattBatteryPollerTest, DoesNotModifyBatteryValueAfterAnError) {
mock_device_->set_battery_percentage(kBatteryPercentage);
mock_device_->SetBatteryPercentage(kBatteryPercentage);
CreateGattBatteryPoller();
EXPECT_EQ(1, fetchers_created_count());
......@@ -180,7 +180,7 @@ TEST_F(GattBatteryPollerTest, DoesNotModifyBatteryValueAfterAnError) {
TEST_F(GattBatteryPollerTest, StopsRetryingAfterMaxRetryCount) {
// Set a battery level to the device. Expect it resets after maximum retry
// count is exceeded.
mock_device_->set_battery_percentage(kBatteryPercentage);
mock_device_->SetBatteryPercentage(kBatteryPercentage);
CreateGattBatteryPoller();
const int kMaxRetryCount = 3;
......
......@@ -5,6 +5,7 @@
#ifndef ASH_SYSTEM_POWER_PERIPHERAL_BATTERY_NOTIFIER_H_
#define ASH_SYSTEM_POWER_PERIPHERAL_BATTERY_NOTIFIER_H_
#include <cstdint>
#include <map>
#include "ash/ash_export.h"
......@@ -12,6 +13,7 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/time/tick_clock.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "device/bluetooth/bluetooth_adapter.h"
......@@ -33,9 +35,6 @@ class ASH_EXPORT PeripheralBatteryNotifier
PeripheralBatteryNotifier();
~PeripheralBatteryNotifier() override;
void set_testing_clock(const base::TickClock* clock) {
testing_clock_ = clock;
}
// chromeos::PowerManagerClient::Observer:
void PeripheralBatteryStatusReceived(const std::string& path,
......@@ -43,8 +42,13 @@ class ASH_EXPORT PeripheralBatteryNotifier
int level) override;
// device::BluetoothAdapter::Observer:
void DeviceChanged(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) override;
void DeviceBatteryChanged(
device::BluetoothAdapter* adapter,
device::BluetoothDevice* device,
base::Optional<uint8_t> new_battery_percentage) override;
void DeviceConnectedStateChanged(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device,
bool is_now_connected) override;
void DeviceRemoved(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) override;
......@@ -57,10 +61,19 @@ class ASH_EXPORT PeripheralBatteryNotifier
FRIEND_TEST_ALL_PREFIXES(PeripheralBatteryNotifierTest, DeviceRemove);
struct BatteryInfo {
BatteryInfo();
BatteryInfo(const base::string16& name,
base::Optional<uint8_t> level,
base::TimeTicks last_notification_timestamp,
bool is_stylus,
const std::string& bluetooth_address);
~BatteryInfo();
BatteryInfo(const BatteryInfo& info);
// Human readable name for the device. It is changeable.
std::string name;
// Battery level within range [0, 100], and -1 for unknown level.
int level = -1;
base::string16 name;
// Battery level within range [0, 100].
base::Optional<uint8_t> level;
base::TimeTicks last_notification_timestamp;
bool is_stylus = false;
// Peripheral's Bluetooth address. Empty for non-Bluetooth devices.
......@@ -75,21 +88,39 @@ class ASH_EXPORT PeripheralBatteryNotifier
// changed or removed.
void RemoveBluetoothBattery(const std::string& bluetooth_address);
// Posts a low battery notification with unique id |path|. Returns true
// if the notification is posted, false if not.
bool PostNotification(const std::string& path, const BatteryInfo& battery);
// Updates the battery information of the peripheral with the corresponding
// |map_key|, and calls to post a notification if the battery level is under
// the threshold.
void UpdateBattery(const std::string& map_key,
const BatteryInfo& battery_info);
// Updates the battery percentage in the corresponding notification.
void UpdateBatteryNotificationIfVisible(const std::string& map_key,
const BatteryInfo& battery);
// Calls to display a notification only if kNotificationInterval seconds have
// passed since the last notification showed, avoiding the case where the
// battery level oscillates around the threshold level.
void ShowNotification(const std::string& map_key, const BatteryInfo& battery);
// Posts a low battery notification with id as |map_key|. If a notification
// with the same id exists, its content gets updated.
void ShowOrUpdateNotification(const std::string& map_key,
const BatteryInfo& battery);
void CancelNotification(const std::string& path);
void CancelNotification(const std::string& map_key);
// Record of existing battery infomation. The key is the device path.
// Record of existing battery information. For Bluetooth Devices, the key is
// kBluetoothDeviceIdPrefix + the device's address. For HID devices, the key
// is the device path. If a device uses HID over Bluetooth, it is indexed as a
// Bluetooth device.
std::map<std::string, BatteryInfo> batteries_;
// PeripheralBatteryNotifier is an observer of |bluetooth_adapter_| for
// bluetooth device change/remove events.
scoped_refptr<device::BluetoothAdapter> bluetooth_adapter_;
// Used only for helping test. Not owned and can be nullptr.
const base::TickClock* testing_clock_ = nullptr;
const base::TickClock* clock_;
std::unique_ptr<base::WeakPtrFactory<PeripheralBatteryNotifier>>
weakptr_factory_;
......
......@@ -230,6 +230,15 @@ void BluetoothAdapter::NotifyDevicePairedChanged(BluetoothDevice* device,
}
#endif
#if defined(OS_CHROMEOS)
void BluetoothAdapter::NotifyDeviceBatteryChanged(BluetoothDevice* device) {
DCHECK_EQ(device->GetAdapter(), this);
for (auto& observer : observers_) {
observer.DeviceBatteryChanged(this, device, device->battery_percentage());
}
}
#endif
void BluetoothAdapter::NotifyGattServiceAdded(
BluetoothRemoteGattService* service) {
DCHECK_EQ(service->GetDevice()->GetAdapter(), this);
......
......@@ -170,6 +170,14 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapter
bool is_now_connected) {}
#endif
#if defined(OS_CHROMEOS)
// Called when the battery level of the device has been updated.
virtual void DeviceBatteryChanged(
BluetoothAdapter* adapter,
BluetoothDevice* device,
base::Optional<uint8_t> new_battery_percentage) {}
#endif
// Called when the device |device| is removed from the adapter |adapter|,
// either as a result of a discovered device being lost between discovering
// phases or pairing information deleted. |device| should not be
......@@ -582,6 +590,11 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapter
void NotifyDevicePairedChanged(BluetoothDevice* device,
bool new_paired_status);
#endif
#if defined(OS_CHROMEOS)
void NotifyDeviceBatteryChanged(BluetoothDevice* device);
#endif
void NotifyGattServiceAdded(BluetoothRemoteGattService* service);
void NotifyGattServiceRemoved(BluetoothRemoteGattService* service);
void NotifyGattServiceChanged(BluetoothRemoteGattService* service);
......
......@@ -465,6 +465,20 @@ BluetoothDevice::GetPrimaryServicesByUUID(const BluetoothUUID& service_uuid) {
return services;
}
#if defined(OS_CHROMEOS)
void BluetoothDevice::SetBatteryPercentage(
base::Optional<uint8_t> battery_percentage) {
if (battery_percentage)
DCHECK_LE(battery_percentage.value(), 100);
if (battery_percentage_ == battery_percentage)
return;
battery_percentage_ = battery_percentage;
GetAdapter()->NotifyDeviceBatteryChanged(this);
}
#endif
void BluetoothDevice::DidConnectGatt() {
for (const auto& callback : create_gatt_connection_success_callbacks_) {
callback.Run(
......
......@@ -579,7 +579,6 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice {
// Aborts all the previous prepare writes in a reliable write session.
virtual void AbortWrite(const base::Closure& callback,
const AbortWriteErrorCallback& error_callback) = 0;
#endif
// Set the remaining battery of the device to show in the UI. This value must
// be between 0 and 100, inclusive.
......@@ -587,13 +586,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice {
// ash::GattBatteryPoller and used only by Chrome OS. In the future, when
// there is a unified Mojo service, this logic will be moved to
// BluetoothDeviceInfo.
void set_battery_percentage(base::Optional<uint8_t> battery_percentage) {
if (battery_percentage) {
DCHECK(battery_percentage.value() >= 0 &&
battery_percentage.value() <= 100);
}
battery_percentage_ = battery_percentage;
}
void SetBatteryPercentage(base::Optional<uint8_t> battery_percentage);
// Returns the remaining battery for the device.
// TODO(https://crbug.com/973237): Battery percentage is populated by
......@@ -603,6 +596,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice {
const base::Optional<uint8_t>& battery_percentage() const {
return battery_percentage_;
}
#endif
protected:
// BluetoothGattConnection is a friend to call Add/RemoveGattConnection.
......@@ -737,13 +731,13 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice {
// a device type for display when |name_| is empty.
base::string16 GetAddressWithLocalizedDeviceTypeName() const;
#if defined(OS_CHROMEOS)
// Remaining battery level of the device.
// TODO(https://crbug.com/973237): This field is populated by
// ash::GattBatteryPoller and used only by Chrome OS. This field is different
// from others because it is not filled by the platform. In the future, when
// there is a unified Mojo service, this field will be moved to
// BluetoothDeviceInfo.
// TODO(https://crbug.com/973237): This field is different from others because
// it is not filled by the platform. In the future, when there is a unified
// Mojo service, this field will be moved to BluetoothDeviceInfo.
base::Optional<uint8_t> battery_percentage_;
#endif
DISALLOW_COPY_AND_ASSIGN(BluetoothDevice);
};
......
......@@ -159,10 +159,12 @@ void BluetoothDeviceToApiDevice(const device::BluetoothDevice& device,
else
out->inquiry_tx_power.reset();
#if defined(OS_CHROMEOS)
if (device.battery_percentage())
out->battery_percentage.reset(new int(device.battery_percentage().value()));
else
out->battery_percentage.reset();
#endif
#if defined(OS_LINUX)
ConvertTransportToApi(device.GetType(), &(out->transport));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment