Merge "Add floating point AudioRecord read"
diff --git a/api/current.txt b/api/current.txt
index 8457968..e2ac046 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14669,6 +14669,7 @@
     method public int getState();
     method public int read(byte[], int, int);
     method public int read(short[], int, int);
+    method public int read(float[], int, int, int);
     method public int read(java.nio.ByteBuffer, int);
     method public void release();
     method public int setNotificationMarkerPosition(int);
@@ -14681,6 +14682,8 @@
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
+    field public static final int READ_BLOCKING = 0; // 0x0
+    field public static final int READ_NON_BLOCKING = 1; // 0x1
     field public static final int RECORDSTATE_RECORDING = 3; // 0x3
     field public static final int RECORDSTATE_STOPPED = 1; // 0x1
     field public static final int STATE_INITIALIZED = 1; // 0x1
diff --git a/api/system-current.txt b/api/system-current.txt
index f70f705..162a37c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -15876,6 +15876,7 @@
     method public int getState();
     method public int read(byte[], int, int);
     method public int read(short[], int, int);
+    method public int read(float[], int, int, int);
     method public int read(java.nio.ByteBuffer, int);
     method public void release();
     method public int setNotificationMarkerPosition(int);
@@ -15888,6 +15889,8 @@
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
+    field public static final int READ_BLOCKING = 0; // 0x0
+    field public static final int READ_NON_BLOCKING = 1; // 0x1
     field public static final int RECORDSTATE_RECORDING = 3; // 0x3
     field public static final int RECORDSTATE_STOPPED = 1; // 0x1
     field public static final int STATE_INITIALIZED = 1; // 0x1
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index b2e8f16..ec2b590 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -441,6 +441,57 @@
 }
 
 // ----------------------------------------------------------------------------
