/*
 * 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.
 */

#include "device_port_proxy.h"
#define LOG_TAG "BTAudioHalStream"

#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <inttypes.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include <memory>

#include "BluetoothAudioSession.h"
#include "stream_apis.h"
#include "utils.h"

using ::android::base::StringPrintf;
using ::android::bluetooth::audio::utils::FrameCount;
using ::android::bluetooth::audio::utils::GetAudioParamString;
using ::android::bluetooth::audio::utils::ParseAudioParams;

namespace {

constexpr unsigned int kMinimumDelayMs = 50;
constexpr unsigned int kMaximumDelayMs = 1000;
constexpr int kExtraAudioSyncMs = 200;

std::ostream& operator<<(std::ostream& os, const audio_config& config) {
  return os << "audio_config[sample_rate=" << config.sample_rate
            << ", channels=" << StringPrintf("%#x", config.channel_mask)
            << ", format=" << config.format << "]";
}

void out_calculate_feeding_delay_ms(const BluetoothStreamOut* out, uint32_t* latency_ms,
                                    uint64_t* frames = nullptr,
                                    struct timespec* timestamp = nullptr) {
  if (latency_ms == nullptr && frames == nullptr && timestamp == nullptr) {
    return;
  }

  // delay_report is the audio delay from the remote headset receiving data to
  // the headset playing sound in units of nanoseconds
  uint64_t delay_report_ns = 0;
  uint64_t delay_report_ms = 0;
  // absorbed_bytes is the total number of bytes sent by the Bluetooth stack to
  // a remote headset
  uint64_t absorbed_bytes = 0;
  // absorbed_timestamp is the ...
  struct timespec absorbed_timestamp = {};
  bool timestamp_fetched = false;

  std::unique_lock<std::mutex> lock(out->mutex_);
  if (out->bluetooth_output_->GetPresentationPosition(&delay_report_ns, &absorbed_bytes,
                                                      &absorbed_timestamp)) {
    delay_report_ms = delay_report_ns / 1000000;
    // assume kMinimumDelayMs (50ms) < delay_report_ns < kMaximumDelayMs
    // (1000ms), or it is invalid / ignored and use old delay calculated
    // by ourselves.
    if (delay_report_ms > kMinimumDelayMs && delay_report_ms < kMaximumDelayMs) {
      timestamp_fetched = true;
    } else if (delay_report_ms >= kMaximumDelayMs) {
      LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                << ", delay_report=" << delay_report_ns << "ns abnormal";
    }
  }
  if (!timestamp_fetched) {
    // default to old delay if any failure is found when fetching from ports
    // audio_a2dp_hw:
    //   frames_count = buffer_size / frame_size
    //   latency (sec.) = frames_count / samples_per_second (sample_rate)
    // Sync from audio_a2dp_hw to add extra delay kExtraAudioSyncMs(+200ms)
    delay_report_ms = out->frames_count_ * 1000 / out->sample_rate_ + kExtraAudioSyncMs;
    if (timestamp != nullptr) {
      clock_gettime(CLOCK_MONOTONIC, &absorbed_timestamp);
    }
    LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                 << " uses the legacy delay " << delay_report_ms << " ms";
  }
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << ", delay=" << delay_report_ms << "ms, data=" << absorbed_bytes
               << " bytes, timestamp=" << absorbed_timestamp.tv_sec << "."
               << StringPrintf("%09ld", absorbed_timestamp.tv_nsec) << "s";

  if (latency_ms != nullptr) {
    *latency_ms = delay_report_ms;
  }
  if (frames != nullptr) {
    const uint64_t latency_frames = delay_report_ms * out->sample_rate_ / 1000;
    *frames = absorbed_bytes / audio_stream_out_frame_size(&out->stream_out_);
    if (out->frames_presented_ < *frames) {
      // Are we (the audio HAL) reset?! The stack counter is obsoleted.
      *frames = out->frames_presented_;
    } else if ((out->frames_presented_ - *frames) > latency_frames) {
      // Is the Bluetooth output reset / restarted by AVDTP reconfig?! Its
      // counter was reset but could not be used.
      *frames = out->frames_presented_;
    }
    // suppose frames would be queued in the headset buffer for delay_report
    // period, so those frames in buffers should not be included in the number
    // of presented frames at the timestamp.
    if (*frames > latency_frames) {
      *frames -= latency_frames;
    } else {
      *frames = 0;
    }
  }
  if (timestamp != nullptr) {
    *timestamp = absorbed_timestamp;
  }
}

void in_calculate_starving_delay_ms(const BluetoothStreamIn* in, int64_t* frames, int64_t* time) {
  // delay_report is the audio delay from the remote headset receiving data to
  // the headset playing sound in units of nanoseconds
  uint64_t delay_report_ns = 0;
  uint64_t delay_report_ms = 0;
  // dispersed_bytes is the total number of bytes received by the Bluetooth
  // stack from a remote headset
  uint64_t dispersed_bytes = 0;
  struct timespec dispersed_timestamp = {};

  std::unique_lock<std::mutex> lock(in->mutex_);
  in->bluetooth_input_->GetPresentationPosition(&delay_report_ns, &dispersed_bytes,
                                                &dispersed_timestamp);
  delay_report_ms = delay_report_ns / 1000000;

  const uint64_t latency_frames = delay_report_ms * in->sample_rate_ / 1000;
  *frames = dispersed_bytes / audio_stream_in_frame_size(&in->stream_in_);

  LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
               << ", delay=" << delay_report_ms << "ms, data=" << dispersed_bytes
               << " bytes, timestamp=" << dispersed_timestamp.tv_sec << "."
               << StringPrintf("%09ld", dispersed_timestamp.tv_nsec) << "s";

  if (in->frames_presented_ < *frames) {
    // Was audio HAL reset?! The stack counter is obsoleted.
    *frames = in->frames_presented_;
  } else if ((in->frames_presented_ - *frames) > latency_frames) {
    // Is the Bluetooth input reset ?! Its counter was reset but could not be
    // used.
    *frames = in->frames_presented_;
  }
  // suppose frames would be queued in the headset buffer for delay_report
  // period, so those frames in buffers should not be included in the number
  // of presented frames at the timestamp.
  if (*frames > latency_frames) {
    *frames -= latency_frames;
  } else {
    *frames = 0;
  }

  *time = (dispersed_timestamp.tv_sec * 1000000000LL + dispersed_timestamp.tv_nsec) / 1000;
}

}  // namespace

