Implemented OpenSL-ES audio recording on Android
diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
index 9b872cd..6ee101e 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
@@ -783,6 +783,7 @@
     public static native void nativeSetenv(String name, String value);
     public static native void onNativeOrientationChanged(int orientation);
     public static native void nativeAddTouch(int touchId, String name);
+    public static native void nativePermissionResult(int requestCode, boolean result);
 
     /**
      * This method is called by SDL using JNI.
@@ -1600,6 +1601,42 @@
         }
         return true;
     }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void requestPermission(String permission, int requestCode) {
+        if (mSingleton != null) {
+            mSingleton.checkPermission(permission, requestCode);
+        } else {
+            nativePermissionResult(requestCode, false);
+        }
+    }
+
+    /**
+     * This can be overridden
+     */
+    public void checkPermission(String permission, int requestCode) {
+        if (Build.VERSION.SDK_INT < 23) {
+            nativePermissionResult(requestCode, true);
+            return;
+        }
+
+        if (this.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
+            this.requestPermissions(new String[]{permission}, requestCode);
+        } else {
+            nativePermissionResult(requestCode, true);
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+            nativePermissionResult(requestCode, true);
+        } else {
+            nativePermissionResult(requestCode, false);
+        }
+    }
 }
 
 /**
diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index 532998d..3137493 100644
--- a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -1076,7 +1076,7 @@
         return NULL;
     }
 
-    if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
+    if (iscapture && !current_audio.impl.HasCaptureSupport) {
         SDL_SetError("No capture support");
         return NULL;
     }
@@ -1230,7 +1230,7 @@
         return 0;
     }
 
-    if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
+    if (iscapture && !current_audio.impl.HasCaptureSupport) {
         SDL_SetError("No capture support");
         return 0;
     }
diff --git a/src/audio/openslES/SDL_openslES.c b/src/audio/openslES/SDL_openslES.c
index 0936524..08189dd 100644
--- a/src/audio/openslES/SDL_openslES.c
+++ b/src/audio/openslES/SDL_openslES.c
@@ -26,8 +26,10 @@
    https://googlesamples.github.io/android-audio-high-performance/guides/opensl_es.html
 */
 
+#include "SDL_assert.h"
 #include "SDL_audio.h"
 #include "../SDL_audio_c.h"
+#include "../../core/android/SDL_android.h"
 #include "SDL_openslES.h"
 
 /* for native audio */
@@ -48,42 +50,50 @@
 #define LOGV(...)
 #endif
 
+/*
+#define SL_SPEAKER_FRONT_LEFT            ((SLuint32) 0x00000001)
+#define SL_SPEAKER_FRONT_RIGHT           ((SLuint32) 0x00000002)
+#define SL_SPEAKER_FRONT_CENTER          ((SLuint32) 0x00000004)
+#define SL_SPEAKER_LOW_FREQUENCY         ((SLuint32) 0x00000008)
+#define SL_SPEAKER_BACK_LEFT             ((SLuint32) 0x00000010)
+#define SL_SPEAKER_BACK_RIGHT            ((SLuint32) 0x00000020)
+#define SL_SPEAKER_FRONT_LEFT_OF_CENTER  ((SLuint32) 0x00000040)
+#define SL_SPEAKER_FRONT_RIGHT_OF_CENTER ((SLuint32) 0x00000080)
+#define SL_SPEAKER_BACK_CENTER           ((SLuint32) 0x00000100)
+#define SL_SPEAKER_SIDE_LEFT             ((SLuint32) 0x00000200)
+#define SL_SPEAKER_SIDE_RIGHT            ((SLuint32) 0x00000400)
+#define SL_SPEAKER_TOP_CENTER            ((SLuint32) 0x00000800)
+#define SL_SPEAKER_TOP_FRONT_LEFT        ((SLuint32) 0x00001000)
+#define SL_SPEAKER_TOP_FRONT_CENTER      ((SLuint32) 0x00002000)
+#define SL_SPEAKER_TOP_FRONT_RIGHT       ((SLuint32) 0x00004000)
+#define SL_SPEAKER_TOP_BACK_LEFT         ((SLuint32) 0x00008000)
+#define SL_SPEAKER_TOP_BACK_CENTER       ((SLuint32) 0x00010000)
+#define SL_SPEAKER_TOP_BACK_RIGHT        ((SLuint32) 0x00020000)
+*/
+#define SL_ANDROID_SPEAKER_STEREO (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT)
+#define SL_ANDROID_SPEAKER_QUAD (SL_ANDROID_SPEAKER_STEREO | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT)
+#define SL_ANDROID_SPEAKER_5DOT1 (SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER  | SL_SPEAKER_LOW_FREQUENCY)
+#define SL_ANDROID_SPEAKER_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT)
+
 /* engine interfaces */
