Merge "audio: Add microphone settings to IModule, IStreamIn" am: 77494f934a

Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/2240746

Change-Id: I2c95083fcaede0e37576375173fd0468c2d53c2f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Treehugger Robot
2022-12-07 23:51:37 +00:00
committed by Automerger Merge Worker
19 changed files with 858 additions and 68 deletions

View File

@@ -117,6 +117,8 @@ aidl_interface {
"android/hardware/audio/core/IStreamIn.aidl",
"android/hardware/audio/core/IStreamOut.aidl",
"android/hardware/audio/core/ITelephony.aidl",
"android/hardware/audio/core/MicrophoneDynamicInfo.aidl",
"android/hardware/audio/core/MicrophoneInfo.aidl",
"android/hardware/audio/core/MmapBufferDescriptor.aidl",
"android/hardware/audio/core/ModuleDebug.aidl",
"android/hardware/audio/core/StreamDescriptor.aidl",

View File

@@ -56,6 +56,7 @@ interface IModule {
void setMasterVolume(float volume);
boolean getMicMute();
void setMicMute(boolean mute);
android.hardware.audio.core.MicrophoneInfo[] getMicrophones();
void updateAudioMode(android.hardware.audio.core.AudioMode mode);
void updateScreenRotation(android.hardware.audio.core.IModule.ScreenRotation rotation);
void updateScreenState(boolean isTurnedOn);

View File

@@ -35,5 +35,20 @@ package android.hardware.audio.core;
@VintfStability
interface IStreamIn {
void close();
android.hardware.audio.core.MicrophoneDynamicInfo[] getActiveMicrophones();
android.hardware.audio.core.IStreamIn.MicrophoneDirection getMicrophoneDirection();
void setMicrophoneDirection(android.hardware.audio.core.IStreamIn.MicrophoneDirection direction);
float getMicrophoneFieldDimension();
void setMicrophoneFieldDimension(float zoom);
void updateMetadata(in android.hardware.audio.common.SinkMetadata sinkMetadata);
const int MIC_FIELD_DIMENSION_WIDE_ANGLE = -1;
const int MIC_FIELD_DIMENSION_NO_ZOOM = 0;
const int MIC_FIELD_DIMENSION_MAX_ZOOM = 1;
@Backing(type="int") @VintfStability
enum MicrophoneDirection {
UNSPECIFIED = 0,
FRONT = 1,
BACK = 2,
EXTERNAL = 3,
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2022 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.audio.core;
@JavaDerive(equals=true, toString=true) @VintfStability
parcelable MicrophoneDynamicInfo {
@utf8InCpp String id;
android.hardware.audio.core.MicrophoneDynamicInfo.ChannelMapping[] channelMapping;
@Backing(type="int") @VintfStability
enum ChannelMapping {
UNUSED = 0,
DIRECT = 1,
PROCESSED = 2,
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2022 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.audio.core;
@JavaDerive(equals=true, toString=true) @VintfStability
parcelable MicrophoneInfo {
@utf8InCpp String id;
android.media.audio.common.AudioDevice device;
android.hardware.audio.core.MicrophoneInfo.Location location = android.hardware.audio.core.MicrophoneInfo.Location.UNKNOWN;
int group = -1;
int indexInTheGroup = -1;
@nullable android.hardware.audio.core.MicrophoneInfo.Sensitivity sensitivity;
android.hardware.audio.core.MicrophoneInfo.Directionality directionality = android.hardware.audio.core.MicrophoneInfo.Directionality.UNKNOWN;
android.hardware.audio.core.MicrophoneInfo.FrequencyResponsePoint[] frequencyResponse;
@nullable android.hardware.audio.core.MicrophoneInfo.Coordinate position;
@nullable android.hardware.audio.core.MicrophoneInfo.Coordinate orientation;
const int GROUP_UNKNOWN = -1;
const int INDEX_IN_THE_GROUP_UNKNOWN = -1;
@Backing(type="int") @VintfStability
enum Location {
UNKNOWN = 0,
MAINBODY = 1,
MAINBODY_MOVABLE = 2,
PERIPHERAL = 3,
}
@VintfStability
parcelable Sensitivity {
float leveldBFS;
float maxSpldB;
float minSpldB;
}
@Backing(type="int") @VintfStability
enum Directionality {
UNKNOWN = 0,
OMNI = 1,
BI_DIRECTIONAL = 2,
CARDIOID = 3,
HYPER_CARDIOID = 4,
SUPER_CARDIOID = 5,
}
@VintfStability
parcelable FrequencyResponsePoint {
float frequencyHz;
float leveldB;
}
@VintfStability
parcelable Coordinate {
float x;
float y;
float z;
}
}

View File

@@ -25,6 +25,7 @@ import android.hardware.audio.core.IStreamCallback;
import android.hardware.audio.core.IStreamIn;
import android.hardware.audio.core.IStreamOut;
import android.hardware.audio.core.ITelephony;
import android.hardware.audio.core.MicrophoneInfo;
import android.hardware.audio.core.ModuleDebug;
import android.hardware.audio.core.StreamDescriptor;
import android.media.audio.common.AudioOffloadInfo;
@@ -604,6 +605,22 @@ interface IModule {
*/
void setMicMute(boolean mute);
/**
* Provide information describing built-in microphones of the HAL module.
*
* If there are no built-in microphones in the HAL module, it must return an
* empty vector. If there are microphones, but the HAL module does not
* possess the required information about them, EX_UNSUPPORTED_OPERATION
* must be thrown.
*
* If this method is supported by the HAL module, it must also support
* 'IStreamIn.getActiveMicrophones' method.
*
* @return The vector with information about each microphone.
* @throws EX_UNSUPPORTED_OPERATION If the information is unavailable.
*/
MicrophoneInfo[] getMicrophones();
/**
* Notify the HAL module on the change of the current audio mode.
*

View File

@@ -17,6 +17,7 @@
package android.hardware.audio.core;
import android.hardware.audio.common.SinkMetadata;
import android.hardware.audio.core.MicrophoneDynamicInfo;
/**
* This interface provides means for receiving audio data from input devices.
@@ -38,6 +39,92 @@ interface IStreamIn {
*/
void close();
/**
* Provides information on the microphones that are active for this stream.
*
* The returned array contains dynamic information on the microphones which
* are active for this stream. Each entry in the returned array must have a
* corresponding entry (matched by the 'MicrophoneInfo.id' field value) in
* the list of all available microphones which is provided by the
* 'IModule.getMicrophones' method.
*
* This method must be supported by the HAL module if
* 'IModule.getMicrophones' is supported.
*
* @return The vector with dynamic information on the microphones.
* @throws EX_ILLEGAL_STATE If the stream has already been closed.
* @throws EX_UNSUPPORTED_OPERATION If the information is unavailable.
*/
MicrophoneDynamicInfo[] getActiveMicrophones();
@VintfStability
@Backing(type="int")
enum MicrophoneDirection {
/**
* Don't do any directionality processing of the activated microphone(s).
*/
UNSPECIFIED = 0,
/**
* Optimize capture for audio coming from the screen-side of the device.
*/
FRONT = 1,
/**
* Optimize capture for audio coming from the side of the device opposite the screen.
*/
BACK = 2,
/**
* Optimize capture for audio coming from an off-device microphone.
*/
EXTERNAL = 3,
}
/**
* Get the current logical microphone direction.
*
* @return The current logical microphone direction.
* @throws EX_ILLEGAL_STATE If the stream has already been closed.
* @throws EX_UNSUPPORTED_OPERATION If the information is unavailable.
*/
MicrophoneDirection getMicrophoneDirection();
/**
* Set the current logical microphone direction.
*
* The client sets this parameter in order to specify its preference for
* optimizing the direction of capture when multiple microphones are in use.
*
* @param direction The preferred capture direction.
* @throws EX_ILLEGAL_STATE If the stream has already been closed.
* @throws EX_UNSUPPORTED_OPERATION If the operation is not supported.
*/
void setMicrophoneDirection(MicrophoneDirection direction);
const int MIC_FIELD_DIMENSION_WIDE_ANGLE = -1;
const int MIC_FIELD_DIMENSION_NO_ZOOM = 0;
const int MIC_FIELD_DIMENSION_MAX_ZOOM = 1;
/**
* Get the "zoom factor" for the logical microphone.
*
* The returned value must be within the range of [-1.0, 1.0] (see
* MIC_FIELD_DIMENSION_* constants).
*
* @throws EX_ILLEGAL_STATE If the stream has already been closed.
* @throws EX_UNSUPPORTED_OPERATION If the information is unavailable.
*/
float getMicrophoneFieldDimension();
/**
* Set the "zoom factor" for the logical microphone.
*
* If multiple microphones are in use, the provided zoom factor must be
* treated as a preference for their combined field dimension. The zoom
* factor must be within the range of [-1.0, 1.0] (see MIC_FIELD_DIMENSION_*
* constants).
*
* @param zoom The preferred field dimension of the microphone capture.
* @throws EX_ILLEGAL_ARGUMENT If the dimension value is outside of the range.
* @throws EX_ILLEGAL_STATE If the stream has already been closed.
* @throws EX_UNSUPPORTED_OPERATION If the operation is not supported.
*/
void setMicrophoneFieldDimension(float zoom);
/**
* Update stream metadata.
*

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2022 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.
*/
package android.hardware.audio.core;
/**
* Structure providing dynamic information on a microphone. This information
* changes between recording sessions.
*/
@JavaDerive(equals=true, toString=true)
@VintfStability
parcelable MicrophoneDynamicInfo {
/**
* Unique alphanumeric id for the microphone. It must match the id of one of
* the 'MicrophoneInfo' entries returned by 'IModule.getMicrophones'.
*/
@utf8InCpp String id;
@VintfStability
@Backing(type="int")
enum ChannelMapping {
/** Channel not used. */
UNUSED = 0,
/** Channel is used and the signal is not processed. */
DIRECT = 1,
/** Channel is used and the signal has some processing. */
PROCESSED = 2,
}
/**
* The vector is indexes by zero-based channels of the microphone, thus the
* element '0' corresponds to the first channel, '1' is the second, etc. The
* vector must contain at least 1 element.
*/
ChannelMapping[] channelMapping;
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright (C) 2022 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.
*/
package android.hardware.audio.core;
import android.media.audio.common.AudioDevice;
/**
* Structure providing static information on a microphone. This information
* never changes during the lifetime of the IModule which owns the microphone.
* The information presented in this structure indicates the location and
* orientation of the microphone on the device as well as useful information
* like frequency response and sensitivity.
*/
@JavaDerive(equals=true, toString=true)
@VintfStability
parcelable MicrophoneInfo {
/**
* Unique alphanumeric id for the microphone. It must remain the same across
* device reboots. The client must never attempt to parse the value of this
* field.
*/
@utf8InCpp String id;
/**
* Describes the location of the microphone in terms of managed audio devices.
*/
AudioDevice device;
@VintfStability
@Backing(type="int")
enum Location {
/** Microphone location is unknown. */
UNKNOWN = 0,
/** The microphone is located on the main body of the device. */
MAINBODY = 1,
/** The microphone is located on a movable main body of the device. */
MAINBODY_MOVABLE = 2,
/** The microphone is located on a peripheral. */
PERIPHERAL = 3,
}
/** Location of the microphone in regard to the body of the device */
Location location = Location.UNKNOWN;
/**
* This value is used when the group of the microphone is unknown.
*/
const int GROUP_UNKNOWN = -1;
/**
* An identifier to group related microphones together, for example,
* microphones of a microphone array should all belong to the same group.
* Note that microphones assigned to 'GROUP_UNKNOWN' do not form a group.
*/
int group = GROUP_UNKNOWN;
/**
* This value is used when the index in the group of the microphone is
* unknown.
*/
const int INDEX_IN_THE_GROUP_UNKNOWN = -1;
/**
* Index of this microphone within the group. The pair (group, index) must
* be unique within the same HAL module, except the pair
* (GROUP_UNKNOWN, INDEX_IN_THE_GROUP_UNKNOWN).
*/
int indexInTheGroup = INDEX_IN_THE_GROUP_UNKNOWN;
@VintfStability
parcelable Sensitivity {
/** Level in dBFS produced by a 1000 Hz tone at 94 dB SPL. */
float leveldBFS;
/** Level in dB of the max SPL supported at 1000 Hz */
float maxSpldB;
/** Level in dB of the min SPL supported at 1000 Hz */
float minSpldB;
}
/**
* If provided, must describe acceptable sound pressure levels (SPL)
* for a 1 kHz sine wave, and the resulting level in dBFS.
*/
@nullable Sensitivity sensitivity;
@VintfStability
@Backing(type="int")
enum Directionality {
UNKNOWN = 0,
OMNI = 1,
BI_DIRECTIONAL = 2,
CARDIOID = 3,
HYPER_CARDIOID = 4,
SUPER_CARDIOID = 5,
}
/**
* The standard polar pattern of the microphone.
*/
Directionality directionality = Directionality.UNKNOWN;
/**
* A (frequency, level) pair. Used to represent frequency response.
*/
@VintfStability
parcelable FrequencyResponsePoint {
float frequencyHz;
float leveldB;
}
/**
* Vector with ordered frequency responses (from low to high frequencies)
* with the frequency response of the microphone. Levels are in dB,
* relative to level at 1000 Hz.
*/
FrequencyResponsePoint[] frequencyResponse;
/**
* A 3D point used to represent position or orientation of a microphone.
*/
@VintfStability
parcelable Coordinate {
float x;
float y;
float z;
}
/**
* If provided, must specify distances of the microphone's capsule, in
* meters, from the bottom-left-back corner of the bounding box of device in
* its natural orientation (PORTRAIT for phones, LANDSCAPE for tablets, TVs,
* etc).
*/
@nullable Coordinate position;
/**
* If provided, describes the normalized point which defines the main
* orientation of the microphone's capsule.
* Magnitude = sqrt(x^2 + y^2 + z^2) = 1.
*/
@nullable Coordinate orientation;
}

View File

@@ -243,6 +243,13 @@ Configuration& getNullPrimaryConfiguration() {
AudioChannelLayout::LAYOUT_MONO, 48000, 0, true,
createDeviceExt(AudioDeviceType::IN_MICROPHONE, 0)));
MicrophoneInfo mic;
mic.id = "zero";
mic.device = zeroInDevice.ext.get<AudioPortExt::Tag::device>().device;
mic.group = 0;
mic.indexInTheGroup = 0;
c.microphones = std::vector<MicrophoneInfo>{mic};
AudioPort primaryInMix =
createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(2, 2));
primaryInMix.profiles.push_back(

View File

@@ -31,6 +31,7 @@
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioDevice;
using aidl::android::media::audio::common::AudioFormatDescription;
using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::AudioInputFlags;
@@ -136,7 +137,8 @@ ndk::ScopedAStatus Module::createStreamContext(int32_t in_portConfigId, int64_t
StreamContext temp(
std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
frameSize, std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
portConfigIt->format.value(), portConfigIt->channelMask.value(),
std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
asyncCallback, mDebug.streamTransientStateDelayMs);
if (temp.isValid()) {
*out_context = std::move(temp);
@@ -149,6 +151,39 @@ ndk::ScopedAStatus Module::createStreamContext(int32_t in_portConfigId, int64_t
return ndk::ScopedAStatus::ok();
}
std::vector<AudioDevice> Module::findConnectedDevices(int32_t portConfigId) {
std::vector<AudioDevice> result;
auto& ports = getConfig().ports;
auto portIds = portIdsFromPortConfigIds(findConnectedPortConfigIds(portConfigId));
for (auto it = portIds.begin(); it != portIds.end(); ++it) {
auto portIt = findById<AudioPort>(ports, *it);
if (portIt != ports.end() && portIt->ext.getTag() == AudioPortExt::Tag::device) {
result.push_back(portIt->ext.template get<AudioPortExt::Tag::device>().device);
}
}
return result;
}
std::set<int32_t> Module::findConnectedPortConfigIds(int32_t portConfigId) {
std::set<int32_t> result;
auto patchIdsRange = mPatches.equal_range(portConfigId);
auto& patches = getConfig().patches;
for (auto it = patchIdsRange.first; it != patchIdsRange.second; ++it) {
auto patchIt = findById<AudioPatch>(patches, it->second);
if (patchIt == patches.end()) {
LOG(FATAL) << __func__ << ": patch with id " << it->second << " taken from mPatches "
<< "not found in the configuration";
}
if (std::find(patchIt->sourcePortConfigIds.begin(), patchIt->sourcePortConfigIds.end(),
portConfigId) != patchIt->sourcePortConfigIds.end()) {
result.insert(patchIt->sinkPortConfigIds.begin(), patchIt->sinkPortConfigIds.end());
} else {
result.insert(patchIt->sourcePortConfigIds.begin(), patchIt->sourcePortConfigIds.end());
}
}
return result;
}
ndk::ScopedAStatus Module::findPortIdForNewStream(int32_t in_portConfigId, AudioPort** port) {
auto& configs = getConfig().portConfigs;
auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
@@ -187,6 +222,19 @@ ndk::ScopedAStatus Module::findPortIdForNewStream(int32_t in_portConfigId, Audio
return ndk::ScopedAStatus::ok();
}
template <typename C>
std::set<int32_t> Module::portIdsFromPortConfigIds(C portConfigIds) {
std::set<int32_t> result;
auto& portConfigs = getConfig().portConfigs;
for (auto it = portConfigIds.begin(); it != portConfigIds.end(); ++it) {
auto portConfigIt = findById<AudioPortConfig>(portConfigs, *it);
if (portConfigIt != portConfigs.end()) {
result.insert(portConfigIt->portId);
}
}
return result;
}
internal::Configuration& Module::getConfig() {
if (!mConfig) {
mConfig.reset(new internal::Configuration(internal::getNullPrimaryConfiguration()));
@@ -223,12 +271,16 @@ void Module::updateStreamsConnectedState(const AudioPatch& oldPatch, const Audio
idsToConnect.insert(newPatch.sinkPortConfigIds.begin(), newPatch.sinkPortConfigIds.end());
std::for_each(idsToDisconnect.begin(), idsToDisconnect.end(), [&](const auto& portConfigId) {
if (idsToConnect.count(portConfigId) == 0) {
mStreams.setStreamIsConnected(portConfigId, false);
LOG(DEBUG) << "The stream on port config id " << portConfigId << " is not connected";
mStreams.setStreamIsConnected(portConfigId, {});
}
});
std::for_each(idsToConnect.begin(), idsToConnect.end(), [&](const auto& portConfigId) {
if (idsToDisconnect.count(portConfigId) == 0) {
mStreams.setStreamIsConnected(portConfigId, true);
const auto connectedDevices = findConnectedDevices(portConfigId);
LOG(DEBUG) << "The stream on port config id " << portConfigId
<< " is connected to: " << ::android::internal::ToString(connectedDevices);
mStreams.setStreamIsConnected(portConfigId, connectedDevices);
}
});
}
@@ -468,14 +520,15 @@ ndk::ScopedAStatus Module::openInputStream(const OpenInputStreamArguments& in_ar
return status;
}
context.fillDescriptor(&_aidl_return->desc);
auto stream = ndk::SharedRefBase::make<StreamIn>(in_args.sinkMetadata, std::move(context));
auto stream = ndk::SharedRefBase::make<StreamIn>(in_args.sinkMetadata, std::move(context),
mConfig->microphones);
if (auto status = stream->init(); !status.isOk()) {
return status;
}
StreamWrapper streamWrapper(stream);
auto patchIt = mPatches.find(in_args.portConfigId);
if (patchIt != mPatches.end()) {
streamWrapper.setStreamIsConnected(true);
streamWrapper.setStreamIsConnected(findConnectedDevices(in_args.portConfigId));
}
mStreams.insert(port->id, in_args.portConfigId, std::move(streamWrapper));
_aidl_return->stream = std::move(stream);
@@ -525,7 +578,7 @@ ndk::ScopedAStatus Module::openOutputStream(const OpenOutputStreamArguments& in_
StreamWrapper streamWrapper(stream);
auto patchIt = mPatches.find(in_args.portConfigId);
if (patchIt != mPatches.end()) {
streamWrapper.setStreamIsConnected(true);
streamWrapper.setStreamIsConnected(findConnectedDevices(in_args.portConfigId));
}
mStreams.insert(port->id, in_args.portConfigId, std::move(streamWrapper));
_aidl_return->stream = std::move(stream);
@@ -844,6 +897,12 @@ ndk::ScopedAStatus Module::setMicMute(bool in_mute) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Module::getMicrophones(std::vector<MicrophoneInfo>* _aidl_return) {
*_aidl_return = mConfig->microphones;
LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Module::updateAudioMode(AudioMode in_mode) {
// No checks for supported audio modes here, it's an informative notification.
LOG(DEBUG) << __func__ << ": " << toString(in_mode);

View File

@@ -18,12 +18,17 @@
#include <android-base/logging.h>
#include <utils/SystemClock.h>
#include <Utils.h>
#include "core-impl/Module.h"
#include "core-impl/Stream.h"
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
using aidl::android::media::audio::common::AudioDevice;
using aidl::android::media::audio::common::AudioOffloadInfo;
using android::hardware::audio::common::getChannelCount;
using android::hardware::audio::common::getFrameSizeInBytes;
namespace aidl::android::hardware::audio::core {
@@ -35,13 +40,17 @@ void StreamContext::fillDescriptor(StreamDescriptor* desc) {
desc->reply = mReplyMQ->dupeDesc();
}
if (mDataMQ) {
desc->frameSizeBytes = mFrameSize;
desc->bufferSizeFrames =
mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / mFrameSize;
const size_t frameSize = getFrameSize();
desc->frameSizeBytes = frameSize;
desc->bufferSizeFrames = mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / frameSize;
desc->audio.set<StreamDescriptor::AudioBuffer::Tag::fmq>(mDataMQ->dupeDesc());
}
}
size_t StreamContext::getFrameSize() const {
return getFrameSizeInBytes(mFormat, mChannelLayout);
}
bool StreamContext::isValid() const {
if (mCommandMQ && !mCommandMQ->isValid()) {
LOG(ERROR) << "command FMQ is invalid";
@@ -51,8 +60,8 @@ bool StreamContext::isValid() const {
LOG(ERROR) << "reply FMQ is invalid";
return false;
}
if (mFrameSize == 0) {
LOG(ERROR) << "frame size is not set";
if (getFrameSize() == 0) {
LOG(ERROR) << "frame size is invalid";
return false;
}
if (mDataMQ && !mDataMQ->isValid()) {
@@ -530,11 +539,64 @@ ndk::ScopedAStatus StreamCommon<Metadata, StreamWorker>::updateMetadata(const Me
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
StreamIn::StreamIn(const SinkMetadata& sinkMetadata, StreamContext context)
: StreamCommon<SinkMetadata, StreamInWorker>(sinkMetadata, std::move(context)) {
namespace {
static std::map<AudioDevice, std::string> transformMicrophones(
const std::vector<MicrophoneInfo>& microphones) {
std::map<AudioDevice, std::string> result;
std::transform(microphones.begin(), microphones.end(), std::inserter(result, result.begin()),
[](const auto& mic) { return std::make_pair(mic.device, mic.id); });
return result;
}
} // namespace
StreamIn::StreamIn(const SinkMetadata& sinkMetadata, StreamContext context,
const std::vector<MicrophoneInfo>& microphones)
: StreamCommon<SinkMetadata, StreamInWorker>(sinkMetadata, std::move(context)),
mMicrophones(transformMicrophones(microphones)) {
LOG(DEBUG) << __func__;
}
ndk::ScopedAStatus StreamIn::getActiveMicrophones(
std::vector<MicrophoneDynamicInfo>* _aidl_return) {
std::vector<MicrophoneDynamicInfo> result;
std::vector<MicrophoneDynamicInfo::ChannelMapping> channelMapping{
getChannelCount(mContext.getChannelLayout()),
MicrophoneDynamicInfo::ChannelMapping::DIRECT};
for (auto it = mConnectedDevices.begin(); it != mConnectedDevices.end(); ++it) {
if (auto micIt = mMicrophones.find(*it); micIt != mMicrophones.end()) {
MicrophoneDynamicInfo dynMic;
dynMic.id = micIt->second;
dynMic.channelMapping = channelMapping;
result.push_back(std::move(dynMic));
}
}
*_aidl_return = std::move(result);
LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus StreamIn::getMicrophoneDirection(MicrophoneDirection* _aidl_return) {
LOG(DEBUG) << __func__;
(void)_aidl_return;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus StreamIn::setMicrophoneDirection(MicrophoneDirection in_direction) {
LOG(DEBUG) << __func__ << ": direction " << toString(in_direction);
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus StreamIn::getMicrophoneFieldDimension(float* _aidl_return) {
LOG(DEBUG) << __func__;
(void)_aidl_return;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus StreamIn::setMicrophoneFieldDimension(float in_zoom) {
LOG(DEBUG) << __func__ << ": zoom " << in_zoom;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
StreamOut::StreamOut(const SourceMetadata& sourceMetadata, StreamContext context,
const std::optional<AudioOffloadInfo>& offloadInfo)
: StreamCommon<SourceMetadata, StreamOutWorker>(sourceMetadata, std::move(context)),

View File

@@ -21,12 +21,14 @@
#include <aidl/android/hardware/audio/core/AudioPatch.h>
#include <aidl/android/hardware/audio/core/AudioRoute.h>
#include <aidl/android/hardware/audio/core/MicrophoneInfo.h>
#include <aidl/android/media/audio/common/AudioPort.h>
#include <aidl/android/media/audio/common/AudioPortConfig.h>
namespace aidl::android::hardware::audio::core::internal {
struct Configuration {
std::vector<MicrophoneInfo> microphones;
std::vector<::aidl::android::media::audio::common::AudioPort> ports;
std::vector<::aidl::android::media::audio::common::AudioPortConfig> portConfigs;
std::vector<::aidl::android::media::audio::common::AudioPortConfig> initialConfigs;

View File

@@ -77,6 +77,7 @@ class Module : public BnModule {
ndk::ScopedAStatus setMasterVolume(float in_volume) override;
ndk::ScopedAStatus getMicMute(bool* _aidl_return) override;
ndk::ScopedAStatus setMicMute(bool in_mute) override;
ndk::ScopedAStatus getMicrophones(std::vector<MicrophoneInfo>* _aidl_return) override;
ndk::ScopedAStatus updateAudioMode(
::aidl::android::hardware::audio::core::AudioMode in_mode) override;
ndk::ScopedAStatus updateScreenRotation(
@@ -88,9 +89,14 @@ class Module : public BnModule {
int32_t in_portConfigId, int64_t in_bufferSizeFrames,
std::shared_ptr<IStreamCallback> asyncCallback,
::aidl::android::hardware::audio::core::StreamContext* out_context);
std::vector<::aidl::android::media::audio::common::AudioDevice> findConnectedDevices(
int32_t portConfigId);
std::set<int32_t> findConnectedPortConfigIds(int32_t portConfigId);
ndk::ScopedAStatus findPortIdForNewStream(
int32_t in_portConfigId, ::aidl::android::media::audio::common::AudioPort** port);
internal::Configuration& getConfig();
template <typename C>
std::set<int32_t> portIdsFromPortConfigIds(C portConfigIds);
void registerPatch(const AudioPatch& patch);
void updateStreamsConnectedState(const AudioPatch& oldPatch, const AudioPatch& newPatch);

View File

@@ -30,7 +30,9 @@
#include <aidl/android/hardware/audio/core/BnStreamIn.h>
#include <aidl/android/hardware/audio/core/BnStreamOut.h>
#include <aidl/android/hardware/audio/core/IStreamCallback.h>
#include <aidl/android/hardware/audio/core/MicrophoneInfo.h>
#include <aidl/android/hardware/audio/core/StreamDescriptor.h>
#include <aidl/android/media/audio/common/AudioDevice.h>
#include <aidl/android/media/audio/common/AudioOffloadInfo.h>
#include <fmq/AidlMessageQueue.h>
#include <system/thread_defs.h>
@@ -61,12 +63,15 @@ class StreamContext {
StreamContext() = default;
StreamContext(std::unique_ptr<CommandMQ> commandMQ, std::unique_ptr<ReplyMQ> replyMQ,
size_t frameSize, std::unique_ptr<DataMQ> dataMQ,
std::shared_ptr<IStreamCallback> asyncCallback, int transientStateDelayMs)
const ::aidl::android::media::audio::common::AudioFormatDescription& format,
const ::aidl::android::media::audio::common::AudioChannelLayout& channelLayout,
std::unique_ptr<DataMQ> dataMQ, std::shared_ptr<IStreamCallback> asyncCallback,
int transientStateDelayMs)
: mCommandMQ(std::move(commandMQ)),
mInternalCommandCookie(std::rand()),
mReplyMQ(std::move(replyMQ)),
mFrameSize(frameSize),
mFormat(format),
mChannelLayout(channelLayout),
mDataMQ(std::move(dataMQ)),
mAsyncCallback(asyncCallback),
mTransientStateDelayMs(transientStateDelayMs) {}
@@ -74,7 +79,8 @@ class StreamContext {
: mCommandMQ(std::move(other.mCommandMQ)),
mInternalCommandCookie(other.mInternalCommandCookie),
mReplyMQ(std::move(other.mReplyMQ)),
mFrameSize(other.mFrameSize),
mFormat(other.mFormat),
mChannelLayout(other.mChannelLayout),
mDataMQ(std::move(other.mDataMQ)),
mAsyncCallback(other.mAsyncCallback),
mTransientStateDelayMs(other.mTransientStateDelayMs) {}
@@ -82,7 +88,8 @@ class StreamContext {
mCommandMQ = std::move(other.mCommandMQ);
mInternalCommandCookie = other.mInternalCommandCookie;
mReplyMQ = std::move(other.mReplyMQ);
mFrameSize = other.mFrameSize;
mFormat = std::move(other.mFormat);
mChannelLayout = std::move(other.mChannelLayout);
mDataMQ = std::move(other.mDataMQ);
mAsyncCallback = other.mAsyncCallback;
mTransientStateDelayMs = other.mTransientStateDelayMs;
@@ -91,9 +98,15 @@ class StreamContext {
void fillDescriptor(StreamDescriptor* desc);
std::shared_ptr<IStreamCallback> getAsyncCallback() const { return mAsyncCallback; }
::aidl::android::media::audio::common::AudioChannelLayout getChannelLayout() const {
return mChannelLayout;
}
CommandMQ* getCommandMQ() const { return mCommandMQ.get(); }
DataMQ* getDataMQ() const { return mDataMQ.get(); }
size_t getFrameSize() const { return mFrameSize; }
::aidl::android::media::audio::common::AudioFormatDescription getFormat() const {
return mFormat;
}
size_t getFrameSize() const;
int getInternalCommandCookie() const { return mInternalCommandCookie; }
ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
int getTransientStateDelayMs() const { return mTransientStateDelayMs; }
@@ -104,7 +117,8 @@ class StreamContext {
std::unique_ptr<CommandMQ> mCommandMQ;
int mInternalCommandCookie; // The value used to confirm that the command was posted internally
std::unique_ptr<ReplyMQ> mReplyMQ;
size_t mFrameSize;
::aidl::android::media::audio::common::AudioFormatDescription mFormat;
::aidl::android::media::audio::common::AudioChannelLayout mChannelLayout;
std::unique_ptr<DataMQ> mDataMQ;
std::shared_ptr<IStreamCallback> mAsyncCallback;
int mTransientStateDelayMs;
@@ -193,7 +207,11 @@ class StreamCommon {
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
bool isClosed() const { return mWorker.isClosed(); }
void setIsConnected(bool connected) { mWorker.setIsConnected(connected); }
void setIsConnected(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
mWorker.setIsConnected(!devices.empty());
mConnectedDevices = devices;
}
ndk::ScopedAStatus updateMetadata(const Metadata& metadata);
protected:
@@ -205,6 +223,7 @@ class StreamCommon {
Metadata mMetadata;
StreamContext mContext;
StreamWorker mWorker;
std::vector<::aidl::android::media::audio::common::AudioDevice> mConnectedDevices;
};
class StreamIn
@@ -214,6 +233,12 @@ class StreamIn
return StreamCommon<::aidl::android::hardware::audio::common::SinkMetadata,
StreamInWorker>::close();
}
ndk::ScopedAStatus getActiveMicrophones(
std::vector<MicrophoneDynamicInfo>* _aidl_return) override;
ndk::ScopedAStatus getMicrophoneDirection(MicrophoneDirection* _aidl_return) override;
ndk::ScopedAStatus setMicrophoneDirection(MicrophoneDirection in_direction) override;
ndk::ScopedAStatus getMicrophoneFieldDimension(float* _aidl_return) override;
ndk::ScopedAStatus setMicrophoneFieldDimension(float in_zoom) override;
ndk::ScopedAStatus updateMetadata(const ::aidl::android::hardware::audio::common::SinkMetadata&
in_sinkMetadata) override {
return StreamCommon<::aidl::android::hardware::audio::common::SinkMetadata,
@@ -222,7 +247,10 @@ class StreamIn
public:
StreamIn(const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext context);
StreamContext context, const std::vector<MicrophoneInfo>& microphones);
private:
const std::map<::aidl::android::media::audio::common::AudioDevice, std::string> mMicrophones;
};
class StreamOut : public StreamCommon<::aidl::android::hardware::audio::common::SourceMetadata,
@@ -261,11 +289,12 @@ class StreamWrapper {
},
mStream);
}
void setStreamIsConnected(bool connected) {
void setStreamIsConnected(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
std::visit(
[&](auto&& ws) {
auto s = ws.lock();
if (s) s->setIsConnected(connected);
if (s) s->setIsConnected(devices);
},
mStream);
}
@@ -288,9 +317,11 @@ class Streams {
mStreams.insert(std::pair{portConfigId, sw});
mStreams.insert(std::pair{portId, std::move(sw)});
}
void setStreamIsConnected(int32_t portConfigId, bool connected) {
void setStreamIsConnected(
int32_t portConfigId,
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
if (auto it = mStreams.find(portConfigId); it != mStreams.end()) {
it->second.setStreamIsConnected(connected);
it->second.setStreamIsConnected(devices);
}
}

View File

@@ -31,7 +31,7 @@ class AudioHalBinderServiceUtil {
public:
ndk::SpAIBinder connectToService(const std::string& serviceName) {
mServiceName = serviceName;
mBinder = ndk::SpAIBinder(AServiceManager_getService(serviceName.c_str()));
mBinder = ndk::SpAIBinder(AServiceManager_waitForService(serviceName.c_str()));
if (mBinder == nullptr) {
LOG(ERROR) << "Failed to get service " << serviceName;
} else {

View File

@@ -28,6 +28,7 @@ using namespace std::chrono_literals;
using aidl::android::hardware::audio::core::IModule;
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioDeviceType;
using aidl::android::media::audio::common::AudioEncapsulationMode;
using aidl::android::media::audio::common::AudioFormatDescription;
using aidl::android::media::audio::common::AudioFormatType;
@@ -62,6 +63,18 @@ std::optional<AudioOffloadInfo> ModuleConfig::generateOffloadInfoIfNeeded(
return {};
}
// static
std::vector<aidl::android::media::audio::common::AudioPort> ModuleConfig::getBuiltInMicPorts(
const std::vector<aidl::android::media::audio::common::AudioPort>& ports) {
std::vector<AudioPort> result;
std::copy_if(ports.begin(), ports.end(), std::back_inserter(result), [](const auto& port) {
const auto type = port.ext.template get<AudioPortExt::Tag::device>().device.type;
return type.connection.empty() && (type.type == AudioDeviceType::IN_MICROPHONE ||
type.type == AudioDeviceType::IN_MICROPHONE_BACK);
});
return result;
}
template <typename T>
auto findById(const std::vector<T>& v, int32_t id) {
return std::find_if(v.begin(), v.end(), [&](const auto& p) { return p.id == id; });
@@ -107,38 +120,45 @@ std::vector<AudioPort> ModuleConfig::getExternalDevicePorts() const {
return result;
}
std::vector<AudioPort> ModuleConfig::getInputMixPorts() const {
std::vector<AudioPort> ModuleConfig::getInputMixPorts(bool attachedOnly) const {
std::vector<AudioPort> result;
std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [](const auto& port) {
std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
return port.ext.getTag() == AudioPortExt::Tag::mix &&
port.flags.getTag() == AudioIoFlags::Tag::input;
port.flags.getTag() == AudioIoFlags::Tag::input &&
(!attachedOnly || !getAttachedSourceDevicesPortsForMixPort(port).empty());
});
return result;
}
std::vector<AudioPort> ModuleConfig::getOutputMixPorts() const {
std::vector<AudioPort> ModuleConfig::getOutputMixPorts(bool attachedOnly) const {
std::vector<AudioPort> result;
std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [](const auto& port) {
std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
return port.ext.getTag() == AudioPortExt::Tag::mix &&
port.flags.getTag() == AudioIoFlags::Tag::output;
port.flags.getTag() == AudioIoFlags::Tag::output &&
(!attachedOnly || !getAttachedSinkDevicesPortsForMixPort(port).empty());
});
return result;
}
std::vector<AudioPort> ModuleConfig::getNonBlockingMixPorts(bool attachedOnly,
bool singlePort) const {
return findMixPorts(false /*isInput*/, singlePort, [&](const AudioPort& port) {
return findMixPorts(false /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) {
return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
AudioOutputFlags::NON_BLOCKING) &&
(!attachedOnly || !getAttachedSinkDevicesPortsForMixPort(port).empty());
AudioOutputFlags::NON_BLOCKING);
});
}
std::vector<AudioPort> ModuleConfig::getOffloadMixPorts(bool attachedOnly, bool singlePort) const {
return findMixPorts(false /*isInput*/, singlePort, [&](const AudioPort& port) {
return findMixPorts(false /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) {
return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
AudioOutputFlags::COMPRESS_OFFLOAD) &&
(!attachedOnly || !getAttachedSinkDevicesPortsForMixPort(port).empty());
AudioOutputFlags::COMPRESS_OFFLOAD);
});
}
std::vector<AudioPort> ModuleConfig::getPrimaryMixPorts(bool attachedOnly, bool singlePort) const {
return findMixPorts(false /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) {
return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
AudioOutputFlags::PRIMARY);
});
}
@@ -193,7 +213,7 @@ std::optional<AudioPort> ModuleConfig::getSourceMixPortForAttachedDevice() const
std::optional<ModuleConfig::SrcSinkPair> ModuleConfig::getNonRoutableSrcSinkPair(
bool isInput) const {
const auto mixPorts = getMixPorts(isInput);
const auto mixPorts = getMixPorts(isInput, false /*attachedOnly*/);
std::set<std::pair<int32_t, int32_t>> allowedRoutes;
for (const auto& route : mRoutes) {
for (const auto srcPortId : route.sourcePortIds) {
@@ -344,9 +364,10 @@ static bool isDynamicProfile(const AudioProfile& profile) {
}
std::vector<AudioPort> ModuleConfig::findMixPorts(
bool isInput, bool singlePort, std::function<bool(const AudioPort&)> pred) const {
bool isInput, bool attachedOnly, bool singlePort,
const std::function<bool(const AudioPort&)>& pred) const {
std::vector<AudioPort> result;
const auto mixPorts = getMixPorts(isInput);
const auto mixPorts = getMixPorts(isInput, attachedOnly);
for (auto mixPortIt = mixPorts.begin(); mixPortIt != mixPorts.end();) {
mixPortIt = std::find_if(mixPortIt, mixPorts.end(), pred);
if (mixPortIt == mixPorts.end()) break;

View File

@@ -37,22 +37,32 @@ class ModuleConfig {
static std::optional<aidl::android::media::audio::common::AudioOffloadInfo>
generateOffloadInfoIfNeeded(
const aidl::android::media::audio::common::AudioPortConfig& portConfig);
static std::vector<aidl::android::media::audio::common::AudioPort> getBuiltInMicPorts(
const std::vector<aidl::android::media::audio::common::AudioPort>& ports);
explicit ModuleConfig(aidl::android::hardware::audio::core::IModule* module);
const ndk::ScopedAStatus& getStatus() const { return mStatus; }
std::string getError() const { return mStatus.getMessage(); }
std::vector<aidl::android::media::audio::common::AudioPort> getAttachedDevicePorts() const;
std::vector<aidl::android::media::audio::common::AudioPort> getAttachedMicrophonePorts() const {
return getBuiltInMicPorts(getAttachedDevicePorts());
}
std::vector<aidl::android::media::audio::common::AudioPort> getExternalDevicePorts() const;
std::vector<aidl::android::media::audio::common::AudioPort> getInputMixPorts() const;
std::vector<aidl::android::media::audio::common::AudioPort> getOutputMixPorts() const;
std::vector<aidl::android::media::audio::common::AudioPort> getMixPorts(bool isInput) const {
return isInput ? getInputMixPorts() : getOutputMixPorts();
std::vector<aidl::android::media::audio::common::AudioPort> getInputMixPorts(
bool attachedOnly) const;
std::vector<aidl::android::media::audio::common::AudioPort> getOutputMixPorts(
bool attachedOnly) const;
std::vector<aidl::android::media::audio::common::AudioPort> getMixPorts(
bool isInput, bool attachedOnly) const {
return isInput ? getInputMixPorts(attachedOnly) : getOutputMixPorts(attachedOnly);
}
std::vector<aidl::android::media::audio::common::AudioPort> getNonBlockingMixPorts(
bool attachedOnly, bool singlePort) const;
std::vector<aidl::android::media::audio::common::AudioPort> getOffloadMixPorts(
bool attachedOnly, bool singlePort) const;
std::vector<aidl::android::media::audio::common::AudioPort> getPrimaryMixPorts(
bool attachedOnly, bool singlePort) const;
std::vector<aidl::android::media::audio::common::AudioPort> getAttachedDevicesPortsForMixPort(
bool isInput, const aidl::android::media::audio::common::AudioPort& mixPort) const {
@@ -81,14 +91,17 @@ class ModuleConfig {
}
std::vector<aidl::android::media::audio::common::AudioPortConfig> getPortConfigsForMixPorts()
const {
auto inputs = generateAudioMixPortConfigs(getInputMixPorts(), true, false);
auto outputs = generateAudioMixPortConfigs(getOutputMixPorts(), false, false);
auto inputs =
generateAudioMixPortConfigs(getInputMixPorts(false /*attachedOnly*/), true, false);
auto outputs = generateAudioMixPortConfigs(getOutputMixPorts(false /*attachedOnly*/), false,
false);
inputs.insert(inputs.end(), outputs.begin(), outputs.end());
return inputs;
}
std::vector<aidl::android::media::audio::common::AudioPortConfig> getPortConfigsForMixPorts(
bool isInput) const {
return generateAudioMixPortConfigs(getMixPorts(isInput), isInput, false);
return generateAudioMixPortConfigs(getMixPorts(isInput, false /*attachedOnly*/), isInput,
false);
}
std::vector<aidl::android::media::audio::common::AudioPortConfig> getPortConfigsForMixPorts(
bool isInput, const aidl::android::media::audio::common::AudioPort& port) const {
@@ -96,7 +109,8 @@ class ModuleConfig {
}
std::optional<aidl::android::media::audio::common::AudioPortConfig> getSingleConfigForMixPort(
bool isInput) const {
const auto config = generateAudioMixPortConfigs(getMixPorts(isInput), isInput, true);
const auto config = generateAudioMixPortConfigs(
getMixPorts(isInput, false /*attachedOnly*/), isInput, true);
if (!config.empty()) {
return *config.begin();
}
@@ -125,8 +139,9 @@ class ModuleConfig {
private:
std::vector<aidl::android::media::audio::common::AudioPort> findMixPorts(
bool isInput, bool singlePort,
std::function<bool(const aidl::android::media::audio::common::AudioPort&)> pred) const;
bool isInput, bool attachedOnly, bool singlePort,
const std::function<bool(const aidl::android::media::audio::common::AudioPort&)>& pred)
const;
std::vector<aidl::android::media::audio::common::AudioPortConfig> generateAudioMixPortConfigs(
const std::vector<aidl::android::media::audio::common::AudioPort>& ports, bool isInput,
bool singleProfile) const;

View File

@@ -59,6 +59,8 @@ using aidl::android::hardware::audio::core::IModule;
using aidl::android::hardware::audio::core::IStreamIn;
using aidl::android::hardware::audio::core::IStreamOut;
using aidl::android::hardware::audio::core::ITelephony;
using aidl::android::hardware::audio::core::MicrophoneDynamicInfo;
using aidl::android::hardware::audio::core::MicrophoneInfo;
using aidl::android::hardware::audio::core::ModuleDebug;
using aidl::android::hardware::audio::core::StreamDescriptor;
using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
@@ -192,6 +194,7 @@ void TestAccessors(Instance* inst, Getter getter, Setter setter,
*isSupported = false;
return;
}
ASSERT_TRUE(status.isOk()) << "Unexpected status from a getter: " << status;
*isSupported = true;
for (const auto v : validValues) {
EXPECT_IS_OK((inst->*setter)(v)) << "for valid value: " << v;
@@ -1567,6 +1570,42 @@ TEST_P(AudioCoreModule, MicMute) {
// TODO: Test that mic mute actually mutes input.
}
TEST_P(AudioCoreModule, GetMicrophones) {
ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
const std::vector<AudioPort> builtInMicPorts = moduleConfig->getAttachedMicrophonePorts();
std::vector<MicrophoneInfo> micInfos;
ScopedAStatus status = module->getMicrophones(&micInfos);
if (!status.isOk()) {
EXPECT_EQ(EX_UNSUPPORTED_OPERATION, status.getExceptionCode());
ASSERT_FALSE(builtInMicPorts.empty())
<< "When the HAL module does not have built-in microphones, IModule.getMicrophones"
<< " must complete with no error and return an empty list";
GTEST_SKIP() << "Microphone info is not supported";
}
std::set<int32_t> micPortIdsWithInfo;
for (const auto& micInfo : micInfos) {
const auto& micDevice = micInfo.device;
const auto it =
std::find_if(builtInMicPorts.begin(), builtInMicPorts.end(), [&](const auto& port) {
return port.ext.template get<AudioPortExt::Tag::device>().device == micDevice;
});
if (it != builtInMicPorts.end()) {
micPortIdsWithInfo.insert(it->id);
} else {
ADD_FAILURE() << "No device port found with a device specified for the microphone \""
<< micInfo.id << "\": " << micDevice.toString();
}
}
if (micPortIdsWithInfo.size() != builtInMicPorts.size()) {
std::vector<AudioPort> micPortsNoInfo;
std::copy_if(builtInMicPorts.begin(), builtInMicPorts.end(),
std::back_inserter(micPortsNoInfo),
[&](const auto& port) { return micPortIdsWithInfo.count(port.id) == 0; });
ADD_FAILURE() << "No MicrophoneInfo is provided for the following microphone device ports: "
<< ::android::internal::ToString(micPortsNoInfo);
}
}
TEST_P(AudioCoreModule, UpdateAudioMode) {
for (const auto mode : ::ndk::enum_range<AudioMode>()) {
EXPECT_IS_OK(module->updateAudioMode(mode)) << toString(mode);
@@ -1747,13 +1786,11 @@ class AudioStream : public AudioCoreModule {
void OpenOverMaxCount() {
constexpr bool isInput = IOTraits<Stream>::is_input;
auto ports = moduleConfig->getMixPorts(isInput);
auto ports = moduleConfig->getMixPorts(isInput, true /*attachedOnly*/);
bool hasSingleRun = false;
for (const auto& port : ports) {
const size_t maxStreamCount = port.ext.get<AudioPortExt::Tag::mix>().maxOpenStreamCount;
if (maxStreamCount == 0 ||
moduleConfig->getAttachedDevicesPortsForMixPort(isInput, port).empty()) {
// No restrictions or no permanently attached devices.
if (maxStreamCount == 0) {
continue;
}
auto portConfigs = moduleConfig->getPortConfigsForMixPorts(isInput, port);
@@ -1878,20 +1915,127 @@ TEST_IN_AND_OUT_STREAM(OpenTwiceSamePortConfig);
TEST_IN_AND_OUT_STREAM(ResetPortConfigWithOpenStream);
TEST_IN_AND_OUT_STREAM(SendInvalidCommand);
namespace aidl::android::hardware::audio::core {
std::ostream& operator<<(std::ostream& os, const IStreamIn::MicrophoneDirection& md) {
os << toString(md);
return os;
}
} // namespace aidl::android::hardware::audio::core
TEST_P(AudioStreamIn, ActiveMicrophones) {
std::vector<MicrophoneInfo> micInfos;
ScopedAStatus status = module->getMicrophones(&micInfos);
if (!status.isOk()) {
GTEST_SKIP() << "Microphone info is not supported";
}
const auto ports = moduleConfig->getInputMixPorts(true /*attachedOnly*/);
if (ports.empty()) {
GTEST_SKIP() << "No input mix ports for attached devices";
}
for (const auto& port : ports) {
const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port);
ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for input mix port";
WithStream<IStreamIn> stream(portConfig.value());
ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
{
// The port of the stream is not connected, thus the list of active mics must be empty.
std::vector<MicrophoneDynamicInfo> activeMics;
EXPECT_IS_OK(stream.get()->getActiveMicrophones(&activeMics));
EXPECT_TRUE(activeMics.empty()) << "a stream on an unconnected port returns a "
"non-empty list of active microphones";
}
if (auto micDevicePorts = ModuleConfig::getBuiltInMicPorts(
moduleConfig->getAttachedSourceDevicesPortsForMixPort(port));
!micDevicePorts.empty()) {
auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(micDevicePorts[0]);
WithAudioPatch patch(true /*isInput*/, stream.getPortConfig(), devicePortConfig);
ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
std::vector<MicrophoneDynamicInfo> activeMics;
EXPECT_IS_OK(stream.get()->getActiveMicrophones(&activeMics));
for (const auto& mic : activeMics) {
EXPECT_NE(micInfos.end(),
std::find_if(micInfos.begin(), micInfos.end(),
[&](const auto& micInfo) { return micInfo.id == mic.id; }))
<< "active microphone \"" << mic.id << "\" is not listed in "
<< "microphone infos returned by the module: "
<< ::android::internal::ToString(micInfos);
EXPECT_NE(0UL, mic.channelMapping.size())
<< "No channels specified for the microphone \"" << mic.id << "\"";
}
}
{
// Now the port of the stream is not connected again, re-check that there are no
// active microphones.
std::vector<MicrophoneDynamicInfo> activeMics;
EXPECT_IS_OK(stream.get()->getActiveMicrophones(&activeMics));
EXPECT_TRUE(activeMics.empty()) << "a stream on an unconnected port returns a "
"non-empty list of active microphones";
}
}
}
TEST_P(AudioStreamIn, MicrophoneDirection) {
using MD = IStreamIn::MicrophoneDirection;
const auto ports = moduleConfig->getInputMixPorts(true /*attachedOnly*/);
if (ports.empty()) {
GTEST_SKIP() << "No input mix ports for attached devices";
}
bool isSupported = false;
for (const auto& port : ports) {
const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port);
ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for input mix port";
WithStream<IStreamIn> stream(portConfig.value());
ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
EXPECT_NO_FATAL_FAILURE(
TestAccessors<MD>(stream.get(), &IStreamIn::getMicrophoneDirection,
&IStreamIn::setMicrophoneDirection,
std::vector<MD>(enum_range<MD>().begin(), enum_range<MD>().end()),
{}, &isSupported));
if (!isSupported) break;
}
if (!isSupported) {
GTEST_SKIP() << "Microphone direction is not supported";
}
}
TEST_P(AudioStreamIn, MicrophoneFieldDimension) {
const auto ports = moduleConfig->getInputMixPorts(true /*attachedOnly*/);
if (ports.empty()) {
GTEST_SKIP() << "No input mix ports for attached devices";
}
bool isSupported = false;
for (const auto& port : ports) {
const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port);
ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for input mix port";
WithStream<IStreamIn> stream(portConfig.value());
ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
EXPECT_NO_FATAL_FAILURE(TestAccessors<float>(
stream.get(), &IStreamIn::getMicrophoneFieldDimension,
&IStreamIn::setMicrophoneFieldDimension,
{IStreamIn::MIC_FIELD_DIMENSION_WIDE_ANGLE,
IStreamIn::MIC_FIELD_DIMENSION_WIDE_ANGLE / 2.0f,
IStreamIn::MIC_FIELD_DIMENSION_NO_ZOOM,
IStreamIn::MIC_FIELD_DIMENSION_MAX_ZOOM / 2.0f,
IStreamIn::MIC_FIELD_DIMENSION_MAX_ZOOM},
{IStreamIn::MIC_FIELD_DIMENSION_WIDE_ANGLE * 2,
IStreamIn::MIC_FIELD_DIMENSION_MAX_ZOOM * 2,
IStreamIn::MIC_FIELD_DIMENSION_WIDE_ANGLE * 1.1f,
IStreamIn::MIC_FIELD_DIMENSION_MAX_ZOOM * 1.1f, -INFINITY, INFINITY, -NAN, NAN},
&isSupported));
if (!isSupported) break;
}
if (!isSupported) {
GTEST_SKIP() << "Microphone direction is not supported";
}
}
TEST_P(AudioStreamOut, OpenTwicePrimary) {
const auto mixPorts = moduleConfig->getMixPorts(false);
auto primaryPortIt = std::find_if(mixPorts.begin(), mixPorts.end(), [](const AudioPort& port) {
return port.flags.getTag() == AudioIoFlags::Tag::output &&
isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
AudioOutputFlags::PRIMARY);
});
if (primaryPortIt == mixPorts.end()) {
GTEST_SKIP() << "No primary mix port";
const auto mixPorts =
moduleConfig->getPrimaryMixPorts(true /*attachedOnly*/, true /*singlePort*/);
if (mixPorts.empty()) {
GTEST_SKIP() << "No primary mix port which could be routed to attached devices";
}
if (moduleConfig->getAttachedSinkDevicesPortsForMixPort(*primaryPortIt).empty()) {
GTEST_SKIP() << "Primary mix port can not be routed to any of attached devices";
}
const auto portConfig = moduleConfig->getSingleConfigForMixPort(false, *primaryPortIt);
const auto portConfig = moduleConfig->getSingleConfigForMixPort(false, *mixPorts.begin());
ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for the primary mix port";
EXPECT_NO_FATAL_FAILURE(OpenTwiceSamePortConfigImpl(portConfig.value()));
}