Improve resource recovery.

Better cleanup in AudioPlayer.Destroy.
Avoid mallocs in LEDArray and Equalizer.
Line length 100.

Change-Id: I2d22880b27ccf7e9038b398cae9287781fd82253

Continued work on threading.

Lay the groundwork for true asynchronous realize.
Call all callbacks with mutex unlocked.
C volatile is meaningless.
Start adding support for suspend/resume on interfaces.
Line length 100.

Change-Id: I631cb4f123143e4ef79c6c491d12b1e559857cab
diff --git a/libopensles/CAudioPlayer.c b/libopensles/CAudioPlayer.c
index 1208aa7..dd78033 100644
--- a/libopensles/CAudioPlayer.c
+++ b/libopensles/CAudioPlayer.c
@@ -67,12 +67,14 @@
 void CAudioPlayer_Destroy(void *self)
 {
     CAudioPlayer *this = (CAudioPlayer *) self;
+    freeDataLocatorFormat(&this->mDataSource);
+    freeDataLocatorFormat(&this->mDataSink);
     // FIXME stop the player in a way that app can't restart it
     // Free the buffer queue, if it was larger than typical
     if (NULL != this->mBufferQueue.mArray &&
         this->mBufferQueue.mArray != this->mBufferQueue.mTypical) {
-        free(this->mBufferQueue.mArray);
-        this->mBufferQueue.mArray = NULL;
+            free(this->mBufferQueue.mArray);
+            this->mBufferQueue.mArray = NULL;
     }
 #ifdef USE_SNDFILE
     if (NULL != this->mSndFile.mSNDFILE) {
diff --git a/libopensles/IBufferQueue.c b/libopensles/IBufferQueue.c
index 9596050..70366cf 100644
--- a/libopensles/IBufferQueue.c
+++ b/libopensles/IBufferQueue.c
@@ -84,7 +84,6 @@
     slBufferQueueCallback callback, void *pContext)
 {
     IBufferQueue *this = (IBufferQueue *) self;
-    // FIXME This could be a poke lock, if we had atomic double-word load/store
     interface_lock_exclusive(this);
     this->mCallback = callback;
     this->mContext = pContext;
diff --git a/libopensles/IDynamicInterfaceManagement.c b/libopensles/IDynamicInterfaceManagement.c
index d7a7f55..166eb0b 100644
--- a/libopensles/IDynamicInterfaceManagement.c
+++ b/libopensles/IDynamicInterfaceManagement.c
@@ -18,9 +18,8 @@
 
 #include "sles_allinclusive.h"
 
-static SLresult IDynamicInterfaceManagement_AddInterface(
-    SLDynamicInterfaceManagementItf self, const SLInterfaceID iid,
-    SLboolean async)
+static SLresult IDynamicInterfaceManagement_AddInterface(SLDynamicInterfaceManagementItf self,
+    const SLInterfaceID iid, SLboolean async)
 {
     if (NULL == iid)
         return SL_RESULT_PARAMETER_INVALID;
@@ -39,27 +38,33 @@
     size_t size = ((SLuint32) (index + 1) == class__->mInterfaceCount ?
         class__->mSize : x[1].mOffset) - offset;
     unsigned mask = 1 << index;
+    slDynamicInterfaceManagementCallback callback = NULL;
+    void *context = NULL;
     // Lock the object rather than the DIM interface, because
     // we modify both the object (exposed) and interface (added)
     object_lock_exclusive(thisObject);
     if (thisObject->mExposedMask & mask) {
         result = SL_RESULT_PRECONDITIONS_VIOLATED;
     } else {
-        // FIXME Currently do initialization here, but might be asynchronous
+        // FIXME Currently all initialization is done here, even if requested to be asynchronous
         memset(thisItf, 0, size);
         ((void **) thisItf)[1] = thisObject;
         if (NULL != init)
             (*init)(thisItf);
         thisObject->mExposedMask |= mask;
+        assert(!(this->mAddedMask & mask));
         this->mAddedMask |= mask;
+        assert(!(this->mSuspendedMask & mask));
         result = SL_RESULT_SUCCESS;
-        if (async && (NULL != this->mCallback)) {
-            // FIXME Callback runs with mutex locked
-            (*this->mCallback)(self, this->mContext,
-                SL_DYNAMIC_ITF_EVENT_RESOURCES_AVAILABLE, result, iid);
+        // Make a copy of these, so we can call the callback with mutex unlocked
+        if (async) {
+            callback = this->mCallback;
+            context = this->mContext;
         }
     }
     object_unlock_exclusive(thisObject);
+    if (NULL != callback)
+        (*callback)(self, context, SL_DYNAMIC_ITF_EVENT_ASYNC_TERMINATION, result, iid);
     return result;
 }
 
@@ -92,6 +97,7 @@
     if (!(this->mAddedMask & mask)) {
         result = SL_RESULT_PRECONDITIONS_VIOLATED;
     } else {
+        // FIXME When async resume is implemented, a pending async resume should be cancelled
         if (NULL != deinit)
             (*deinit)(thisItf);
 #ifndef NDEBUG
@@ -99,13 +105,13 @@
 #endif
         thisObject->mExposedMask &= ~mask;
         this->mAddedMask &= ~mask;
+        this->mSuspendedMask &= ~mask;
     }
     object_unlock_exclusive(thisObject);
     return result;
 }
 
-static SLresult IDynamicInterfaceManagement_ResumeInterface(
-    SLDynamicInterfaceManagementItf self,
+static SLresult IDynamicInterfaceManagement_ResumeInterface(SLDynamicInterfaceManagementItf self,
     const SLInterfaceID iid, SLboolean async)
 {
     if (NULL == iid)
@@ -119,24 +125,38 @@
     int index = class__->mMPH_to_index[MPH];
     if (0 > index)
         return SL_RESULT_PRECONDITIONS_VIOLATED;
-    SLresult result = SL_RESULT_SUCCESS;
+    SLresult result;
     unsigned mask = 1 << index;
+    slDynamicInterfaceManagementCallback callback = NULL;
+    void *context = NULL;
     // FIXME Change to exclusive when resume hook implemented
     object_lock_shared(thisObject);
-    if (!(this->mAddedMask & mask))
+    if (!(this->mSuspendedMask & mask))
         result = SL_RESULT_PRECONDITIONS_VIOLATED;
+    else {
+        assert(this->mAddedMask & mask);
+        assert(thisObject->mExposedMask & mask);
+        // FIXME Currently the resume is done here, even if requested to be asynchronous
+        this->mSuspendedMask &= ~mask;
+        result = SL_RESULT_SUCCESS;
+        // Make a copy of these, so we can call the callback with mutex unlocked
+        if (async) {
+            callback = this->mCallback;
+            context = this->mContext;
+        }
+    }
     // FIXME Call a resume hook on the interface, if suspended
     object_unlock_shared(thisObject);
+    if (NULL != callback)
+        (*callback)(self, context, SL_DYNAMIC_ITF_EVENT_ASYNC_TERMINATION, result, iid);
     return result;
 }
 
-static SLresult IDynamicInterfaceManagement_RegisterCallback(
-    SLDynamicInterfaceManagementItf self,
+static SLresult IDynamicInterfaceManagement_RegisterCallback(SLDynamicInterfaceManagementItf self,
     slDynamicInterfaceManagementCallback callback, void *pContext)
 {
     IDynamicInterfaceManagement *this = (IDynamicInterfaceManagement *) self;
     IObject *thisObject = this->mThis;
-    // FIXME This could be a poke lock, if we had atomic double-word load/store
     object_lock_exclusive(thisObject);
     this->mCallback = callback;
     this->mContext = pContext;
@@ -156,6 +176,7 @@
     IDynamicInterfaceManagement *this = (IDynamicInterfaceManagement *) self;
     this->mItf = &IDynamicInterfaceManagement_Itf;
     this->mAddedMask = 0;
+    this->mSuspendedMask = 0;
     this->mCallback = NULL;
     this->mContext = NULL;
 }
diff --git a/libopensles/IEngine.c b/libopensles/IEngine.c
index 9601601..22ffc8a 100644
--- a/libopensles/IEngine.c
+++ b/libopensles/IEngine.c
@@ -33,16 +33,6 @@
     CLEDDevice *this = (CLEDDevice *) construct(pCLEDDevice_class, exposedMask, self);
     if (NULL == this)
         return SL_RESULT_MEMORY_FAILURE;
-    SLHSL *color = (SLHSL *) malloc(sizeof(SLHSL) * this->mLEDArray.mCount);
-    assert(NULL != this->mLEDArray.mColor);
-    this->mLEDArray.mColor = color;
-    unsigned i;
-    for (i = 0; i < this->mLEDArray.mCount; ++i) {
-        // per specification 1.0.1 pg. 259: "Default color is undefined."
-        color->hue = 0;
-        color->saturation = 1000;
-        color->lightness = 1000;
-    }
     this->mDeviceID = deviceID;
     *pDevice = &this->mObject.mItf;
     return SL_RESULT_SUCCESS;
@@ -157,12 +147,7 @@
     return SL_RESULT_SUCCESS;
 
 abort:
-    freeDataLocatorFormat(&this->mDataSource);
-    freeDataLocatorFormat(&this->mDataSink);
-    if ((NULL != this->mBufferQueue.mArray) &&
-        (this->mBufferQueue.mTypical != this->mBufferQueue.mArray)) {
-            free(this->mBufferQueue.mArray);
-    }
+    CAudioPlayer_Destroy(this);
     free(this);
     return result;
 }
diff --git a/libopensles/IEqualizer.c b/libopensles/IEqualizer.c
index 4664ee4..4628190 100644
--- a/libopensles/IEqualizer.c
+++ b/libopensles/IEqualizer.c
@@ -18,28 +18,26 @@
 
 #include "sles_allinclusive.h"
 
-// FIXME move
+// FIXME move to platform-specific configuration
 
-const struct EqualizerBand EqualizerBands[] = {
+#define MAX_EQ_PRESETS 3
+
+static const struct EqualizerBand EqualizerBands[MAX_EQ_BANDS] = {
     {1000, 1500, 2000},
     {2000, 3000, 4000},
     {4000, 5500, 7000},
     {7000, 8000, 9000}
 };
 
-#define MAX_BANDS (sizeof(EqualizerBands)/sizeof(EqualizerBands[0]))
-
-const struct EqualizerPreset {
-    const SLchar *mName;
-    SLmillibel mLevels[MAX_BANDS];
-} EqualizerPresets[] = {
-    {(const SLchar *) "Default", {0, 0, 0, 0}},
-    {(const SLchar *) "Bass", {500, 200, 100, 0}},
-    {(const SLchar *) "Treble", {0, 100, 200, 500}}
+static const struct EqualizerPreset {
+    const char *mName;
+    SLmillibel mLevels[MAX_EQ_BANDS];
+} EqualizerPresets[MAX_EQ_PRESETS] = {
+    {"Default", {0, 0, 0, 0}},
+    {"Bass", {500, 200, 100, 0}},
+    {"Treble", {0, 100, 200, 500}}
 };
 
-#define MAX_PRESETS (sizeof(EqualizerPresets)/sizeof(EqualizerPresets[0]))
-
 static SLresult IEqualizer_SetEnabled(SLEqualizerItf self, SLboolean enabled)
 {
     IEqualizer *this = (IEqualizer *) self;
@@ -61,8 +59,7 @@
     return SL_RESULT_SUCCESS;
 }
 
-static SLresult IEqualizer_GetNumberOfBands(SLEqualizerItf self,
-    SLuint16 *pNumBands)
+static SLresult IEqualizer_GetNumberOfBands(SLEqualizerItf self, SLuint16 *pNumBands)
 {
     if (NULL == pNumBands)
         return SL_RESULT_PARAMETER_INVALID;
@@ -86,8 +83,7 @@
     return SL_RESULT_SUCCESS;
 }
 
-static SLresult IEqualizer_SetBandLevel(SLEqualizerItf self, SLuint16 band,
-    SLmillibel level)
+static SLresult IEqualizer_SetBandLevel(SLEqualizerItf self, SLuint16 band, SLmillibel level)
 {
     IEqualizer *this = (IEqualizer *) self;
     if (band >= this->mNumBands)
@@ -114,8 +110,7 @@
     return SL_RESULT_SUCCESS;
 }
 
-static SLresult IEqualizer_GetCenterFreq(SLEqualizerItf self, SLuint16 band,
-    SLmilliHertz *pCenter)
+static SLresult IEqualizer_GetCenterFreq(SLEqualizerItf self, SLuint16 band, SLmilliHertz *pCenter)
 {
     if (NULL == pCenter)
         return SL_RESULT_PARAMETER_INVALID;
@@ -143,8 +138,7 @@
     return SL_RESULT_SUCCESS;
 }
 
-static SLresult IEqualizer_GetBand(SLEqualizerItf self, SLmilliHertz frequency,
-    SLuint16 *pBand)
+static SLresult IEqualizer_GetBand(SLEqualizerItf self, SLmilliHertz frequency, SLuint16 *pBand)
 {
     if (NULL == pBand)
         return SL_RESULT_PARAMETER_INVALID;
@@ -219,7 +213,7 @@
     IEqualizer *this = (IEqualizer *) self;
     if (index >= this->mNumPresets)
         return SL_RESULT_PARAMETER_INVALID;
-    *ppName = this->mPresetNames[index];
+    *ppName = (SLchar *) this->mPresets[index].mName;
     return SL_RESULT_SUCCESS;
 }
 
@@ -244,15 +238,15 @@
     IEqualizer *this = (IEqualizer *) self;
     this->mItf = &IEqualizer_Itf;
     this->mEnabled = SL_BOOLEAN_FALSE;
-    this->mNumBands = MAX_BANDS;
+    this->mPreset = SL_EQUALIZER_UNDEFINED;
+    unsigned band;
+    for (band = 0; band < MAX_EQ_BANDS; ++band)
+        this->mLevels[band] = 0;
+    // const fields
+    this->mNumPresets = MAX_EQ_PRESETS;
+    this->mNumBands = MAX_EQ_BANDS;
+    this->mBands = EqualizerBands;
+    this->mPresets = EqualizerPresets;
     this->mBandLevelRangeMin = 0;
     this->mBandLevelRangeMax = 1000;
-    this->mBands = EqualizerBands;
-    SLmillibel *levels;
-    levels = (SLmillibel *) malloc(sizeof(SLmillibel) * MAX_BANDS);
-    assert(NULL != levels);
-    unsigned band;
-    for (band = 0; band < this->mNumBands; ++band)
-        this->mLevels[band] = 0;
-    this->mPreset = SL_EQUALIZER_UNDEFINED;
 }
diff --git a/libopensles/ILEDArray.c b/libopensles/ILEDArray.c
index 3d13304..e9136e7 100644
--- a/libopensles/ILEDArray.c
+++ b/libopensles/ILEDArray.c
@@ -18,8 +18,7 @@
 
 #include "sles_allinclusive.h"
 
-static SLresult ILEDArray_ActivateLEDArray(SLLEDArrayItf self,
-    SLuint32 lightMask)
+static SLresult ILEDArray_ActivateLEDArray(SLLEDArrayItf self, SLuint32 lightMask)
 {
     ILEDArray *this = (ILEDArray *) self;
     interface_lock_poke(this);
@@ -28,8 +27,7 @@
     return SL_RESULT_SUCCESS;
 }
 
-static SLresult ILEDArray_IsLEDArrayActivated(SLLEDArrayItf self,
-    SLuint32 *pLightMask)
+static SLresult ILEDArray_IsLEDArrayActivated(SLLEDArrayItf self, SLuint32 *pLightMask)
 {
     if (NULL == pLightMask)
         return SL_RESULT_PARAMETER_INVALID;
@@ -41,8 +39,7 @@
     return SL_RESULT_SUCCESS;
 }
 
-static SLresult ILEDArray_SetColor(SLLEDArrayItf self, SLuint8 index,
-    const SLHSL *pColor)
+static SLresult ILEDArray_SetColor(SLLEDArrayItf self, SLuint8 index, const SLHSL *pColor)
 {
     if (NULL == pColor)
         return SL_RESULT_PARAMETER_INVALID;
@@ -54,21 +51,22 @@
     if (!(0 <= color.lightness && color.lightness <= 1000))
         return SL_RESULT_PARAMETER_INVALID;
     ILEDArray *this = (ILEDArray *) self;
-    interface_lock_poke(this);
-    this->mColor[index] = color;
-    interface_unlock_poke(this);
+    // can't use poke because struct copy might not be atomic
+    interface_lock_exclusive(this);
+    this->mColors[index] = color;
+    interface_unlock_exclusive(this);
     return SL_RESULT_SUCCESS;
 }
 
 static SLresult ILEDArray_GetColor(SLLEDArrayItf self, SLuint8 index,
-    SLHSL *pColor)
-{
+    SLHSL *pColor) {
     if (NULL == pColor)
         return SL_RESULT_PARAMETER_INVALID;
     ILEDArray *this = (ILEDArray *) self;
-    interface_lock_peek(this);
-    SLHSL color = this->mColor[index];
-    interface_unlock_peek(this);
+    // can't use peek because struct copy might not be atomic
+    interface_lock_shared(this);
+    SLHSL color = this->mColors[index];
+    interface_unlock_shared(this);
     *pColor = color;
     return SL_RESULT_SUCCESS;
 }
@@ -84,19 +82,14 @@
 {
     ILEDArray *this = (ILEDArray *) self;
     this->mItf = &ILEDArray_Itf;
-#ifndef NDEBUG
     this->mLightMask = 0;
-    this->mColor = NULL;
-#endif
-    this->mCount = 8;   // FIXME wrong place
-    SLHSL *color;
-    color = (SLHSL *) malloc(sizeof(SLHSL) * this->mCount);
-    assert(NULL != color);
-    this->mColor = color;
+    SLHSL *color = this->mColors;
     SLuint8 index;
-    for (index = 0; index < this->mCount; ++index, ++color) {
-        color->hue = 0; // red
+    for (index = 0; index < MAX_LED_COUNT; ++index, ++color) {
+        color->hue = 0; // red, but per specification 1.0.1 pg. 259: "Default color is undefined."
         color->saturation = 1000;
         color->lightness = 1000;
     }
+    // const
+    this->mCount = MAX_LED_COUNT;
 }
diff --git a/libopensles/IObject.c b/libopensles/IObject.c
index 168e5eb..efda65a 100644
--- a/libopensles/IObject.c
+++ b/libopensles/IObject.c
@@ -23,22 +23,28 @@
     IObject *this = (IObject *) self;
     const ClassTable *class__ = this->mClass;
     AsyncHook realize = class__->mRealize;
-    SLresult result = SL_RESULT_SUCCESS;
+    SLresult result;
+    slObjectCallback callback = NULL;
+    void *context = NULL;
+    SLuint32 state = 0;
     object_lock_exclusive(this);
-    // FIXME The realize hook and callback should be asynchronous if requested
     if (SL_OBJECT_STATE_UNREALIZED != this->mState) {
         result = SL_RESULT_PRECONDITIONS_VIOLATED;
     } else {
-        if (NULL != realize)
-            result = (*realize)(this, async);
+        // FIXME The realize hook and callback should be asynchronous if requested
+        result = NULL != realize ? (*realize)(this, async) : SL_RESULT_SUCCESS;
         if (SL_RESULT_SUCCESS == result)
             this->mState = SL_OBJECT_STATE_REALIZED;
-        // FIXME callback should not run with mutex lock
-        if (async && (NULL != this->mCallback))
-            (*this->mCallback)(self, this->mContext,
-            SL_OBJECT_EVENT_ASYNC_TERMINATION, result, this->mState, NULL);
+        // Make a copy of these, so we can call the callback with mutex unlocked
+        if (async) {
+            callback = this->mCallback;
+            context = this->mContext;
+            state = this->mState;
+        }
     }
     object_unlock_exclusive(this);
+    if (NULL != callback)
+        (*callback)(self, context, SL_OBJECT_EVENT_ASYNC_TERMINATION, result, state, NULL);
     return result;
 }
 
@@ -46,23 +52,29 @@
 {
     IObject *this = (IObject *) self;
     const ClassTable *class__ = this->mClass;
-    AsyncHook resume = class__->mResume;
-    SLresult result = SL_RESULT_SUCCESS;
+    StatusHook resume = class__->mResume;
+    SLresult result;
+    slObjectCallback callback = NULL;
+    void *context = NULL;
+    SLuint32 state = 0;
     object_lock_exclusive(this);
-    // FIXME The resume hook and callback should be asynchronous if requested
     if (SL_OBJECT_STATE_SUSPENDED != this->mState) {
         result = SL_RESULT_PRECONDITIONS_VIOLATED;
     } else {
-        if (NULL != resume)
-            result = (*resume)(this, async);
+        // FIXME The resume hook and callback should be asynchronous if requested
+        result = NULL != resume ? (*resume)(this) : SL_RESULT_SUCCESS;
         if (SL_RESULT_SUCCESS == result)
             this->mState = SL_OBJECT_STATE_REALIZED;
-        // FIXME callback should not run with mutex lock
-        if (async && (NULL != this->mCallback))
-            (*this->mCallback)(self, this->mContext,
-            SL_OBJECT_EVENT_ASYNC_TERMINATION, result, this->mState, NULL);
+        // Make a copy of these, so we can call the callback with mutex unlocked
+        if (async) {
+            callback = this->mCallback;
+            context = this->mContext;
+            state = this->mState;
+        }
     }
     object_unlock_exclusive(this);
+    if (NULL != callback)
+        (*callback)(self, context, SL_OBJECT_EVENT_ASYNC_TERMINATION, result, state, NULL);
     return result;
 }
 
@@ -71,16 +83,15 @@
     if (NULL == pState)
         return SL_RESULT_PARAMETER_INVALID;
     IObject *this = (IObject *) self;
-    // FIXME Investigate what it would take to change to a peek lock
-    object_lock_shared(this);
+    // Note that the state is immediately obsolete, so a peek lock is safe
+    object_lock_peek(this);
     SLuint32 state = this->mState;
-    object_unlock_shared(this);
+    object_unlock_peek(this);
     *pState = state;
     return SL_RESULT_SUCCESS;
 }
 
-static SLresult IObject_GetInterface(SLObjectItf self, const SLInterfaceID iid,
-    void *pInterface)
+static SLresult IObject_GetInterface(SLObjectItf self, const SLInterfaceID iid, void *pInterface)
 {
     if (NULL == pInterface)
         return SL_RESULT_PARAMETER_INVALID;
@@ -120,7 +131,6 @@
     slObjectCallback callback, void *pContext)
 {
     IObject *this = (IObject *) self;
-    // FIXME This could be a poke lock, if we had atomic double-word load/store
     object_lock_exclusive(this);
     this->mCallback = callback;
     this->mContext = pContext;
@@ -135,6 +145,7 @@
 
 static void IObject_Destroy(SLObjectItf self)
 {
+    // FIXME The abort should be atomic w.r.t. destroy, so another async can't be started in window
     IObject_AbortAsyncOperation(self);
     IObject *this = (IObject *) self;
     const ClassTable *class__ = this->mClass;
@@ -161,8 +172,7 @@
     free(this);
 }
 
-static SLresult IObject_SetPriority(SLObjectItf self, SLint32 priority,
-    SLboolean preemptable)
+static SLresult IObject_SetPriority(SLObjectItf self, SLint32 priority, SLboolean preemptable)
 {
     IObject *this = (IObject *) self;
     object_lock_exclusive(this);
@@ -172,8 +182,7 @@
     return SL_RESULT_SUCCESS;
 }
 
-static SLresult IObject_GetPriority(SLObjectItf self, SLint32 *pPriority,
-    SLboolean *pPreemptable)
+static SLresult IObject_GetPriority(SLObjectItf self, SLint32 *pPriority, SLboolean *pPreemptable)
 {
     if (NULL == pPriority || NULL == pPreemptable)
         return SL_RESULT_PARAMETER_INVALID;
diff --git a/libopensles/IOutputMixExt.c b/libopensles/IOutputMixExt.c
index 64dbb46..b5b1e35 100644
--- a/libopensles/IOutputMixExt.c
+++ b/libopensles/IOutputMixExt.c
@@ -50,8 +50,8 @@
         void *dstWriter = pBuffer;
         unsigned desired = size;
         SLboolean trackContributedToMix = SL_BOOLEAN_FALSE;
+        IBufferQueue *bufferQueue = track->mBufferQueue;
         while (desired > 0) {
-            IBufferQueue *bufferQueue;
             const struct BufferHeader *oldFront, *newFront, *rear;
             unsigned actual = desired;
             if (track->mAvail < actual)
@@ -82,8 +82,8 @@
                 track->mReader = (char *) track->mReader + actual;
                 track->mAvail -= actual;
                 if (track->mAvail == 0) {
-                    bufferQueue = track->mBufferQueue;
                     if (NULL != bufferQueue) {
+                        interface_lock_exclusive(bufferQueue);
                         oldFront = bufferQueue->mFront;
                         rear = bufferQueue->mRear;
                         assert(oldFront != rear);
@@ -95,6 +95,7 @@
                         --bufferQueue->mState.count;
                         // FIXME here or in Enqueue?
                         ++bufferQueue->mState.playIndex;
+                        interface_unlock_exclusive(bufferQueue);
                         // FIXME a good time to do an early warning
                         // callback depending on buffer count
                     }
@@ -102,8 +103,8 @@
                 continue;
             }
             // actual == 0
-            bufferQueue = track->mBufferQueue;
             if (NULL != bufferQueue) {
+                interface_lock_shared(bufferQueue);
                 oldFront = bufferQueue->mFront;
                 rear = bufferQueue->mRear;
                 if (oldFront != rear) {
@@ -111,17 +112,22 @@
                     assert(0 < bufferQueue->mState.count);
                     track->mReader = oldFront->mBuffer;
                     track->mAvail = oldFront->mSize;
+                    interface_unlock_shared(bufferQueue);
                     continue;
                 }
                 // FIXME should be able to configure when to
                 // kick off the callback e.g. high/low water-marks etc.
                 // need data but none available, attempt a desperate callback
                 slBufferQueueCallback callback = bufferQueue->mCallback;
+                void *context = bufferQueue->mContext;
+                interface_unlock_shared(bufferQueue);
                 if (NULL != callback) {
-                    (*callback)((SLBufferQueueItf) bufferQueue, bufferQueue->mContext);
+                    (*callback)((SLBufferQueueItf) bufferQueue, context);
                     // if lucky, the callback enqueued a buffer
+                    interface_lock_shared(bufferQueue);
                     if (rear != bufferQueue->mRear)
                         goto got_one;
+                    interface_unlock_shared(bufferQueue);
                     // unlucky, queue still empty, the callback failed
                 }
                 // here on underflow due to no callback, or failed callback
diff --git a/libopensles/IPlay.c b/libopensles/IPlay.c
index 51bcc0f..e7ba632 100644
--- a/libopensles/IPlay.c
+++ b/libopensles/IPlay.c
@@ -101,7 +101,6 @@
     void *pContext)
 {
     IPlay *this = (IPlay *) self;
-    // FIXME This could be a poke lock, if we had atomic double-word load/store
     interface_lock_exclusive(this);
     this->mCallback = callback;
     this->mContext = pContext;
diff --git a/libopensles/classes.c b/libopensles/classes.c
index 1433afe..c671f0e 100644
--- a/libopensles/classes.c
+++ b/libopensles/classes.c
@@ -105,9 +105,6 @@
         offsetof(CAudioPlayer, mVisualization)}
 };
 
-extern SLresult CAudioPlayer_Realize(void *self, SLboolean async);
-extern void CAudioPlayer_Destroy(void *self);
-
 static const ClassTable CAudioPlayer_class = {
     AudioPlayer_interfaces,
     sizeof(AudioPlayer_interfaces)/sizeof(AudioPlayer_interfaces[0]),
diff --git a/libopensles/locks.h b/libopensles/locks.h
index e0eb93e..04a8017 100644
--- a/libopensles/locks.h
+++ b/libopensles/locks.h
@@ -35,6 +35,8 @@
 
 // Peek and poke are an optimization for small atomic fields that don't "matter"
 
+#define object_lock_peek(this)      /* object_lock_shared(this) */
+#define object_unlock_peek(this)    /* object_unlock_shared(this) */
 #define interface_lock_poke(this)   /* interface_lock_exclusive(this) */
 #define interface_unlock_poke(this) /* interface_unlock_exclusive(this) */
 #define interface_lock_peek(this)   /* interface_lock_shared(this) */
diff --git a/libopensles/sles_allinclusive.h b/libopensles/sles_allinclusive.h
index d2df099..e405108 100644
--- a/libopensles/sles_allinclusive.h
+++ b/libopensles/sles_allinclusive.h
@@ -95,7 +95,7 @@
     size_t mSize;
     SLuint32 mObjectID;
     AsyncHook mRealize;
-    AsyncHook mResume;
+    StatusHook mResume;
     VoidHook mDestroy;
     // append per-class data here
 } ClassTable;
@@ -178,7 +178,7 @@
     // but look for lingering code that assumes it is here before deleting
     struct Object_interface *mThis;
     const ClassTable *mClass;
-    volatile SLuint32 mState;
+    SLuint32 mState;
     slObjectCallback mCallback;
     void *mContext;
     unsigned mExposedMask;  // exposed interfaces
@@ -356,7 +356,7 @@
 typedef struct BufferQueue_interface {
     const struct SLBufferQueueItf_ *mItf;
     IObject *mThis;
-    volatile SLBufferQueueState mState;
+    SLBufferQueueState mState;
     slBufferQueueCallback mCallback;
     void *mContext;
     SLuint32 mNumBuffers;
@@ -377,7 +377,8 @@
 typedef struct {
     const struct SLDynamicInterfaceManagementItf_ *mItf;
     IObject *mThis;
-    unsigned mAddedMask;    // added interfaces, a subset of exposed interfaces
+    unsigned mAddedMask;     // added interfaces, a subset of exposed interfaces
+    unsigned mSuspendedMask; // suspended interfaces, a subset of added interfaces
     slDynamicInterfaceManagementCallback mCallback;
     void *mContext;
 } IDynamicInterfaceManagement;
@@ -450,26 +451,30 @@
     SLmilliHertz mMax;
 };
 
+#define MAX_EQ_BANDS 4  // compile-time limit, runtime limit may be smaller
+
 typedef struct {
     const struct SLEqualizerItf_ *mItf;
     IObject *mThis;
     SLboolean mEnabled;
     SLuint16 mPreset;
-    SLmillibel *mLevels;
-    // const
+    SLmillibel mLevels[MAX_EQ_BANDS];
+    // const to end of struct
     SLuint16 mNumPresets;
     SLuint16 mNumBands;
     const struct EqualizerBand *mBands;
-    const SLchar * const *mPresetNames;
+    const struct EqualizerPreset *mPresets;
     SLmillibel mBandLevelRangeMin;
     SLmillibel mBandLevelRangeMax;
 } IEqualizer;
 
+#define MAX_LED_COUNT 32
+
 typedef struct {
     const struct SLLEDArrayItf_ *mItf;
     IObject *mThis;
     SLuint32 mLightMask;
-    SLHSL *mColor;
+    SLHSL mColors[MAX_LED_COUNT];
     // const
     SLuint8 mCount;
 } ILEDArray;
@@ -573,7 +578,7 @@
 typedef struct Play_interface {
     const struct SLPlayItf_ *mItf;
     IObject *mThis;
-    volatile SLuint32 mState;
+    SLuint32 mState;
     SLmillisecond mDuration;
     SLmillisecond mPosition;
     // unsigned mPositionSamples;  // position in sample units
@@ -768,8 +773,6 @@
         android::AudioTrack *mAudioTrack;
         android::MediaPlayer *mMediaPlayer;
     };
-    char* mUri;// FIXME temporary storage before we handle that correctly
-    pthread_t mThread;
 #endif
 } /*CAudioPlayer*/;
 
@@ -918,3 +921,5 @@
 extern SLresult checkDataSource(const SLDataSource *pDataSrc, DataLocatorFormat *myDataSourceLocator);
 extern SLresult checkDataSink(const SLDataSink *pDataSink, DataLocatorFormat *myDataSinkLocator);
 extern void freeDataLocatorFormat(DataLocatorFormat *dlf);
+extern SLresult CAudioPlayer_Realize(void *self, SLboolean async);
+extern void CAudioPlayer_Destroy(void *self);
diff --git a/libopensles/sles_to_android.cpp b/libopensles/sles_to_android.cpp
index 3622def..ede374b 100644
--- a/libopensles/sles_to_android.cpp
+++ b/libopensles/sles_to_android.cpp
@@ -279,10 +279,10 @@
 
     case (android::AudioTrack::EVENT_MORE_DATA) : {
         //fprintf(stdout, "received event EVENT_MORE_DATA from AudioTrack\n");
+        slBufferQueueCallback callback = NULL;
         android::AudioTrack::Buffer* pBuff = (android::AudioTrack::Buffer*)info;
         // retrieve data from the buffer queue
         interface_lock_exclusive(&pAudioPlayer->mBufferQueue);
-        slBufferQueueCallback callback = NULL;
         if (pAudioPlayer->mBufferQueue.mState.count != 0) {
             //fprintf(stderr, "nbBuffers in queue = %lu\n",pAudioPlayer->mBufferQueue.mState.count);
             assert(pAudioPlayer->mBufferQueue.mFront != pAudioPlayer->mBufferQueue.mRear);
@@ -298,6 +298,7 @@
                 pAudioPlayer->mBufferQueue.mSizeConsumed += pBuff->size;
                 // leave pBuff->size untouched
                 // consume data
+                // FIXME can we avoid holding the lock during the copy?
                 memcpy (pBuff->i16, pSrc, pBuff->size);
             } else {
                 // finish consuming the buffer or consume the buffer in one shot
@@ -315,15 +316,14 @@
                 pAudioPlayer->mBufferQueue.mState.playIndex++;
 
                 // consume data
+                // FIXME can we avoid holding the lock during the copy?
                 memcpy (pBuff->i16, pSrc, pBuff->size);
 
                 // data has been consumed, and the buffer queue state has been updated
                 // we can notify the client if applicable
                 callback = pAudioPlayer->mBufferQueue.mCallback;
-                if (NULL != callback) {
-                    // save callback data
-                    callbackPContext = pAudioPlayer->mBufferQueue.mContext;
-                }
+                // save callback data
+                callbackPContext = pAudioPlayer->mBufferQueue.mContext;
             }
         } else {
             // no data available
@@ -369,8 +369,9 @@
         interface_lock_shared(&pAudioPlayer->mPlay);
         callback = pAudioPlayer->mPlay.mCallback;
         callbackPContext = pAudioPlayer->mPlay.mContext;
+        bool headStalled = (pAudioPlayer->mPlay.mEventFlags & SL_PLAYEVENT_HEADSTALLED) != 0;
         interface_unlock_shared(&pAudioPlayer->mPlay);
-        if ((NULL != callback) && (pAudioPlayer->mPlay.mEventFlags & SL_PLAYEVENT_HEADSTALLED)) {
+        if ((NULL != callback) && headStalled) {
             (*callback)(&pAudioPlayer->mPlay.mItf, callbackPContext, SL_PLAYEVENT_HEADSTALLED);
         }
     }
@@ -423,13 +424,8 @@
         break;
     //   -----------------------------------
     //   URI to MediaPlayer
-    case SL_DATALOCATOR_URI: {
+    case SL_DATALOCATOR_URI:
         pAudioPlayer->mAndroidObjType = MEDIAPLAYER;
-        // save URI
-        SLDataLocator_URI *dl_uri =  (SLDataLocator_URI *) pAudioSrc->pLocator;
-        pAudioPlayer->mUri = (char*) malloc(1 + sizeof(SLchar)*strlen((char*)dl_uri->URI));
-        strcpy(pAudioPlayer->mUri, (char*)dl_uri->URI);
-        }
         break;
     default:
         pAudioPlayer->mAndroidObjType = INVALID_TYPE;
@@ -483,7 +479,7 @@
             break;
         }
         pAudioPlayer->mMediaPlayer->setAudioStreamType(ANDROID_DEFAULT_OUTPUT_STREAM_TYPE);
-        if (pAudioPlayer->mMediaPlayer->setDataSource(android::String8(pAudioPlayer->mUri), NULL)
+        if (pAudioPlayer->mMediaPlayer->setDataSource(android::String8((const char *) pAudioPlayer->mDataSource.mLocator.mURI.URI), NULL)
                 != android::NO_ERROR) {
             result = SL_RESULT_CONTENT_UNSUPPORTED;
             break;
@@ -494,7 +490,7 @@
         if (async == SL_BOOLEAN_FALSE) {
             if (pAudioPlayer->mMediaPlayer->prepare() != android::NO_ERROR) {
                 fprintf(stderr, "Failed to prepare() MediaPlayer in synchronous mode for %s\n",
-                        pAudioPlayer->mUri);
+                        pAudioPlayer->mDataSource.mLocator.mURI.URI);
                 result = SL_RESULT_CONTENT_UNSUPPORTED;
             }
         } else {
@@ -537,7 +533,6 @@
             fprintf(stderr, "FIXME destroy MediaPlayer\n");
             //delete pAudioPlayer->mMediaPlayer;
             pAudioPlayer->mMediaPlayer = NULL;
-            free(pAudioPlayer->mUri);
         }
         break;
     default: