Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 895430b2 authored by Cheney Ni's avatar Cheney Ni Committed by Hansong Zhang
Browse files

Add audio channel of Bluetooth Audio Hal v2 for Hearing Aid

This brings a new interface for Hearing Aid to communicate with audio.
Both audio control and data path go through the new Bluetooth audio
HAL and is similar to A2DP usecase. It initials this interface of client
side and get audio provider at setup and manage session with audio hal.

There is a system property to switch between the UIPC socket and the
Bluetooth Audio Hal v2 interface:
    persist.bluetooth.bluetooth_audio_hal.enabled

Bug: 111519504
Bug: 120222104
Test: manual test with Hearing Aid
TODO: Pass audio matadata into Hearing Aid service

Change-Id: I648775d61a8a4fe4fa680dd31ffc39fc89267c06
parent af906e96
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -12,6 +12,7 @@ cc_library_static {
    srcs: [
    srcs: [
        "a2dp_software_encoding.cc",
        "a2dp_software_encoding.cc",
        "client_interface.cc",
        "client_interface.cc",
        "hearing_aid_software_encoding.cc",
    ],
    ],
    shared_libs: [
    shared_libs: [
        "android.hardware.bluetooth.audio@2.0",
        "android.hardware.bluetooth.audio@2.0",
+205 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright 2019 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.
 */

#define LOG_TAG "BTAudioClientHearingAid"

#include "hearing_aid_software_encoding.h"
#include "client_interface.h"

#include "audio_hearing_aid_hw/include/audio_hearing_aid_hw.h"
#include "osi/include/log.h"
#include "osi/include/properties.h"

namespace {

using ::android::hardware::bluetooth::audio::V2_0::CodecType;
using ::bluetooth::audio::AudioConfiguration;
using ::bluetooth::audio::BitsPerSample;
using ::bluetooth::audio::BluetoothAudioCtrlAck;
using ::bluetooth::audio::ChannelMode;
using ::bluetooth::audio::PcmParameters;
using ::bluetooth::audio::SampleRate;
using ::bluetooth::audio::SessionType;
using ::bluetooth::audio::hearing_aid::StreamCallbacks;

// Transport implementation for Hearing Aids
class HearingAidTransport
    : public bluetooth::audio::IBluetoothTransportInstance {
 public:
  HearingAidTransport(StreamCallbacks stream_cb)
      : IBluetoothTransportInstance(
            SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH, {}),
        stream_cb_(std::move(stream_cb)),
        total_bytes_read_(0),
        data_position_({}){};

  BluetoothAudioCtrlAck StartRequest() override {
    LOG(INFO) << __func__;
    if (stream_cb_.on_resume_(true)) {
      return BluetoothAudioCtrlAck::SUCCESS_FINISHED;
    }
    return BluetoothAudioCtrlAck::FAILURE;
  }

  BluetoothAudioCtrlAck SuspendRequest() override {
    LOG(INFO) << __func__;
    if (stream_cb_.on_suspend_()) {
      uint8_t p_buf[AUDIO_STREAM_OUTPUT_BUFFER_SZ * 2];
      ::bluetooth::audio::hearing_aid::read(p_buf, sizeof(p_buf));
      return BluetoothAudioCtrlAck::SUCCESS_FINISHED;
    } else {
      return BluetoothAudioCtrlAck::FAILURE;
    }
  }

  void StopRequest() override {
    LOG(INFO) << __func__;
    stream_cb_.on_suspend_();

    // flush
    uint8_t p_buf[AUDIO_STREAM_OUTPUT_BUFFER_SZ * 2];
    ::bluetooth::audio::hearing_aid::read(p_buf, sizeof(p_buf));
  }

  bool GetPresentationPosition(uint64_t* remote_delay_report_ns,
                               uint64_t* total_bytes_read,
                               timespec* data_position) override {
    VLOG(2) << __func__ << ": data=" << total_bytes_read_
            << " byte(s), timestamp=" << data_position_.tv_sec << "."
            << data_position_.tv_nsec << "s";
    if (remote_delay_report_ns != nullptr) *remote_delay_report_ns = 0;
    if (total_bytes_read != nullptr) *total_bytes_read = total_bytes_read_;
    if (data_position != nullptr) *data_position = data_position_;
    return true;
  }

  void MetadataChanged(const source_metadata_t& source_metadata) override {
    auto track_count = source_metadata.track_count;
    auto tracks = source_metadata.tracks;
    LOG(INFO) << __func__ << ": " << track_count << " track(s) received";
    while (track_count) {
      VLOG(1) << __func__ << ": usage=" << tracks->usage
              << ", content_type=" << tracks->content_type
              << ", gain=" << tracks->gain;
      --track_count;
      ++tracks;
    }
  }

  void ResetPresentationPosition() override {
    total_bytes_read_ = 0;
    data_position_ = {};
  }

  void LogBytesRead(size_t bytes_read) override {
    if (bytes_read) {
      total_bytes_read_ += bytes_read;
      clock_gettime(CLOCK_MONOTONIC, &data_position_);
    }
  }

 private:
  StreamCallbacks stream_cb_;
  uint64_t total_bytes_read_;
  timespec data_position_;
};

bool HearingAidGetSelectedHalPcmConfig(PcmParameters* hal_pcm_config) {
  if (hal_pcm_config == nullptr) return false;
  // TODO: we only support one config for now!
  hal_pcm_config->sampleRate = SampleRate::RATE_16000;
  hal_pcm_config->bitsPerSample = BitsPerSample::BITS_16;
  hal_pcm_config->channelMode = ChannelMode::STEREO;
  return true;
}

// Sink instance of Hearing Aids to provide call-in APIs for Bluetooth Audio Hal
HearingAidTransport* hearing_aid_sink = nullptr;
// Common interface to call-out into Bluetooth Audio Hal
bluetooth::audio::BluetoothAudioClientInterface*
    hearing_aid_hal_clientinterface = nullptr;
bool btaudio_hearing_aid_supported = false;
bool is_configured = false;

}  // namespace

namespace bluetooth {
namespace audio {
namespace hearing_aid {

bool is_hal_2_0_supported() {
  if (!is_configured) {
    btaudio_hearing_aid_supported =
        osi_property_get_bool(BLUETOOTH_AUDIO_PROP_ENABLED, false);
    is_configured = true;
  }
  return btaudio_hearing_aid_supported;
}

bool is_hal_2_0_enabled() { return hearing_aid_hal_clientinterface != nullptr; }

bool init(StreamCallbacks stream_cb,
          bluetooth::common::MessageLoopThread* message_loop) {
  LOG(INFO) << __func__;

  if (!is_hal_2_0_supported()) return false;

  hearing_aid_sink = new HearingAidTransport(std::move(stream_cb));
  hearing_aid_hal_clientinterface =
      new bluetooth::audio::BluetoothAudioClientInterface(hearing_aid_sink,
                                                          message_loop);
  return true;
}

void cleanup() {
  LOG(INFO) << __func__;
  if (!is_hal_2_0_enabled()) return;
  end_session();
  hearing_aid_hal_clientinterface = nullptr;
  hearing_aid_sink = nullptr;
}

void start_session() {
  LOG(INFO) << __func__;
  if (!is_hal_2_0_enabled()) return;
  AudioConfiguration audio_config;
  PcmParameters pcm_config{};
  if (!HearingAidGetSelectedHalPcmConfig(&pcm_config)) {
    LOG(ERROR) << __func__ << ": cannot get PCM config";
    return;
  }
  audio_config.pcmConfig(pcm_config);
  if (!hearing_aid_hal_clientinterface->UpdateAudioConfig(audio_config)) {
    LOG(ERROR) << __func__ << ": cannot update audio config to HAL";
    return;
  }
  hearing_aid_hal_clientinterface->StartSession();
}

void end_session() {
  LOG(INFO) << __func__;
  if (!is_hal_2_0_enabled()) return;
  hearing_aid_hal_clientinterface->EndSession();
}

size_t read(uint8_t* p_buf, uint32_t len) {
  if (!is_hal_2_0_enabled()) return 0;
  return hearing_aid_hal_clientinterface->ReadAudioData(p_buf, len);
}

}  // namespace hearing_aid
}  // namespace audio
}  // namespace bluetooth
+52 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright 2019 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 <functional>
#include "common/message_loop_thread.h"

namespace bluetooth {
namespace audio {
namespace hearing_aid {

struct StreamCallbacks {
  std::function<bool(bool start_media_task)> on_resume_;
  std::function<bool(void)> on_suspend_;
};

bool is_hal_2_0_supported();

// Check if new bluetooth_audio is enabled
bool is_hal_2_0_enabled();

// Initialize BluetoothAudio HAL: openProvider
bool init(StreamCallbacks stream_cb,
          bluetooth::common::MessageLoopThread* message_loop);

// Clean up BluetoothAudio HAL
void cleanup();

// Send command to the BluetoothAudio HAL: StartSession, EndSession
void start_session();
void end_session();

// Read from the FMQ of BluetoothAudio HAL
size_t read(uint8_t* p_buf, uint32_t len);

}  // namespace hearing_aid
}  // namespace audio
}  // namespace bluetooth
+1 −0
Original line number Original line Diff line number Diff line
@@ -135,6 +135,7 @@ cc_test {
    static_libs: [
    static_libs: [
        "libbtcore",
        "libbtcore",
        "libbt-bta",
        "libbt-bta",
        "libbt-audio-hal-interface",
        "libbluetooth-types",
        "libbluetooth-types",
        "libbt-protos-lite",
        "libbt-protos-lite",
        "libosi",
        "libosi",
+8 −2
Original line number Original line Diff line number Diff line
@@ -901,9 +901,9 @@ class HearingAidImpl : public HearingAid {
    if (encoder_state_left != nullptr) {
    if (encoder_state_left != nullptr) {
      g722_encode_release(encoder_state_left);
      g722_encode_release(encoder_state_left);
      g722_encode_release(encoder_state_right);
      g722_encode_release(encoder_state_right);
    }
    encoder_state_left = g722_encode_init(nullptr, 64000, G722_PACKED);
    encoder_state_left = g722_encode_init(nullptr, 64000, G722_PACKED);
    encoder_state_right = g722_encode_init(nullptr, 64000, G722_PACKED);
    encoder_state_right = g722_encode_init(nullptr, 64000, G722_PACKED);
    }
    seq_counter = 0;
    seq_counter = 0;


    for (auto& device : hearingDevices.devices) {
    for (auto& device : hearingDevices.devices) {
@@ -991,6 +991,12 @@ class HearingAidImpl : public HearingAid {


    if (left == nullptr && right == nullptr) {
    if (left == nullptr && right == nullptr) {
      HearingAidAudioSource::Stop();
      HearingAidAudioSource::Stop();
      if (encoder_state_left != nullptr) {
        g722_encode_release(encoder_state_left);
        encoder_state_left = nullptr;
        g722_encode_release(encoder_state_right);
        encoder_state_right = nullptr;
      }
      current_volume = VOLUME_UNKNOWN;
      current_volume = VOLUME_UNKNOWN;
      return;
      return;
    }
    }
Loading