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