Merge "Refactor for AudioTrack/Record callback interface"
diff --git a/src/android/AudioPlayer_to_android.cpp b/src/android/AudioPlayer_to_android.cpp
index a324b4a..ef466b6 100644
--- a/src/android/AudioPlayer_to_android.cpp
+++ b/src/android/AudioPlayer_to_android.cpp
@@ -19,6 +19,7 @@
 #include "android/android_AudioToCbRenderer.h"
 #include "android/android_StreamPlayer.h"
 #include "android/android_LocAVPlayer.h"
+#include "android/AudioTrackCallback.h"
 #include "android/include/AacBqToPcmCbRenderer.h"
 #include "android/channels.h"
 
@@ -1233,23 +1234,15 @@
 //-----------------------------------------------------------------------------
 // Callback associated with an AudioTrack of an SL ES AudioPlayer that gets its data
 // from a buffer queue. This will not be called once the AudioTrack has been destroyed.
-static void audioTrack_callBack_pullFromBuffQueue(int event, void* user, void *info) {
-    CAudioPlayer *ap = (CAudioPlayer *)user;
-
-    if (!android::CallbackProtector::enterCbIfOk(ap->mCallbackProtector)) {
-        // it is not safe to enter the callback (the track is about to go away)
-        return;
-    }
+size_t audioTrack_handleMoreData_lockPlay(CAudioPlayer* ap,
+                                        const android::AudioTrack::Buffer& buffer) {
 
     void * callbackPContext = NULL;
-    switch (event) {
-
-    case android::AudioTrack::EVENT_MORE_DATA: {
+    size_t bytesWritten = 0;
         //SL_LOGV("received event EVENT_MORE_DATA from AudioTrack TID=%d", gettid());
         slPrefetchCallback prefetchCallback = NULL;
         void *prefetchContext = NULL;
         SLuint32 prefetchEvents = SL_PREFETCHEVENT_NONE;
-        android::AudioTrack::Buffer* pBuff = (android::AudioTrack::Buffer*)info;
 
         // retrieve data from the buffer queue
         interface_lock_exclusive(&ap->mBufferQueue);
@@ -1274,17 +1267,15 @@
             BufferHeader *newFront = &oldFront[1];
 
             size_t availSource = oldFront->mSize - ap->mBufferQueue.mSizeConsumed;
-            size_t availSink = pBuff->size;
+            size_t availSink = buffer.size();
             size_t bytesToCopy = availSource < availSink ? availSource : availSink;
             void *pSrc = (char *)oldFront->mBuffer + ap->mBufferQueue.mSizeConsumed;
-            memcpy(pBuff->raw, pSrc, bytesToCopy);
-
+            memcpy(buffer.data(), pSrc, bytesToCopy);
+            bytesWritten = bytesToCopy;
             if (bytesToCopy < availSource) {
                 ap->mBufferQueue.mSizeConsumed += bytesToCopy;
-                // pBuff->size is already equal to bytesToCopy in this case
             } else {
                 // consumed an entire buffer, dequeue
-                pBuff->size = bytesToCopy;
                 ap->mBufferQueue.mSizeConsumed = 0;
                 if (newFront ==
                         &ap->mBufferQueue.mArray
@@ -1299,8 +1290,6 @@
                 ap->mBufferQueue.mCallbackPending = true;
             }
         } else { // empty queue
-            // signal no data available
-            pBuff->size = 0;
 
             // signal we're at the end of the content, but don't pause (see note in function)
             audioPlayer_dispatch_headAtEnd_lockPlay(ap, false /*set state to paused?*/, false);
@@ -1336,41 +1325,8 @@
                         SL_PREFETCHEVENT_FILLLEVELCHANGE);
             }
         }
-    }
-    break;
 
-    case android::AudioTrack::EVENT_MARKER:
-        //SL_LOGI("received event EVENT_MARKER from AudioTrack");
-        audioTrack_handleMarker_lockPlay(ap);
-        break;
-
-    case android::AudioTrack::EVENT_NEW_POS:
-        //SL_LOGI("received event EVENT_NEW_POS from AudioTrack");
-        audioTrack_handleNewPos_lockPlay(ap);
-        break;
-
-    case android::AudioTrack::EVENT_UNDERRUN:
-        //SL_LOGI("received event EVENT_UNDERRUN from AudioTrack");
-        audioTrack_handleUnderrun_lockPlay(ap);
-        break;
-
-    case android::AudioTrack::EVENT_NEW_IAUDIOTRACK:
-        // ignore for now
-        break;
-
-    case android::AudioTrack::EVENT_BUFFER_END:
-    case android::AudioTrack::EVENT_LOOP_END:
-    case android::AudioTrack::EVENT_STREAM_END:
-        // These are unexpected so fall through
-        FALLTHROUGH_INTENDED;
-    default:
-        // FIXME where does the notification of SL_PLAYEVENT_HEADMOVING fit?
-        SL_LOGE("Encountered unknown AudioTrack event %d for CAudioPlayer %p", event,
-                (CAudioPlayer *)user);
-        break;
-    }
-
-    ap->mCallbackProtector->exitCb();
+    return bytesWritten;
 }
 
 
