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

Commit 23490efd authored by Eric Laurent's avatar Eric Laurent
Browse files

Fix issue 2045911: Camera Shutter tone does not play correctly while listening to music.

Add the possibility to delay routing and volume commands in AudioPolicyClientInterface. The delay is not blocking for the caller.
parent 734b2ba8
Loading
Loading
Loading
Loading
+187 −66
Original line number Diff line number Diff line
@@ -16,6 +16,13 @@

#define LOG_TAG "AudioPolicyService"
//#define LOG_NDEBUG 0

#undef __STRICT_ANSI__
#define __STDINT_LIMITS
#define __STDC_LIMIT_MACROS
#include <stdint.h>

#include <sys/time.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>
#include <cutils/properties.h>
@@ -54,7 +61,7 @@ AudioPolicyService::AudioPolicyService()
    char value[PROPERTY_VALUE_MAX];

    // start tone playback thread
    mTonePlaybacThread = new AudioCommandThread();
    mTonePlaybackThread = new AudioCommandThread();
    // start audio commands thread
    mAudioCommandThread = new AudioCommandThread();

@@ -80,8 +87,8 @@ AudioPolicyService::AudioPolicyService()

AudioPolicyService::~AudioPolicyService()
{
    mTonePlaybacThread->exit();
    mTonePlaybacThread.clear();
    mTonePlaybackThread->exit();
    mTonePlaybackThread.clear();
    mAudioCommandThread->exit();
    mAudioCommandThread.clear();

@@ -451,9 +458,9 @@ status_t AudioPolicyService::closeInput(audio_io_handle_t input)
    return af->closeInput(input);
}

status_t AudioPolicyService::setStreamVolume(AudioSystem::stream_type stream, float volume, audio_io_handle_t output)
status_t AudioPolicyService::setStreamVolume(AudioSystem::stream_type stream, float volume, audio_io_handle_t output, int delayMs)
{
    return mAudioCommandThread->volumeCommand((int)stream, volume, (int)output);
    return mAudioCommandThread->volumeCommand((int)stream, volume, (int)output, delayMs);
}

status_t AudioPolicyService::setStreamOutput(AudioSystem::stream_type stream, audio_io_handle_t output)
@@ -465,9 +472,9 @@ status_t AudioPolicyService::setStreamOutput(AudioSystem::stream_type stream, au
}


void AudioPolicyService::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
void AudioPolicyService::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs)
{
    mAudioCommandThread->parametersCommand((int)ioHandle, keyValuePairs);
    mAudioCommandThread->parametersCommand((int)ioHandle, keyValuePairs, delayMs);
}

String8 AudioPolicyService::getParameters(audio_io_handle_t ioHandle, const String8& keys)
@@ -478,13 +485,13 @@ String8 AudioPolicyService::getParameters(audio_io_handle_t ioHandle, const Stri

status_t AudioPolicyService::startTone(ToneGenerator::tone_type tone, AudioSystem::stream_type stream)
{
    mTonePlaybacThread->startToneCommand(tone, stream);
    mTonePlaybackThread->startToneCommand(tone, stream);
    return NO_ERROR;
}

status_t AudioPolicyService::stopTone()
{
    mTonePlaybacThread->stopToneCommand();
    mTonePlaybackThread->stopToneCommand();
    return NO_ERROR;
}

@@ -516,10 +523,15 @@ void AudioPolicyService::AudioCommandThread::onFirstRef()

bool AudioPolicyService::AudioCommandThread::threadLoop()
{
    nsecs_t waitTime = INT64_MAX;

    mLock.lock();
    while (!exitPending())
    {
        while(!mAudioCommands.isEmpty()) {
            nsecs_t curTime = systemTime();
            // commands are sorted by increasing time stamp: execute them from index 0 and up
            if (mAudioCommands[0]->mTime <= curTime) {
                AudioCommand *command = mAudioCommands[0];
                mAudioCommands.removeAt(0);
                switch (command->mCommand) {
@@ -548,26 +560,35 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
                case SET_VOLUME: {
                    VolumeData *data = (VolumeData *)command->mParam;
                    LOGV("AudioCommandThread() processing set volume stream %d, volume %f, output %d", data->mStream, data->mVolume, data->mIO);
                mCommandStatus = AudioSystem::setStreamVolume(data->mStream, data->mVolume, data->mIO);
                mCommandCond.signal();
                    command->mStatus = AudioSystem::setStreamVolume(data->mStream, data->mVolume, data->mIO);
                    if (command->mWaitStatus) {
                        command->mCond.signal();
                        mWaitWorkCV.wait(mLock);
                    }
                    delete data;
                    }break;
                case SET_PARAMETERS: {
                     ParametersData *data = (ParametersData *)command->mParam;
                     LOGV("AudioCommandThread() processing set parameters string %s, io %d", data->mKeyValuePairs.string(), data->mIO);
                 mCommandStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs);
                 mCommandCond.signal();
                     command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs);
                     if (command->mWaitStatus) {
                         command->mCond.signal();
                         mWaitWorkCV.wait(mLock);
                     }
                     delete data;
                     }break;
                default:
                    LOGW("AudioCommandThread() unknown command %d", command->mCommand);
                }
                delete command;
                waitTime = INT64_MAX;
            } else {
                waitTime = mAudioCommands[0]->mTime - curTime;
                break;
            }
        }
        LOGV("AudioCommandThread() going to sleep");
        mWaitWorkCV.wait(mLock);
        mWaitWorkCV.waitRelative(mLock, waitTime);
        LOGV("AudioCommandThread() waking up");
    }
    mLock.unlock();
