blob: 7752c53275e6e407bf858155ef589ed7c9750d88 [file] [log] [blame]
// Copyright 2017 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/system/bluetooth/bluetooth_power_controller.h"
#include <memory>
#include "ash/public/cpp/ash_pref_names.h"
#include "ash/session/session_controller.h"
#include "ash/shell.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/device_event_log/device_event_log.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
namespace ash {
// The delay between bluez started and bluez power initialized. This number is
// determined empirically: most of the time bluez takes less than 200 ms to
// initialize power, so taking 1000 ms has enough time buffer for worst cases.
const int kBluetoothInitializationDelay = 1000;
BluetoothPowerController::BluetoothPowerController() : weak_ptr_factory_(this) {
device::BluetoothAdapterFactory::GetAdapter(
base::BindOnce(&BluetoothPowerController::InitializeOnAdapterReady,
weak_ptr_factory_.GetWeakPtr()));
Shell::Get()->AddShellObserver(this);
Shell::Get()->session_controller()->AddObserver(this);
}
BluetoothPowerController::~BluetoothPowerController() {
if (bluetooth_adapter_)
bluetooth_adapter_->RemoveObserver(this);
Shell::Get()->RemoveShellObserver(this);
Shell::Get()->session_controller()->RemoveObserver(this);
}
void BluetoothPowerController::SetBluetoothEnabled(bool enabled) {
if (active_user_pref_service_) {
active_user_pref_service_->SetBoolean(prefs::kUserBluetoothAdapterEnabled,
enabled);
} else if (local_state_pref_service_) {
local_state_pref_service_->SetBoolean(prefs::kSystemBluetoothAdapterEnabled,
enabled);
} else {
DLOG(ERROR)
<< "active user and local state pref service cannot both be null";
}
}
// static
void BluetoothPowerController::RegisterLocalStatePrefs(
PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(prefs::kSystemBluetoothAdapterEnabled, false);
}
// static
void BluetoothPowerController::RegisterProfilePrefs(
PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(prefs::kUserBluetoothAdapterEnabled, false,
PrefRegistry::PUBLIC);
}
void BluetoothPowerController::StartWatchingActiveUserPrefsChanges() {
DCHECK(active_user_pref_service_);
DCHECK(Shell::Get()->session_controller()->IsUserPrimary());
active_user_pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
active_user_pref_change_registrar_->Init(active_user_pref_service_);
active_user_pref_change_registrar_->Add(
prefs::kUserBluetoothAdapterEnabled,
base::BindRepeating(
&BluetoothPowerController::OnBluetoothPowerActiveUserPrefChanged,
base::Unretained(this)));
}
void BluetoothPowerController::StartWatchingLocalStatePrefsChanges() {
DCHECK(local_state_pref_service_);
local_state_pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
local_state_pref_change_registrar_->Init(local_state_pref_service_);
local_state_pref_change_registrar_->Add(
prefs::kSystemBluetoothAdapterEnabled,
base::Bind(
&BluetoothPowerController::OnBluetoothPowerLocalStatePrefChanged,
base::Unretained(this)));
}
void BluetoothPowerController::StopWatchingActiveUserPrefsChanges() {
active_user_pref_change_registrar_.reset();
}
void BluetoothPowerController::OnBluetoothPowerActiveUserPrefChanged() {
DCHECK(active_user_pref_service_);
BLUETOOTH_LOG(EVENT) << "Active user bluetooth power pref changed";
SetBluetoothPower(active_user_pref_service_->GetBoolean(
prefs::kUserBluetoothAdapterEnabled));
}
void BluetoothPowerController::OnBluetoothPowerLocalStatePrefChanged() {
DCHECK(local_state_pref_service_);
BLUETOOTH_LOG(EVENT) << "Local state bluetooth power pref changed";
SetBluetoothPower(local_state_pref_service_->GetBoolean(
prefs::kSystemBluetoothAdapterEnabled));
}
void BluetoothPowerController::SetPrimaryUserBluetoothPowerSetting(
bool enabled) {
// This method should only be called when the primary user is the active user.
CHECK(Shell::Get()->session_controller()->IsUserPrimary());
active_user_pref_service_->SetBoolean(prefs::kUserBluetoothAdapterEnabled,
enabled);
}
void BluetoothPowerController::InitializeOnAdapterReady(
scoped_refptr<device::BluetoothAdapter> adapter) {
bluetooth_adapter_ = std::move(adapter);
bluetooth_adapter_->AddObserver(this);
bool adapter_present = bluetooth_adapter_->IsPresent();
BLUETOOTH_LOG(EVENT) << "Bluetooth adapter ready, IsPresent = "
<< adapter_present;
if (adapter_present)
TriggerRunPendingBluetoothTasks();
}
void BluetoothPowerController::OnActiveUserPrefServiceChanged(
PrefService* pref_service) {
active_user_pref_service_ = pref_service;
// Only listen to primary user's pref changes since non-primary users
// are not able to change bluetooth pref.
if (!Shell::Get()->session_controller()->IsUserPrimary()) {
StopWatchingActiveUserPrefsChanges();
return;
}
StartWatchingActiveUserPrefsChanges();
// 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 (!is_primary_user_bluetooth_applied_) {
ApplyBluetoothPrimaryUserPref();
is_primary_user_bluetooth_applied_ = true;
}
}
void BluetoothPowerController::OnLocalStatePrefServiceInitialized(
PrefService* pref_service) {
// AppLaunchTest.TestQuickLaunch fails under target=linux due to
// pref_service being nullptr.
if (!pref_service)
return;
local_state_pref_service_ = pref_service;
StartWatchingLocalStatePrefsChanges();
if (!Shell::Get()->session_controller()->IsActiveUserSessionStarted()) {
// Apply the local state pref only if no user has logged in (still in login
// screen).
ApplyBluetoothLocalStatePref();
}
}
void BluetoothPowerController::AdapterPresentChanged(
device::BluetoothAdapter* adapter,
bool present) {
BLUETOOTH_LOG(EVENT) << "Bluetooth adapter present changed = " << present;
if (present) {
// If adapter->IsPresent() has just changed from false to true, this means
// that bluez has just started but not yet finished power initialization,
// so we should not start any bluetooth tasks until bluez power
// initialization is done. Since there is no signal from bluez when that
// happens, this adds a bit delay before triggering pending bluetooth tasks.
//
// TODO(sonnysasaka): Replace this delay hack with a signal from bluez when
// it has "initialized" signal in the future (http://crbug.com/765390).
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::Bind(&BluetoothPowerController::TriggerRunPendingBluetoothTasks,
weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kBluetoothInitializationDelay));
}
}
void BluetoothPowerController::ApplyBluetoothPrimaryUserPref() {
base::Optional<user_manager::UserType> user_type =
Shell::Get()->session_controller()->GetUserType();
if (!user_type || !ShouldApplyUserBluetoothSetting(*user_type)) {
// Do not apply bluetooth setting if user is not of the allowed types.
return;
}
DCHECK(Shell::Get()->session_controller()->IsUserPrimary());
PrefService* prefs = active_user_pref_service_;
if (!prefs->FindPreference(prefs::kUserBluetoothAdapterEnabled)
->IsDefaultValue()) {
bool enabled = prefs->GetBoolean(prefs::kUserBluetoothAdapterEnabled);
BLUETOOTH_LOG(EVENT) << "Applying primary user pref bluetooth power: "
<< enabled;
SetBluetoothPower(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 (Shell::Get()->session_controller()->IsUserFirstLogin()) {
prefs->SetBoolean(prefs::kUserBluetoothAdapterEnabled, true);
} else {
SavePrefValue(prefs, prefs::kUserBluetoothAdapterEnabled);
}
}
void BluetoothPowerController::ApplyBluetoothLocalStatePref() {
PrefService* prefs = local_state_pref_service_;
if (prefs->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.
SavePrefValue(prefs, prefs::kSystemBluetoothAdapterEnabled);
} else {
bool enabled = prefs->GetBoolean(prefs::kSystemBluetoothAdapterEnabled);
BLUETOOTH_LOG(EVENT) << "Applying local state pref bluetooth power: "
<< enabled;
SetBluetoothPower(enabled);
}
}
void BluetoothPowerController::SetBluetoothPower(bool enabled) {
if (pending_bluetooth_power_target_.has_value()) {
// There is already a pending bluetooth power change request, so don't
// enqueue a new SetPowered operation but rather change the target power.
pending_bluetooth_power_target_ = enabled;
return;
}
pending_bluetooth_power_target_ = enabled;
RunBluetoothTaskWhenAdapterReady(
base::BindOnce(&BluetoothPowerController::SetBluetoothPowerOnAdapterReady,
weak_ptr_factory_.GetWeakPtr()));
}
void BluetoothPowerController::SetBluetoothPowerOnAdapterReady() {
DCHECK(pending_bluetooth_power_target_.has_value());
bool enabled = pending_bluetooth_power_target_.value();
pending_bluetooth_power_target_.reset();
// Always run the next pending task after SetPowered completes regardless
// the error.
bluetooth_adapter_->SetPowered(
enabled,
base::Bind(&BluetoothPowerController::RunNextPendingBluetoothTask,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothPowerController::RunNextPendingBluetoothTask,
weak_ptr_factory_.GetWeakPtr()));
}
void BluetoothPowerController::RunBluetoothTaskWhenAdapterReady(
BluetoothTask task) {
pending_bluetooth_tasks_.push(std::move(task));
TriggerRunPendingBluetoothTasks();
}
void BluetoothPowerController::TriggerRunPendingBluetoothTasks() {
if (pending_tasks_busy_)
return;
pending_tasks_busy_ = true;
RunNextPendingBluetoothTask();
}
void BluetoothPowerController::RunNextPendingBluetoothTask() {
if (!bluetooth_adapter_ || !bluetooth_adapter_->IsPresent() ||
pending_bluetooth_tasks_.empty()) {
// Stop running pending tasks if either adapter becomes not present or
// all the pending tasks have been run.
pending_tasks_busy_ = false;
return;
}
BluetoothTask task = std::move(pending_bluetooth_tasks_.front());
pending_bluetooth_tasks_.pop();
std::move(task).Run();
}
void BluetoothPowerController::SavePrefValue(PrefService* prefs,
const char* pref_name) {
RunBluetoothTaskWhenAdapterReady(
base::BindOnce(&BluetoothPowerController::SavePrefValueOnAdapterReady,
weak_ptr_factory_.GetWeakPtr(), prefs, pref_name));
}
void BluetoothPowerController::SavePrefValueOnAdapterReady(
PrefService* prefs,
const char* pref_name) {
prefs->SetBoolean(pref_name, bluetooth_adapter_->IsPowered());
RunNextPendingBluetoothTask();
}
bool BluetoothPowerController::ShouldApplyUserBluetoothSetting(
user_manager::UserType user_type) const {
return user_type == user_manager::USER_TYPE_REGULAR ||
user_type == user_manager::USER_TYPE_CHILD ||
user_type == user_manager::USER_TYPE_SUPERVISED ||
user_type == user_manager::USER_TYPE_ACTIVE_DIRECTORY;
}
} // namespace ash