Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 09f1ed2c authored by Eric Laurent's avatar Eric Laurent
Browse files

audio: improve audio routing callbacks

Do not force audio device changed callback when the client
(AudioTrack or AudioRecord) registers to AudioFlinger but only when
it starts playback or capture. Doing so prevents a spurious callback
happing at registration time where a stale device
(previously selected by AudioFlinger thread) but irrelevant to this
client is indicated. This causes a disconnection of AAudio streams
despite no real device change.

Bug: 128630993
Test: CTS: android.nativemedia.aaudio.AAudioTests, android.media.cts.RoutingTest
      CTS Verifier: Audio Input/Output Routing Notifications Test

Change-Id: Ia7f1d11490989b0287c97479466c1c07a223aab3
parent 2f7a6072
Loading
Loading
Loading
Loading
+10 −10
Original line number Diff line number Diff line
@@ -177,7 +177,7 @@ AudioRecord::~AudioRecord()
        }
        // No lock here: worst case we remove a NULL callback which will be a nop
        if (mDeviceCallback != 0 && mInput != AUDIO_IO_HANDLE_NONE) {
            AudioSystem::removeAudioDeviceCallback(this, mInput);
            AudioSystem::removeAudioDeviceCallback(this, mInput, mPortId);
        }
        IInterface::asBinder(mAudioRecord)->unlinkToDeath(mDeathNotifier, this);
        mAudioRecord.clear();
@@ -790,14 +790,13 @@ status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch, const String
    mAudioRecord = record;
    mCblkMemory = output.cblk;
    mBufferMemory = output.buffers;
    mPortId = output.portId;
    IPCThreadState::self()->flushCommands();

    mCblk = cblk;
    // note that output.frameCount is the (possibly revised) value of mReqFrameCount
    if (output.frameCount < mReqFrameCount || (mReqFrameCount == 0 && output.frameCount == 0)) {
        ALOGW("%s(%d): Requested frameCount %zu but received frameCount %zu",
              __func__, mPortId,
              __func__, output.portId,
              mReqFrameCount,  output.frameCount);
    }