@@ -583,7 +604,8 @@ void AudioPolicyService::AudioCommandThread::startToneCommand(int type, int stre
    data->mType = type;
    data->mStream = stream;
    command->mParam = (void *)data;
    mAudioCommands.add(command);
    command->mWaitStatus = false;
    insertCommand_l(command);
    LOGV("AudioCommandThread() adding tone start type %d, stream %d", type, stream);
    mWaitWorkCV.signal();
}
@@ -594,13 +616,16 @@ void AudioPolicyService::AudioCommandThread::stopToneCommand()
    AudioCommand *command = new AudioCommand();
    command->mCommand = STOP_TONE;
    command->mParam = NULL;
    mAudioCommands.add(command);
    command->mWaitStatus = false;
    insertCommand_l(command);
    LOGV("AudioCommandThread() adding tone stop");
    mWaitWorkCV.signal();
}

status_t AudioPolicyService::AudioCommandThread::volumeCommand(int stream, float volume, int output)
status_t AudioPolicyService::AudioCommandThread::volumeCommand(int stream, float volume, int output, int delayMs)
{
    status_t status = NO_ERROR;

    Mutex::Autolock _l(mLock);
    AudioCommand *command = new AudioCommand();
    command->mCommand = SET_VOLUME;
@@ -609,17 +634,26 @@ status_t AudioPolicyService::AudioCommandThread::volumeCommand(int stream, float
    data->mVolume = volume;
    data->mIO = output;
    command->mParam = data;
    mAudioCommands.add(command);
    if (delayMs == 0) {
        command->mWaitStatus = true;
    } else {
        command->mWaitStatus = false;
    }
    insertCommand_l(command, delayMs);
    LOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %d", stream, volume, output);
    mWaitWorkCV.signal();
    mCommandCond.wait(mLock);
    status_t status =  mCommandStatus;
    if (command->mWaitStatus) {
        command->mCond.wait(mLock);
        status =  command->mStatus;
        mWaitWorkCV.signal();
    }
    return status;
}

status_t AudioPolicyService::AudioCommandThread::parametersCommand(int ioHandle, const String8& keyValuePairs)
status_t AudioPolicyService::AudioCommandThread::parametersCommand(int ioHandle, const String8& keyValuePairs, int delayMs)
{
    status_t status = NO_ERROR;

    Mutex::Autolock _l(mLock);
    AudioCommand *command = new AudioCommand();
    command->mCommand = SET_PARAMETERS;
@@ -627,15 +661,102 @@ status_t AudioPolicyService::AudioCommandThread::parametersCommand(int ioHandle,
    data->mIO = ioHandle;
    data->mKeyValuePairs = keyValuePairs;
    command->mParam = data;
    mAudioCommands.add(command);
    LOGV("AudioCommandThread() adding set parameter string %s, io %d", keyValuePairs.string(), ioHandle);
    if (delayMs == 0) {
        command->mWaitStatus = true;
    } else {
        command->mWaitStatus = false;
    }
    insertCommand_l(command, delayMs);
    LOGV("AudioCommandThread() adding set parameter string %s, io %d ,delay %d", keyValuePairs.string(), ioHandle, delayMs);
    mWaitWorkCV.signal();
    mCommandCond.wait(mLock);
    status_t status =  mCommandStatus;
    if (command->mWaitStatus) {
        command->mCond.wait(mLock);
        status =  command->mStatus;
        mWaitWorkCV.signal();
    }
    return status;
}

// insertCommand_l() must be called with mLock held
void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs)
{
    ssize_t i;
    Vector <AudioCommand *> removedCommands;

    command->mTime = systemTime() + milliseconds(delayMs);

    // check same pending commands with later time stamps and eliminate them
    for (i = mAudioCommands.size()-1; i >= 0; i--) {
        AudioCommand *command2 = mAudioCommands[i];
        // commands are sorted by increasing time stamp: no need to scan the rest of mAudioCommands
        if (command2->mTime <= command->mTime) break;
        if (command2->mCommand != command->mCommand) continue;

        switch (command->mCommand) {
        case SET_PARAMETERS: {
            ParametersData *data = (ParametersData *)command->mParam;
            ParametersData *data2 = (ParametersData *)command2->mParam;
            if (data->mIO != data2->mIO) break;
            LOGV("Comparing parameter command %s to new command %s", data2->mKeyValuePairs.string(), data->mKeyValuePairs.string());
            AudioParameter param = AudioParameter(data->mKeyValuePairs);
            AudioParameter param2 = AudioParameter(data2->mKeyValuePairs);
            for (size_t j = 0; j < param.size(); j++) {
               String8 key;
               String8 value;
               param.getAt(j, key, value);
               for (size_t k = 0; k < param2.size(); k++) {
                  String8 key2;
                  String8 value2;
                  param2.getAt(k, key2, value2);
                  if (key2 == key) {
                      param2.remove(key2);
                      LOGV("Filtering out parameter %s", key2.string());
                      break;
                  }
               }
            }
            // if all keys have been filtered out, remove the command.
            // otherwise, update the key value pairs
            if (param2.size() == 0) {
                removedCommands.add(command2);
            } else {
                data2->mKeyValuePairs = param2.toString();
            }
        } break;

        case SET_VOLUME: {
            VolumeData *data = (VolumeData *)command->mParam;
            VolumeData *data2 = (VolumeData *)command2->mParam;
            if (data->mIO != data2->mIO) break;
            if (data->mStream != data2->mStream) break;
            LOGV("Filtering out volume command on output %d for stream %d", data->mIO, data->mStream);
            removedCommands.add(command2);
        } break;
        case START_TONE:
        case STOP_TONE:
        default:
            break;
        }
    }

    // remove filtered commands
    for (size_t j = 0; j < removedCommands.size(); j++) {
        // removed commands always have time stamps greater than current command
        for (size_t k = i + 1; k < mAudioCommands.size(); k++) {
            if (mAudioCommands[k] == removedCommands[j]) {
                LOGV("suppressing command: %d", mAudioCommands[k]->mCommand);
                mAudioCommands.removeAt(k);
                break;
            }
        }
    }
    removedCommands.clear();

    // insert command at the right place according to its time stamp
    LOGV("inserting command: %d at index %ld, num commands %d", command->mCommand, i+1, mAudioCommands.size());
    mAudioCommands.insertAt(command, i + 1);
}

void AudioPolicyService::AudioCommandThread::exit()
{
    LOGV("AudioCommandThread::exit");
+14 −9
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <media/IAudioPolicyService.h>
#include <hardware_legacy/AudioPolicyInterface.h>
#include <media/ToneGenerator.h>
#include <utils/Vector.h>

namespace android {

@@ -98,9 +99,9 @@ public:
                                    uint32_t *pChannels,
                                    uint32_t acoustics);
    virtual status_t closeInput(audio_io_handle_t input);
    virtual status_t setStreamVolume(AudioSystem::stream_type stream, float volume, audio_io_handle_t output);
    virtual status_t setStreamVolume(AudioSystem::stream_type stream, float volume, audio_io_handle_t output, int delayMs = 0);
    virtual status_t setStreamOutput(AudioSystem::stream_type stream, audio_io_handle_t output);
    virtual void setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs);
    virtual void setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs = 0);
    virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys);
    virtual status_t startTone(ToneGenerator::tone_type tone, AudioSystem::stream_type stream);
    virtual status_t stopTone();
