Skip to content

Commit

Permalink
[CrOS Bluetooth] Create BluetoothPowerController.
Browse files Browse the repository at this point in the history
Create BluetoothPowerController to manage setting the Bluetooth adapter
state, saving the state to prefs, and applying the pref on device
startup or user login. The logic in this class is largely taken from
the BluetoothPowerController in ash/system/bluetooth, except
CrosBluetoothConfig APIs are used instead of BluetoothAdapter and
ash-specific SessionManager methods are replaced with UserManager
equivalents. For design doc, see
go/cros-bluetooth-revamp-power-controller.

Bug: 1010321
Change-Id: I17c6e1a03d6368c8caf74e69f78b4e28cae37892
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3263257
Commit-Queue: Gordon Seto <gordonseto@google.com>
Reviewed-by: Kyle Horimoto <khorimoto@chromium.org>
Reviewed-by: Xiyuan Xia <xiyuan@chromium.org>
Reviewed-by: Chad Duffin <chadduffin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#940471}
  • Loading branch information
Gordon Seto authored and Chromium LUCI CQ committed Nov 10, 2021
1 parent d7362e0 commit 293e498
Show file tree
Hide file tree
Showing 13 changed files with 672 additions and 2 deletions.
9 changes: 9 additions & 0 deletions chromeos/services/bluetooth_config/BUILD.gn
Expand Up @@ -10,6 +10,9 @@ static_library("bluetooth_config") {
"adapter_state_controller.h",
"adapter_state_controller_impl.cc",
"adapter_state_controller_impl.h",
"bluetooth_power_controller.h",
"bluetooth_power_controller_impl.cc",
"bluetooth_power_controller_impl.h",
"cros_bluetooth_config.cc",
"cros_bluetooth_config.h",
"device_cache.cc",
Expand Down Expand Up @@ -45,6 +48,7 @@ static_library("bluetooth_config") {
]

deps = [
"//ash/constants",
"//base",
"//chromeos/services/bluetooth_config/public/cpp",
"//chromeos/services/bluetooth_config/public/mojom",
Expand Down Expand Up @@ -86,6 +90,8 @@ static_library("test_support") {
"fake_adapter_state_controller.h",
"fake_bluetooth_discovery_delegate.cc",
"fake_bluetooth_discovery_delegate.h",
"fake_bluetooth_power_controller.cc",
"fake_bluetooth_power_controller.h",
"fake_device_cache.cc",
"fake_device_cache.h",
"fake_device_name_manager.cc",
Expand All @@ -112,6 +118,7 @@ static_library("test_support") {
":bluetooth_config",
":in_process_bluetooth_config",
"//base",
"//chromeos/services/bluetooth_config/public/cpp",
"//chromeos/services/bluetooth_config/public/mojom",
"//components/session_manager/core",
"//device/bluetooth",
Expand All @@ -124,6 +131,7 @@ source_set("unit_tests") {

sources = [
"adapter_state_controller_impl_unittest.cc",
"bluetooth_power_controller_impl_unittest.cc",
"cros_bluetooth_config_unittest.cc",
"device_cache_impl_unittest.cc",
"device_conversion_util_unittest.cc",
Expand All @@ -137,6 +145,7 @@ source_set("unit_tests") {
deps = [
":bluetooth_config",
":test_support",
"//ash/constants",
"//base",
"//base/test:test_support",
"//components/session_manager/core",
Expand Down
3 changes: 2 additions & 1 deletion chromeos/services/bluetooth_config/DEPS
@@ -1,8 +1,9 @@
include_rules = [
"+ash/constants",
"+components/device_event_log",
"+components/user_manager",
"+components/session_manager/core",
"+components/sync_preferences/testing_pref_service_syncable.h",
"+components/user_manager",
"+device/bluetooth",
"+mojo/public",
]
Expand Up @@ -11,7 +11,11 @@ namespace chromeos {
namespace bluetooth_config {

// Controls the state of the Bluetooth adapter and serves as the source of truth
// for the adapter's current state.
// for the adapter's current state. This class modifies the Bluetooth adapter
// directly and should only be used by classes that do not wish to persist the
// adapter state to prefs. For classes that do wish to persist the adapter state
// to prefs, such as those processing incoming user requests,
// BluetoothPowerController should be used instead.
class AdapterStateController {
public:
class Observer : public base::CheckedObserver {
Expand All @@ -30,6 +34,9 @@ class AdapterStateController {

// Turns Bluetooth on or off. If Bluetooth is unavailable or already in the
// desired state, this function is a no-op.
// This does not save to |enabled| to prefs. If |enabled| is wished to be
// saved to prefs, BluetoothPowerController::SetBluetoothEnabledState() should
// be used instead.
virtual void SetBluetoothEnabledState(bool enabled) = 0;

void AddObserver(Observer* observer);
Expand Down
Expand Up @@ -103,6 +103,7 @@ void AdapterStateControllerImpl::AttemptSetEnabled(bool enabled) {
weak_ptr_factory_.GetWeakPtr(), enabled),
base::BindOnce(&AdapterStateControllerImpl::OnSetPoweredError,
weak_ptr_factory_.GetWeakPtr(), enabled));
// TODO(gordonseto): Add power metric here.

// State has changed to kEnabling or kDisabling; notify observers.
NotifyAdapterStateChanged();
Expand All @@ -118,6 +119,7 @@ void AdapterStateControllerImpl::OnSetPoweredSuccess(bool enabled) {
void AdapterStateControllerImpl::OnSetPoweredError(bool enabled) {
BLUETOOTH_LOG(ERROR) << "Error attempting to "
<< (enabled ? "enable" : "disable") << " Bluetooth";
// TODO(gordonseto): Add power metric here.
in_progress_state_change_ = PowerStateChange::kNoChange;

// State is no longer kEnabling or kDisabling; notify observers.
Expand Down
36 changes: 36 additions & 0 deletions chromeos/services/bluetooth_config/bluetooth_power_controller.h
@@ -0,0 +1,36 @@
// Copyright 2021 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 CHROMEOS_SERVICES_BLUETOOTH_CONFIG_BLUETOOTH_POWER_CONTROLLER_H_
#define CHROMEOS_SERVICES_BLUETOOTH_CONFIG_BLUETOOTH_POWER_CONTROLLER_H_

class PrefService;

namespace chromeos {
namespace bluetooth_config {

// Sets the Bluetooth power state and saves the state to prefs. Also initializes
// the Bluetooth power state during system startup and user session startup.
//
// Classes that wish to set the Bluetooth adapter state and save that value to
// prefs should use this class. Classes that do not want to persist the state to
// prefs should use AdapterStateController instead.
class BluetoothPowerController {
public:
virtual ~BluetoothPowerController() = default;

// Changes the Bluetooth power setting to |enabled|, persisting |enabled| to
// user prefs if a user is logged in. If no user is logged in, the pref is
// persisted to local state.
virtual void SetBluetoothEnabledState(bool enabled) = 0;

// Sets the PrefServices used to save and retrieve the Bluetooth power state.
virtual void SetPrefs(PrefService* primary_profile_prefs_,
PrefService* local_state) = 0;
};

} // namespace bluetooth_config
} // namespace chromeos

#endif // CHROMEOS_SERVICES_BLUETOOTH_CONFIG_BLUETOOTH_POWER_CONTROLLER_H_
184 changes: 184 additions & 0 deletions chromeos/services/bluetooth_config/bluetooth_power_controller_impl.cc
@@ -0,0 +1,184 @@
// Copyright 2021 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 "chromeos/services/bluetooth_config/bluetooth_power_controller_impl.h"

#include "ash/constants/ash_pref_names.h"
#include "chromeos/services/bluetooth_config/public/cpp/cros_bluetooth_config_util.h"
#include "components/device_event_log/device_event_log.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user_manager.h"

namespace chromeos {
namespace bluetooth_config {
namespace {

// Decides whether to apply Bluetooth setting based on user type.
// Returns true if the user type represents a human individual, currently this
// includes: regular, child, supervised, or active directory. The other types
// do not represent human account so those account should follow system-wide
// Bluetooth setting instead.
bool ShouldApplyUserBluetoothSetting(user_manager::UserType user_type) {
return user_type == user_manager::USER_TYPE_REGULAR ||
user_type == user_manager::USER_TYPE_CHILD ||
user_type == user_manager::USER_TYPE_ACTIVE_DIRECTORY;
}

} // namespace

// static
void BluetoothPowerControllerImpl::RegisterLocalStatePrefs(
PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(prefs::kSystemBluetoothAdapterEnabled,
/*default_value=*/false);
}

// static
void BluetoothPowerControllerImpl::RegisterProfilePrefs(
PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(prefs::kUserBluetoothAdapterEnabled,
/*default_value=*/false);
}

BluetoothPowerControllerImpl::BluetoothPowerControllerImpl(
AdapterStateController* adapter_state_controller)
: adapter_state_controller_(adapter_state_controller) {}

BluetoothPowerControllerImpl::~BluetoothPowerControllerImpl() = default;

void BluetoothPowerControllerImpl::SetBluetoothEnabledState(bool enabled) {
if (primary_profile_prefs_) {
BLUETOOTH_LOG(EVENT) << "Saving Bluetooth power state of " << enabled
<< " to user prefs.";

primary_profile_prefs_->SetBoolean(ash::prefs::kUserBluetoothAdapterEnabled,
enabled);
} else if (local_state_) {
BLUETOOTH_LOG(EVENT) << "Saving Bluetooth power state of " << enabled
<< " to local state.";

local_state_->SetBoolean(ash::prefs::kSystemBluetoothAdapterEnabled,
enabled);
} else {
BLUETOOTH_LOG(ERROR)
<< "SetBluetoothEnabledState() called before preferences were set";
}
SetAdapterState(enabled);
}

void BluetoothPowerControllerImpl::SetPrefs(PrefService* primary_profile_prefs,
PrefService* local_state) {
InitLocalStatePrefService(local_state);
InitPrimaryUserPrefService(primary_profile_prefs);
}

void BluetoothPowerControllerImpl::InitLocalStatePrefService(
PrefService* local_state) {
// Return early if |local_state_| has already been initialized or
// |local_state| is invalid.
if (local_state_ || !local_state)
return;

local_state_ = local_state;

// Apply the local state pref if no user has logged in (still in login
// screen).
if (!user_manager::UserManager::Get()->GetActiveUser())
ApplyBluetoothLocalStatePref();
}

void BluetoothPowerControllerImpl::ApplyBluetoothLocalStatePref() {
if (local_state_->FindPreference(prefs::kSystemBluetoothAdapterEnabled)
->IsDefaultValue()) {
// If the device has not had the local state Bluetooth pref, set the pref
// according to whatever the current Bluetooth power is.
BLUETOOTH_LOG(EVENT) << "Saving current power state of "
<< adapter_state_controller_->GetAdapterState()
<< " to local state.";
SaveCurrentPowerStateToPrefs(local_state_,
prefs::kSystemBluetoothAdapterEnabled);
return;
}

bool enabled =
local_state_->GetBoolean(prefs::kSystemBluetoothAdapterEnabled);
BLUETOOTH_LOG(EVENT) << "Applying local state pref Bluetooth power: "
<< enabled;
SetAdapterState(enabled);
}

void BluetoothPowerControllerImpl::InitPrimaryUserPrefService(
PrefService* primary_profile_prefs) {
primary_profile_prefs_ = primary_profile_prefs;
if (!primary_profile_prefs_) {
return;
}

DCHECK_EQ(user_manager::UserManager::Get()->GetActiveUser(),
user_manager::UserManager::Get()->GetPrimaryUser());

if (!has_attempted_apply_primary_user_pref_) {
ApplyBluetoothPrimaryUserPref();
has_attempted_apply_primary_user_pref_ = true;
}
}

void BluetoothPowerControllerImpl::ApplyBluetoothPrimaryUserPref() {
absl::optional<user_manager::UserType> user_type =
user_manager::UserManager::Get()->GetActiveUser()->GetType();

// Apply the Bluetooth pref only for regular users (i.e. users representing
// a human individual). We don't want to apply Bluetooth pref for other users
// e.g. kiosk, guest etc. For non-human users, Bluetooth power should be left
// to the current power state.
if (!user_type || !ShouldApplyUserBluetoothSetting(*user_type)) {
BLUETOOTH_LOG(EVENT) << "Not applying primary user pref because user has "
"no type or is not a regular user.";
return;
}

if (!primary_profile_prefs_
->FindPreference(prefs::kUserBluetoothAdapterEnabled)
->IsDefaultValue()) {
bool enabled =
primary_profile_prefs_->GetBoolean(prefs::kUserBluetoothAdapterEnabled);
BLUETOOTH_LOG(EVENT) << "Applying primary user pref Bluetooth power: "
<< enabled;
SetAdapterState(enabled);
return;
}

// If the user has not had the Bluetooth pref yet, set the user pref
// according to whatever the current Bluetooth power is, except for
// new users (first login on the device) always set the new pref to true.
if (user_manager::UserManager::Get()->IsCurrentUserNew()) {
BLUETOOTH_LOG(EVENT) << "Setting Bluetooth power to enabled for new user.";
SetBluetoothEnabledState(true);
return;
}

BLUETOOTH_LOG(EVENT) << "Saving current power state of "
<< adapter_state_controller_->GetAdapterState()
<< " to user prefs.";
SaveCurrentPowerStateToPrefs(primary_profile_prefs_,
prefs::kUserBluetoothAdapterEnabled);
}

void BluetoothPowerControllerImpl::SetAdapterState(bool enabled) {
BLUETOOTH_LOG(EVENT) << "Setting adapter state to "
<< (enabled ? "enabled " : "disabled");
adapter_state_controller_->SetBluetoothEnabledState(enabled);
}

void BluetoothPowerControllerImpl::SaveCurrentPowerStateToPrefs(
PrefService* prefs,
const char* pref_name) {
prefs->SetBoolean(pref_name,
IsBluetoothEnabledOrEnabling(
adapter_state_controller_->GetAdapterState()));
}

} // namespace bluetooth_config
} // namespace chromeos
@@ -0,0 +1,68 @@
// Copyright 2021 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 CHROMEOS_SERVICES_BLUETOOTH_CONFIG_BLUETOOTH_POWER_CONTROLLER_IMPL_H_
#define CHROMEOS_SERVICES_BLUETOOTH_CONFIG_BLUETOOTH_POWER_CONTROLLER_IMPL_H_

#include "chromeos/services/bluetooth_config/bluetooth_power_controller.h"

#include "chromeos/services/bluetooth_config/adapter_state_controller.h"
#include "components/user_manager/user_type.h"

class PrefRegistrySimple;

namespace chromeos {
namespace bluetooth_config {

// Concrete BluetoothPowerController implementation that uses prefs to save and
// apply the Bluetooth power state.
class BluetoothPowerControllerImpl : public BluetoothPowerController {
public:
static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
static void RegisterProfilePrefs(PrefRegistrySimple* registry);

explicit BluetoothPowerControllerImpl(
AdapterStateController* adapter_state_controller);
~BluetoothPowerControllerImpl() override;

private:
// BluetoothPowerController:
void SetBluetoothEnabledState(bool enabled) override;
void SetPrefs(PrefService* primary_profile_prefs,
PrefService* local_state) override;

void InitLocalStatePrefService(PrefService* local_state);

// At login screen startup, applies the local state Bluetooth power setting
// or sets the default pref value if the device doesn't have the setting yet.
void ApplyBluetoothLocalStatePref();

void InitPrimaryUserPrefService(PrefService* primary_profile_prefs);

// At primary user session startup, applies the user's Bluetooth power setting
// or sets the default pref value if the user doesn't have the setting yet.
void ApplyBluetoothPrimaryUserPref();

// Sets the Bluetooth power state.
void SetAdapterState(bool enabled);

// Saves to prefs the current Bluetooth power state.
void SaveCurrentPowerStateToPrefs(PrefService* prefs, const char* pref_name);

// Remembers whether we have ever attempted to apply the primary user's
// Bluetooth setting. If this variable is true, we will ignore any active
// user change event since we know that the primary user's Bluetooth setting
// has been attempted to be applied.
bool has_attempted_apply_primary_user_pref_ = false;

PrefService* primary_profile_prefs_ = nullptr;
PrefService* local_state_ = nullptr;

AdapterStateController* adapter_state_controller_;
};

} // namespace bluetooth_config
} // namespace chromeos

#endif // CHROMEOS_SERVICES_BLUETOOTH_CONFIG_BLUETOOTH_POWER_CONTROLLER_IMPL_H_

0 comments on commit 293e498

Please sign in to comment.