std::ostream& operator<<(std::ostream& os, const BluetoothStreamState& state) {
  switch (state) {
    case BluetoothStreamState::DISABLED:
      return os << "DISABLED";
    case BluetoothStreamState::STANDBY:
      return os << "STANDBY";
    case BluetoothStreamState::STARTING:
      return os << "STARTING";
    case BluetoothStreamState::STARTED:
      return os << "STARTED";
    case BluetoothStreamState::SUSPENDING:
      return os << "SUSPENDING";
    case BluetoothStreamState::UNKNOWN:
      return os << "UNKNOWN";
    default:
      return os << StringPrintf("%#x", static_cast<unsigned>(state));
  }
}

static uint32_t out_get_sample_rate(const struct audio_stream* stream) {
  const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
  audio_config_t audio_cfg;
  if (out->bluetooth_output_->LoadAudioConfig(&audio_cfg)) {
    LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                 << " audio_cfg=" << audio_cfg;
    return audio_cfg.sample_rate;
  } else {
    LOG(WARNING) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                 << ", sample_rate=" << out->sample_rate_ << " failed";
    return out->sample_rate_;
  }
}

static int out_set_sample_rate(struct audio_stream* stream, uint32_t rate) {
  auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << ", sample_rate=" << out->sample_rate_;
  return rate == out->sample_rate_ ? 0 : -1;
}

static size_t out_get_buffer_size(const struct audio_stream* stream) {
  const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
  size_t buffer_size = out->frames_count_ * audio_stream_out_frame_size(&out->stream_out_);
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << ", buffer_size=" << buffer_size;
  return buffer_size;
}

static audio_channel_mask_t out_get_channels(const struct audio_stream* stream) {
  const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
  audio_config_t audio_cfg;
  if (out->bluetooth_output_->LoadAudioConfig(&audio_cfg)) {
    LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                 << " audio_cfg=" << audio_cfg;
    return audio_cfg.channel_mask;
  } else {
    LOG(WARNING) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                 << ", channels=" << StringPrintf("%#x", out->channel_mask_) << " failure";
    return out->channel_mask_;
  }
}

static audio_format_t out_get_format(const struct audio_stream* stream) {
  const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
  audio_config_t audio_cfg;
  if (out->bluetooth_output_->LoadAudioConfig(&audio_cfg)) {
    LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                 << " audio_cfg=" << audio_cfg;
    return audio_cfg.format;
  } else {
    LOG(WARNING) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                 << ", format=" << out->format_ << " failure";
    return out->format_;
  }
}

static int out_set_format(struct audio_stream* stream, audio_format_t format) {
  auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << ", format=" << out->format_;
  return format == out->format_ ? 0 : -1;
}

static int out_standby(struct audio_stream* stream) {
  auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
  std::unique_lock<std::mutex> lock(out->mutex_);
  int retval = 0;

  // out->last_write_time_us_ = 0; unnecessary as a stale write time has same
  // effect
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << " being standby (suspend)";
  if (out->bluetooth_output_->GetState() == BluetoothStreamState::STARTED) {
    out->frames_rendered_ = 0;
    retval = (out->bluetooth_output_->Suspend() ? 0 : -EIO);
  } else if (out->bluetooth_output_->GetState() == BluetoothStreamState::STARTING ||
             out->bluetooth_output_->GetState() == BluetoothStreamState::SUSPENDING) {
    LOG(WARNING) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                 << " NOT ready to be standby";
    retval = -EBUSY;
  } else {
    LOG(DEBUG) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << " standby already";
  }
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << " standby (suspend) retval=" << retval;

  return retval;
}

static int out_dump(const struct audio_stream* stream, int /*fd*/) {
  const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState();
  return 0;
}