@@ -116,6 +117,7 @@ private:
    // For audio config commands, it is necessary because audio flinger requires that the calling process (user)
    // has permission to modify audio settings.
    class AudioCommandThread : public Thread {
        class AudioCommand;
    public:

        // commands for tone AudioCommand
@@ -136,15 +138,20 @@ private:
                    void        exit();
                    void        startToneCommand(int type = 0, int stream = 0);
                    void        stopToneCommand();
                    status_t    volumeCommand(int stream, float volume, int output);
                    status_t    parametersCommand(int ioHandle, const String8& keyValuePairs);
                    status_t    volumeCommand(int stream, float volume, int output, int delayMs = 0);
                    status_t    parametersCommand(int ioHandle, const String8& keyValuePairs, int delayMs = 0);
                    void        insertCommand_l(AudioCommand *command, int delayMs = 0);

    private:
        // descriptor for requested tone playback event
        class AudioCommand {
        public:
            int mCommand;   // START_TONE, STOP_TONE ...
            void *mParam;
            nsecs_t mTime;  // time stamp
            Condition mCond; // condition for status return
            status_t mStatus; // command status
            bool mWaitStatus; // true if caller is waiting for status
            void *mParam;     // command parameter (ToneData, VolumeData, ParametersData)
        };

        class ToneData {
@@ -168,9 +175,7 @@ private:

        Mutex   mLock;
        Condition mWaitWorkCV;
        Vector<AudioCommand *> mAudioCommands;    // list of pending tone events
        Condition              mCommandCond;
        status_t               mCommandStatus;
        Vector <AudioCommand *> mAudioCommands; // list of pending commands
        ToneGenerator *mpToneGenerator;     // the tone generator
    };

@@ -182,7 +187,7 @@ private:
                        // connection stated our routing
    AudioPolicyInterface* mpPolicyManager;          // the platform specific policy manager
    sp <AudioCommandThread> mAudioCommandThread;    // audio commands thread
    sp <AudioCommandThread> mTonePlaybacThread;     // tone playback thread
    sp <AudioCommandThread> mTonePlaybackThread;     // tone playback thread
};

}; // namespace android