diff --git a/audio/aidl/Android.bp b/audio/aidl/Android.bp index 563ee626b5..691cf34177 100644 --- a/audio/aidl/Android.bp +++ b/audio/aidl/Android.bp @@ -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", diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl index 7f960e0b1b..0c7ca27c61 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl @@ -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); diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamIn.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamIn.aidl index d5ab3e8b22..e9c727f571 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamIn.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamIn.aidl @@ -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, + } } diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MicrophoneDynamicInfo.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MicrophoneDynamicInfo.aidl new file mode 100644 index 0000000000..50a5528309 --- /dev/null +++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MicrophoneDynamicInfo.aidl @@ -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 -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, + } +} diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MicrophoneInfo.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MicrophoneInfo.aidl new file mode 100644 index 0000000000..68c7f880f8 --- /dev/null +++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MicrophoneInfo.aidl @@ -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 -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; + } +} diff --git a/audio/aidl/android/hardware/audio/core/IModule.aidl b/audio/aidl/android/hardware/audio/core/IModule.aidl index 974e7e8c57..786d5eecbe 100644 --- a/audio/aidl/android/hardware/audio/core/IModule.aidl +++ b/audio/aidl/android/hardware/audio/core/IModule.aidl @@ -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. * diff --git a/audio/aidl/android/hardware/audio/core/IStreamIn.aidl b/audio/aidl/android/hardware/audio/core/IStreamIn.aidl index 0c3e3d1bb6..0b6e02cda7 100644 --- a/audio/aidl/android/hardware/audio/core/IStreamIn.aidl +++ b/audio/aidl/android/hardware/audio/core/IStreamIn.aidl @@ -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. * diff --git a/audio/aidl/android/hardware/audio/core/MicrophoneDynamicInfo.aidl b/audio/aidl/android/hardware/audio/core/MicrophoneDynamicInfo.aidl new file mode 100644 index 0000000000..36cc51fa00 --- /dev/null +++ b/audio/aidl/android/hardware/audio/core/MicrophoneDynamicInfo.aidl @@ -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; +} diff --git a/audio/aidl/android/hardware/audio/core/MicrophoneInfo.aidl b/audio/aidl/android/hardware/audio/core/MicrophoneInfo.aidl new file mode 100644 index 0000000000..3b8c7f37f1 --- /dev/null +++ b/audio/aidl/android/hardware/audio/core/MicrophoneInfo.aidl @@ -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; +} diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp index a3e5ff7d6a..280814f8a7 100644 --- a/audio/aidl/default/Configuration.cpp +++ b/audio/aidl/default/Configuration.cpp @@ -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().device; + mic.group = 0; + mic.indexInTheGroup = 0; + c.microphones = std::vector{mic}; + AudioPort primaryInMix = createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(2, 2)); primaryInMix.profiles.push_back( diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp index 9dbd61c442..1e561d4ec7 100644 --- a/audio/aidl/default/Module.cpp +++ b/audio/aidl/default/Module.cpp @@ -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(1, true /*configureEventFlagWord*/), std::make_unique(1, true /*configureEventFlagWord*/), - frameSize, std::make_unique(frameSize * in_bufferSizeFrames), + portConfigIt->format.value(), portConfigIt->channelMask.value(), + std::make_unique(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 Module::findConnectedDevices(int32_t portConfigId) { + std::vector result; + auto& ports = getConfig().ports; + auto portIds = portIdsFromPortConfigIds(findConnectedPortConfigIds(portConfigId)); + for (auto it = portIds.begin(); it != portIds.end(); ++it) { + auto portIt = findById(ports, *it); + if (portIt != ports.end() && portIt->ext.getTag() == AudioPortExt::Tag::device) { + result.push_back(portIt->ext.template get().device); + } + } + return result; +} + +std::set Module::findConnectedPortConfigIds(int32_t portConfigId) { + std::set result; + auto patchIdsRange = mPatches.equal_range(portConfigId); + auto& patches = getConfig().patches; + for (auto it = patchIdsRange.first; it != patchIdsRange.second; ++it) { + auto patchIt = findById(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(configs, in_portConfigId); @@ -187,6 +222,19 @@ ndk::ScopedAStatus Module::findPortIdForNewStream(int32_t in_portConfigId, Audio return ndk::ScopedAStatus::ok(); } +template +std::set Module::portIdsFromPortConfigIds(C portConfigIds) { + std::set result; + auto& portConfigs = getConfig().portConfigs; + for (auto it = portConfigIds.begin(); it != portConfigIds.end(); ++it) { + auto portConfigIt = findById(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(in_args.sinkMetadata, std::move(context)); + auto stream = ndk::SharedRefBase::make(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* _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); diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp index d7c352fe86..be5887c0e4 100644 --- a/audio/aidl/default/Stream.cpp +++ b/audio/aidl/default/Stream.cpp @@ -18,12 +18,17 @@ #include #include +#include + #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(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::updateMetadata(const Me return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } -StreamIn::StreamIn(const SinkMetadata& sinkMetadata, StreamContext context) - : StreamCommon(sinkMetadata, std::move(context)) { +namespace { +static std::map transformMicrophones( + const std::vector& microphones) { + std::map 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& microphones) + : StreamCommon(sinkMetadata, std::move(context)), + mMicrophones(transformMicrophones(microphones)) { LOG(DEBUG) << __func__; } +ndk::ScopedAStatus StreamIn::getActiveMicrophones( + std::vector* _aidl_return) { + std::vector result; + std::vector 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& offloadInfo) : StreamCommon(sourceMetadata, std::move(context)), diff --git a/audio/aidl/default/include/core-impl/Configuration.h b/audio/aidl/default/include/core-impl/Configuration.h index d5cd30b248..95adcd71a6 100644 --- a/audio/aidl/default/include/core-impl/Configuration.h +++ b/audio/aidl/default/include/core-impl/Configuration.h @@ -21,12 +21,14 @@ #include #include +#include #include #include namespace aidl::android::hardware::audio::core::internal { struct Configuration { + std::vector 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; diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h index f7b85ed7d8..3509327a3b 100644 --- a/audio/aidl/default/include/core-impl/Module.h +++ b/audio/aidl/default/include/core-impl/Module.h @@ -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* _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 asyncCallback, ::aidl::android::hardware::audio::core::StreamContext* out_context); + std::vector<::aidl::android::media::audio::common::AudioDevice> findConnectedDevices( + int32_t portConfigId); + std::set findConnectedPortConfigIds(int32_t portConfigId); ndk::ScopedAStatus findPortIdForNewStream( int32_t in_portConfigId, ::aidl::android::media::audio::common::AudioPort** port); internal::Configuration& getConfig(); + template + std::set portIdsFromPortConfigIds(C portConfigIds); void registerPatch(const AudioPatch& patch); void updateStreamsConnectedState(const AudioPatch& oldPatch, const AudioPatch& newPatch); diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h index 3c96973f2d..7a07eeb4f5 100644 --- a/audio/aidl/default/include/core-impl/Stream.h +++ b/audio/aidl/default/include/core-impl/Stream.h @@ -30,7 +30,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -61,12 +63,15 @@ class StreamContext { StreamContext() = default; StreamContext(std::unique_ptr commandMQ, std::unique_ptr replyMQ, - size_t frameSize, std::unique_ptr dataMQ, - std::shared_ptr asyncCallback, int transientStateDelayMs) + const ::aidl::android::media::audio::common::AudioFormatDescription& format, + const ::aidl::android::media::audio::common::AudioChannelLayout& channelLayout, + std::unique_ptr dataMQ, std::shared_ptr 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 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 mCommandMQ; int mInternalCommandCookie; // The value used to confirm that the command was posted internally std::unique_ptr mReplyMQ; - size_t mFrameSize; + ::aidl::android::media::audio::common::AudioFormatDescription mFormat; + ::aidl::android::media::audio::common::AudioChannelLayout mChannelLayout; std::unique_ptr mDataMQ; std::shared_ptr 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* _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& 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); } } diff --git a/audio/aidl/vts/AudioHalBinderServiceUtil.h b/audio/aidl/vts/AudioHalBinderServiceUtil.h index c8d81b1955..b4b46326a6 100644 --- a/audio/aidl/vts/AudioHalBinderServiceUtil.h +++ b/audio/aidl/vts/AudioHalBinderServiceUtil.h @@ -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 { diff --git a/audio/aidl/vts/ModuleConfig.cpp b/audio/aidl/vts/ModuleConfig.cpp index c081402106..7e4b148259 100644 --- a/audio/aidl/vts/ModuleConfig.cpp +++ b/audio/aidl/vts/ModuleConfig.cpp @@ -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 ModuleConfig::generateOffloadInfoIfNeeded( return {}; } +// static +std::vector ModuleConfig::getBuiltInMicPorts( + const std::vector& ports) { + std::vector result; + std::copy_if(ports.begin(), ports.end(), std::back_inserter(result), [](const auto& port) { + const auto type = port.ext.template get().device.type; + return type.connection.empty() && (type.type == AudioDeviceType::IN_MICROPHONE || + type.type == AudioDeviceType::IN_MICROPHONE_BACK); + }); + return result; +} + template auto findById(const std::vector& 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 ModuleConfig::getExternalDevicePorts() const { return result; } -std::vector ModuleConfig::getInputMixPorts() const { +std::vector ModuleConfig::getInputMixPorts(bool attachedOnly) const { std::vector 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 ModuleConfig::getOutputMixPorts() const { +std::vector ModuleConfig::getOutputMixPorts(bool attachedOnly) const { std::vector 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 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(), - AudioOutputFlags::NON_BLOCKING) && - (!attachedOnly || !getAttachedSinkDevicesPortsForMixPort(port).empty()); + AudioOutputFlags::NON_BLOCKING); }); } std::vector 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(), - AudioOutputFlags::COMPRESS_OFFLOAD) && - (!attachedOnly || !getAttachedSinkDevicesPortsForMixPort(port).empty()); + AudioOutputFlags::COMPRESS_OFFLOAD); + }); +} + +std::vector ModuleConfig::getPrimaryMixPorts(bool attachedOnly, bool singlePort) const { + return findMixPorts(false /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) { + return isBitPositionFlagSet(port.flags.get(), + AudioOutputFlags::PRIMARY); }); } @@ -193,7 +213,7 @@ std::optional ModuleConfig::getSourceMixPortForAttachedDevice() const std::optional ModuleConfig::getNonRoutableSrcSinkPair( bool isInput) const { - const auto mixPorts = getMixPorts(isInput); + const auto mixPorts = getMixPorts(isInput, false /*attachedOnly*/); std::set> allowedRoutes; for (const auto& route : mRoutes) { for (const auto srcPortId : route.sourcePortIds) { @@ -344,9 +364,10 @@ static bool isDynamicProfile(const AudioProfile& profile) { } std::vector ModuleConfig::findMixPorts( - bool isInput, bool singlePort, std::function pred) const { + bool isInput, bool attachedOnly, bool singlePort, + const std::function& pred) const { std::vector 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; diff --git a/audio/aidl/vts/ModuleConfig.h b/audio/aidl/vts/ModuleConfig.h index a85aa7f0d7..7247f3b582 100644 --- a/audio/aidl/vts/ModuleConfig.h +++ b/audio/aidl/vts/ModuleConfig.h @@ -37,22 +37,32 @@ class ModuleConfig { static std::optional generateOffloadInfoIfNeeded( const aidl::android::media::audio::common::AudioPortConfig& portConfig); + static std::vector getBuiltInMicPorts( + const std::vector& 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 getAttachedDevicePorts() const; + std::vector getAttachedMicrophonePorts() const { + return getBuiltInMicPorts(getAttachedDevicePorts()); + } std::vector getExternalDevicePorts() const; - std::vector getInputMixPorts() const; - std::vector getOutputMixPorts() const; - std::vector getMixPorts(bool isInput) const { - return isInput ? getInputMixPorts() : getOutputMixPorts(); + std::vector getInputMixPorts( + bool attachedOnly) const; + std::vector getOutputMixPorts( + bool attachedOnly) const; + std::vector getMixPorts( + bool isInput, bool attachedOnly) const { + return isInput ? getInputMixPorts(attachedOnly) : getOutputMixPorts(attachedOnly); } std::vector getNonBlockingMixPorts( bool attachedOnly, bool singlePort) const; std::vector getOffloadMixPorts( bool attachedOnly, bool singlePort) const; + std::vector getPrimaryMixPorts( + bool attachedOnly, bool singlePort) const; std::vector getAttachedDevicesPortsForMixPort( bool isInput, const aidl::android::media::audio::common::AudioPort& mixPort) const { @@ -81,14 +91,17 @@ class ModuleConfig { } std::vector 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 getPortConfigsForMixPorts( bool isInput) const { - return generateAudioMixPortConfigs(getMixPorts(isInput), isInput, false); + return generateAudioMixPortConfigs(getMixPorts(isInput, false /*attachedOnly*/), isInput, + false); } std::vector getPortConfigsForMixPorts( bool isInput, const aidl::android::media::audio::common::AudioPort& port) const { @@ -96,7 +109,8 @@ class ModuleConfig { } std::optional 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 findMixPorts( - bool isInput, bool singlePort, - std::function pred) const; + bool isInput, bool attachedOnly, bool singlePort, + const std::function& pred) + const; std::vector generateAudioMixPortConfigs( const std::vector& ports, bool isInput, bool singleProfile) const; diff --git a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp index 79b20fe0b7..c0c04f42a5 100644 --- a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp +++ b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp @@ -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 builtInMicPorts = moduleConfig->getAttachedMicrophonePorts(); + std::vector 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 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().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 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()) { EXPECT_IS_OK(module->updateAudioMode(mode)) << toString(mode); @@ -1747,13 +1786,11 @@ class AudioStream : public AudioCoreModule { void OpenOverMaxCount() { constexpr bool isInput = IOTraits::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().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 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 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 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 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 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 stream(portConfig.value()); + ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); + EXPECT_NO_FATAL_FAILURE( + TestAccessors(stream.get(), &IStreamIn::getMicrophoneDirection, + &IStreamIn::setMicrophoneDirection, + std::vector(enum_range().begin(), enum_range().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 stream(portConfig.value()); + ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames)); + EXPECT_NO_FATAL_FAILURE(TestAccessors( + 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(), - 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())); }