fix AudioTrack and AudioRecord JNI
AudioTrack and AudioRecord JNI should not cast
jshortArray to jbyteArray. This appeared to work with Dalvik
but causes data corruption with ART.
(cherry picked from commit 9d02848e902d04417df616354db3a18c03e639b7)
Change-Id: Ie36624d3ea06042373c64edced4b5e30b7b1ee86
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index ab70f25..3f7df50 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -399,13 +399,46 @@
jshortArray javaAudioData,
jint offsetInShorts, jint sizeInShorts) {
- jint read = android_media_AudioRecord_readInByteArray(env, thiz,
- (jbyteArray) javaAudioData,
- offsetInShorts*2, sizeInShorts*2);
- if (read > 0) {
- read /= 2;
+ jshort* recordBuff = NULL;
+ // get the audio recorder from which we'll read new audio samples
+ sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+ if (lpRecorder == NULL) {
+ ALOGE("Unable to retrieve AudioRecord object, can't record");
+ return 0;
}
- return read;
+
+ if (!javaAudioData) {
+ ALOGE("Invalid Java array to store recorded audio, can't record");
+ return 0;
+ }
+
+ // get the pointer to where we'll record the audio
+ // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
+ // a way that it becomes much more efficient. When doing so, we will have to prevent the
+ // AudioSystem callback to be called while in critical section (in case of media server
+ // process crash for instance)
+ recordBuff = (jshort *)env->GetShortArrayElements(javaAudioData, NULL);
+
+ if (recordBuff == NULL) {
+ ALOGE("Error retrieving destination for recorded audio data, can't record");
+ return 0;
+ }
+
+ // read the new audio data from the native AudioRecord object
+ const size_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
+ const size_t sizeInBytes = sizeInShorts * sizeof(short);
+ ssize_t readSize = lpRecorder->read(recordBuff + offsetInShorts * sizeof(short),
+ sizeInBytes > recorderBuffSize ?
+ recorderBuffSize : sizeInBytes);
+
+ env->ReleaseShortArrayElements(javaAudioData, recordBuff, 0);
+
+ if (readSize < 0) {
+ readSize = AUDIORECORD_ERROR_INVALID_OPERATION;
+ } else {
+ readSize /= sizeof(short);
+ }
+ return (jint) readSize;
}
// ----------------------------------------------------------------------------
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index dc8d9d8..1bf42e9 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -595,13 +595,39 @@
jshortArray javaAudioData,
jint offsetInShorts, jint sizeInShorts,
jint javaAudioFormat) {
- jint written = android_media_AudioTrack_native_write_byte(env, thiz,
- (jbyteArray) javaAudioData,
- offsetInShorts*2, sizeInShorts*2,
- javaAudioFormat);
- if (written > 0) {
- written /= 2;
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve AudioTrack pointer for write()");
+ return 0;
}
+
+ // get the pointer for the audio data from the java array
+ // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
+ // a way that it becomes much more efficient. When doing so, we will have to prevent the
+ // AudioSystem callback to be called while in critical section (in case of media server
+ // process crash for instance)
+ jshort* cAudioData = NULL;
+ if (javaAudioData) {
+ cAudioData = (jshort *)env->GetShortArrayElements(javaAudioData, NULL);
+ if (cAudioData == NULL) {
+ ALOGE("Error retrieving source of audio data to play, can't play");
+ return 0; // out of memory or no data to load
+ }
+ } else {
+ ALOGE("NULL java array of audio data to play, can't play");
+ return 0;
+ }
+ jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData,
+ offsetInShorts * sizeof(short), sizeInShorts * sizeof(short));
+ env->ReleaseShortArrayElements(javaAudioData, cAudioData, 0);
+
+ if (written > 0) {
+ written /= sizeof(short);
+ }
+ //ALOGV("write wrote %d (tried %d) shorts in the native AudioTrack with offset %d",
+ // (int)written, (int)(sizeInShorts), (int)offsetInShorts);
+
return written;
}