@@ -805,19 +804,20 @@ status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch, const String
    // The computation is done on server side.
    if (mNotificationFramesReq > 0 && output.notificationFrameCount != mNotificationFramesReq) {
        ALOGW("%s(%d): Server adjusted notificationFrames from %u to %zu for frameCount %zu",
                __func__, mPortId,
                __func__, output.portId,
                mNotificationFramesReq, output.notificationFrameCount, output.frameCount);
    }
    mNotificationFramesAct = (uint32_t)output.notificationFrameCount;

    //mInput != input includes the case where mInput == AUDIO_IO_HANDLE_NONE for first creation
    if (mDeviceCallback != 0 && mInput != output.inputId) {
    if (mDeviceCallback != 0) {
        if (mInput != AUDIO_IO_HANDLE_NONE) {
            AudioSystem::removeAudioDeviceCallback(this, mInput);
            AudioSystem::removeAudioDeviceCallback(this, mInput, mPortId);
        }
        AudioSystem::addAudioDeviceCallback(this, output.inputId);
        AudioSystem::addAudioDeviceCallback(this, output.inputId, output.portId);
    }

    mPortId = output.portId;
    // We retain a copy of the I/O handle, but don't own the reference
    mInput = output.inputId;
    mRefreshRemaining = true;
@@ -1332,9 +1332,9 @@ status_t AudioRecord::addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCa
    if (mInput != AUDIO_IO_HANDLE_NONE) {
        if (mDeviceCallback != 0) {
            ALOGW("%s(%d): callback already present!", __func__, mPortId);
            AudioSystem::removeAudioDeviceCallback(this, mInput);
            AudioSystem::removeAudioDeviceCallback(this, mInput, mPortId);
        }
        status = AudioSystem::addAudioDeviceCallback(this, mInput);
        status = AudioSystem::addAudioDeviceCallback(this, mInput, mPortId);
    }
    mDeviceCallback = callback;
    return status;
@@ -1354,7 +1354,7 @@ status_t AudioRecord::removeAudioDeviceCallback(
    }
    mDeviceCallback.clear();
    if (mInput != AUDIO_IO_HANDLE_NONE) {
        AudioSystem::removeAudioDeviceCallback(this, mInput);
        AudioSystem::removeAudioDeviceCallback(this, mInput, mPortId);
    }
    return NO_ERROR;
}
+57 −74
Original line number Diff line number Diff line
@@ -523,12 +523,10 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even
    if (ioDesc == 0 || ioDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) return;

    audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
    Vector<sp<AudioDeviceCallback>> callbacksToCall;
    std::vector<sp<AudioDeviceCallback>> callbacksToCall;
    {
        Mutex::Autolock _l(mLock);
        bool deviceValidOrChanged = false;
        bool sendCallbacks = false;
        ssize_t ioIndex = -1;
        auto callbacks = std::map<audio_port_handle_t, wp<AudioDeviceCallback>>();

        switch (event) {
        case AUDIO_OUTPUT_OPENED:
@@ -546,17 +544,11 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even
            if (ioDesc->getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
                deviceId = ioDesc->getDeviceId();
                if (event == AUDIO_OUTPUT_OPENED || event == AUDIO_INPUT_OPENED) {
                    ioIndex = mAudioDeviceCallbackProxies.indexOfKey(ioDesc->mIoHandle);
                    if (ioIndex >= 0) {
                        sendCallbacks = true;
                        deviceValidOrChanged = true;
                    auto it = mAudioDeviceCallbacks.find(ioDesc->mIoHandle);
                    if (it != mAudioDeviceCallbacks.end()) {
                        callbacks = it->second;
                    }
                }
                if (event == AUDIO_OUTPUT_REGISTERED || event == AUDIO_INPUT_REGISTERED) {
                    ioIndex = mAudioDeviceCallbackProxies.indexOfKey(ioDesc->mIoHandle);
                    sendCallbacks = (ioIndex >= 0)
                            && !mAudioDeviceCallbackProxies.valueAt(ioIndex).notifiedOnce();
                }
            }
            ALOGV("ioConfigChanged() new %s %s %d samplingRate %u, format %#x channel mask %#x "
                    "frameCount %zu deviceId %d",
@@ -578,7 +570,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even
                  event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle);

            mIoDescriptors.removeItem(ioDesc->mIoHandle);
            mAudioDeviceCallbackProxies.removeItem(ioDesc->mIoHandle);
            mAudioDeviceCallbacks.erase(ioDesc->mIoHandle);
            } break;

        case AUDIO_OUTPUT_CONFIG_CHANGED:
@@ -593,10 +585,11 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even
            mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc);

            if (deviceId != ioDesc->getDeviceId()) {
                deviceValidOrChanged = true;
                deviceId = ioDesc->getDeviceId();
                ioIndex = mAudioDeviceCallbackProxies.indexOfKey(ioDesc->mIoHandle);
                sendCallbacks = ioIndex >= 0;
                auto it = mAudioDeviceCallbacks.find(ioDesc->mIoHandle);
                if (it != mAudioDeviceCallbacks.end()) {
                    callbacks = it->second;
                }
            }
            ALOGV("ioConfigChanged() new config for %s %d samplingRate %u, format %#x "
                    "channel mask %#x frameCount %zu frameCountHAL %zu deviceId %d",
@@ -606,35 +599,40 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even
                    ioDesc->getDeviceId());

        } break;
        case AUDIO_CLIENT_STARTED: {
            sp<AudioIoDescriptor> oldDesc = getIoDescriptor_l(ioDesc->mIoHandle);
            if (oldDesc == 0) {
                ALOGW("ioConfigChanged() start client on unknown io! %d", ioDesc->mIoHandle);
                break;
            }

        // sendCallbacks true =>  ioDesc->mIoHandle and deviceId are valid
        if (sendCallbacks) {
            AudioDeviceCallbackProxies &callbackProxies =
                mAudioDeviceCallbackProxies.editValueAt(ioIndex);
            for (size_t i = 0; i < callbackProxies.size(); ) {
                sp<AudioDeviceCallback> callback = callbackProxies[i]->callback();
                if (callback.get() != nullptr) {
                    // Call the callback only if the device actually changed, the input or output
                    // was opened or closed or the client was newly registered and the callback
                    // was never called
                    if (!callbackProxies[i]->notifiedOnce() || deviceValidOrChanged) {
                        callbacksToCall.add(callback);
                        callbackProxies[i]->setNotifiedOnce();
                    }
                    i++;
                } else {
                    callbackProxies.removeAt(i);
            ALOGV("ioConfigChanged() AUDIO_CLIENT_STARTED  io %d port %d num callbacks %zu",
                ioDesc->mIoHandle, ioDesc->mPortId, mAudioDeviceCallbacks.size());
            oldDesc->mPatch = ioDesc->mPatch;
            auto it = mAudioDeviceCallbacks.find(ioDesc->mIoHandle);
            if (it != mAudioDeviceCallbacks.end()) {
                auto cbks = it->second;
                auto it2 = cbks.find(ioDesc->mPortId);
                if (it2 != cbks.end()) {
                   callbacks.emplace(ioDesc->mPortId, it2->second);
                   deviceId = oldDesc->getDeviceId();
                }
            }
            callbackProxies.setNotifiedOnce();
        } break;
        }

        for (auto wpCbk : callbacks) {
            sp<AudioDeviceCallback> spCbk = wpCbk.second.promote();
            if (spCbk != nullptr) {
                callbacksToCall.push_back(spCbk);
            }
        }
    }

    // Callbacks must be called without mLock held. May lead to dead lock if calling for
    // example getRoutedDevice that updates the device and tries to acquire mLock.
    for (size_t i = 0; i < callbacksToCall.size(); i++) {
        callbacksToCall[i]->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId);
    for (auto cb  : callbacksToCall) {
        // If callbacksToCall is not empty, it implies ioDesc->mIoHandle and deviceId are valid
        cb->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId);
    }
}

