Merge "System context should inherit base package name."
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 2fe0b9e..0f04e6f 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -28,6 +28,8 @@
 #include <android_runtime/AndroidRuntime.h>
 
 #include <utils/Log.h>
+#include <utils/SortedVector.h>
+#include <utils/threads.h>
 #include <media/AudioRecord.h>
 #include <media/mediarecorder.h>
 
@@ -55,9 +57,12 @@
 struct audiorecord_callback_cookie {
     jclass      audioRecord_class;
     jobject     audioRecord_ref;
- };
+    bool        busy;
+    Condition   cond;
+};
 
-Mutex sLock;
+static Mutex sLock;
+static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
 
 // ----------------------------------------------------------------------------
 
@@ -87,13 +92,21 @@
 
 // ----------------------------------------------------------------------------
 static void recorderCallback(int event, void* user, void *info) {
+
+    audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
+    {
+        Mutex::Autolock l(sLock);
+        if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) {
+            return;
+        }
+        callbackInfo->busy = true;
+    }
     if (event == AudioRecord::EVENT_MORE_DATA) {
         // set size to 0 to signal we're not using the callback to read more data
         AudioRecord::Buffer* pBuff = (AudioRecord::Buffer*)info;
         pBuff->size = 0;
 
     } else if (event == AudioRecord::EVENT_MARKER) {
-        audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
         JNIEnv *env = AndroidRuntime::getJNIEnv();
         if (user && env) {
             env->CallStaticVoidMethod(
@@ -107,7 +120,6 @@
         }
 
     } else if (event == AudioRecord::EVENT_NEW_POS) {
-        audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
         JNIEnv *env = AndroidRuntime::getJNIEnv();
         if (user && env) {
             env->CallStaticVoidMethod(
@@ -120,8 +132,36 @@
             }
         }
     }
+    {
+        Mutex::Autolock l(sLock);
+        callbackInfo->busy = false;
+        callbackInfo->cond.broadcast();
+    }
 }
 
+// ----------------------------------------------------------------------------
+static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
+{
+    Mutex::Autolock l(sLock);
+    AudioRecord* const ar =
+            (AudioRecord*)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
+    return sp<AudioRecord>(ar);
+}
+
+static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
+{
+    Mutex::Autolock l(sLock);
+    sp<AudioRecord> old =
+            (AudioRecord*)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
+    if (ar.get()) {
+        ar->incStrong(thiz);
+    }
+    if (old != 0) {
+        old->decStrong(thiz);
+    }
+    env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)ar.get());
+    return old;
+}
 
 // ----------------------------------------------------------------------------
 static int
@@ -162,6 +202,12 @@
         return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE;
     }
 
+    jclass clazz = env->GetObjectClass(thiz);
+    if (clazz == NULL) {
+        ALOGE("Can't find %s when setting up callback.", kClassPathName);
+        return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
+    }
+
     if (jSession == NULL) {
         ALOGE("Error creating AudioRecord: invalid session ID pointer");
         return AUDIORECORD_ERROR;
@@ -176,27 +222,20 @@
     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
     nSession = NULL;
 
-    audiorecord_callback_cookie *lpCallbackData = NULL;
-    AudioRecord* lpRecorder = NULL;
-
     // create an uninitialized AudioRecord object
-    lpRecorder = new AudioRecord();
-        if (lpRecorder == NULL) {
+    sp<AudioRecord> lpRecorder = new AudioRecord();
+    if (lpRecorder == NULL) {
         ALOGE("Error creating AudioRecord instance.");
         return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
     }
 
     // create the callback information:
     // this data will be passed with every AudioRecord callback
-    jclass clazz = env->GetObjectClass(thiz);
-    if (clazz == NULL) {
-        ALOGE("Can't find %s when setting up callback.", kClassPathName);
-        goto native_track_failure;
-    }
-    lpCallbackData = new audiorecord_callback_cookie;
+    audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie;
     lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
     // we use a weak reference so the AudioRecord object can be garbage collected.
     lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
+    lpCallbackData->busy = false;
 
     lpRecorder->set((audio_source_t) source,
         sampleRateInHertz,
@@ -225,9 +264,13 @@
     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
     nSession = NULL;
 
+    {   // scope for the lock
+        Mutex::Autolock l(sLock);
+        sAudioRecordCallBackCookies.add(lpCallbackData);
+    }
     // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
     // of the Java object
-    env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)lpRecorder);
+    setAudioRecord(env, thiz, lpRecorder);
 
     // save our newly created callback information in the "nativeCallbackCookie" field
     // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
@@ -240,11 +283,6 @@
     env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
     env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
     delete lpCallbackData;
-
-native_track_failure:
-    delete lpRecorder;
-
-    env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0);
     env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
 
     return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
