chromium/chrome/browser/ash/policy/handlers/bluetooth_policy_handler.cc

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

#include "chrome/browser/ash/policy/handlers/bluetooth_policy_handler.h"

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ref_counted.h"
#include "base/syslog_logging.h"
#include "base/values.h"
#include "chromeos/ash/components/settings/cros_settings_names.h"
#include "chromeos/ash/components/settings/cros_settings_provider.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/public/cpp/bluetooth_uuid.h"

namespace policy {

BluetoothPolicyHandler::BluetoothPolicyHandler(ash::CrosSettings* cros_settings)
    : cros_settings_(cros_settings) {
  allow_bluetooth_subscription_ = cros_settings_->AddSettingsObserver(
      ash::kAllowBluetooth,
      base::BindRepeating(&BluetoothPolicyHandler::OnBluetoothPolicyChanged,
                          weak_factory_.GetWeakPtr()));

  allowed_services_subscription_ = cros_settings_->AddSettingsObserver(
      ash::kDeviceAllowedBluetoothServices,
      base::BindRepeating(&BluetoothPolicyHandler::OnBluetoothPolicyChanged,
                          weak_factory_.GetWeakPtr()));

  device::BluetoothAdapterFactory::Get()->GetAdapter(
      base::BindOnce(&BluetoothPolicyHandler::InitializeOnAdapterReady,
                     weak_factory_.GetWeakPtr()));

  // Fire it once so we're sure we get an invocation on startup.
  OnBluetoothPolicyChanged();
}

BluetoothPolicyHandler::~BluetoothPolicyHandler() {
  if (adapter_) {
    adapter_->RemoveObserver(this);
  }
}

void BluetoothPolicyHandler::AdapterPresentChanged(
    device::BluetoothAdapter* adapter,
    bool present) {
  SetBluetoothPolicy();
}

void BluetoothPolicyHandler::AdapterPoweredChanged(
    device::BluetoothAdapter* adapter,
    bool powered) {
  SetBluetoothPolicy();
}

void BluetoothPolicyHandler::InitializeOnAdapterReady(
    scoped_refptr<device::BluetoothAdapter> adapter) {
  adapter_ = std::move(adapter);
  adapter_->AddObserver(this);

  SetBluetoothPolicy();
}

void BluetoothPolicyHandler::OnBluetoothPolicyChanged() {
  ash::CrosSettingsProvider::TrustedStatus status =
      cros_settings_->PrepareTrustedValues(
          base::BindOnce(&BluetoothPolicyHandler::OnBluetoothPolicyChanged,
                         weak_factory_.GetWeakPtr()));
  if (status != ash::CrosSettingsProvider::TRUSTED)
    return;

  new_policy_update_pending_ = true;
  SetBluetoothPolicy();
}

void SetServiceAllowListSuccess() {
  SYSLOG(INFO) << "Set ServiceAllowList Success";
}

void SetServiceAllowListFailed() {
  SYSLOG(ERROR) << "Set ServiceAllowList Failed";
}

void BluetoothPolicyHandler::SetBluetoothPolicy() {
  // Get the updated policy.
  bool allow_bluetooth = true;
  const base::Value::List* allowed_services_list = nullptr;
  std::vector<device::BluetoothUUID> allowed_services;

  if (!adapter_ || !adapter_->IsInitialized() || !adapter_->IsPresent() ||
      !adapter_->IsPowered() || !new_policy_update_pending_) {
    return;
  }

  cros_settings_->GetBoolean(ash::kAllowBluetooth, &allow_bluetooth);
  if (!allow_bluetooth) {
    adapter_->SetPowered(false, base::DoNothing(), base::DoNothing());
    adapter_->Shutdown();
  }

  // Pass empty list to SetServiceAllowList means no restriction for users,
  // which is the same behavior as leaving DeviceAllowedBluetoothService unset.
  // Therefore, we don't need to handle the case when device management server
  // returns an empty list even if the policy did not set.
  if (cros_settings_->GetList(ash::kDeviceAllowedBluetoothServices,
                              &allowed_services_list)) {
    for (const auto& list_value : *allowed_services_list) {
      if (!list_value.is_string())
        continue;

      const std::string& uuid_str = list_value.GetString();
      device::BluetoothUUID uuid(uuid_str);
      if (!uuid.IsValid()) {
        SYSLOG(WARNING) << "Failed to parse '" << uuid_str
                        << "' into UUID struct";
        continue;
      }

      allowed_services.push_back(uuid);
    }

    std::ostringstream info_stream;
    info_stream << "Setting " << allowed_services.size()
                << " UUIDs as allowed services;";
    for (size_t i = 0; i < allowed_services.size(); i++) {
      info_stream << " " << i << "th allowed service uuid: "
                  << allowed_services[i].canonical_value() << ";";
    }
    SYSLOG(INFO) << info_stream.str();

    adapter_->SetServiceAllowList(allowed_services,
                                  base::BindOnce(SetServiceAllowListSuccess),
                                  base::BindOnce(SetServiceAllowListFailed));
  }

  // Reset the indicator to false to make sure we don't bother setting the same
  // policy to the daemon, although it is harmless.
  new_policy_update_pending_ = false;
}

}  // namespace policy