193 lines
5.8 KiB
C++
193 lines
5.8 KiB
C++
/*
|
|
* Copyright 2021 HIMSA II K/S - www.himsa.com.
|
|
* Represented by EHIMA - www.ehima.com
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
#include "bta/include/bta_gatt_api.h"
|
|
#include "bta/vc/types.h"
|
|
#include "include/hardware/bt_vc.h"
|
|
#include "types/raw_address.h"
|
|
|
|
namespace bluetooth {
|
|
namespace vc {
|
|
namespace internal {
|
|
|
|
class VolumeControlDevice {
|
|
public:
|
|
RawAddress address;
|
|
/* This is true only during first connection to profile, until we store the
|
|
* device
|
|
*/
|
|
bool first_connection;
|
|
|
|
/* we are making active attempt to connect to this device, 'direct connect'.
|
|
* This is true only during initial phase of first connection. */
|
|
bool connecting_actively;
|
|
|
|
bool service_changed_rcvd;
|
|
|
|
uint8_t volume;
|
|
uint8_t change_counter;
|
|
bool mute;
|
|
uint8_t flags;
|
|
|
|
uint16_t connection_id;
|
|
|
|
/* Volume Control Service */
|
|
uint16_t volume_state_handle;
|
|
uint16_t volume_state_ccc_handle;
|
|
uint16_t volume_control_point_handle;
|
|
uint16_t volume_flags_handle;
|
|
uint16_t volume_flags_ccc_handle;
|
|
|
|
bool device_ready; /* Set when device read server status and registgered for
|
|
notifications */
|
|
|
|
VolumeControlDevice(const RawAddress& address, bool first_connection)
|
|
: address(address),
|
|
first_connection(first_connection),
|
|
connecting_actively(first_connection),
|
|
service_changed_rcvd(false),
|
|
volume(0),
|
|
change_counter(0),
|
|
mute(false),
|
|
flags(0),
|
|
connection_id(GATT_INVALID_CONN_ID),
|
|
volume_state_handle(0),
|
|
volume_state_ccc_handle(0),
|
|
volume_control_point_handle(0),
|
|
volume_flags_handle(0),
|
|
volume_flags_ccc_handle(0),
|
|
device_ready(false) {}
|
|
|
|
~VolumeControlDevice() = default;
|
|
|
|
inline std::string ToString() { return address.ToString(); }
|
|
|
|
void DebugDump(int fd) { dprintf(fd, "%s\n", this->ToString().c_str()); }
|
|
|
|
bool IsConnected() { return connection_id != GATT_INVALID_CONN_ID; }
|
|
|
|
void Disconnect(tGATT_IF gatt_if);
|
|
|
|
bool UpdateHandles(void);
|
|
|
|
void ResetHandles(void);
|
|
|
|
bool HasHandles(void) { return GATT_HANDLE_IS_VALID(volume_state_handle); }
|
|
|
|
void ControlPointOperation(uint8_t opcode, const std::vector<uint8_t>* arg,
|
|
GATT_WRITE_OP_CB cb, void* cb_data);
|
|
bool IsEncryptionEnabled();
|
|
|
|
bool EnableEncryption(tBTM_SEC_CALLBACK* callback);
|
|
|
|
bool EnqueueInitialRequests(tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb,
|
|
GATT_WRITE_OP_CB cccd_write_cb);
|
|
void EnqueueRemainingRequests(tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb,
|
|
GATT_WRITE_OP_CB cccd_write_cb);
|
|
bool VerifyReady(uint16_t handle);
|
|
|
|
private:
|
|
/*
|
|
* This is used to track the pending GATT operation handles. Once the list is
|
|
* empty the device is assumed ready and connected. We are doing it because we
|
|
* want to make sure all the required characteristics and descritors are
|
|
* available on server side.
|
|
*/
|
|
std::unordered_set<uint16_t> handles_pending;
|
|
|
|
uint16_t find_ccc_handle(uint16_t chrc_handle);
|
|
bool set_volume_control_service_handles(const gatt::Service& service);
|
|
bool subscribe_for_notifications(tGATT_IF gatt_if, uint16_t handle,
|
|
uint16_t ccc_handle, GATT_WRITE_OP_CB cb);
|
|
};
|
|
|
|
class VolumeControlDevices {
|
|
public:
|
|
void Add(const RawAddress& address, bool first_connection) {
|
|
if (FindByAddress(address) != nullptr) return;
|
|
|
|
devices_.emplace_back(address, first_connection);
|
|
}
|
|
|
|
void Remove(const RawAddress& address) {
|
|
for (auto it = devices_.begin(); it != devices_.end(); it++) {
|
|
if (it->address == address) {
|
|
it = devices_.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
VolumeControlDevice* FindByAddress(const RawAddress& address) {
|
|
auto iter = std::find_if(devices_.begin(), devices_.end(),
|
|
[&address](const VolumeControlDevice& device) {
|
|
return device.address == address;
|
|
});
|
|
|
|
return (iter == devices_.end()) ? nullptr : &(*iter);
|
|
}
|
|
|
|
VolumeControlDevice* FindByConnId(uint16_t connection_id) {
|
|
auto iter =
|
|
std::find_if(devices_.begin(), devices_.end(),
|
|
[&connection_id](const VolumeControlDevice& device) {
|
|
return device.connection_id == connection_id;
|
|
});
|
|
|
|
return (iter == devices_.end()) ? nullptr : &(*iter);
|
|
}
|
|
|
|
size_t Size() { return (devices_.size()); }
|
|
|
|
void Clear() { devices_.clear(); }
|
|
|
|
void DebugDump(int fd) {
|
|
for (auto& device : devices_) {
|
|
device.DebugDump(fd);
|
|
}
|
|
}
|
|
|
|
void Disconnect(tGATT_IF gatt_if) {
|
|
for (auto& device : devices_) {
|
|
device.Disconnect(gatt_if);
|
|
}
|
|
}
|
|
|
|
void ControlPointOperation(std::vector<RawAddress>& devices, uint8_t opcode,
|
|
const std::vector<uint8_t>* arg,
|
|
GATT_WRITE_OP_CB cb, void* cb_data) {
|
|
for (auto& addr : devices) {
|
|
VolumeControlDevice* device = FindByAddress(addr);
|
|
if (device && device->IsConnected())
|
|
device->ControlPointOperation(opcode, arg, cb, cb_data);
|
|
}
|
|
}
|
|
|
|
private:
|
|
std::vector<VolumeControlDevice> devices_;
|
|
};
|
|
|
|
} // namespace internal
|
|
} // namespace vc
|
|
} // namespace bluetooth
|