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

Commit f8728771 authored by Eric Laurent's avatar Eric Laurent Committed by Android (Google) Code Review
Browse files

Merge "audio: add JNI AudioAttribute helper class"

parents e98248eb b4691282
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -166,6 +166,7 @@ cc_library_shared {
        "android_media_AudioRecord.cpp",
        "android_media_AudioSystem.cpp",
        "android_media_AudioTrack.cpp",
        "android_media_AudioAttributes.cpp",
        "android_media_DeviceCallback.cpp",
        "android_media_JetPlayer.cpp",
        "android_media_MediaMetricsJNI.cpp",
+2 −0
Original line number Diff line number Diff line
@@ -107,6 +107,7 @@ extern int register_android_media_AudioEffectDescriptor(JNIEnv *env);
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_AudioAttributes(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);
@@ -1470,6 +1471,7 @@ static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_android_media_AudioSystem),
    REG_JNI(register_android_media_AudioRecord),
    REG_JNI(register_android_media_AudioTrack),
    REG_JNI(register_android_media_AudioAttributes),
    REG_JNI(register_android_media_JetPlayer),
    REG_JNI(register_android_media_MicrophoneInfo),
    REG_JNI(register_android_media_RemoteDisplay),
+205 −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.
 */

//#define LOG_NDEBUG 0

#define LOG_TAG "AudioAttributes-JNI"

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

#include <utils/Log.h>
#include <vector>

#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedUtfChars.h>

#include "android_media_AudioAttributes.h"
#include "android_media_AudioErrors.h"

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

using namespace android;

// ----------------------------------------------------------------------------
static const char* const kClassPathName = "android/media/AudioAttributes";

static jclass gAudioAttributesClass;
static struct {
    jfieldID    mUsage;         // AudioAttributes.mUsage
    jfieldID    mSource;        // AudioAttributes.mSource
    jfieldID    mContentType;   // AudioAttributes.mContentType
    jfieldID    mFlags;         // AudioAttributes.mFlags
    jfieldID    mFormattedTags; // AudioAttributes.mFormattedTags
} gAudioAttributesFields;

static jclass gAudioAttributesBuilderClass;
static jmethodID gAudioAttributesBuilderCstor;
static struct {
    jmethodID build;
    jmethodID setUsage;
    jmethodID setInternalCapturePreset;
    jmethodID setContentType;
    jmethodID setFlags;
    jmethodID addTag;
} gAudioAttributesBuilderMethods;


