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

Commit 296fb13d authored by Eric Laurent's avatar Eric Laurent
Browse files

Implement audio device callback

Add class AudioSystem::AudioDeviceCallback notifying
AudioSystem clients upon device selection change on a given
input or output thread.
Maintain a list of installed callback per I/O handle in AudioSystem
and call registered callbacks when an OPEN of CONFIG_CHANGED event
is received on IAudioFlingerClient::ioConfigChanged().
Add methods to AudioTrack and AudioRecord to add and remove device
change callbacks.
Add methods to AudioTrack and AudioRecord to query currently selected
device.
ioConfigChanged() events now convey the audio patch describing
the input or output thread routing.

Fix AudioRecord failure to start when invalidation is
handled by start().

Change-Id: I9e938adf025fa712337c63b1e02a8c18f2a20d39
parent 32fa6d0e
Loading
Loading
Loading
Loading
+20 −1
Original line number Diff line number Diff line
@@ -33,12 +33,31 @@ enum audio_io_config_event {
class AudioIoDescriptor : public RefBase {
public:
    AudioIoDescriptor() :
        mIoHandle(AUDIO_IO_HANDLE_NONE),
        mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT), mChannelMask(AUDIO_CHANNEL_NONE),
        mFrameCount(0), mLatency(0) {}
        mFrameCount(0), mLatency(0)
    {
        memset(&mPatch, 0, sizeof(struct audio_patch));
    }

    virtual ~AudioIoDescriptor() {}

    audio_port_handle_t getDeviceId() {
        if (mPatch.num_sources != 0 && mPatch.num_sinks != 0) {
            if (mPatch.sources[0].type == AUDIO_PORT_TYPE_MIX) {
                // this is an output mix
                // FIXME: the API only returns the first device in case of multiple device selection
                return mPatch.sinks[0].id;
            } else {
                // this is an input mix
                return mPatch.sources[0].id;
            }
        }
        return AUDIO_PORT_HANDLE_NONE;
    }

    audio_io_handle_t mIoHandle;
    struct audio_patch mPatch;
    uint32_t mSamplingRate;
    audio_format_t mFormat;
    audio_channel_mask_t mChannelMask;
+34 −0
Original line number Diff line number Diff line
@@ -390,6 +390,39 @@ public:
     * TODO Document this method.
     */
            audio_port_handle_t getInputDevice();

     /* Returns the ID of the audio device actually used by the input to which this AudioRecord
      * is attached.
      * A value of AUDIO_PORT_HANDLE_NONE indicates the AudioRecord is not attached to any input.
      *
      * Parameters:
      *  none.
      */
     audio_port_handle_t getRoutedDeviceId();

    /* Add an AudioDeviceCallback. The caller will be notified when the audio device
     * to which this AudioRecord is routed is updated.
     * Replaces any previously installed callback.
     * Parameters:
     *  callback:  The callback interface
     * Returns NO_ERROR if successful.
     *         INVALID_OPERATION if the same callback is already installed.
     *         NO_INIT or PREMISSION_DENIED if AudioFlinger service is not reachable
     *         BAD_VALUE if the callback is NULL
     */
            status_t addAudioDeviceCallback(
                    const sp<AudioSystem::AudioDeviceCallback>& callback);

    /* remove an AudioDeviceCallback.
     * Parameters:
     *  callback:  The callback interface
     * Returns NO_ERROR if successful.
     *         INVALID_OPERATION if the callback is not installed
     *         BAD_VALUE if the callback is NULL
     */
            status_t removeAudioDeviceCallback(
                    const sp<AudioSystem::AudioDeviceCallback>& callback);

private:
    /* If nonContig is non-NULL, it is an output parameter that will be set to the number of
     * additional non-contiguous frames that are predicted to be available immediately,
@@ -582,6 +615,7 @@ private:
    // For Device Selection API
    //  a value of AUDIO_PORT_HANDLE_NONE indicated default (AudioPolicyManager) routing.
    audio_port_handle_t    mSelectedDeviceId;
    sp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
};

}; // namespace android
+37 −6
Original line number Diff line number Diff line
@@ -332,8 +332,26 @@ public:

    };

    static status_t addAudioPortCallback(const sp<AudioPortCallback>& callBack);
    static status_t removeAudioPortCallback(const sp<AudioPortCallback>& callBack);
    static status_t addAudioPortCallback(const sp<AudioPortCallback>& callback);
    static status_t removeAudioPortCallback(const sp<AudioPortCallback>& callback);

    class AudioDeviceCallback : public RefBase
    {
    public:

                AudioDeviceCallback() {}
        virtual ~AudioDeviceCallback() {}

        virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
                                         audio_port_handle_t deviceId) = 0;
    };

    static status_t addAudioDeviceCallback(const sp<AudioDeviceCallback>& callback,
                                           audio_io_handle_t audioIo);
    static status_t removeAudioDeviceCallback(const sp<AudioDeviceCallback>& callback,
                                              audio_io_handle_t audioIo);

    static audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo);

private:

@@ -359,10 +377,20 @@ private:
        // values for output/input parameters up-to-date in client process
        virtual void ioConfigChanged(audio_io_config_event event,
                                     const sp<AudioIoDescriptor>& ioDesc);


        status_t addAudioDeviceCallback(const sp<AudioDeviceCallback>& callback,
                                               audio_io_handle_t audioIo);
        status_t removeAudioDeviceCallback(const sp<AudioDeviceCallback>& callback,
                                           audio_io_handle_t audioIo);

        audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo);

    private:
        Mutex                               mLock;
        DefaultKeyedVector<audio_io_handle_t, sp<AudioIoDescriptor> >   mIoDescriptors;

        DefaultKeyedVector<audio_io_handle_t, Vector < sp<AudioDeviceCallback> > >
                                                                        mAudioDeviceCallbacks;
        // cached values for recording getInputBufferSize() queries
        size_t                              mInBuffSize;    // zero indicates cache is invalid
        uint32_t                            mInSamplingRate;
@@ -377,8 +405,8 @@ private:
        AudioPolicyServiceClient() {
        }

        status_t addAudioPortCallback(const sp<AudioPortCallback>& callBack);
        status_t removeAudioPortCallback(const sp<AudioPortCallback>& callBack);
        status_t addAudioPortCallback(const sp<AudioPortCallback>& callback);
        status_t removeAudioPortCallback(const sp<AudioPortCallback>& callback);

        // DeathRecipient
        virtual void binderDied(const wp<IBinder>& who);
@@ -393,6 +421,9 @@ private:
        Vector <sp <AudioPortCallback> >    mAudioPortCallbacks;
    };

    static const sp<AudioFlingerClient> getAudioFlingerClient();
    static sp<AudioIoDescriptor> getIoDescriptor(audio_io_handle_t ioHandle);

    static sp<AudioFlingerClient> gAudioFlingerClient;
    static sp<AudioPolicyServiceClient> gAudioPolicyServiceClient;
    friend class AudioFlingerClient;
+34 −1
Original line number Diff line number Diff line
@@ -510,7 +510,7 @@ public:
     */
            status_t    setOutputDevice(audio_port_handle_t deviceId);

    /* Returns the ID of the audio device used for output of this AudioTrack.
    /* Returns the ID of the audio device selected for this AudioTrack.
     * A value of AUDIO_PORT_HANDLE_NONE indicates default (AudioPolicyManager) routing.
     *
     * Parameters:
@@ -518,6 +518,15 @@ public:
     */
     audio_port_handle_t getOutputDevice();

     /* Returns the ID of the audio device actually used by the output to which this AudioTrack is
      * attached.
      * A value of AUDIO_PORT_HANDLE_NONE indicates the audio track is not attached to any output.
      *
      * Parameters:
      *  none.
      */
     audio_port_handle_t getRoutedDeviceId();

    /* Returns the unique session ID associated with this track.
     *
     * Parameters:
@@ -664,6 +673,28 @@ public:
     */
            status_t    getTimestamp(AudioTimestamp& timestamp);

    /* Add an AudioDeviceCallback. The caller will be notified when the audio device to which this
     * AudioTrack is routed is updated.
     * Replaces any previously installed callback.
     * Parameters:
     *  callback:  The callback interface
     * Returns NO_ERROR if successful.
     *         INVALID_OPERATION if the same callback is already installed.
     *         NO_INIT or PREMISSION_DENIED if AudioFlinger service is not reachable
     *         BAD_VALUE if the callback is NULL
     */
            status_t addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);

    /* remove an AudioDeviceCallback.
     * Parameters:
     *  callback:  The callback interface
     * Returns NO_ERROR if successful.
     *         INVALID_OPERATION if the callback is not installed
     *         BAD_VALUE if the callback is NULL
     */
            status_t removeAudioDeviceCallback(
                    const sp<AudioSystem::AudioDeviceCallback>& callback);

