audio policy: audio sessions on input descriptors
In preparation for concurrent capture, add support for multiple
audio sessions per input stream.
Each session keeps its own properties, open and active reference
counting.
No functional change for now: still one session per input and one active
input at a time.
Bug: 18815985.
Bug: 22702906.
Change-Id: I915a65989a7fd0d3cbe2fcf5a0aee2ea0df5f4f5
diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk
index dc7eff7..d3df3ef 100644
--- a/services/audiopolicy/common/managerdefinitions/Android.mk
+++ b/services/audiopolicy/common/managerdefinitions/Android.mk
@@ -18,7 +18,8 @@
src/SoundTriggerSession.cpp \
src/SessionRoute.cpp \
src/AudioSourceDescriptor.cpp \
- src/TypeConverter.cpp
+ src/TypeConverter.cpp \
+ src/AudioSession.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
index 6d04811..7e5ef5d 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
@@ -17,6 +17,7 @@
#pragma once
#include "AudioPort.h"
+#include "AudioSession.h"
#include <utils/Errors.h>
#include <system/audio.h>
#include <utils/SortedVector.h>
@@ -36,7 +37,6 @@
void setIoHandle(audio_io_handle_t ioHandle);
audio_port_handle_t getId() const;
audio_module_handle_t getModuleHandle() const;
- void changeOpenRefCount(int delta);
uint32_t getOpenRefCount() const;
status_t dump(int fd);
@@ -45,13 +45,7 @@
audio_devices_t mDevice; // current device this input is routed to
AudioMix *mPolicyMix; // non NULL when used by a dynamic policy
audio_patch_handle_t mPatchHandle;
- uint32_t mRefCount; // number of AudioRecord clients active on
- // this input
- audio_source_t mInputSource; // input source selected by application
- //(mediarecorder.h)
const sp<IOProfile> mProfile; // I/O profile this output derives from
- SortedVector<audio_session_t> mSessions; // audio sessions attached to this input
- bool mIsSoundTrigger; // used by a soundtrigger capture
virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
const struct audio_port_config *srcConfig = NULL) const;
@@ -61,10 +55,20 @@
SortedVector<audio_session_t> getPreemptedSessions() const;
bool hasPreemptedSession(audio_session_t session) const;
void clearPreemptedSessions();
+ bool isActive() const;
+ bool isSourceActive(audio_source_t source) const;
+ audio_source_t inputSource() const;
+ bool isSoundTrigger() const;
+ status_t addAudioSession(audio_session_t session,
+ const sp<AudioSession>& audioSession);
+ status_t removeAudioSession(audio_session_t session);
+ sp<AudioSession> getAudioSession(audio_session_t session) const;
+ AudioSessionCollection getActiveAudioSessions() const;
private:
audio_port_handle_t mId;
- uint32_t mOpenRefCount;
+ // audio sessions attached to this input
+ AudioSessionCollection mSessions;
// Because a preemtible capture session can preempt another one, we end up in an endless loop
// situation were each session is allowed to restart after being preempted,
// thus preempting the other one which restarts and so on.
@@ -72,7 +76,6 @@
// a particular input started and prevent preemption of this active input by this session.
// We also inherit sessions from the preempted input to avoid a 3 way preemption loop etc...
SortedVector<audio_session_t> mPreemptedSessions;
-
};
class AudioInputCollection :
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
new file mode 100644
index 0000000..6feef80
--- /dev/null
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <system/audio.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+class AudioSession : public RefBase
+{
+public:
+ AudioSession(audio_session_t session,
+ audio_source_t inputSource,
+ audio_format_t format,
+ uint32_t sampleRate,
+ audio_channel_mask_t channelMask,
+ audio_input_flags_t flags,
+ uid_t uid,
+ bool isSoundTrigger);
+
+ status_t dump(int fd, int spaces, int index) const;
+
+ audio_session_t session() const { return mSession; }
+ audio_source_t inputSource()const { return mInputSource; }
+ audio_format_t format() const { return mFormat; }
+ uint32_t sampleRate() const { return mSampleRate; }
+ audio_channel_mask_t channelMask() const { return mChannelMask; }
+ audio_input_flags_t flags() const { return mFlags; }
+ uid_t uid() const { return mUid; }
+ bool matches(const sp<AudioSession> &other) const;
+ bool isSoundTrigger() const { return mIsSoundTrigger; }
+ uint32_t openCount() const { return mOpenCount; } ;
+ uint32_t activeCount() const { return mActiveCount; } ;
+
+ uint32_t changeOpenCount(int delta);
+ uint32_t changeActiveCount(int delta);
+
+private:
+ const audio_session_t mSession;
+ const audio_source_t mInputSource;
+ const audio_format_t mFormat;
+ const uint32_t mSampleRate;
+ const audio_channel_mask_t mChannelMask;
+ const audio_input_flags_t mFlags;
+ const uid_t mUid;
+ bool mIsSoundTrigger;
+ uint32_t mOpenCount;
+ uint32_t mActiveCount;
+};
+
+class AudioSessionCollection :
+ public DefaultKeyedVector<audio_session_t, sp<AudioSession> >
+{
+public:
+ status_t addSession(audio_session_t session,
+ const sp<AudioSession>& audioSession);
+
+ status_t removeSession(audio_session_t session);
+
+ uint32_t getOpenCount() const;
+
+ AudioSessionCollection getActiveSessions() const;
+ bool hasActiveSession() const;
+ bool isSourceActive(audio_source_t source) const;
+
+ status_t dump(int fd, int spaces) const;
+};
+
+}; // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index e828cc0..76470c5 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -28,9 +28,8 @@
AudioInputDescriptor::AudioInputDescriptor(const sp<IOProfile>& profile)
: mIoHandle(0),
- mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL), mPatchHandle(0), mRefCount(0),
- mInputSource(AUDIO_SOURCE_DEFAULT), mProfile(profile), mIsSoundTrigger(false), mId(0),
- mOpenRefCount(0)
+ mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL), mPatchHandle(0),
+ mProfile(profile), mId(0)
{
if (profile != NULL) {
mSamplingRate = profile->pickSamplingRate();
@@ -56,20 +55,9 @@
return mProfile->getModuleHandle();
}
-void AudioInputDescriptor::changeOpenRefCount(int delta)
-{
- if ((delta + (int)mOpenRefCount) < 0) {
- ALOGW("changeOpenRefCount() invalid delta %d, refCount %d", delta, mOpenRefCount);
- mOpenRefCount = 0;
- return;
- }
- mOpenRefCount += delta;
- ALOGV("changeOpenRefCount() count %d", mOpenRefCount);
-}
-
uint32_t AudioInputDescriptor::getOpenRefCount() const
{
- return mOpenRefCount;
+ return mSessions.getOpenCount();
}
audio_port_handle_t AudioInputDescriptor::getId() const
@@ -77,6 +65,13 @@
return mId;
}
+audio_source_t AudioInputDescriptor::inputSource() const
+{
+ // TODO: return highest priority input source
+ return mSessions.size() > 0 ? mSessions.valueAt(0)->inputSource() :
+ AUDIO_SOURCE_DEFAULT;
+}
+
void AudioInputDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig,
const struct audio_port_config *srcConfig) const
{
@@ -95,7 +90,7 @@
dstConfig->type = AUDIO_PORT_TYPE_MIX;
dstConfig->ext.mix.hw_module = getModuleHandle();
dstConfig->ext.mix.handle = mIoHandle;
- dstConfig->ext.mix.usecase.source = mInputSource;
+ dstConfig->ext.mix.usecase.source = inputSource();
}
void AudioInputDescriptor::toAudioPort(struct audio_port *port) const
@@ -130,6 +125,40 @@
mPreemptedSessions.clear();
}
+bool AudioInputDescriptor::isActive() const {
+ return mSessions.hasActiveSession();
+}
+
+bool AudioInputDescriptor::isSourceActive(audio_source_t source) const
+{
+ return mSessions.isSourceActive(source);
+}
+
+bool AudioInputDescriptor::isSoundTrigger() const {
+ // sound trigger and non sound trigger sessions are not mixed
+ // on a given input
+ return mSessions.valueAt(0)->isSoundTrigger();
+}
+
+sp<AudioSession> AudioInputDescriptor::getAudioSession(
+ audio_session_t session) const {
+ return mSessions.valueFor(session);
+}
+
+AudioSessionCollection AudioInputDescriptor::getActiveAudioSessions() const
+{
+ return mSessions.getActiveSessions();
+}
+
+status_t AudioInputDescriptor::addAudioSession(audio_session_t session,
+ const sp<AudioSession>& audioSession) {
+ return mSessions.addSession(session, audioSession);
+}
+
+status_t AudioInputDescriptor::removeAudioSession(audio_session_t session) {
+ return mSessions.removeSession(session);
+}
+
status_t AudioInputDescriptor::dump(int fd)
{
const size_t SIZE = 256;
@@ -146,13 +175,11 @@
result.append(buffer);
snprintf(buffer, SIZE, " Devices %08x\n", mDevice);
result.append(buffer);
- snprintf(buffer, SIZE, " Ref Count %d\n", mRefCount);
- result.append(buffer);
- snprintf(buffer, SIZE, " Open Ref Count %d\n", mOpenRefCount);
- result.append(buffer);
write(fd, result.string(), result.size());
+ mSessions.dump(fd, 1);
+
return NO_ERROR;
}
@@ -160,10 +187,7 @@
{
for (size_t i = 0; i < size(); i++) {
const sp<AudioInputDescriptor> inputDescriptor = valueAt(i);
- if (inputDescriptor->mRefCount == 0) {
- continue;
- }
- if (inputDescriptor->mInputSource == (int)source) {
+ if (inputDescriptor->isSourceActive(source)) {
return true;
}
}
@@ -186,8 +210,8 @@
{
uint32_t count = 0;
for (size_t i = 0; i < size(); i++) {
- const sp<AudioInputDescriptor> desc = valueAt(i);
- if (desc->mRefCount > 0) {
+ const sp<AudioInputDescriptor> inputDescriptor = valueAt(i);
+ if (inputDescriptor->isActive()) {
count++;
}
}
@@ -197,9 +221,10 @@
audio_io_handle_t AudioInputCollection::getActiveInput(bool ignoreVirtualInputs)
{
for (size_t i = 0; i < size(); i++) {
- const sp<AudioInputDescriptor> input_descriptor = valueAt(i);
- if ((input_descriptor->mRefCount > 0)
- && (!ignoreVirtualInputs || !is_virtual_input_device(input_descriptor->mDevice))) {
+ const sp<AudioInputDescriptor> inputDescriptor = valueAt(i);
+ if ((inputDescriptor->isActive())
+ && (!ignoreVirtualInputs ||
+ !is_virtual_input_device(inputDescriptor->mDevice))) {
return keyAt(i);
}
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp
new file mode 100644
index 0000000..2cec951
--- /dev/null
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "APM::AudioSession"
+//#define LOG_NDEBUG 0
+
+#include "AudioSession.h"
+#include "AudioGain.h"
+#include "TypeConverter.h"
+#include <cutils/log.h>
+#include <utils/String8.h>
+
+namespace android {
+
+AudioSession::AudioSession(audio_session_t session,
+ audio_source_t inputSource,
+ audio_format_t format,
+ uint32_t sampleRate,
+ audio_channel_mask_t channelMask,
+ audio_input_flags_t flags,
+ uid_t uid,
+ bool isSoundTrigger) :
+ mSession(session), mInputSource(inputSource),
+ mFormat(format), mSampleRate(sampleRate), mChannelMask(channelMask),
+ mFlags(flags), mUid(uid), mIsSoundTrigger(isSoundTrigger),
+ mOpenCount(1), mActiveCount(0)
+{
+}
+
+uint32_t AudioSession::changeOpenCount(int delta)
+{
+ if ((delta + (int)mOpenCount) < 0) {
+ ALOGW("%s invalid delta %d, open count %d",
+ __FUNCTION__, delta, mOpenCount);
+ mOpenCount = (uint32_t)(-delta);
+ }
+ mOpenCount += delta;
+ ALOGV("%s open count %d", __FUNCTION__, mOpenCount);
+ return mOpenCount;
+}
+
+uint32_t AudioSession::changeActiveCount(int delta)
+{
+ if ((delta + (int)mActiveCount) < 0) {
+ ALOGW("%s invalid delta %d, active count %d",
+ __FUNCTION__, delta, mActiveCount);
+ mActiveCount = (uint32_t)(-delta);
+ }
+ mActiveCount += delta;
+ ALOGV("%s active count %d", __FUNCTION__, mActiveCount);
+ return mActiveCount;
+}
+
+bool AudioSession::matches(const sp<AudioSession> &other) const
+{
+ if (other->session() == mSession &&
+ other->inputSource() == mInputSource &&
+ other->format() == mFormat &&
+ other->sampleRate() == mSampleRate &&
+ other->channelMask() == mChannelMask &&
+ other->flags() == mFlags &&
+ other->uid() == mUid) {
+ return true;
+ }
+ return false;
+}
+
+
+status_t AudioSession::dump(int fd, int spaces, int index) const
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "%*sAudio session %d:\n", spaces, "", index+1);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- session: %2d\n", spaces, "", mSession);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- owner uid: %2d\n", spaces, "", mUid);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- input source: %d\n", spaces, "", mInputSource);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- format: %08x\n", spaces, "", mFormat);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- sample: %d\n", spaces, "", mSampleRate);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- channel mask: %08x\n",
+ spaces, "", mChannelMask);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- is soundtrigger: %s\n",
+ spaces, "", mIsSoundTrigger ? "true" : "false");
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- open count: %d\n", spaces, "", mOpenCount);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- active count: %d\n", spaces, "", mActiveCount);
+ result.append(buffer);
+
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+status_t AudioSessionCollection::addSession(audio_session_t session,
+ const sp<AudioSession>& audioSession)
+{
+ ssize_t index = indexOfKey(session);
+
+ if (index >= 0) {
+ ALOGW("addSession() session %d already in", session);
+ return ALREADY_EXISTS;
+ }
+ add(session, audioSession);
+ ALOGV("addSession() session %d client %d source %d",
+ session, audioSession->uid(), audioSession->inputSource());
+ return NO_ERROR;
+}
+
+status_t AudioSessionCollection::removeSession(audio_session_t session)
+{
+ ssize_t index = indexOfKey(session);
+
+ if (index < 0) {
+ ALOGW("removeSession() session %d not in", session);
+ return ALREADY_EXISTS;
+ }
+ ALOGV("removeSession() session %d", session);
+ removeItemsAt(index);
+ return NO_ERROR;
+}
+
+uint32_t AudioSessionCollection::getOpenCount() const
+{
+ uint32_t openCount = 0;
+ for (size_t i = 0; i < size(); i++) {
+ openCount += valueAt(i)->openCount();
+ }
+ return openCount;
+}
+
+AudioSessionCollection AudioSessionCollection::getActiveSessions() const
+{
+ AudioSessionCollection activeSessions;
+ for (size_t i = 0; i < size(); i++) {
+ if (valueAt(i)->activeCount() != 0) {
+ activeSessions.add(valueAt(i)->session(), valueAt(i));
+ }
+ }
+ return activeSessions;
+}
+
+bool AudioSessionCollection::hasActiveSession() const
+{
+ return getActiveSessions().size() != 0;
+}
+
+bool AudioSessionCollection::isSourceActive(audio_source_t source) const
+{
+ for (size_t i = 0; i < size(); i++) {
+ const sp<AudioSession> audioSession = valueAt(i);
+ // AUDIO_SOURCE_HOTWORD is equivalent to AUDIO_SOURCE_VOICE_RECOGNITION only if it
+ // corresponds to an active capture triggered by a hardware hotword recognition
+ if (audioSession->activeCount() > 0 &&
+ ((audioSession->inputSource() == source) ||
+ ((source == AUDIO_SOURCE_VOICE_RECOGNITION) &&
+ (audioSession->inputSource() == AUDIO_SOURCE_HOTWORD) &&
+ audioSession->isSoundTrigger()))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+status_t AudioSessionCollection::dump(int fd, int spaces) const
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ snprintf(buffer, SIZE, "%*sAudio Sessions:\n", spaces, "");
+ write(fd, buffer, strlen(buffer));
+ for (size_t i = 0; i < size(); i++) {
+ valueAt(i)->dump(fd, spaces + 2, i);
+ }
+ return NO_ERROR;
+}
+
+}; // namespace android
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 4a67c4a..322e599 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -418,7 +418,9 @@
if (activeInput != 0) {
sp<AudioInputDescriptor> activeDesc = mInputs.valueFor(activeInput);
if (activeDesc->getModuleHandle() == txSourceDeviceDesc->getModuleHandle()) {
- audio_session_t activeSession = activeDesc->mSessions.itemAt(0);
+ //FIXME: consider all active sessions
+ AudioSessionCollection activeSessions = activeDesc->getActiveAudioSessions();
+ audio_session_t activeSession = activeSessions.keyAt(0);
stopInput(activeInput, activeSession);
releaseInput(activeInput, activeSession);
}
@@ -1329,7 +1331,6 @@
audio_devices_t device;
// handle legacy remote submix case where the address was not always specified
String8 address = String8("");
- bool isSoundTrigger = false;
audio_source_t inputSource = attr->source;
audio_source_t halInputSource;
AudioMix *policyMix = NULL;
@@ -1385,16 +1386,44 @@
*inputType = API_INPUT_LEGACY;
}
- if (inputSource == AUDIO_SOURCE_HOTWORD) {
- ssize_t index = mSoundTriggerSessions.indexOfKey(session);
- if (index >= 0) {
- *input = mSoundTriggerSessions.valueFor(session);
- isSoundTrigger = true;
- flags = (audio_input_flags_t)(flags | AUDIO_INPUT_FLAG_HW_HOTWORD);
- ALOGV("SoundTrigger capture on session %d input %d", session, *input);
- } else {
- halInputSource = AUDIO_SOURCE_VOICE_RECOGNITION;
- }
+ }
+
+ *input = getInputForDevice(device, address, session, uid, inputSource,
+ samplingRate, format, channelMask, flags,
+ policyMix);
+ if (*input == AUDIO_IO_HANDLE_NONE) {
+ mInputRoutes.removeRoute(session);
+ return INVALID_OPERATION;
+ }
+ ALOGV("getInputForAttr() returns input type = %d", *inputType);
+ return NO_ERROR;
+}
+
+
+audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device,
+ String8 address,
+ audio_session_t session,
+ uid_t uid,
+ audio_source_t inputSource,
+ uint32_t samplingRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ audio_input_flags_t flags,
+ AudioMix *policyMix)
+{
+ audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
+ audio_source_t halInputSource = inputSource;
+ bool isSoundTrigger = false;
+
+ if (inputSource == AUDIO_SOURCE_HOTWORD) {
+ ssize_t index = mSoundTriggerSessions.indexOfKey(session);
+ if (index >= 0) {
+ input = mSoundTriggerSessions.valueFor(session);
+ isSoundTrigger = true;
+ flags = (audio_input_flags_t)(flags | AUDIO_INPUT_FLAG_HW_HOTWORD);
+ ALOGV("SoundTrigger capture on session %d input %d", session, input);
+ } else {
+ halInputSource = AUDIO_SOURCE_VOICE_RECOGNITION;
}
}
@@ -1414,25 +1443,62 @@
} else if (profileFlags != AUDIO_INPUT_FLAG_NONE) {
profileFlags = AUDIO_INPUT_FLAG_NONE; // retry
} else { // fail
- ALOGW("getInputForAttr() could not find profile for device 0x%X, samplingRate %u,"
- "format %#x, channelMask 0x%X, flags %#x",
+ ALOGW("getInputForDevice() could not find profile for device 0x%X,"
+ "samplingRate %u, format %#x, channelMask 0x%X, flags %#x",
device, samplingRate, format, channelMask, flags);
- return BAD_VALUE;
+ return input;
}
}
if (profile->getModuleHandle() == 0) {
ALOGE("getInputForAttr(): HW module %s not opened", profile->getModuleName());
- return NO_INIT;
+ return input;
}
+ sp<AudioSession> audioSession = new AudioSession(session,
+ inputSource,
+ format,
+ samplingRate,
+ channelMask,
+ flags,
+ uid,
+ isSoundTrigger);
+
+// TODO enable input reuse
+#if 0
+ // reuse an open input if possible
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ sp<AudioInputDescriptor> desc = mInputs.valueAt(i);
+ // reuse input if it shares the same profile and same sound trigger attribute
+ if (profile == desc->mProfile &&
+ isSoundTrigger == desc->isSoundTrigger()) {
+
+ sp<AudioSession> as = desc->getAudioSession(session);
+ if (as != 0) {
+ // do not allow unmatching properties on same session
+ if (as->matches(audioSession)) {
+ as->changeOpenCount(1);
+ } else {
+ ALOGW("getInputForDevice() record with different attributes"
+ " exists for session %d", session);
+ return input;
+ }
+ } else {
+ desc->addAudioSession(session, audioSession);
+ }
+ ALOGV("getInputForDevice() reusing input %d", mInputs.keyAt(i));
+ return mInputs.keyAt(i);
+ }
+ }
+#endif
+
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = profileSamplingRate;
config.channel_mask = profileChannelMask;
config.format = profileFormat;
status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
- input,
+ &input,
&config,
&device,
address,
@@ -1440,37 +1506,31 @@
profileFlags);
// only accept input with the exact requested set of parameters
- if (status != NO_ERROR || *input == AUDIO_IO_HANDLE_NONE ||
+ if (status != NO_ERROR || input == AUDIO_IO_HANDLE_NONE ||
(profileSamplingRate != config.sample_rate) ||
(profileFormat != config.format) ||
(profileChannelMask != config.channel_mask)) {
- ALOGW("getInputForAttr() failed opening input: samplingRate %d, format %d,"
- " channelMask %x",
+ ALOGW("getInputForAttr() failed opening input: samplingRate %d"
+ ", format %d, channelMask %x",
samplingRate, format, channelMask);
- if (*input != AUDIO_IO_HANDLE_NONE) {
- mpClientInterface->closeInput(*input);
+ if (input != AUDIO_IO_HANDLE_NONE) {
+ mpClientInterface->closeInput(input);
}
- return BAD_VALUE;
+ return AUDIO_IO_HANDLE_NONE;
}
sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(profile);
- inputDesc->mInputSource = inputSource;
- inputDesc->mRefCount = 0;
inputDesc->mSamplingRate = profileSamplingRate;
inputDesc->mFormat = profileFormat;
inputDesc->mChannelMask = profileChannelMask;
inputDesc->mDevice = device;
- inputDesc->mSessions.add(session);
- inputDesc->mIsSoundTrigger = isSoundTrigger;
inputDesc->mPolicyMix = policyMix;
- inputDesc->changeOpenRefCount(1);
+ inputDesc->addAudioSession(session, audioSession);
- ALOGV("getInputForAttr() returns input type = %d", *inputType);
-
- addInput(*input, inputDesc);
+ addInput(input, inputDesc);
mpClientInterface->onAudioPortListUpdate();
- return NO_ERROR;
+ return input;
}
status_t AudioPolicyManager::startInput(audio_io_handle_t input,
@@ -1484,8 +1544,8 @@
}
sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index);
- index = inputDesc->mSessions.indexOf(session);
- if (index < 0) {
+ sp<AudioSession> audioSession = inputDesc->getAudioSession(session);
+ if (audioSession == 0) {
ALOGW("startInput() unknown session %d on input %d", session, input);
return BAD_VALUE;
}
@@ -1500,11 +1560,14 @@
// If the already active input uses AUDIO_SOURCE_HOTWORD then it is closed,
// otherwise the active input continues and the new input cannot be started.
sp<AudioInputDescriptor> activeDesc = mInputs.valueFor(activeInput);
- if ((activeDesc->mInputSource == AUDIO_SOURCE_HOTWORD) &&
+ if ((activeDesc->inputSource() == AUDIO_SOURCE_HOTWORD) &&
!activeDesc->hasPreemptedSession(session)) {
ALOGW("startInput(%d) preempting low-priority input %d", input, activeInput);
- audio_session_t activeSession = activeDesc->mSessions.itemAt(0);
- SortedVector<audio_session_t> sessions = activeDesc->getPreemptedSessions();
+ //FIXME: consider all active sessions
+ AudioSessionCollection activeSessions = activeDesc->getActiveAudioSessions();
+ audio_session_t activeSession = activeSessions.keyAt(0);
+ SortedVector<audio_session_t> sessions =
+ activeDesc->getPreemptedSessions();
sessions.add(activeSession);
inputDesc->setPreemptedSessions(sessions);
stopInput(activeInput, activeSession);
@@ -1528,7 +1591,7 @@
// Routing?
mInputRoutes.incRouteActivity(session);
- if (inputDesc->mRefCount == 0 || mInputRoutes.hasRouteChanged(session)) {
+ if (!inputDesc->isActive() || mInputRoutes.hasRouteChanged(session)) {
// if input maps to a dynamic policy with an activity listener, notify of state change
if ((inputDesc->mPolicyMix != NULL)
&& ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
@@ -1559,9 +1622,9 @@
}
}
- ALOGV("AudioPolicyManager::startInput() input source = %d", inputDesc->mInputSource);
+ ALOGV("AudioPolicyManager::startInput() input source = %d", audioSession->inputSource());
- inputDesc->mRefCount++;
+ audioSession->changeActiveCount(1);
return NO_ERROR;
}
@@ -1576,23 +1639,23 @@
}
sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index);
- index = inputDesc->mSessions.indexOf(session);
+ sp<AudioSession> audioSession = inputDesc->getAudioSession(session);
if (index < 0) {
ALOGW("stopInput() unknown session %d on input %d", session, input);
return BAD_VALUE;
}
- if (inputDesc->mRefCount == 0) {
+ if (audioSession->activeCount() == 0) {
ALOGW("stopInput() input %d already stopped", input);
return INVALID_OPERATION;
}
- inputDesc->mRefCount--;
+ audioSession->changeActiveCount(-1);
// Routing?
mInputRoutes.decRouteActivity(session);
- if (inputDesc->mRefCount == 0) {
+ if (!inputDesc->isActive()) {
// if input maps to a dynamic policy with an activity listener, notify of state change
if ((inputDesc->mPolicyMix != NULL)
&& ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
@@ -1642,17 +1705,22 @@
sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index);
ALOG_ASSERT(inputDesc != 0);
- index = inputDesc->mSessions.indexOf(session);
+ sp<AudioSession> audioSession = inputDesc->getAudioSession(session);
if (index < 0) {
ALOGW("releaseInput() unknown session %d on input %d", session, input);
return;
}
- inputDesc->mSessions.remove(session);
- if (inputDesc->getOpenRefCount() == 0) {
- ALOGW("releaseInput() invalid open ref count %d", inputDesc->getOpenRefCount());
+
+ if (audioSession->openCount() == 0) {
+ ALOGW("releaseInput() invalid open count %d on session %d",
+ audioSession->openCount(), session);
return;
}
- inputDesc->changeOpenRefCount(-1);
+
+ if (audioSession->changeOpenCount(-1) == 0) {
+ inputDesc->removeAudioSession(session);
+ }
+
if (inputDesc->getOpenRefCount() > 0) {
ALOGV("releaseInput() exit > 0");
return;
@@ -1867,23 +1935,9 @@
{
for (size_t i = 0; i < mInputs.size(); i++) {
const sp<AudioInputDescriptor> inputDescriptor = mInputs.valueAt(i);
- if (inputDescriptor->mRefCount == 0) {
- continue;
- }
- if (inputDescriptor->mInputSource == (int)source) {
+ if (inputDescriptor->isSourceActive(source)) {
return true;
}
- // AUDIO_SOURCE_HOTWORD is equivalent to AUDIO_SOURCE_VOICE_RECOGNITION only if it
- // corresponds to an active capture triggered by a hardware hotword recognition
- if ((source == AUDIO_SOURCE_VOICE_RECOGNITION) &&
- (inputDescriptor->mInputSource == AUDIO_SOURCE_HOTWORD)) {
- // FIXME: we should not assume that the first session is the active one and keep
- // activity count per session. Same in startInput().
- ssize_t index = mSoundTriggerSessions.indexOfKey(inputDescriptor->mSessions.itemAt(0));
- if (index >= 0) {
- return true;
- }
- }
}
return false;
}
@@ -2663,7 +2717,7 @@
SortedVector<audio_io_handle_t> inputsToClose;
for (size_t i = 0; i < mInputs.size(); i++) {
sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(i);
- if (affectedSources.indexOf(inputDesc->mInputSource) >= 0) {
+ if (affectedSources.indexOf(inputDesc->inputSource()) >= 0) {
inputsToClose.add(inputDesc->mIoHandle);
}
}
@@ -3026,7 +3080,6 @@
}
sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(inProfile);
- inputDesc->mInputSource = AUDIO_SOURCE_MIC;
inputDesc->mDevice = profileType;
// find the address
@@ -4171,7 +4224,7 @@
}
}
- audio_devices_t device = getDeviceAndMixForInputSource(inputDesc->mInputSource);
+ audio_devices_t device = getDeviceAndMixForInputSource(inputDesc->inputSource());
return device;
}
@@ -4583,7 +4636,7 @@
// AUDIO_SOURCE_HOTWORD is for internal use only:
// handled as AUDIO_SOURCE_VOICE_RECOGNITION by the audio HAL
if (patch.sinks[0].ext.mix.usecase.source == AUDIO_SOURCE_HOTWORD &&
- !inputDesc->mIsSoundTrigger) {
+ !inputDesc->isSoundTrigger()) {
patch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_VOICE_RECOGNITION;
}
patch.num_sinks = 1;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index bb71090..38cf67b 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -610,6 +610,18 @@
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
const audio_offload_info_t *offloadInfo);
+ // internal method to return the input handle for the given device and format
+ audio_io_handle_t getInputForDevice(audio_devices_t device,
+ String8 address,
+ audio_session_t session,
+ uid_t uid,
+ audio_source_t inputSource,
+ uint32_t samplingRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ audio_input_flags_t flags,
+ AudioMix *policyMix);
+
// internal function to derive a stream type value from audio attributes
audio_stream_type_t streamTypefromAttributesInt(const audio_attributes_t *attr);
// event is one of STARTING_OUTPUT, STARTING_BEACON, STOPPING_OUTPUT, STOPPING_BEACON