+static jint android_media_AudioRecord_readInFloatArray(JNIEnv *env,  jobject thiz,
+                                                        jfloatArray javaAudioData,
+                                                        jint offsetInFloats, jint sizeInFloats,
+                                                        jboolean isReadBlocking) {
+    // 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");
+        return (jint)AUDIO_JAVA_INVALID_OPERATION;
+    }
+
+    if (javaAudioData == NULL) {
+         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);
+    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);
+
+    env->ReleaseFloatArrayElements(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 (jint)(readSize / sizeof(float));
+}
+
+// ----------------------------------------------------------------------------
 static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env,  jobject thiz,
                                                   jobject jBuffer, jint sizeInBytes) {
     // get the audio recorder from which we'll read new audio samples
@@ -574,6 +625,8 @@
                              "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
     {"native_read_in_short_array",
                              "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
+    {"native_read_in_float_array",
+                             "([FIIZ)I", (void *)android_media_AudioRecord_readInFloatArray},
     {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
                                        (void *)android_media_AudioRecord_readInDirectBuffer},
     {"native_set_marker_pos","(I)I",   (void *)android_media_AudioRecord_set_marker_pos},
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 99b7bee..b7a851f 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -16,10 +16,13 @@
 
 package android.media;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.nio.ByteBuffer;
 import java.util.Iterator;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Binder;
@@ -109,6 +112,26 @@
     /** @hide */
     public final static String SUBMIX_FIXED_VOLUME = "fixedVolume";
 
+    /** @hide */
+    @IntDef({
+        READ_BLOCKING,
+        READ_NON_BLOCKING
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ReadMode {}
+
+    /**
+     * The read mode indicating the read operation will block until all data
+     * requested has been read.
+     */
+    public final static int READ_BLOCKING = 0;
+
+    /**
+     * The read mode indicating the read operation will return immediately after
+     * reading as much audio data as possible without blocking.
+     */
+    public final static int READ_NON_BLOCKING = 1;
+
     //---------------------------------------------------------
     // Used exclusively by native code
     //--------------------
@@ -144,6 +167,7 @@
      * The encoding of the audio samples.
      * @see AudioFormat#ENCODING_PCM_8BIT
      * @see AudioFormat#ENCODING_PCM_16BIT
+     * @see AudioFormat#ENCODING_PCM_FLOAT
      */
     private int mAudioFormat;
     /**
@@ -212,9 +236,9 @@
      *   See {@link AudioFormat#CHANNEL_IN_MONO} and
      *   {@link AudioFormat#CHANNEL_IN_STEREO}.  {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed
      *   to work on all devices.
-     * @param audioFormat the format in which the audio data is represented.
-     *   See {@link AudioFormat#ENCODING_PCM_16BIT} and
-     *   {@link AudioFormat#ENCODING_PCM_8BIT}
+     * @param audioFormat the format in which the audio data is to be returned.
+     *   See {@link AudioFormat#ENCODING_PCM_8BIT}, {@link AudioFormat#ENCODING_PCM_16BIT},
+     *   and {@link AudioFormat#ENCODING_PCM_FLOAT}.
      * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written
      *   to during the recording. New audio data can be read from this buffer in smaller chunks
      *   than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum
@@ -559,13 +583,14 @@
         case AudioFormat.ENCODING_DEFAULT:
             mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
             break;
+        case AudioFormat.ENCODING_PCM_FLOAT:
         case AudioFormat.ENCODING_PCM_16BIT:
         case AudioFormat.ENCODING_PCM_8BIT:
             mAudioFormat = audioFormat;
             break;
         default:
             throw new IllegalArgumentException("Unsupported sample encoding."
-                    + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT.");
+                    + " Should be ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, or ENCODING_PCM_FLOAT.");
         }
     }
 
@@ -573,7 +598,8 @@
     // Convenience method for the contructor's audio buffer size check.
     // preconditions:
     //    mChannelCount is valid
-    //    mAudioFormat is AudioFormat.ENCODING_PCM_8BIT OR AudioFormat.ENCODING_PCM_16BIT
+    //    mAudioFormat is AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT,
+    //                 or AudioFormat.ENCODING_PCM_FLOAT
     // postcondition:
     //    mNativeBufferSizeInBytes is valid (multiple of frame size, positive)
     private void audioBuffSizeCheck(int audioBufferSize) throws IllegalArgumentException {
@@ -632,8 +658,8 @@
     }
 
     /**
-     * Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_16BIT}
-     * and {@link AudioFormat#ENCODING_PCM_8BIT}.
+     * Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_8BIT},
+     * {@link AudioFormat#ENCODING_PCM_16BIT}, and {@link AudioFormat#ENCODING_PCM_FLOAT}.
      */
     public int getAudioFormat() {
         return mAudioFormat;
@@ -732,12 +758,6 @@
             return ERROR_BAD_VALUE;
         }
 
-        // PCM_8BIT is not supported at the moment
-        if (audioFormat != AudioFormat.ENCODING_PCM_16BIT) {
-            loge("getMinBufferSize(): Invalid audio format.");
-            return ERROR_BAD_VALUE;
-        }
-
         int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
         if (size == 0) {
             return ERROR_BAD_VALUE;
@@ -841,11 +861,13 @@
     // Audio data supply
     //--------------------
     /**
-     * Reads audio data from the audio hardware for recording into a buffer.
+     * 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.
-     * @return the number of bytes that were read or or {@link #ERROR_INVALID_OPERATION}
+     * @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.
@@ -866,11 +888,13 @@
 
 
     /**
-     * Reads audio data from the audio hardware for recording into a buffer.
+     * 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.
-     * @return the number of shorts that were read or or {@link #ERROR_INVALID_OPERATION}
+     * @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.
@@ -889,18 +913,55 @@
         return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts);
     }
 
+    /**
+     * Reads audio data from the audio hardware for recording into a float array.
+     * The format specified in the AudioRecord constructor should be
+     * {@link AudioFormat#ENCODING_PCM_FLOAT} to correspond to the data in the array.
+     * @param audioData the array to which the recorded audio data is written.
+     * @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
+     *     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 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,
+            @ReadMode int readMode) {
+        if (mState != STATE_INITIALIZED) {
+            return ERROR_INVALID_OPERATION;
+        }
+
+        if ( (audioData == null) || (offsetInFloats < 0 ) || (sizeInFloats < 0)
+                || (offsetInFloats + sizeInFloats < 0)  // detect integer overflow
+                || (offsetInFloats + sizeInFloats > audioData.length)) {
+            return ERROR_BAD_VALUE;
+        }
+
+        return native_read_in_float_array(audioData, offsetInFloats, sizeInFloats,
+                readMode == 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.
-     * @return the number of bytes that were read or or {@link #ERROR_INVALID_OPERATION}
+     * @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).
+     * @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(ByteBuffer audioBuffer, int sizeInBytes) {
         if (mState != STATE_INITIALIZED) {
@@ -1101,6 +1162,9 @@
     private native final int native_read_in_short_array(short[] audioData,
             int offsetInShorts, int sizeInShorts);
 
+    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_set_marker_pos(int marker);