protected:
    /* copying audio tracks is not allowed */
                        AudioTrack(const AudioTrack& other);
@@ -885,6 +916,8 @@ private:
    uint32_t                mSequence;              // incremented for each new IAudioTrack attempt
    int                     mClientUid;
    pid_t                   mClientPid;

    sp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
};

class TimedAudioTrack : public AudioTrack
+70 −1
Original line number Diff line number Diff line
@@ -112,6 +112,10 @@ AudioRecord::~AudioRecord()
            mAudioRecordThread->requestExitAndWait();
            mAudioRecordThread.clear();
        }
        // 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(mDeviceCallback, mInput);
        }
        IInterface::asBinder(mAudioRecord)->unlinkToDeath(mDeathNotifier, this);
        mAudioRecord.clear();
        mCblkMemory.clear();
@@ -286,6 +290,8 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession)
    mNewPosition = mProxy->getPosition() + mUpdatePeriod;
    int32_t flags = android_atomic_acquire_load(&mCblk->mFlags);

    mActive = true;

    status_t status = NO_ERROR;
    if (!(flags & CBLK_INVALID)) {
        status = mAudioRecord->start(event, triggerSession);
@@ -298,9 +304,9 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession)
    }

    if (status != NO_ERROR) {
        mActive = false;
        ALOGE("start() status %d", status);
    } else {
        mActive = true;
        sp<AudioRecordThread> t = mAudioRecordThread;
        if (t != 0) {
            t->resume();
@@ -425,6 +431,11 @@ status_t AudioRecord::setInputDevice(audio_port_handle_t deviceId) {
    AutoMutex lock(mLock);
    if (mSelectedDeviceId != deviceId) {
        mSelectedDeviceId = deviceId;
        // stop capture so that audio policy manager does not reject the new instance start request
        // as only one capture can be active at a time.
        if (mAudioRecord != 0 && mActive) {
            mAudioRecord->stop();
        }
        android_atomic_or(CBLK_INVALID, &mCblk->mFlags);
    }
    return NO_ERROR;
@@ -435,6 +446,14 @@ audio_port_handle_t AudioRecord::getInputDevice() {
    return mSelectedDeviceId;
}

audio_port_handle_t AudioRecord::getRoutedDeviceId() {
    AutoMutex lock(mLock);
    if (mInput == AUDIO_IO_HANDLE_NONE) {
        return AUDIO_PORT_HANDLE_NONE;
    }
    return AudioSystem::getDeviceIdForIo(mInput);
}

// -------------------------------------------------------------------------

// must be called with mLock held
@@ -478,6 +497,10 @@ status_t AudioRecord::openRecord_l(size_t epoch, const String16& opPackageName)
        }
    }

    if (mDeviceCallback != 0 && mInput != AUDIO_IO_HANDLE_NONE) {
        AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
    }

    audio_io_handle_t input;
    status_t status = AudioSystem::getInputForAttr(&mAttributes, &input,
                                        (audio_session_t)mSessionId,
@@ -609,6 +632,10 @@ status_t AudioRecord::openRecord_l(size_t epoch, const String16& opPackageName)
    mDeathNotifier = new DeathNotifier(this);
    IInterface::asBinder(mAudioRecord)->linkToDeath(mDeathNotifier, this);

    if (mDeviceCallback != 0) {
        AudioSystem::addAudioDeviceCallback(mDeviceCallback, mInput);
    }

    return NO_ERROR;
    }

@@ -1054,6 +1081,48 @@ status_t AudioRecord::restoreRecord_l(const char *from)
    return result;
}