@@ -1684,16 +1640,15 @@
         } else {
             notificationFrames = 0;
         }
-
-        android::AudioTrack* pat = new android::AudioTrack(
+        const auto callbackHandle = android::sp<android::AudioTrackCallback>::make(pAudioPlayer);
+        const auto pat = android::sp<android::AudioTrack>::make(
                 pAudioPlayer->mStreamType,                           // streamType
                 sampleRate,                                          // sampleRate
                 sles_to_android_sampleFormat(df_pcm),                // format
                 channelMask,                                         // channel mask
                 0,                                                   // frameCount
                 policy,                                              // flags
-                audioTrack_callBack_pullFromBuffQueue,               // callback
-                (void *) pAudioPlayer,                               // user
+                callbackHandle,                                      // callback
                 notificationFrames,                                  // see comment above
                 pAudioPlayer->mSessionId);
 
@@ -1702,17 +1657,17 @@
 
         android::status_t status = pat->initCheck();
         if (status != android::NO_ERROR) {
-            // AudioTracks are meant to be refcounted, so their dtor is protected.
-            static_cast<void>(android::sp<android::AudioTrack>(pat));
-
             SL_LOGE("AudioTrack::initCheck status %u", status);
             // FIXME should return a more specific result depending on status
             result = SL_RESULT_CONTENT_UNSUPPORTED;
             return result;
         }
 
-        pAudioPlayer->mTrackPlayer->init(pat, android::PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE,
-                usageForStreamType(pAudioPlayer->mStreamType), pAudioPlayer->mSessionId);
+        pAudioPlayer->mTrackPlayer->init(
+            pat, callbackHandle,
+            android::PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE,
+            usageForStreamType(pAudioPlayer->mStreamType),
+            pAudioPlayer->mSessionId);
 
         // update performance mode according to actual flags granted to AudioTrack
         checkAndSetPerformanceModePost(pAudioPlayer);
diff --git a/src/android/AudioRecordCallback.h b/src/android/AudioRecordCallback.h
new file mode 100644
index 0000000..53b7e35
--- /dev/null
+++ b/src/android/AudioRecordCallback.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#ifndef SL_PREFETCHEVENT_NONE // This is defined in slesl_allinclusive, which isn't guarded
+#include "sles_allinclusive.h"
+#endif
+
+#include "media/AudioRecord.h"
+
+void audioRecorder_handleOverrun_lockRecord(CAudioRecorder* ar);
+void audioRecorder_handleNewPos_lockRecord(CAudioRecorder* ar);
+void audioRecorder_handleMarker_lockRecord(CAudioRecorder* ar);
+size_t audioRecorder_handleMoreData_lockRecord(CAudioRecorder* ar,
+                                               const android::AudioRecord::Buffer&);
+//--------------------------------------------------------------------------------------------------
+namespace android {
+
+class AudioRecordCallback : public android::AudioRecord::IAudioRecordCallback {
+  public:
+    AudioRecordCallback(CAudioRecorder * audioRecorder) : mAr(audioRecorder) {}
+    AudioRecordCallback(const AudioRecordCallback&) = delete;
+    AudioRecordCallback& operator=(const AudioRecordCallback&) = delete;
+
+  private:
+    size_t onMoreData(const android::AudioRecord::Buffer& buffer) override {
+        if (!android::CallbackProtector::enterCbIfOk(mAr->mCallbackProtector)) {
+            // it is not safe to enter the callback (the track is about to go away)
+            return buffer.size(); // replicate existing behavior
+        }
+        size_t bytesRead = audioRecorder_handleMoreData_lockRecord(mAr, buffer);
+        mAr->mCallbackProtector->exitCb();
+        return bytesRead;
+    }
+
+
+    void onOverrun() override {
+        if (!android::CallbackProtector::enterCbIfOk(mAr->mCallbackProtector)) {
+            // it is not safe to enter the callback (the track is about to go away)
+            return;
+        }
+        audioRecorder_handleOverrun_lockRecord(mAr);
+        mAr->mCallbackProtector->exitCb();
+    }
+    void onMarker(uint32_t) override {
+        if (!android::CallbackProtector::enterCbIfOk(mAr->mCallbackProtector)) {
+            // it is not safe to enter the callback (the track is about to go away)
+            return;
+        }
+
+        audioRecorder_handleMarker_lockRecord(mAr);
+        mAr->mCallbackProtector->exitCb();
+    }
+    void onNewPos(uint32_t) override {
+        if (!android::CallbackProtector::enterCbIfOk(mAr->mCallbackProtector)) {
+            // it is not safe to enter the callback (the track is about to go away)
+            return;
+        }
+
+        audioRecorder_handleNewPos_lockRecord(mAr);
+        mAr->mCallbackProtector->exitCb();
+    }
+    CAudioRecorder * const mAr;
+};
+
+} // namespace android
diff --git a/src/android/AudioRecorder_to_android.cpp b/src/android/AudioRecorder_to_android.cpp
index 411beff..d4b2964 100644
--- a/src/android/AudioRecorder_to_android.cpp
+++ b/src/android/AudioRecorder_to_android.cpp
@@ -24,6 +24,7 @@
 #include <SLES/OpenSLES_Android.h>
 
 #include <android_runtime/AndroidRuntime.h>
+#include <android/AudioRecordCallback.h>
 
 #define KEY_RECORDING_SOURCE_PARAMSIZE  sizeof(SLuint32)
 #define KEY_RECORDING_PRESET_PARAMSIZE  sizeof(SLuint32)
@@ -307,96 +308,58 @@
     return SL_RESULT_SUCCESS;
 }
 //-----------------------------------------------------------------------------