static jint nativeAudioAttributesFromJavaAudioAttributes(
        JNIEnv* env, jobject jAudioAttributes, audio_attributes_t *aa)
{
    if (env == nullptr) {
        return AUDIO_JAVA_DEAD_OBJECT;
    }
    if (jAudioAttributes == nullptr) {
        ALOGE("Invalid AudioAttributes java object");
        return (jint)AUDIO_JAVA_BAD_VALUE;
    }
    if (!env->IsInstanceOf(jAudioAttributes, gAudioAttributesClass)) {
        return (jint)AUDIO_JAVA_BAD_VALUE;
    }
    const jstring jtags =
            (jstring) env->GetObjectField(jAudioAttributes, gAudioAttributesFields.mFormattedTags);
    if (jtags == nullptr) {
        return AUDIO_JAVA_NO_INIT;
    }
    const char* tags = env->GetStringUTFChars(jtags, NULL);
    // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
    strncpy(aa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
    env->ReleaseStringUTFChars(jtags, tags);

    // Record ?
    aa->source = (audio_source_t) env->GetIntField(jAudioAttributes,
                                                    gAudioAttributesFields.mSource);
    // Track ?
    aa->usage = (audio_usage_t) env->GetIntField(jAudioAttributes, gAudioAttributesFields.mUsage);

    aa->content_type =
            (audio_content_type_t) env->GetIntField(jAudioAttributes,
                                                    gAudioAttributesFields.mContentType);

    aa->flags = (audio_flags_mask_t)env->GetIntField(jAudioAttributes,
                                                      gAudioAttributesFields.mFlags);

    ALOGV("AudioAttributes for usage=%d content=%d source=%d tags=%s flags=%08x tags=%s",
          aa->usage, aa->content_type, aa->source, aa->tags, aa->flags, aa->tags);
    return (jint)AUDIO_JAVA_SUCCESS;
}

static jint nativeAudioAttributesToJavaAudioAttributes(
        JNIEnv* env, jobject *jAudioAttributes, const audio_attributes_t &attributes)
{
    ScopedLocalRef<jobject> jAttributeBuilder(env, env->NewObject(gAudioAttributesBuilderClass,
                                                                  gAudioAttributesBuilderCstor));
    if (jAttributeBuilder.get() == nullptr) {
        return (jint)AUDIO_JAVA_ERROR;
    }
    env->CallObjectMethod(jAttributeBuilder.get(),
                          gAudioAttributesBuilderMethods.setUsage,
                          attributes.usage);
    env->CallObjectMethod(jAttributeBuilder.get(),
                          gAudioAttributesBuilderMethods.setInternalCapturePreset,
                          attributes.source);
    env->CallObjectMethod(jAttributeBuilder.get(),
                          gAudioAttributesBuilderMethods.setContentType,
                          attributes.content_type);
    env->CallObjectMethod(jAttributeBuilder.get(),
                          gAudioAttributesBuilderMethods.setFlags,
                          attributes.flags);
    env->CallObjectMethod(jAttributeBuilder.get(),
                          gAudioAttributesBuilderMethods.addTag,
                          env->NewStringUTF(attributes.tags));

    *jAudioAttributes = env->CallObjectMethod(jAttributeBuilder.get(),
                                              gAudioAttributesBuilderMethods.build);
    return (jint)AUDIO_JAVA_SUCCESS;
}

// ----------------------------------------------------------------------------
JNIAudioAttributeHelper::UniqueAaPtr JNIAudioAttributeHelper::makeUnique()
{
    audio_attributes_t *aa = new (calloc(1, sizeof(audio_attributes_t)))
                audio_attributes_t{AUDIO_ATTRIBUTES_INITIALIZER};
    return UniqueAaPtr{aa, free};
}

jint JNIAudioAttributeHelper::nativeFromJava(JNIEnv* env, jobject jAudioAttributes,
                                             audio_attributes_t *paa)
{
    return nativeAudioAttributesFromJavaAudioAttributes(env, jAudioAttributes, paa);
}

jint JNIAudioAttributeHelper::nativeToJava(
        JNIEnv* env, jobject *jAudioAttributes, const audio_attributes_t &attributes)
{
    return nativeAudioAttributesToJavaAudioAttributes(env, jAudioAttributes, attributes);
}

jint JNIAudioAttributeHelper::getJavaArray(
        JNIEnv* env, jobjectArray *jAudioAttributeArray, jint numAudioAttributes)
{
    *jAudioAttributeArray = env->NewObjectArray(numAudioAttributes, gAudioAttributesClass, NULL);
    return jAudioAttributeArray == NULL? (jint)AUDIO_JAVA_ERROR : (jint)AUDIO_JAVA_SUCCESS;
}

/*
 * JNI registration.
 */
static const JNINativeMethod gMethods[] = {
    // n/a
};

int register_android_media_AudioAttributes(JNIEnv *env)
{
    jclass audioAttributesClass = FindClassOrDie(env, kClassPathName);
    gAudioAttributesClass = MakeGlobalRefOrDie(env, audioAttributesClass);
    gAudioAttributesFields.mUsage = GetFieldIDOrDie(env, audioAttributesClass, "mUsage", "I");
    gAudioAttributesFields.mSource = GetFieldIDOrDie(env, audioAttributesClass, "mSource", "I");
    gAudioAttributesFields.mContentType =
            GetFieldIDOrDie(env, audioAttributesClass, "mContentType", "I");
    gAudioAttributesFields.mFlags = GetFieldIDOrDie(env, audioAttributesClass, "mFlags", "I");
    gAudioAttributesFields.mFormattedTags =
            GetFieldIDOrDie(env, audioAttributesClass, "mFormattedTags", "Ljava/lang/String;");

    jclass audioAttributesBuilderClass = FindClassOrDie(
                env, "android/media/AudioAttributes$Builder");
    gAudioAttributesBuilderClass = MakeGlobalRefOrDie(env, audioAttributesBuilderClass);
    gAudioAttributesBuilderCstor = GetMethodIDOrDie(
                env, audioAttributesBuilderClass, "<init>", "()V");
    gAudioAttributesBuilderMethods.build = GetMethodIDOrDie(
                env, audioAttributesBuilderClass, "build", "()Landroid/media/AudioAttributes;");
    gAudioAttributesBuilderMethods.setUsage = GetMethodIDOrDie(
                env, audioAttributesBuilderClass, "setUsage",
                "(I)Landroid/media/AudioAttributes$Builder;");
    gAudioAttributesBuilderMethods.setInternalCapturePreset = GetMethodIDOrDie(
                env, audioAttributesBuilderClass, "setInternalCapturePreset",
                "(I)Landroid/media/AudioAttributes$Builder;");
    gAudioAttributesBuilderMethods.setContentType = GetMethodIDOrDie(
                env, audioAttributesBuilderClass, "setContentType",
                "(I)Landroid/media/AudioAttributes$Builder;");
    gAudioAttributesBuilderMethods.setFlags = GetMethodIDOrDie(
                env, audioAttributesBuilderClass, "setFlags",
                "(I)Landroid/media/AudioAttributes$Builder;");
    gAudioAttributesBuilderMethods.addTag = GetMethodIDOrDie(
                env, audioAttributesBuilderClass, "addTag",
                "(Ljava/lang/String;)Landroid/media/AudioAttributes$Builder;");

    env->DeleteLocalRef(audioAttributesClass);

    return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
+70 −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.
 */

#pragma once

#include "jni.h"

#include <memory.h>

#include <system/audio.h>

namespace android {

class JNIAudioAttributeHelper
{
public:
    using UniqueAaPtr = std::unique_ptr<audio_attributes_t, decltype(free)*>;

    /**
     * @brief makeUnique helper to prevent leak
     * @return a unique ptr of 0-initialized native audio attributes structure
     */
    static UniqueAaPtr makeUnique();

    /**
     * @brief nativeFromJava Gets the underlying AudioAttributes from an AudioAttributes Java
     * object.
     * @param env
     * @param jAudioAttributes JAVA AudioAttribute object
     * @param paa native AudioAttribute pointer
     * @return AUDIO_JAVA_SUCCESS on success, error code otherwise
     */
    static jint nativeFromJava(
            JNIEnv* env, jobject jAudioAttributes, audio_attributes_t *attributes);

    /**
     * @brief nativeToJava AudioAttributes Java object from a native AudioAttributes.
     * @param env
     * @param jAudioAttributes JAVA AudioAttribute object
     * @param attributes native AudioAttribute
     * @return AUDIO_JAVA_SUCCESS on success, error code otherwise
     */
    static jint nativeToJava(
            JNIEnv* env, jobject *jAudioAttributes, const audio_attributes_t &attributes);

    /**
     * @brief getJavaArray: creates an array of JAVA AudioAttributes objects
     * @param env
     * @param jAudioAttributeArray
     * @param numAudioAttributes
     * @return Array of AudioAttributes objects
     */
    static jint getJavaArray(
            JNIEnv* env, jobjectArray *jAudioAttributeArray, jint numAudioAttributes);
};

}; // namespace android
+7 −34
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@
#include "android_media_DeviceCallback.h"
#include "android_media_MediaMetricsJNI.h"
#include "android_media_MicrophoneInfo.h"
#include "android_media_AudioAttributes.h"

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

@@ -42,7 +43,6 @@ using namespace android;

// ----------------------------------------------------------------------------
static const char* const kClassPathName = "android/media/AudioRecord";
static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";

static jclass gArrayListClass;
static struct {
@@ -56,12 +56,6 @@ struct audio_record_fields_t {
    jfieldID  nativeCallbackCookie;    // provides access to the AudioRecord callback data
    jfieldID  nativeDeviceCallback;    // provides access to the JNIDeviceCallback instance
};
struct audio_attributes_fields_t {
    jfieldID  fieldRecSource;    // AudioAttributes.mSource
    jfieldID  fieldFlags;        // AudioAttributes.mFlags
    jfieldID  fieldFormattedTags;// AudioAttributes.mFormattedTags
};
static audio_attributes_fields_t javaAudioAttrFields;
static audio_record_fields_t     javaAudioRecordFields;
static struct {
    jfieldID  fieldFramePosition;     // AudioTimestamp.framePosition
@@ -213,7 +207,6 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
    nSession = NULL;

    audio_attributes_t *paa = NULL;
    sp<AudioRecord> lpRecorder = 0;
    audiorecord_callback_cookie *lpCallbackData = NULL;

@@ -275,15 +268,11 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
        lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));

        // read the AudioAttributes values
        paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
        const jstring jtags =
                (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
        const char* tags = env->GetStringUTFChars(jtags, NULL);
        // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
        strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
        env->ReleaseStringUTFChars(jtags, tags);
        paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource);
        paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
        auto paa = JNIAudioAttributeHelper::makeUnique();
        jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());
        if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
            return jStatus;
        }
        ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags);

        audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;