@@ -687,51 +685,34 @@ sp<AudioIoDescriptor> AudioSystem::AudioFlingerClient::getIoDescriptor(audio_io_
}

status_t AudioSystem::AudioFlingerClient::addAudioDeviceCallback(
        const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
        const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo,
        audio_port_handle_t portId)
{
    ALOGV("%s audioIo %d portId %d", __func__, audioIo, portId);
    Mutex::Autolock _l(mLock);
    AudioDeviceCallbackProxies callbackProxies;
    ssize_t ioIndex = mAudioDeviceCallbackProxies.indexOfKey(audioIo);
    if (ioIndex >= 0) {
        callbackProxies = mAudioDeviceCallbackProxies.valueAt(ioIndex);
    }

    for (size_t cbIndex = 0; cbIndex < callbackProxies.size(); cbIndex++) {
        sp<AudioDeviceCallback> cbk = callbackProxies[cbIndex]->callback();
        if (cbk.get() == callback.unsafe_get()) {
    auto& callbacks = mAudioDeviceCallbacks.emplace(audioIo, std::map<audio_port_handle_t, wp<AudioDeviceCallback>>()).first->second;
    auto result = callbacks.try_emplace(portId, callback);
    if (!result.second) {
        return INVALID_OPERATION;
    }
    }
    callbackProxies.add(new AudioDeviceCallbackProxy(callback));
    callbackProxies.resetNotifiedOnce();
    mAudioDeviceCallbackProxies.replaceValueFor(audioIo, callbackProxies);
    return NO_ERROR;
}

status_t AudioSystem::AudioFlingerClient::removeAudioDeviceCallback(
        const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
        const wp<AudioDeviceCallback>& callback __unused, audio_io_handle_t audioIo,
        audio_port_handle_t portId)
{
    ALOGV("%s audioIo %d portId %d", __func__, audioIo, portId);
    Mutex::Autolock _l(mLock);
    ssize_t ioIndex = mAudioDeviceCallbackProxies.indexOfKey(audioIo);
    if (ioIndex < 0) {
    auto it = mAudioDeviceCallbacks.find(audioIo);
    if (it == mAudioDeviceCallbacks.end()) {
        return INVALID_OPERATION;
    }
    AudioDeviceCallbackProxies callbackProxies = mAudioDeviceCallbackProxies.valueAt(ioIndex);
    size_t cbIndex;
    for (cbIndex = 0; cbIndex < callbackProxies.size(); cbIndex++) {
        sp<AudioDeviceCallback> cbk = callbackProxies[cbIndex]->callback();
        if (cbk.get() == callback.unsafe_get()) {
            break;
        }
    }
    if (cbIndex == callbackProxies.size()) {
    if (it->second.erase(portId) == 0) {
        return INVALID_OPERATION;
    }
    callbackProxies.removeAt(cbIndex);
    if (callbackProxies.size() != 0) {
        mAudioDeviceCallbackProxies.replaceValueFor(audioIo, callbackProxies);
    } else {
        mAudioDeviceCallbackProxies.removeItem(audioIo);
    if (it->second.size() == 0) {
        mAudioDeviceCallbacks.erase(audioIo);
    }
    return NO_ERROR;
}
@@ -1271,13 +1252,14 @@ status_t AudioSystem::removeAudioVolumeGroupCallback(const sp<AudioVolumeGroupCa
}

status_t AudioSystem::addAudioDeviceCallback(
        const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
        const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo,
        audio_port_handle_t portId)
{
    const sp<AudioFlingerClient> afc = getAudioFlingerClient();
    if (afc == 0) {
        return NO_INIT;
    }
    status_t status = afc->addAudioDeviceCallback(callback, audioIo);
    status_t status = afc->addAudioDeviceCallback(callback, audioIo, portId);
    if (status == NO_ERROR) {
        const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
        if (af != 0) {
@@ -1288,13 +1270,14 @@ status_t AudioSystem::addAudioDeviceCallback(
}

status_t AudioSystem::removeAudioDeviceCallback(
        const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
        const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo,
        audio_port_handle_t portId)
{
    const sp<AudioFlingerClient> afc = getAudioFlingerClient();
    if (afc == 0) {
        return NO_INIT;
    }
    return afc->removeAudioDeviceCallback(callback, audioIo);
    return afc->removeAudioDeviceCallback(callback, audioIo, portId);
}

audio_port_handle_t AudioSystem::getDeviceIdForIo(audio_io_handle_t audioIo)
+11 −9
Original line number Diff line number Diff line
@@ -309,7 +309,7 @@ AudioTrack::~AudioTrack()
        }
        // No lock here: worst case we remove a NULL callback which will be a nop
        if (mDeviceCallback != 0 && mOutput != AUDIO_IO_HANDLE_NONE) {
            AudioSystem::removeAudioDeviceCallback(this, mOutput);
            AudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId);
        }
        IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this);
        mAudioTrack.clear();
@@ -1495,7 +1495,6 @@ status_t AudioTrack::createTrack_l()
    mAfFrameCount = output.afFrameCount;
    mAfSampleRate = output.afSampleRate;
    mAfLatency = output.afLatencyMs;
    mPortId = output.portId;

    mLatency = mAfLatency + (1000LL * mFrameCount) / mSampleRate;

@@ -1543,14 +1542,15 @@ status_t AudioTrack::createTrack_l()
    mFlags = output.flags;

    //mOutput != output includes the case where mOutput == AUDIO_IO_HANDLE_NONE for first creation
    if (mDeviceCallback != 0 && mOutput != output.outputId) {
    if (mDeviceCallback != 0) {
        if (mOutput != AUDIO_IO_HANDLE_NONE) {
            AudioSystem::removeAudioDeviceCallback(this, mOutput);
            AudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId);
        }
        AudioSystem::addAudioDeviceCallback(this, output.outputId);
        AudioSystem::addAudioDeviceCallback(this, output.outputId, output.portId);
        callbackAdded = true;
    }

    mPortId = output.portId;
    // We retain a copy of the I/O handle, but don't own the reference
    mOutput = output.outputId;
    mRefreshRemaining = true;
@@ -1615,7 +1615,7 @@ status_t AudioTrack::createTrack_l()
exit:
    if (status != NO_ERROR && callbackAdded) {
        // note: mOutput is always valid is callbackAdded is true
        AudioSystem::removeAudioDeviceCallback(this, mOutput);
        AudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId);
    }

    mStatus = status;
@@ -2922,6 +2922,7 @@ uint32_t AudioTrack::getUnderrunFrames() const

status_t AudioTrack::addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback)
{

    if (callback == 0) {
        ALOGW("%s(%d): adding NULL callback!", __func__, mPortId);
        return BAD_VALUE;
@@ -2935,9 +2936,9 @@ status_t AudioTrack::addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCal
    if (mOutput != AUDIO_IO_HANDLE_NONE) {
        if (mDeviceCallback != 0) {
            ALOGW("%s(%d): callback already present!", __func__, mPortId);
            AudioSystem::removeAudioDeviceCallback(this, mOutput);
            AudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId);
        }
        status = AudioSystem::addAudioDeviceCallback(this, mOutput);
        status = AudioSystem::addAudioDeviceCallback(this, mOutput, mPortId);
    }
    mDeviceCallback = callback;
    return status;
@@ -2957,7 +2958,7 @@ status_t AudioTrack::removeAudioDeviceCallback(
    }
    mDeviceCallback.clear();
    if (mOutput != AUDIO_IO_HANDLE_NONE) {
        AudioSystem::removeAudioDeviceCallback(this, mOutput);
        AudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId);
    }
    return NO_ERROR;
}
@@ -2979,6 +2980,7 @@ void AudioTrack::onAudioDeviceUpdate(audio_io_handle_t audioIo,
            mRoutedDeviceId = deviceId;
        }
    }

    if (callback.get() != nullptr) {
        callback->onAudioDeviceUpdate(mOutput, mRoutedDeviceId);
    }
