Merge "SoundPool: Clean up JNI weak reference handling" into tm-dev
This commit is contained in:
@@ -29,7 +29,6 @@ import android.util.Log;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
|
||||||
@@ -156,8 +155,7 @@ public class SoundPool extends PlayerBase {
|
|||||||
super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL);
|
super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL);
|
||||||
|
|
||||||
// do native setup
|
// do native setup
|
||||||
if (native_setup(new WeakReference<SoundPool>(this),
|
if (native_setup(maxStreams, attributes, getCurrentOpPackageName()) != 0) {
|
||||||
maxStreams, attributes, getCurrentOpPackageName()) != 0) {
|
|
||||||
throw new RuntimeException("Native setup failed");
|
throw new RuntimeException("Native setup failed");
|
||||||
}
|
}
|
||||||
mAttributes = attributes;
|
mAttributes = attributes;
|
||||||
@@ -510,7 +508,7 @@ public class SoundPool extends PlayerBase {
|
|||||||
|
|
||||||
private native final int _load(FileDescriptor fd, long offset, long length, int priority);
|
private native final int _load(FileDescriptor fd, long offset, long length, int priority);
|
||||||
|
|
||||||
private native final int native_setup(Object weakRef, int maxStreams,
|
private native int native_setup(int maxStreams,
|
||||||
@NonNull Object/*AudioAttributes*/ attributes, @NonNull String opPackageName);
|
@NonNull Object/*AudioAttributes*/ attributes, @NonNull String opPackageName);
|
||||||
|
|
||||||
private native final int _play(int soundID, float leftVolume, float rightVolume,
|
private native final int _play(int soundID, float leftVolume, float rightVolume,
|
||||||
@@ -522,17 +520,11 @@ public class SoundPool extends PlayerBase {
|
|||||||
|
|
||||||
// post event from native code to message handler
|
// post event from native code to message handler
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static void postEventFromNative(Object ref, int msg, int arg1, int arg2, Object obj) {
|
private void postEventFromNative(int msg, int arg1, int arg2, Object obj) {
|
||||||
SoundPool soundPool = ((WeakReference<SoundPool>) ref).get();
|
Handler eventHandler = mEventHandler.get();
|
||||||
if (soundPool == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Handler eventHandler = soundPool.mEventHandler.get();
|
|
||||||
if (eventHandler == null) {
|
if (eventHandler == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Message message = eventHandler.obtainMessage(msg, arg1, arg2, obj);
|
Message message = eventHandler.obtainMessage(msg, arg1, arg2, obj);
|
||||||
eventHandler.sendMessage(message);
|
eventHandler.sendMessage(message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,10 +262,19 @@ using JObjectValue = std::remove_pointer_t<jobject>; // _jobject
|
|||||||
// Note std::remove_ptr_t<NonPointerType> == NonPointerType.
|
// Note std::remove_ptr_t<NonPointerType> == NonPointerType.
|
||||||
static_assert(std::is_same_v<JObjectValue*, jobject>);
|
static_assert(std::is_same_v<JObjectValue*, jobject>);
|
||||||
|
|
||||||
|
// *jweak is needed to fit the jweak into a std::shared_ptr.
|
||||||
|
// This is the Android type _jobject, but we derive this type as JWeakValue.
|
||||||
|
using JWeakValue = std::remove_pointer_t<jweak>; // this is just _jobject
|
||||||
|
|
||||||
|
// Check that jweak is really a pointer to JWeakValue.
|
||||||
|
static_assert(std::is_same_v<JWeakValue*, jweak>);
|
||||||
|
|
||||||
// We store the ancillary data associated with a SoundPool object in a concurrent
|
// We store the ancillary data associated with a SoundPool object in a concurrent
|
||||||
// hash map indexed on the SoundPool native object pointer.
|
// hash map indexed on the SoundPool native object pointer.
|
||||||
auto& getSoundPoolJavaRefManager() {
|
auto& getSoundPoolJavaRefManager() {
|
||||||
static ConcurrentHashMap<SoundPool *, std::shared_ptr<JObjectValue>> concurrentHashMap;
|
// Note this can store shared_ptrs to either jweak and jobject,
|
||||||
|
// as the underlying type is identical.
|
||||||
|
static ConcurrentHashMap<SoundPool *, std::shared_ptr<JWeakValue>> concurrentHashMap;
|
||||||
return concurrentHashMap;
|
return concurrentHashMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,6 +293,8 @@ auto& getSoundPoolJavaRefManager() {
|
|||||||
// https://developer.android.com/training/articles/perf-jni
|
// https://developer.android.com/training/articles/perf-jni
|
||||||
// https://android-developers.googleblog.com/2011/11/jni-local-reference-changes-in-ics.html
|
// https://android-developers.googleblog.com/2011/11/jni-local-reference-changes-in-ics.html
|
||||||
//
|
//
|
||||||
|
// Consider using a weak reference if this is self-referential.
|
||||||
|
[[maybe_unused]]
|
||||||
inline auto make_shared_globalref_from_localref(JNIEnv *env, jobject localRef) {
|
inline auto make_shared_globalref_from_localref(JNIEnv *env, jobject localRef) {
|
||||||
return std::shared_ptr<JObjectValue>(
|
return std::shared_ptr<JObjectValue>(
|
||||||
env->NewGlobalRef(localRef),
|
env->NewGlobalRef(localRef),
|
||||||
@@ -292,6 +303,32 @@ inline auto make_shared_globalref_from_localref(JNIEnv *env, jobject localRef) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a weak global reference from local ref.
|
||||||
|
inline auto make_shared_weakglobalref_from_localref(JNIEnv *env, jobject localRef) {
|
||||||
|
return std::shared_ptr<JWeakValue>(
|
||||||
|
env->NewWeakGlobalRef(localRef),
|
||||||
|
[](JWeakValue* weak) { // cannot cache env as don't know which thread we're on.
|
||||||
|
if (weak != nullptr) AndroidRuntime::getJNIEnv()->DeleteWeakGlobalRef(weak);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// std::unique_ptr<> does not store a type-erased deleter like std::shared_ptr<>.
|
||||||
|
// Define a lambda here to use for the std::unique_ptr<> type definition.
|
||||||
|
auto LocalRefDeleter = [](JObjectValue* object) {
|
||||||
|
if (object != nullptr) AndroidRuntime::getJNIEnv()->DeleteLocalRef(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a local reference from another reference.
|
||||||
|
// This is a unique_ptr to avoid the temptation of sharing with other threads.
|
||||||
|
//
|
||||||
|
// This can be used to promote a WeakGlobalRef jweak into a stable LocalRef jobject.
|
||||||
|
//
|
||||||
|
inline auto make_unique_localref_from_ref(JNIEnv *env, jobject object) {
|
||||||
|
return std::unique_ptr<JObjectValue, decltype(LocalRefDeleter)>(
|
||||||
|
env->NewLocalRef(object),
|
||||||
|
LocalRefDeleter);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
|
static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
|
||||||
@@ -438,10 +475,15 @@ static void android_media_callback(SoundPoolEvent event, SoundPool* soundPool, v
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
JNIEnv *env = AndroidRuntime::getJNIEnv();
|
JNIEnv *env = AndroidRuntime::getJNIEnv();
|
||||||
env->CallStaticVoidMethod(
|
// "promote" the WeakGlobalRef into a LocalRef.
|
||||||
fields.mSoundPoolClass, fields.mPostEvent,
|
auto javaSoundPool = make_unique_localref_from_ref(env, weakRef.get());
|
||||||
weakRef.get(), event.mMsg, event.mArg1, event.mArg2,
|
if (!javaSoundPool) {
|
||||||
nullptr /* object */);
|
ALOGW("%s: weak reference promotes to null (release() not called?), "
|
||||||
|
"ignoring callback", __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
env->CallVoidMethod(javaSoundPool.get(), fields.mPostEvent,
|
||||||
|
event.mMsg, event.mArg1, event.mArg2, nullptr /* object */);
|
||||||
|
|
||||||
if (env->ExceptionCheck() != JNI_FALSE) {
|
if (env->ExceptionCheck() != JNI_FALSE) {
|
||||||
ALOGE("%s: Uncaught exception returned from Java callback", __func__);
|
ALOGE("%s: Uncaught exception returned from Java callback", __func__);
|
||||||
@@ -451,7 +493,7 @@ static void android_media_callback(SoundPoolEvent event, SoundPool* soundPool, v
|
|||||||
}
|
}
|
||||||
|
|
||||||
static jint
|
static jint
|
||||||
android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef,
|
android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz,
|
||||||
jint maxChannels, jobject jaa, jstring opPackageName)
|
jint maxChannels, jobject jaa, jstring opPackageName)
|
||||||
{
|
{
|
||||||
ALOGV("android_media_SoundPool_native_setup");
|
ALOGV("android_media_SoundPool_native_setup");
|
||||||
@@ -485,7 +527,7 @@ android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef,
|
|||||||
auto oldSoundPool = setSoundPool(env, thiz, soundPool);
|
auto oldSoundPool = setSoundPool(env, thiz, soundPool);
|
||||||
// register Java SoundPool WeakRef using native SoundPool * as the key, for the callback.
|
// register Java SoundPool WeakRef using native SoundPool * as the key, for the callback.
|
||||||
auto oldSoundPoolJavaRef = getSoundPoolJavaRefManager().set(
|
auto oldSoundPoolJavaRef = getSoundPoolJavaRefManager().set(
|
||||||
soundPool.get(), make_shared_globalref_from_localref(env, weakRef));
|
soundPool.get(), make_shared_weakglobalref_from_localref(env, thiz));
|
||||||
|
|
||||||
ALOGW_IF(oldSoundPool != nullptr, "%s: Aliased SoundPool object %p",
|
ALOGW_IF(oldSoundPool != nullptr, "%s: Aliased SoundPool object %p",
|
||||||
__func__, oldSoundPool.get());
|
__func__, oldSoundPool.get());
|
||||||
@@ -565,7 +607,7 @@ static JNINativeMethod gMethods[] = {
|
|||||||
(void *)android_media_SoundPool_setRate
|
(void *)android_media_SoundPool_setRate
|
||||||
},
|
},
|
||||||
{ "native_setup",
|
{ "native_setup",
|
||||||
"(Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/String;)I",
|
"(ILjava/lang/Object;Ljava/lang/String;)I",
|
||||||
(void*)android_media_SoundPool_native_setup
|
(void*)android_media_SoundPool_native_setup
|
||||||
},
|
},
|
||||||
{ "native_release",
|
{ "native_release",
|
||||||
@@ -600,8 +642,8 @@ jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
|
fields.mPostEvent = env->GetMethodID(
|
||||||
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
|
clazz, "postEventFromNative", "(IIILjava/lang/Object;)V");
|
||||||
if (fields.mPostEvent == nullptr) {
|
if (fields.mPostEvent == nullptr) {
|
||||||
ALOGE("Can't find android/media/SoundPool.postEventFromNative");
|
ALOGE("Can't find android/media/SoundPool.postEventFromNative");
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
Reference in New Issue
Block a user