| // 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 "device/bluetooth/floss/floss_dbus_client.h" |
| |
| #include <string> |
| |
| #include "base/compiler_specific.h" |
| #include "base/logging.h" |
| #include "base/strings/stringprintf.h" |
| #include "dbus/message.h" |
| #include "device/bluetooth/floss/floss_adapter_client.h" |
| #include "device/bluetooth/floss/floss_lescan_client.h" |
| |
| namespace floss { |
| |
| // All Floss D-Bus methods return immediately, so the timeout can be very short. |
| int kDBusTimeoutMs = 2000; |
| // Timeout for waiting HCI enabled changed. Make it longer since it takes longer |
| // when there is a connected device. |
| int kAdapterEnabledTimeoutMs = 5000; |
| |
| namespace { |
| constexpr char kDeviceIdNameKey[] = "name"; |
| constexpr char kDeviceIdAddressKey[] = "address"; |
| } // namespace |
| |
| Error::Error(const std::string& name, const std::string& message) |
| : name(name), message(message) {} |
| |
| std::ostream& operator<<(std::ostream& os, const Error& error) { |
| os << error.name; |
| |
| if (error.name.size() == 0) { |
| os << "<no error name>"; |
| } |
| |
| if (error.message.size()) { |
| os << ": " << error.message; |
| } |
| |
| return os; |
| } |
| |
| std::string Error::ToString() { |
| std::stringstream ss; |
| ss << *this; |
| return ss.str(); |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const bool*) { |
| static DBusTypeInfo info{"b", "bool"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const uint8_t*) { |
| static DBusTypeInfo info{"y", "uint8_t"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const int8_t*) { |
| static DBusTypeInfo info{"n", "int8"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const uint16_t*) { |
| static DBusTypeInfo info{"q", "uint16"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const int16_t*) { |
| static DBusTypeInfo info{"n", "int16"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const uint32_t*) { |
| static DBusTypeInfo info{"u", "uint32"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const int32_t*) { |
| static DBusTypeInfo info{"i", "int32"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const uint64_t*) { |
| static DBusTypeInfo info{"t", "uint64"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const int64_t*) { |
| static DBusTypeInfo info{"x", "int64"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const double*) { |
| static DBusTypeInfo info{"d", "double"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const std::string*) { |
| static DBusTypeInfo info{"s", "string"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const dbus::ObjectPath*) { |
| static DBusTypeInfo info{"o", "object_path"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const base::ScopedFD*) { |
| static DBusTypeInfo info{"h", "FD"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const FlossDeviceId*) { |
| static DBusTypeInfo info{"a{sv}", "FlossDeviceId"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo( |
| const FlossAdapterClient::VendorProductInfo*) { |
| static DBusTypeInfo info{"a{sv}", "VendorProductInfo"}; |
| return info; |
| } |
| |
| template <> |
| DEVICE_BLUETOOTH_EXPORT const DBusTypeInfo& GetDBusTypeInfo( |
| const device::BluetoothUUID*) { |
| static DBusTypeInfo info{"ay", "BluetoothUUID"}; |
| return info; |
| } |
| |
| FlossDBusClient::FlossDBusClient() = default; |
| FlossDBusClient::~FlossDBusClient() = default; |
| |
| const char FlossDBusClient::kErrorDBus[] = "org.chromium.Error.DBus"; |
| const char FlossDBusClient::kErrorNoResponse[] = |
| "org.chromium.Error.NoResponse"; |
| const char FlossDBusClient::kErrorInvalidParameters[] = |
| "org.chromium.Error.InvalidParameters"; |
| const char FlossDBusClient::kErrorInvalidReturn[] = |
| "org.chromium.Error.InvalidReturn"; |
| const char FlossDBusClient::kErrorDoesNotExist[] = |
| "org.chromium.Error.DoesNotExist"; |
| const char FlossDBusClient::kOptionalValueKey[] = "optional_value"; |
| |
| // static |
| dbus::ObjectPath FlossDBusClient::GenerateAdapterPath(int adapter_index) { |
| return dbus::ObjectPath( |
| base::StringPrintf(kAdapterObjectFormat, adapter_index)); |
| } |
| |
| // static |
| dbus::ObjectPath FlossDBusClient::GenerateGattPath(int adapter_index) { |
| return dbus::ObjectPath(base::StringPrintf(kGattObjectFormat, adapter_index)); |
| } |
| |
| // static |
| dbus::ObjectPath FlossDBusClient::GenerateBatteryManagerPath( |
| int adapter_index) { |
| return dbus::ObjectPath( |
| base::StringPrintf(kBatteryManagerObjectFormat, adapter_index)); |
| } |
| |
| // static |
| dbus::ObjectPath FlossDBusClient::GenerateBluetoothTelephonyPath( |
| int adapter_index) { |
| return dbus::ObjectPath( |
| base::StringPrintf(kBluetoothTelephonyObjectFormat, adapter_index)); |
| } |
| |
| dbus::ObjectPath FlossDBusClient::GenerateAdminPath(int adapter_index) { |
| return dbus::ObjectPath( |
| base::StringPrintf(kAdminObjectFormat, adapter_index)); |
| } |
| |
| dbus::ObjectPath FlossDBusClient::GenerateLoggingPath(int adapter_index) { |
| return dbus::ObjectPath( |
| base::StringPrintf(kAdapterLoggingObjectFormat, adapter_index)); |
| } |
| |
| device::BluetoothDevice::ConnectErrorCode |
| FlossDBusClient::BtifStatusToConnectErrorCode( |
| FlossDBusClient::BtifStatus status) { |
| switch (status) { |
| case BtifStatus::kSuccess: |
| NOTREACHED(); |
| case BtifStatus::kFail: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_FAILED; |
| case BtifStatus::kNotReady: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_DEVICE_NOT_READY; |
| case BtifStatus::kAuthFailure: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_AUTH_FAILED; |
| case BtifStatus::kAuthRejected: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_AUTH_REJECTED; |
| case BtifStatus::kDone: |
| case BtifStatus::kBusy: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_INPROGRESS; |
| case BtifStatus::kUnsupported: |
| return device::BluetoothDevice::ConnectErrorCode:: |
| ERROR_UNSUPPORTED_DEVICE; |
| case BtifStatus::kNomem: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_NO_MEMORY; |
| case BtifStatus::kParmInvalid: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_INVALID_ARGS; |
| case BtifStatus::kUnhandled: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_UNKNOWN; |
| case BtifStatus::kRmtDevDown: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_DOES_NOT_EXIST; |
| case BtifStatus::kJniEnvironmentError: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_JNI_ENVIRONMENT; |
| case BtifStatus::kJniThreadAttachError: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_JNI_THREAD_ATTACH; |
| case BtifStatus::kWakelockError: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_WAKELOCK; |
| case BtifStatus::kTimeout: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_NON_AUTH_TIMEOUT; |
| case BtifStatus::kDeviceNotFound: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_DOES_NOT_EXIST; |
| case BtifStatus::kUnexpectedState: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_UNEXPECTED_STATE; |
| case BtifStatus::kSocketError: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_SOCKET; |
| default: |
| return device::BluetoothDevice::ConnectErrorCode::ERROR_FAILED; |
| } |
| } |
| |
| // Default error handler for dbus clients is to just print the error right now. |
| // TODO(abps) - Deprecate this once error handling is implemented in the upper |
| // layers. |
| void FlossDBusClient::LogErrorResponse(const std::string& message, |
| dbus::ErrorResponse* error) { |
| if (!error) { |
| return; |
| } |
| |
| dbus::MessageReader reader(error); |
| auto error_name = error->GetErrorName(); |
| std::string error_message; |
| reader.PopString(&error_message); |
| |
| LOG(ERROR) << message << ": " << error_name << ": " << error_message; |
| } |
| |
| // static |
| Error FlossDBusClient::ErrorResponseToError(const std::string& default_name, |
| const std::string& default_message, |
| dbus::ErrorResponse* error) { |
| Error result(default_name, default_message); |
| |
| if (error) { |
| dbus::MessageReader reader(error); |
| result.name = error->GetErrorName(); |
| reader.PopString(&result.message); |
| } |
| |
| return result; |
| } |
| |
| // static |
| // No-op read for a void value. |
| template <> |
| bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader, Void* value) { |
| return true; |
| } |
| |
| // static |
| template <> |
| bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader, bool* value) { |
| return reader->PopBool(value); |
| } |
| |
| // static |
| template <> |
| bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader, |
| uint8_t* value) { |
| return reader->PopByte(value); |
| } |
| |
| // static |
| template <> |
| bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader, |
| int8_t* value) { |
| int16_t val; |
| bool success; |
| |
| success = reader->PopInt16(&val); |
| *value = static_cast<int8_t>(val); |
| |
| return success; |
| } |
| |
| // static |
| template <> |
| bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader, |
| uint16_t* value) { |
| return reader->PopUint16(value); |
| } |
| |
| // static |
| template <> |
| bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader, |
| uint32_t* value) { |
| return reader->PopUint32(value); |
| } |
| |
| // static |
| template <> |
| bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader, |
| uint64_t* value) { |
| return reader->PopUint64(value); |
| } |
| |
| // static |
| template <> |
| bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader, |
| int32_t* value) { |
| return reader->PopInt32(value); |
| } |
| |
| // static |
| template bool FlossDBusClient::ReadDBusParam<int32_t>( |
| dbus::MessageReader* reader, |
| std::optional<int32_t>* value); |
| |
| // static |
| template <> |
| bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader, |
| std::string* value) { |
| return reader->PopString(value); |
| } |
| |
| // static |
| template bool FlossDBusClient::ReadDBusParam<std::string>( |
| dbus::MessageReader* reader, |
| std::optional<std::string>* value); |
| |
| // static |
| template <> |
| bool FlossDBusClient::ReadDBusParam( |
| dbus::MessageReader* reader, |
| FlossAdapterClient::BluetoothDeviceType* value) { |
| uint32_t val; |
| bool success; |
| |
| success = reader->PopUint32(&val); |
| *value = static_cast<FlossAdapterClient::BluetoothDeviceType>(val); |
| |
| return success; |
| } |
| |
| // static |
| template <> |
| bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader, |
| device::BluetoothUUID* uuid) { |
| base::span<const uint8_t> bytes; |
| if (!reader->PopArrayOfBytes(&bytes)) { |
| return false; |
| } |
| |
| if (bytes.size() != 16U) { |
| return false; |
| } |
| |
| *uuid = device::BluetoothUUID(bytes); |
| DCHECK(uuid->IsValid()); |
| return true; |
| } |
| |
| // static |
| template bool FlossDBusClient::ReadDBusParam<device::BluetoothUUID>( |
| dbus::MessageReader* reader, |
| std::optional<device::BluetoothUUID>* uuid); |
| |
| // static |
| template <> |
| bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader, |
| FlossDBusClient::BtifStatus* status) { |
| uint32_t raw_type = 0; |
| bool read = FlossDBusClient::ReadDBusParam(reader, &raw_type); |
| |
| if (read) { |
| *status = static_cast<FlossDBusClient::BtifStatus>(raw_type); |
| } |
| |
| return read; |
| } |
| |
| // static |
| template <> |
| bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader, |
| base::ScopedFD* fd) { |
| return reader->PopFileDescriptor(fd); |
| } |
| |
| // static |
| template bool FlossDBusClient::ReadDBusParam<base::ScopedFD>( |
| dbus::MessageReader* reader, |
| std::optional<base::ScopedFD>* fd); |
| |
| // static |
| template <> |
| bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader, |
| FlossDeviceId* device) { |
| static StructReader<FlossDeviceId> struct_reader({ |
| {"address", CreateFieldReader(&FlossDeviceId::address)}, |
| {"name", CreateFieldReader(&FlossDeviceId::name)}, |
| }); |
| |
| return struct_reader.ReadDBusParam(reader, device); |
| } |
| |
| // static |
| template <> |
| bool FlossDBusClient::ReadDBusParam( |
| dbus::MessageReader* reader, |
| FlossAdapterClient::VendorProductInfo* vpi) { |
| static StructReader<FlossAdapterClient::VendorProductInfo> struct_reader({ |
| {"vendor_id_src", |
| CreateFieldReader(&FlossAdapterClient::VendorProductInfo::vendorIdSrc)}, |
| {"vendor_id", |
| CreateFieldReader(&FlossAdapterClient::VendorProductInfo::vendorId)}, |
| {"product_id", |
| CreateFieldReader(&FlossAdapterClient::VendorProductInfo::productId)}, |
| {"version", |
| CreateFieldReader(&FlossAdapterClient::VendorProductInfo::version)}, |
| }); |
| |
| return struct_reader.ReadDBusParam(reader, vpi); |
| } |
| |
| // static |
| template <> |
| bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader, |
| FlossAdapterClient::BtAddressType* type) { |
| uint32_t val; |
| bool success; |
| |
| success = reader->PopUint32(&val); |
| *type = static_cast<FlossAdapterClient::BtAddressType>(val); |
| |
| return success; |
| } |
| |
| template <> |
| bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader, |
| FlossAdapterClient::BtAdapterRole* type) { |
| uint32_t val; |
| bool success; |
| |
| success = reader->PopUint32(&val); |
| *type = static_cast<FlossAdapterClient::BtAdapterRole>(val); |
| |
| return success; |
| } |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer, |
| const FlossDeviceId& device) { |
| dbus::MessageWriter array(nullptr); |
| dbus::MessageWriter dict(nullptr); |
| |
| writer->OpenArray("{sv}", &array); |
| |
| WriteDictEntry(&array, kDeviceIdNameKey, device.name); |
| WriteDictEntry(&array, kDeviceIdAddressKey, device.address); |
| |
| writer->CloseContainer(&array); |
| } |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer, |
| const uint64_t& data) { |
| writer->AppendUint64(data); |
| } |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer, |
| const uint32_t& data) { |
| writer->AppendUint32(data); |
| } |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer, |
| const uint8_t& data) { |
| writer->AppendByte(data); |
| } |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer, |
| const int8_t& data) { |
| return writer->AppendInt16(static_cast<int16_t>(data)); |
| } |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer, |
| const uint16_t& data) { |
| return writer->AppendUint16(data); |
| } |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer, |
| const int16_t& data) { |
| return writer->AppendInt16(data); |
| } |
| |
| template void FlossDBusClient::WriteDBusParam<uint32_t>( |
| dbus::MessageWriter* writer, |
| const std::optional<uint32_t>& data); |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer, |
| const int32_t& data) { |
| writer->AppendInt32(data); |
| } |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer, |
| const std::string& data) { |
| writer->AppendString(data); |
| } |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer, |
| const bool& data) { |
| writer->AppendBool(data); |
| } |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer, |
| const std::vector<uint8_t>& data) { |
| writer->AppendArrayOfBytes(data); |
| } |
| |
| template <> |
| DEVICE_BLUETOOTH_EXPORT void FlossDBusClient::WriteDBusParam( |
| dbus::MessageWriter* writer, |
| const device::BluetoothUUID& uuid) { |
| WriteDBusParam(writer, uuid.GetBytes()); |
| } |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer, |
| const base::ScopedFD& fd) { |
| writer->AppendFileDescriptor(fd.get()); |
| } |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer, |
| const dbus::ObjectPath& path) { |
| writer->AppendObjectPath(path); |
| } |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam( |
| dbus::MessageWriter* writer, |
| const FlossDBusClient::BtifStatus& status) { |
| uint32_t raw_type = static_cast<uint32_t>(status); |
| WriteDBusParam(writer, raw_type); |
| } |
| |
| template void FlossDBusClient::DefaultResponseWithCallback( |
| ResponseCallback<bool> callback, |
| dbus::Response* response, |
| dbus::ErrorResponse* error_response); |
| |
| template void FlossDBusClient::DefaultResponseWithCallback( |
| ResponseCallback<uint8_t> callback, |
| dbus::Response* response, |
| dbus::ErrorResponse* error_response); |
| |
| template void FlossDBusClient::DefaultResponseWithCallback( |
| ResponseCallback<uint32_t> callback, |
| dbus::Response* response, |
| dbus::ErrorResponse* error_response); |
| |
| template void FlossDBusClient::DefaultResponseWithCallback( |
| ResponseCallback<std::string> callback, |
| dbus::Response* response, |
| dbus::ErrorResponse* error_response); |
| |
| template void FlossDBusClient::DefaultResponseWithCallback( |
| ResponseCallback<std::vector<FlossDeviceId>> callback, |
| dbus::Response* response, |
| dbus::ErrorResponse* error_response); |
| |
| template void FlossDBusClient::DefaultResponseWithCallback( |
| ResponseCallback<FlossAdapterClient::BluetoothDeviceType> callback, |
| dbus::Response* response, |
| dbus::ErrorResponse* error_response); |
| |
| template void FlossDBusClient::DefaultResponseWithCallback( |
| ResponseCallback<device::BluetoothDevice::UUIDList> callback, |
| dbus::Response* response, |
| dbus::ErrorResponse* error_response); |
| |
| template void FlossDBusClient::DefaultResponseWithCallback( |
| ResponseCallback<FlossAdapterClient::VendorProductInfo> callback, |
| dbus::Response* response, |
| dbus::ErrorResponse* error_response); |
| |
| template void FlossDBusClient::DefaultResponseWithCallback( |
| ResponseCallback<FlossAdapterClient::BtAddressType> callback, |
| dbus::Response* response, |
| dbus::ErrorResponse* error_response); |
| |
| template void FlossDBusClient::DefaultResponseWithCallback( |
| ResponseCallback<FlossDBusClient::BtifStatus> callback, |
| dbus::Response* response, |
| dbus::ErrorResponse* error_response); |
| |
| void FlossDBusClient::DefaultResponse(const std::string& caller, |
| dbus::Response* response, |
| dbus::ErrorResponse* error_response) { |
| if (error_response) { |
| FlossDBusClient::LogErrorResponse(caller, error_response); |
| } else { |
| DVLOG(1) << caller << "::OnResponse"; |
| } |
| } |
| |
| } // namespace floss |