@@ -256,8 +294,7 @@
 static int
 android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
 {
-    AudioRecord *lpRecorder =
-            (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
+    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
     if (lpRecorder == NULL ) {
         jniThrowException(env, "java/lang/IllegalStateException", NULL);
         return AUDIORECORD_ERROR;
@@ -272,8 +309,7 @@
 static void
 android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
 {
-    AudioRecord *lpRecorder =
-            (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
+    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
     if (lpRecorder == NULL ) {
         jniThrowException(env, "java/lang/IllegalStateException", NULL);
         return;
@@ -285,30 +321,35 @@
 
 
 // ----------------------------------------------------------------------------
-static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
 
-    // serialize access. Ugly, but functional.
-    Mutex::Autolock lock(&sLock);
-    AudioRecord *lpRecorder =
-            (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
+#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
+static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
+    sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
+    if (lpRecorder == NULL) {
+        return;
+    }
+    ALOGV("About to delete lpRecorder: %x\n", (int)lpRecorder.get());
+    lpRecorder->stop();
+
     audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField(
         thiz, javaAudioRecordFields.nativeCallbackCookie);
 
     // reset the native resources in the Java object so any attempt to access
     // them after a call to release fails.
-    env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0);
     env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
 
-    // delete the AudioRecord object
-    if (lpRecorder) {
-        ALOGV("About to delete lpRecorder: %x\n", (int)lpRecorder);
-        lpRecorder->stop();
-        delete lpRecorder;
-    }
-
     // delete the callback information
     if (lpCookie) {
+        Mutex::Autolock l(sLock);
         ALOGV("deleting lpCookie: %x\n", (int)lpCookie);
+        while (lpCookie->busy) {
+            if (lpCookie->cond.waitRelative(sLock,
+                                            milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
+                                                    NO_ERROR) {
+                break;
+            }
+        }
+        sAudioRecordCallBackCookies.remove(lpCookie);
         env->DeleteGlobalRef(lpCookie->audioRecord_class);
         env->DeleteGlobalRef(lpCookie->audioRecord_ref);
         delete lpCookie;
@@ -327,11 +368,8 @@
                                                         jbyteArray javaAudioData,
                                                         jint offsetInBytes, jint sizeInBytes) {
     jbyte* recordBuff = NULL;
-    AudioRecord *lpRecorder = NULL;
-
     // get the audio recorder from which we'll read new audio samples
-    lpRecorder =
-            (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
+    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
     if (lpRecorder == NULL) {
         ALOGE("Unable to retrieve AudioRecord object, can't record");
         return 0;
@@ -378,12 +416,8 @@
 // ----------------------------------------------------------------------------
 static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env,  jobject thiz,
                                                   jobject jBuffer, jint sizeInBytes) {
-    AudioRecord *lpRecorder = NULL;
-    //ALOGV("Entering android_media_AudioRecord_readInBuffer");
-
     // get the audio recorder from which we'll read new audio samples
-    lpRecorder =
-        (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
+    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
     if (lpRecorder==NULL)
         return 0;
 
@@ -411,35 +445,29 @@
 static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env,  jobject thiz,
         jint markerPos) {
 
-    AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
-                thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
-
-    if (lpRecorder) {
-        return
-            android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) );
-    } else {
+    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+    if (lpRecorder == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
         return AUDIORECORD_ERROR;
     }
+    return android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) );
 }
 
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env,  jobject thiz) {
 
-    AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
-                thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
+    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
     uint32_t markerPos = 0;
 
-    if (lpRecorder) {
-        lpRecorder->getMarkerPosition(&markerPos);
-        return (jint)markerPos;
-    } else {
+    if (lpRecorder == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
         return AUDIORECORD_ERROR;
     }
+    lpRecorder->getMarkerPosition(&markerPos);
+    return (jint)markerPos;
 }
 
 
@@ -447,35 +475,30 @@
 static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env,  jobject thiz,
         jint period) {
 
-    AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
-                thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
+    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
 
-    if (lpRecorder) {
-        return
-            android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) );
-    } else {
+    if (lpRecorder == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
         return AUDIORECORD_ERROR;
     }
+    return android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) );
 }
 
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env,  jobject thiz) {
 
-    AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
-                thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
+    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
     uint32_t period = 0;
 
-    if (lpRecorder) {
-        lpRecorder->getPositionUpdatePeriod(&period);
-        return (jint)period;
-    } else {
+    if (lpRecorder == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
         return AUDIORECORD_ERROR;
     }
+    lpRecorder->getPositionUpdatePeriod(&period);
+    return (jint)period;
 }
 
 
@@ -486,7 +509,8 @@
 static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,
     jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
 
-    ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", sampleRateInHertz, nbChannels, audioFormat);
+    ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
+          sampleRateInHertz, nbChannels, audioFormat);
 
     int frameCount = 0;
     status_t result = AudioRecord::getMinFrameCount(&frameCount,
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index a46714e..7e5263d 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -59,7 +59,9 @@
 struct audiotrack_callback_cookie {
     jclass      audioTrack_class;
     jobject     audioTrack_ref;
- };
+    bool        busy;
+    Condition   cond;
+};
 
 // ----------------------------------------------------------------------------
 class AudioTrackJniStorage {
@@ -90,6 +92,9 @@
     }
 };
 
+static Mutex sLock;
+static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies;
+
 // ----------------------------------------------------------------------------
 #define DEFAULT_OUTPUT_SAMPLE_RATE   44100
 
@@ -120,13 +125,22 @@
 
 // ----------------------------------------------------------------------------
 static void audioCallback(int event, void* user, void *info) {
+
+    audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
+    {
+        Mutex::Autolock l(sLock);
+        if (sAudioTrackCallBackCookies.indexOf(callbackInfo) < 0) {
+            return;
+        }
+        callbackInfo->busy = true;
+    }
+
     if (event == AudioTrack::EVENT_MORE_DATA) {
         // set size to 0 to signal we're not using the callback to write more data
         AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)info;
         pBuff->size = 0;
 
     } else if (event == AudioTrack::EVENT_MARKER) {
-        audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
         JNIEnv *env = AndroidRuntime::getJNIEnv();
         if (user && env) {
             env->CallStaticVoidMethod(
@@ -140,7 +154,6 @@
         }
 
     } else if (event == AudioTrack::EVENT_NEW_POS) {
-        audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
         JNIEnv *env = AndroidRuntime::getJNIEnv();
         if (user && env) {
             env->CallStaticVoidMethod(
@@ -153,10 +166,39 @@
             }
         }
     }
+    {
+        Mutex::Autolock l(sLock);
+        callbackInfo->busy = false;
+        callbackInfo->cond.broadcast();
+    }
 }
 
 
 // ----------------------------------------------------------------------------
+static sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz)
+{
+    Mutex::Autolock l(sLock);
+    AudioTrack* const at =
+            (AudioTrack*)env->GetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
+    return sp<AudioTrack>(at);
+}
+
+static sp<AudioTrack> setAudioTrack(JNIEnv* env, jobject thiz, const sp<AudioTrack>& at)
+{
+    Mutex::Autolock l(sLock);
+    sp<AudioTrack> old =
+            (AudioTrack*)env->GetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
+    if (at.get()) {
+        at->incStrong(thiz);
+    }
+    if (old != 0) {
+        old->decStrong(thiz);
+    }
+    env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)at.get());
+    return old;
+}
+
+// ----------------------------------------------------------------------------
 static int
 android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
         jint streamType, jint sampleRateInHertz, jint javaChannelMask,
