Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c0f49440 authored by jiabin's avatar jiabin
Browse files

Add support for getting all available microphones characteristics.

Add MicrophoneInfo to represent the microphone characteristics which
including location, orientation, frequency response, sensitivity,
channel mapping and other useful information.
Add a new API in AudioManager to query all current available
microphones.

Bug: 64038649
Test: Run test and check the print log.
Change-Id: Ie0dbfeeb84b88db426518b93c7bb83c8913bca85
parent 0e694a63
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -21917,6 +21917,7 @@ package android.media {
    method public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
    method public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
    method public android.media.AudioDeviceInfo[] getDevices(int);
    method public java.util.List<android.media.MicrophoneInfo> getMicrophones() throws java.io.IOException;
    method public int getMode();
    method public java.lang.String getParameters(java.lang.String);
    method public java.lang.String getProperty(java.lang.String);
@@ -24411,6 +24412,41 @@ package android.media {
    method public float getMediaClockRate();
  }
  public final class MicrophoneInfo {
    method public java.util.List<android.util.Pair<java.lang.Integer, java.lang.Integer>> getChannelMapping();
    method public java.lang.String getDescription();
    method public int getDirectionality();
    method public java.util.List<android.util.Pair<java.lang.Float, java.lang.Float>> getFrequencyResponse();
    method public int getGroup();
    method public int getId();
    method public int getIndexInTheGroup();
    method public int getLocation();
    method public float getMaxSpl();
    method public float getMinSpl();
    method public android.media.MicrophoneInfo.Coordinate3F getOrientation();
    method public android.media.MicrophoneInfo.Coordinate3F getPosition();
    method public float getSensitivity();
    method public int getType();
    field public static final int CHANNEL_MAPPING_DIRECT = 1; // 0x1
    field public static final int CHANNEL_MAPPING_PROCESSED = 2; // 0x2
    field public static final int DIRECTIONALITY_BI_DIRECTIONAL = 2; // 0x2
    field public static final int DIRECTIONALITY_CARDIOID = 3; // 0x3
    field public static final int DIRECTIONALITY_HYPER_CARDIOID = 4; // 0x4
    field public static final int DIRECTIONALITY_OMNI = 1; // 0x1
    field public static final int DIRECTIONALITY_SUPER_CARDIOID = 5; // 0x5
    field public static final int DIRECTIONALITY_UNKNOW = 0; // 0x0
    field public static final int LOCATION_MAINBODY = 1; // 0x1
    field public static final int LOCATION_MAINBODY_MOVABLE = 2; // 0x2
    field public static final int LOCATION_PERIPHERAL = 3; // 0x3
    field public static final int LOCATION_UNKNOWN = 0; // 0x0
  }
  public class MicrophoneInfo.Coordinate3F {
    field public final float x;
    field public final float y;
    field public final float z;
  }
  public final class NotProvisionedException extends android.media.MediaDrmException {
    ctor public NotProvisionedException(java.lang.String);
  }
+1 −0
Original line number Diff line number Diff line
@@ -164,6 +164,7 @@ cc_library_shared {
        "android_media_DeviceCallback.cpp",
        "android_media_JetPlayer.cpp",
        "android_media_MediaMetricsJNI.cpp",
        "android_media_MicrophoneInfo.cpp",
        "android_media_RemoteDisplay.cpp",
        "android_media_ToneGenerator.cpp",
        "android_hardware_Camera.cpp",
+2 −0
Original line number Diff line number Diff line
@@ -104,6 +104,7 @@ extern int register_android_hardware_location_ActivityRecognitionHardware(JNIEnv
extern int register_android_media_AudioRecord(JNIEnv *env);
extern int register_android_media_AudioSystem(JNIEnv *env);
extern int register_android_media_AudioTrack(JNIEnv *env);
extern int register_android_media_MicrophoneInfo(JNIEnv *env);
extern int register_android_media_JetPlayer(JNIEnv *env);
extern int register_android_media_ToneGenerator(JNIEnv *env);

@@ -1457,6 +1458,7 @@ static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_android_media_AudioSystem),
    REG_JNI(register_android_media_AudioTrack),
    REG_JNI(register_android_media_JetPlayer),
    REG_JNI(register_android_media_MicrophoneInfo),
    REG_JNI(register_android_media_RemoteDisplay),
    REG_JNI(register_android_media_ToneGenerator),

+43 −2
Original line number Diff line number Diff line
@@ -21,17 +21,20 @@
#include <utils/Log.h>

#include <sstream>
#include <vector>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
#include "core_jni_helpers.h"

#include <media/AudioSystem.h>
#include <media/AudioPolicy.h>
#include <media/MicrophoneInfo.h>
#include <nativehelper/ScopedLocalRef.h>
#include <system/audio.h>
#include <system/audio_policy.h>
#include "android_media_AudioFormat.h"
#include "android_media_AudioErrors.h"
#include "android_media_MicrophoneInfo.h"

// ----------------------------------------------------------------------------

@@ -143,7 +146,6 @@ static struct {
    jfieldID    mSource;
} gAudioAttributesFields;


static const char* const kEventHandlerClassPathName =
        "android/media/AudioPortEventHandler";
static struct {
@@ -1158,7 +1160,6 @@ exit:
    return jStatus;
}


static jint
android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz,
                                         jobject jPorts, jintArray jGeneration)
@@ -1789,6 +1790,45 @@ android_media_AudioSystem_isOffloadSupported(JNIEnv *env, jobject thiz,
    return AudioSystem::isOffloadSupported(format);
}

static jint
android_media_AudioSystem_getMicrophones(JNIEnv *env, jobject thiz, jobject jMicrophonesInfo)
{
    ALOGV("getMicrophones");

    if (jMicrophonesInfo == NULL) {
        ALOGE("jMicrophonesInfo NULL MicrophoneInfo ArrayList");
        return (jint)AUDIO_JAVA_BAD_VALUE;
    }
    if (!env->IsInstanceOf(jMicrophonesInfo, gArrayListClass)) {
        ALOGE("getMicrophones not an arraylist");
        return (jint)AUDIO_JAVA_BAD_VALUE;
    }

    jint jStatus;
    std::vector<media::MicrophoneInfo> microphones;
    status_t status = AudioSystem::getMicrophones(&microphones);
    if (status != NO_ERROR) {
        ALOGE_IF(status != NO_ERROR, "AudioSystem::getMicrophones error %d", status);
        jStatus = nativeToJavaStatus(status);
        return jStatus;
    }
    if (microphones.size() == 0) {
        jStatus = (jint)AUDIO_JAVA_SUCCESS;
        return jStatus;
    }
    for (size_t i = 0; i < microphones.size(); i++) {
        jobject jMicrophoneInfo;
        jStatus = convertMicrophoneInfoFromNative(env, &jMicrophoneInfo, &microphones[i]);
        if (jStatus != AUDIO_JAVA_SUCCESS) {
            return jStatus;
        }
        env->CallBooleanMethod(jMicrophonesInfo, gArrayListMethods.add, jMicrophoneInfo);
        env->DeleteLocalRef(jMicrophoneInfo);
    }

    return jStatus;
}

// ----------------------------------------------------------------------------

static const JNINativeMethod gMethods[] = {
@@ -1843,6 +1883,7 @@ static const JNINativeMethod gMethods[] = {
    {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady},
    {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB},
    {"native_is_offload_supported", "(IIII)Z", (void *)android_media_AudioSystem_isOffloadSupported},
    {"getMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioSystem_getMicrophones},
};


+187 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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 "android_media_MicrophoneInfo.h"
#include "android_media_AudioErrors.h"
#include "core_jni_helpers.h"

using namespace android;

static jclass gArrayListClass;
static jmethodID gArrayListCstor;
static struct {
    jmethodID add;
} gArrayListMethods;

static jclass gFloatClass;
static jmethodID gFloatCstor;

static jclass gFloatArrayClass;

static jclass gIntegerClass;
static jmethodID gIntegerCstor;

static jclass gMicrophoneInfoClass;
static jmethodID gMicrophoneInfoCstor;

static jclass gMicrophoneInfoCoordinateClass;
static jmethodID gMicrophoneInfoCoordinateCstor;

static jclass gPairClass;
static jmethodID gPairCstor;

namespace android {

jint convertMicrophoneInfoFromNative(JNIEnv *env, jobject *jMicrophoneInfo,
        const media::MicrophoneInfo *microphoneInfo)
{
    jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
    jstring jDeviceId = NULL;
    jstring jAddress = NULL;
    jobject jGeometricLocation = NULL;
    jobject jOrientation = NULL;
    jobject jFrequencyResponses = NULL;
    jobject jChannelMappings = NULL;

    jDeviceId = env->NewStringUTF(String8(microphoneInfo->getDeviceId()).string());
    jAddress = env->NewStringUTF(String8(microphoneInfo->getAddress()).string());
    if (microphoneInfo->getGeometricLocation().size() != 3 ||
            microphoneInfo->getOrientation().size() != 3) {
        jStatus = nativeToJavaStatus(BAD_VALUE);
        goto exit;
    }
    jGeometricLocation = env->NewObject(gMicrophoneInfoCoordinateClass,
                                        gMicrophoneInfoCoordinateCstor,
                                        NULL,
                                        microphoneInfo->getGeometricLocation()[0],
                                        microphoneInfo->getGeometricLocation()[1],
                                        microphoneInfo->getGeometricLocation()[2]);
    jOrientation = env->NewObject(gMicrophoneInfoCoordinateClass,
                                  gMicrophoneInfoCoordinateCstor,
                                  NULL,
                                  microphoneInfo->getOrientation()[0],
                                  microphoneInfo->getOrientation()[1],
                                  microphoneInfo->getOrientation()[2]);
    // Create a list of Pair for frequency response.
    if (microphoneInfo->getFrequencyResponses().size() != 2 ||
            microphoneInfo->getFrequencyResponses()[0].size() !=
                    microphoneInfo->getFrequencyResponses()[1].size()) {
        jStatus = nativeToJavaStatus(BAD_VALUE);
        goto exit;
    }
    jFrequencyResponses = env->NewObject(gArrayListClass, gArrayListCstor);
    for (size_t i = 0; i < microphoneInfo->getFrequencyResponses()[0].size(); i++) {
        jobject jFrequency = env->NewObject(gFloatClass, gFloatCstor,
                                            microphoneInfo->getFrequencyResponses()[0][i]);
        jobject jResponse = env->NewObject(gFloatClass, gFloatCstor,
                                          microphoneInfo->getFrequencyResponses()[1][i]);
        jobject jFrequencyResponse = env->NewObject(gPairClass, gPairCstor, jFrequency, jResponse);
        env->CallBooleanMethod(jFrequencyResponses, gArrayListMethods.add, jFrequencyResponse);
        env->DeleteLocalRef(jFrequency);
        env->DeleteLocalRef(jResponse);
        env->DeleteLocalRef(jFrequencyResponse);
    }
    // Create a list of Pair for channel mapping.
    if (microphoneInfo->getChannelMapping().size() != AUDIO_CHANNEL_COUNT_MAX) {
        jStatus = nativeToJavaStatus(BAD_VALUE);
        goto exit;
    }
    jChannelMappings = env->NewObject(gArrayListClass, gArrayListCstor);
    for (size_t i = 0; i < microphoneInfo->getChannelMapping().size(); i++) {
        int channelMappingType = microphoneInfo->getChannelMapping()[i];
        if (channelMappingType != AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED) {
            jobject jChannelIndex = env->NewObject(gIntegerClass, gIntegerCstor, i);
            jobject jChannelMappingType = env->NewObject(gIntegerClass, gIntegerCstor,
                                                         channelMappingType);
            jobject jChannelMapping = env->NewObject(gPairClass, gPairCstor,
                                                     jChannelIndex, jChannelMappingType);
            env->CallBooleanMethod(jChannelMappings, gArrayListMethods.add, jChannelMapping);
            env->DeleteLocalRef(jChannelIndex);
            env->DeleteLocalRef(jChannelMappingType);
            env->DeleteLocalRef(jChannelMapping);
        }
    }
    *jMicrophoneInfo = env->NewObject(gMicrophoneInfoClass, gMicrophoneInfoCstor, jDeviceId,
                                      microphoneInfo->getType(), jAddress,
                                      microphoneInfo->getDeviceLocation(),
                                      microphoneInfo->getDeviceGroup(),
                                      microphoneInfo->getIndexInTheGroup(),
                                      jGeometricLocation, jOrientation,
                                      jFrequencyResponses, jChannelMappings,
                                      microphoneInfo->getSensitivity(),
                                      microphoneInfo->getMaxSpl(),
                                      microphoneInfo->getMinSpl(),
                                      microphoneInfo->getDirectionality());

exit:
    if (jDeviceId != NULL) {
        env->DeleteLocalRef(jDeviceId);
    }
    if (jAddress != NULL) {
        env->DeleteLocalRef(jAddress);
    }
    if (jFrequencyResponses != NULL) {
        env->DeleteLocalRef(jFrequencyResponses);
    }
    if (jChannelMappings != NULL) {
        env->DeleteLocalRef(jChannelMappings);
    }
    if (jGeometricLocation != NULL) {
        env->DeleteLocalRef(jGeometricLocation);
    }
    if (jOrientation != NULL) {
        env->DeleteLocalRef(jOrientation);
    }
    return jStatus;
}

}

int register_android_media_MicrophoneInfo(JNIEnv *env)
{
    jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
    gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
    gArrayListCstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V");
    gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");

    jclass floatClass = FindClassOrDie(env, "java/lang/Float");
    gFloatClass = MakeGlobalRefOrDie(env, floatClass);
    gFloatCstor = GetMethodIDOrDie(env, floatClass, "<init>", "(F)V");

    jclass floatArrayClass = FindClassOrDie(env, "[F");
    gFloatArrayClass = MakeGlobalRefOrDie(env, floatArrayClass);

    jclass integerClass = FindClassOrDie(env, "java/lang/Integer");
    gIntegerClass = MakeGlobalRefOrDie(env, integerClass);
    gIntegerCstor = GetMethodIDOrDie(env, integerClass, "<init>", "(I)V");

    jclass microphoneInfoClass = FindClassOrDie(env, "android/media/MicrophoneInfo");
    gMicrophoneInfoClass = MakeGlobalRefOrDie(env, microphoneInfoClass);
    gMicrophoneInfoCstor = GetMethodIDOrDie(env, microphoneInfoClass, "<init>",
            "(Ljava/lang/String;ILjava/lang/String;IIILandroid/media/MicrophoneInfo$Coordinate3F;Landroid/media/MicrophoneInfo$Coordinate3F;Ljava/util/List;Ljava/util/List;FFFI)V");

    jclass microphoneInfoCoordinateClass = FindClassOrDie(
            env, "android/media/MicrophoneInfo$Coordinate3F");
    gMicrophoneInfoCoordinateClass = MakeGlobalRefOrDie(env, microphoneInfoCoordinateClass);
    gMicrophoneInfoCoordinateCstor = GetMethodIDOrDie(env, microphoneInfoCoordinateClass, "<init>",
           "(Landroid/media/MicrophoneInfo;FFF)V");

    jclass pairClass = FindClassOrDie(env, "android/util/Pair");
    gPairClass = MakeGlobalRefOrDie(env, pairClass);
    gPairCstor = GetMethodIDOrDie(env, pairClass, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");

    return 0;
}
Loading