#include "media/audio/android/audio_manager_android.h"
#include <algorithm>
#include <memory>
#include <optional>
#include <vector>
#include "base/android/android_info.h"
#include "base/android/device_info.h"
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/containers/flat_map.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/to_string.h"
#include "base/system/system_monitor.h"
#include "media/audio/android/aaudio_bluetooth_output.h"
#include "media/audio/android/aaudio_input.h"
#include "media/audio/android/aaudio_output.h"
#include "media/audio/android/audio_device.h"
#include "media/audio/android/audio_device_id.h"
#include "media/audio/android/audio_device_type.h"
#include "media/audio/android/audio_track_output_stream.h"
#include "media/audio/audio_device_description.h"
#include "media/audio/audio_features.h"
#include "media/audio/audio_manager.h"
#include "media/audio/fake_audio_input_stream.h"
#include "media/base/audio_latency.h"
#include "media/base/audio_parameters.h"
#include "media/base/channel_layout.h"
#include "media/base/localized_strings.h"
#include "media/base/media_switches.h"
#include "media/media_buildflags.h"
#if BUILDFLAG(USE_OPENSLES)
#include "media/audio/android/opensles_input.h"
#include "media/audio/android/opensles_output.h"
#endif
#include "media/base/android/media_jni_headers/AudioManagerAndroid_jni.h"
using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaParamRef;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
using media::android::AudioDevice;
using media::android::AudioDeviceId;
using media::android::AudioDeviceType;
using media::android::IntToAudioDeviceType;
using JniAudioDevice = media::AudioManagerAndroid::JniAudioDevice;
namespace media {
namespace {
constexpr int kMaxOutputStreams = 10;
constexpr int kDefaultInputBufferSize = 1024;
constexpr int kDefaultOutputBufferSize = 2048;
constexpr int kDefaultLowLatencyOutputBufferSize = 256;
constexpr char kPreferredOutputFramesPerBufferMetricsName[] =
"Media.Audio.Android.PreferredOutputFramesPerBuffer";
constexpr char kRequestedOutputFramesPerBufferMetricsName[] =
"Media.Audio.Android.RequestedOutputFramesPerBuffer";
constexpr char kRequestedInputFramesPerBufferMetricsName[] =
"Media.Audio.Android.RequestedInputFramesPerBuffer";
enum class DeviceChangeKind {
kAdded = 0,
kRemoved = 1,
kMaxValue = kRemoved,
};
class JniDelegateImpl : public AudioManagerAndroid::JniDelegate {
public:
explicit JniDelegateImpl(AudioManagerAndroid* audio_manager)
: j_audio_manager_(Java_AudioManagerAndroid_createAudioManagerAndroid(
AttachCurrentThread(),
reinterpret_cast<jlong>(audio_manager))) {
Java_AudioManagerAndroid_init(AttachCurrentThread(), j_audio_manager_);
}
~JniDelegateImpl() override {
Java_AudioManagerAndroid_close(AttachCurrentThread(), j_audio_manager_);
j_audio_manager_.Reset();
}
void InitDeviceListener() override {
Java_AudioManagerAndroid_initDeviceListener(AttachCurrentThread(),
j_audio_manager_);
}
void InitScoStateListener() override {
Java_AudioManagerAndroid_initScoStateListener(AttachCurrentThread(),
j_audio_manager_);
}
std::vector<JniAudioDevice> GetDevices(bool inputs) override {
ScopedJavaLocalRef<jobjectArray> j_devices =
Java_AudioManagerAndroid_getDevices(AttachCurrentThread(),
j_audio_manager_, inputs);
std::vector<JniAudioDevice> devices;
for (ScopedJavaLocalRef<jobject> j_device :
j_devices.ReadElements<jobject>()) {
devices.emplace_back(
Java_AudioDevice_id(AttachCurrentThread(), j_device),
Java_AudioDevice_name(AttachCurrentThread(), j_device),
Java_AudioDevice_type(AttachCurrentThread(), j_device),
Java_AudioDevice_sampleRates(AttachCurrentThread(), j_device));
}
return devices;
}
std::optional<std::vector<JniAudioDevice>> GetCommunicationDevices()
override {
ScopedJavaLocalRef<jobjectArray> j_devices =
Java_AudioManagerAndroid_getCommunicationDevices(AttachCurrentThread(),
j_audio_manager_);
if (j_devices.is_null()) {
return std::nullopt;
}
std::vector<JniAudioDevice> devices;
for (ScopedJavaLocalRef<jobject> j_device :
j_devices.ReadElements<jobject>()) {
devices.emplace_back(
Java_AudioDevice_id(AttachCurrentThread(), j_device),
Java_AudioDevice_name(AttachCurrentThread(), j_device),
Java_AudioDevice_type(AttachCurrentThread(), j_device),
Java_AudioDevice_sampleRates(AttachCurrentThread(), j_device));
}
return devices;
}
int GetMinInputFramesPerBuffer(int sample_rate, int channels) override {
return Java_AudioManagerAndroid_getMinInputFramesPerBuffer(
AttachCurrentThread(), sample_rate, channels);
}
bool AcousticEchoCancelerIsAvailable() override {
return Java_AudioManagerAndroid_acousticEchoCancelerIsAvailable(
AttachCurrentThread());
}
base::TimeDelta GetOutputLatency() override {
return base::Milliseconds(Java_AudioManagerAndroid_getOutputLatency(
AttachCurrentThread(), j_audio_manager_));
}
void SetCommunicationAudioModeOn(bool on) override {
Java_AudioManagerAndroid_setCommunicationAudioModeOn(AttachCurrentThread(),
j_audio_manager_, on);
}
bool SetCommunicationDevice(std::string_view device_id) override {
ScopedJavaLocalRef<jstring> j_device_id =
base::android::ConvertUTF8ToJavaString(
AttachCurrentThread(),
device_id == AudioDeviceDescription::kDefaultDeviceId
? std::string()
: device_id);
return Java_AudioManagerAndroid_setCommunicationDevice(
AttachCurrentThread(), j_audio_manager_, j_device_id);
}
void MaybeSetBluetoothScoState(bool state) override {
DVLOG(1) << __func__ << "(" << base::ToString(state) << ")";
return Java_AudioManagerAndroid_maybeSetBluetoothScoState(
AttachCurrentThread(), j_audio_manager_, state);
}
int GetNativeOutputSampleRate() override {
return Java_AudioManagerAndroid_getNativeOutputSampleRate(
AttachCurrentThread(), j_audio_manager_);
}
bool IsAudioLowLatencySupported() override {
return Java_AudioManagerAndroid_isAudioLowLatencySupported(
AttachCurrentThread(), j_audio_manager_);
}
int GetAudioLowLatencyOutputFramesPerBuffer() override {
return Java_AudioManagerAndroid_getAudioLowLatencyOutputFramesPerBuffer(
AttachCurrentThread(), j_audio_manager_);
}
int GetMinOutputFramesPerBuffer(int sample_rate, int channels) override {
return Java_AudioManagerAndroid_getMinOutputFramesPerBuffer(
AttachCurrentThread(), sample_rate, channels);
}
AudioParameters::Format GetHdmiOutputEncodingFormats() override {
return Java_AudioManagerAndroid_getHdmiOutputEncodingFormats(
AttachCurrentThread());
}
int GetLayoutWithMaxChannels() override {
return Java_AudioManagerAndroid_getLayoutWithMaxChannels(
AttachCurrentThread(), j_audio_manager_);
}
private:
base::android::ScopedJavaGlobalRef<jobject> j_audio_manager_;
};
void AddDefaultDevice(AudioDeviceNames* device_names) {
DCHECK(device_names->empty());
device_names->push_front(AudioDeviceName::CreateDefault());
}
std::string GetFallbackDeviceNameForType(AudioDeviceType type) {
switch (type) {
case AudioDeviceType::kBuiltinEarpiece:
case AudioDeviceType::kBuiltinSpeaker:
case AudioDeviceType::kBuiltinSpeakerSafe:
return GetLocalizedStringUTF8(
MessageId::INTERNAL_SPEAKER_AUDIO_DEVICE_NAME);
case AudioDeviceType::kBuiltinMic:
return GetLocalizedStringUTF8(MessageId::INTERNAL_MIC_AUDIO_DEVICE_NAME);
case AudioDeviceType::kWiredHeadset:
case AudioDeviceType::kWiredHeadphones:
return GetLocalizedStringUTF8(
MessageId::WIRED_HEADPHONES_AUDIO_DEVICE_NAME);
case AudioDeviceType::kBluetoothSco:
case AudioDeviceType::kBluetoothA2dp:
case AudioDeviceType::kBleHeadset:
case AudioDeviceType::kBleSpeaker:
case AudioDeviceType::kBleBroadcast:
case AudioDeviceType::kHearingAid:
return GetLocalizedStringUTF8(MessageId::BLUETOOTH_AUDIO_DEVICE_NAME);
case AudioDeviceType::kUsbDevice:
case AudioDeviceType::kUsbAccessory:
case AudioDeviceType::kUsbHeadset:
return GetLocalizedStringUTF8(MessageId::USB_AUDIO_DEVICE_NAME);
case AudioDeviceType::kHdmi:
case AudioDeviceType::kHdmiArc:
case AudioDeviceType::kHdmiEarc:
return GetLocalizedStringUTF8(MessageId::HDMI_AUDIO_DEVICE_NAME);
case AudioDeviceType::kUnknown:
case AudioDeviceType::kLineAnalog:
case AudioDeviceType::kLineDigital:
case AudioDeviceType::kDock:
case AudioDeviceType::kFm:
case AudioDeviceType::kFmTuner:
case AudioDeviceType::kTvTuner:
case AudioDeviceType::kTelephony:
case AudioDeviceType::kAuxLine:
case AudioDeviceType::kIp:
case AudioDeviceType::kBus:
case AudioDeviceType::kRemoteSubmix:
case AudioDeviceType::kEchoReference:
case AudioDeviceType::kDockAnalog:
case AudioDeviceType::kMultichannelGroup:
return GetLocalizedStringUTF8(MessageId::GENERIC_AUDIO_DEVICE_NAME);
}
}
void CombineBluetoothClassicDevices(std::vector<AudioDevice>& devices) {
constexpr auto is_a2dp_predicate = [](const AudioDevice& device) -> bool {
return device.GetType() == AudioDeviceType::kBluetoothA2dp;
};
constexpr auto is_sco_predicate = [](const AudioDevice& device) -> bool {
return device.GetType() == AudioDeviceType::kBluetoothSco;
};
if (std::ranges::count_if(devices, is_a2dp_predicate) > 1 ||
std::ranges::count_if(devices, is_sco_predicate) > 1) {
LOG(WARNING) << "Found multiple A2DP or SCO output devices";
return;
}
auto a2dp_device = std::ranges::find_if(devices, is_a2dp_predicate);
if (a2dp_device == devices.end()) {
return;
}
auto sco_device = std::ranges::find_if(devices, is_sco_predicate);
if (sco_device == devices.end()) {
return;
}
a2dp_device->SetAssociatedScoDevice(
std::make_unique<AudioDevice>(*sco_device));
devices.erase(sco_device);
}
bool UseAAudioOutput() {
return base::FeatureList::IsEnabled(features::kUseAAudioDriver);
}
bool UseAAudioInput() {
if (!base::FeatureList::IsEnabled(features::kUseAAudioInput)) {
return false;
}
if (base::StartsWith(base::android::android_info::board(), "ums",
base::CompareCase::INSENSITIVE_ASCII) &&
base::android::android_info::sdk_int() <
base::android::android_info::SDK_VERSION_S) {
return false;
}
return true;
}
bool UseAAudioPerStreamDeviceSelection() {
return UseAAudioInput() && UseAAudioOutput() &&
base::FeatureList::IsEnabled(
features::kAAudioPerStreamDeviceSelection);
}
}
static void JNI_AudioManagerAndroid_OnDevicesChanged(JNIEnv* env,
jboolean added) {
auto* system_monitor = base::SystemMonitor::Get();
if (system_monitor) {
system_monitor->ProcessDevicesChanged(base::SystemMonitor::DEVTYPE_AUDIO);
base::UmaHistogramEnumeration(
"Media.Audio.Android.DevicesChanged",
added ? DeviceChangeKind::kAdded : DeviceChangeKind::kRemoved);
}
}
std::unique_ptr<AudioManager> CreateAudioManager(
std::unique_ptr<AudioThread> audio_thread,
AudioLogFactory* audio_log_factory) {
return std::make_unique<AudioManagerAndroid>(std::move(audio_thread),
audio_log_factory);
}
JniAudioDevice::JniAudioDevice(int id,
std::optional<std::string> name,
int type,
std::vector<int> sample_rates) {
this->id = id;
this->name = std::move(name);
this->type = type;
this->sample_rates = sample_rates;
}
JniAudioDevice::JniAudioDevice(const JniAudioDevice&) = default;
JniAudioDevice& JniAudioDevice::operator=(const JniAudioDevice&) = default;
JniAudioDevice::JniAudioDevice(JniAudioDevice&&) = default;
JniAudioDevice& JniAudioDevice::operator=(JniAudioDevice&&) = default;
JniAudioDevice::~JniAudioDevice() = default;
AudioManagerAndroid::AudioManagerAndroid(
std::unique_ptr<AudioThread> audio_thread,
AudioLogFactory* audio_log_factory)
: AudioManagerBase(std::move(audio_thread), audio_log_factory),
communication_mode_is_on_(false),
output_volume_override_set_(false),
output_volume_override_(0) {
SetMaxOutputStreamsAllowed(kMaxOutputStreams);
}
AudioManagerAndroid::~AudioManagerAndroid() = default;
void AudioManagerAndroid::InitializeIfNeeded() {
GetTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(base::IgnoreResult(&AudioManagerAndroid::GetJniDelegate),
base::Unretained(this)));
}
void AudioManagerAndroid::ShutdownOnAudioThread() {
AudioManagerBase::ShutdownOnAudioThread();
jni_delegate_.reset();
}
bool AudioManagerAndroid::HasAudioOutputDevices() {
return true;
}
bool AudioManagerAndroid::HasAudioInputDevices() {
return true;
}
void AudioManagerAndroid::GetAudioInputDeviceNames(
AudioDeviceNames* device_names) {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
if (UseAAudioPerStreamDeviceSelection()) {
GetDeviceNames(device_names, AudioDeviceDirection::kInput);
return;
}
GetCommunicationDeviceNames(device_names);
}
void AudioManagerAndroid::GetAudioOutputDeviceNames(
AudioDeviceNames* device_names) {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
if (UseAAudioPerStreamDeviceSelection()) {
GetDeviceNames(device_names, AudioDeviceDirection::kOutput);
return;
}
AddDefaultDevice(device_names);
}
void AudioManagerAndroid::GetDeviceNames(AudioDeviceNames* device_names,
AudioDeviceDirection direction) {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
DCHECK(device_names);
DCHECK(device_names->empty());
AddDefaultDevice(device_names);
UpdateDeviceCache(direction);
const DeviceCache& devices = GetDeviceCache(direction);
for (auto& pair : devices) {
const AudioDevice& device = pair.second;
std::string device_name;
if (device.GetName().has_value()) {
device_name = device.GetName().value();
} else {
device_name = GetFallbackDeviceNameForType(device.GetType());
}
std::string device_id_string =
base::NumberToString(device.GetId().ToAAudioDeviceId());
device_names->emplace_back(std::move(device_name),
std::move(device_id_string));
}
}
void AudioManagerAndroid::GetCommunicationDeviceNames(
AudioDeviceNames* device_names) {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
DCHECK(device_names);
DCHECK(device_names->empty());
AddDefaultDevice(device_names);
std::optional<std::vector<JniAudioDevice>> j_devices =
GetJniDelegate().GetCommunicationDevices();
if (!j_devices) {
return;
}
for (auto& j_device : j_devices.value()) {
CHECK(j_device.name);
std::string device_id_string = base::NumberToString(j_device.id);
device_names->emplace_back(std::move(j_device.name).value(),
std::move(device_id_string));
}
}
void AudioManagerAndroid::UpdateDeviceCache(AudioDeviceDirection direction) {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
std::vector<JniAudioDevice> j_devices =
GetJniDelegate().GetDevices(direction == AudioDeviceDirection::kInput);
std::vector<AudioDevice> devices;
for (auto& j_device : j_devices) {
std::optional<AudioDeviceId> device_id =
AudioDeviceId::NonDefault(j_device.id);
if (!device_id.has_value()) {
LOG(WARNING) << "Unexpectedly received device with default ID";
continue;
}
std::optional<AudioDeviceType> device_type =
IntToAudioDeviceType(j_device.type);
if (!device_type.has_value()) {
LOG(WARNING) << "No device type matching integer value: "
<< j_device.type;
device_type = AudioDeviceType::kUnknown;
}
std::optional<std::string> device_name = std::move(j_device.name);
std::vector<int> sample_rates = std::move(j_device.sample_rates);
devices.emplace_back(std::move(device_id).value(), device_type.value(),
std::move(device_name), std::move(sample_rates));
}
if (direction == AudioDeviceDirection::kOutput) {
CombineBluetoothClassicDevices(devices);
}
auto device_map = base::MakeFlatMap<AudioDeviceId, AudioDevice>(
devices, {}, [](const AudioDevice& device) {
return std::make_pair(device.GetId(), device);
});
switch (direction) {
case AudioDeviceDirection::kInput:
input_device_cache_ = device_map;
break;
case AudioDeviceDirection::kOutput:
output_device_cache_ = device_map;
break;
}
}
const AudioManagerAndroid::DeviceCache& AudioManagerAndroid::GetDeviceCache(
AudioDeviceDirection direction) const {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
switch (direction) {
case AudioDeviceDirection::kInput:
return input_device_cache_;
case AudioDeviceDirection::kOutput:
return output_device_cache_;
}
}
std::optional<AudioDevice> AudioManagerAndroid::GetDeviceForAAudioStream(
std::string_view id_string,
AudioDeviceDirection direction) {
if (!UseAAudioPerStreamDeviceSelection()) {
return AudioDevice::Default();
}
AudioDeviceId id =
AudioDeviceId::Parse(id_string).value_or(AudioDeviceId::Default());
if (id.IsDefault()) {
return AudioDevice::Default();
}
const DeviceCache& devices = GetDeviceCache(direction);
auto device = devices.find(id);
if (device == devices.end()) {
return std::nullopt;
}
return device->second;
}
AudioParameters AudioManagerAndroid::GetInputStreamParameters(
const std::string& device_id) {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
const ChannelLayoutConfig channel_layout_config =
base::FeatureList::IsEnabled(features::kAudioStereoInputStreamParameters)
? ChannelLayoutConfig::Stereo()
: ChannelLayoutConfig::Mono();
int sample_rate;
if (UseAAudioPerStreamDeviceSelection()) {
AudioDevice device =
GetDeviceForAAudioStream(device_id, AudioDeviceDirection::kInput)
.value_or(AudioDevice::Default());
sample_rate =
SelectSampleRate(device, std::nullopt);
} else {
sample_rate = GetJniDelegate().GetNativeOutputSampleRate();
}
int frames_per_buffer = GetJniDelegate().GetMinInputFramesPerBuffer(
sample_rate, channel_layout_config.channels());
if (frames_per_buffer <= 0) {
frames_per_buffer = kDefaultInputBufferSize;
}
int user_buffer_size = GetUserBufferSize();
if (user_buffer_size) {
frames_per_buffer = user_buffer_size;
}
AudioParameters::PlatformEffectsMask effects =
GetJniDelegate().AcousticEchoCancelerIsAvailable()
? AudioParameters::ECHO_CANCELLER
: AudioParameters::NO_EFFECTS;
AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
channel_layout_config, sample_rate, frames_per_buffer);
params.set_effects(effects);
return params;
}
const std::string_view AudioManagerAndroid::GetName() {
return "Android";
}
AudioOutputStream* AudioManagerAndroid::MakeAudioOutputStream(
const AudioParameters& params,
const std::string& device_id,
const LogCallback& log_callback) {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
AudioOutputStream* stream = AudioManagerBase::MakeAudioOutputStream(
params, device_id, AudioManager::LogCallback());
if (stream) {
output_streams_.insert(static_cast<MuteableAudioOutputStream*>(stream));
base::UmaHistogramSparse(kRequestedOutputFramesPerBufferMetricsName,
params.frames_per_buffer());
base::UmaHistogramSparse(
base::StrCat({kRequestedOutputFramesPerBufferMetricsName, ".",
media::AudioLatency::ToString(params.latency_tag())}),
params.frames_per_buffer());
}
return stream;
}
AudioInputStream* AudioManagerAndroid::MakeAudioInputStream(
const AudioParameters& params,
const std::string& device_id,
const LogCallback& log_callback) {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
bool has_input_streams = !HasNoAudioInputStreams();
AudioInputStream* stream = AudioManagerBase::MakeAudioInputStream(
params, device_id, AudioManager::LogCallback());
if (stream) {
base::UmaHistogramSparse(kRequestedInputFramesPerBufferMetricsName,
params.frames_per_buffer());
base::UmaHistogramSparse(
base::StrCat({kRequestedInputFramesPerBufferMetricsName, ".",
media::AudioLatency::ToString(params.latency_tag())}),
params.frames_per_buffer());
}
if (!stream || has_input_streams || UseAAudioPerStreamDeviceSelection()) {
return stream;
}
if (params.effects() != AudioParameters::NO_EFFECTS) {
communication_mode_is_on_ = true;
GetJniDelegate().SetCommunicationAudioModeOn(true);
}
return stream;
}
void AudioManagerAndroid::ReleaseOutputStream(AudioOutputStream* stream) {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
output_streams_.erase(static_cast<MuteableAudioOutputStream*>(stream));
bluetooth_output_streams_.erase(stream);
AudioManagerBase::ReleaseOutputStream(stream);
}
void AudioManagerAndroid::ReleaseInputStream(AudioInputStream* stream) {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
AudioManagerBase::ReleaseInputStream(stream);
if (HasNoAudioInputStreams() && communication_mode_is_on_) {
communication_mode_is_on_ = false;
GetJniDelegate().SetCommunicationAudioModeOn(false);
}
}
AudioOutputStream* AudioManagerAndroid::MakeLinearOutputStream(
const AudioParameters& params,
const LogCallback& log_callback) {
DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
if (UseAAudioOutput()) {
return new AAudioOutputStream(
this, params, AudioDevice::Default(), AAUDIO_USAGE_MEDIA,
base::BindRepeating(&AudioManager::TraceAmplitudePeak,
base::Unretained(this),
false));
}
#if BUILDFLAG(USE_OPENSLES)
return new OpenSLESOutputStream(this, params, SL_ANDROID_STREAM_MEDIA);
#else
return nullptr;
#endif
}
AudioOutputStream* AudioManagerAndroid::MakeLowLatencyOutputStream(
const AudioParameters& params,
const std::string& device_id,
const LogCallback& log_callback) {
DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
if (UseAAudioOutput()) {
DLOG_IF(WARNING, !UseAAudioPerStreamDeviceSelection() &&
!AudioDeviceDescription::IsDefaultDevice(device_id))
<< "Non-default output device requested for output communication "
"stream.";
std::optional<AudioDevice> device =
GetDeviceForAAudioStream(device_id, AudioDeviceDirection::kOutput);
if (!device.has_value()) {
return nullptr;
}
const aaudio_usage_t usage = communication_mode_is_on_
? AAUDIO_USAGE_VOICE_COMMUNICATION
: AAUDIO_USAGE_MEDIA;
auto peak_detected_cb = base::BindRepeating(
&AudioManager::TraceAmplitudePeak, base::Unretained(this),
false);
if (device->GetAssociatedScoDevice().has_value()) {
auto* stream = new AAudioBluetoothOutputStream(
*this, params, std::move(device).value(),
is_bluetooth_sco_enabled_, usage,
peak_detected_cb);
bluetooth_output_streams_.insert(stream);
return stream;
}
return new AAudioOutputStream(this, params, std::move(device).value(),
usage, peak_detected_cb);
}
#if BUILDFLAG(USE_OPENSLES)
const SLint32 stream_type = communication_mode_is_on_
? SL_ANDROID_STREAM_VOICE
: SL_ANDROID_STREAM_MEDIA;
return new OpenSLESOutputStream(this, params, stream_type);
#else
return nullptr;
#endif
}
AudioOutputStream* AudioManagerAndroid::MakeBitstreamOutputStream(
const AudioParameters& params,
const std::string& device_id,
const LogCallback& log_callback) {
DCHECK(params.IsBitstreamFormat());
return new AudioTrackOutputStream(this, params);
}
AudioInputStream* AudioManagerAndroid::MakeLinearInputStream(
const AudioParameters& params,
const std::string& device_id,
const LogCallback& log_callback) {
DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
if (UseAAudioInput()) {
std::optional<AudioDevice> device =
GetDeviceForAAudioStream(device_id, AudioDeviceDirection::kInput);
if (!device.has_value()) {
return nullptr;
}
return new AAudioInputStream(this, params, std::move(device).value());
}
#if BUILDFLAG(USE_OPENSLES)
return new OpenSLESInputStream(this, params);
#else
return nullptr;
#endif
}
AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream(
const AudioParameters& params,
const std::string& device_id,
const LogCallback& log_callback) {
DVLOG(1) << "MakeLowLatencyInputStream: " << params.effects();
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
DLOG_IF(ERROR, device_id.empty()) << "Invalid device ID!";
if (!UseAAudioPerStreamDeviceSelection()) {
if (!GetJniDelegate().SetCommunicationDevice(device_id)) {
LOG(ERROR) << "Unable to select communication device!";
return nullptr;
}
}
if (UseAAudioInput()) {
std::optional<AudioDevice> device =
GetDeviceForAAudioStream(device_id, AudioDeviceDirection::kInput);
if (!device.has_value()) {
return nullptr;
}
return new AAudioInputStream(this, params, std::move(device).value());
}
#if BUILDFLAG(USE_OPENSLES)
return new OpenSLESInputStream(this, params);
#else
return nullptr;
#endif
}
void AudioManagerAndroid::OnStartAAudioInputStream(AAudioInputStream* stream) {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
if (!UseAAudioPerStreamDeviceSelection()) {
return;
}
std::optional<AudioDeviceId> actual_device_id = stream->GetActualDeviceId();
if (!actual_device_id.has_value()) {
return;
}
auto devices = GetDeviceCache(AudioDeviceDirection::kInput);
auto actual_device = devices.find(actual_device_id);
if (actual_device == devices.end()) {
UpdateDeviceCache(AudioDeviceDirection::kInput);
devices = GetDeviceCache(AudioDeviceDirection::kInput);
actual_device = devices.find(actual_device_id);
if (actual_device == devices.end()) {
return;
}
}
if (actual_device->second.GetType() != AudioDeviceType::kBluetoothSco) {
return;
}
input_streams_requiring_sco_.insert(stream);
GetJniDelegate().MaybeSetBluetoothScoState(true);
}
void AudioManagerAndroid::OnStopAAudioInputStream(AAudioInputStream* stream) {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
if (input_streams_requiring_sco_.erase(stream) == 0) {
return;
}
if (!input_streams_requiring_sco_.empty()) {
return;
}
GetJniDelegate().MaybeSetBluetoothScoState(false);
}
void AudioManagerAndroid::SetMute(JNIEnv* env, jboolean muted) {
GetTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&AudioManagerAndroid::DoSetMuteOnAudioThread,
base::Unretained(this), muted));
}
void AudioManagerAndroid::OnScoStateChanged(JNIEnv* env, jboolean state) {
GetTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(&AudioManagerAndroid::OnScoStateChangedOnAudioThread,
base::Unretained(this), state));
}
void AudioManagerAndroid::SetOutputVolumeOverride(double volume) {
GetTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&AudioManagerAndroid::DoSetVolumeOnAudioThread,
base::Unretained(this), volume));
}
bool AudioManagerAndroid::HasOutputVolumeOverride(double* out_volume) const {
if (output_volume_override_set_) {
*out_volume = output_volume_override_;
}
return output_volume_override_set_;
}
base::TimeDelta AudioManagerAndroid::GetOutputLatency() {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
return GetJniDelegate().GetOutputLatency();
}
AudioParameters AudioManagerAndroid::GetPreferredOutputStreamParameters(
const std::string& output_device_id,
const AudioParameters& input_params) {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
bool input_params_are_valid = input_params.IsValid();
std::optional<int> input_sample_rate =
input_params_are_valid ? std::optional<int>(input_params.sample_rate())
: std::nullopt;
ChannelLayoutConfig channel_layout_config = ChannelLayoutConfig::Stereo();
int sample_rate;
if (UseAAudioPerStreamDeviceSelection()) {
AudioDevice device = GetDeviceForAAudioStream(output_device_id,
AudioDeviceDirection::kOutput)
.value_or(AudioDevice::Default());
if (is_bluetooth_sco_enabled_) {
std::optional<AudioDevice> associated_sco_device =
device.GetAssociatedScoDevice();
if (associated_sco_device.has_value()) {
device = associated_sco_device.value();
}
}
sample_rate =
SelectSampleRate(device, input_sample_rate);
} else {
sample_rate = input_sample_rate.value_or(
GetJniDelegate().GetNativeOutputSampleRate());
}
int frames_per_buffer = GetOptimalOutputFramesPerBuffer(sample_rate, 2);
if (input_params_are_valid) {
if (input_params.channels() <= 2 ||
GetJniDelegate().IsAudioLowLatencySupported() ||
(base::FeatureList::IsEnabled(media::kMatchSourceAudioChannelLayout) &&
base::android::device_info::is_automotive())) {
channel_layout_config = input_params.channel_layout_config();
}
if (input_params.latency_tag() == AudioLatency::Type::kPlayback) {
frames_per_buffer = input_params.frames_per_buffer();
} else {
frames_per_buffer = GetOptimalOutputFramesPerBuffer(
sample_rate, channel_layout_config.channels());
}
base::UmaHistogramSparse(
base::StrCat(
{kPreferredOutputFramesPerBufferMetricsName, ".",
media::AudioLatency::ToString(input_params.latency_tag())}),
frames_per_buffer);
} else {
base::UmaHistogramSparse(
base::StrCat({kPreferredOutputFramesPerBufferMetricsName,
".InvalidInputParams"}),
frames_per_buffer);
}
base::UmaHistogramSparse(kPreferredOutputFramesPerBufferMetricsName,
frames_per_buffer);
if (base::FeatureList::IsEnabled(kUseAudioManagerMaxChannelLayout)) {
ChannelLayoutConfig max_channel_layout_config = GetLayoutWithMaxChannels();
if (max_channel_layout_config.channels() >
channel_layout_config.channels()) {
channel_layout_config = max_channel_layout_config;
}
}
int user_buffer_size = GetUserBufferSize();
if (user_buffer_size) {
frames_per_buffer = user_buffer_size;
}
AudioParameters::HardwareCapabilities hardware_capabilities(
GetJniDelegate().GetHdmiOutputEncodingFormats(),
false);
return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
channel_layout_config, sample_rate, frames_per_buffer,
hardware_capabilities);
}
bool AudioManagerAndroid::HasNoAudioInputStreams() {
return input_stream_count() == 0;
}
AudioManagerAndroid::JniDelegate& AudioManagerAndroid::GetJniDelegate() {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
if (!jni_delegate_) {
jni_delegate_ = std::make_unique<JniDelegateImpl>(this);
if (base::FeatureList::IsEnabled(features::kAndroidAudioDeviceListener)) {
jni_delegate_->InitDeviceListener();
}
if (base::FeatureList::IsEnabled(
features::kAAudioPerStreamDeviceSelection)) {
jni_delegate_->InitScoStateListener();
}
}
return *jni_delegate_;
}
int AudioManagerAndroid::SelectSampleRate(
const AudioDevice& device,
std::optional<int> preferred_sample_rate) {
const std::optional<std::vector<int>>& supported_sample_rates =
device.GetSampleRates();
if (!supported_sample_rates.has_value()) {
return preferred_sample_rate.value_or(
GetJniDelegate().GetNativeOutputSampleRate());
}
constexpr int kDefaultTargetSampleRate = 48000;
int target_sample_rate =
preferred_sample_rate.value_or(kDefaultTargetSampleRate);
if (supported_sample_rates->empty()) {
return target_sample_rate;
}
return std::ranges::min(supported_sample_rates.value(),
{},
[target_sample_rate](int sample_rate) {
return abs(sample_rate - target_sample_rate);
});
}
int AudioManagerAndroid::GetOptimalOutputFramesPerBuffer(int sample_rate,
int channels) {
if (base::FeatureList::IsEnabled(
features::kAlwaysUseAudioManagerOutputFramesPerBuffer)) {
int frames_per_buffer =
GetJniDelegate().GetAudioLowLatencyOutputFramesPerBuffer();
if (frames_per_buffer) {
return frames_per_buffer;
}
if (GetJniDelegate().IsAudioLowLatencySupported()) {
return kDefaultLowLatencyOutputBufferSize;
}
} else if (GetJniDelegate().IsAudioLowLatencySupported()) {
int frames_per_buffer =
GetJniDelegate().GetAudioLowLatencyOutputFramesPerBuffer();
if (frames_per_buffer == 0) {
frames_per_buffer = kDefaultLowLatencyOutputBufferSize;
}
return frames_per_buffer;
}
return std::max(
kDefaultOutputBufferSize,
GetJniDelegate().GetMinOutputFramesPerBuffer(sample_rate, channels));
}
AudioParameters::Format AudioManagerAndroid::GetHdmiOutputEncodingFormats() {
JNIEnv* env = AttachCurrentThread();
return Java_AudioManagerAndroid_getHdmiOutputEncodingFormats(env);
}
void AudioManagerAndroid::DoSetMuteOnAudioThread(bool muted) {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
for (auto stream : output_streams_) {
stream->SetMute(muted);
}
}
void AudioManagerAndroid::DoSetVolumeOnAudioThread(double volume) {
output_volume_override_set_ = true;
output_volume_override_ = volume;
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
for (auto stream : output_streams_) {
stream->SetVolume(volume);
}
}
void AudioManagerAndroid::OnScoStateChangedOnAudioThread(bool state) {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
DCHECK_NE(is_bluetooth_sco_enabled_, state);
is_bluetooth_sco_enabled_ = state;
if (bluetooth_output_streams_.empty()) {
return;
}
DVLOG(1) << "Calling SetUseScoDevice(" << base::ToString(state) << ") for "
<< bluetooth_output_streams_.size() << " Bluetooth streams";
for (auto stream : bluetooth_output_streams_) {
stream->SetUseSco(state);
}
}
ChannelLayoutConfig AudioManagerAndroid::GetLayoutWithMaxChannels() {
int value = GetJniDelegate().GetLayoutWithMaxChannels();
CHECK_GT(value, 0);
CHECK_LE(value, CHANNEL_LAYOUT_MAX);
ChannelLayout channel_layout = static_cast<ChannelLayout>(value);
int channel_count = ChannelLayoutToChannelCount(channel_layout);
return ChannelLayoutConfig(channel_layout, channel_count);
}
void AudioManagerAndroid::SetJniDelegateForTesting(
std::unique_ptr<JniDelegate> jni_delegate) {
jni_delegate_ = std::move(jni_delegate);
}
}
DEFINE_JNI(AudioManagerAndroid)