-static SLObjectItf engineObject = NULL;
-static SLEngineItf engineEngine = NULL;
+static SLObjectItf engineObject;
+static SLEngineItf engineEngine;
 
 /* output mix interfaces */
-static SLObjectItf outputMixObject = NULL;
-// static SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;
-
-/* aux effect on the output mix, used by the buffer queue player */
-/* static const SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR; */
+static SLObjectItf outputMixObject;
 
 /* buffer queue player interfaces */
-static SLObjectItf                   bqPlayerObject      = NULL;
-static SLPlayItf                     bqPlayerPlay        = NULL;
-static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
+static SLObjectItf bqPlayerObject;
+static SLPlayItf bqPlayerPlay;
+static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
 #if 0
-static SLEffectSendItf               bqPlayerEffectSend  = NULL;
-static SLMuteSoloItf                 bqPlayerMuteSolo    = NULL;
-static SLVolumeItf                   bqPlayerVolume      = NULL;
+static SLVolumeItf bqPlayerVolume;
 #endif
 
-#if 0
-/* recorder interfaces TODO */
-static SLObjectItf                   recorderObject = NULL;
-static SLRecordItf                   recorderRecord;
+/* recorder interfaces */
+static SLObjectItf recorderObject;
+static SLRecordItf recorderRecord;
 static SLAndroidSimpleBufferQueueItf recorderBufferQueue;
-#endif
-
-/* pointer and size of the next player buffer to enqueue, and number of remaining buffers */
-#if 0
-static short      *nextBuffer;
-static unsigned    nextSize;
-static int         nextCount;
-#endif
-
-// static SDL_AudioDevice* audioDevice = NULL;
 
 #if 0
 static const char *sldevaudiorecorderstr = "SLES Audio Recorder";
@@ -93,19 +103,34 @@
 #define  SLES_DEV_AUDIO_PLAYER  sldevaudioplayerstr
 static void openslES_DetectDevices( int iscapture )
 {
-  LOGI( "openSLES_DetectDevices()" );
+    LOGI( "openSLES_DetectDevices()" );
     if ( iscapture )
             addfn( SLES_DEV_AUDIO_RECORDER );
-  else
+    else
             addfn( SLES_DEV_AUDIO_PLAYER );
-  return;
 }
 #endif
 
-static void openslES_DestroyEngine(void);
+static void openslES_DestroyEngine(void)
+{
+    LOGI("openslES_DestroyEngine()");
+
+    /* destroy output mix object, and invalidate all associated interfaces */
+    if (outputMixObject != NULL) {
+        (*outputMixObject)->Destroy(outputMixObject);
+        outputMixObject = NULL;
+    }
+
+    /* destroy engine object, and invalidate all associated interfaces */
+    if (engineObject != NULL) {
+        (*engineObject)->Destroy(engineObject);
+        engineObject = NULL;
+        engineEngine = NULL;
+    }
+}
 
 static int
