blob: bae8fb99a9af778e15ebebd3e83d42c8da3b486d [file] [log] [blame]
// Copyright 2018 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 "device/bluetooth/bluetooth_device_winrt.h"
#include <windows.foundation.h>
#include <utility>
#include "base/bind_helpers.h"
#include "base/logging.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/win/core_winrt_util.h"
#include "base/win/scoped_hstring.h"
#include "device/bluetooth/bluetooth_adapter_winrt.h"
#include "device/bluetooth/bluetooth_gatt_discoverer_winrt.h"
#include "device/bluetooth/bluetooth_remote_gatt_service_winrt.h"
#include "device/bluetooth/event_utils_winrt.h"
namespace device {
namespace {
using ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus;
using ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus_Connected;
using ABI::Windows::Devices::Bluetooth::BluetoothLEDevice;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCommunicationStatus;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCommunicationStatus_Success;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattDeviceServicesResult;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
IGattDeviceServicesResult;
using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice;
using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice3;
using ABI::Windows::Devices::Bluetooth::IBluetoothLEDeviceStatics;
using ABI::Windows::Foundation::IAsyncOperation;
using ABI::Windows::Foundation::IClosable;
using Microsoft::WRL::ComPtr;
void CloseDevice(ComPtr<IBluetoothLEDevice> ble_device) {
if (!ble_device)
return;
ComPtr<IClosable> closable;
HRESULT hr = ble_device.As(&closable);
if (FAILED(hr)) {
VLOG(2) << "As IClosable failed: " << logging::SystemErrorCodeToString(hr);
return;
}
hr = closable->Close();
if (FAILED(hr)) {
VLOG(2) << "IClosable::close() failed: "
<< logging::SystemErrorCodeToString(hr);
}
}
void RemoveConnectionStatusHandler(IBluetoothLEDevice* ble_device,
EventRegistrationToken token) {
HRESULT hr = ble_device->remove_ConnectionStatusChanged(token);
if (FAILED(hr)) {
VLOG(2) << "Removing ConnectionStatus Handler failed: "
<< logging::SystemErrorCodeToString(hr);
}
}
void RemoveGattServicesChangedHandler(IBluetoothLEDevice* ble_device,
EventRegistrationToken token) {
HRESULT hr = ble_device->remove_GattServicesChanged(token);
if (FAILED(hr)) {
VLOG(2) << "Removing Gatt Services Changed Handler failed: "
<< logging::SystemErrorCodeToString(hr);
}
}
} // namespace
BluetoothDeviceWinrt::BluetoothDeviceWinrt(
BluetoothAdapterWinrt* adapter,
uint64_t raw_address,
base::Optional<std::string> local_name)
: BluetoothDevice(adapter),
raw_address_(raw_address),
address_(CanonicalizeAddress(raw_address)),
local_name_(std::move(local_name)),
weak_ptr_factory_(this) {}
BluetoothDeviceWinrt::~BluetoothDeviceWinrt() {
CloseDevice(ble_device_);
if (!connection_changed_token_)
return;
RemoveConnectionStatusHandler(ble_device_.Get(), *connection_changed_token_);
}
uint32_t BluetoothDeviceWinrt::GetBluetoothClass() const {
NOTIMPLEMENTED();
return 0;
}
std::string BluetoothDeviceWinrt::GetAddress() const {
return address_;
}
BluetoothDevice::VendorIDSource BluetoothDeviceWinrt::GetVendorIDSource()
const {
NOTIMPLEMENTED();
return VendorIDSource();
}
uint16_t BluetoothDeviceWinrt::GetVendorID() const {
NOTIMPLEMENTED();
return 0;
}
uint16_t BluetoothDeviceWinrt::GetProductID() const {
NOTIMPLEMENTED();
return 0;
}
uint16_t BluetoothDeviceWinrt::GetDeviceID() const {
NOTIMPLEMENTED();
return 0;
}
uint16_t BluetoothDeviceWinrt::GetAppearance() const {
NOTIMPLEMENTED();
return 0;
}
base::Optional<std::string> BluetoothDeviceWinrt::GetName() const {
if (!ble_device_)
return local_name_;
HSTRING name;
HRESULT hr = ble_device_->get_Name(&name);
if (FAILED(hr)) {
VLOG(2) << "Getting Name failed: " << logging::SystemErrorCodeToString(hr);
return local_name_;
}
return base::win::ScopedHString(name).GetAsUTF8();
}
bool BluetoothDeviceWinrt::IsPaired() const {
NOTIMPLEMENTED();
return false;
}
bool BluetoothDeviceWinrt::IsConnected() const {
return IsGattConnected();
}
bool BluetoothDeviceWinrt::IsGattConnected() const {
if (!ble_device_)
return false;
BluetoothConnectionStatus status;
HRESULT hr = ble_device_->get_ConnectionStatus(&status);
if (FAILED(hr)) {
VLOG(2) << "Getting ConnectionStatus failed: "
<< logging::SystemErrorCodeToString(hr);
return false;
}
return status == BluetoothConnectionStatus_Connected;
}
bool BluetoothDeviceWinrt::IsConnectable() const {
NOTIMPLEMENTED();
return false;
}
bool BluetoothDeviceWinrt::IsConnecting() const {
NOTIMPLEMENTED();
return false;
}
bool BluetoothDeviceWinrt::ExpectingPinCode() const {
NOTIMPLEMENTED();
return false;
}
bool BluetoothDeviceWinrt::ExpectingPasskey() const {
NOTIMPLEMENTED();
return false;
}
bool BluetoothDeviceWinrt::ExpectingConfirmation() const {
NOTIMPLEMENTED();
return false;
}
void BluetoothDeviceWinrt::GetConnectionInfo(
const ConnectionInfoCallback& callback) {
NOTIMPLEMENTED();
}
void BluetoothDeviceWinrt::SetConnectionLatency(
ConnectionLatency connection_latency,
const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
void BluetoothDeviceWinrt::Connect(PairingDelegate* pairing_delegate,
const base::Closure& callback,
const ConnectErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
void BluetoothDeviceWinrt::SetPinCode(const std::string& pincode) {
NOTIMPLEMENTED();
}
void BluetoothDeviceWinrt::SetPasskey(uint32_t passkey) {
NOTIMPLEMENTED();
}
void BluetoothDeviceWinrt::ConfirmPairing() {
NOTIMPLEMENTED();
}
void BluetoothDeviceWinrt::RejectPairing() {
NOTIMPLEMENTED();
}
void BluetoothDeviceWinrt::CancelPairing() {
NOTIMPLEMENTED();
}
void BluetoothDeviceWinrt::Disconnect(const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
void BluetoothDeviceWinrt::Forget(const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
void BluetoothDeviceWinrt::ConnectToService(
const BluetoothUUID& uuid,
const ConnectToServiceCallback& callback,
const ConnectToServiceErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
void BluetoothDeviceWinrt::ConnectToServiceInsecurely(
const device::BluetoothUUID& uuid,
const ConnectToServiceCallback& callback,
const ConnectToServiceErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
// static
std::string BluetoothDeviceWinrt::CanonicalizeAddress(uint64_t address) {
std::string bluetooth_address = BluetoothDevice::CanonicalizeAddress(
base::StringPrintf("%012llX", address));
DCHECK(!bluetooth_address.empty());
return bluetooth_address;
}
void BluetoothDeviceWinrt::CreateGattConnectionImpl() {
ComPtr<IBluetoothLEDeviceStatics> device_statics;
HRESULT hr = GetBluetoothLEDeviceStaticsActivationFactory(&device_statics);
if (FAILED(hr)) {
VLOG(2) << "GetBluetoothLEDeviceStaticsActivationFactory failed: "
<< logging::SystemErrorCodeToString(hr);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&BluetoothDeviceWinrt::DidFailToConnectGatt,
weak_ptr_factory_.GetWeakPtr(),
ConnectErrorCode::ERROR_FAILED));
return;
}
// Note: Even though we might have obtained a BluetoothLEDevice instance in
// the past, we need to request a new instance as the old device might have
// been closed. See also
// https://docs.microsoft.com/en-us/windows/uwp/devices-sensors/gatt-client#connecting-to-the-device
ComPtr<IAsyncOperation<BluetoothLEDevice*>> from_bluetooth_address_op;
hr = device_statics->FromBluetoothAddressAsync(raw_address_,
&from_bluetooth_address_op);
if (FAILED(hr)) {
VLOG(2) << "BluetoothLEDevice::FromBluetoothAddressAsync failed: "
<< logging::SystemErrorCodeToString(hr);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&BluetoothDeviceWinrt::DidFailToConnectGatt,
weak_ptr_factory_.GetWeakPtr(),
ConnectErrorCode::ERROR_FAILED));
return;
}
hr = PostAsyncResults(
std::move(from_bluetooth_address_op),
base::BindOnce(&BluetoothDeviceWinrt::OnFromBluetoothAddress,
weak_ptr_factory_.GetWeakPtr()));
if (FAILED(hr)) {
VLOG(2) << "PostAsyncResults failed: "
<< logging::SystemErrorCodeToString(hr);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&BluetoothDeviceWinrt::DidFailToConnectGatt,
weak_ptr_factory_.GetWeakPtr(),
ConnectErrorCode::ERROR_FAILED));
}
}
void BluetoothDeviceWinrt::DisconnectGatt() {
CloseDevice(ble_device_);
}
HRESULT BluetoothDeviceWinrt::GetBluetoothLEDeviceStaticsActivationFactory(
IBluetoothLEDeviceStatics** statics) const {
return base::win::GetActivationFactory<
IBluetoothLEDeviceStatics,
RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice>(statics);
}
void BluetoothDeviceWinrt::OnFromBluetoothAddress(
ComPtr<IBluetoothLEDevice> ble_device) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!ble_device) {
VLOG(2) << "Getting Device From Bluetooth Address failed.";
DidFailToConnectGatt(ConnectErrorCode::ERROR_FAILED);
return;
}
if (connection_changed_token_) {
// As we are about to replace |ble_device_| with |ble_device| we will also
// unregister the existing event handler and add a new one to the new
// device.
RemoveConnectionStatusHandler(ble_device_.Get(),
*connection_changed_token_);
}
ble_device_ = std::move(ble_device);
connection_changed_token_ = AddTypedEventHandler(
ble_device_.Get(), &IBluetoothLEDevice::add_ConnectionStatusChanged,
base::BindRepeating(&BluetoothDeviceWinrt::OnConnectionStatusChanged,
weak_ptr_factory_.GetWeakPtr()));
if (gatt_services_changed_token_) {
RemoveGattServicesChangedHandler(ble_device_.Get(),
*gatt_services_changed_token_);
}
gatt_services_changed_token_ = AddTypedEventHandler(
ble_device_.Get(), &IBluetoothLEDevice::add_GattServicesChanged,
base::BindRepeating(&BluetoothDeviceWinrt::OnGattServicesChanged,
weak_ptr_factory_.GetWeakPtr()));
gatt_discoverer_ =
std::make_unique<BluetoothGattDiscovererWinrt>(ble_device_);
// Initiating the GATT discovery will result in a GATT connection attempt as
// well and triggers OnConnectionStatusChanged on success.
gatt_discoverer_->StartGattDiscovery(
base::BindOnce(&BluetoothDeviceWinrt::OnGattDiscoveryComplete,
weak_ptr_factory_.GetWeakPtr()));
}
void BluetoothDeviceWinrt::OnConnectionStatusChanged(
IBluetoothLEDevice* ble_device,
IInspectable* object) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (IsGattConnected()) {
DidConnectGatt();
} else {
gatt_discoverer_.reset();
gatt_services_.clear();
device_uuids_.ClearServiceUUIDs();
SetGattServicesDiscoveryComplete(false);
DidDisconnectGatt();
}
}
void BluetoothDeviceWinrt::OnGattServicesChanged(IBluetoothLEDevice* ble_device,
IInspectable* object) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
device_uuids_.ClearServiceUUIDs();
SetGattServicesDiscoveryComplete(false);
adapter_->NotifyDeviceChanged(this);
if (IsGattConnected()) {
// In order to stop a potential ongoing GATT discovery, the GattDiscoverer
// is reset and a new discovery is initiated.
gatt_discoverer_ =
std::make_unique<BluetoothGattDiscovererWinrt>(ble_device);
gatt_discoverer_->StartGattDiscovery(
base::BindOnce(&BluetoothDeviceWinrt::OnGattDiscoveryComplete,
weak_ptr_factory_.GetWeakPtr()));
}
}
void BluetoothDeviceWinrt::OnGattDiscoveryComplete(bool success) {
if (!success) {
if (!IsGattConnected())
DidFailToConnectGatt(ConnectErrorCode::ERROR_FAILED);
return;
}
for (const auto& gatt_service : gatt_discoverer_->GetGattServices()) {
auto gatt_service_winrt =
BluetoothRemoteGattServiceWinrt::Create(this, gatt_service);
if (!gatt_service_winrt)
continue;
const auto& service = *gatt_service_winrt;
gatt_services_.emplace(service.GetIdentifier(),
std::move(gatt_service_winrt));
}
device_uuids_.ReplaceServiceUUIDs(gatt_services_);
SetGattServicesDiscoveryComplete(true);
adapter_->NotifyGattServicesDiscovered(this);
adapter_->NotifyDeviceChanged(this);
gatt_discoverer_.reset();
}
} // namespace device