static int out_set_parameters(struct audio_stream* stream, const char* kvpairs) {
  auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
  std::unique_lock<std::mutex> lock(out->mutex_);
  int retval = 0;

  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState() << ", kvpairs=["
               << kvpairs << "]";

  std::unordered_map<std::string, std::string> params = ParseAudioParams(kvpairs);
  if (params.empty()) {
    return retval;
  }

  LOG(VERBOSE) << __func__ << ": ParamsMap=[" << GetAudioParamString(params) << "]";

  audio_config_t audio_cfg;
  if (params.find(AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES) != params.end() ||
      params.find(AUDIO_PARAMETER_STREAM_SUP_CHANNELS) != params.end() ||
      params.find(AUDIO_PARAMETER_STREAM_SUP_FORMATS) != params.end()) {
    if (out->bluetooth_output_->LoadAudioConfig(&audio_cfg)) {
      out->sample_rate_ = audio_cfg.sample_rate;
      out->channel_mask_ = audio_cfg.channel_mask;
      out->format_ = audio_cfg.format;
      LOG(VERBOSE) << "state=" << out->bluetooth_output_->GetState()
                   << ", sample_rate=" << out->sample_rate_
                   << ", channels=" << StringPrintf("%#x", out->channel_mask_)
                   << ", format=" << out->format_;
    } else {
      LOG(WARNING) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                   << " failed to get audio config";
    }
  }

  if (params.find(AUDIO_PARAMETER_STREAM_ROUTING) != params.end()) {
    auto routing_param = params.find("routing");
    LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState() << ", stream param '"
              << routing_param->first.c_str() << "=" << routing_param->second.c_str() << "'";
  }

  if (params.find("A2dpSuspended") != params.end() && out->bluetooth_output_->IsA2dp()) {
    if (params["A2dpSuspended"] == "true") {
      LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                << " stream param stopped";
      out->frames_rendered_ = 0;
      if (out->bluetooth_output_->GetState() == BluetoothStreamState::STARTED) {
        out->bluetooth_output_->Suspend();
        out->bluetooth_output_->SetState(BluetoothStreamState::DISABLED);
      } else if (out->bluetooth_output_->GetState() != BluetoothStreamState::DISABLED) {
        out->bluetooth_output_->Stop();
      }
    } else {
      LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                << " stream param standby";
      if (out->bluetooth_output_->GetState() == BluetoothStreamState::DISABLED) {
        out->bluetooth_output_->SetState(BluetoothStreamState::STANDBY);
      }
    }
  }

  if (params.find("LeAudioSuspended") != params.end() && out->bluetooth_output_->IsLeAudio()) {
    LOG(INFO) << __func__
              << ": LeAudioSuspended found LEAudio=" << out->bluetooth_output_->IsLeAudio();
    if (params["LeAudioSuspended"] == "true") {
      LOG(INFO) << __func__
                << ": LeAudioSuspended true, state=" << out->bluetooth_output_->GetState()
                << " stream param standby";
      if (out->bluetooth_output_->GetState() == BluetoothStreamState::STARTED) {
        LOG(INFO) << __func__ << ": Stream is started, suspending LE Audio";
      } else if (out->bluetooth_output_->GetState() != BluetoothStreamState::DISABLED) {
        LOG(INFO) << __func__ << ": Stream is disabled, suspending LE Audio";
      }
    } else {
      LOG(INFO) << __func__
                << ": LeAudioSuspended false, state=" << out->bluetooth_output_->GetState()
                << " stream param standby";
      if (out->bluetooth_output_->GetState() == BluetoothStreamState::DISABLED) {
        LOG(INFO) << __func__ << ": Stream is disabled, unsuspending LE Audio";
      }
    }
  }

  if (params.find(AUDIO_PARAMETER_KEY_CLOSING) != params.end()) {
    if (params[AUDIO_PARAMETER_KEY_CLOSING] == "true") {
      LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                << " stream param closing, disallow any writes?";
      if (out->bluetooth_output_->GetState() != BluetoothStreamState::DISABLED) {
        out->frames_rendered_ = 0;
        out->frames_presented_ = 0;
        out->bluetooth_output_->Stop();
      }
    }
  }

  if (params.find(AUDIO_PARAMETER_KEY_EXITING) != params.end()) {
    if (params[AUDIO_PARAMETER_KEY_EXITING] == "1") {
      LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                << " stream param exiting";
      if (out->bluetooth_output_->GetState() != BluetoothStreamState::DISABLED) {
        out->frames_rendered_ = 0;
        out->frames_presented_ = 0;
        out->bluetooth_output_->Stop();
      }
    }
  }

  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState() << ", kvpairs=["
               << kvpairs << "], retval=" << retval;
  return retval;
}

static char* out_get_parameters(const struct audio_stream* stream, const char* keys) {
  const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
  std::unique_lock<std::mutex> lock(out->mutex_);

  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState() << ", keys=[" << keys
               << "]";

  std::unordered_map<std::string, std::string> params = ParseAudioParams(keys);
  if (params.empty()) {
    return strdup("");
  }

  audio_config_t audio_cfg;
  if (out->bluetooth_output_->LoadAudioConfig(&audio_cfg)) {
    LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                 << " audio_cfg=" << audio_cfg;
  } else {
    LOG(ERROR) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << " failed to get audio config";
  }

  std::unordered_map<std::string, std::string> return_params;
  if (params.find(AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES) != params.end()) {
    std::string param;
    if (audio_cfg.sample_rate == 16000) {
      param = "16000";
    }
    if (audio_cfg.sample_rate == 24000) {
      param = "24000";
    }
    if (audio_cfg.sample_rate == 44100) {
      param = "44100";
    }
    if (audio_cfg.sample_rate == 48000) {
      param = "48000";
    }
    if (audio_cfg.sample_rate == 88200) {
      param = "88200";
    }
    if (audio_cfg.sample_rate == 96000) {
      param = "96000";
    }
    if (audio_cfg.sample_rate == 176400) {
      param = "176400";
    }
    if (audio_cfg.sample_rate == 192000) {
      param = "192000";
    }
    return_params[AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES] = param;
  }

  if (params.find(AUDIO_PARAMETER_STREAM_SUP_CHANNELS) != params.end()) {
    std::string param;
    if (audio_cfg.channel_mask == AUDIO_CHANNEL_OUT_MONO) {
      param = "AUDIO_CHANNEL_OUT_MONO";
    }
    if (audio_cfg.channel_mask == AUDIO_CHANNEL_OUT_STEREO) {
      param = "AUDIO_CHANNEL_OUT_STEREO";
    }
    return_params[AUDIO_PARAMETER_STREAM_SUP_CHANNELS] = param;
  }

  if (params.find(AUDIO_PARAMETER_STREAM_SUP_FORMATS) != params.end()) {
    std::string param;
    if (audio_cfg.format == AUDIO_FORMAT_PCM_16_BIT) {
      param = "AUDIO_FORMAT_PCM_16_BIT";
    }
    if (audio_cfg.format == AUDIO_FORMAT_PCM_24_BIT_PACKED) {
      param = "AUDIO_FORMAT_PCM_24_BIT_PACKED";
    }
    if (audio_cfg.format == AUDIO_FORMAT_PCM_8_24_BIT) {
      param = "AUDIO_FORMAT_PCM_8_24_BIT";
    }
    if (audio_cfg.format == AUDIO_FORMAT_PCM_32_BIT) {
      param = "AUDIO_FORMAT_PCM_32_BIT";
    }
    return_params[AUDIO_PARAMETER_STREAM_SUP_FORMATS] = param;
  }

  std::string result;
  for (const auto& ptr : return_params) {
    result += ptr.first + "=" + ptr.second + ";";
  }

  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState() << ", result=["
               << result << "]";
  return strdup(result.c_str());
}