-openslES_CreateEngine()
+openslES_CreateEngine(void)
 {
     SLresult result;
 
@@ -114,40 +139,33 @@
     /* create engine */
     result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
     if (SL_RESULT_SUCCESS != result) {
-        LOGE("slCreateEngine failed");
+        LOGE("slCreateEngine failed: %d", result);
         goto error;
     }
-
     LOGI("slCreateEngine OK");
 
     /* realize the engine */
     result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
     if (SL_RESULT_SUCCESS != result) {
-        LOGE("RealizeEngine failed");
+        LOGE("RealizeEngine failed: %d", result);
         goto error;
     }
-
     LOGI("RealizeEngine OK");
 
     /* get the engine interface, which is needed in order to create other objects */
     result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
     if (SL_RESULT_SUCCESS != result) {
-        LOGE("EngineGetInterface failed");
+        LOGE("EngineGetInterface failed: %d", result);
         goto error;
     }
-
     LOGI("EngineGetInterface OK");
 
-    /* create output mix, with environmental reverb specified as a non-required interface */
-    /* const SLInterfaceID ids[1] = { SL_IID_ENVIRONMENTALREVERB }; */
-    /* const SLboolean req[1] = { SL_BOOLEAN_FALSE }; */
-
+    /* create output mix */
     const SLInterfaceID ids[1] = { SL_IID_VOLUME };
     const SLboolean req[1] = { SL_BOOLEAN_FALSE };
     result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
-
     if (SL_RESULT_SUCCESS != result) {
-        LOGE("CreateOutputMix failed");
+        LOGE("CreateOutputMix failed: %d", result);
         goto error;
     }
     LOGI("CreateOutputMix OK");
@@ -155,7 +173,7 @@
     /* realize the output mix */
     result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
     if (SL_RESULT_SUCCESS != result) {
-        LOGE("RealizeOutputMix failed");
+        LOGE("RealizeOutputMix failed: %d", result);
         goto error;
     }
     return 1;
@@ -165,31 +183,181 @@
     return 0;
 }
 
-static void openslES_DestroyPCMPlayer(_THIS);
-static void openslES_DestroyPCMRecorder(_THIS);
-
-static void openslES_DestroyEngine()
+/* this callback handler is called every time a buffer finishes recording */
+static void
+bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
 {
-    LOGI("openslES_DestroyEngine()");
+    struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) context;
 
-//        openslES_DestroyPCMPlayer(this);
-//    openslES_DestroyPCMRecorder(this);
+    LOGV("SLES: Recording Callback");
+    SDL_SemPost(audiodata->playsem);
+}
 
