C++程序  |  251行  |  8.46 KB

/******************************************************************************
 *
 *  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 <deque>
#include <future>
#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;
  virtual void OnAudioSuspend(std::promise<void> do_suspend_promise) = 0;
  virtual void OnAudioResume(std::promise<void> do_resume_promise) = 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_white_listed);

  static int GetDeviceCount();

  virtual void Connect(const RawAddress& address) = 0;
  virtual void Disconnect(const RawAddress& address) = 0;
  virtual void AddToWhiteList(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);
};