@@ -231,32 +273,20 @@
             AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT;
     int frameCount = buffSizeInBytes / (nbChannels * bytesPerSample);
 
-    AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
-
-    // initialize the callback information:
-    // this data will be passed with every AudioTrack callback
     jclass clazz = env->GetObjectClass(thiz);
     if (clazz == NULL) {
         ALOGE("Can't find %s when setting up callback.", kClassPathName);
-        delete lpJniStorage;
         return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
     }
-    lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
-    // we use a weak reference so the AudioTrack object can be garbage collected.
-    lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
-
-    lpJniStorage->mStreamType = atStreamType;
 
     if (jSession == NULL) {
         ALOGE("Error creating AudioTrack: invalid session ID pointer");
-        delete lpJniStorage;
         return AUDIOTRACK_ERROR;
     }
 
     jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
     if (nSession == NULL) {
         ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
-        delete lpJniStorage;
         return AUDIOTRACK_ERROR;
     }
     int sessionId = nSession[0];
@@ -264,12 +294,21 @@
     nSession = NULL;
 
     // create the native AudioTrack object
-    AudioTrack* lpTrack = new AudioTrack();
+    sp<AudioTrack> lpTrack = new AudioTrack();
     if (lpTrack == NULL) {
         ALOGE("Error creating uninitialized AudioTrack");
-        goto native_track_failure;
+        return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
     }
 
