You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
266 lines
9.0 KiB
266 lines
9.0 KiB
/******************************************************************************
|
|
*
|
|
* Copyright 2018 The Android Open Source Project
|
|
*
|
|
* 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 <base/callback_forward.h>
|
|
#include <hardware/bt_hearing_aid.h>
|
|
#include <cstdint>
|
|
#include <deque>
|
|
#include <functional>
|
|
#include <vector>
|
|
|
|
constexpr uint16_t HEARINGAID_MAX_NUM_UUIDS = 1;
|
|
|
|
constexpr uint16_t HA_INTERVAL_10_MS = 10;
|
|
constexpr uint16_t HA_INTERVAL_20_MS = 20;
|
|
|
|
// Masks for checking capability support
|
|
constexpr uint8_t CAPABILITY_SIDE = 0x01;
|
|
constexpr uint8_t CAPABILITY_BINAURAL = 0x02;
|
|
constexpr uint8_t CAPABILITY_RESERVED = 0xFC;
|
|
|
|
/** Implementations of HearingAid will also implement this interface */
|
|
class HearingAidAudioReceiver {
|
|
public:
|
|
virtual ~HearingAidAudioReceiver() = default;
|
|
virtual void OnAudioDataReady(const std::vector<uint8_t>& data) = 0;
|
|
|
|
// API to stop our feeding timer, and notify hearing aid devices that the
|
|
// streaming would stop, too.
|
|
//
|
|
// @param stop_audio_ticks a callable function calls out to stop the media
|
|
// timer for reading data.
|
|
virtual void OnAudioSuspend(
|
|
const std::function<void()>& stop_audio_ticks) = 0;
|
|
|
|
// To notify hearing aid devices to be ready for streaming, and start the
|
|
// media timer to feed the audio data.
|
|
//
|
|
// @param start_audio_ticks a callable function calls out to start a periodic
|
|
// timer for feeding data from the audio HAL.
|
|
virtual void OnAudioResume(
|
|
const std::function<void()>& start_audio_ticks) = 0;
|
|
};
|
|
|
|
// Number of rssi reads to attempt when requested
|
|
constexpr int READ_RSSI_NUM_TRIES = 10;
|
|
constexpr int PERIOD_TO_READ_RSSI_IN_INTERVALS = 5;
|
|
// Depth of RSSI History in DumpSys
|
|
constexpr int MAX_RSSI_HISTORY = 15;
|
|
|
|
struct rssi_log {
|
|
struct timespec timestamp;
|
|
std::vector<int8_t> rssi;
|
|
};
|
|
|
|
struct AudioStats {
|
|
size_t packet_flush_count;
|
|
size_t packet_send_count;
|
|
size_t frame_flush_count;
|
|
size_t frame_send_count;
|
|
std::deque<rssi_log> rssi_history;
|
|
|
|
AudioStats() { Reset(); }
|
|
|
|
void Reset() {
|
|
packet_flush_count = 0;
|
|
packet_send_count = 0;
|
|
frame_flush_count = 0;
|
|
frame_send_count = 0;
|
|
}
|
|
};
|
|
|
|
/** Possible states for the Connection Update status */
|
|
typedef enum {
|
|
NONE, // Not Connected
|
|
AWAITING, // Waiting for start the Connection Update operation
|
|
STARTED, // Connection Update has started
|
|
COMPLETED // Connection Update is completed successfully
|
|
} connection_update_status_t;
|
|
|
|
struct HearingDevice {
|
|
RawAddress address;
|
|
/* This is true only during first connection to profile, until we store the
|
|
* device */
|
|
bool first_connection;
|
|
bool service_changed_rcvd;
|
|
|
|
/* 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;
|
|
|
|
/* For two hearing aids, you must update their parameters one after another,
|
|
* not simulteanously, to ensure start of connection events for both devices
|
|
* are far from each other. This status tracks whether this device is waiting
|
|
* for update of parameters, that should happen after "LE Connection Update
|
|
* Complete" event
|
|
*/
|
|
connection_update_status_t connection_update_status;
|
|
uint16_t requested_connection_interval;
|
|
|
|
/* if true, we are connected, L2CAP socket is open, we can stream audio.
|
|
However, the actual audio stream also depends on whether the
|
|
Audio Service has resumed.
|
|
*/
|
|
bool accepting_audio;
|
|
|
|
uint16_t conn_id;
|
|
uint16_t gap_handle;
|
|
uint16_t audio_control_point_handle;
|
|
uint16_t audio_status_handle;
|
|
uint16_t audio_status_ccc_handle;
|
|
uint16_t service_changed_ccc_handle;
|
|
uint16_t volume_handle;
|
|
uint16_t read_psm_handle;
|
|
|
|
uint8_t capabilities;
|
|
uint64_t hi_sync_id;
|
|
uint16_t render_delay;
|
|
uint16_t preparation_delay;
|
|
uint16_t codecs;
|
|
|
|
AudioStats audio_stats;
|
|
/* Keep tracks of whether the "Start Cmd" has been send to this device. When
|
|
the "Stop Cmd" is send or when this device disconnects, then this flag is
|
|
cleared. Please note that the "Start Cmd" is not send during device
|
|
connection in the case when the audio is suspended. */
|
|
bool playback_started;
|
|
/* This tracks whether the last command to Hearing Aids device is
|
|
* ACKnowledged. */
|
|
bool command_acked;
|
|
|
|
/* When read_rssi_count is > 0, then read the rssi. The interval between rssi
|
|
reads is tracked by num_intervals_since_last_rssi_read. */
|
|
int read_rssi_count;
|
|
int num_intervals_since_last_rssi_read;
|
|
|
|
HearingDevice(const RawAddress& address, uint8_t capabilities,
|
|
uint16_t codecs, uint16_t audio_control_point_handle,
|
|
uint16_t audio_status_handle, uint16_t audio_status_ccc_handle,
|
|
uint16_t service_changed_ccc_handle, uint16_t volume_handle,
|
|
uint16_t read_psm_handle, uint64_t hiSyncId,
|
|
uint16_t render_delay, uint16_t preparation_delay)
|
|
: address(address),
|
|
first_connection(false),
|
|
service_changed_rcvd(false),
|
|
connecting_actively(false),
|
|
connection_update_status(NONE),
|
|
accepting_audio(false),
|
|
conn_id(0),
|
|
gap_handle(0),
|
|
audio_control_point_handle(audio_control_point_handle),
|
|
audio_status_handle(audio_status_handle),
|
|
audio_status_ccc_handle(audio_status_ccc_handle),
|
|
service_changed_ccc_handle(service_changed_ccc_handle),
|
|
volume_handle(volume_handle),
|
|
read_psm_handle(read_psm_handle),
|
|
capabilities(capabilities),
|
|
hi_sync_id(hiSyncId),
|
|
render_delay(render_delay),
|
|
preparation_delay(preparation_delay),
|
|
codecs(codecs),
|
|
playback_started(false),
|
|
command_acked(false),
|
|
read_rssi_count(0) {}
|
|
|
|
HearingDevice(const RawAddress& address, bool first_connection)
|
|
: address(address),
|
|
first_connection(first_connection),
|
|
service_changed_rcvd(false),
|
|
connecting_actively(first_connection),
|
|
connection_update_status(NONE),
|
|
accepting_audio(false),
|
|
conn_id(0),
|
|
gap_handle(0),
|
|
audio_status_handle(0),
|
|
audio_status_ccc_handle(0),
|
|
service_changed_ccc_handle(0),
|
|
read_psm_handle(0),
|
|
capabilities(0),
|
|
hi_sync_id(0),
|
|
render_delay(0),
|
|
preparation_delay(0),
|
|
codecs(0),
|
|
playback_started(false),
|
|
command_acked(false),
|
|
read_rssi_count(0) {}
|
|
|
|
HearingDevice() : HearingDevice(RawAddress::kEmpty, false) {}
|
|
|
|
/* return true if this device represents left Hearing Aid. Returned value is
|
|
* valid only after capabilities are discovered */
|
|
bool isLeft() const { return !(capabilities & CAPABILITY_SIDE); }
|
|
};
|
|
|
|
class HearingAid {
|
|
public:
|
|
virtual ~HearingAid() = default;
|
|
|
|
static void Initialize(bluetooth::hearing_aid::HearingAidCallbacks* callbacks,
|
|
base::Closure initCb);
|
|
static void CleanUp();
|
|
static bool IsHearingAidRunning();
|
|
static HearingAid* Get();
|
|
static void DebugDump(int fd);
|
|
|
|
static void AddFromStorage(const HearingDevice& dev_info,
|
|
uint16_t is_acceptlisted);
|
|
|
|
static int GetDeviceCount();
|
|
|
|
virtual void Connect(const RawAddress& address) = 0;
|
|
virtual void Disconnect(const RawAddress& address) = 0;
|
|
virtual void AddToAcceptlist(const RawAddress& address) = 0;
|
|
virtual void SetVolume(int8_t volume) = 0;
|
|
};
|
|
|
|
/* Represents configuration of audio codec, as exchanged between hearing aid and
|
|
* phone.
|
|
* It can also be passed to the audio source to configure its parameters.
|
|
*/
|
|
struct CodecConfiguration {
|
|
/** sampling rate that the codec expects to receive from audio framework */
|
|
uint32_t sample_rate;
|
|
|
|
/** bitrate that codec expects to receive from audio framework in bits per
|
|
* channel */
|
|
uint32_t bit_rate;
|
|
|
|
/** Data interval determines how often we send samples to the remote. This
|
|
* should match how often we grab data from audio source, optionally we can
|
|
* grab data every 2 or 3 intervals, but this would increase latency.
|
|
*
|
|
* Value is provided in ms, must be divisable by 1.25 to make sure the
|
|
* connection interval is integer.
|
|
*/
|
|
uint16_t data_interval_ms;
|
|
};
|
|
|
|
/** Represents source of audio for hearing aids */
|
|
class HearingAidAudioSource {
|
|
public:
|
|
static void Start(const CodecConfiguration& codecConfiguration,
|
|
HearingAidAudioReceiver* audioReceiver,
|
|
uint16_t remote_delay_ms);
|
|
static void Stop();
|
|
static void Initialize();
|
|
static void CleanUp();
|
|
static void DebugDump(int fd);
|
|
};
|