libaaudio: implement callback

Use AudioTrack and AudioRecord TRANSFER_CALLBACK.
Add FixedBlockAdapter to provide fixed size callbacks.

Bug: 36489240
Test: CTS test_aaudio.cpp
Change-Id: Id2034dd640f878dd27fee6b43ad80a01c627dfd6
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/media/libaaudio/src/utility/FixedBlockAdapter.cpp b/media/libaaudio/src/utility/FixedBlockAdapter.cpp
new file mode 100644
index 0000000..f4666af
--- /dev/null
+++ b/media/libaaudio/src/utility/FixedBlockAdapter.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include "FixedBlockAdapter.h"
+
+FixedBlockAdapter::~FixedBlockAdapter() {
+    close();
+}
+
+int32_t FixedBlockAdapter::open(int32_t bytesPerFixedBlock)
+{
+    mSize = bytesPerFixedBlock;
+    mStorage = new uint8_t[bytesPerFixedBlock]; // TODO use std::nothrow
+    mPosition = 0;
+    return 0;
+}
+
+int32_t FixedBlockAdapter::close()
+{
+    delete[] mStorage;
+    mStorage = nullptr;
+    mSize = 0;
+    mPosition = 0;
+    return 0;
+}
diff --git a/media/libaaudio/src/utility/FixedBlockAdapter.h b/media/libaaudio/src/utility/FixedBlockAdapter.h
new file mode 100644
index 0000000..7008b25
--- /dev/null
+++ b/media/libaaudio/src/utility/FixedBlockAdapter.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAUDIO_FIXED_BLOCK_ADAPTER_H
+#define AAUDIO_FIXED_BLOCK_ADAPTER_H
+
+#include <stdio.h>
+
+/**
+ * Interface for a class that needs fixed-size blocks.
+ */
+class FixedBlockProcessor {
+public:
+    virtual ~FixedBlockProcessor() = default;
+    virtual int32_t onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) = 0;
+};
+
+/**
+ * Base class for a variable-to-fixed-size block adapter.
+ */
+class FixedBlockAdapter
+{
+public:
+    FixedBlockAdapter(FixedBlockProcessor &fixedBlockProcessor)
+    : mFixedBlockProcessor(fixedBlockProcessor) {}
+
+    virtual ~FixedBlockAdapter();
+
+    /**
+     * Allocate internal resources needed for buffering data.
+     */
+    virtual int32_t open(int32_t bytesPerFixedBlock);
+
+    /**
+     * Note that if the fixed-sized blocks must be aligned, then the variable-sized blocks
+     * must have the same alignment.
+     * For example, if the fixed-size blocks must be a multiple of 8, then the variable-sized
+     * blocks must also be a multiple of 8.
+     *
+     * @param buffer
+     * @param numBytes
+     * @return zero if OK or a non-zero code
+     */
+    virtual int32_t processVariableBlock(uint8_t *buffer, int32_t numBytes) = 0;
+
+    /**
+     * Free internal resources.
+     */
+    int32_t close();
+
+protected:
+    FixedBlockProcessor  &mFixedBlockProcessor;
+    uint8_t              *mStorage = nullptr;    // Store data here while assembling buffers.
+    int32_t               mSize = 0;             // Size in bytes of the fixed size buffer.
+    int32_t               mPosition = 0;         // Offset of the last byte read or written.
+};
+
+#endif /* AAUDIO_FIXED_BLOCK_ADAPTER_H */
diff --git a/media/libaaudio/src/utility/FixedBlockReader.cpp b/media/libaaudio/src/utility/FixedBlockReader.cpp
new file mode 100644
index 0000000..21ea70e
--- /dev/null
+++ b/media/libaaudio/src/utility/FixedBlockReader.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <memory.h>
+
+#include "FixedBlockAdapter.h"
+
+#include "FixedBlockReader.h"
+
+
+FixedBlockReader::FixedBlockReader(FixedBlockProcessor &fixedBlockProcessor)
+    : FixedBlockAdapter(fixedBlockProcessor) {
+    mPosition = mSize;
+}
+
+int32_t FixedBlockReader::open(int32_t bytesPerFixedBlock) {
+    int32_t result = FixedBlockAdapter::open(bytesPerFixedBlock);
+    mPosition = mSize; // Indicate no data in storage.
+    return result;
+}
+
+int32_t FixedBlockReader::readFromStorage(uint8_t *buffer, int32_t numBytes) {
+    int32_t bytesToRead = numBytes;
+    int32_t dataAvailable = mSize - mPosition;
+    if (bytesToRead > dataAvailable) {
+        bytesToRead = dataAvailable;
+    }
+    memcpy(buffer, mStorage + mPosition, bytesToRead);
+    mPosition += bytesToRead;
+    return bytesToRead;
+}
+
+int32_t FixedBlockReader::processVariableBlock(uint8_t *buffer, int32_t numBytes) {
+    int32_t result = 0;
+    int32_t bytesLeft = numBytes;
+    while(bytesLeft > 0 && result == 0) {
+        if (mPosition < mSize) {
+            // Use up bytes currently in storage.
+            int32_t bytesRead = readFromStorage(buffer, bytesLeft);
+            buffer += bytesRead;
+            bytesLeft -= bytesRead;
+        } else if (bytesLeft >= mSize) {
+            // Read through if enough for a complete block.
+            result = mFixedBlockProcessor.onProcessFixedBlock(buffer, mSize);
+            buffer += mSize;
+            bytesLeft -= mSize;
+        } else {
+            // Just need a partial block so we have to use storage.
+            result = mFixedBlockProcessor.onProcessFixedBlock(mStorage, mSize);
+            mPosition = 0;
+        }
+    }
+    return result;
+}
+
diff --git a/media/libaaudio/src/utility/FixedBlockReader.h b/media/libaaudio/src/utility/FixedBlockReader.h
new file mode 100644
index 0000000..128dd52
--- /dev/null
+++ b/media/libaaudio/src/utility/FixedBlockReader.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAUDIO_FIXED_BLOCK_READER_H
+#define AAUDIO_FIXED_BLOCK_READER_H
+
+#include <stdint.h>
+
+#include "FixedBlockAdapter.h"
+
+/**
+ * Read from a fixed-size block to a variable sized block.
+ *
+ * This can be used to convert a pull data flow from fixed sized buffers to variable sized buffers.
+ * An example would be an audio output callback that reads from the app.
+ */
+class FixedBlockReader : public FixedBlockAdapter
+{
+public:
+    FixedBlockReader(FixedBlockProcessor &fixedBlockProcessor);
+
+    virtual ~FixedBlockReader() = default;
+
+    int32_t open(int32_t bytesPerFixedBlock) override;
+
+    int32_t readFromStorage(uint8_t *buffer, int32_t numBytes);
+
+    /**
+     * Read into a variable sized block.
+     */
+    int32_t processVariableBlock(uint8_t *buffer, int32_t numBytes) override;
+};
+
+
+#endif /* AAUDIO_FIXED_BLOCK_READER_H */
diff --git a/media/libaaudio/src/utility/FixedBlockWriter.cpp b/media/libaaudio/src/utility/FixedBlockWriter.cpp
new file mode 100644
index 0000000..2ce8046
--- /dev/null
+++ b/media/libaaudio/src/utility/FixedBlockWriter.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <memory.h>
+
+#include "FixedBlockAdapter.h"
+#include "FixedBlockWriter.h"
+
+FixedBlockWriter::FixedBlockWriter(FixedBlockProcessor &fixedBlockProcessor)
+        : FixedBlockAdapter(fixedBlockProcessor) {}
+
+
+int32_t FixedBlockWriter::writeToStorage(uint8_t *buffer, int32_t numBytes) {
+    int32_t bytesToStore = numBytes;
+    int32_t roomAvailable = mSize - mPosition;
+    if (bytesToStore > roomAvailable) {
+        bytesToStore = roomAvailable;
+    }
+    memcpy(mStorage + mPosition, buffer, bytesToStore);
+    mPosition += bytesToStore;
+    return bytesToStore;
+}
+
+int32_t FixedBlockWriter::processVariableBlock(uint8_t *buffer, int32_t numBytes) {
+    int32_t result = 0;
+    int32_t bytesLeft = numBytes;
+
+    // If we already have data in storage then add to it.
+    if (mPosition > 0) {
+        int32_t bytesWritten = writeToStorage(buffer, bytesLeft);
+        buffer += bytesWritten;
+        bytesLeft -= bytesWritten;
+        // If storage full then flush it out
+        if (mPosition == mSize) {
+            result = mFixedBlockProcessor.onProcessFixedBlock(mStorage, mSize);
+            mPosition = 0;
+        }
+    }
+
+    // Write through if enough for a complete block.
+    while(bytesLeft > mSize && result == 0) {
+        result = mFixedBlockProcessor.onProcessFixedBlock(buffer, mSize);
+        buffer += mSize;
+        bytesLeft -= mSize;
+    }
+
+    // Save any remaining partial block for next time.
+    if (bytesLeft > 0) {
+        writeToStorage(buffer, bytesLeft);
+    }
+
+    return result;
+}
diff --git a/media/libaaudio/src/utility/FixedBlockWriter.h b/media/libaaudio/src/utility/FixedBlockWriter.h
new file mode 100644
index 0000000..f1d917c
--- /dev/null
+++ b/media/libaaudio/src/utility/FixedBlockWriter.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAUDIO_FIXED_BLOCK_WRITER_H
+#define AAUDIO_FIXED_BLOCK_WRITER_H
+
+#include <stdint.h>
+
+#include "FixedBlockAdapter.h"
+
+/**
+ * This can be used to convert a push data flow from variable sized buffers to fixed sized buffers.
+ * An example would be an audio input callback.
+ */
+class FixedBlockWriter : public FixedBlockAdapter
+{
+public:
+    FixedBlockWriter(FixedBlockProcessor &fixedBlockProcessor);
+
+    virtual ~FixedBlockWriter() = default;
+
+    int32_t writeToStorage(uint8_t *buffer, int32_t numBytes);
+
+    /**
+     * Write from a variable sized block.
+     */
+    int32_t processVariableBlock(uint8_t *buffer, int32_t numBytes) override;
+};
+
+#endif /* AAUDIO_FIXED_BLOCK_WRITER_H */