+2 −0
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ public:
        data.writeInt64(ioDesc->mFrameCount);
        data.writeInt64(ioDesc->mFrameCountHAL);
        data.writeInt32(ioDesc->mLatency);
        data.writeInt32(ioDesc->mPortId);
        remote()->transact(IO_CONFIG_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
    }
};
@@ -76,6 +77,7 @@ status_t BnAudioFlingerClient::onTransact(
            ioDesc->mFrameCount = data.readInt64();
            ioDesc->mFrameCountHAL = data.readInt64();
            ioDesc->mLatency = data.readInt32();
            ioDesc->mPortId = data.readInt32();
            ioConfigChanged(event, ioDesc);
            return NO_ERROR;
        } break;
+3 −1
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ enum audio_io_config_event {
    AUDIO_INPUT_OPENED,
    AUDIO_INPUT_CLOSED,
    AUDIO_INPUT_CONFIG_CHANGED,
    AUDIO_CLIENT_STARTED,
};

// audio input/output descriptor used to cache output configurations in client process to avoid
@@ -37,7 +38,7 @@ public:
    AudioIoDescriptor() :
        mIoHandle(AUDIO_IO_HANDLE_NONE),
        mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT), mChannelMask(AUDIO_CHANNEL_NONE),
        mFrameCount(0), mFrameCountHAL(0), mLatency(0)
        mFrameCount(0), mFrameCountHAL(0), mLatency(0), mPortId(AUDIO_PORT_HANDLE_NONE)
    {
        memset(&mPatch, 0, sizeof(struct audio_patch));
    }
@@ -66,6 +67,7 @@ public:
    size_t                  mFrameCount;
    size_t                  mFrameCountHAL;
    uint32_t                mLatency;   // only valid for output
    audio_port_handle_t     mPortId;    // valid for event AUDIO_CLIENT_STARTED
};


Loading