Loading core/jni/android_media_AudioTrack.cpp +158 −169 Original line number Diff line number Diff line Loading @@ -63,12 +63,94 @@ static audio_track_fields_t javaAudioTrackFields; static PlaybackParams::fields_t gPlaybackParamsFields; static VolumeShaperHelper::fields_t gVolumeShaperFields; struct audiotrack_callback_cookie { jclass audioTrack_class; jobject audioTrack_ref; bool busy; Condition cond; bool isOffload; class AudioTrackCallbackImpl : public AudioTrack::IAudioTrackCallback { public: enum event_type { // Keep in sync with java EVENT_MORE_DATA = 0, EVENT_UNDERRUN = 1, EVENT_LOOP_END = 2, EVENT_MARKER = 3, EVENT_NEW_POS = 4, EVENT_BUFFER_END = 5, EVENT_NEW_IAUDIOTRACK = 6, EVENT_STREAM_END = 7, // 8 is reserved for future use EVENT_CAN_WRITE_MORE_DATA = 9 }; AudioTrackCallbackImpl(jclass audioTrackClass, jobject audioTrackWeakRef, bool isOffload) : mIsOffload(isOffload) { const auto env = getJNIEnv(); mAudioTrackClass = (jclass)env->NewGlobalRef(audioTrackClass); // we use a weak reference so the AudioTrack object can be garbage collected. mAudioTrackWeakRef = env->NewGlobalRef(audioTrackWeakRef); } AudioTrackCallbackImpl(const AudioTrackCallbackImpl&) = delete; AudioTrackCallbackImpl& operator=(const AudioTrackCallbackImpl&) = delete; ~AudioTrackCallbackImpl() { const auto env = getJNIEnv(); env->DeleteGlobalRef(mAudioTrackClass); env->DeleteGlobalRef(mAudioTrackWeakRef); } size_t onCanWriteMoreData(const AudioTrack::Buffer& buffer) override { if (!mIsOffload) { LOG_FATAL("Received canWrite callback for non-offload track"); return 0; } const size_t availableForWrite = buffer.size(); const int arg = availableForWrite > INT32_MAX ? INT32_MAX : (int) availableForWrite; postEvent(EVENT_CAN_WRITE_MORE_DATA, arg); return 0; } void onMarker([[maybe_unused]] uint32_t markerPosition) override { postEvent(EVENT_MARKER); } void onNewPos([[maybe_unused]] uint32_t newPos) override { postEvent(EVENT_NEW_POS); } void onNewIAudioTrack() override { if (!mIsOffload) return; postEvent(EVENT_NEW_IAUDIOTRACK); } void onStreamEnd() override { if (!mIsOffload) return; postEvent(EVENT_STREAM_END); } protected: jobject mAudioTrackWeakRef; private: JNIEnv* getJNIEnv() { auto jni = AndroidRuntime::getJNIEnv(); if (jni == nullptr) { LOG_ALWAYS_FATAL("AudioTrackCallback thread JNI reference is null"); } return jni; } void postEvent(int event, int arg = 0) { auto env = getJNIEnv(); env->CallStaticVoidMethod( mAudioTrackClass, javaAudioTrackFields.postNativeEventInJava, mAudioTrackWeakRef, event, arg, 0, NULL); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); } } jclass mAudioTrackClass; const bool mIsOffload; }; // keep these values in sync with AudioTrack.java Loading @@ -76,22 +158,21 @@ struct audiotrack_callback_cookie { #define MODE_STREAM 1 // ---------------------------------------------------------------------------- class AudioTrackJniStorage { class AudioTrackJniStorage : public virtual RefBase, public AudioTrackCallbackImpl { public: sp<MemoryHeapBase> mMemHeap; sp<MemoryBase> mMemBase; audiotrack_callback_cookie mCallbackData{}; // TODO do we always want to initialize the callback implementation? AudioTrackJniStorage(jclass audioTrackClass, jobject audioTrackRef, bool isOffload = false) : AudioTrackCallbackImpl(audioTrackClass, audioTrackRef, isOffload) {} sp<JNIDeviceCallback> mDeviceCallback; sp<JNIAudioTrackCallback> mAudioTrackCallback; bool allocSharedMem(int sizeInBytes) { mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base"); if (mMemHeap->getHeapID() < 0) { return false; } mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes); return true; jobject getAudioTrackWeakRef() const { return mAudioTrackWeakRef; } }; class TunerConfigurationHelper { Loading Loading @@ -136,7 +217,6 @@ public: }; static Mutex sLock; static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies; // ---------------------------------------------------------------------------- #define DEFAULT_OUTPUT_SAMPLE_RATE 44100 Loading @@ -147,102 +227,49 @@ static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies; #define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE (-19) #define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED (-20) // ---------------------------------------------------------------------------- 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; namespace { sp<IMemory> allocSharedMem(int sizeInBytes) { const auto heap = sp<MemoryHeapBase>::make(sizeInBytes, 0, "AudioTrack Heap Base"); if (heap->getBase() == MAP_FAILED || heap->getBase() == nullptr) { return nullptr; } callbackInfo->busy = true; } // used as default argument when event callback doesn't have any, or number of // frames for EVENT_CAN_WRITE_MORE_DATA int arg = 0; bool postEvent = false; switch (event) { // Offload only events case AudioTrack::EVENT_CAN_WRITE_MORE_DATA: // this event will read the info return parameter of the callback: // for JNI offload, use the returned size to indicate: // 1/ no data is returned through callback, as it's all done through write() // 2/ do not wait as AudioTrack does when it receives 0 bytes if (callbackInfo->isOffload) { AudioTrack::Buffer* pBuffer = (AudioTrack::Buffer*) info; const size_t availableForWrite = pBuffer->size; arg = availableForWrite > INT32_MAX ? INT32_MAX : (int) availableForWrite; pBuffer->size = 0; } FALLTHROUGH_INTENDED; case AudioTrack::EVENT_STREAM_END: case AudioTrack::EVENT_NEW_IAUDIOTRACK: // a.k.a. tear down if (callbackInfo->isOffload) { postEvent = true; return sp<MemoryBase>::make(heap, 0, sizeInBytes); } break; // PCM and offload events case AudioTrack::EVENT_MARKER: case AudioTrack::EVENT_NEW_POS: postEvent = true; break; default: // event will not be posted break; } if (postEvent) { JNIEnv *env = AndroidRuntime::getJNIEnv(); if (env != NULL) { env->CallStaticVoidMethod( callbackInfo->audioTrack_class, javaAudioTrackFields.postNativeEventInJava, callbackInfo->audioTrack_ref, event, arg, 0, NULL); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); } } } { Mutex::Autolock l(sLock); callbackInfo->busy = false; callbackInfo->cond.broadcast(); } } // ---------------------------------------------------------------------------- static sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz) // TODO(b/218351957) move somewhere? template<typename T> sp<T> getFieldSp(JNIEnv* env, jobject thiz, jfieldID id) { // make these fields atomic longs on the java side Mutex::Autolock l(sLock); AudioTrack* const at = (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj); return sp<AudioTrack>(at); return sp<T>::fromExisting(reinterpret_cast<T*>(env->GetLongField(thiz, id))); } static sp<AudioTrack> setAudioTrack(JNIEnv* env, jobject thiz, const sp<AudioTrack>& at) // This (semantically) should only be called on AudioTrack creation and release template <typename T> sp<T> setFieldSp(JNIEnv* env, jobject thiz, const sp<T>& at, jfieldID id) { Mutex::Autolock l(sLock); sp<AudioTrack> old = (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj); // I don't think this synchronization actually prevents a race // We can still invalidate under our feet in release sp<T> old = sp<T>::fromExisting(reinterpret_cast<T*>(env->GetLongField(thiz, id))); if (at.get()) { at->incStrong((void*)setAudioTrack); at->incStrong((void*)setFieldSp<T>); } if (old != 0) { old->decStrong((void*)setAudioTrack); old->decStrong((void*)setFieldSp<T>); } env->SetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (jlong)at.get()); env->SetLongField(thiz, id, (jlong)at.get()); return old; } sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz) { return getFieldSp<AudioTrack>(env, thiz, javaAudioTrackFields.nativeTrackInJavaObj); } } // anonymous // ---------------------------------------------------------------------------- sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audioTrackObj) { return getAudioTrack(env, audioTrackObj); return getFieldSp<AudioTrack>(env, audioTrackObj, javaAudioTrackFields.nativeTrackInJavaObj); } // ---------------------------------------------------------------------------- Loading Loading @@ -274,7 +301,6 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); nSession = NULL; AudioTrackJniStorage* lpJniStorage = NULL; jclass clazz = env->GetObjectClass(thiz); if (clazz == NULL) { Loading @@ -284,6 +310,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one. sp<AudioTrack> lpTrack; const auto lpJniStorage = sp<AudioTrackJniStorage>::make(clazz, weak_this, offload); if (nativeAudioTrack == 0) { if (jaa == 0) { ALOGE("Error creating AudioTrack: invalid audio attributes"); Loading Loading @@ -332,7 +359,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we AttributionSourceState attributionSource; attributionSource.packageName = std::string(opPackageNameStr.c_str()); attributionSource.token = sp<BBinder>::make(); lpTrack = new AudioTrack(attributionSource); lpTrack = sp<AudioTrack>::make(attributionSource); // read the AudioAttributes values auto paa = JNIAudioAttributeHelper::makeUnique(); Loading @@ -345,13 +372,6 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we // initialize the callback information: // this data will be passed with every AudioTrack callback lpJniStorage = new AudioTrackJniStorage(); 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.isOffload = offload; lpJniStorage->mCallbackData.busy = false; audio_offload_info_t offloadInfo; if (offload == JNI_TRUE) { offloadInfo = AUDIO_INFO_INITIALIZER; Loading Loading @@ -385,8 +405,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we nativeChannelMask, offload ? 0 : frameCount, offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_NONE, audioCallback, &(lpJniStorage->mCallbackData), // callback, callback data (user) lpJniStorage, 0, // notificationFrames == 0 since not using EVENT_MORE_DATA // to feed the AudioTrack 0, // shared mem Loading @@ -400,9 +419,10 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we break; case MODE_STATIC: { // AudioTrack is using shared memory if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) { const auto iMem = allocSharedMem(buffSizeInBytes); if (iMem == nullptr) { ALOGE("Error creating AudioTrack in static mode: error creating mem heap base"); goto native_init_failure; } Loading @@ -412,19 +432,18 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we sampleRateInHertz, format, // word length, PCM nativeChannelMask, frameCount, AUDIO_OUTPUT_FLAG_NONE, audioCallback, &(lpJniStorage->mCallbackData), // callback, callback data (user) lpJniStorage, 0, // notificationFrames == 0 since not using EVENT_MORE_DATA // to feed the AudioTrack lpJniStorage->mMemBase, // shared mem iMem, // shared mem true, // thread can call Java sessionId, // audio session ID AudioTrack::TRANSFER_SHARED, NULL, // default offloadInfo nullptr , // default offloadInfo AttributionSourceState(), // default uid, pid values paa.get()); break; } default: ALOGE("Unknown mode %d", memoryMode); goto native_init_failure; Loading @@ -438,7 +457,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_JAVA lpTrack->setCallerName("java"); } else { // end if (nativeAudioTrack == 0) lpTrack = (AudioTrack*)nativeAudioTrack; lpTrack = sp<AudioTrack>::fromExisting(reinterpret_cast<AudioTrack*>(nativeAudioTrack)); // TODO: We need to find out which members of the Java AudioTrack might // need to be initialized from the Native AudioTrack // these are directly returned from getters: Loading @@ -456,14 +475,18 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we // initialize the callback information: // this data will be passed with every AudioTrack callback lpJniStorage = new AudioTrackJniStorage(); // TODO this callback information is useless, it isn't passed to the // native AudioTrack object /* 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; */ } lpJniStorage->mAudioTrackCallback = new JNIAudioTrackCallback(env, thiz, lpJniStorage->mCallbackData.audioTrack_ref, sp<JNIAudioTrackCallback>::make(env, thiz, lpJniStorage->getAudioTrackWeakRef(), javaAudioTrackFields.postNativeEventInJava); lpTrack->setAudioTrackCallback(lpJniStorage->mAudioTrackCallback); Loading @@ -482,17 +505,13 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we env->SetIntArrayRegion(jSampleRate, 0, 1, elements); } { // 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) setAudioTrack(env, thiz, lpTrack); setFieldSp(env, thiz, lpTrack, javaAudioTrackFields.nativeTrackInJavaObj); // save the JNI resources so we can free them later //ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage); env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage); setFieldSp(env, thiz, lpJniStorage, javaAudioTrackFields.jniData); // since we had audio attributes, the stream type was derived from them during the // creation of the native AudioTrack: push the same value to the Java object Loading @@ -505,9 +524,6 @@ native_init_failure: if (nSession != NULL) { env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); } env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class); env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref); delete lpJniStorage; env->SetLongField(thiz, javaAudioTrackFields.jniData, 0); // lpTrack goes out of scope, so reference count drops to zero Loading Loading @@ -607,38 +623,9 @@ android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, j // ---------------------------------------------------------------------------- #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000 static void android_media_AudioTrack_release(JNIEnv *env, jobject thiz) { sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0); if (lpTrack == NULL) { return; } //ALOGV("deleting lpTrack: %x\n", (int)lpTrack); // delete the JNI data AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField( thiz, javaAudioTrackFields.jniData); // reset the native resources in the Java object so any attempt to access // them after a call to release fails. env->SetLongField(thiz, javaAudioTrackFields.jniData, 0); if (pJniStorage) { 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; } setFieldSp(env, thiz, sp<AudioTrack>(nullptr), javaAudioTrackFields.nativeTrackInJavaObj); setFieldSp(env, thiz, sp<AudioTrackJniStorage>(nullptr), javaAudioTrackFields.jniData); } Loading Loading @@ -1249,16 +1236,17 @@ static void android_media_AudioTrack_enableDeviceCallback( JNIEnv *env, jobject thiz) { sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); if (lpTrack == NULL) { if (lpTrack == nullptr) { return; } AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField( thiz, javaAudioTrackFields.jniData); if (pJniStorage == NULL || pJniStorage->mDeviceCallback != 0) { const auto pJniStorage = getFieldSp<AudioTrackJniStorage>(env, thiz, javaAudioTrackFields.jniData); if (pJniStorage == nullptr || pJniStorage->mDeviceCallback != nullptr) { return; } pJniStorage->mDeviceCallback = new JNIDeviceCallback(env, thiz, pJniStorage->mCallbackData.audioTrack_ref, sp<JNIDeviceCallback>::make(env, thiz, pJniStorage->getAudioTrackWeakRef(), javaAudioTrackFields.postNativeEventInJava); lpTrack->addAudioDeviceCallback(pJniStorage->mDeviceCallback); } Loading @@ -1267,12 +1255,13 @@ static void android_media_AudioTrack_disableDeviceCallback( JNIEnv *env, jobject thiz) { sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); if (lpTrack == NULL) { if (lpTrack == nullptr) { return; } AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField( thiz, javaAudioTrackFields.jniData); if (pJniStorage == NULL || pJniStorage->mDeviceCallback == 0) { const auto pJniStorage = getFieldSp<AudioTrackJniStorage>(env, thiz, javaAudioTrackFields.jniData); if (pJniStorage == nullptr || pJniStorage->mDeviceCallback == nullptr) { return; } lpTrack->removeAudioDeviceCallback(pJniStorage->mDeviceCallback); Loading core/jni/android_media_AudioTrack.h +1 −7 Original line number Diff line number Diff line Loading @@ -18,15 +18,9 @@ #define ANDROID_MEDIA_AUDIOTRACK_H #include "jni.h" #include <media/AudioTrack.h> #include <utils/StrongPointer.h> namespace android { class AudioTrack; }; // namespace android /* Gets the underlying AudioTrack from an AudioTrack Java object. */ extern android::sp<android::AudioTrack> android_media_AudioTrack_getAudioTrack( JNIEnv* env, jobject audioTrackObj); Loading Loading
core/jni/android_media_AudioTrack.cpp +158 −169 Original line number Diff line number Diff line Loading @@ -63,12 +63,94 @@ static audio_track_fields_t javaAudioTrackFields; static PlaybackParams::fields_t gPlaybackParamsFields; static VolumeShaperHelper::fields_t gVolumeShaperFields; struct audiotrack_callback_cookie { jclass audioTrack_class; jobject audioTrack_ref; bool busy; Condition cond; bool isOffload; class AudioTrackCallbackImpl : public AudioTrack::IAudioTrackCallback { public: enum event_type { // Keep in sync with java EVENT_MORE_DATA = 0, EVENT_UNDERRUN = 1, EVENT_LOOP_END = 2, EVENT_MARKER = 3, EVENT_NEW_POS = 4, EVENT_BUFFER_END = 5, EVENT_NEW_IAUDIOTRACK = 6, EVENT_STREAM_END = 7, // 8 is reserved for future use EVENT_CAN_WRITE_MORE_DATA = 9 }; AudioTrackCallbackImpl(jclass audioTrackClass, jobject audioTrackWeakRef, bool isOffload) : mIsOffload(isOffload) { const auto env = getJNIEnv(); mAudioTrackClass = (jclass)env->NewGlobalRef(audioTrackClass); // we use a weak reference so the AudioTrack object can be garbage collected. mAudioTrackWeakRef = env->NewGlobalRef(audioTrackWeakRef); } AudioTrackCallbackImpl(const AudioTrackCallbackImpl&) = delete; AudioTrackCallbackImpl& operator=(const AudioTrackCallbackImpl&) = delete; ~AudioTrackCallbackImpl() { const auto env = getJNIEnv(); env->DeleteGlobalRef(mAudioTrackClass); env->DeleteGlobalRef(mAudioTrackWeakRef); } size_t onCanWriteMoreData(const AudioTrack::Buffer& buffer) override { if (!mIsOffload) { LOG_FATAL("Received canWrite callback for non-offload track"); return 0; } const size_t availableForWrite = buffer.size(); const int arg = availableForWrite > INT32_MAX ? INT32_MAX : (int) availableForWrite; postEvent(EVENT_CAN_WRITE_MORE_DATA, arg); return 0; } void onMarker([[maybe_unused]] uint32_t markerPosition) override { postEvent(EVENT_MARKER); } void onNewPos([[maybe_unused]] uint32_t newPos) override { postEvent(EVENT_NEW_POS); } void onNewIAudioTrack() override { if (!mIsOffload) return; postEvent(EVENT_NEW_IAUDIOTRACK); } void onStreamEnd() override { if (!mIsOffload) return; postEvent(EVENT_STREAM_END); } protected: jobject mAudioTrackWeakRef; private: JNIEnv* getJNIEnv() { auto jni = AndroidRuntime::getJNIEnv(); if (jni == nullptr) { LOG_ALWAYS_FATAL("AudioTrackCallback thread JNI reference is null"); } return jni; } void postEvent(int event, int arg = 0) { auto env = getJNIEnv(); env->CallStaticVoidMethod( mAudioTrackClass, javaAudioTrackFields.postNativeEventInJava, mAudioTrackWeakRef, event, arg, 0, NULL); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); } } jclass mAudioTrackClass; const bool mIsOffload; }; // keep these values in sync with AudioTrack.java Loading @@ -76,22 +158,21 @@ struct audiotrack_callback_cookie { #define MODE_STREAM 1 // ---------------------------------------------------------------------------- class AudioTrackJniStorage { class AudioTrackJniStorage : public virtual RefBase, public AudioTrackCallbackImpl { public: sp<MemoryHeapBase> mMemHeap; sp<MemoryBase> mMemBase; audiotrack_callback_cookie mCallbackData{}; // TODO do we always want to initialize the callback implementation? AudioTrackJniStorage(jclass audioTrackClass, jobject audioTrackRef, bool isOffload = false) : AudioTrackCallbackImpl(audioTrackClass, audioTrackRef, isOffload) {} sp<JNIDeviceCallback> mDeviceCallback; sp<JNIAudioTrackCallback> mAudioTrackCallback; bool allocSharedMem(int sizeInBytes) { mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base"); if (mMemHeap->getHeapID() < 0) { return false; } mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes); return true; jobject getAudioTrackWeakRef() const { return mAudioTrackWeakRef; } }; class TunerConfigurationHelper { Loading Loading @@ -136,7 +217,6 @@ public: }; static Mutex sLock; static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies; // ---------------------------------------------------------------------------- #define DEFAULT_OUTPUT_SAMPLE_RATE 44100 Loading @@ -147,102 +227,49 @@ static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies; #define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE (-19) #define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED (-20) // ---------------------------------------------------------------------------- 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; namespace { sp<IMemory> allocSharedMem(int sizeInBytes) { const auto heap = sp<MemoryHeapBase>::make(sizeInBytes, 0, "AudioTrack Heap Base"); if (heap->getBase() == MAP_FAILED || heap->getBase() == nullptr) { return nullptr; } callbackInfo->busy = true; } // used as default argument when event callback doesn't have any, or number of // frames for EVENT_CAN_WRITE_MORE_DATA int arg = 0; bool postEvent = false; switch (event) { // Offload only events case AudioTrack::EVENT_CAN_WRITE_MORE_DATA: // this event will read the info return parameter of the callback: // for JNI offload, use the returned size to indicate: // 1/ no data is returned through callback, as it's all done through write() // 2/ do not wait as AudioTrack does when it receives 0 bytes if (callbackInfo->isOffload) { AudioTrack::Buffer* pBuffer = (AudioTrack::Buffer*) info; const size_t availableForWrite = pBuffer->size; arg = availableForWrite > INT32_MAX ? INT32_MAX : (int) availableForWrite; pBuffer->size = 0; } FALLTHROUGH_INTENDED; case AudioTrack::EVENT_STREAM_END: case AudioTrack::EVENT_NEW_IAUDIOTRACK: // a.k.a. tear down if (callbackInfo->isOffload) { postEvent = true; return sp<MemoryBase>::make(heap, 0, sizeInBytes); } break; // PCM and offload events case AudioTrack::EVENT_MARKER: case AudioTrack::EVENT_NEW_POS: postEvent = true; break; default: // event will not be posted break; } if (postEvent) { JNIEnv *env = AndroidRuntime::getJNIEnv(); if (env != NULL) { env->CallStaticVoidMethod( callbackInfo->audioTrack_class, javaAudioTrackFields.postNativeEventInJava, callbackInfo->audioTrack_ref, event, arg, 0, NULL); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); } } } { Mutex::Autolock l(sLock); callbackInfo->busy = false; callbackInfo->cond.broadcast(); } } // ---------------------------------------------------------------------------- static sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz) // TODO(b/218351957) move somewhere? template<typename T> sp<T> getFieldSp(JNIEnv* env, jobject thiz, jfieldID id) { // make these fields atomic longs on the java side Mutex::Autolock l(sLock); AudioTrack* const at = (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj); return sp<AudioTrack>(at); return sp<T>::fromExisting(reinterpret_cast<T*>(env->GetLongField(thiz, id))); } static sp<AudioTrack> setAudioTrack(JNIEnv* env, jobject thiz, const sp<AudioTrack>& at) // This (semantically) should only be called on AudioTrack creation and release template <typename T> sp<T> setFieldSp(JNIEnv* env, jobject thiz, const sp<T>& at, jfieldID id) { Mutex::Autolock l(sLock); sp<AudioTrack> old = (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj); // I don't think this synchronization actually prevents a race // We can still invalidate under our feet in release sp<T> old = sp<T>::fromExisting(reinterpret_cast<T*>(env->GetLongField(thiz, id))); if (at.get()) { at->incStrong((void*)setAudioTrack); at->incStrong((void*)setFieldSp<T>); } if (old != 0) { old->decStrong((void*)setAudioTrack); old->decStrong((void*)setFieldSp<T>); } env->SetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (jlong)at.get()); env->SetLongField(thiz, id, (jlong)at.get()); return old; } sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz) { return getFieldSp<AudioTrack>(env, thiz, javaAudioTrackFields.nativeTrackInJavaObj); } } // anonymous // ---------------------------------------------------------------------------- sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audioTrackObj) { return getAudioTrack(env, audioTrackObj); return getFieldSp<AudioTrack>(env, audioTrackObj, javaAudioTrackFields.nativeTrackInJavaObj); } // ---------------------------------------------------------------------------- Loading Loading @@ -274,7 +301,6 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); nSession = NULL; AudioTrackJniStorage* lpJniStorage = NULL; jclass clazz = env->GetObjectClass(thiz); if (clazz == NULL) { Loading @@ -284,6 +310,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one. sp<AudioTrack> lpTrack; const auto lpJniStorage = sp<AudioTrackJniStorage>::make(clazz, weak_this, offload); if (nativeAudioTrack == 0) { if (jaa == 0) { ALOGE("Error creating AudioTrack: invalid audio attributes"); Loading Loading @@ -332,7 +359,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we AttributionSourceState attributionSource; attributionSource.packageName = std::string(opPackageNameStr.c_str()); attributionSource.token = sp<BBinder>::make(); lpTrack = new AudioTrack(attributionSource); lpTrack = sp<AudioTrack>::make(attributionSource); // read the AudioAttributes values auto paa = JNIAudioAttributeHelper::makeUnique(); Loading @@ -345,13 +372,6 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we // initialize the callback information: // this data will be passed with every AudioTrack callback lpJniStorage = new AudioTrackJniStorage(); 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.isOffload = offload; lpJniStorage->mCallbackData.busy = false; audio_offload_info_t offloadInfo; if (offload == JNI_TRUE) { offloadInfo = AUDIO_INFO_INITIALIZER; Loading Loading @@ -385,8 +405,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we nativeChannelMask, offload ? 0 : frameCount, offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_NONE, audioCallback, &(lpJniStorage->mCallbackData), // callback, callback data (user) lpJniStorage, 0, // notificationFrames == 0 since not using EVENT_MORE_DATA // to feed the AudioTrack 0, // shared mem Loading @@ -400,9 +419,10 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we break; case MODE_STATIC: { // AudioTrack is using shared memory if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) { const auto iMem = allocSharedMem(buffSizeInBytes); if (iMem == nullptr) { ALOGE("Error creating AudioTrack in static mode: error creating mem heap base"); goto native_init_failure; } Loading @@ -412,19 +432,18 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we sampleRateInHertz, format, // word length, PCM nativeChannelMask, frameCount, AUDIO_OUTPUT_FLAG_NONE, audioCallback, &(lpJniStorage->mCallbackData), // callback, callback data (user) lpJniStorage, 0, // notificationFrames == 0 since not using EVENT_MORE_DATA // to feed the AudioTrack lpJniStorage->mMemBase, // shared mem iMem, // shared mem true, // thread can call Java sessionId, // audio session ID AudioTrack::TRANSFER_SHARED, NULL, // default offloadInfo nullptr , // default offloadInfo AttributionSourceState(), // default uid, pid values paa.get()); break; } default: ALOGE("Unknown mode %d", memoryMode); goto native_init_failure; Loading @@ -438,7 +457,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_JAVA lpTrack->setCallerName("java"); } else { // end if (nativeAudioTrack == 0) lpTrack = (AudioTrack*)nativeAudioTrack; lpTrack = sp<AudioTrack>::fromExisting(reinterpret_cast<AudioTrack*>(nativeAudioTrack)); // TODO: We need to find out which members of the Java AudioTrack might // need to be initialized from the Native AudioTrack // these are directly returned from getters: Loading @@ -456,14 +475,18 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we // initialize the callback information: // this data will be passed with every AudioTrack callback lpJniStorage = new AudioTrackJniStorage(); // TODO this callback information is useless, it isn't passed to the // native AudioTrack object /* 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; */ } lpJniStorage->mAudioTrackCallback = new JNIAudioTrackCallback(env, thiz, lpJniStorage->mCallbackData.audioTrack_ref, sp<JNIAudioTrackCallback>::make(env, thiz, lpJniStorage->getAudioTrackWeakRef(), javaAudioTrackFields.postNativeEventInJava); lpTrack->setAudioTrackCallback(lpJniStorage->mAudioTrackCallback); Loading @@ -482,17 +505,13 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we env->SetIntArrayRegion(jSampleRate, 0, 1, elements); } { // 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) setAudioTrack(env, thiz, lpTrack); setFieldSp(env, thiz, lpTrack, javaAudioTrackFields.nativeTrackInJavaObj); // save the JNI resources so we can free them later //ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage); env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage); setFieldSp(env, thiz, lpJniStorage, javaAudioTrackFields.jniData); // since we had audio attributes, the stream type was derived from them during the // creation of the native AudioTrack: push the same value to the Java object Loading @@ -505,9 +524,6 @@ native_init_failure: if (nSession != NULL) { env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); } env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class); env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref); delete lpJniStorage; env->SetLongField(thiz, javaAudioTrackFields.jniData, 0); // lpTrack goes out of scope, so reference count drops to zero Loading Loading @@ -607,38 +623,9 @@ android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, j // ---------------------------------------------------------------------------- #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000 static void android_media_AudioTrack_release(JNIEnv *env, jobject thiz) { sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0); if (lpTrack == NULL) { return; } //ALOGV("deleting lpTrack: %x\n", (int)lpTrack); // delete the JNI data AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField( thiz, javaAudioTrackFields.jniData); // reset the native resources in the Java object so any attempt to access // them after a call to release fails. env->SetLongField(thiz, javaAudioTrackFields.jniData, 0); if (pJniStorage) { 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; } setFieldSp(env, thiz, sp<AudioTrack>(nullptr), javaAudioTrackFields.nativeTrackInJavaObj); setFieldSp(env, thiz, sp<AudioTrackJniStorage>(nullptr), javaAudioTrackFields.jniData); } Loading Loading @@ -1249,16 +1236,17 @@ static void android_media_AudioTrack_enableDeviceCallback( JNIEnv *env, jobject thiz) { sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); if (lpTrack == NULL) { if (lpTrack == nullptr) { return; } AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField( thiz, javaAudioTrackFields.jniData); if (pJniStorage == NULL || pJniStorage->mDeviceCallback != 0) { const auto pJniStorage = getFieldSp<AudioTrackJniStorage>(env, thiz, javaAudioTrackFields.jniData); if (pJniStorage == nullptr || pJniStorage->mDeviceCallback != nullptr) { return; } pJniStorage->mDeviceCallback = new JNIDeviceCallback(env, thiz, pJniStorage->mCallbackData.audioTrack_ref, sp<JNIDeviceCallback>::make(env, thiz, pJniStorage->getAudioTrackWeakRef(), javaAudioTrackFields.postNativeEventInJava); lpTrack->addAudioDeviceCallback(pJniStorage->mDeviceCallback); } Loading @@ -1267,12 +1255,13 @@ static void android_media_AudioTrack_disableDeviceCallback( JNIEnv *env, jobject thiz) { sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); if (lpTrack == NULL) { if (lpTrack == nullptr) { return; } AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField( thiz, javaAudioTrackFields.jniData); if (pJniStorage == NULL || pJniStorage->mDeviceCallback == 0) { const auto pJniStorage = getFieldSp<AudioTrackJniStorage>(env, thiz, javaAudioTrackFields.jniData); if (pJniStorage == nullptr || pJniStorage->mDeviceCallback == nullptr) { return; } lpTrack->removeAudioDeviceCallback(pJniStorage->mDeviceCallback); Loading
core/jni/android_media_AudioTrack.h +1 −7 Original line number Diff line number Diff line Loading @@ -18,15 +18,9 @@ #define ANDROID_MEDIA_AUDIOTRACK_H #include "jni.h" #include <media/AudioTrack.h> #include <utils/StrongPointer.h> namespace android { class AudioTrack; }; // namespace android /* Gets the underlying AudioTrack from an AudioTrack Java object. */ extern android::sp<android::AudioTrack> android_media_AudioTrack_getAudioTrack( JNIEnv* env, jobject audioTrackObj); Loading