Merge "Support query active microphones information in AudioRecord."
diff --git a/api/current.txt b/api/current.txt
index 6fa7976..ded0e43 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22155,6 +22155,7 @@
     method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
     method protected void finalize();
+    method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException;
     method public int getAudioFormat();
     method public int getAudioSessionId();
     method public int getAudioSource();
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index ebd16c7..375d68b 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -25,6 +25,8 @@
 
 #include <utils/Log.h>
 #include <media/AudioRecord.h>
+#include <media/MicrophoneInfo.h>
+#include <vector>
 
 #include <nativehelper/ScopedUtfChars.h>
 
@@ -32,6 +34,7 @@
 #include "android_media_AudioErrors.h"
 #include "android_media_DeviceCallback.h"
 #include "android_media_MediaMetricsJNI.h"
+#include "android_media_MicrophoneInfo.h"
 
 // ----------------------------------------------------------------------------
 
@@ -41,6 +44,11 @@
 static const char* const kClassPathName = "android/media/AudioRecord";
 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
 
+static jclass gArrayListClass;
+static struct {
+    jmethodID add;
+} gArrayListMethods;
+
 struct audio_record_fields_t {
     // these fields provide access from C++ to the...
     jmethodID postNativeEventInJava; //... event post callback method
@@ -785,6 +793,46 @@
 }
 
 // ----------------------------------------------------------------------------
+static jint android_media_AudioRecord_get_active_microphones(JNIEnv *env,
+        jobject thiz, jobject jActiveMicrophones) {
+    if (jActiveMicrophones == NULL) {
+        ALOGE("jActiveMicrophones is null");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    if (!env->IsInstanceOf(jActiveMicrophones, gArrayListClass)) {
+        ALOGE("getActiveMicrophones not an arraylist");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+    if (lpRecorder == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "Unable to retrieve AudioRecord pointer for getActiveMicrophones()");
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+
+    jint jStatus = AUDIO_JAVA_SUCCESS;
+    std::vector<media::MicrophoneInfo> activeMicrophones;
+    status_t status = lpRecorder->getActiveMicrophones(&activeMicrophones);
+    if (status != NO_ERROR) {
+        ALOGE_IF(status != NO_ERROR, "AudioRecord::getActiveMicrophones error %d", status);
+        jStatus = nativeToJavaStatus(status);
+        return jStatus;
+    }
+
+    for (size_t i = 0; i < activeMicrophones.size(); i++) {
+        jobject jMicrophoneInfo;
+        jStatus = convertMicrophoneInfoFromNative(env, &jMicrophoneInfo, &activeMicrophones[i]);
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            return jStatus;
+        }
+        env->CallBooleanMethod(jActiveMicrophones, gArrayListMethods.add, jMicrophoneInfo);
+        env->DeleteLocalRef(jMicrophoneInfo);
+    }
+    return jStatus;
+}
+
+// ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 static const JNINativeMethod gMethods[] = {
     // name,               signature,  funcPtr
@@ -824,6 +872,8 @@
                                         (void *)android_media_AudioRecord_disableDeviceCallback},
     {"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I",
                                        (void *)android_media_AudioRecord_get_timestamp},
+    {"native_get_active_microphones", "(Ljava/util/ArrayList;)I",
+                                        (void *)android_media_AudioRecord_get_active_microphones},
 };
 
 // field names found in android/media/AudioRecord.java
@@ -873,6 +923,10 @@
     javaAudioTimestampFields.fieldNanoTime =
             GetFieldIDOrDie(env, audioTimestampClass, "nanoTime", "J");
 
+    jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
+    gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
+    gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
+
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
 }
 
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index eb6e830..d0963cb 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -16,12 +16,15 @@
 
 package android.media;
 
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.nio.ByteBuffer;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.List;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -35,6 +38,7 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 
@@ -1601,6 +1605,32 @@
         }
     }
 
+    //--------------------------------------------------------------------------
+    // Microphone information
+    //--------------------
+    /**
+     * Returns a lists of {@link MicrophoneInfo} representing the active microphones.
+     * By querying channel mapping for each active microphone, developer can know how
+     * the microphone is used by each channels or a capture stream.
+     * Note that the information about the active microphones may change during a recording.
+     * See {@link AudioManager#registerAudioDeviceCallback} to be notified of changes
+     * in the audio devices, querying the active microphones then will return the latest
+     * information.
+     *
+     * @return a lists of {@link MicrophoneInfo} representing the active microphones.
+     * @throws IOException if an error occurs
+     */
+    public List<MicrophoneInfo> getActiveMicrophones() throws IOException {
+        ArrayList<MicrophoneInfo> activeMicrophones = new ArrayList<>();
+        int status = native_get_active_microphones(activeMicrophones);
+        if (status != AudioManager.SUCCESS) {
+            Log.e(TAG, "getActiveMicrophones failed:" + status);
+            return new ArrayList<MicrophoneInfo>();
+        }
+        AudioManager.setPortIdForMicrophones(activeMicrophones);
+        return activeMicrophones;
+    }
+
     //---------------------------------------------------------
     // Interface definitions
     //--------------------
@@ -1746,6 +1776,9 @@
     private native final int native_get_timestamp(@NonNull AudioTimestamp outTimestamp,
             @AudioTimestamp.Timebase int timebase);
 
+    private native final int native_get_active_microphones(
+            ArrayList<MicrophoneInfo> activeMicrophones);
+
     //---------------------------------------------------------
     // Utility methods
     //------------------