static uint32_t out_get_latency_ms(const struct audio_stream_out* stream) {
  const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
  uint32_t latency_ms = 0;
  out_calculate_feeding_delay_ms(out, &latency_ms);
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << ", latency=" << latency_ms << "ms";
  return latency_ms;
}

static int out_set_volume(struct audio_stream_out* stream, float left, float right) {
  auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState() << ", Left=" << left
               << ", Right=" << right;
  return -1;
}

static ssize_t out_write(struct audio_stream_out* stream, const void* buffer, size_t bytes) {
  auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
  std::unique_lock<std::mutex> lock(out->mutex_);
  size_t totalWritten = 0;

  if (out->bluetooth_output_->GetState() != BluetoothStreamState::STARTED) {
    LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
              << " first time bytes=" << bytes;
    lock.unlock();
    if (stream->resume(stream)) {
      LOG(ERROR) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                 << " failed to resume";
      if (out->bluetooth_output_->GetState() == BluetoothStreamState::DISABLED) {
        // drop data for cases of A2dpSuspended=true / closing=true
        totalWritten = bytes;
      }
      usleep(out->preferred_data_interval_us);
      return totalWritten;
    }
    lock.lock();
  }
  lock.unlock();
  totalWritten = out->bluetooth_output_->WriteData(buffer, bytes);
  lock.lock();

  struct timespec ts = {.tv_sec = 0, .tv_nsec = 0};
  clock_gettime(CLOCK_MONOTONIC, &ts);
  if (totalWritten) {
    const size_t frames = bytes / audio_stream_out_frame_size(stream);
    out->frames_rendered_ += frames;
    out->frames_presented_ += frames;
    out->last_write_time_us_ = (ts.tv_sec * 1000000000LL + ts.tv_nsec) / 1000;
  } else {
    const int64_t now = (ts.tv_sec * 1000000000LL + ts.tv_nsec) / 1000;
    const int64_t elapsed_time_since_last_write = now - out->last_write_time_us_;
    // frames_count = written_data / frame_size
    // play_time (ms) = frames_count / (sample_rate (Sec.) / 1000000)
    // sleep_time (ms) = play_time - elapsed_time
    int64_t sleep_time = bytes * 1000000LL / audio_stream_out_frame_size(stream) /
                                 out_get_sample_rate(&stream->common) -
                         elapsed_time_since_last_write;
    if (sleep_time > 0) {
      LOG(VERBOSE) << __func__ << ": sleep " << (sleep_time / 1000)
                   << " ms when writting FMQ datapath";
      lock.unlock();
      usleep(sleep_time);
      lock.lock();
    } else {
      // we don't sleep when we exit standby (this is typical for a real alsa
      // buffer).
      sleep_time = 0;
    }
    out->last_write_time_us_ = now + sleep_time;
  }
  return totalWritten;
}

static int out_get_render_position(const struct audio_stream_out* stream, uint32_t* dsp_frames) {
  if (dsp_frames == nullptr) {
    return -EINVAL;
  }

  const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
  // frames = (latency (ms) / 1000) * samples_per_second (sample_rate)
  const uint64_t latency_frames = (uint64_t)out_get_latency_ms(stream) * out->sample_rate_ / 1000;
  if (out->frames_rendered_ >= latency_frames) {
    *dsp_frames = (uint32_t)(out->frames_rendered_ - latency_frames);
  } else {
    *dsp_frames = 0;
  }

  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << ", dsp_frames=" << *dsp_frames;
  return 0;
}

static int out_add_audio_effect(const struct audio_stream* stream, effect_handle_t effect) {
  const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << ", effect=" << effect;
  return 0;
}

static int out_remove_audio_effect(const struct audio_stream* stream, effect_handle_t effect) {
  const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << ", effect=" << effect;
  return 0;
}

static int out_get_next_write_timestamp(const struct audio_stream_out* stream, int64_t* timestamp) {
  const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
  *timestamp = 0;
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << ", timestamp=" << *timestamp;
  return -EINVAL;
}

static int out_pause(struct audio_stream_out* stream) {
  auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
  std::unique_lock<std::mutex> lock(out->mutex_);
  int retval = 0;
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << ", pausing (suspend)";
  if (out->bluetooth_output_->GetState() == BluetoothStreamState::STARTED) {
    out->frames_rendered_ = 0;
    retval = (out->bluetooth_output_->Suspend() ? 0 : -EIO);
  } else if (out->bluetooth_output_->GetState() == BluetoothStreamState::STARTING ||
             out->bluetooth_output_->GetState() == BluetoothStreamState::SUSPENDING) {
    LOG(WARNING) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                 << " NOT ready to pause?!";
    retval = -EBUSY;
  } else {
    LOG(DEBUG) << __func__ << ": state=" << out->bluetooth_output_->GetState() << " paused already";
  }
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << ", pausing (suspend) retval=" << retval;

  return retval;
}

static int out_resume(struct audio_stream_out* stream) {
  auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
  std::unique_lock<std::mutex> lock(out->mutex_);
  int retval = 0;

  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << ", resuming (start)";
  if (out->bluetooth_output_->GetState() == BluetoothStreamState::STANDBY) {
    retval = (out->bluetooth_output_->Start() ? 0 : -EIO);
  } else if (out->bluetooth_output_->GetState() == BluetoothStreamState::STARTING ||
             out->bluetooth_output_->GetState() == BluetoothStreamState::SUSPENDING) {
    LOG(WARNING) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                 << " NOT ready to resume?!";
    retval = -EBUSY;
  } else if (out->bluetooth_output_->GetState() == BluetoothStreamState::DISABLED) {
    LOG(WARNING) << __func__ << ": state=" << out->bluetooth_output_->GetState()
                 << " NOT allow to resume?!";
    retval = -EINVAL;
  } else {
    LOG(DEBUG) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << " resumed already";
  }
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << ", resuming (start) retval=" << retval;

  return retval;
}