+    // initialize the callback information:
+    // this data will be passed with every AudioTrack callback
+    AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
+    lpJniStorage->mStreamType = atStreamType;
+    lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
+    // we use a weak reference so the AudioTrack object can be garbage collected.
+    lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
+    lpJniStorage->mCallbackData.busy = false;
+
     // initialize the native AudioTrack object
     if (memoryMode == javaAudioTrackFields.MODE_STREAM) {
 
@@ -323,9 +362,13 @@
     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
     nSession = NULL;
 
+    {   // scope for the lock
+        Mutex::Autolock l(sLock);
+        sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);
+    }
     // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field
     // of the Java object (in mNativeTrackInJavaObj)
-    env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)lpTrack);
+    setAudioTrack(env, thiz, lpTrack);
 
     // save the JNI resources so we can free them later
     //ALOGV("storing lpJniStorage: %x\n", (int)lpJniStorage);
@@ -335,10 +378,6 @@
 
     // failures:
 native_init_failure:
-    delete lpTrack;
-    env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, 0);
-
-native_track_failure:
     if (nSession != NULL) {
         env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
     }
@@ -346,8 +385,8 @@
     env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);
     delete lpJniStorage;
     env->SetIntField(thiz, javaAudioTrackFields.jniData, 0);
-    return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
 
+    return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
 }
 
 