-    /* destroy output mix object, and invalidate all associated interfaces */
-    if (outputMixObject != NULL) {
-        (*outputMixObject)->Destroy(outputMixObject);
-        outputMixObject = NULL;
-        /* outputMixEnvironmentalReverb = NULL; */
+static void
+openslES_DestroyPCMRecorder(_THIS)
+{
+    struct SDL_PrivateAudioData *audiodata = this->hidden;
+    SLresult result;
+
+    /* stop recording */
+    if (recorderRecord != NULL) {
+        result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
+        if (SL_RESULT_SUCCESS != result) {
+            LOGE("SetRecordState stopped: %d", result);
+        }
     }
 
-    /* destroy engine object, and invalidate all associated interfaces */
-    if (engineObject != NULL) {
-        (*engineObject)->Destroy(engineObject);
-        engineObject = NULL;
-        engineEngine = NULL;
+    /* destroy audio recorder object, and invalidate all associated interfaces */
+    if (recorderObject != NULL) {
+        (*recorderObject)->Destroy(recorderObject);
+        recorderObject = NULL;
+        recorderRecord = NULL;
+        recorderBufferQueue = NULL;
     }
 
-    return;
+    if (audiodata->playsem) {
+        SDL_DestroySemaphore(audiodata->playsem);
+        audiodata->playsem = NULL;
+    }
+
+    if (audiodata->mixbuff) {
+        SDL_free(audiodata->mixbuff);
+    }
+}
+
+static int
+openslES_CreatePCMRecorder(_THIS)
+{
+    struct SDL_PrivateAudioData *audiodata = this->hidden;
+    SLDataFormat_PCM format_pcm;
+    SLresult result;
+    int i;
+
+    if (!Android_JNI_RequestPermission("android.permission.RECORD_AUDIO")) {
+        return SDL_SetError("This app doesn't have RECORD_AUDIO permission");
+    }
+
+    /* Just go with signed 16-bit audio as it's the most compatible */
+    this->spec.format = AUDIO_S16SYS;
+    this->spec.channels = 1;
+    /*this->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/
+
+    /* Update the fragment size as size in bytes */
+    SDL_CalculateAudioSpec(&this->spec);
+
+    LOGI("Try to open %u hz %u bit chan %u %s samples %u",
+          this->spec.freq, SDL_AUDIO_BITSIZE(this->spec.format),
+          this->spec.channels, (this->spec.format & 0x1000) ? "BE" : "LE", this->spec.samples);
+
+    /* configure audio source */
+    SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
+    SLDataSource audioSrc = {&loc_dev, NULL};
+
+    /* configure audio sink */
+    SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, NUM_BUFFERS };
+
+    format_pcm.formatType    = SL_DATAFORMAT_PCM;
+    format_pcm.numChannels   = this->spec.channels;
+    format_pcm.samplesPerSec = this->spec.freq * 1000;  /* / kilo Hz to milli Hz */
+    format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
+    format_pcm.containerSize = SDL_AUDIO_BITSIZE(this->spec.format);
+    format_pcm.endianness    = SL_BYTEORDER_LITTLEENDIAN;
+    format_pcm.channelMask   = SL_SPEAKER_FRONT_CENTER;
+
+    SLDataSink audioSnk = { &loc_bufq, &format_pcm };
+
+    /* create audio recorder */
+    /* (requires the RECORD_AUDIO permission) */
+    const SLInterfaceID ids[1] = {
+        SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+    };
+    const SLboolean req[1] = {
+        SL_BOOLEAN_TRUE,
+    };
+
+    result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc, &audioSnk, 1, ids, req);
+    if (SL_RESULT_SUCCESS != result) {
+        LOGE("CreateAudioRecorder failed: %d", result);
+        goto failed;
+    }
+
+    /* realize the recorder */
+    result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
+    if (SL_RESULT_SUCCESS != result) {
+        LOGE("RealizeAudioPlayer failed: %d", result);
+        goto failed;
+    }
+
+    /* get the record interface */
+    result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
+    if (SL_RESULT_SUCCESS != result) {
+        LOGE("SL_IID_RECORD interface get failed: %d", result);
+        goto failed;
+    }
+
+    /* get the buffer queue interface */
+    result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue);
+    if (SL_RESULT_SUCCESS != result) {
+        LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
+        goto failed;
+    }
+
+    /* register callback on the buffer queue */
+    /* context is '(SDL_PrivateAudioData *)this->hidden' */
+    result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback, this->hidden);
+    if (SL_RESULT_SUCCESS != result) {
+        LOGE("RegisterCallback failed: %d", result);
+        goto failed;
+    }
+
+    /* Create the audio buffer semaphore */
+    audiodata->playsem = SDL_CreateSemaphore(0);
+    if (!audiodata->playsem) {
+        LOGE("cannot create Semaphore!");
+        goto failed;
+    }
+
+    /* Create the sound buffers */
+    audiodata->mixbuff = (Uint8 *) SDL_malloc(NUM_BUFFERS * this->spec.size);
+    if (audiodata->mixbuff == NULL) {
+        LOGE("mixbuffer allocate - out of memory");
+        goto failed;
+    }
+
+    for (i = 0; i < NUM_BUFFERS; i++) {
+        audiodata->pmixbuff[i] = audiodata->mixbuff + i * this->spec.size;
+    }
+
+    /* in case already recording, stop recording and clear buffer queue */
+    result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
+    if (SL_RESULT_SUCCESS != result) {
+        LOGE("Record set state failed: %d", result);
+        goto failed;
+    }
+
+    /* enqueue empty buffers to be filled by the recorder */
+    for (i = 0; i < NUM_BUFFERS; i++) {
+        result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[i], this->spec.size);
+        if (SL_RESULT_SUCCESS != result) {
+            LOGE("Record enqueue buffers failed: %d", result);
+            goto failed;
+        }
+    }
+
+    /* start recording */
+    result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
+    if (SL_RESULT_SUCCESS != result) {
+        LOGE("Record set state failed: %d", result);
+        goto failed;
+    }
+
+    return 0;
+
+failed:
+
+    openslES_DestroyPCMRecorder(this);
+
+    return SDL_SetError("Open device failed!");
 }
 
 /* this callback handler is called every time a buffer finishes playing */