-static void audioRecorder_callback(int event, void* user, void *info) {
+size_t audioRecorder_handleMoreData_lockRecord(CAudioRecorder* ar,
+                                               const android::AudioRecord::Buffer& buffer) {
     //SL_LOGV("audioRecorder_callback(%d, %p, %p) entering", event, user, info);
 
-    CAudioRecorder *ar = (CAudioRecorder *)user;
-
-    if (!android::CallbackProtector::enterCbIfOk(ar->mCallbackProtector)) {
-        // it is not safe to enter the callback (the track is about to go away)
-        return;
-    }
-
     void * callbackPContext = NULL;
+    size_t bytesRead = 0;
+    slBufferQueueCallback callback = NULL;
 
-    switch (event) {
-    case android::AudioRecord::EVENT_MORE_DATA: {
-        slBufferQueueCallback callback = NULL;
-        android::AudioRecord::Buffer* pBuff = (android::AudioRecord::Buffer*)info;
+    // push data to the buffer queue
+    interface_lock_exclusive(&ar->mBufferQueue);
 
-        // push data to the buffer queue
-        interface_lock_exclusive(&ar->mBufferQueue);
+    if (ar->mBufferQueue.mState.count != 0) {
+        assert(ar->mBufferQueue.mFront != ar->mBufferQueue.mRear);
 
-        if (ar->mBufferQueue.mState.count != 0) {
-            assert(ar->mBufferQueue.mFront != ar->mBufferQueue.mRear);
+        BufferHeader *oldFront = ar->mBufferQueue.mFront;
+        BufferHeader *newFront = &oldFront[1];
 
-            BufferHeader *oldFront = ar->mBufferQueue.mFront;
-            BufferHeader *newFront = &oldFront[1];
-
-            size_t availSink = oldFront->mSize - ar->mBufferQueue.mSizeConsumed;
-            size_t availSource = pBuff->size;
-            size_t bytesToCopy = availSink < availSource ? availSink : availSource;
-            void *pDest = (char *)oldFront->mBuffer + ar->mBufferQueue.mSizeConsumed;
-            memcpy(pDest, pBuff->raw, bytesToCopy);
-
-            if (bytesToCopy < availSink) {
-                // can't consume the whole or rest of the buffer in one shot
-                ar->mBufferQueue.mSizeConsumed += availSource;
-                // pBuff->size is already equal to bytesToCopy in this case
-            } else {
-                // finish pushing the buffer or push the buffer in one shot
-                pBuff->size = bytesToCopy;
-                ar->mBufferQueue.mSizeConsumed = 0;
-                if (newFront == &ar->mBufferQueue.mArray[ar->mBufferQueue.mNumBuffers + 1]) {
-                    newFront = ar->mBufferQueue.mArray;
-                }
-                ar->mBufferQueue.mFront = newFront;
-
-                ar->mBufferQueue.mState.count--;
-                ar->mBufferQueue.mState.playIndex++;
-
-                // data has been copied to the buffer, and the buffer queue state has been updated
-                // we will notify the client if applicable
-                callback = ar->mBufferQueue.mCallback;
-                // save callback data
-                callbackPContext = ar->mBufferQueue.mContext;
+        size_t availSink = oldFront->mSize - ar->mBufferQueue.mSizeConsumed;
+        size_t availSource = buffer.size();
+        size_t bytesToCopy = availSink < availSource ? availSink : availSource;
+        void *pDest = (char *)oldFront->mBuffer + ar->mBufferQueue.mSizeConsumed;
+        memcpy(pDest, buffer.data(), bytesToCopy);
+        bytesRead = bytesToCopy;
+        if (bytesToCopy < availSink) {
+            // can't consume the whole or rest of the buffer in one shot
+            ar->mBufferQueue.mSizeConsumed += availSource;
+        } else {
+            // finish pushing the buffer or push the buffer in one shot
+            ar->mBufferQueue.mSizeConsumed = 0;
+            if (newFront == &ar->mBufferQueue.mArray[ar->mBufferQueue.mNumBuffers + 1]) {
+                newFront = ar->mBufferQueue.mArray;
             }
-        } else { // empty queue
-            // no destination to push the data
-            pBuff->size = 0;
+            ar->mBufferQueue.mFront = newFront;
+
+            ar->mBufferQueue.mState.count--;
+            ar->mBufferQueue.mState.playIndex++;
+
+            // data has been copied to the buffer, and the buffer queue state has been updated
+            // we will notify the client if applicable
+            callback = ar->mBufferQueue.mCallback;
+            // save callback data
+            callbackPContext = ar->mBufferQueue.mContext;
         }
-
-        interface_unlock_exclusive(&ar->mBufferQueue);
-
-        // notify client
-        if (NULL != callback) {
-            (*callback)(&ar->mBufferQueue.mItf, callbackPContext);
-        }
-        }
-        break;
-
-    case android::AudioRecord::EVENT_OVERRUN:
-        audioRecorder_handleOverrun_lockRecord(ar);
-        break;
-
-    case android::AudioRecord::EVENT_MARKER:
-        audioRecorder_handleMarker_lockRecord(ar);
-        break;
-
-    case android::AudioRecord::EVENT_NEW_POS:
-        audioRecorder_handleNewPos_lockRecord(ar);
-        break;
-
-    case android::AudioRecord::EVENT_NEW_IAUDIORECORD:
-        // ignore for now
-        break;
-
-    default:
-        SL_LOGE("Encountered unknown AudioRecord event %d for CAudioRecord %p", event, ar);
-        break;
     }
 
-    ar->mCallbackProtector->exitCb();
+    interface_unlock_exclusive(&ar->mBufferQueue);
+
+    // notify client
+    if (NULL != callback) {
+        (*callback)(&ar->mBufferQueue.mItf, callbackPContext);
+    }
+    return bytesRead;
 }
 
 
@@ -420,6 +383,7 @@
         // microphone to simple buffer queue
         ar->mAndroidObjType = AUDIORECORDER_FROM_MIC_TO_PCM_BUFFERQUEUE;
         ar->mAudioRecord.clear();
+        ar->mCallbackHandle.clear();
         ar->mCallbackProtector = new android::CallbackProtector();
         ar->mRecordSource = AUDIO_SOURCE_DEFAULT;
         ar->mPerformanceMode = ANDROID_PERFORMANCE_MODE_DEFAULT;
@@ -694,7 +658,7 @@
     attributionSource.uid = VALUE_OR_FATAL(android::legacy2aidl_uid_t_int32_t(getuid()));
     attributionSource.pid = VALUE_OR_FATAL(android::legacy2aidl_pid_t_int32_t(getpid()));
     attributionSource.token = android::sp<android::BBinder>::make();
-
+    ar->mCallbackHandle = android::sp<android::AudioRecordCallback>::make(ar);
     // initialize platform-specific CAudioRecorder fields
     ar->mAudioRecord = new android::AudioRecord(
             ar->mRecordSource,     // source
@@ -703,8 +667,7 @@
             channelMask,           // channel mask
             attributionSource,
             0,                     // frameCount
-            audioRecorder_callback,// callback_t
-            (void*)ar,             // user, callback data, here the AudioRecorder
+            ar->mCallbackHandle,
             0,                     // notificationFrames
             AUDIO_SESSION_ALLOCATE,
             android::AudioRecord::TRANSFER_CALLBACK,
@@ -721,6 +684,7 @@
         // FIXME should return a more specific result depending on status
         result = SL_RESULT_CONTENT_UNSUPPORTED;
         ar->mAudioRecord.clear();
+        ar->mCallbackHandle.clear();
         return result;
     }
 
@@ -744,6 +708,7 @@
             SL_LOGE("Java exception releasing recorder routing object.");
             result = SL_RESULT_INTERNAL_ERROR;
             ar->mAudioRecord.clear();
+            ar->mCallbackHandle.clear();
             return result;
         }
    }
@@ -810,6 +775,7 @@
         ar->mAudioRecord.clear();
     }
     // explicit destructor
+    ar->mCallbackHandle.~sp();
     ar->mAudioRecord.~sp();
     ar->mCallbackProtector.~sp();
 }