@@ -355,9 +394,8 @@
 static void
 android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
 {
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
-    if (lpTrack == NULL ) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for start()");
         return;
@@ -371,9 +409,8 @@
 static void
 android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
 {
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
-    if (lpTrack == NULL ) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for stop()");
         return;
@@ -387,9 +424,8 @@
 static void
 android_media_AudioTrack_pause(JNIEnv *env, jobject thiz)
 {
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
-    if (lpTrack == NULL ) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for pause()");
         return;
@@ -403,9 +439,8 @@
 static void
 android_media_AudioTrack_flush(JNIEnv *env, jobject thiz)
 {
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
-    if (lpTrack == NULL ) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for flush()");
         return;
@@ -418,9 +453,8 @@
 static void
 android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol )
 {
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
-    if (lpTrack == NULL ) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for setVolume()");
         return;
@@ -430,68 +464,75 @@
 }
 
 // ----------------------------------------------------------------------------
-static void android_media_AudioTrack_native_finalize(JNIEnv *env,  jobject thiz) {
-    //ALOGV("android_media_AudioTrack_native_finalize jobject: %x\n", (int)thiz);
 
-    // delete the AudioTrack object
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
-    if (lpTrack) {
-        //ALOGV("deleting lpTrack: %x\n", (int)lpTrack);
-        lpTrack->stop();
-        delete lpTrack;
+#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
+static void android_media_AudioTrack_native_release(JNIEnv *env,  jobject thiz) {
+    sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0);
+    if (lpTrack == NULL) {
+        return;
     }
+    //ALOGV("deleting lpTrack: %x\n", (int)lpTrack);
+    lpTrack->stop();
 
     // delete the JNI data
     AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetIntField(
         thiz, javaAudioTrackFields.jniData);
+    // reset the native resources in the Java object so any attempt to access
+    // them after a call to release fails.
+    env->SetIntField(thiz, javaAudioTrackFields.jniData, 0);
+
     if (pJniStorage) {
-        // delete global refs created in native_setup
-        env->DeleteGlobalRef(pJniStorage->mCallbackData.audioTrack_class);
-        env->DeleteGlobalRef(pJniStorage->mCallbackData.audioTrack_ref);
+        Mutex::Autolock l(sLock);
+        audiotrack_callback_cookie *lpCookie = &pJniStorage->mCallbackData;
         //ALOGV("deleting pJniStorage: %x\n", (int)pJniStorage);
+        while (lpCookie->busy) {
+            if (lpCookie->cond.waitRelative(sLock,
+                                            milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
+                                                    NO_ERROR) {
+                break;
+            }
+        }
+        sAudioTrackCallBackCookies.remove(lpCookie);
+        // delete global refs created in native_setup
+        env->DeleteGlobalRef(lpCookie->audioTrack_class);
+        env->DeleteGlobalRef(lpCookie->audioTrack_ref);
         delete pJniStorage;
     }
 }
 
-// ----------------------------------------------------------------------------
-static void android_media_AudioTrack_native_release(JNIEnv *env,  jobject thiz) {
 
-    // do everything a call to finalize would
-    android_media_AudioTrack_native_finalize(env, thiz);
-    // + reset the native resources in the Java object so any attempt to access
-    // them after a call to release fails.
-    env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, 0);
-    env->SetIntField(thiz, javaAudioTrackFields.jniData, 0);
+// ----------------------------------------------------------------------------
+static void android_media_AudioTrack_native_finalize(JNIEnv *env,  jobject thiz) {
+    //ALOGV("android_media_AudioTrack_native_finalize jobject: %x\n", (int)thiz);
+    android_media_AudioTrack_native_release(env, thiz);
 }
 
-
 // ----------------------------------------------------------------------------
-jint writeToTrack(AudioTrack* pTrack, jint audioFormat, jbyte* data,
+jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, jbyte* data,
                   jint offsetInBytes, jint sizeInBytes) {
     // give the data to the native AudioTrack object (the data starts at the offset)
     ssize_t written = 0;
     // regular write() or copy the data to the AudioTrack's shared memory?
-    if (pTrack->sharedBuffer() == 0) {
-        written = pTrack->write(data + offsetInBytes, sizeInBytes);
+    if (track->sharedBuffer() == 0) {
+        written = track->write(data + offsetInBytes, sizeInBytes);
     } else {
         if (audioFormat == javaAudioTrackFields.PCM16) {
             // writing to shared memory, check for capacity
-            if ((size_t)sizeInBytes > pTrack->sharedBuffer()->size()) {
-                sizeInBytes = pTrack->sharedBuffer()->size();
+            if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
+                sizeInBytes = track->sharedBuffer()->size();
             }
-            memcpy(pTrack->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes);
+            memcpy(track->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes);
             written = sizeInBytes;
         } else if (audioFormat == javaAudioTrackFields.PCM8) {
             // data contains 8bit data we need to expand to 16bit before copying
             // to the shared memory
             // writing to shared memory, check for capacity,
             // note that input data will occupy 2X the input space due to 8 to 16bit conversion
-            if (((size_t)sizeInBytes)*2 > pTrack->sharedBuffer()->size()) {
-                sizeInBytes = pTrack->sharedBuffer()->size() / 2;
+            if (((size_t)sizeInBytes)*2 > track->sharedBuffer()->size()) {
+                sizeInBytes = track->sharedBuffer()->size() / 2;
             }
             int count = sizeInBytes;
-            int16_t *dst = (int16_t *)pTrack->sharedBuffer()->pointer();
+            int16_t *dst = (int16_t *)track->sharedBuffer()->pointer();
             const int8_t *src = (const int8_t *)(data + offsetInBytes);
             while (count--) {
                 *dst++ = (int16_t)(*src++^0x80) << 8;
@@ -510,13 +551,9 @@
                                                   jbyteArray javaAudioData,
                                                   jint offsetInBytes, jint sizeInBytes,
                                                   jint javaAudioFormat) {
-    jbyte* cAudioData = NULL;
-    AudioTrack *lpTrack = NULL;
     //ALOGV("android_media_AudioTrack_native_write_byte(offset=%d, sizeInBytes=%d) called",
     //    offsetInBytes, sizeInBytes);
-
-    // get the audio track to load with samples
-    lpTrack = (AudioTrack *)env->GetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
     if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for write()");
@@ -528,6 +565,7 @@
     // a way that it becomes much more efficient. When doing so, we will have to prevent the
     // AudioSystem callback to be called while in critical section (in case of media server
     // process crash for instance)
+    jbyte* cAudioData = NULL;
     if (javaAudioData) {
         cAudioData = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
         if (cAudioData == NULL) {
@@ -564,183 +602,148 @@
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env,  jobject thiz) {
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
-
-    if (lpTrack) {
-        return lpTrack->frameCount();
-    } else {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for frameCount()");
         return AUDIOTRACK_ERROR;
     }
+
+    return lpTrack->frameCount();
 }
 
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env,  jobject thiz,
         jint sampleRateInHz) {
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
-
-    if (lpTrack) {
-        return android_media_translateErrorCode(lpTrack->setSampleRate(sampleRateInHz));
-    } else {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for setSampleRate()");
         return AUDIOTRACK_ERROR;
     }
+    return android_media_translateErrorCode(lpTrack->setSampleRate(sampleRateInHz));
 }
 
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env,  jobject thiz) {
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
-
-    if (lpTrack) {
-        return (jint) lpTrack->getSampleRate();
-    } else {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for getSampleRate()");
         return AUDIOTRACK_ERROR;
     }
+    return (jint) lpTrack->getSampleRate();
 }
 
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env,  jobject thiz,
         jint markerPos) {
-
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
-
-    if (lpTrack) {
-        return android_media_translateErrorCode( lpTrack->setMarkerPosition(markerPos) );
-    } else {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for setMarkerPosition()");
         return AUDIOTRACK_ERROR;
     }
+    return android_media_translateErrorCode( lpTrack->setMarkerPosition(markerPos) );
 }
 
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env,  jobject thiz) {
-
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
     uint32_t markerPos = 0;
 
-    if (lpTrack) {
-        lpTrack->getMarkerPosition(&markerPos);
-        return (jint)markerPos;
-    } else {
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for getMarkerPosition()");
         return AUDIOTRACK_ERROR;
     }
+    lpTrack->getMarkerPosition(&markerPos);
+    return (jint)markerPos;
 }
 
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env,  jobject thiz,
         jint period) {
-
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
-
-    if (lpTrack) {
-        return android_media_translateErrorCode( lpTrack->setPositionUpdatePeriod(period) );
-    } else {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()");
         return AUDIOTRACK_ERROR;
     }
+    return android_media_translateErrorCode( lpTrack->setPositionUpdatePeriod(period) );
 }
 
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env,  jobject thiz) {
-
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
     uint32_t period = 0;
 
-    if (lpTrack) {
-        lpTrack->getPositionUpdatePeriod(&period);
-        return (jint)period;
-    } else {
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()");
         return AUDIOTRACK_ERROR;
     }
+    lpTrack->getPositionUpdatePeriod(&period);
+    return (jint)period;
 }
 
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_set_position(JNIEnv *env,  jobject thiz,
         jint position) {
-
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
-
-    if (lpTrack) {
-        return android_media_translateErrorCode( lpTrack->setPosition(position) );
-    } else {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for setPosition()");
         return AUDIOTRACK_ERROR;
     }
+    return android_media_translateErrorCode( lpTrack->setPosition(position) );
 }
 
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_get_position(JNIEnv *env,  jobject thiz) {
-
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
     uint32_t position = 0;
 
-    if (lpTrack) {
-        lpTrack->getPosition(&position);
-        return (jint)position;
-    }  else {
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for getPosition()");
         return AUDIOTRACK_ERROR;
     }
+    lpTrack->getPosition(&position);
+    return (jint)position;
 }
 
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_set_loop(JNIEnv *env,  jobject thiz,
         jint loopStart, jint loopEnd, jint loopCount) {
-
-     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
-     if (lpTrack) {
-        return android_media_translateErrorCode( lpTrack->setLoop(loopStart, loopEnd, loopCount) );
-     }  else {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for setLoop()");
         return AUDIOTRACK_ERROR;
     }
+    return android_media_translateErrorCode( lpTrack->setLoop(loopStart, loopEnd, loopCount) );
 }
 
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_reload(JNIEnv *env,  jobject thiz) {
-
-     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
-     if (lpTrack) {
-        return android_media_translateErrorCode( lpTrack->reload() );
-     } else {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for reload()");
         return AUDIOTRACK_ERROR;
     }
+    return android_media_translateErrorCode( lpTrack->reload() );
 }
 
 
@@ -795,8 +798,7 @@
 static void
 android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level )
 {
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
     if (lpTrack == NULL ) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for setAuxEffectSendLevel()");
@@ -809,17 +811,13 @@
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_attachAuxEffect(JNIEnv *env,  jobject thiz,
         jint effectId) {
-
-    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
-                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
-
-    if (lpTrack) {
-        return android_media_translateErrorCode( lpTrack->attachAuxEffect(effectId) );
-    } else {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for attachAuxEffect()");
         return AUDIOTRACK_ERROR;
     }
+    return android_media_translateErrorCode( lpTrack->attachAuxEffect(effectId) );
 }
 
 // ----------------------------------------------------------------------------