@@ -197,32 +365,49 @@
 bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
 {
     struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) context;
-    LOGV("SLES: Playback Callmeback");
+
+    LOGV("SLES: Playback Callback");
     SDL_SemPost(audiodata->playsem);
-    return;
-}
-
-static int
-openslES_CreatePCMRecorder(_THIS)
-{
-/*    struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; */
-
-    LOGE("openslES_CreatePCMRecorder not implimented yet!");
-    return SDL_SetError("openslES_CreatePCMRecorder not implimented yet!");
 }
 
 static void
-openslES_DestroyPCMRecorder(_THIS)
+openslES_DestroyPCMPlayer(_THIS)
 {
-/*    struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; */
+    struct SDL_PrivateAudioData *audiodata = this->hidden;
+    SLresult result;
 
-    return;
+    /* set the player's state to 'stopped' */
+    if (bqPlayerPlay != NULL) {
+        result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
+        if (SL_RESULT_SUCCESS != result) {
+            LOGE("SetPlayState stopped failed: %d", result);
+        }
+    }
+
+    /* destroy buffer queue audio player object, and invalidate all associated interfaces */
+    if (bqPlayerObject != NULL) {
+
+        (*bqPlayerObject)->Destroy(bqPlayerObject);
+
+        bqPlayerObject = NULL;
+        bqPlayerPlay = NULL;
+        bqPlayerBufferQueue = NULL;
+    }
+
+    if (audiodata->playsem) {
+        SDL_DestroySemaphore(audiodata->playsem);
+        audiodata->playsem = NULL;
+    }
+
+    if (audiodata->mixbuff) {
+        SDL_free(audiodata->mixbuff);
+    }
 }
 
 static int
 openslES_CreatePCMPlayer(_THIS)
 {
-    struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden;
+    struct SDL_PrivateAudioData *audiodata = this->hidden;
     SLDataFormat_PCM format_pcm;
     SLresult result;
     int i;
@@ -273,31 +458,6 @@
         format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
     }
 
-/*
-#define SL_SPEAKER_FRONT_LEFT            ((SLuint32) 0x00000001)
-#define SL_SPEAKER_FRONT_RIGHT           ((SLuint32) 0x00000002)
-#define SL_SPEAKER_FRONT_CENTER          ((SLuint32) 0x00000004)
-#define SL_SPEAKER_LOW_FREQUENCY         ((SLuint32) 0x00000008)
-#define SL_SPEAKER_BACK_LEFT             ((SLuint32) 0x00000010)
-#define SL_SPEAKER_BACK_RIGHT            ((SLuint32) 0x00000020)
-#define SL_SPEAKER_FRONT_LEFT_OF_CENTER  ((SLuint32) 0x00000040)
-#define SL_SPEAKER_FRONT_RIGHT_OF_CENTER ((SLuint32) 0x00000080)
-#define SL_SPEAKER_BACK_CENTER           ((SLuint32) 0x00000100)
-#define SL_SPEAKER_SIDE_LEFT             ((SLuint32) 0x00000200)
-#define SL_SPEAKER_SIDE_RIGHT            ((SLuint32) 0x00000400)
-#define SL_SPEAKER_TOP_CENTER            ((SLuint32) 0x00000800)
-#define SL_SPEAKER_TOP_FRONT_LEFT        ((SLuint32) 0x00001000)
-#define SL_SPEAKER_TOP_FRONT_CENTER      ((SLuint32) 0x00002000)
-#define SL_SPEAKER_TOP_FRONT_RIGHT       ((SLuint32) 0x00004000)
-#define SL_SPEAKER_TOP_BACK_LEFT         ((SLuint32) 0x00008000)
-#define SL_SPEAKER_TOP_BACK_CENTER       ((SLuint32) 0x00010000)
-#define SL_SPEAKER_TOP_BACK_RIGHT        ((SLuint32) 0x00020000)
-*/
-#define SL_ANDROID_SPEAKER_STEREO (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT)
-#define SL_ANDROID_SPEAKER_QUAD (SL_ANDROID_SPEAKER_STEREO | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT)
-#define SL_ANDROID_SPEAKER_5DOT1 (SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER  | SL_SPEAKER_LOW_FREQUENCY)
-#define SL_ANDROID_SPEAKER_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT)
-
     switch (this->spec.channels)
     {
     case 1:
@@ -350,28 +510,28 @@
 
     result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
     if (SL_RESULT_SUCCESS != result) {
-        LOGE("CreateAudioPlayer failed");
+        LOGE("CreateAudioPlayer failed: %d", result);
         goto failed;
     }
 
     /* realize the player */
     result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
     if (SL_RESULT_SUCCESS != result) {
-        LOGE("RealizeAudioPlayer failed");
+        LOGE("RealizeAudioPlayer failed: %d", result);
         goto failed;
     }
 
     /* get the play interface */
     result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
     if (SL_RESULT_SUCCESS != result) {
-        LOGE("SL_IID_PLAY interface get failed");
+        LOGE("SL_IID_PLAY interface get failed: %d", result);
         goto failed;
     }
 
     /* get the buffer queue interface */
     result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bqPlayerBufferQueue);
     if (SL_RESULT_SUCCESS != result) {
-        LOGE("SL_IID_BUFFERQUEUE interface get failed");
+        LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
         goto failed;
     }
 
