diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 8c33e0719f73a..1afd2effef08e 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -37,6 +37,7 @@ #include "android_media_AudioFormat.h" #include "android_media_AudioTrackCallback.h" #include "android_media_DeviceCallback.h" +#include "android_media_JNIUtils.h" #include "android_media_MediaMetricsJNI.h" #include "android_media_PlaybackParams.h" #include "android_media_VolumeShaper.h" @@ -82,7 +83,7 @@ class AudioTrackCallbackImpl : public AudioTrack::IAudioTrackCallback { AudioTrackCallbackImpl(jclass audioTrackClass, jobject audioTrackWeakRef, bool isOffload) : mIsOffload(isOffload) { - const auto env = getJNIEnv(); + const auto env = getJNIEnvOrDie(); mAudioTrackClass = (jclass)env->NewGlobalRef(audioTrackClass); // we use a weak reference so the AudioTrack object can be garbage collected. mAudioTrackWeakRef = env->NewGlobalRef(audioTrackWeakRef); @@ -92,7 +93,7 @@ class AudioTrackCallbackImpl : public AudioTrack::IAudioTrackCallback { AudioTrackCallbackImpl(const AudioTrackCallbackImpl&) = delete; AudioTrackCallbackImpl& operator=(const AudioTrackCallbackImpl&) = delete; ~AudioTrackCallbackImpl() { - const auto env = getJNIEnv(); + const auto env = getJNIEnvOrDie(); env->DeleteGlobalRef(mAudioTrackClass); env->DeleteGlobalRef(mAudioTrackWeakRef); } @@ -129,16 +130,9 @@ class AudioTrackCallbackImpl : public AudioTrack::IAudioTrackCallback { 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(); + auto env = getJNIEnvOrDie(); env->CallStaticVoidMethod( mAudioTrackClass, javaAudioTrackFields.postNativeEventInJava, @@ -216,7 +210,6 @@ public: static void initCheckOrDie(JNIEnv *env) { (void)getIds(env); } }; -static Mutex sLock; // ---------------------------------------------------------------------------- #define DEFAULT_OUTPUT_SAMPLE_RATE 44100 @@ -235,32 +228,6 @@ sp allocSharedMem(int sizeInBytes) { } return sp::make(heap, 0, sizeInBytes); } -// TODO(b/218351957) move somewhere? -template -sp getFieldSp(JNIEnv* env, jobject thiz, jfieldID id) -{ - // make these fields atomic longs on the java side - Mutex::Autolock l(sLock); - return sp::fromExisting(reinterpret_cast(env->GetLongField(thiz, id))); -} - -// This (semantically) should only be called on AudioTrack creation and release -template -sp setFieldSp(JNIEnv* env, jobject thiz, const sp& at, jfieldID id) -{ - Mutex::Autolock l(sLock); - // I don't think this synchronization actually prevents a race - // We can still invalidate under our feet in release - sp old = sp::fromExisting(reinterpret_cast(env->GetLongField(thiz, id))); - if (at.get()) { - at->incStrong((void*)setFieldSp); - } - if (old != 0) { - old->decStrong((void*)setFieldSp); - } - env->SetLongField(thiz, id, (jlong)at.get()); - return old; -} sp getAudioTrack(JNIEnv* env, jobject thiz) { return getFieldSp(env, thiz, javaAudioTrackFields.nativeTrackInJavaObj); @@ -268,8 +235,9 @@ sp getAudioTrack(JNIEnv* env, jobject thiz) { } // anonymous // ---------------------------------------------------------------------------- +// For MediaSync sp android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audioTrackObj) { - return getFieldSp(env, audioTrackObj, javaAudioTrackFields.nativeTrackInJavaObj); + return getAudioTrack(env, audioTrackObj); } // ---------------------------------------------------------------------------- @@ -524,8 +492,9 @@ native_init_failure: if (nSession != NULL) { env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); } - env->SetLongField(thiz, javaAudioTrackFields.jniData, 0); + setFieldSp(env, thiz, sp{}, javaAudioTrackFields.nativeTrackInJavaObj); + setFieldSp(env, thiz, sp{}, javaAudioTrackFields.jniData); // lpTrack goes out of scope, so reference count drops to zero return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; } diff --git a/core/jni/android_media_JNIUtils.h b/core/jni/android_media_JNIUtils.h new file mode 100644 index 0000000000000..9da5fa37a8f39 --- /dev/null +++ b/core/jni/android_media_JNIUtils.h @@ -0,0 +1,68 @@ +/* + * 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 + +#include + +#include + +#include +#include +#include + +namespace android { + +// TODO (b/218351957) remove static lock +// This lock is intentionally static, it is local to each TU which is +// responsible for a single Java class. +static std::mutex sFieldSpLock; + +// getFieldSp and setFieldSp are used to logically represent an owning reference +// to a native object from across the JNI. The data (underlying ptr) is stored +// in a field in the Java object as a long. Setting a Java field to a sp +// involves incrementing the reference count to prevent object destruction. +// Resetting the field decrements the reference count to avoid memory leaks. + +template +sp getFieldSp(JNIEnv* env, jobject thiz, jfieldID id) { + const std::lock_guard l{sFieldSpLock}; + return sp::fromExisting(reinterpret_cast(env->GetLongField(thiz, id))); +} + +template +sp setFieldSp(JNIEnv* env, jobject thiz, const sp& newSp, jfieldID id) { + const std::lock_guard l{sFieldSpLock}; + sp old = sp::fromExisting(reinterpret_cast(env->GetLongField(thiz, id))); + if (newSp) { + newSp->incStrong((void*)setFieldSp); + } + if (old) { + old->decStrong((void*)setFieldSp); + } + env->SetLongField(thiz, id, (jlong)newSp.get()); + return old; +} + +inline JNIEnv* getJNIEnvOrDie() { + const auto env = AndroidRuntime::getJNIEnv(); + LOG_ALWAYS_FATAL_IF(env == nullptr, + "Thread JNI reference is null. Thread not prepared for Java."); + return env; +} + + +} // namespace android