static int out_get_presentation_position(const struct audio_stream_out* stream, uint64_t* frames,
                                         struct timespec* timestamp) {
  if (frames == nullptr || timestamp == nullptr) {
    return -EINVAL;
  }

  const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
  out_calculate_feeding_delay_ms(out, nullptr, frames, timestamp);
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << ", frames=" << *frames << ", timestamp=" << timestamp->tv_sec << "."
               << StringPrintf("%09ld", timestamp->tv_nsec) << "s";
  return 0;
}

static void out_update_source_metadata_v7(struct audio_stream_out* stream,
                                          const struct source_metadata_v7* source_metadata_v7) {
  auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
  std::unique_lock<std::mutex> lock(out->mutex_);
  if (source_metadata_v7 == nullptr || source_metadata_v7->track_count == 0) {
    return;
  }
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState() << ", "
               << source_metadata_v7->track_count << " track(s)";
  if (!out->is_aidl) {
    struct source_metadata source_metadata;
    source_metadata.track_count = source_metadata_v7->track_count;
    struct playback_track_metadata playback_track;
    playback_track_metadata_from_v7(&playback_track, source_metadata_v7->tracks);
    source_metadata.tracks = &playback_track;

    static_cast<::android::bluetooth::audio::hidl::BluetoothAudioPortHidl*>(
            out->bluetooth_output_.get())
            ->UpdateTracksMetadata(&source_metadata);
  } else {
    static_cast<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl*>(
            out->bluetooth_output_.get())
            ->UpdateSourceMetadata(source_metadata_v7);
  }
}

int adev_open_output_stream(struct audio_hw_device* dev, audio_io_handle_t /*handle*/,
                            audio_devices_t devices, audio_output_flags_t flags,
                            struct audio_config* config, struct audio_stream_out** stream_out,
                            const char* address __unused) {
  *stream_out = nullptr;
  auto out = std::make_unique<BluetoothStreamOut>();
  if (::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession::IsAidlAvailable()) {
    out->bluetooth_output_ =
            std::make_unique<::android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut>();
    out->is_aidl = true;
  } else {
    out->bluetooth_output_ =
            std::make_unique<::android::bluetooth::audio::hidl::BluetoothAudioPortHidlOut>();
    out->is_aidl = false;
  }
  if (!out->bluetooth_output_->SetUp(devices)) {
    out->bluetooth_output_ = nullptr;
    LOG(ERROR) << __func__ << ": cannot init HAL";
    return -EINVAL;
  }
  LOG(VERBOSE) << __func__ << ": device=" << StringPrintf("%#x", devices);

  out->stream_out_.common.get_sample_rate = out_get_sample_rate;
  out->stream_out_.common.set_sample_rate = out_set_sample_rate;
  out->stream_out_.common.get_buffer_size = out_get_buffer_size;
  out->stream_out_.common.get_channels = out_get_channels;
  out->stream_out_.common.get_format = out_get_format;
  out->stream_out_.common.set_format = out_set_format;
  out->stream_out_.common.standby = out_standby;
  out->stream_out_.common.dump = out_dump;
  out->stream_out_.common.set_parameters = out_set_parameters;
  out->stream_out_.common.get_parameters = out_get_parameters;
  out->stream_out_.common.add_audio_effect = out_add_audio_effect;
  out->stream_out_.common.remove_audio_effect = out_remove_audio_effect;
  out->stream_out_.get_latency = out_get_latency_ms;
  out->stream_out_.set_volume = out_set_volume;
  out->stream_out_.write = out_write;
  out->stream_out_.get_render_position = out_get_render_position;
  out->stream_out_.get_next_write_timestamp = out_get_next_write_timestamp;
  out->stream_out_.pause = out_pause;
  out->stream_out_.resume = out_resume;
  out->stream_out_.get_presentation_position = out_get_presentation_position;
  out->stream_out_.update_source_metadata_v7 = out_update_source_metadata_v7;
  /** Fix Coverity Scan Issue @{ */
  out->channel_mask_ = AUDIO_CHANNEL_NONE;
  /** @} */

  if (!out->bluetooth_output_->LoadAudioConfig(config)) {
    LOG(ERROR) << __func__ << ": state=" << out->bluetooth_output_->GetState()
               << " failed to get audio config";
  }
  // WAR to support Mono / 16 bits per sample as the Bluetooth stack required
  if (config->channel_mask == AUDIO_CHANNEL_OUT_MONO && config->format == AUDIO_FORMAT_PCM_16_BIT) {
    LOG(INFO) << __func__ << ": force channels=" << StringPrintf("%#x", out->channel_mask_)
              << " to be AUDIO_CHANNEL_OUT_STEREO";
    out->bluetooth_output_->ForcePcmStereoToMono(true);
    config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
  }
  out->sample_rate_ = config->sample_rate;
  out->channel_mask_ = config->channel_mask;
  out->format_ = config->format;
  // frame is number of samples per channel

  size_t preferred_data_interval_us = kBluetoothDefaultOutputBufferMs * 1000;
  if (out->bluetooth_output_->GetPreferredDataIntervalUs(&preferred_data_interval_us) &&
      preferred_data_interval_us != 0) {
    out->preferred_data_interval_us = preferred_data_interval_us;
  } else {
    out->preferred_data_interval_us = kBluetoothDefaultOutputBufferMs * 1000;
  }

  // Ensure minimum buffer duration for spatialized output
  if ((flags == (AUDIO_OUTPUT_FLAG_FAST | AUDIO_OUTPUT_FLAG_DEEP_BUFFER) ||
       flags == AUDIO_OUTPUT_FLAG_SPATIALIZER) &&
      out->preferred_data_interval_us < kBluetoothSpatializerOutputBufferMs * 1000) {
    out->preferred_data_interval_us = kBluetoothSpatializerOutputBufferMs * 1000;
    LOG(INFO) << __func__ << ": adjusting to minimum buffer duration for spatializer: "
              << StringPrintf("%zu", out->preferred_data_interval_us);
  }

  out->frames_count_ = FrameCount(out->preferred_data_interval_us, out->sample_rate_);

  out->frames_rendered_ = 0;
  out->frames_presented_ = 0;
  out->last_write_time_us_ = 0;

  BluetoothStreamOut* out_ptr = out.release();
  {
    auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(dev);
    std::lock_guard<std::mutex> guard(bluetooth_device->mutex_);
    bluetooth_device->opened_stream_outs_.push_back(out_ptr);
  }

  *stream_out = &out_ptr->stream_out_;
  LOG(INFO) << __func__ << ": state=" << out_ptr->bluetooth_output_->GetState()
            << ", sample_rate=" << out_ptr->sample_rate_
            << ", channels=" << StringPrintf("%#x", out_ptr->channel_mask_)
            << ", format=" << out_ptr->format_
            << ", preferred_data_interval_us=" << out_ptr->preferred_data_interval_us
            << ", frames=" << out_ptr->frames_count_;
  return 0;
}