@@ -379,33 +539,15 @@
     /* context is '(SDL_PrivateAudioData *)this->hidden' */
     result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, this->hidden);
     if (SL_RESULT_SUCCESS != result) {
-        LOGE("RegisterCallback failed");
+        LOGE("RegisterCallback failed: %d", result);
         goto failed;
     }
 
 #if 0
-    /* get the effect send interface */
-    result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_EFFECTSEND, &bqPlayerEffectSend);
-    if (SL_RESULT_SUCCESS != result)
-    {
-
-        LOGE("SL_IID_EFFECTSEND interface get failed");
-        goto failed;
-    }
-#endif
-
-#if 0   /* mute/solo is not supported for sources that are known to be mono, as this is */
-    /* get the mute/solo interface */
-    result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_MUTESOLO, &bqPlayerMuteSolo);
-    assert(SL_RESULT_SUCCESS == result);
-    (void) result;
-#endif
-
-#if 0
     /* get the volume interface */
     result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
     if (SL_RESULT_SUCCESS != result) {
-        LOGE("SL_IID_VOLUME interface get failed");
+        LOGE("SL_IID_VOLUME interface get failed: %d", result);
         /* goto failed; */
     }
 #endif
@@ -431,7 +573,7 @@
     /* set the player's state to playing */
     result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
     if (SL_RESULT_SUCCESS != result) {
-        LOGE("Play set state failed");
+        LOGE("Play set state failed: %d", result);
         goto failed;
     }
 
@@ -444,47 +586,6 @@
     return SDL_SetError("Open device failed!");
 }
 
-static void
-openslES_DestroyPCMPlayer(_THIS)
-{
-    struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden;
-    SLresult result;
-
-    /* set the player's state to 'stopped' */
-    if (bqPlayerPlay != NULL) {
-        result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
-        if (SL_RESULT_SUCCESS != result) {
-            SDL_SetError("Stopped set state failed");
-        }
-    }
-
-    /* destroy buffer queue audio player object, and invalidate all associated interfaces */
-    if (bqPlayerObject != NULL) {
-
-        (*bqPlayerObject)->Destroy(bqPlayerObject);
-
-        bqPlayerObject = NULL;
-        bqPlayerPlay = NULL;
-        bqPlayerBufferQueue = NULL;
-#if 0
-        bqPlayerEffectSend = NULL;
-        bqPlayerMuteSolo = NULL;
-        bqPlayerVolume = NULL;
-#endif
-    }
-
-    if (audiodata->playsem) {
-        SDL_DestroySemaphore(audiodata->playsem);
-        audiodata->playsem = NULL;
-    }
-
-    if (audiodata->mixbuff) {
-        SDL_free(audiodata->mixbuff);
-    }
-
-    return;
-}
-
 static int
 openslES_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 {
@@ -494,44 +595,46 @@
     }
 
     if (iscapture) {
-        LOGI("openslES_OpenDevice( ) %s for capture", devname);
+        LOGI("openslES_OpenDevice() %s for capture", devname);
         return openslES_CreatePCMRecorder(this);
     } else {
-        LOGI("openslES_OpenDevice( ) %s for playing", devname);
+        LOGI("openslES_OpenDevice() %s for playing", devname);
         return openslES_CreatePCMPlayer(this);
     }
 }
 
 static void
