blob: a651ab8b882c40c2d6df2214e44f02131a648a69 [file] [log] [blame]
// Copyright 2019 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/power/gatt_battery_percentage_fetcher.h"
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "components/device_event_log/device_event_log.h"
#include "device/bluetooth/bluetooth_gatt_connection.h"
#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_remote_gatt_service.h"
#include "device/bluetooth/public/cpp/bluetooth_uuid.h"
using device::BluetoothDevice;
using device::BluetoothGattService;
namespace ash {
namespace {
// UUIDs for the standardized Battery Service and Characteristic, defined by the
// Bluetooth GATT Specification.
// https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Services/org.bluetooth.service.battery_service.xml
constexpr char kBatteryServiceUUID[] = "180F";
constexpr char kBatteryLevelUUID[] = "2A19";
GattBatteryPercentageFetcher::Factory* g_test_factory_instance_ = nullptr;
const device::BluetoothUUID& GetBatteryServiceUUID() {
static const device::BluetoothUUID battery_service_uuid(kBatteryServiceUUID);
return battery_service_uuid;
}
const device::BluetoothUUID& GetBatteryLevelUUID() {
static const device::BluetoothUUID battery_level_uuid(kBatteryLevelUUID);
return battery_level_uuid;
}
const char* BluetoothDeviceErrorCodeToString(
BluetoothDevice::ConnectErrorCode error_code) {
switch (error_code) {
case BluetoothDevice::ERROR_AUTH_CANCELED:
return "ERROR_AUTH_CANCELED";
case BluetoothDevice::ERROR_AUTH_FAILED:
return "ERROR_AUTH_FAILED";
case BluetoothDevice::ERROR_AUTH_REJECTED:
return "ERROR_AUTH_REJECTED";
case BluetoothDevice::ERROR_AUTH_TIMEOUT:
return "ERROR_AUTH_TIMEOUT";
case BluetoothDevice::ERROR_FAILED:
return "ERROR_FAILED";
case BluetoothDevice::ERROR_INPROGRESS:
return "ERROR_INPROGRESS";
case BluetoothDevice::ERROR_UNKNOWN:
return "ERROR_UNKNOWN";
case BluetoothDevice::ERROR_UNSUPPORTED_DEVICE:
return "ERROR_UNSUPPORTED_DEVICE";
case BluetoothDevice::NUM_CONNECT_ERROR_CODES:
NOTREACHED();
return "";
}
}
const char* GattErrorCodeToString(
BluetoothGattService::GattErrorCode error_code) {
switch (error_code) {
case BluetoothGattService::GATT_ERROR_UNKNOWN:
return "GATT_ERROR_UNKNOWN";
case BluetoothGattService::GATT_ERROR_FAILED:
return "GATT_ERROR_FAILED";
case BluetoothGattService::GATT_ERROR_IN_PROGRESS:
return "GATT_ERROR_IN_PROGRESS";
case BluetoothGattService::GATT_ERROR_INVALID_LENGTH:
return "GATT_ERROR_INVALID_LENGTH";
case BluetoothGattService::GATT_ERROR_NOT_PERMITTED:
return "GATT_ERROR_NOT_PERMITTED";
case BluetoothGattService::GATT_ERROR_NOT_AUTHORIZED:
return "GATT_ERROR_NOT_AUTHORIZED";
case BluetoothGattService::GATT_ERROR_NOT_PAIRED:
return "GATT_ERROR_NOT_PAIRED";
case BluetoothGattService::GATT_ERROR_NOT_SUPPORTED:
return "GATT_ERROR_NOT_SUPPORTED";
}
}
device::BluetoothRemoteGattService* GetGattBatteryService(
BluetoothDevice* device) {
for (device::BluetoothRemoteGattService* service :
device->GetGattServices()) {
if (service->GetUUID() == GetBatteryServiceUUID())
return service;
}
return nullptr;
}
} // namespace
// static
std::unique_ptr<GattBatteryPercentageFetcher>
GattBatteryPercentageFetcher::Factory::NewInstance(
scoped_refptr<device::BluetoothAdapter> adapter,
const std::string& device_address,
BatteryPercentageCallback callback) {
if (g_test_factory_instance_) {
return g_test_factory_instance_->BuildInstance(adapter, device_address,
std::move(callback));
}
auto instance = base::WrapUnique(
new GattBatteryPercentageFetcher(device_address, std::move(callback)));
instance->SetAdapterAndStartFetching(adapter);
return instance;
}
// static
void GattBatteryPercentageFetcher::Factory::SetFactoryForTesting(
Factory* factory) {
g_test_factory_instance_ = factory;
}
GattBatteryPercentageFetcher::GattBatteryPercentageFetcher(
const std::string& device_address,
BatteryPercentageCallback callback)
: device_address_(device_address), callback_(std::move(callback)) {}
GattBatteryPercentageFetcher::~GattBatteryPercentageFetcher() {
if (adapter_)
adapter_->RemoveObserver(this);
}
void GattBatteryPercentageFetcher::SetAdapterAndStartFetching(
scoped_refptr<device::BluetoothAdapter> adapter) {
DCHECK(adapter);
adapter_ = adapter;
adapter_->AddObserver(this);
// Create Gatt Connection.
BluetoothDevice* device = adapter_->GetDevice(device_address());
if (!device) {
BLUETOOTH_LOG(ERROR)
<< "GattBatteryPercentageFetcher error for device: " << device_address()
<< ". Unable to get device from adapter on CreateGattConnection.";
InvokeCallbackWithFailedFetch();
return;
}
DCHECK(!connection_);
device->CreateGattConnection(
base::Bind(&GattBatteryPercentageFetcher::OnGattConnected,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&GattBatteryPercentageFetcher::OnGattConnectError,
weak_ptr_factory_.GetWeakPtr()));
}
void GattBatteryPercentageFetcher::OnGattConnected(
std::unique_ptr<device::BluetoothGattConnection> connection) {
DCHECK_EQ(connection->GetDeviceAddress(), this->device_address());
BluetoothDevice* device = adapter_->GetDevice(device_address());
if (!device) {
BLUETOOTH_LOG(ERROR)
<< "GattBatteryPercentageFetcher error for device: " << device_address()
<< ". Unable to get device from adapter on OnGattConnected.";
InvokeCallbackWithFailedFetch();
return;
}
connection_ = std::move(connection);
// If Gatt Services Discovery is not complete yet, wait until
// GattServicesDiscovered() is called to continue fetching the battery level.
if (device->IsGattServicesDiscoveryComplete())
AttemptToReadBatteryCharacteristic();
}
void GattBatteryPercentageFetcher::OnGattConnectError(
BluetoothDevice::ConnectErrorCode error_code) {
BLUETOOTH_LOG(ERROR) << "GattBatteryPercentageFetcher error for device: "
<< device_address() << ". OnGattConnectError"
<< BluetoothDeviceErrorCodeToString(error_code);
InvokeCallbackWithFailedFetch();
}
void GattBatteryPercentageFetcher::GattServicesDiscovered(
device::BluetoothAdapter* adapter,
BluetoothDevice* device) {
if (device->GetAddress() == device_address())
AttemptToReadBatteryCharacteristic();
}
void GattBatteryPercentageFetcher::AttemptToReadBatteryCharacteristic() {
// This function should only be called once with an active GATT connection.
if (!connection_ || attempted_to_read_the_battery_characteristic_)
return;
attempted_to_read_the_battery_characteristic_ = true;
BluetoothDevice* device = adapter_->GetDevice(device_address());
if (!device) {
BLUETOOTH_LOG(ERROR) << "GattBatteryPercentageFetcher error for device: "
<< device_address()
<< ". Unable to get device from adapter on "
"AttemptToReadBatteryCharacteristic.";
InvokeCallbackWithFailedFetch();
return;
}
device::BluetoothRemoteGattService* service = GetGattBatteryService(device);
if (!service) {
BLUETOOTH_LOG(ERROR) << "GattBatteryPercentageFetcher error for device: "
<< device_address() << ". No battery service.";
InvokeCallbackWithFailedFetch();
return;
}
std::vector<device::BluetoothRemoteGattCharacteristic*> characteristics =
service->GetCharacteristicsByUUID(GetBatteryLevelUUID());
// If no battery characteristic exists, the value cannot be retrieved.
if (characteristics.empty()) {
BLUETOOTH_LOG(ERROR) << "GattBatteryPercentageFetcher error for device: "
<< device_address()
<< ". Bad format for battery level characteristic.";
InvokeCallbackWithFailedFetch();
return;
}
// Only one characteristic is expected to exist according to the GATT Battery
// Service standard. If multiple characteristics are present, arbitrarily
// choose the first one.
characteristics[0]->ReadRemoteCharacteristic(
base::Bind(&GattBatteryPercentageFetcher::OnReadBatteryLevel,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&GattBatteryPercentageFetcher::OnReadBatteryLevelError,
weak_ptr_factory_.GetWeakPtr()));
}
void GattBatteryPercentageFetcher::OnReadBatteryLevel(
const std::vector<uint8_t>& value) {
if (value.size() != 1 || value[0] > 100) {
BLUETOOTH_LOG(ERROR) << "GattBatteryPercentageFetcher error for device: "
<< device_address()
<< ". Wrong format for battery level.";
InvokeCallbackWithFailedFetch();
return;
}
InvokeCallbackWithSuccessfulFetch(value[0]);
}
void GattBatteryPercentageFetcher::OnReadBatteryLevelError(
BluetoothGattService::GattErrorCode error_code) {
BLUETOOTH_LOG(ERROR) << "GattBatteryPercentageFetcher error for device: "
<< device_address() << ". OnReadBatteryLevelError - "
<< GattErrorCodeToString(error_code);
InvokeCallbackWithFailedFetch();
}
void GattBatteryPercentageFetcher::InvokeCallbackWithSuccessfulFetch(
uint8_t battery_percentage) {
connection_.reset();
DCHECK(callback_);
std::move(callback_).Run(battery_percentage);
}
void GattBatteryPercentageFetcher::InvokeCallbackWithFailedFetch() {
connection_.reset();
DCHECK(callback_);
std::move(callback_).Run(base::nullopt);
}
} // namespace ash