chromium/chromeos/ash/services/bluetooth_config/bluetooth_power_controller_impl.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/services/bluetooth_config/bluetooth_power_controller_impl.h"

#include "ash/constants/ash_pref_names.h"
#include "chromeos/ash/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 ash::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::UserType::kRegular ||
         user_type == user_manager::UserType::kChild;
}

}  // 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) {
  adapter_state_controller_observation_.Observe(
      adapter_state_controller_.get());
}

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(prefs::kUserBluetoothAdapterEnabled,
                                       enabled);
  } else if (local_state_) {
    BLUETOOTH_LOG(EVENT) << "Saving Bluetooth power state of " << enabled
                         << " to local state.";

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

void BluetoothPowerControllerImpl::SetBluetoothEnabledWithoutPersistence() {
  BLUETOOTH_LOG(EVENT) << "Enabling adapter without persistence...";
  SetAdapterState(true);
}

void BluetoothPowerControllerImpl::SetBluetoothHidDetectionInactive(
    bool is_using_bluetooth) {
  if (is_using_bluetooth) {
    BLUETOOTH_LOG(EVENT) << "HID detection finished and Bluetooth is being "
                         << "used. Ensuring Bluetooth is enabled and the "
                         << "enabled state persisted.";
    SetBluetoothEnabledState(true);
    return;
  }

  DCHECK(local_state_)
      << "HID detection finished but unable to restore persisted Bluetooth "
         "state because local_state_ is null.";
  DCHECK(!user_manager::UserManager::Get()->GetActiveUser());

  BLUETOOTH_LOG(EVENT)
      << "HID detection finished, restoring persisted Bluetooth state.";
  ApplyBluetoothLocalStatePref();
}

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

void BluetoothPowerControllerImpl::OnAdapterStateChanged() {
  if (!pending_adapter_enabled_state_.has_value())
    return;

  if (adapter_state_controller_->GetAdapterState() ==
      mojom::BluetoothSystemState::kUnavailable) {
    return;
  }

  // Adapter is now available after being unavailable. Set adapter state to
  // |pending_adapter_enabled_state_|.
  bool enabled = pending_adapter_enabled_state_.value();
  BLUETOOTH_LOG(EVENT) << "Adapter is now available after being unavailable, "
                       << "setting adapter state to " << enabled;

  pending_adapter_enabled_state_.reset();

  SetAdapterState(enabled);
}

void BluetoothPowerControllerImpl::InitLocalStatePrefService(
    PrefService* local_state) {
  BLUETOOTH_LOG(EVENT) << "Initializing local state pref service";

  // Return early if |local_state_| has already been initialized or
  // |local_state| is invalid.
  if (local_state_) {
    BLUETOOTH_LOG(EVENT) << "Local state has already be initialized";
    return;
  }

  if (!local_state) {
    BLUETOOTH_LOG(EVENT) << "local_state is null, not initializing";
    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, this is a
    // fresh install. On fresh installs, the Bluetooth adapter defaults to
    // powered on. Save this state to prefs.
    BLUETOOTH_LOG(EVENT) << "No local state pref has been set, saving"
                         << "Bluetooth power state of enabled to local state";
    local_state_->SetBoolean(prefs::kSystemBluetoothAdapterEnabled, true);
    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) {
  BLUETOOTH_LOG(EVENT) << "Initializing primary user pref service";

  primary_profile_prefs_ = primary_profile_prefs;
  if (!primary_profile_prefs_) {
    BLUETOOTH_LOG(EVENT) << "primary_profile_prefs_ is null, not initializing";
    return;
  }

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

  if (!has_attempted_apply_primary_user_pref_) {
    BLUETOOTH_LOG(EVENT)
        << "Primary user pref has not been attempted to be applied, applying";
    ApplyBluetoothPrimaryUserPref();
    has_attempted_apply_primary_user_pref_ = true;
  }
}

void BluetoothPowerControllerImpl::ApplyBluetoothPrimaryUserPref() {
  std::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");

  // On device startup, the local prefs may attempted to be applied before the
  // adapter is available. Cache the value so it can be set once the adapter
  // is available in OnAdapterStateChanged().
  if (adapter_state_controller_->GetAdapterState() ==
      mojom::BluetoothSystemState::kUnavailable) {
    BLUETOOTH_LOG(EVENT) << "Adapter is currently unavailable, setting "
                         << "pending_adapter_enabled_state_ to " << enabled;
    pending_adapter_enabled_state_ = enabled;
    return;
  }

  adapter_state_controller_->SetBluetoothEnabledState(enabled);
}

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

}  // namespace ash::bluetooth_config