Merge "Add non-blocking reads to AudioRecord"
diff --git a/api/current.txt b/api/current.txt
index 5f4d6e9..6f2cf45 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14910,9 +14910,12 @@
     method public int getSampleRate();
     method public int getState();
     method public int read(byte[], int, int);
+    method public int read(byte[], int, int, int);
     method public int read(short[], int, int);
+    method public int read(short[], int, int, int);
     method public int read(float[], int, int, int);
     method public int read(java.nio.ByteBuffer, int);
+    method public int read(java.nio.ByteBuffer, int, int);
     method public void release();
     method public int setNotificationMarkerPosition(int);
     method public int setPositionNotificationPeriod(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index e44ee6e..b853a38 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -16120,9 +16120,12 @@
     method public int getSampleRate();
     method public int getState();
     method public int read(byte[], int, int);
+    method public int read(byte[], int, int, int);
     method public int read(short[], int, int);
+    method public int read(short[], int, int, int);
     method public int read(float[], int, int, int);
     method public int read(java.nio.ByteBuffer, int);
+    method public int read(java.nio.ByteBuffer, int, int);
     method public void release();
     method public int setNotificationMarkerPosition(int);
     method public int setPositionNotificationPeriod(int);
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index c9b0e76..33db4a85 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -356,95 +356,58 @@
     android_media_AudioRecord_release(env, thiz);
 }
 
-
-// ----------------------------------------------------------------------------
-static jint android_media_AudioRecord_readInByteArray(JNIEnv *env,  jobject thiz,
-                                                        jbyteArray javaAudioData,
-                                                        jint offsetInBytes, jint sizeInBytes) {
-    jbyte* 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;
-    }
-
-    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 = (jbyte *)env->GetByteArrayElements(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
-    ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes, sizeInBytes);
-    env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
-
-    if (readSize < 0) {
-        readSize = (jint)AUDIO_JAVA_INVALID_OPERATION;
-    }
-    return (jint) readSize;
+// overloaded JNI array helper functions
+static inline
+jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
+    return env->GetByteArrayElements(array, isCopy);
 }
 
-// ----------------------------------------------------------------------------
-static jint android_media_AudioRecord_readInShortArray(JNIEnv *env,  jobject thiz,
-                                                        jshortArray javaAudioData,
-                                                        jint offsetInShorts, jint sizeInShorts) {
-
-    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;
-    }
-
-    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 sizeInBytes = sizeInShorts * sizeof(short);
-    ssize_t readSize = lpRecorder->read(recordBuff + offsetInShorts, sizeInBytes);
-
-    env->ReleaseShortArrayElements(javaAudioData, recordBuff, 0);
-
-    if (readSize < 0) {
-        readSize = (jint)AUDIO_JAVA_INVALID_OPERATION;
-    } else {
-        readSize /= sizeof(short);
-    }
-    return (jint) readSize;
+static inline
+void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
+    env->ReleaseByteArrayElements(array, elems, mode);
 }
 