status_t AudioRecord::addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback)
{
    if (callback == 0) {
        ALOGW("%s adding NULL callback!", __FUNCTION__);
        return BAD_VALUE;
    }
    AutoMutex lock(mLock);
    if (mDeviceCallback == callback) {
        ALOGW("%s adding same callback!", __FUNCTION__);
        return INVALID_OPERATION;
    }
    status_t status = NO_ERROR;
    if (mInput != AUDIO_IO_HANDLE_NONE) {
        if (mDeviceCallback != 0) {
            ALOGW("%s callback already present!", __FUNCTION__);
            AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
        }
        status = AudioSystem::addAudioDeviceCallback(callback, mInput);
    }
    mDeviceCallback = callback;
    return status;
}

status_t AudioRecord::removeAudioDeviceCallback(
        const sp<AudioSystem::AudioDeviceCallback>& callback)
{
    if (callback == 0) {
        ALOGW("%s removing NULL callback!", __FUNCTION__);
        return BAD_VALUE;
    }
    AutoMutex lock(mLock);
    if (mDeviceCallback != callback) {
        ALOGW("%s removing different callback!", __FUNCTION__);
        return INVALID_OPERATION;
    }
    if (mInput != AUDIO_IO_HANDLE_NONE) {
        AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
    }
    mDeviceCallback = 0;
    return NO_ERROR;
}

// =========================================================================

void AudioRecord::DeathNotifier::binderDied(const wp<IBinder>& who __unused)
Loading