diff --git a/src/android/AudioTrackCallback.h b/src/android/AudioTrackCallback.h
new file mode 100644
index 0000000..7020606
--- /dev/null
+++ b/src/android/AudioTrackCallback.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#ifndef SL_PREFETCHEVENT_NONE // This is defined in slesl_allinclusive, which isn't guarded
+#include "sles_allinclusive.h"
+#endif
+
+#include "media/AudioTrack.h"
+
+void audioPlayer_dispatch_headAtEnd_lockPlay(CAudioPlayer*, bool, bool);
+
+void audioTrack_handleUnderrun_lockPlay(CAudioPlayer* ap);
+void audioTrack_handleMarker_lockPlay(CAudioPlayer* ap);
+void audioTrack_handleNewPos_lockPlay(CAudioPlayer* ap);
+size_t audioTrack_handleMoreData_lockPlay(CAudioPlayer* ap,
+                                        const android::AudioTrack::Buffer& buffer);
+//--------------------------------------------------------------------------------------------------
+namespace android {
+class AudioTrackCallback : public AudioTrack::IAudioTrackCallback {
+  public:
+    AudioTrackCallback(CAudioPlayer * player) : mAp(player) {}
+
+    size_t onMoreData(const AudioTrack::Buffer& buffer) override {
+        if (!android::CallbackProtector::enterCbIfOk(mAp->mCallbackProtector)) {
+          // it is not safe to enter the callback (the track is about to go away)
+          return buffer.size(); // duplicate existing behavior
+        }
+        size_t bytesCopied = audioTrack_handleMoreData_lockPlay(mAp, buffer);
+        mAp->mCallbackProtector->exitCb();
+        return bytesCopied;
+      }
+
+    void onUnderrun() override {
+        if (!android::CallbackProtector::enterCbIfOk(mAp->mCallbackProtector)) {
+          // it is not safe to enter the callback (the track is about to go away)
+            return;
+        }
+        audioTrack_handleUnderrun_lockPlay(mAp);
+        mAp->mCallbackProtector->exitCb();
+    }
+
+    void onLoopEnd([[maybe_unused]] int32_t loopsRemaining) override {
+        SL_LOGE("Encountered loop end for CAudioPlayer %p", mAp);
+    }
+    void onMarker([[maybe_unused]] uint32_t markerPosition) override {
+        if (!android::CallbackProtector::enterCbIfOk(mAp->mCallbackProtector)) {
+          // it is not safe to enter the callback (the track is about to go away)
+          return;
+        }
+        audioTrack_handleMarker_lockPlay(mAp);
+        mAp->mCallbackProtector->exitCb();
+    }
+
+    void onNewPos([[maybe_unused]] uint32_t newPos) override {
+        if (!android::CallbackProtector::enterCbIfOk(mAp->mCallbackProtector)) {
+          // it is not safe to enter the callback (the track is about to go away)
+          return;
+        }
+        audioTrack_handleNewPos_lockPlay(mAp);
+        mAp->mCallbackProtector->exitCb();
+    }
+    void onBufferEnd() override {
+        SL_LOGE("Encountered buffer end for CAudioPlayer %p", mAp);
+    }
+    // Ignore
+    void onNewIAudioTrack() override {}
+    void onStreamEnd() override {
+        SL_LOGE("Encountered buffer end for CAudioPlayer %p", mAp);
+    }
+    void onNewTimestamp([[maybe_unused]] AudioTimestamp timestamp) {
+        SL_LOGE("Encountered write more data for CAudioPlayer %p", mAp);
+    }
+    size_t onCanWriteMoreData([[maybe_unused]] const AudioTrack::Buffer& buffer) {
+        SL_LOGE("Encountered write more data for CAudioPlayer %p", mAp);
+        return 0;
+    }
+
+  private:
+    AudioTrackCallback(const AudioTrackCallback&) = delete;
+    AudioTrackCallback& operator=(const AudioTrackCallback&) = delete;
+    CAudioPlayer* const mAp;
+};
+}  // namespace android
diff --git a/src/classes.h b/src/classes.h
index fc64f25..c42fde4 100644
--- a/src/classes.h
+++ b/src/classes.h
@@ -21,6 +21,7 @@
 #include "android/android_GenericPlayer.h"
 #include <media/TrackPlayerBase.h>
 #include <audiomanager/IAudioManager.h>
+namespace android { class AudioRecordCallback; };
 #endif
 
 // Class structures
@@ -161,6 +162,7 @@
     enum AndroidObjectType mAndroidObjType;
     android::sp<android::AudioRecord> mAudioRecord;
     android::sp<android::CallbackProtector> mCallbackProtector;
+    android::sp<android::AudioRecordCallback> mCallbackHandle;
     audio_source_t mRecordSource;
     SLuint32 mPerformanceMode;
 #endif
diff --git a/src/itf/IEngine.cpp b/src/itf/IEngine.cpp
index da5623c..be2d2b0 100644
--- a/src/itf/IEngine.cpp
+++ b/src/itf/IEngine.cpp
@@ -459,6 +459,7 @@
                     // FIXME unnecessary once those fields are encapsulated in one class, rather
                     //   than a structure
                     (void) new (&thiz->mAudioRecord) android::sp<android::AudioRecord>();
+                    (void) new (&thiz->mCallbackHandle) android::sp<android::AudioRecordCallback>();
                     (void) new (&thiz->mCallbackProtector)
                             android::sp<android::CallbackProtector>();
                     thiz->mRecordSource = AUDIO_SOURCE_DEFAULT;