summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/audioflinger/AudioFlinger.cpp10
-rw-r--r--services/audioflinger/AudioResamplerDyn.cpp56
-rw-r--r--services/audioflinger/AudioWatchdog.cpp2
-rw-r--r--services/audioflinger/FastMixer.cpp63
-rw-r--r--services/audioflinger/StateQueue.cpp4
-rw-r--r--services/audioflinger/Threads.cpp73
-rw-r--r--services/audioflinger/tests/Android.mk31
-rwxr-xr-xservices/audioflinger/tests/build_and_run_all_unit_tests.sh22
-rw-r--r--services/audioflinger/tests/resampler_tests.cpp471
-rwxr-xr-xservices/audioflinger/tests/run_all_unit_tests.sh11
-rw-r--r--services/audiopolicy/Android.mk16
-rw-r--r--services/audiopolicy/AudioPolicyFactory.cpp32
-rw-r--r--services/audiopolicy/AudioPolicyManager.cpp26
-rw-r--r--services/audiopolicy/AudioPolicyManager.h1
-rw-r--r--services/audiopolicy/AudioPolicyService.cpp28
-rw-r--r--services/audiopolicy/AudioPolicyService.h2
-rw-r--r--services/camera/libcameraservice/utils/CameraTraces.cpp4
-rw-r--r--services/medialog/MediaLogService.cpp4
-rw-r--r--services/soundtrigger/Android.mk41
-rw-r--r--services/soundtrigger/SoundTriggerHwService.cpp570
-rw-r--r--services/soundtrigger/SoundTriggerHwService.h185
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