-// ----------------------------------------------------------------------------
-static jint android_media_AudioRecord_readInFloatArray(JNIEnv *env,  jobject thiz,
-                                                        jfloatArray javaAudioData,
-                                                        jint offsetInFloats, jint sizeInFloats,
-                                                        jboolean isReadBlocking) {
+static inline
+jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
+    return env->GetShortArrayElements(array, isCopy);
+}
+
+static inline
+void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
+    env->ReleaseShortArrayElements(array, elems, mode);
+}
+
+static inline
+jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
+    return env->GetFloatArrayElements(array, isCopy);
+}
+
+static inline
+void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
+    env->ReleaseFloatArrayElements(array, elems, mode);
+}
+
+static inline
+jint interpretReadSizeError(ssize_t readSize) {
+    ALOGE_IF(readSize != WOULD_BLOCK, "Error %zd during AudioRecord native read", readSize);
+    switch (readSize) {
+    case WOULD_BLOCK:
+        return (jint)0;
+    case BAD_VALUE:
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    default:
+        // may be possible for other errors such as
+        // NO_INIT to happen if restoreRecord_l fails.
+    case INVALID_OPERATION:
+        return (jint)AUDIO_JAVA_INVALID_OPERATION;
+    }
+}
+
+template <typename T>
+static jint android_media_AudioRecord_readInArray(JNIEnv *env,  jobject thiz,
+                                                  T javaAudioData,
+                                                  jint offsetInSamples, jint sizeInSamples,
+                                                  jboolean isReadBlocking) {
     // get the audio recorder from which we'll read new audio samples
     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
     if (lpRecorder == NULL) {
@@ -453,76 +416,68 @@
     }
 
     if (javaAudioData == NULL) {
-         ALOGE("Invalid Java array to store recorded audio");
-         return (jint)AUDIO_JAVA_BAD_VALUE;
-     }
+        ALOGE("Invalid Java array to store recorded audio");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
 
-    // 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)
-    jfloat *recordBuff = (jfloat *)env->GetFloatArrayElements(javaAudioData, NULL);
+
+    // get the pointer to where we'll record the audio
+    auto *recordBuff = envGetArrayElements(env, javaAudioData, NULL);
     if (recordBuff == NULL) {
         ALOGE("Error retrieving destination for recorded audio data");
         return (jint)AUDIO_JAVA_BAD_VALUE;
     }
 
     // read the new audio data from the native AudioRecord object
-    const size_t sizeInBytes = sizeInFloats * sizeof(float);
-    ssize_t readSize = lpRecorder->read(recordBuff + offsetInFloats, sizeInBytes);
+    const size_t sizeInBytes = sizeInSamples * sizeof(*recordBuff);
+    ssize_t readSize = lpRecorder->read(
+            recordBuff + offsetInSamples, sizeInBytes, isReadBlocking == JNI_TRUE /* blocking */);
 
-    env->ReleaseFloatArrayElements(javaAudioData, recordBuff, 0);
+    envReleaseArrayElements(env, javaAudioData, recordBuff, 0);
 
     if (readSize < 0) {
-        ALOGE_IF(readSize != WOULD_BLOCK, "Error %zd during AudioRecord native read", readSize);
-        switch (readSize) {
-        case WOULD_BLOCK:
-            return (jint)0;
-        case BAD_VALUE:
-            return (jint)AUDIO_JAVA_BAD_VALUE;
-        default:
-            // may be possible for other errors such as
-            // NO_INIT to happen if restoreRecord_l fails.
-        case INVALID_OPERATION:
-            return (jint)AUDIO_JAVA_INVALID_OPERATION;
-        }
+        return interpretReadSizeError(readSize);
     }
-    return (jint)(readSize / sizeof(float));
+    return (jint)(readSize / sizeof(*recordBuff));
 }
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env,  jobject thiz,
-                                                  jobject jBuffer, jint sizeInBytes) {
+                                                         jobject jBuffer, jint sizeInBytes,
+                                                         jboolean isReadBlocking) {
     // get the audio recorder from which we'll read new audio samples
     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
     if (lpRecorder==NULL)
-        return 0;
+        return (jint)AUDIO_JAVA_INVALID_OPERATION;
 
     // direct buffer and direct access supported?
     long capacity = env->GetDirectBufferCapacity(jBuffer);
     if (capacity == -1) {
         // buffer direct access is not supported
         ALOGE("Buffer direct access is not supported, can't record");
-        return 0;
+        return (jint)AUDIO_JAVA_BAD_VALUE;
     }
     //ALOGV("capacity = %ld", capacity);
     jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
     if (nativeFromJavaBuf==NULL) {
         ALOGE("Buffer direct access is not supported, can't record");
-        return 0;
+        return (jint)AUDIO_JAVA_BAD_VALUE;
     }
 
     // read new data from the recorder
     ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
-                                   capacity < sizeInBytes ? capacity : sizeInBytes);
+                                        capacity < sizeInBytes ? capacity : sizeInBytes,
+                                        isReadBlocking == JNI_TRUE /* blocking */);
     if (readSize < 0) {
-        readSize = (jint)AUDIO_JAVA_INVALID_OPERATION;
+        return interpretReadSizeError(readSize);
     }
     return (jint)readSize;
 }
 