void adev_close_output_stream(struct audio_hw_device* dev, struct audio_stream_out* stream) {
  auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState() << ", stopping";
  {
    auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(dev);
    std::lock_guard<std::mutex> guard(bluetooth_device->mutex_);
    bluetooth_device->opened_stream_outs_.remove(out);
  }
  if (out->bluetooth_output_->GetState() != BluetoothStreamState::DISABLED) {
    out->frames_rendered_ = 0;
    out->frames_presented_ = 0;
    out->bluetooth_output_->Stop();
  }
  out->bluetooth_output_->TearDown();
  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState() << ", stopped";
  delete out;
}

size_t adev_get_input_buffer_size(const struct audio_hw_device* /*dev*/,
                                  const struct audio_config* /*config*/) {
  /* TODO: Adjust this value */
  LOG(VERBOSE) << __func__;
  return 320;
}

static uint32_t in_get_sample_rate(const struct audio_stream* stream) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);

  return in->sample_rate_;
}

static int in_set_sample_rate(struct audio_stream* stream, uint32_t rate) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);

  LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
               << ", sample_rate=" << in->sample_rate_;
  return rate == in->sample_rate_ ? 0 : -ENOSYS;
}

static size_t in_get_buffer_size(const struct audio_stream* stream) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  size_t buffer_size = in->frames_count_ * audio_stream_in_frame_size(&in->stream_in_);
  LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
               << ", buffer_size=" << buffer_size;
  return buffer_size;
}

static audio_channel_mask_t in_get_channels(const struct audio_stream* stream) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  audio_config_t audio_cfg;
  if (in->bluetooth_input_->LoadAudioConfig(&audio_cfg)) {
    LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
                 << " audio_cfg=" << audio_cfg;
    return audio_cfg.channel_mask;
  } else {
    LOG(WARNING) << __func__ << ": state=" << in->bluetooth_input_->GetState()
                 << ", channels=" << StringPrintf("%#x", in->channel_mask_) << " failure";
    return in->channel_mask_;
  }
}

static audio_format_t in_get_format(const struct audio_stream* stream) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  audio_config_t audio_cfg;
  if (in->bluetooth_input_->LoadAudioConfig(&audio_cfg)) {
    LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
                 << " audio_cfg=" << audio_cfg;
    return audio_cfg.format;
  } else {
    LOG(WARNING) << __func__ << ": state=" << in->bluetooth_input_->GetState()
                 << ", format=" << in->format_ << " failure";
    return in->format_;
  }
}

static int in_set_format(struct audio_stream* stream, audio_format_t format) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
               << ", format=" << in->format_;
  return format == in->format_ ? 0 : -ENOSYS;
}

static bool in_state_transition_timeout(BluetoothStreamIn* in, std::unique_lock<std::mutex>& lock,
                                        const BluetoothStreamState& state, uint16_t timeout_ms) {
  /* Don't loose suspend request, AF will not retry */
  while (in->bluetooth_input_->GetState() == state) {
    lock.unlock();
    usleep(1000);
    lock.lock();

    /* Don't block AF forever */
    if (--timeout_ms <= 0) {
      LOG(WARNING) << __func__ << ", can't suspend - stucked in: " << static_cast<int>(state)
                   << " state";
      return false;
    }
  }

  return true;
}

static int in_standby(struct audio_stream* stream) {
  auto* in = reinterpret_cast<BluetoothStreamIn*>(stream);
  std::unique_lock<std::mutex> lock(in->mutex_);
  int retval = 0;

  LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
               << " being standby (suspend)";

  /* Give some time to start up */
  if (!in_state_transition_timeout(in, lock, BluetoothStreamState::STARTING,
                                   kBluetoothDefaultInputStateTimeoutMs)) {
    LOG(ERROR) << __func__ << ": state=" << in->bluetooth_input_->GetState()
               << " NOT ready to by standby";
    return retval;
  }

  if (in->bluetooth_input_->GetState() == BluetoothStreamState::STARTED) {
    retval = (in->bluetooth_input_->Suspend() ? 0 : -EIO);
  } else if (in->bluetooth_input_->GetState() != BluetoothStreamState::SUSPENDING) {
    LOG(DEBUG) << __func__ << ": state=" << in->bluetooth_input_->GetState() << " standby already";
    return retval;
  }

  /* Give some time to suspend */
  if (!in_state_transition_timeout(in, lock, BluetoothStreamState::SUSPENDING,
                                   kBluetoothDefaultInputStateTimeoutMs)) {
    LOG(ERROR) << __func__ << ": state=" << in->bluetooth_input_->GetState()
               << " NOT ready to by standby";
    return 0;
  }

  LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
               << " standby (suspend) retval=" << retval;

  return retval;
}

static int in_dump(const struct audio_stream* stream, int /*fd*/) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState();

  return 0;
}