-openslES_CloseDevice(_THIS)
+openslES_WaitDevice(_THIS)
 {
-    /* struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; */
+    struct SDL_PrivateAudioData *audiodata = this->hidden;
 
-    if (this->iscapture) {
-        LOGI("openslES_CloseDevice( ) for capture");
-        openslES_DestroyPCMRecorder(this);
-    } else {
-        LOGI("openslES_CloseDevice( ) for playing");
-        openslES_DestroyPCMPlayer(this);
-    }
+    LOGV("openslES_WaitDevice()");
 
-    SDL_free(this->hidden);
-
-    return;
+    /* Wait for an audio chunk to finish */
+    SDL_SemWait(audiodata->playsem);
 }
 
 static void
-openslES_WaitDevice(_THIS)
+openslES_PlayDevice(_THIS)
 {
-    struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden;
+    struct SDL_PrivateAudioData *audiodata = this->hidden;
+    SLresult result;
 
-    LOGV("openslES_WaitDevice( )");
+    LOGV("======openslES_PlayDevice()======");
 
-    /* Wait for an audio chunk to finish */
-    /* WaitForSingleObject(this->hidden->audio_sem, INFINITE); */
-    SDL_SemWait(audiodata->playsem);
+    /* Queue it up */
+    result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], this->spec.size);
 
-    return;
+    audiodata->next_buffer++;
+    if (audiodata->next_buffer >= NUM_BUFFERS) {
+        audiodata->next_buffer = 0;
+    }
+
+    /* If Enqueue fails, callback won't be called.
+     * Post the semphore, not to run out of buffer */
+    if (SL_RESULT_SUCCESS != result) {
+        SDL_SemPost(audiodata->playsem);
+    }
 }
 
 /*/           n   playn sem */
@@ -549,35 +652,54 @@
 static Uint8 *
 openslES_GetDeviceBuf(_THIS)
 {
-    struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden;
+    struct SDL_PrivateAudioData *audiodata = this->hidden;
 
-    LOGV("openslES_GetDeviceBuf( )");
+    LOGV("openslES_GetDeviceBuf()");
     return audiodata->pmixbuff[audiodata->next_buffer];
 }
 
-static void
-openslES_PlayDevice(_THIS)
+static int
+openslES_CaptureFromDevice(_THIS, void *buffer, int buflen)
 {
-    struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden;
+    struct SDL_PrivateAudioData *audiodata = this->hidden;
     SLresult result;
 
-    LOGV("======openslES_PlayDevice( )======");
+    /* Wait for new recorded data */
+    SDL_SemWait(audiodata->playsem);
 
-    /* Queue it up */
-    result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], this->spec.size);
+    /* Copy it to the output buffer */
+    SDL_assert(buflen == this->spec.size);
+    SDL_memcpy(buffer, audiodata->pmixbuff[audiodata->next_buffer], this->spec.size);
+
+    /* Re-enqueue the buffer */
+    result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], this->spec.size);
+    if (SL_RESULT_SUCCESS != result) {
+        LOGE("Record enqueue buffers failed: %d", result);
+        return -1;
+    }
 
     audiodata->next_buffer++;
     if (audiodata->next_buffer >= NUM_BUFFERS) {
         audiodata->next_buffer = 0;
     }
 
