blob: 985009a7846012bbcc7376df46876a3d7ed33e2b [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/tray_bluetooth_helper_legacy.h"
#include <string>
#include <utility>
#include "ash/shell.h"
#include "ash/system/bluetooth/bluetooth_power_controller.h"
#include "ash/system/model/system_tray_model.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/metrics/user_metrics.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_discovery_session.h"
#include "device/bluetooth/chromeos/bluetooth_utils.h"
#include "services/device/public/cpp/bluetooth/bluetooth_utils.h"
using device::mojom::BluetoothSystem;
using device::mojom::BluetoothDeviceInfo;
using device::mojom::BluetoothDeviceInfoPtr;
namespace ash {
namespace {
// System tray shows a limited number of bluetooth devices.
const int kMaximumDevicesShown = 50;
void BluetoothSetDiscoveringError() {
LOG(ERROR) << "BluetoothSetDiscovering failed.";
}
void BluetoothDeviceConnectError(
device::BluetoothDevice::ConnectErrorCode error_code) {}
std::string BluetoothAddressToStr(const BluetoothAddress& address) {
static constexpr char kAddressFormat[] =
"%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX";
return base::StringPrintf(kAddressFormat, address[0], address[1], address[2],
address[3], address[4], address[5]);
}
// Converts a MAC Address string e.g. "00:11:22:33:44:55" into an
// BluetoothAddress e.g. {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}.
BluetoothAddress AddressStrToBluetoothAddress(const std::string& address_str) {
std::string numbers;
bool success = base::ReplaceChars(address_str, ":", "", &numbers);
DCHECK(success);
std::vector<uint8_t> address_vector;
success = base::HexStringToBytes(numbers, &address_vector);
DCHECK(success);
// If the size is not 6, then the underlying Bluetooth API returned an
// incorrect value.
CHECK_EQ(6u, address_vector.size());
BluetoothAddress address_array;
std::copy_n(address_vector.begin(), 6, address_array.begin());
return address_array;
}
BluetoothDeviceInfoPtr GetBluetoothDeviceInfo(device::BluetoothDevice* device) {
BluetoothDeviceInfoPtr info = BluetoothDeviceInfo::New();
info->address = AddressStrToBluetoothAddress(device->GetAddress());
info->name = device->GetName();
info->is_paired = device->IsPaired();
switch (device->GetDeviceType()) {
case device::BluetoothDeviceType::UNKNOWN:
info->device_type = BluetoothDeviceInfo::DeviceType::kUnknown;
break;
case device::BluetoothDeviceType::COMPUTER:
info->device_type = BluetoothDeviceInfo::DeviceType::kComputer;
break;
case device::BluetoothDeviceType::PHONE:
info->device_type = BluetoothDeviceInfo::DeviceType::kPhone;
break;
case device::BluetoothDeviceType::MODEM:
info->device_type = BluetoothDeviceInfo::DeviceType::kModem;
break;
case device::BluetoothDeviceType::AUDIO:
info->device_type = BluetoothDeviceInfo::DeviceType::kAudio;
break;
case device::BluetoothDeviceType::CAR_AUDIO:
info->device_type = BluetoothDeviceInfo::DeviceType::kCarAudio;
break;
case device::BluetoothDeviceType::VIDEO:
info->device_type = BluetoothDeviceInfo::DeviceType::kVideo;
break;
case device::BluetoothDeviceType::PERIPHERAL:
info->device_type = BluetoothDeviceInfo::DeviceType::kPeripheral;
break;
case device::BluetoothDeviceType::JOYSTICK:
info->device_type = BluetoothDeviceInfo::DeviceType::kJoystick;
break;
case device::BluetoothDeviceType::GAMEPAD:
info->device_type = BluetoothDeviceInfo::DeviceType::kGamepad;
break;
case device::BluetoothDeviceType::KEYBOARD:
info->device_type = BluetoothDeviceInfo::DeviceType::kKeyboard;
break;
case device::BluetoothDeviceType::MOUSE:
info->device_type = BluetoothDeviceInfo::DeviceType::kMouse;
break;
case device::BluetoothDeviceType::TABLET:
info->device_type = BluetoothDeviceInfo::DeviceType::kTablet;
break;
case device::BluetoothDeviceType::KEYBOARD_MOUSE_COMBO:
info->device_type = BluetoothDeviceInfo::DeviceType::kKeyboardMouseCombo;
break;
}
if (device->IsConnecting()) {
info->connection_state = BluetoothDeviceInfo::ConnectionState::kConnecting;
} else if (device->IsConnected()) {
info->connection_state = BluetoothDeviceInfo::ConnectionState::kConnected;
} else {
info->connection_state =
BluetoothDeviceInfo::ConnectionState::kNotConnected;
}
return info;
}
} // namespace
TrayBluetoothHelperLegacy::TrayBluetoothHelperLegacy()
: weak_ptr_factory_(this) {}
TrayBluetoothHelperLegacy::~TrayBluetoothHelperLegacy() {
if (adapter_)
adapter_->RemoveObserver(this);
}
void TrayBluetoothHelperLegacy::InitializeOnAdapterReady(
scoped_refptr<device::BluetoothAdapter> adapter) {
adapter_ = adapter;
CHECK(adapter_);
adapter_->AddObserver(this);
last_state_ = GetBluetoothState();
StartOrStopRefreshingDeviceList();
}
void TrayBluetoothHelperLegacy::Initialize() {
device::BluetoothAdapterFactory::GetAdapter(
base::Bind(&TrayBluetoothHelperLegacy::InitializeOnAdapterReady,
weak_ptr_factory_.GetWeakPtr()));
}
void TrayBluetoothHelperLegacy::StartBluetoothDiscovering() {
if (HasBluetoothDiscoverySession()) {
LOG(WARNING) << "Already have active Bluetooth device discovery session.";
return;
}
VLOG(1) << "Requesting new Bluetooth device discovery session.";
should_run_discovery_ = true;
adapter_->StartDiscoverySession(
base::Bind(&TrayBluetoothHelperLegacy::OnStartDiscoverySession,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothSetDiscoveringError));
}
void TrayBluetoothHelperLegacy::StopBluetoothDiscovering() {
should_run_discovery_ = false;
if (!HasBluetoothDiscoverySession()) {
LOG(WARNING) << "No active Bluetooth device discovery session.";
return;
}
VLOG(1) << "Stopping Bluetooth device discovery session.";
discovery_session_->Stop(base::DoNothing(),
base::Bind(&BluetoothSetDiscoveringError));
}
void TrayBluetoothHelperLegacy::ConnectToBluetoothDevice(
const BluetoothAddress& address) {
device::BluetoothDevice* device =
adapter_->GetDevice(BluetoothAddressToStr(address));
if (!device || device->IsConnecting() ||
(device->IsConnected() && device->IsPaired())) {
return;
}
if (device->IsPaired() && !device->IsConnectable())
return;
if (device->IsPaired() || !device->IsPairable()) {
base::RecordAction(
base::UserMetricsAction("StatusArea_Bluetooth_Connect_Known"));
device->Connect(NULL, base::DoNothing(),
base::Bind(&BluetoothDeviceConnectError));
return;
}
// Show pairing dialog for the unpaired device.
Shell::Get()->system_tray_model()->client_ptr()->ShowBluetoothPairingDialog(
device->GetAddress(), device->GetNameForDisplay(), device->IsPaired(),
device->IsConnected());
}
BluetoothSystem::State TrayBluetoothHelperLegacy::GetBluetoothState() {
// Eventually this will use the BluetoothSystem Mojo interface, but for now
// use the current Bluetooth API to get a BluetoothSystem::State.
if (!adapter_)
return BluetoothSystem::State::kUnavailable;
if (!adapter_->IsPresent())
return BluetoothSystem::State::kUnavailable;
if (adapter_->IsPowered())
return BluetoothSystem::State::kPoweredOn;
return BluetoothSystem::State::kPoweredOff;
}
void TrayBluetoothHelperLegacy::SetBluetoothEnabled(bool enabled) {
if (enabled != (GetBluetoothState() == BluetoothSystem::State::kPoweredOn)) {
Shell::Get()->metrics()->RecordUserMetricsAction(
enabled ? UMA_STATUS_AREA_BLUETOOTH_ENABLED
: UMA_STATUS_AREA_BLUETOOTH_DISABLED);
}
Shell::Get()->bluetooth_power_controller()->SetBluetoothEnabled(enabled);
}
bool TrayBluetoothHelperLegacy::HasBluetoothDiscoverySession() {
return discovery_session_ && discovery_session_->IsActive();
}
void TrayBluetoothHelperLegacy::GetBluetoothDevices(
GetBluetoothDevicesCallback callback) const {
BluetoothDeviceList device_list;
device::BluetoothAdapter::DeviceList devices =
device::FilterBluetoothDeviceList(adapter_->GetDevices(),
device::BluetoothFilterType::KNOWN,
kMaximumDevicesShown);
for (device::BluetoothDevice* device : devices)
device_list.push_back(GetBluetoothDeviceInfo(device));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), std::move(device_list)));
}
////////////////////////////////////////////////////////////////////////////////
// BluetoothAdapter::Observer:
void TrayBluetoothHelperLegacy::AdapterPresentChanged(
device::BluetoothAdapter* adapter,
bool present) {
if (last_state_ == GetBluetoothState())
return;
last_state_ = GetBluetoothState();
NotifyBluetoothSystemStateChanged();
StartOrStopRefreshingDeviceList();
}
void TrayBluetoothHelperLegacy::AdapterPoweredChanged(
device::BluetoothAdapter* adapter,
bool powered) {
if (last_state_ == GetBluetoothState())
return;
last_state_ = GetBluetoothState();
NotifyBluetoothSystemStateChanged();
StartOrStopRefreshingDeviceList();
}
void TrayBluetoothHelperLegacy::AdapterDiscoveringChanged(
device::BluetoothAdapter* adapter,
bool discovering) {
NotifyBluetoothScanStateChanged();
}
void TrayBluetoothHelperLegacy::OnStartDiscoverySession(
std::unique_ptr<device::BluetoothDiscoverySession> discovery_session) {
// If the discovery session was returned after a request to stop discovery
// (e.g. the user dismissed the Bluetooth detailed view before the call
// returned), don't claim the discovery session and let it clean up.
if (!should_run_discovery_)
return;
VLOG(1) << "Claiming new Bluetooth device discovery session.";
discovery_session_ = std::move(discovery_session);
NotifyBluetoothScanStateChanged();
}
} // namespace ash