static int in_set_parameters(struct audio_stream* stream, const char* kvpairs) {
  auto* in = reinterpret_cast<BluetoothStreamIn*>(stream);
  std::unique_lock<std::mutex> lock(in->mutex_);
  int retval = 0;

  LOG(INFO) << __func__ << ": NOT HANDLED! state=" << in->bluetooth_input_->GetState()
            << ", kvpairs=[" << kvpairs << "]";

  std::unordered_map<std::string, std::string> params = ParseAudioParams(kvpairs);

  if (params.empty()) {
    return retval;
  }

  LOG(INFO) << __func__ << ": ParamsMap=[" << GetAudioParamString(params) << "]";

  return retval;
}

static char* in_get_parameters(const struct audio_stream* stream, const char* keys) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  std::unique_lock<std::mutex> lock(in->mutex_);

  LOG(VERBOSE) << __func__ << ": NOT HANDLED! state=" << in->bluetooth_input_->GetState()
               << ", keys=[" << keys << "]";

  std::unordered_map<std::string, std::string> params = ParseAudioParams(keys);
  if (params.empty()) {
    return strdup("");
  }

  audio_config_t audio_cfg;
  if (in->bluetooth_input_->LoadAudioConfig(&audio_cfg)) {
    LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
                 << " audio_cfg=" << audio_cfg;
  } else {
    LOG(ERROR) << __func__ << ": state=" << in->bluetooth_input_->GetState()
               << " failed to get audio config";
  }

  std::unordered_map<std::string, std::string> return_params;

  /* TODO: Implement parameter getter */

  std::string result;
  for (const auto& ptr : return_params) {
    result += ptr.first + "=" + ptr.second + ";";
  }

  return strdup(result.c_str());
}

static int in_add_audio_effect(const struct audio_stream* stream, effect_handle_t effect) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
               << ", effect=" << effect;
  return 0;
}

static int in_remove_audio_effect(const struct audio_stream* stream, effect_handle_t effect) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
               << ", effect=" << effect;
  return 0;
}

static int in_set_gain(struct audio_stream_in* stream, float /*gain*/) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  LOG(VERBOSE) << __func__ << ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();

  return 0;
}

static ssize_t in_read(struct audio_stream_in* stream, void* buffer, size_t bytes) {
  auto* in = reinterpret_cast<BluetoothStreamIn*>(stream);
  std::unique_lock<std::mutex> lock(in->mutex_);
  size_t totalRead = 0;

  /* Give some time to start up */
  if (!in_state_transition_timeout(in, lock, BluetoothStreamState::STARTING,
                                   kBluetoothDefaultInputStateTimeoutMs)) {
    return -EBUSY;
  }

  if (in->bluetooth_input_->GetState() != BluetoothStreamState::STARTED) {
    LOG(INFO) << __func__ << ": state=" << in->bluetooth_input_->GetState()
              << " first time bytes=" << bytes;

    int retval = 0;
    LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState() << ", starting";
    if (in->bluetooth_input_->GetState() == BluetoothStreamState::STANDBY) {
      retval = (in->bluetooth_input_->Start() ? 0 : -EIO);
    } else if (in->bluetooth_input_->GetState() == BluetoothStreamState::SUSPENDING) {
      LOG(WARNING) << __func__ << ": state=" << in->bluetooth_input_->GetState()
                   << " NOT ready to start?!";
      retval = -EBUSY;
    } else if (in->bluetooth_input_->GetState() == BluetoothStreamState::DISABLED) {
      LOG(WARNING) << __func__ << ": state=" << in->bluetooth_input_->GetState()
                   << " NOT allow to start?!";
      retval = -EINVAL;
    } else {
      LOG(DEBUG) << __func__ << ": state=" << in->bluetooth_input_->GetState()
                 << " started already";
    }
    LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
                 << ", starting (start) retval=" << retval;

    if (retval) {
      LOG(ERROR) << __func__ << ": state=" << in->bluetooth_input_->GetState()
                 << " failed to start";
      return retval;
    }
  }

  lock.unlock();
  totalRead = in->bluetooth_input_->ReadData(buffer, bytes);
  lock.lock();

  struct timespec ts = {.tv_sec = 0, .tv_nsec = 0};
  clock_gettime(CLOCK_MONOTONIC, &ts);
  in->last_read_time_us_ = (ts.tv_sec * 1000000000LL + ts.tv_nsec) / 1000;

  const size_t frames = totalRead / audio_stream_in_frame_size(stream);
  in->frames_presented_ += frames;

  return totalRead;
}

static uint32_t in_get_input_frames_lost(struct audio_stream_in* stream) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  LOG(VERBOSE) << __func__ << ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();

  return 0;
}

static int in_get_capture_position(const struct audio_stream_in* stream, int64_t* frames,
                                   int64_t* time) {
  if (stream == NULL || frames == NULL || time == NULL) {
    return -EINVAL;
  }
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);

  if (in->bluetooth_input_->GetState() == BluetoothStreamState::STANDBY) {
    LOG(WARNING) << __func__ << ": state= " << in->bluetooth_input_->GetState();
    return -ENOSYS;
  }

  in_calculate_starving_delay_ms(in, frames, time);

  return 0;
}

static int in_start(const struct audio_stream_in* stream) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  LOG(VERBOSE) << __func__ << ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();

  return 0;
}

static int in_stop(const struct audio_stream_in* stream) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  LOG(VERBOSE) << __func__ << ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();

  return 0;
}

static int in_create_mmap_buffer(const struct audio_stream_in* stream, int32_t /*min_size_frames*/,
                                 struct audio_mmap_buffer_info* /*info*/) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  LOG(VERBOSE) << __func__ << ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();

  return -ENOSYS;
}

static int in_get_mmap_position(const struct audio_stream_in* stream,
                                struct audio_mmap_position* /*position*/) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  LOG(VERBOSE) << __func__ << ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();

  return -ENOSYS;
}

