summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/jni/android_media_AudioTrack.cpp327
-rw-r--r--core/jni/android_media_AudioTrack.h8
2 files changed, 159 insertions, 176 deletions
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 73d2d8d949cd..8c33e0719f73 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -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
@@ -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 {
@@ -136,7 +217,6 @@ public:
};
static Mutex sLock;
-static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies;
// ----------------------------------------------------------------------------
#define DEFAULT_OUTPUT_SAMPLE_RATE 44100
@@ -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;
- }
- 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;
- }
- 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();
+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;
}
+ return sp<MemoryBase>::make(heap, 0, sizeInBytes);
}
-
-
-// ----------------------------------------------------------------------------
-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);
}
// ----------------------------------------------------------------------------
@@ -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) {
@@ -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");
@@ -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();
@@ -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;
@@ -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
@@ -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;
}
@@ -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;
@@ -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:
@@ -456,15 +475,19 @@ 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,
- javaAudioTrackFields.postNativeEventInJava);
+ sp<JNIAudioTrackCallback>::make(env, thiz, lpJniStorage->getAudioTrackWeakRef(),
+ javaAudioTrackFields.postNativeEventInJava);
lpTrack->setAudioTrackCallback(lpJniStorage->mAudioTrackCallback);
nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
@@ -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
@@ -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
@@ -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);
}
@@ -1249,17 +1236,18 @@ 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,
- javaAudioTrackFields.postNativeEventInJava);
+ sp<JNIDeviceCallback>::make(env, thiz, pJniStorage->getAudioTrackWeakRef(),
+ javaAudioTrackFields.postNativeEventInJava);
lpTrack->addAudioDeviceCallback(pJniStorage->mDeviceCallback);
}
@@ -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);
diff --git a/core/jni/android_media_AudioTrack.h b/core/jni/android_media_AudioTrack.h
index ef2aa669db26..d5b858c89c27 100644
--- a/core/jni/android_media_AudioTrack.h
+++ b/core/jni/android_media_AudioTrack.h
@@ -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);