Commit 8a896dcc authored by Alberto Herrera's avatar Alberto Herrera Committed by Commit Bot

[Bluetooth] Create PeripheralBatteryTracker and GattBatteryController

The PeripheralBatteryTracker is instantiated by ash::Shell and creates
one instance of GattBatteryController. The latter creates a
GattBatteryPoller object for each new connected Bluetooth Device.

Design doc at go/cros-bt-battery.

Bug: 785758
Change-Id: If7cda46a95afb36cc631f88047c6a117f0675894
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1660098
Commit-Queue: Alberto Herrera <ahrfgb@google.com>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarRyan Hansberry <hansberry@chromium.org>
Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Cr-Commit-Position: refs/heads/master@{#680632}
parent 9248cf3e
......@@ -508,10 +508,16 @@ component("ash") {
"policy/policy_recommendation_restorer.h",
"power/fake_gatt_battery_percentage_fetcher.cc",
"power/fake_gatt_battery_percentage_fetcher.h",
"power/fake_gatt_battery_poller.cc",
"power/fake_gatt_battery_poller.h",
"power/gatt_battery_controller.cc",
"power/gatt_battery_controller.h",
"power/gatt_battery_percentage_fetcher.cc",
"power/gatt_battery_percentage_fetcher.h",
"power/gatt_battery_poller.cc",
"power/gatt_battery_poller.h",
"power/peripheral_battery_tracker.cc",
"power/peripheral_battery_tracker.h",
"root_window_controller.cc",
"root_window_settings.cc",
"root_window_settings.h",
......@@ -1718,6 +1724,7 @@ test("ash_unittests") {
"metrics/user_metrics_recorder_unittest.cc",
"multi_device_setup/multi_device_notification_presenter_unittest.cc",
"policy/policy_recommendation_restorer_unittest.cc",
"power/gatt_battery_controller_unittest.cc",
"power/gatt_battery_percentage_fetcher_unittest.cc",
"power/gatt_battery_poller_unittest.cc",
"root_window_controller_unittest.cc",
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/power/fake_gatt_battery_poller.h"
namespace ash {
FakeGattBatteryPoller::FakeGattBatteryPoller(
const std::string& device_address,
base::OnceClosure destructor_callback)
: GattBatteryPoller(device_address),
destructor_callback_(std::move(destructor_callback)) {}
FakeGattBatteryPoller::~FakeGattBatteryPoller() {
std::move(destructor_callback_).Run();
}
} // namespace ash
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_POWER_FAKE_GATT_BATTERY_POLLER_H_
#define ASH_POWER_FAKE_GATT_BATTERY_POLLER_H_
#include <memory>
#include "ash/ash_export.h"
#include "ash/power/gatt_battery_poller.h"
#include "base/callback.h"
#include "base/macros.h"
namespace ash {
// Fake implementation of a GattBatteryPoller to use in tests.
class ASH_EXPORT FakeGattBatteryPoller : public GattBatteryPoller {
public:
FakeGattBatteryPoller(const std::string& device_address,
base::OnceClosure destructor_callback);
~FakeGattBatteryPoller() override;
private:
base::OnceClosure destructor_callback_;
DISALLOW_COPY_AND_ASSIGN(FakeGattBatteryPoller);
};
} // namespace ash
#endif // ASH_POWER_FAKE_GATT_BATTERY_POLLER_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/power/gatt_battery_controller.h"
#include "ash/power/gatt_battery_poller.h"
#include "base/stl_util.h"
#include "base/timer/timer.h"
#include "device/bluetooth/bluetooth_device.h"
namespace ash {
GattBatteryController::GattBatteryController(
scoped_refptr<device::BluetoothAdapter> adapter)
: adapter_(adapter) {
adapter_->AddObserver(this);
}
GattBatteryController::~GattBatteryController() {
adapter_->RemoveObserver(this);
}
void GattBatteryController::DeviceConnectedStateChanged(
device::BluetoothAdapter* adapter,
device::BluetoothDevice* device,
bool is_now_connected) {
if (is_now_connected) {
EnsurePollerExistsForDevice(device);
return;
}
poller_map_.erase(device->GetAddress());
}
void GattBatteryController::DeviceAdded(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) {
if (device->IsConnected())
EnsurePollerExistsForDevice(device);
}
void GattBatteryController::DeviceRemoved(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) {
poller_map_.erase(device->GetAddress());
}
void GattBatteryController::EnsurePollerExistsForDevice(
device::BluetoothDevice* device) {
const std::string device_address = device->GetAddress();
// Don't do anything if the device is already registered.
if (base::Contains(poller_map_, device_address))
return;
poller_map_[device_address] = GattBatteryPoller::Factory::NewInstance(
adapter_, device_address, std::make_unique<base::OneShotTimer>());
}
} // namespace ash
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_POWER_GATT_BATTERY_CONTROLLER_H_
#define ASH_POWER_GATT_BATTERY_CONTROLLER_H_
#include <memory>
#include "ash/ash_export.h"
#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "device/bluetooth/bluetooth_adapter.h"
namespace device {
class BluetoothDevice;
} // namespace device
namespace ash {
class GattBatteryPoller;
// Creates a GattBatteryPoller for each new connected Bluetooth device.
class ASH_EXPORT GattBatteryController
: public device::BluetoothAdapter::Observer {
public:
GattBatteryController(scoped_refptr<device::BluetoothAdapter> adapter);
~GattBatteryController() override;
private:
friend class GattBatteryControllerTest;
// device::BluetoothAdapter::Observer:
void DeviceConnectedStateChanged(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device,
bool is_now_connected) override;
void DeviceAdded(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) override;
void DeviceRemoved(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) override;
// Creates a GattBatteryPoller for the device if one doesn't exist already.
void EnsurePollerExistsForDevice(device::BluetoothDevice* device);
scoped_refptr<device::BluetoothAdapter> adapter_;
// Map of Pollers indexed by the Bluetooth address of the connected device
// they refer to.
base::flat_map<std::string, std::unique_ptr<GattBatteryPoller>> poller_map_;
DISALLOW_COPY_AND_ASSIGN(GattBatteryController);
};
} // namespace ash
#endif // ASH_POWER_GATT_BATTERY_CONTROLLER_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/power/gatt_battery_controller.h"
#include "ash/power/fake_gatt_battery_poller.h"
#include "ash/power/gatt_battery_poller.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/memory/scoped_refptr.h"
#include "base/timer/timer.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_device.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::NiceMock;
using testing::Return;
namespace ash {
namespace {
class FakeGattBatteryPollerFactory : public GattBatteryPoller::Factory {
public:
FakeGattBatteryPollerFactory() = default;
~FakeGattBatteryPollerFactory() override = default;
FakeGattBatteryPoller* GetFakeBatteryPoller(const std::string& address) {
auto it = poller_map_.find(address);
return it != poller_map_.end() ? it->second : nullptr;
}
int pollers_created_count() { return pollers_created_count_; }
private:
std::unique_ptr<GattBatteryPoller> BuildInstance(
scoped_refptr<device::BluetoothAdapter> adapter,
const std::string& device_address,
std::unique_ptr<base::OneShotTimer> poll_timer) override {
++pollers_created_count_;
auto fake_gatt_battery_poller = std::make_unique<FakeGattBatteryPoller>(
device_address,
base::BindOnce(&FakeGattBatteryPollerFactory::OnPollerDestroyed,
base::Unretained(this), device_address));
poller_map_[device_address] = fake_gatt_battery_poller.get();
return std::move(fake_gatt_battery_poller);
}
void OnPollerDestroyed(const std::string address) {
poller_map_.erase(address);
}
// Map of fake Pollers, indexed by the address of their Bluetooth device.
base::flat_map<std::string, FakeGattBatteryPoller*> poller_map_;
int pollers_created_count_ = 0;
DISALLOW_COPY_AND_ASSIGN(FakeGattBatteryPollerFactory);
};
} // namespace
class GattBatteryControllerTest : public testing::Test {
public:
GattBatteryControllerTest() = default;
~GattBatteryControllerTest() override = default;
void SetUp() override {
mock_adapter_ =
base::MakeRefCounted<NiceMock<device::MockBluetoothAdapter>>();
mock_device_1_ =
std::make_unique<testing::NiceMock<device::MockBluetoothDevice>>(
nullptr /* adapter */, 0 /* bluetooth_class */, "name_1",
"address_1", true /* paired */, true /* connected */);
mock_device_2_ =
std::make_unique<testing::NiceMock<device::MockBluetoothDevice>>(
nullptr /* adapter */, 0 /* bluetooth_class */, "name_2",
"address_2", true /* paired */, true /* connected */);
fake_gatt_battery_poller_factory_ =
std::make_unique<FakeGattBatteryPollerFactory>();
GattBatteryPoller::Factory::SetFactoryForTesting(
fake_gatt_battery_poller_factory_.get());
gatt_controller_ = std::make_unique<GattBatteryController>(mock_adapter_);
}
void TearDown() override {
GattBatteryPoller::Factory::SetFactoryForTesting(nullptr);
}
void NotifyConnectedStateChanged(device::BluetoothDevice* device,
bool is_connected) {
DCHECK(gatt_controller_);
gatt_controller_->DeviceConnectedStateChanged(mock_adapter_.get(), device,
is_connected);
}
void NotifyDeviceAdded(device::BluetoothDevice* device) {
DCHECK(gatt_controller_);
gatt_controller_->DeviceAdded(mock_adapter_.get(), device);
}
void NotifyDeviceRemoved(device::BluetoothDevice* device) {
DCHECK(gatt_controller_);
gatt_controller_->DeviceRemoved(mock_adapter_.get(), device);
}
FakeGattBatteryPoller* GetFakeBatteryPoller(
device::MockBluetoothDevice* mock_device) {
return fake_gatt_battery_poller_factory_->GetFakeBatteryPoller(
mock_device->GetAddress());
}
int pollers_created_count() {
return fake_gatt_battery_poller_factory_->pollers_created_count();
}
protected:
scoped_refptr<NiceMock<device::MockBluetoothAdapter>> mock_adapter_;
std::unique_ptr<device::MockBluetoothDevice> mock_device_1_;
std::unique_ptr<device::MockBluetoothDevice> mock_device_2_;
std::unique_ptr<FakeGattBatteryPollerFactory>
fake_gatt_battery_poller_factory_;
std::unique_ptr<GattBatteryController> gatt_controller_;
private:
DISALLOW_COPY_AND_ASSIGN(GattBatteryControllerTest);
};
TEST_F(GattBatteryControllerTest,
CreatesPollerForNewConnectedDevices_ConnectedStateChanged) {
// Indicate a connection to a device was made. A Poller should be created.
NotifyConnectedStateChanged(mock_device_1_.get(), true /* is_connected */);
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_1_.get()));
EXPECT_EQ(1, pollers_created_count());
// Another device connected. Should create another poller.
NotifyConnectedStateChanged(mock_device_2_.get(), true /* is_connected */);
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_1_.get()));
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_2_.get()));
EXPECT_EQ(2, pollers_created_count());
}
TEST_F(GattBatteryControllerTest,
CreatesPollerForNewConnectedDevices_DeviceAdded) {
NotifyDeviceAdded(mock_device_1_.get());
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_1_.get()));
EXPECT_EQ(1, pollers_created_count());
NotifyDeviceAdded(mock_device_2_.get());
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_1_.get()));
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_2_.get()));
EXPECT_EQ(2, pollers_created_count());
}
TEST_F(GattBatteryControllerTest,
DoesntCreatePollerForAlreadyConnectedDevices_ConnectedStateChanged) {
// Simulate a device connected, should create a poller.
NotifyConnectedStateChanged(mock_device_1_.get(), true /* is_connected */);
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_1_.get()));
EXPECT_EQ(1, pollers_created_count());
// Calls to DeviceConnectedStateChanged() with a connected device doesn't
// create new instances.
NotifyConnectedStateChanged(mock_device_1_.get(), true /* is_connected */);
EXPECT_EQ(1, pollers_created_count());
}
TEST_F(GattBatteryControllerTest,
DoesntCreatePollerForAlreadyConnectedDevices_DeviceAdded) {
NotifyConnectedStateChanged(mock_device_1_.get(), true /* is_connected */);
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_1_.get()));
EXPECT_EQ(1, pollers_created_count());
// Calls to DeviceAdded() with a connected device doesn't create new
// instances.
NotifyDeviceAdded(mock_device_1_.get());
EXPECT_EQ(1, pollers_created_count());
}
TEST_F(GattBatteryControllerTest,
RemovesDisconnectedDevices_ConnectedStateChanged) {
NotifyConnectedStateChanged(mock_device_1_.get(), true /* is_connected */);
NotifyConnectedStateChanged(mock_device_2_.get(), true /* is_connected */);
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_1_.get()));
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_2_.get()));
EXPECT_EQ(2, pollers_created_count());
// Indicate a device is no longer connected.
NotifyConnectedStateChanged(mock_device_1_.get(), false /* is_connected */);
// Should stop tracking just that device.
EXPECT_FALSE(GetFakeBatteryPoller(mock_device_1_.get()));
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_2_.get()));
// Disconnect the second device.
NotifyConnectedStateChanged(mock_device_2_.get(), false /* is_connected */);
EXPECT_FALSE(GetFakeBatteryPoller(mock_device_2_.get()));
EXPECT_EQ(2, pollers_created_count());
}
TEST_F(GattBatteryControllerTest, RemovesDisconnectedDevices_DeviceRemoved) {
NotifyConnectedStateChanged(mock_device_1_.get(), true /* is_connected */);
NotifyConnectedStateChanged(mock_device_2_.get(), true /* is_connected */);
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_1_.get()));
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_2_.get()));
EXPECT_EQ(2, pollers_created_count());
// Indicate a device is no longer connected.
NotifyDeviceRemoved(mock_device_1_.get());
// Should stop tracking just that device.
EXPECT_FALSE(GetFakeBatteryPoller(mock_device_1_.get()));
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_2_.get()));
// Disconnect the second device.
NotifyDeviceRemoved(mock_device_2_.get());
EXPECT_FALSE(GetFakeBatteryPoller(mock_device_2_.get()));
EXPECT_EQ(2, pollers_created_count());
}
TEST_F(GattBatteryControllerTest,
DoesntDoAnythingRemovingUntrackedDevices_ConnectedStateChanged) {
// Indicate a device is no longer connected.
NotifyConnectedStateChanged(mock_device_1_.get(), false /* is_connected */);
// Should not create any poller nor crash.
EXPECT_EQ(0, pollers_created_count());
}
TEST_F(GattBatteryControllerTest,
DoesntDoAnythingRemovingUntrackedDevices_DeviceRemoved) {
// Indicate a device is no longer connected.
NotifyDeviceRemoved(mock_device_1_.get());
// Should not create any poller nor crash.
EXPECT_EQ(0, pollers_created_count());
}
TEST_F(GattBatteryControllerTest,
CreatesNewPollerAfterDeviceReconnects_ConnectedStateChanged) {
// Simulate a device connected.
NotifyConnectedStateChanged(mock_device_1_.get(), true /* is_connected */);
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_1_.get()));
EXPECT_EQ(1, pollers_created_count());
// Indicate the device is no longer connected.
NotifyConnectedStateChanged(mock_device_1_.get(), false /* is_connected */);
EXPECT_FALSE(GetFakeBatteryPoller(mock_device_1_.get()));
// Reconnect the device.
NotifyConnectedStateChanged(mock_device_1_.get(), true /* is_connected */);
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_1_.get()));
EXPECT_EQ(2, pollers_created_count());
}
TEST_F(GattBatteryControllerTest,
CreatesNewPollerAfterDeviceReconnects_DeviceAdded_DeviceRemoved) {
NotifyDeviceAdded(mock_device_1_.get());
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_1_.get()));
EXPECT_EQ(1, pollers_created_count());
// Indicate the device is no longer connected.
NotifyDeviceRemoved(mock_device_1_.get());
EXPECT_FALSE(GetFakeBatteryPoller(mock_device_1_.get()));
// Reconnect the device.
NotifyDeviceAdded(mock_device_1_.get());
EXPECT_TRUE(GetFakeBatteryPoller(mock_device_1_.get()));
EXPECT_EQ(2, pollers_created_count());
}
} // namespace ash
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/power/peripheral_battery_tracker.h"
#include "ash/power/gatt_battery_controller.h"
#include "base/bind.h"
#include "base/logging.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
namespace ash {
PeripheralBatteryTracker::PeripheralBatteryTracker() {
device::BluetoothAdapterFactory::GetAdapter(
base::BindOnce(&PeripheralBatteryTracker::InitializeOnBluetoothReady,
weak_ptr_factory_.GetWeakPtr()));
}
PeripheralBatteryTracker::~PeripheralBatteryTracker() = default;
void PeripheralBatteryTracker::InitializeOnBluetoothReady(
scoped_refptr<device::BluetoothAdapter> adapter) {
adapter_ = adapter;
DCHECK(adapter_.get());
gatt_battery_controller_ = std::make_unique<GattBatteryController>(adapter_);
}
} // namespace ash
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_POWER_PERIPHERAL_BATTERY_TRACKER_H_
#define ASH_POWER_PERIPHERAL_BATTERY_TRACKER_H_
#include "ash/ash_export.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
namespace device {
class BluetoothAdapter;
} // namespace device
namespace ash {
class GattBatteryController;
// Creates instances of classes to collect the battery status of peripheral
// devices. Currently only tracks Bluetooth devices that support GATT.
// TODO(https://crbug.com/785758): Add support for other protocols, like HID.
class ASH_EXPORT PeripheralBatteryTracker {
public:
PeripheralBatteryTracker();
~PeripheralBatteryTracker();
private:
void InitializeOnBluetoothReady(
scoped_refptr<device::BluetoothAdapter> adapter);
scoped_refptr<device::BluetoothAdapter> adapter_;
std::unique_ptr<GattBatteryController> gatt_battery_controller_;
base::WeakPtrFactory<PeripheralBatteryTracker> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(PeripheralBatteryTracker);
};
} // namespace ash
#endif // ASH_POWER_PERIPHERAL_BATTERY_TRACKER_H_
......@@ -66,6 +66,7 @@
#include "ash/media/media_notification_controller_impl.h"
#include "ash/multi_device_setup/multi_device_notification_presenter.h"
#include "ash/policy/policy_recommendation_restorer.h"
#include "ash/power/peripheral_battery_tracker.h"
#include "ash/public/cpp/ash_constants.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/ash_prefs.h"
......@@ -1123,6 +1124,10 @@ void Shell::Init(
cursor_manager_->SetCursor(ui::CursorType::kPointer);
peripheral_battery_notifier_ = std::make_unique<PeripheralBatteryNotifier>();
if (base::FeatureList::IsEnabled(
chromeos::features::kShowBluetoothDeviceBattery)) {
peripheral_battery_tracker_ = std::make_unique<PeripheralBatteryTracker>();
}
power_event_observer_.reset(new PowerEventObserver());
user_activity_notifier_ =
std::make_unique<ui::UserActivityPowerManagerNotifier>(
......
......@@ -148,6 +148,7 @@ class OverlayEventFilter;
class OverviewController;
class PartialMagnificationController;
class PeripheralBatteryNotifier;
class PeripheralBatteryTracker;
class PersistentWindowController;
class PolicyRecommendationRestorer;
class PowerButtonController;
......@@ -743,6 +744,7 @@ class ASH_EXPORT Shell : public SessionObserver,
std::unique_ptr<ScreenPinningController> screen_pinning_controller_;
std::unique_ptr<PeripheralBatteryNotifier> peripheral_battery_notifier_;
std::unique_ptr<PeripheralBatteryTracker> peripheral_battery_tracker_;
std::unique_ptr<PowerEventObserver> power_event_observer_;
std::unique_ptr<PowerPrefs> power_prefs_;
std::unique_ptr<ui::UserActivityPowerManagerNotifier> user_activity_notifier_;
......
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