static int in_get_active_microphones(const struct audio_stream_in* stream,
                                     struct audio_microphone_characteristic_t* /*mic_array*/,
                                     size_t* /*mic_count*/) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  LOG(VERBOSE) << __func__ << ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();

  return -ENOSYS;
}

static int in_set_microphone_direction(const struct audio_stream_in* stream,
                                       audio_microphone_direction_t /*direction*/) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  LOG(VERBOSE) << __func__ << ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();

  return -ENOSYS;
}

static int in_set_microphone_field_dimension(const struct audio_stream_in* stream, float /*zoom*/) {
  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  LOG(VERBOSE) << __func__ << ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();

  return -ENOSYS;
}

static void in_update_sink_metadata_v7(struct audio_stream_in* stream,
                                       const struct sink_metadata_v7* sink_metadata) {
  LOG(INFO) << __func__;
  if (sink_metadata == nullptr || sink_metadata->track_count == 0) {
    return;
  }

  const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
  LOG(INFO) << __func__ << ": state=" << in->bluetooth_input_->GetState() << ", "
            << sink_metadata->track_count << " track(s)";

  if (!in->is_aidl) {
    LOG(WARNING) << __func__ << " is only supported in AIDL but using HIDL now!";
    return;
  }
  static_cast<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl*>(
          in->bluetooth_input_.get())
          ->UpdateSinkMetadata(sink_metadata);
}

int adev_open_input_stream(struct audio_hw_device* /*dev*/, audio_io_handle_t /*handle*/,
                           audio_devices_t devices, struct audio_config* config,
                           struct audio_stream_in** stream_in, audio_input_flags_t flags __unused,
                           const char* address __unused, audio_source_t source __unused) {
  *stream_in = nullptr;
  auto in = std::make_unique<BluetoothStreamIn>();
  if (::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession::IsAidlAvailable()) {
    in->bluetooth_input_ =
            std::make_unique<::android::bluetooth::audio::aidl::BluetoothAudioPortAidlIn>();
    in->is_aidl = true;
  } else {
    in->bluetooth_input_ =
            std::make_unique<::android::bluetooth::audio::hidl::BluetoothAudioPortHidlIn>();
    in->is_aidl = false;
  }
  if (!in->bluetooth_input_->SetUp(devices)) {
    in->bluetooth_input_ = nullptr;
    LOG(ERROR) << __func__ << ": cannot init HAL";
    return -EINVAL;
  }

  LOG(INFO) << __func__ << ": device=" << StringPrintf("%#x", devices);

  in->stream_in_.common.get_sample_rate = in_get_sample_rate;
  in->stream_in_.common.set_sample_rate = in_set_sample_rate;
  in->stream_in_.common.get_buffer_size = in_get_buffer_size;
  in->stream_in_.common.get_channels = in_get_channels;
  in->stream_in_.common.get_format = in_get_format;
  in->stream_in_.common.set_format = in_set_format;
  in->stream_in_.common.standby = in_standby;
  in->stream_in_.common.dump = in_dump;
  in->stream_in_.common.set_parameters = in_set_parameters;
  in->stream_in_.common.get_parameters = in_get_parameters;
  in->stream_in_.common.add_audio_effect = in_add_audio_effect;
  in->stream_in_.common.remove_audio_effect = in_remove_audio_effect;
  in->stream_in_.set_gain = in_set_gain;
  in->stream_in_.read = in_read;
  in->stream_in_.get_input_frames_lost = in_get_input_frames_lost;
  in->stream_in_.get_capture_position = in_get_capture_position;
  in->stream_in_.start = in_start;
  in->stream_in_.stop = in_stop;
  in->stream_in_.create_mmap_buffer = in_create_mmap_buffer;
  in->stream_in_.get_mmap_position = in_get_mmap_position;
  in->stream_in_.get_active_microphones = in_get_active_microphones;
  in->stream_in_.set_microphone_direction = in_set_microphone_direction;
  in->stream_in_.set_microphone_field_dimension = in_set_microphone_field_dimension;
  in->stream_in_.update_sink_metadata_v7 = in_update_sink_metadata_v7;

  if (!in->bluetooth_input_->LoadAudioConfig(config)) {
    LOG(ERROR) << __func__ << ": state=" << in->bluetooth_input_->GetState()
               << " failed to get audio config";
    return -EINVAL;
  }

  in->sample_rate_ = config->sample_rate;
  in->channel_mask_ = config->channel_mask;
  in->format_ = config->format;
  // frame is number of samples per channel

  size_t preferred_data_interval_us = kBluetoothDefaultInputBufferMs * 1000;
  if (in->bluetooth_input_->GetPreferredDataIntervalUs(&preferred_data_interval_us) &&
      preferred_data_interval_us != 0) {
    in->preferred_data_interval_us = preferred_data_interval_us;
  } else {
    in->preferred_data_interval_us = kBluetoothDefaultInputBufferMs * 1000;
  }

  in->frames_count_ = FrameCount(in->preferred_data_interval_us, in->sample_rate_);
  in->frames_presented_ = 0;

  BluetoothStreamIn* in_ptr = in.release();
  *stream_in = &in_ptr->stream_in_;
  LOG(INFO) << __func__ << ": state=" << in_ptr->bluetooth_input_->GetState()
            << ", sample_rate=" << in_ptr->sample_rate_
            << ", channels=" << StringPrintf("%#x", in_ptr->channel_mask_)
            << ", format=" << in_ptr->format_
            << ", preferred_data_interval_us=" << in_ptr->preferred_data_interval_us
            << ", frames=" << in_ptr->frames_count_;

  return 0;
}

void adev_close_input_stream(struct audio_hw_device* /*dev*/, struct audio_stream_in* stream) {
  auto* in = reinterpret_cast<BluetoothStreamIn*>(stream);

  if (in->bluetooth_input_->GetState() != BluetoothStreamState::DISABLED) {
    in->bluetooth_input_->Stop();
  }

  in->bluetooth_input_->TearDown();
  LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState() << ", stopped";

  delete in;
}