-    /* If Enqueue fails, callback won't be called.
-     * Post the semphore, not to run out of buffer */
-    if (SL_RESULT_SUCCESS != result) {
-        SDL_SemPost(audiodata->playsem);
+    return this->spec.size;
+}
+
+static void
+openslES_CloseDevice(_THIS)
+{
+    /* struct SDL_PrivateAudioData *audiodata = this->hidden; */
+
+    if (this->iscapture) {
+        LOGI("openslES_CloseDevice() for capture");
+        openslES_DestroyPCMRecorder(this);
+    } else {
+        LOGI("openslES_CloseDevice() for playing");
+        openslES_DestroyPCMPlayer(this);
     }
 
-    return;
+    SDL_free(this->hidden);
 }
 
 static int
@@ -594,18 +716,19 @@
     /* Set the function pointers */
     /* impl->DetectDevices = openslES_DetectDevices; */
     impl->OpenDevice    = openslES_OpenDevice;
-    impl->CloseDevice   = openslES_CloseDevice;
+    impl->WaitDevice    = openslES_WaitDevice;
     impl->PlayDevice    = openslES_PlayDevice;
     impl->GetDeviceBuf  = openslES_GetDeviceBuf;
+    impl->CaptureFromDevice = openslES_CaptureFromDevice;
+    impl->CloseDevice   = openslES_CloseDevice;
     impl->Deinitialize  = openslES_DestroyEngine;
-    impl->WaitDevice    = openslES_WaitDevice;
 
     /* and the capabilities */
-    impl->HasCaptureSupport             = 0;        /* TODO */
-    impl->OnlyHasDefaultOutputDevice    = 1;
-    /* impl->OnlyHasDefaultInputDevice  = 1; */
+    impl->HasCaptureSupport = 1;
+    impl->OnlyHasDefaultOutputDevice = 1;
+    impl->OnlyHasDefaultCaptureDevice = 1;
 
-    LOGI("openslES_Init() - succes");
+    LOGI("openslES_Init() - success");
 
     /* this audio target is available. */
     return 1;
@@ -615,24 +738,24 @@
     "openslES", "opensl ES audio driver", openslES_Init, 0
 };
 
-void openslES_ResumeDevices()
+void openslES_ResumeDevices(void)
 {
     if (bqPlayerPlay != NULL) {
         /* set the player's state to 'playing' */
         SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
         if (SL_RESULT_SUCCESS != result) {
-            SDL_SetError("openslES_ResumeDevices failed");
+            LOGE("openslES_ResumeDevices failed: %d", result);
         }
     }
 }
 
-void openslES_PauseDevices()
+void openslES_PauseDevices(void)
 {
     if (bqPlayerPlay != NULL) {
         /* set the player's state to 'paused' */
         SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED);
         if (SL_RESULT_SUCCESS != result) {
-            SDL_SetError("openslES_PauseDevices failed");
+            LOGE("openslES_PauseDevices failed: %d", result);
         }
     }
 }
diff --git a/src/audio/openslES/SDL_openslES.h b/src/audio/openslES/SDL_openslES.h
index 691f9f0..542870a 100644
--- a/src/audio/openslES/SDL_openslES.h
+++ b/src/audio/openslES/SDL_openslES.h
@@ -32,14 +32,10 @@
 
 struct SDL_PrivateAudioData
 {
-    /* The file descriptor for the audio device */
     Uint8   *mixbuff;
     int      next_buffer;
     Uint8   *pmixbuff[NUM_BUFFERS];
     SDL_sem *playsem;
-#if 0
-    SDL_sem *recsem;
-#endif
 };
 
 void openslES_ResumeDevices(void);
diff --git a/src/core/android/SDL_android.h b/src/core/android/SDL_android.h
index d51dc28..9f3f3cc 100644
--- a/src/core/android/SDL_android.h
+++ b/src/core/android/SDL_android.h
@@ -123,6 +123,8 @@
 SDL_bool Android_JNI_SupportsRelativeMouse(void);
 SDL_bool Android_JNI_SetRelativeMouseEnabled(SDL_bool enabled);
 
+/* Request permission */
+SDL_bool Android_JNI_RequestPermission(const char *permission);
 
 int SDL_GetAndroidSDKVersion(void);