diff options
Diffstat (limited to 'services')
21 files changed, 1529 insertions, 123 deletions
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index a4cad4e..527fd65 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -431,7 +431,7 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args) if (mLogMemoryDealer != 0) { sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log")); if (binder != 0) { - fdprintf(fd, "\nmedia.log:\n"); + dprintf(fd, "\nmedia.log:\n"); Vector<String16> args; binder->dump(fd, args); } @@ -1266,7 +1266,7 @@ AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid) : RefBase(), mAudioFlinger(audioFlinger), // FIXME should be a "k" constant not hard-coded, in .h or ro. property, see 4 lines below - mMemoryDealer(new MemoryDealer(4 * 1024*1024, "AudioFlinger::Client")), + mMemoryDealer(new MemoryDealer(1024*1024, "AudioFlinger::Client")), mPid(pid), mTimedTrackCount(0) { @@ -2609,7 +2609,7 @@ void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_hand } } else { if (fd >= 0) { - fdprintf(fd, "unable to rotate tees in %s: %s\n", teePath, strerror(errno)); + dprintf(fd, "unable to rotate tees in %s: %s\n", teePath, strerror(errno)); } } char teeTime[16]; @@ -2663,11 +2663,11 @@ void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_hand write(teeFd, &temp, sizeof(temp)); close(teeFd); if (fd >= 0) { - fdprintf(fd, "tee copied to %s\n", teePath); + dprintf(fd, "tee copied to %s\n", teePath); } } else { if (fd >= 0) { - fdprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno)); + dprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno)); } } } diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp index a4446a4..318eb57 100644 --- a/services/audioflinger/AudioResamplerDyn.cpp +++ b/services/audioflinger/AudioResamplerDyn.cpp @@ -460,9 +460,15 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, const uint32_t phaseIncrement = mPhaseIncrement; size_t outputIndex = 0; size_t outputSampleCount = outFrameCount * 2; // stereo output - size_t inFrameCount = getInFrameCountRequired(outFrameCount) + (phaseFraction != 0); - ALOG_ASSERT(0 < inFrameCount && inFrameCount < (1U << 31)); const uint32_t phaseWrapLimit = c.mL << c.mShift; + size_t inFrameCount = (phaseIncrement * (uint64_t)outFrameCount + phaseFraction) + / phaseWrapLimit; + // sanity check that inFrameCount is in signed 32 bit integer range. + ALOG_ASSERT(0 <= inFrameCount && inFrameCount < (1U << 31)); + + //ALOGV("inFrameCount:%d outFrameCount:%d" + // " phaseIncrement:%u phaseFraction:%u phaseWrapLimit:%u", + // inFrameCount, outFrameCount, phaseIncrement, phaseFraction, phaseWrapLimit); // NOTE: be very careful when modifying the code here. register // pressure is very high and a small change might cause the compiler @@ -472,10 +478,17 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, // the following logic is a bit convoluted to keep the main processing loop // as tight as possible with register allocation. while (outputIndex < outputSampleCount) { - // buffer is empty, fetch a new one - while (mBuffer.frameCount == 0) { + //ALOGV("LOOP: inFrameCount:%d outputIndex:%d outFrameCount:%d" + // " phaseFraction:%u phaseWrapLimit:%u", + // inFrameCount, outputIndex, outFrameCount, phaseFraction, phaseWrapLimit); + + // check inputIndex overflow + ALOG_ASSERT(inputIndex <= mBuffer.frameCount, "inputIndex%d > frameCount%d", + inputIndex, mBuffer.frameCount); + // Buffer is empty, fetch a new one if necessary (inFrameCount > 0). + // We may not fetch a new buffer if the existing data is sufficient. + while (mBuffer.frameCount == 0 && inFrameCount > 0) { mBuffer.frameCount = inFrameCount; - ALOG_ASSERT(inFrameCount > 0); provider->getNextBuffer(&mBuffer, calculateOutputPTS(outputIndex / 2)); if (mBuffer.raw == NULL) { @@ -486,9 +499,9 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, mInBuffer.template readAdvance<CHANNELS>( impulse, c.mHalfNumCoefs, reinterpret_cast<TI*>(mBuffer.raw), inputIndex); + inputIndex++; phaseFraction -= phaseWrapLimit; while (phaseFraction >= phaseWrapLimit) { - inputIndex++; if (inputIndex >= mBuffer.frameCount) { inputIndex = 0; provider->releaseBuffer(&mBuffer); @@ -497,6 +510,7 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, mInBuffer.template readAdvance<CHANNELS>( impulse, c.mHalfNumCoefs, reinterpret_cast<TI*>(mBuffer.raw), inputIndex); + inputIndex++; phaseFraction -= phaseWrapLimit; } } @@ -507,9 +521,6 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, const int halfNumCoefs = c.mHalfNumCoefs; const TO* const volumeSimd = mVolumeSimd; - // reread the last input in. - mInBuffer.template readAgain<CHANNELS>(impulse, halfNumCoefs, in, inputIndex); - // main processing loop while (CC_LIKELY(outputIndex < outputSampleCount)) { // caution: fir() is inlined and may be large. @@ -518,6 +529,10 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, // from the input samples in impulse[-halfNumCoefs+1]... impulse[halfNumCoefs] // from the polyphase filter of (phaseFraction / phaseWrapLimit) in coefs. // + //ALOGV("LOOP2: inFrameCount:%d outputIndex:%d outFrameCount:%d" + // " phaseFraction:%u phaseWrapLimit:%u", + // inFrameCount, outputIndex, outFrameCount, phaseFraction, phaseWrapLimit); + ALOG_ASSERT(phaseFraction < phaseWrapLimit); fir<CHANNELS, LOCKED, STRIDE>( &out[outputIndex], phaseFraction, phaseWrapLimit, @@ -527,17 +542,20 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, phaseFraction += phaseIncrement; while (phaseFraction >= phaseWrapLimit) { - inputIndex++; if (inputIndex >= frameCount) { goto done; // need a new buffer } mInBuffer.template readAdvance<CHANNELS>(impulse, halfNumCoefs, in, inputIndex); + inputIndex++; phaseFraction -= phaseWrapLimit; } } done: - // often arrives here when input buffer runs out - if (inputIndex >= frameCount) { + // We arrive here when we're finished or when the input buffer runs out. + // Regardless we need to release the input buffer if we've acquired it. + if (inputIndex > 0) { // we've acquired a buffer (alternatively could check frameCount) + ALOG_ASSERT(inputIndex == frameCount, "inputIndex(%d) != frameCount(%d)", + inputIndex, frameCount); // must have been fully read. inputIndex = 0; provider->releaseBuffer(&mBuffer); ALOG_ASSERT(mBuffer.frameCount == 0); @@ -545,14 +563,12 @@ done: } resample_exit: - // Release frames to avoid the count being inaccurate for pts timing. - // TODO: Avoid this extra check by making fetch count exact. This is tricky - // due to the overfetching mechanism which loads unnecessarily when - // mBuffer.frameCount == 0. - if (inputIndex) { - mBuffer.frameCount = inputIndex; - provider->releaseBuffer(&mBuffer); - } + // inputIndex must be zero in all three cases: + // (1) the buffer never was been acquired; (2) the buffer was + // released at "done:"; or (3) getNextBuffer() failed. + ALOG_ASSERT(inputIndex == 0, "Releasing: inputindex:%d frameCount:%d phaseFraction:%u", + inputIndex, mBuffer.frameCount, phaseFraction); + ALOG_ASSERT(mBuffer.frameCount == 0); // there must be no frames in the buffer mInBuffer.setImpulse(impulse); mPhaseFraction = phaseFraction; } diff --git a/services/audioflinger/AudioWatchdog.cpp b/services/audioflinger/AudioWatchdog.cpp index 93d185e..877e776 100644 --- a/services/audioflinger/AudioWatchdog.cpp +++ b/services/audioflinger/AudioWatchdog.cpp @@ -34,7 +34,7 @@ void AudioWatchdogDump::dump(int fd) } else { strcpy(buf, "N/A\n"); } - fdprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s", + dprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s", mUnderruns, mLogs, buf); } diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index c9a3f10..c486630 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -26,7 +26,6 @@ #define ATRACE_TAG ATRACE_TAG_AUDIO #include "Configuration.h" -#include <sys/atomics.h> #include <time.h> #include <utils/Log.h> #include <utils/Trace.h> @@ -492,7 +491,7 @@ static int compare_uint32_t(const void *pa, const void *pb) void FastMixerDumpState::dump(int fd) const { if (mCommand == FastMixerState::INITIAL) { - fdprintf(fd, " FastMixer not initialized\n"); + dprintf(fd, " FastMixer not initialized\n"); return; } #define COMMAND_MAX 32 @@ -526,10 +525,10 @@ void FastMixerDumpState::dump(int fd) const double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) + (mMeasuredWarmupTs.tv_nsec / 1000000.0); double mixPeriodSec = (double) mFrameCount / (double) mSampleRate; - fdprintf(fd, " FastMixer command=%s writeSequence=%u framesWritten=%u\n" - " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n" - " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n" - " mixPeriod=%.2f ms\n", + dprintf(fd, " FastMixer command=%s writeSequence=%u framesWritten=%u\n" + " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n" + " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n" + " mixPeriod=%.2f ms\n", string, mWriteSequence, mFramesWritten, mNumTracks, mWriteErrors, mUnderruns, mOverruns, mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles, @@ -581,26 +580,26 @@ void FastMixerDumpState::dump(int fd) const #endif } if (n) { - fdprintf(fd, " Simple moving statistics over last %.1f seconds:\n", - wall.n() * mixPeriodSec); - fdprintf(fd, " wall clock time in ms per mix cycle:\n" - " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", - wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6, - wall.stddev()*1e-6); - fdprintf(fd, " raw CPU load in us per mix cycle:\n" - " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", - loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3, - loadNs.stddev()*1e-3); + dprintf(fd, " Simple moving statistics over last %.1f seconds:\n", + wall.n() * mixPeriodSec); + dprintf(fd, " wall clock time in ms per mix cycle:\n" + " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", + wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6, + wall.stddev()*1e-6); + dprintf(fd, " raw CPU load in us per mix cycle:\n" + " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", + loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3, + loadNs.stddev()*1e-3); } else { - fdprintf(fd, " No FastMixer statistics available currently\n"); + dprintf(fd, " No FastMixer statistics available currently\n"); } #ifdef CPU_FREQUENCY_STATISTICS - fdprintf(fd, " CPU clock frequency in MHz:\n" - " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", - kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3); - fdprintf(fd, " adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n" - " mean=%.1f min=%.1f max=%.1f stddev=%.1f\n", - loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev()); + dprintf(fd, " CPU clock frequency in MHz:\n" + " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", + kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3); + dprintf(fd, " adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n" + " mean=%.1f min=%.1f max=%.1f stddev=%.1f\n", + loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev()); #endif if (tail != NULL) { qsort(tail, n, sizeof(uint32_t), compare_uint32_t); @@ -611,12 +610,12 @@ void FastMixerDumpState::dump(int fd) const left.sample(tail[i]); right.sample(tail[n - (i + 1)]); } - fdprintf(fd, " Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n" - " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n" - " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", - left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6, - right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6, - right.stddev()*1e-6); + dprintf(fd, " Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n" + " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n" + " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", + left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6, + right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6, + right.stddev()*1e-6); delete[] tail; } #endif @@ -626,9 +625,9 @@ void FastMixerDumpState::dump(int fd) const // Instead we always display all tracks, with an indication // of whether we think the track is active. uint32_t trackMask = mTrackMask; - fdprintf(fd, " Fast tracks: kMaxFastTracks=%u activeMask=%#x\n", + dprintf(fd, " Fast tracks: kMaxFastTracks=%u activeMask=%#x\n", FastMixerState::kMaxFastTracks, trackMask); - fdprintf(fd, " Index Active Full Partial Empty Recent Ready\n"); + dprintf(fd, " Index Active Full Partial Empty Recent Ready\n"); for (uint32_t i = 0; i < FastMixerState::kMaxFastTracks; ++i, trackMask >>= 1) { bool isActive = trackMask & 1; const FastTrackDump *ftDump = &mTracks[i]; @@ -648,7 +647,7 @@ void FastMixerDumpState::dump(int fd) const mostRecent = "?"; break; } - fdprintf(fd, " %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no", + dprintf(fd, " %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no", (underruns.mBitFields.mFull) & UNDERRUN_MASK, (underruns.mBitFields.mPartial) & UNDERRUN_MASK, (underruns.mBitFields.mEmpty) & UNDERRUN_MASK, diff --git a/services/audioflinger/StateQueue.cpp b/services/audioflinger/StateQueue.cpp index 48399c0..7e01c9f 100644 --- a/services/audioflinger/StateQueue.cpp +++ b/services/audioflinger/StateQueue.cpp @@ -28,12 +28,12 @@ namespace android { #ifdef STATE_QUEUE_DUMP void StateQueueObserverDump::dump(int fd) { - fdprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges); + dprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges); } void StateQueueMutatorDump::dump(int fd) { - fdprintf(fd, "State queue mutator: pushDirty=%u pushAck=%u blockedSequence=%u\n", + dprintf(fd, "State queue mutator: pushDirty=%u pushAck=%u blockedSequence=%u\n", mPushDirty, mPushAck, mBlockedSequence); } #endif diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index d6333be..d08c966 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -574,30 +574,30 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args __u bool locked = AudioFlinger::dumpTryLock(mLock); if (!locked) { - fdprintf(fd, "thread %p maybe dead locked\n", this); + dprintf(fd, "thread %p maybe dead locked\n", this); } - fdprintf(fd, " I/O handle: %d\n", mId); - fdprintf(fd, " TID: %d\n", getTid()); - fdprintf(fd, " Standby: %s\n", mStandby ? "yes" : "no"); - fdprintf(fd, " Sample rate: %u\n", mSampleRate); - fdprintf(fd, " HAL frame count: %zu\n", mFrameCount); - fdprintf(fd, " HAL buffer size: %u bytes\n", mBufferSize); - fdprintf(fd, " Channel Count: %u\n", mChannelCount); - fdprintf(fd, " Channel Mask: 0x%08x (%s)\n", mChannelMask, + dprintf(fd, " I/O handle: %d\n", mId); + dprintf(fd, " TID: %d\n", getTid()); + dprintf(fd, " Standby: %s\n", mStandby ? "yes" : "no"); + dprintf(fd, " Sample rate: %u\n", mSampleRate); + dprintf(fd, " HAL frame count: %zu\n", mFrameCount); + dprintf(fd, " HAL buffer size: %u bytes\n", mBufferSize); + dprintf(fd, " Channel Count: %u\n", mChannelCount); + dprintf(fd, " Channel Mask: 0x%08x (%s)\n", mChannelMask, channelMaskToString(mChannelMask, mType != RECORD).string()); - fdprintf(fd, " Format: 0x%x (%s)\n", mFormat, formatToString(mFormat)); - fdprintf(fd, " Frame size: %zu\n", mFrameSize); - fdprintf(fd, " Pending config events:"); + dprintf(fd, " Format: 0x%x (%s)\n", mFormat, formatToString(mFormat)); + dprintf(fd, " Frame size: %zu\n", mFrameSize); + dprintf(fd, " Pending config events:"); size_t numConfig = mConfigEvents.size(); if (numConfig) { for (size_t i = 0; i < numConfig; i++) { mConfigEvents[i]->dump(buffer, SIZE); - fdprintf(fd, "\n %s", buffer); + dprintf(fd, "\n %s", buffer); } - fdprintf(fd, "\n"); + dprintf(fd, "\n"); } else { - fdprintf(fd, " none\n"); + dprintf(fd, " none\n"); } if (locked) { @@ -1260,15 +1260,15 @@ void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& ar // These values are "raw"; they will wrap around. See prepareTracks_l() for a better way. FastTrackUnderruns underruns = getFastTrackUnderruns(0); - fdprintf(fd, " Normal mixer raw underrun counters: partial=%u empty=%u\n", + dprintf(fd, " Normal mixer raw underrun counters: partial=%u empty=%u\n", underruns.mBitFields.mPartial, underruns.mBitFields.mEmpty); size_t numtracks = mTracks.size(); size_t numactive = mActiveTracks.size(); - fdprintf(fd, " %d Tracks", numtracks); + dprintf(fd, " %d Tracks", numtracks); size_t numactiveseen = 0; if (numtracks) { - fdprintf(fd, " of which %d are active\n", numactive); + dprintf(fd, " of which %d are active\n", numactive); Track::appendDumpHeader(result); for (size_t i = 0; i < numtracks; ++i) { sp<Track> track = mTracks[i]; @@ -1300,22 +1300,21 @@ void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& ar } write(fd, result.string(), result.size()); - } void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args) { - fdprintf(fd, "\nOutput thread %p:\n", this); - fdprintf(fd, " Normal frame count: %zu\n", mNormalFrameCount); - fdprintf(fd, " Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); - fdprintf(fd, " Total writes: %d\n", mNumWrites); - fdprintf(fd, " Delayed writes: %d\n", mNumDelayedWrites); - fdprintf(fd, " Blocked in write: %s\n", mInWrite ? "yes" : "no"); - fdprintf(fd, " Suspend count: %d\n", mSuspended); - fdprintf(fd, " Sink buffer : %p\n", mSinkBuffer); - fdprintf(fd, " Mixer buffer: %p\n", mMixerBuffer); - fdprintf(fd, " Effect buffer: %p\n", mEffectBuffer); - fdprintf(fd, " Fast track availMask=%#x\n", mFastTrackAvailMask); + dprintf(fd, "\nOutput thread %p:\n", this); + dprintf(fd, " Normal frame count: %zu\n", mNormalFrameCount); + dprintf(fd, " Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); + dprintf(fd, " Total writes: %d\n", mNumWrites); + dprintf(fd, " Delayed writes: %d\n", mNumDelayedWrites); + dprintf(fd, " Blocked in write: %s\n", mInWrite ? "yes" : "no"); + dprintf(fd, " Suspend count: %d\n", mSuspended); + dprintf(fd, " Sink buffer : %p\n", mSinkBuffer); + dprintf(fd, " Mixer buffer: %p\n", mMixerBuffer); + dprintf(fd, " Effect buffer: %p\n", mEffectBuffer); + dprintf(fd, " Fast track availMask=%#x\n", mFastTrackAvailMask); dumpBase(fd, args); } @@ -3799,7 +3798,7 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& ar PlaybackThread::dumpInternals(fd, args); - fdprintf(fd, " AudioMixer tracks: 0x%08x\n", mAudioMixer->trackNames()); + dprintf(fd, " AudioMixer tracks: 0x%08x\n", mAudioMixer->trackNames()); // Make a non-atomic copy of fast mixer dump state so it won't change underneath us const FastMixerDumpState copy(mFastMixerDumpState); @@ -5717,12 +5716,12 @@ void AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& args) { - fdprintf(fd, "\nInput thread %p:\n", this); + dprintf(fd, "\nInput thread %p:\n", this); if (mActiveTracks.size() > 0) { - fdprintf(fd, " Buffer size: %zu bytes\n", mBufferSize); + dprintf(fd, " Buffer size: %zu bytes\n", mBufferSize); } else { - fdprintf(fd, " No active record clients\n"); + dprintf(fd, " No active record clients\n"); } dprintf(fd, " Fast track available: %s\n", mFastTrackAvail ? "yes" : "no"); @@ -5738,9 +5737,9 @@ void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args size_t numtracks = mTracks.size(); size_t numactive = mActiveTracks.size(); size_t numactiveseen = 0; - fdprintf(fd, " %d Tracks", numtracks); + dprintf(fd, " %d Tracks", numtracks); if (numtracks) { - fdprintf(fd, " of which %d are active\n", numactive); + dprintf(fd, " of which %d are active\n", numactive); RecordTrack::appendDumpHeader(result); for (size_t i = 0; i < numtracks ; ++i) { sp<RecordTrack> track = mTracks[i]; @@ -5754,7 +5753,7 @@ void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args } } } else { - fdprintf(fd, "\n"); + dprintf(fd, "\n"); } if (numactiveseen != numactive) { diff --git a/services/audioflinger/tests/Android.mk b/services/audioflinger/tests/Android.mk new file mode 100644 index 0000000..874f18f --- /dev/null +++ b/services/audioflinger/tests/Android.mk @@ -0,0 +1,31 @@ +# Build the unit tests for audioflinger + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libutils \ + libcutils \ + libstlport \ + libaudioutils \ + libaudioresampler + +LOCAL_STATIC_LIBRARIES := \ + libgtest \ + libgtest_main + +LOCAL_C_INCLUDES := \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport \ + frameworks/av/services/audioflinger + +LOCAL_SRC_FILES := \ + resampler_tests.cpp + +LOCAL_MODULE := resampler_tests +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/services/audioflinger/tests/build_and_run_all_unit_tests.sh b/services/audioflinger/tests/build_and_run_all_unit_tests.sh new file mode 100755 index 0000000..2c453b0 --- /dev/null +++ b/services/audioflinger/tests/build_and_run_all_unit_tests.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +if [ -z "$ANDROID_BUILD_TOP" ]; then + echo "Android build environment not set" + exit -1 +fi + +# ensure we have mm +. $ANDROID_BUILD_TOP/build/envsetup.sh + +pushd $ANDROID_BUILD_TOP/frameworks/av/services/audioflinger/ +pwd +mm + +echo "waiting for device" +adb root && adb wait-for-device remount +adb push $OUT/system/lib/libaudioresampler.so /system/lib +adb push $OUT/system/bin/resampler_tests /system/bin + +sh $ANDROID_BUILD_TOP/frameworks/av/services/audioflinger/tests/run_all_unit_tests.sh + +popd diff --git a/services/audioflinger/tests/resampler_tests.cpp b/services/audioflinger/tests/resampler_tests.cpp new file mode 100644 index 0000000..8f9c270 --- /dev/null +++ b/services/audioflinger/tests/resampler_tests.cpp @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2014 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_NDEBUG 0 +#define LOG_TAG "audioflinger_resampler_tests" + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <errno.h> +#include <time.h> +#include <math.h> +#include <vector> +#include <utility> +#include <cutils/log.h> +#include <gtest/gtest.h> +#include <media/AudioBufferProvider.h> +#include "AudioResampler.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +template<typename T, typename U> +struct is_same +{ + static const bool value = false; +}; + +template<typename T> +struct is_same<T, T> // partial specialization +{ + static const bool value = true; +}; + +template<typename T> +static inline T convertValue(double val) +{ + if (is_same<T, int16_t>::value) { + return floor(val * 32767.0 + 0.5); + } else if (is_same<T, int32_t>::value) { + return floor(val * (1UL<<31) + 0.5); + } + return val; // assume float or double +} + +/* Creates a type-independent audio buffer provider from + * a buffer base address, size, framesize, and input increment array. + * + * No allocation or deallocation of the provided buffer is done. + */ +class TestProvider : public android::AudioBufferProvider { +public: + TestProvider(const void* addr, size_t frames, size_t frameSize, + const std::vector<size_t>& inputIncr) + : mAddr(addr), + mNumFrames(frames), + mFrameSize(frameSize), + mNextFrame(0), mUnrel(0), mInputIncr(inputIncr), mNextIdx(0) + { + } + + virtual android::status_t getNextBuffer(Buffer* buffer, int64_t pts __unused = kInvalidPTS ) + { + size_t requestedFrames = buffer->frameCount; + if (requestedFrames > mNumFrames - mNextFrame) { + buffer->frameCount = mNumFrames - mNextFrame; + } + if (!mInputIncr.empty()) { + size_t provided = mInputIncr[mNextIdx++]; + ALOGV("getNextBuffer() mValue[%d]=%u not %u", + mNextIdx-1, provided, buffer->frameCount); + if (provided < buffer->frameCount) { + buffer->frameCount = provided; + } + if (mNextIdx >= mInputIncr.size()) { + mNextIdx = 0; + } + } + ALOGV("getNextBuffer() requested %u frames out of %u frames available" + " and returned %u frames\n", + requestedFrames, mNumFrames - mNextFrame, buffer->frameCount); + mUnrel = buffer->frameCount; + if (buffer->frameCount > 0) { + buffer->raw = (char *)mAddr + mFrameSize * mNextFrame; + return android::NO_ERROR; + } else { + buffer->raw = NULL; + return android::NOT_ENOUGH_DATA; + } + } + + virtual void releaseBuffer(Buffer* buffer) + { + if (buffer->frameCount > mUnrel) { + ALOGE("releaseBuffer() released %u frames but only %u available " + "to release\n", buffer->frameCount, mUnrel); + mNextFrame += mUnrel; + mUnrel = 0; + } else { + + ALOGV("releaseBuffer() released %u frames out of %u frames available " + "to release\n", buffer->frameCount, mUnrel); + mNextFrame += buffer->frameCount; + mUnrel -= buffer->frameCount; + } + buffer->frameCount = 0; + buffer->raw = NULL; + } + + void reset() + { + mNextFrame = 0; + } + + size_t getNumFrames() + { + return mNumFrames; + } + + void setIncr(const std::vector<size_t> inputIncr) + { + mNextIdx = 0; + mInputIncr = inputIncr; + } + +protected: + const void* mAddr; // base address + size_t mNumFrames; // total frames + int mFrameSize; // frame size (# channels * bytes per sample) + size_t mNextFrame; // index of next frame to provide + size_t mUnrel; // number of frames not yet released + std::vector<size_t> mInputIncr; // number of frames provided per call + size_t mNextIdx; // index of next entry in mInputIncr to use +}; + +/* Creates a buffer filled with a sine wave. + * + * Returns a pair consisting of the sine signal buffer and the number of frames. + * The caller must delete[] the buffer when no longer needed (no shared_ptr<>). + */ +template<typename T> +static std::pair<T*, size_t> createSine(size_t channels, + double freq, double samplingRate, double time) +{ + double tscale = 1. / samplingRate; + size_t frames = static_cast<size_t>(samplingRate * time); + T* buffer = new T[frames * channels]; + for (size_t i = 0; i < frames; ++i) { + double t = i * tscale; + double y = sin(2. * M_PI * freq * t); + T yt = convertValue<T>(y); + + for (size_t j = 0; j < channels; ++j) { + buffer[i*channels + j] = yt / (j + 1); + } + } + return std::make_pair(buffer, frames); +} + +/* Creates a buffer filled with a chirp signal (a sine wave sweep). + * + * Returns a pair consisting of the chirp signal buffer and the number of frames. + * The caller must delete[] the buffer when no longer needed (no shared_ptr<>). + * + * When creating the Chirp, note that the frequency is the true sinusoidal + * frequency not the sampling rate. + * + * http://en.wikipedia.org/wiki/Chirp + */ +template<typename T> +static std::pair<T*, size_t> createChirp(size_t channels, + double minfreq, double maxfreq, double samplingRate, double time) +{ + double tscale = 1. / samplingRate; + size_t frames = static_cast<size_t>(samplingRate * time); + T *buffer = new T[frames * channels]; + // note the chirp constant k has a divide-by-two. + double k = (maxfreq - minfreq) / (2. * time); + for (size_t i = 0; i < frames; ++i) { + double t = i * tscale; + double y = sin(2. * M_PI * (k * t + minfreq) * t); + T yt = convertValue<T>(y); + + for (size_t j = 0; j < channels; ++j) { + buffer[i*channels + j] = yt / (j + 1); + } + } + return std::make_pair(buffer, frames); +} + +/* This derived class creates a buffer provider of datatype T, + * consisting of an input signal, e.g. from createChirp(). + * The number of frames can be obtained from the base class + * TestProvider::getNumFrames(). + */ +template <typename T> +class SignalProvider : public TestProvider { +public: + SignalProvider(const std::pair<T*, size_t>& bufferInfo, size_t channels, + const std::vector<size_t>& values) + : TestProvider(bufferInfo.first, bufferInfo.second, channels * sizeof(T), values), + mManagedPtr(bufferInfo.first) + { + } + + virtual ~SignalProvider() + { + delete[] mManagedPtr; + } + +protected: + T* mManagedPtr; +}; + +void resample(void *output, size_t outputFrames, const std::vector<size_t> &outputIncr, + android::AudioBufferProvider *provider, android::AudioResampler *resampler) +{ + for (size_t i = 0, j = 0; i < outputFrames; ) { + size_t thisFrames = outputIncr[j++]; + if (j >= outputIncr.size()) { + j = 0; + } + if (thisFrames == 0 || thisFrames > outputFrames - i) { + thisFrames = outputFrames - i; + } + resampler->resample((int32_t*) output + 2*i, thisFrames, provider); + i += thisFrames; + } +} + +void buffercmp(const void *reference, const void *test, + size_t outputFrameSize, size_t outputFrames) +{ + for (size_t i = 0; i < outputFrames; ++i) { + int check = memcmp((const char*)reference + i * outputFrameSize, + (const char*)test + i * outputFrameSize, outputFrameSize); + if (check) { + ALOGE("Failure at frame %d", i); + ASSERT_EQ(check, 0); /* fails */ + } + } +} + +void testBufferIncrement(size_t channels, unsigned inputFreq, unsigned outputFreq, + enum android::AudioResampler::src_quality quality) +{ + // create the provider + std::vector<size_t> inputIncr; + SignalProvider<int16_t> provider(createChirp<int16_t>(channels, + 0., outputFreq/2., outputFreq, outputFreq/2000.), + channels, inputIncr); + + // calculate the output size + size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq; + size_t outputFrameSize = 2 * sizeof(int32_t); + size_t outputSize = outputFrameSize * outputFrames; + outputSize &= ~7; + + // create the resampler + const int volumePrecision = 12; /* typical unity gain */ + android::AudioResampler* resampler; + + resampler = android::AudioResampler::create(16, channels, outputFreq, quality); + resampler->setSampleRate(inputFreq); + resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); + + // set up the reference run + std::vector<size_t> refIncr; + refIncr.push_back(outputFrames); + void* reference = malloc(outputSize); + resample(reference, outputFrames, refIncr, &provider, resampler); + + provider.reset(); + +#if 0 + /* this test will fail - API interface issue: reset() does not clear internal buffers */ + resampler->reset(); +#else + delete resampler; + resampler = android::AudioResampler::create(16, channels, outputFreq, quality); + resampler->setSampleRate(inputFreq); + resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); +#endif + + // set up the test run + std::vector<size_t> outIncr; + outIncr.push_back(1); + outIncr.push_back(2); + outIncr.push_back(3); + void* test = malloc(outputSize); + resample(test, outputFrames, outIncr, &provider, resampler); + + // check + buffercmp(reference, test, outputFrameSize, outputFrames); + + free(reference); + free(test); + delete resampler; +} + +template <typename T> +inline double sqr(T v) +{ + double dv = static_cast<double>(v); + return dv * dv; +} + +template <typename T> +double signalEnergy(T *start, T *end, unsigned stride) +{ + double accum = 0; + + for (T *p = start; p < end; p += stride) { + accum += sqr(*p); + } + unsigned count = (end - start + stride - 1) / stride; + return accum / count; +} + +void testStopbandDownconversion(size_t channels, + unsigned inputFreq, unsigned outputFreq, + unsigned passband, unsigned stopband, + enum android::AudioResampler::src_quality quality) +{ + // create the provider + std::vector<size_t> inputIncr; + SignalProvider<int16_t> provider(createChirp<int16_t>(channels, + 0., inputFreq/2., inputFreq, inputFreq/2000.), + channels, inputIncr); + + // calculate the output size + size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq; + size_t outputFrameSize = 2 * sizeof(int32_t); + size_t outputSize = outputFrameSize * outputFrames; + outputSize &= ~7; + + // create the resampler + const int volumePrecision = 12; /* typical unity gain */ + android::AudioResampler* resampler; + + resampler = android::AudioResampler::create(16, channels, outputFreq, quality); + resampler->setSampleRate(inputFreq); + resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); + + // set up the reference run + std::vector<size_t> refIncr; + refIncr.push_back(outputFrames); + void* reference = malloc(outputSize); + resample(reference, outputFrames, refIncr, &provider, resampler); + + int32_t *out = reinterpret_cast<int32_t *>(reference); + + // check signal energy in passband + const unsigned passbandFrame = passband * outputFreq / 1000.; + const unsigned stopbandFrame = stopband * outputFreq / 1000.; + + // check each channel separately + for (size_t i = 0; i < channels; ++i) { + double passbandEnergy = signalEnergy(out, out + passbandFrame * channels, channels); + double stopbandEnergy = signalEnergy(out + stopbandFrame * channels, + out + outputFrames * channels, channels); + double dbAtten = -10. * log10(stopbandEnergy / passbandEnergy); + ASSERT_GT(dbAtten, 60.); + +#if 0 + // internal verification + printf("if:%d of:%d pbf:%d sbf:%d sbe: %f pbe: %f db: %.2f\n", + provider.getNumFrames(), outputFrames, + passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten); + for (size_t i = 0; i < 10; ++i) { + printf("%d\n", out[i+passbandFrame*channels]); + } + for (size_t i = 0; i < 10; ++i) { + printf("%d\n", out[i+stopbandFrame*channels]); + } +#endif + } + + free(reference); + delete resampler; +} + +/* Buffer increment test + * + * We compare a reference output, where we consume and process the entire + * buffer at a time, and a test output, where we provide small chunks of input + * data and process small chunks of output (which may not be equivalent in size). + * + * Two subtests - fixed phase (3:2 down) and interpolated phase (147:320 up) + */ +TEST(audioflinger_resampler, bufferincrement_fixedphase) { + // all of these work + static const enum android::AudioResampler::src_quality kQualityArray[] = { + android::AudioResampler::LOW_QUALITY, + android::AudioResampler::MED_QUALITY, + android::AudioResampler::HIGH_QUALITY, + android::AudioResampler::VERY_HIGH_QUALITY, + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testBufferIncrement(2, 48000, 32000, kQualityArray[i]); + } +} + +TEST(audioflinger_resampler, bufferincrement_interpolatedphase) { + // all of these work except low quality + static const enum android::AudioResampler::src_quality kQualityArray[] = { +// android::AudioResampler::LOW_QUALITY, + android::AudioResampler::MED_QUALITY, + android::AudioResampler::HIGH_QUALITY, + android::AudioResampler::VERY_HIGH_QUALITY, + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testBufferIncrement(2, 22050, 48000, kQualityArray[i]); + } +} + +/* Simple aliasing test + * + * This checks stopband response of the chirp signal to make sure frequencies + * are properly suppressed. It uses downsampling because the stopband can be + * clearly isolated by input frequencies exceeding the output sample rate (nyquist). + */ +TEST(audioflinger_resampler, stopbandresponse) { + // not all of these may work (old resamplers fail on downsampling) + static const enum android::AudioResampler::src_quality kQualityArray[] = { + //android::AudioResampler::LOW_QUALITY, + //android::AudioResampler::MED_QUALITY, + //android::AudioResampler::HIGH_QUALITY, + //android::AudioResampler::VERY_HIGH_QUALITY, + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + // in this test we assume a maximum transition band between 12kHz and 20kHz. + // there must be at least 60dB relative attenuation between stopband and passband. + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testStopbandDownconversion(2, 48000, 32000, 12000, 20000, kQualityArray[i]); + } + + // in this test we assume a maximum transition band between 7kHz and 15kHz. + // there must be at least 60dB relative attenuation between stopband and passband. + // (the weird ratio triggers interpolative resampling) + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testStopbandDownconversion(2, 48000, 22101, 7000, 15000, kQualityArray[i]); + } +} diff --git a/services/audioflinger/tests/run_all_unit_tests.sh b/services/audioflinger/tests/run_all_unit_tests.sh new file mode 100755 index 0000000..ffae6ae --- /dev/null +++ b/services/audioflinger/tests/run_all_unit_tests.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ -z "$ANDROID_BUILD_TOP" ]; then + echo "Android build environment not set" + exit -1 +fi + +echo "waiting for device" +adb root && adb wait-for-device remount + +adb shell /system/bin/resampler_tests diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk index a22ad9d..cddc503 100644 --- a/services/audiopolicy/Android.mk +++ b/services/audiopolicy/Android.mk @@ -46,8 +46,8 @@ LOCAL_CFLAGS += -fvisibility=hidden include $(BUILD_SHARED_LIBRARY) + ifneq ($(USE_LEGACY_AUDIO_POLICY), 1) -ifneq ($(USE_CUSTOM_AUDIO_POLICY), 1) include $(CLEAR_VARS) @@ -62,6 +62,20 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \ libmedia_helper +LOCAL_MODULE:= libaudiopolicymanagerdefault + +include $(BUILD_SHARED_LIBRARY) + +ifneq ($(USE_CUSTOM_AUDIO_POLICY), 1) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioPolicyFactory.cpp + +LOCAL_SHARED_LIBRARIES := \ + libaudiopolicymanagerdefault + LOCAL_MODULE:= libaudiopolicymanager include $(BUILD_SHARED_LIBRARY) diff --git a/services/audiopolicy/AudioPolicyFactory.cpp b/services/audiopolicy/AudioPolicyFactory.cpp new file mode 100644 index 0000000..2ae7bc1 --- /dev/null +++ b/services/audiopolicy/AudioPolicyFactory.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 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. + */ + +#include "AudioPolicyManager.h" + +namespace android { + +extern "C" AudioPolicyInterface* createAudioPolicyManager( + AudioPolicyClientInterface *clientInterface) +{ + return new AudioPolicyManager(clientInterface); +} + +extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface) +{ + delete interface; +} + +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp index 61edac2..d4c9374 100644 --- a/services/audiopolicy/AudioPolicyManager.cpp +++ b/services/audiopolicy/AudioPolicyManager.cpp @@ -100,6 +100,7 @@ const StringToEnum sDeviceNameToEnumTable[] = { STRING_TO_ENUM(AUDIO_DEVICE_IN_TV_TUNER), STRING_TO_ENUM(AUDIO_DEVICE_IN_LINE), STRING_TO_ENUM(AUDIO_DEVICE_IN_SPDIF), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_A2DP), }; const StringToEnum sFlagNameToEnumTable[] = { @@ -284,16 +285,8 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, 0); } - if (device == AUDIO_DEVICE_OUT_WIRED_HEADSET) { - device = AUDIO_DEVICE_IN_WIRED_HEADSET; - } else if (device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO || - device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET || - device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) { - device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; - } else { - mpClientInterface->onAudioPortListUpdate(); - return NO_ERROR; - } + mpClientInterface->onAudioPortListUpdate(); + return NO_ERROR; } // end if is output device // handle input devices @@ -3809,6 +3802,12 @@ audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t input case AUDIO_SOURCE_DEFAULT: case AUDIO_SOURCE_MIC: + if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) { + device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP; + break; + } + // FALL THROUGH + case AUDIO_SOURCE_VOICE_RECOGNITION: case AUDIO_SOURCE_HOTWORD: case AUDIO_SOURCE_VOICE_COMMUNICATION: @@ -3985,6 +3984,11 @@ const AudioPolicyManager::VolumeCurvePoint }; const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sSpeakerMediaVolumeCurveDrc[AudioPolicyManager::VOLCNT] = { + {1, -56.0f}, {20, -34.0f}, {86, -10.0f}, {100, 0.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint AudioPolicyManager::sSpeakerSonificationVolumeCurve[AudioPolicyManager::VOLCNT] = { {1, -29.7f}, {33, -20.1f}, {66, -10.2f}, {100, 0.0f} }; @@ -4098,6 +4102,8 @@ void AudioPolicyManager::initializeVolumeCurves() sSpeakerSonificationVolumeCurveDrc; mStreams[AUDIO_STREAM_NOTIFICATION].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = sSpeakerSonificationVolumeCurveDrc; + mStreams[AUDIO_STREAM_MUSIC].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = + sSpeakerMediaVolumeCurveDrc; } } diff --git a/services/audiopolicy/AudioPolicyManager.h b/services/audiopolicy/AudioPolicyManager.h index adf1b33..1abeb6a 100644 --- a/services/audiopolicy/AudioPolicyManager.h +++ b/services/audiopolicy/AudioPolicyManager.h @@ -374,6 +374,7 @@ protected: static const VolumeCurvePoint sDefaultMediaVolumeCurve[AudioPolicyManager::VOLCNT]; // volume curve for media strategy on speakers static const VolumeCurvePoint sSpeakerMediaVolumeCurve[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sSpeakerMediaVolumeCurveDrc[AudioPolicyManager::VOLCNT]; // volume curve for sonification strategy on speakers static const VolumeCurvePoint sSpeakerSonificationVolumeCurve[AudioPolicyManager::VOLCNT]; static const VolumeCurvePoint sSpeakerSonificationVolumeCurveDrc[AudioPolicyManager::VOLCNT]; diff --git a/services/audiopolicy/AudioPolicyService.cpp b/services/audiopolicy/AudioPolicyService.cpp index a2a0461..93fab11 100644 --- a/services/audiopolicy/AudioPolicyService.cpp +++ b/services/audiopolicy/AudioPolicyService.cpp @@ -108,7 +108,7 @@ AudioPolicyService::AudioPolicyService() ALOGI("AudioPolicyService CSTOR in new mode"); mAudioPolicyClient = new AudioPolicyClient(this); - mAudioPolicyManager = new AudioPolicyManager(mAudioPolicyClient); + mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient); #endif // load audio pre processing modules @@ -145,7 +145,7 @@ AudioPolicyService::~AudioPolicyService() audio_policy_dev_close(mpAudioPolicyDev); } #else - delete mAudioPolicyManager; + destroyAudioPolicyManager(mAudioPolicyManager); delete mAudioPolicyClient; #endif @@ -399,7 +399,8 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() mLock.lock(); while (!exitPending()) { - while (!mAudioCommands.isEmpty()) { + sp<AudioPolicyService> svc; + while (!mAudioCommands.isEmpty() && !exitPending()) { nsecs_t curTime = systemTime(); // commands are sorted by increasing time stamp: execute them from index 0 and up if (mAudioCommands[0]->mTime <= curTime) { @@ -452,7 +453,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() StopOutputData *data = (StopOutputData *)command->mParam.get(); ALOGV("AudioCommandThread() processing stop output %d", data->mIO); - sp<AudioPolicyService> svc = mService.promote(); + svc = mService.promote(); if (svc == 0) { break; } @@ -464,7 +465,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() ReleaseOutputData *data = (ReleaseOutputData *)command->mParam.get(); ALOGV("AudioCommandThread() processing release output %d", data->mIO); - sp<AudioPolicyService> svc = mService.promote(); + svc = mService.promote(); if (svc == 0) { break; } @@ -494,7 +495,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() } break; case UPDATE_AUDIOPORT_LIST: { ALOGV("AudioCommandThread() processing update audio port list"); - sp<AudioPolicyService> svc = mService.promote(); + svc = mService.promote(); if (svc == 0) { break; } @@ -504,7 +505,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() }break; case UPDATE_AUDIOPATCH_LIST: { ALOGV("AudioCommandThread() processing update audio patch list"); - sp<AudioPolicyService> svc = mService.promote(); + svc = mService.promote(); if (svc == 0) { break; } @@ -542,9 +543,16 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() if (mAudioCommands.isEmpty()) { release_wake_lock(mName.string()); } - ALOGV("AudioCommandThread() going to sleep"); - mWaitWorkCV.waitRelative(mLock, waitTime); - ALOGV("AudioCommandThread() waking up"); + // release mLock before releasing strong reference on the service as + // AudioPolicyService destructor calls AudioCommandThread::exit() which acquires mLock. + mLock.unlock(); + svc.clear(); + mLock.lock(); + if (!exitPending()) { + ALOGV("AudioCommandThread() going to sleep"); + mWaitWorkCV.waitRelative(mLock, waitTime); + ALOGV("AudioCommandThread() waking up"); + } } mLock.unlock(); return false; diff --git a/services/audiopolicy/AudioPolicyService.h b/services/audiopolicy/AudioPolicyService.h index 40f589b..66d9cad 100644 --- a/services/audiopolicy/AudioPolicyService.h +++ b/services/audiopolicy/AudioPolicyService.h @@ -539,7 +539,7 @@ private: sp<AudioCommandThread> mOutputCommandThread; // process stop and release output struct audio_policy_device *mpAudioPolicyDev; struct audio_policy *mpAudioPolicy; - AudioPolicyManager *mAudioPolicyManager; + AudioPolicyInterface *mAudioPolicyManager; AudioPolicyClient *mAudioPolicyClient; KeyedVector< audio_source_t, InputSourceDesc* > mInputSources; diff --git a/services/camera/libcameraservice/utils/CameraTraces.cpp b/services/camera/libcameraservice/utils/CameraTraces.cpp index 346e15f..374dc5e 100644 --- a/services/camera/libcameraservice/utils/CameraTraces.cpp +++ b/services/camera/libcameraservice/utils/CameraTraces.cpp @@ -74,10 +74,10 @@ status_t CameraTraces::dump(int fd, const Vector<String16> &args __attribute__(( return BAD_VALUE; } - fdprintf(fd, "Camera traces (%zu):\n", pcsList.size()); + dprintf(fd, "Camera traces (%zu):\n", pcsList.size()); if (pcsList.empty()) { - fdprintf(fd, " No camera traces collected.\n"); + dprintf(fd, " No camera traces collected.\n"); } // Print newest items first diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp index 0c7fbbd..41dab1f 100644 --- a/services/medialog/MediaLogService.cpp +++ b/services/medialog/MediaLogService.cpp @@ -60,7 +60,7 @@ status_t MediaLogService::dump(int fd, const Vector<String16>& args __unused) static const String16 sDump("android.permission.DUMP"); if (!(IPCThreadState::self()->getCallingUid() == AID_MEDIA || PermissionCache::checkCallingPermission(sDump))) { - fdprintf(fd, "Permission Denial: can't dump media.log from pid=%d, uid=%d\n", + dprintf(fd, "Permission Denial: can't dump media.log from pid=%d, uid=%d\n", IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid()); return NO_ERROR; @@ -74,7 +74,7 @@ status_t MediaLogService::dump(int fd, const Vector<String16>& args __unused) for (size_t i = 0; i < namedReaders.size(); i++) { const NamedReader& namedReader = namedReaders[i]; if (fd >= 0) { - fdprintf(fd, "\n%s:\n", namedReader.name()); + dprintf(fd, "\n%s:\n", namedReader.name()); } else { ALOGI("%s:", namedReader.name()); } diff --git a/services/soundtrigger/Android.mk b/services/soundtrigger/Android.mk new file mode 100644 index 0000000..b7ccaab --- /dev/null +++ b/services/soundtrigger/Android.mk @@ -0,0 +1,41 @@ +# Copyright 2014 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + + +ifeq ($(SOUND_TRIGGER_USE_STUB_MODULE), 1) + LOCAL_CFLAGS += -DSOUND_TRIGGER_USE_STUB_MODULE +endif + +LOCAL_SRC_FILES:= \ + SoundTriggerHwService.cpp + +LOCAL_SHARED_LIBRARIES:= \ + libui \ + liblog \ + libutils \ + libbinder \ + libcutils \ + libhardware \ + libsoundtrigger + +#LOCAL_C_INCLUDES += \ + + +LOCAL_MODULE:= libsoundtriggerservice + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp new file mode 100644 index 0000000..fa59388 --- /dev/null +++ b/services/soundtrigger/SoundTriggerHwService.cpp @@ -0,0 +1,570 @@ +/* + * Copyright (C) 2014 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 "SoundTriggerHwService" +//#define LOG_NDEBUG 0 + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <pthread.h> + +#include <binder/IServiceManager.h> +#include <binder/MemoryBase.h> +#include <binder/MemoryHeapBase.h> +#include <cutils/atomic.h> +#include <cutils/properties.h> +#include <hardware/hardware.h> +#include <utils/Errors.h> +#include <utils/Log.h> + +#include "SoundTriggerHwService.h" +#include <system/sound_trigger.h> +#include <hardware/sound_trigger.h> + +namespace android { + +#ifdef SOUND_TRIGGER_USE_STUB_MODULE +#define HW_MODULE_PREFIX "stub" +#else +#define HW_MODULE_PREFIX "primary" +#endif + +SoundTriggerHwService::SoundTriggerHwService() + : BnSoundTriggerHwService(), + mNextUniqueId(1) +{ +} + +void SoundTriggerHwService::onFirstRef() +{ + const hw_module_t *mod; + int rc; + sound_trigger_hw_device *dev; + + rc = hw_get_module_by_class(SOUND_TRIGGER_HARDWARE_MODULE_ID, HW_MODULE_PREFIX, &mod); + if (rc != 0) { + ALOGE("couldn't load sound trigger module %s.%s (%s)", + SOUND_TRIGGER_HARDWARE_MODULE_ID, "primary", strerror(-rc)); + return; + } + rc = sound_trigger_hw_device_open(mod, &dev); + if (rc != 0) { + ALOGE("couldn't open sound trigger hw device in %s.%s (%s)", + SOUND_TRIGGER_HARDWARE_MODULE_ID, "primary", strerror(-rc)); + return; + } + if (dev->common.version != SOUND_TRIGGER_DEVICE_API_VERSION_CURRENT) { + ALOGE("wrong sound trigger hw device version %04x", dev->common.version); + return; + } + + sound_trigger_module_descriptor descriptor; + rc = dev->get_properties(dev, &descriptor.properties); + if (rc != 0) { + ALOGE("could not read implementation properties"); + return; + } + descriptor.handle = + (sound_trigger_module_handle_t)android_atomic_inc(&mNextUniqueId); + ALOGI("loaded default module %s, handle %d", descriptor.properties.description, + descriptor.handle); + + sp<ISoundTriggerClient> client; + sp<Module> module = new Module(this, dev, descriptor, client); + mModules.add(descriptor.handle, module); + mCallbackThread = new CallbackThread(this); +} + +SoundTriggerHwService::~SoundTriggerHwService() +{ + if (mCallbackThread != 0) { + mCallbackThread->exit(); + } + for (size_t i = 0; i < mModules.size(); i++) { + sound_trigger_hw_device_close(mModules.valueAt(i)->hwDevice()); + } +} + +status_t SoundTriggerHwService::listModules(struct sound_trigger_module_descriptor *modules, + uint32_t *numModules) +{ + ALOGV("listModules"); + AutoMutex lock(mServiceLock); + if (numModules == NULL || (*numModules != 0 && modules == NULL)) { + return BAD_VALUE; + } + size_t maxModules = *numModules; + *numModules = mModules.size(); + for (size_t i = 0; i < mModules.size() && i < maxModules; i++) { + modules[i] = mModules.valueAt(i)->descriptor(); + } + return NO_ERROR; +} + +status_t SoundTriggerHwService::attach(const sound_trigger_module_handle_t handle, + const sp<ISoundTriggerClient>& client, + sp<ISoundTrigger>& moduleInterface) +{ + ALOGV("attach module %d", handle); + AutoMutex lock(mServiceLock); + moduleInterface.clear(); + if (client == 0) { + return BAD_VALUE; + } + ssize_t index = mModules.indexOfKey(handle); + if (index < 0) { + return BAD_VALUE; + } + sp<Module> module = mModules.valueAt(index); + + module->setClient(client); + client->asBinder()->linkToDeath(module); + moduleInterface = module; + + return NO_ERROR; +} + +void SoundTriggerHwService::detachModule(sp<Module> module) { + AutoMutex lock(mServiceLock); + ALOGV("detachModule"); + module->clearClient(); +} + +static const int kDumpLockRetries = 50; +static const int kDumpLockSleep = 60000; + +static bool tryLock(Mutex& mutex) +{ + bool locked = false; + for (int i = 0; i < kDumpLockRetries; ++i) { + if (mutex.tryLock() == NO_ERROR) { + locked = true; + break; + } + usleep(kDumpLockSleep); + } + return locked; +} + +status_t SoundTriggerHwService::dump(int fd, const Vector<String16>& args __unused) { + String8 result; + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + result.appendFormat("Permission Denial: can't dump SoundTriggerHwService"); + write(fd, result.string(), result.size()); + } else { + bool locked = tryLock(mServiceLock); + // failed to lock - SoundTriggerHwService is probably deadlocked + if (!locked) { + result.append("SoundTriggerHwService may be deadlocked\n"); + write(fd, result.string(), result.size()); + } + + if (locked) mServiceLock.unlock(); + } + return NO_ERROR; +} + +status_t SoundTriggerHwService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + return BnSoundTriggerHwService::onTransact(code, data, reply, flags); +} + + +// static +void SoundTriggerHwService::recognitionCallback(struct sound_trigger_recognition_event *event, + void *cookie) +{ + Module *module = (Module *)cookie; + if (module == NULL) { + return; + } + module->sendRecognitionEvent(event); +} + + +void SoundTriggerHwService::sendRecognitionEvent(const sp<RecognitionEvent>& event) +{ + mCallbackThread->sendRecognitionEvent(event); +} + +void SoundTriggerHwService::onRecognitionEvent(const sp<RecognitionEvent>& event) +{ + ALOGV("onRecognitionEvent"); + sp<Module> module; + { + AutoMutex lock(mServiceLock); + module = event->mModule.promote(); + if (module == 0) { + return; + } + } + module->onRecognitionEvent(event->mEventMemory); +} + +// static +void SoundTriggerHwService::soundModelCallback(struct sound_trigger_model_event *event __unused, + void *cookie) +{ + Module *module = (Module *)cookie; + +} + +#undef LOG_TAG +#define LOG_TAG "SoundTriggerHwService::CallbackThread" + +SoundTriggerHwService::CallbackThread::CallbackThread(const wp<SoundTriggerHwService>& service) + : mService(service) +{ +} + +SoundTriggerHwService::CallbackThread::~CallbackThread() +{ + mEventQueue.clear(); +} + +void SoundTriggerHwService::CallbackThread::onFirstRef() +{ + run("soundTrigger cbk", ANDROID_PRIORITY_URGENT_AUDIO); +} + +bool SoundTriggerHwService::CallbackThread::threadLoop() +{ + while (!exitPending()) { + sp<RecognitionEvent> event; + sp<SoundTriggerHwService> service; + { + Mutex::Autolock _l(mCallbackLock); + while (mEventQueue.isEmpty() && !exitPending()) { + ALOGV("CallbackThread::threadLoop() sleep"); + mCallbackCond.wait(mCallbackLock); + ALOGV("CallbackThread::threadLoop() wake up"); + } + if (exitPending()) { + break; + } + event = mEventQueue[0]; + mEventQueue.removeAt(0); + service = mService.promote(); + } + if (service != 0) { + service->onRecognitionEvent(event); + } + } + return false; +} + +void SoundTriggerHwService::CallbackThread::exit() +{ + Mutex::Autolock _l(mCallbackLock); + requestExit(); + mCallbackCond.broadcast(); +} + +void SoundTriggerHwService::CallbackThread::sendRecognitionEvent( + const sp<SoundTriggerHwService::RecognitionEvent>& event) +{ + AutoMutex lock(mCallbackLock); + mEventQueue.add(event); + mCallbackCond.signal(); +} + +SoundTriggerHwService::RecognitionEvent::RecognitionEvent( + sp<IMemory> eventMemory, + wp<Module> module) + : mEventMemory(eventMemory), mModule(module) +{ +} + +SoundTriggerHwService::RecognitionEvent::~RecognitionEvent() +{ +} + +#undef LOG_TAG +#define LOG_TAG "SoundTriggerHwService::Module" + +SoundTriggerHwService::Module::Module(const sp<SoundTriggerHwService>& service, + sound_trigger_hw_device* hwDevice, + sound_trigger_module_descriptor descriptor, + const sp<ISoundTriggerClient>& client) + : mService(service), mHwDevice(hwDevice), mDescriptor(descriptor), + mClient(client) +{ +} + +SoundTriggerHwService::Module::~Module() { +} + +void SoundTriggerHwService::Module::detach() { + ALOGV("detach()"); + { + AutoMutex lock(mLock); + for (size_t i = 0; i < mModels.size(); i++) { + sp<Model> model = mModels.valueAt(i); + ALOGV("detach() unloading model %d", model->mHandle); + if (model->mState == Model::STATE_ACTIVE) { + mHwDevice->stop_recognition(mHwDevice, model->mHandle); + model->deallocateMemory(); + } + mHwDevice->unload_sound_model(mHwDevice, model->mHandle); + } + mModels.clear(); + } + if (mClient != 0) { + mClient->asBinder()->unlinkToDeath(this); + } + sp<SoundTriggerHwService> service = mService.promote(); + if (service == 0) { + return; + } + service->detachModule(this); +} + +status_t SoundTriggerHwService::Module::loadSoundModel(const sp<IMemory>& modelMemory, + sound_model_handle_t *handle) +{ + ALOGV("loadSoundModel() handle"); + + if (modelMemory == 0 || modelMemory->pointer() == NULL) { + ALOGE("loadSoundModel() modelMemory is 0 or has NULL pointer()"); + return BAD_VALUE; + } + struct sound_trigger_sound_model *sound_model = + (struct sound_trigger_sound_model *)modelMemory->pointer(); + + AutoMutex lock(mLock); + status_t status = mHwDevice->load_sound_model(mHwDevice, + sound_model, + SoundTriggerHwService::soundModelCallback, + this, + handle); + if (status == NO_ERROR) { + mModels.replaceValueFor(*handle, new Model(*handle)); + } + + return status; +} + +status_t SoundTriggerHwService::Module::unloadSoundModel(sound_model_handle_t handle) +{ + ALOGV("unloadSoundModel() model handle %d", handle); + + AutoMutex lock(mLock); + ssize_t index = mModels.indexOfKey(handle); + if (index < 0) { + return BAD_VALUE; + } + sp<Model> model = mModels.valueAt(index); + mModels.removeItem(handle); + if (model->mState == Model::STATE_ACTIVE) { + mHwDevice->stop_recognition(mHwDevice, model->mHandle); + model->deallocateMemory(); + } + return mHwDevice->unload_sound_model(mHwDevice, handle); +} + +status_t SoundTriggerHwService::Module::startRecognition(sound_model_handle_t handle, + const sp<IMemory>& dataMemory) +{ + ALOGV("startRecognition() model handle %d", handle); + + if (dataMemory != 0 && dataMemory->pointer() == NULL) { + ALOGE("startRecognition() dataMemory is non-0 but has NULL pointer()"); + return BAD_VALUE; + + } + AutoMutex lock(mLock); + sp<Model> model = getModel(handle); + if (model == 0) { + return BAD_VALUE; + } + + if (model->mState == Model::STATE_ACTIVE) { + return INVALID_OPERATION; + } + model->mState = Model::STATE_ACTIVE; + + char *data = NULL; + unsigned int data_size = 0; + if (dataMemory != 0 && dataMemory->size() != 0) { + data_size = (unsigned int)dataMemory->size(); + data = (char *)dataMemory->pointer(); + ALOGV("startRecognition() data size %d data %d - %d", + data_size, data[0], data[data_size - 1]); + } + + //TODO: get capture handle and device from audio policy service + audio_io_handle_t capture_handle = 0; + return mHwDevice->start_recognition(mHwDevice, handle, capture_handle, AUDIO_DEVICE_NONE, + SoundTriggerHwService::recognitionCallback, + this, + data_size, + data); +} + +status_t SoundTriggerHwService::Module::stopRecognition(sound_model_handle_t handle) +{ + ALOGV("stopRecognition() model handle %d", handle); + + AutoMutex lock(mLock); + sp<Model> model = getModel(handle); + if (model == 0) { + return BAD_VALUE; + } + + if (model->mState != Model::STATE_ACTIVE) { + return INVALID_OPERATION; + } + mHwDevice->stop_recognition(mHwDevice, handle); + model->deallocateMemory(); + model->mState = Model::STATE_IDLE; + return NO_ERROR; +} + +void SoundTriggerHwService::Module::sendRecognitionEvent( + struct sound_trigger_recognition_event *event) +{ + sp<SoundTriggerHwService> service; + sp<IMemory> eventMemory; + ALOGV("sendRecognitionEvent for model %d", event->model); + { + AutoMutex lock(mLock); + sp<Model> model = getModel(event->model); + if (model == 0) { + return; + } + if (model->mState != Model::STATE_ACTIVE) { + ALOGV("sendRecognitionEvent model->mState %d != Model::STATE_ACTIVE", model->mState); + return; + } + if (mClient == 0) { + return; + } + service = mService.promote(); + if (service == 0) { + return; + } + + //sanitize event + switch (event->type) { + case SOUND_MODEL_TYPE_KEYPHRASE: + ALOGW_IF(event->data_offset != + sizeof(struct sound_trigger_phrase_recognition_event), + "sendRecognitionEvent(): invalid data offset %u for keyphrase event type", + event->data_offset); + event->data_offset = sizeof(struct sound_trigger_phrase_recognition_event); + break; + case SOUND_MODEL_TYPE_UNKNOWN: + ALOGW_IF(event->data_offset != + sizeof(struct sound_trigger_recognition_event), + "sendRecognitionEvent(): invalid data offset %u for unknown event type", + event->data_offset); + event->data_offset = sizeof(struct sound_trigger_recognition_event); + break; + default: + return; + } + + size_t size = event->data_offset + event->data_size; + eventMemory = model->allocateMemory(size); + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + return; + } + memcpy(eventMemory->pointer(), event, size); + } + service->sendRecognitionEvent(new RecognitionEvent(eventMemory, this)); +} + +void SoundTriggerHwService::Module::onRecognitionEvent(sp<IMemory> eventMemory) +{ + ALOGV("Module::onRecognitionEvent"); + + AutoMutex lock(mLock); + + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + return; + } + struct sound_trigger_recognition_event *event = + (struct sound_trigger_recognition_event *)eventMemory->pointer(); + + sp<Model> model = getModel(event->model); + if (model == 0) { + ALOGI("%s model == 0", __func__); + return; + } + if (model->mState != Model::STATE_ACTIVE) { + ALOGV("onRecognitionEvent model->mState %d != Model::STATE_ACTIVE", model->mState); + return; + } + if (mClient == 0) { + ALOGI("%s mClient == 0", __func__); + return; + } + mClient->onRecognitionEvent(eventMemory); + model->mState = Model::STATE_IDLE; + model->deallocateMemory(); +} + +sp<SoundTriggerHwService::Model> SoundTriggerHwService::Module::getModel( + sound_model_handle_t handle) +{ + sp<Model> model; + ssize_t index = mModels.indexOfKey(handle); + if (index >= 0) { + model = mModels.valueAt(index); + } + return model; +} + +void SoundTriggerHwService::Module::binderDied( + const wp<IBinder> &who __unused) { + ALOGW("client binder died for module %d", mDescriptor.handle); + detach(); +} + + +SoundTriggerHwService::Model::Model(sound_model_handle_t handle) : + mHandle(handle), mState(STATE_IDLE), mInputHandle(AUDIO_IO_HANDLE_NONE), + mCaptureSession(AUDIO_SESSION_ALLOCATE), + mMemoryDealer(new MemoryDealer(sizeof(struct sound_trigger_recognition_event), + "SoundTriggerHwService::Event")) +{ + +} + + +sp<IMemory> SoundTriggerHwService::Model::allocateMemory(size_t size) +{ + sp<IMemory> memory; + if (mMemoryDealer->getMemoryHeap()->getSize() < size) { + mMemoryDealer = new MemoryDealer(size, "SoundTriggerHwService::Event"); + } + memory = mMemoryDealer->allocate(size); + return memory; +} + +void SoundTriggerHwService::Model::deallocateMemory() +{ + mMemoryDealer->deallocate(0); +} + +status_t SoundTriggerHwService::Module::dump(int fd __unused, + const Vector<String16>& args __unused) { + String8 result; + return NO_ERROR; +} + +}; // namespace android diff --git a/services/soundtrigger/SoundTriggerHwService.h b/services/soundtrigger/SoundTriggerHwService.h new file mode 100644 index 0000000..377f2a1 --- /dev/null +++ b/services/soundtrigger/SoundTriggerHwService.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H +#define ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H + +#include <utils/Vector.h> +//#include <binder/AppOpsManager.h> +#include <binder/MemoryDealer.h> +#include <binder/BinderService.h> +#include <binder/IAppOpsCallback.h> +#include <soundtrigger/ISoundTriggerHwService.h> +#include <soundtrigger/ISoundTrigger.h> +#include <soundtrigger/ISoundTriggerClient.h> +#include <system/sound_trigger.h> +#include <hardware/sound_trigger.h> + +namespace android { + +class MemoryHeapBase; + +class SoundTriggerHwService : + public BinderService<SoundTriggerHwService>, + public BnSoundTriggerHwService +{ + friend class BinderService<SoundTriggerHwService>; +public: + class Module; + + static char const* getServiceName() { return "media.sound_trigger_hw"; } + + SoundTriggerHwService(); + virtual ~SoundTriggerHwService(); + + // ISoundTriggerHwService + virtual status_t listModules(struct sound_trigger_module_descriptor *modules, + uint32_t *numModules); + + virtual status_t attach(const sound_trigger_module_handle_t handle, + const sp<ISoundTriggerClient>& client, + sp<ISoundTrigger>& module); + + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + + virtual status_t dump(int fd, const Vector<String16>& args); + + class Model : public RefBase { + public: + + enum { + STATE_IDLE, + STATE_ACTIVE + }; + + Model(sound_model_handle_t handle); + ~Model() {} + + sp<IMemory> allocateMemory(size_t size); + void deallocateMemory(); + + sound_model_handle_t mHandle; + int mState; + audio_io_handle_t mInputHandle; + audio_session_t mCaptureSession; + sp<MemoryDealer> mMemoryDealer; + }; + + class Module : public virtual RefBase, + public BnSoundTrigger, + public IBinder::DeathRecipient { + public: + + Module(const sp<SoundTriggerHwService>& service, + sound_trigger_hw_device* hwDevice, + sound_trigger_module_descriptor descriptor, + const sp<ISoundTriggerClient>& client); + + virtual ~Module(); + + virtual void detach(); + + virtual status_t loadSoundModel(const sp<IMemory>& modelMemory, + sound_model_handle_t *handle); + + virtual status_t unloadSoundModel(sound_model_handle_t handle); + + virtual status_t startRecognition(sound_model_handle_t handle, + const sp<IMemory>& dataMemory); + virtual status_t stopRecognition(sound_model_handle_t handle); + + virtual status_t dump(int fd, const Vector<String16>& args); + + + sound_trigger_hw_device *hwDevice() const { return mHwDevice; } + struct sound_trigger_module_descriptor descriptor() { return mDescriptor; } + void setClient(sp<ISoundTriggerClient> client) { mClient = client; } + void clearClient() { mClient.clear(); } + sp<ISoundTriggerClient> client() { return mClient; } + + void sendRecognitionEvent(struct sound_trigger_recognition_event *event); + void onRecognitionEvent(sp<IMemory> eventMemory); + + sp<Model> getModel(sound_model_handle_t handle); + + // IBinder::DeathRecipient implementation + virtual void binderDied(const wp<IBinder> &who); + + private: + Mutex mLock; + wp<SoundTriggerHwService> mService; + struct sound_trigger_hw_device* mHwDevice; + struct sound_trigger_module_descriptor mDescriptor; + sp<ISoundTriggerClient> mClient; + DefaultKeyedVector< sound_model_handle_t, sp<Model> > mModels; + }; // class Module + + class RecognitionEvent : public RefBase { + public: + + RecognitionEvent(sp<IMemory> eventMemory, wp<Module> module); + + virtual ~RecognitionEvent(); + + sp<IMemory> mEventMemory; + wp<Module> mModule; + }; + + class CallbackThread : public Thread { + public: + + CallbackThread(const wp<SoundTriggerHwService>& service); + + virtual ~CallbackThread(); + + // Thread virtuals + virtual bool threadLoop(); + + // RefBase + virtual void onFirstRef(); + + void exit(); + void sendRecognitionEvent(const sp<RecognitionEvent>& event); + + private: + wp<SoundTriggerHwService> mService; + Condition mCallbackCond; + Mutex mCallbackLock; + Vector< sp<RecognitionEvent> > mEventQueue; + }; + + void detachModule(sp<Module> module); + + static void recognitionCallback(struct sound_trigger_recognition_event *event, void *cookie); + void sendRecognitionEvent(const sp<RecognitionEvent>& event); + void onRecognitionEvent(const sp<RecognitionEvent>& event); + + static void soundModelCallback(struct sound_trigger_model_event *event, void *cookie); + +private: + + virtual void onFirstRef(); + + Mutex mServiceLock; + volatile int32_t mNextUniqueId; + DefaultKeyedVector< sound_trigger_module_handle_t, sp<Module> > mModules; + sp<CallbackThread> mCallbackThread; +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H |