-
 // ----------------------------------------------------------------------------
 static jint android_media_AudioRecord_get_native_frame_count(JNIEnv *env,  jobject thiz) {
     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
@@ -633,12 +588,15 @@
     {"native_finalize",      "()V",    (void *)android_media_AudioRecord_finalize},
     {"native_release",       "()V",    (void *)android_media_AudioRecord_release},
     {"native_read_in_byte_array",
-                             "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
+                             "([BIIZ)I",
+                                     (void *)android_media_AudioRecord_readInArray<jbyteArray>},
     {"native_read_in_short_array",
-                             "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
+                             "([SIIZ)I",
+                                     (void *)android_media_AudioRecord_readInArray<jshortArray>},
     {"native_read_in_float_array",
-                             "([FIIZ)I", (void *)android_media_AudioRecord_readInFloatArray},
-    {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
+                             "([FIIZ)I",
+                                     (void *)android_media_AudioRecord_readInArray<jfloatArray>},
+    {"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I",
                                        (void *)android_media_AudioRecord_readInDirectBuffer},
     {"native_get_native_frame_count",
                              "()I",    (void *)android_media_AudioRecord_get_native_frame_count},
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 9e1e055..3e771f4 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -901,21 +901,48 @@
      *    the parameters don't resolve to valid data and indexes.
      *    The number of bytes will not exceed sizeInBytes.
      */
-    public int read(byte[] audioData, int offsetInBytes, int sizeInBytes) {
-        if (mState != STATE_INITIALIZED) {
+    public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes) {
+        return read(audioData, offsetInBytes, sizeInBytes, READ_BLOCKING);
+    }
+
+    /**
+     * Reads audio data from the audio hardware for recording into a byte array.
+     * The format specified in the AudioRecord constructor should be
+     * {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array.
+     * @param audioData the array to which the recorded audio data is written.
+     * @param offsetInBytes index in audioData from which the data is written expressed in bytes.
+     * @param sizeInBytes the number of requested bytes.
+     * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
+     *     <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
+     *     is read.
+     *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
+     *     reading as much audio data as possible without blocking.
+     * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION}
+     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
+     *    the parameters don't resolve to valid data and indexes.
+     *    The number of bytes will not exceed sizeInBytes.
+     */
+    public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes,
+            @ReadMode int readMode) {
+        if (mState != STATE_INITIALIZED  || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
             return ERROR_INVALID_OPERATION;
         }
 
+        if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) {
+            Log.e(TAG, "AudioRecord.read() called with invalid blocking mode");
+            return ERROR_BAD_VALUE;
+        }
+
         if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0)
                 || (offsetInBytes + sizeInBytes < 0)  // detect integer overflow
                 || (offsetInBytes + sizeInBytes > audioData.length)) {
             return ERROR_BAD_VALUE;
         }
 
-        return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes);
+        return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes,
+                readMode == READ_BLOCKING);
     }
 
-
     /**
      * Reads audio data from the audio hardware for recording into a short array.
      * The format specified in the AudioRecord constructor should be
@@ -928,18 +955,46 @@
      *    the parameters don't resolve to valid data and indexes.
      *    The number of shorts will not exceed sizeInShorts.
      */
-    public int read(short[] audioData, int offsetInShorts, int sizeInShorts) {
-        if (mState != STATE_INITIALIZED) {
+    public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts) {
+        return read(audioData, offsetInShorts, sizeInShorts, READ_BLOCKING);
+    }
+
+    /**
+     * Reads audio data from the audio hardware for recording into a short array.
+     * The format specified in the AudioRecord constructor should be
+     * {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array.
+     * @param audioData the array to which the recorded audio data is written.
+     * @param offsetInShorts index in audioData from which the data is written expressed in shorts.
+     * @param sizeInShorts the number of requested shorts.
+     * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
+     *     <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
+     *     is read.
+     *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
+     *     reading as much audio data as possible without blocking.
+     * @return the number of shorts that were read or {@link #ERROR_INVALID_OPERATION}
+     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
+     *    the parameters don't resolve to valid data and indexes.
+     *    The number of shorts will not exceed sizeInShorts.
+     */
+    public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts,
+            @ReadMode int readMode) {
+        if (mState != STATE_INITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
             return ERROR_INVALID_OPERATION;
         }
 
+        if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) {
+            Log.e(TAG, "AudioRecord.read() called with invalid blocking mode");
+            return ERROR_BAD_VALUE;
+        }
+
         if ( (audioData == null) || (offsetInShorts < 0 ) || (sizeInShorts < 0)
                 || (offsetInShorts + sizeInShorts < 0)  // detect integer overflow
                 || (offsetInShorts + sizeInShorts > audioData.length)) {
             return ERROR_BAD_VALUE;
         }
 
-        return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts);
+        return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts,
+                readMode == READ_BLOCKING);
     }
 
     /**
@@ -950,22 +1005,33 @@
      * @param offsetInFloats index in audioData from which the data is written.
      * @param sizeInFloats the number of requested floats.
      * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
-     *     <BR>With {@link #READ_BLOCKING}, the read will block until all the requested data
+     *     <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
      *     is read.
-     *     <BR>With {@link #READ_NON_BLOCKING}, the read will return immediately after
+     *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
      *     reading as much audio data as possible without blocking.
      * @return the number of floats that were read or {@link #ERROR_INVALID_OPERATION}
      *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
      *    the parameters don't resolve to valid data and indexes.
      *    The number of floats will not exceed sizeInFloats.
      */