@@ -311,7 +300,7 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
            AudioRecord::TRANSFER_DEFAULT,
            flags,
            -1, -1,        // default uid, pid
            paa);
            paa.get());

        if (status != NO_ERROR) {
            ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
@@ -368,19 +357,10 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
    // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);

    if (paa != NULL) {
        // audio attributes were copied in AudioRecord creation
        free(paa);
        paa = NULL;
    }

    return (jint) AUDIO_JAVA_SUCCESS;

    // failure:
native_init_failure:
    if (paa != NULL) {
        free(paa);
    }
    env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
    env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
    delete lpCallbackData;
@@ -972,13 +952,6 @@ int register_android_media_AudioRecord(JNIEnv *env)
    javaAudioRecordFields.nativeDeviceCallback = GetFieldIDOrDie(env,
            audioRecordClass, JAVA_NATIVEDEVICECALLBACK_FIELD_NAME, "J");

    // Get the AudioAttributes class and fields
    jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName);
    javaAudioAttrFields.fieldRecSource = GetFieldIDOrDie(env, audioAttrClass, "mSource", "I");
    javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I");
    javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env,
            audioAttrClass, "mFormattedTags", "Ljava/lang/String;");

    // Get the RecordTimestamp class and fields
    jclass audioTimestampClass = FindClassOrDie(env, "android/media/AudioTimestamp");
    javaAudioTimestampFields.fieldFramePosition =
Loading