-    public int read(float[] audioData, int offsetInFloats, int sizeInFloats,
+    public int read(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats,
             @ReadMode int readMode) {
-        if (mState != STATE_INITIALIZED) {
+        if (mState == STATE_UNINITIALIZED) {
+            Log.e(TAG, "AudioRecord.read() called in invalid state STATE_UNINITIALIZED");
             return ERROR_INVALID_OPERATION;
         }
 
-        if ( (audioData == null) || (offsetInFloats < 0 ) || (sizeInFloats < 0)
+        if (mAudioFormat != AudioFormat.ENCODING_PCM_FLOAT) {
+            Log.e(TAG, "AudioRecord.read(float[] ...) requires format ENCODING_PCM_FLOAT");
+            return ERROR_INVALID_OPERATION;
+        }
+
+        if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) {
+            Log.e(TAG, "AudioRecord.read() called with invalid blocking mode");
+            return ERROR_BAD_VALUE;
+        }
+
+        if ((audioData == null) || (offsetInFloats < 0) || (sizeInFloats < 0)
                 || (offsetInFloats + sizeInFloats < 0)  // detect integer overflow
                 || (offsetInFloats + sizeInFloats > audioData.length)) {
             return ERROR_BAD_VALUE;
@@ -992,19 +1058,49 @@
      *    The number of bytes will not exceed sizeInBytes.
      *    The number of bytes read will truncated to be a multiple of the frame size.
      */
-    public int read(ByteBuffer audioBuffer, int sizeInBytes) {
+    public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes) {
+        return read(audioBuffer, sizeInBytes, READ_BLOCKING);
+    }
+
+    /**
+     * Reads audio data from the audio hardware for recording into a direct buffer. If this buffer
+     * is not a direct buffer, this method will always return 0.
+     * Note that the value returned by {@link java.nio.Buffer#position()} on this buffer is
+     * unchanged after a call to this method.
+     * The representation of the data in the buffer will depend on the format specified in
+     * the AudioRecord constructor, and will be native endian.
+     * @param audioBuffer the direct buffer to which the recorded audio data is written.
+     * @param sizeInBytes the number of requested bytes. It is recommended but not enforced
+     *    that the number of bytes requested be a multiple of the frame size (sample size in
+     *    bytes multiplied by the channel count).
+     * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
+     *     <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
+     *     is read.
+     *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
+     *     reading as much audio data as possible without blocking.
+     * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION}
+     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
+     *    the parameters don't resolve to valid data and indexes.
+     *    The number of bytes will not exceed sizeInBytes.
+     *    The number of bytes read will truncated to be a multiple of the frame size.
+     */
+    public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes, @ReadMode int readMode) {
         if (mState != STATE_INITIALIZED) {
             return ERROR_INVALID_OPERATION;
         }
 
+        if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) {
+            Log.e(TAG, "AudioRecord.read() called with invalid blocking mode");
+            return ERROR_BAD_VALUE;
+        }
+
         if ( (audioBuffer == null) || (sizeInBytes < 0) ) {
             return ERROR_BAD_VALUE;
         }
 
-        return native_read_in_direct_buffer(audioBuffer, sizeInBytes);
+        return native_read_in_direct_buffer(audioBuffer, sizeInBytes, readMode == READ_BLOCKING);
     }
 
-
     //--------------------------------------------------------------------------
     // Initialization / configuration
     //--------------------
@@ -1186,15 +1282,16 @@
     private native final void native_stop();
 
     private native final int native_read_in_byte_array(byte[] audioData,
-            int offsetInBytes, int sizeInBytes);
+            int offsetInBytes, int sizeInBytes, boolean isBlocking);
 
     private native final int native_read_in_short_array(short[] audioData,
-            int offsetInShorts, int sizeInShorts);
+            int offsetInShorts, int sizeInShorts, boolean isBlocking);
 
     private native final int native_read_in_float_array(float[] audioData,
             int offsetInFloats, int sizeInFloats, boolean isBlocking);
 
-    private native final int native_read_in_direct_buffer(Object jBuffer, int sizeInBytes);
+    private native final int native_read_in_direct_buffer(Object jBuffer,
+            int sizeInBytes, boolean isBlocking);
 
     private native final int native_get_native_frame_count();