AudioSystem JNI: Add support for spatializer APIs

Add support for the following APIs in AudioSystem JNI:
- getSpatializer
- canBeSpatialized

Bug: 188502620
Test: make
Change-Id: I1942edc6c9fec3babfe870586f7a014d55b1ce70
This commit is contained in:
Eric Laurent 2021-07-24 08:47:17 +02:00
parent 03adaa2230
commit 8500f6c136
6 changed files with 149 additions and 10 deletions

View File

@ -341,6 +341,8 @@ java_defaults {
"modules-utils-preconditions",
"modules-utils-os",
"framework-permission-aidl-java",
"spatializer-aidl-java",
"audiopolicy-types-aidl-java",
],
}

View File

@ -243,6 +243,8 @@ cc_library_shared {
shared_libs: [
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
"audiopolicy-types-aidl-cpp",
"spatializer-aidl-cpp",
"av-types-aidl-cpp",
"android.hardware.camera.device@3.2",
"libandroidicu",

View File

@ -24,6 +24,11 @@ using namespace android;
static jclass gAudioDeviceAttributesClass;
static jmethodID gAudioDeviceAttributesCstor;
static struct {
jfieldID mAddress;
jfieldID mNativeType;
// other fields unused by JNI
} gAudioDeviceAttributesFields;
namespace android {
@ -33,12 +38,25 @@ jint createAudioDeviceAttributesFromNative(JNIEnv *env, jobject *jAudioDeviceAtt
jint jNativeType = (jint)devTypeAddr->mType;
ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->getAddress()));
*jAudioDeviceAttributes = env->NewObject(gAudioDeviceAttributesClass, gAudioDeviceAttributesCstor,
jNativeType, jAddress.get());
*jAudioDeviceAttributes =
env->NewObject(gAudioDeviceAttributesClass, gAudioDeviceAttributesCstor,
jNativeType, jAddress.get());
return jStatus;
}
jint createAudioDeviceTypeAddrFromJava(JNIEnv *env, AudioDeviceTypeAddr *devTypeAddr,
const jobject jAudioDeviceAttributes) {
devTypeAddr->mType = (audio_devices_t)env->GetIntField(jAudioDeviceAttributes,
gAudioDeviceAttributesFields.mNativeType);
jstring jAddress = (jstring)env->GetObjectField(jAudioDeviceAttributes,
gAudioDeviceAttributesFields.mAddress);
devTypeAddr->setAddress(ScopedUtfChars(env, jAddress).c_str());
return AUDIO_JAVA_SUCCESS;
}
} // namespace android
int register_android_media_AudioDeviceAttributes(JNIEnv *env) {
@ -48,5 +66,10 @@ int register_android_media_AudioDeviceAttributes(JNIEnv *env) {
gAudioDeviceAttributesCstor =
GetMethodIDOrDie(env, audioDeviceTypeAddressClass, "<init>", "(ILjava/lang/String;)V");
gAudioDeviceAttributesFields.mNativeType =
GetFieldIDOrDie(env, gAudioDeviceAttributesClass, "mNativeType", "I");
gAudioDeviceAttributesFields.mAddress =
GetFieldIDOrDie(env, gAudioDeviceAttributesClass, "mAddress", "Ljava/lang/String;");
return 0;
}

View File

@ -28,6 +28,9 @@ namespace android {
extern jint createAudioDeviceAttributesFromNative(JNIEnv *env, jobject *jAudioDeviceAttributes,
const AudioDeviceTypeAddr *devTypeAddr);
extern jint createAudioDeviceTypeAddrFromJava(JNIEnv *env, AudioDeviceTypeAddr *devTypeAddr,
const jobject jAudioDeviceAttributes);
} // namespace android
#endif

View File

@ -27,6 +27,8 @@
#include "core_jni_helpers.h"
#include <android/media/AudioVibratorInfo.h>
#include <android/media/INativeSpatializerCallback.h>
#include <android/media/ISpatializer.h>
#include <audiomanager/AudioManager.h>
#include <media/AudioPolicy.h>
#include <media/AudioSystem.h>
@ -2023,6 +2025,18 @@ android_media_AudioSystem_registerRoutingCallback(JNIEnv *env, jobject thiz)
AudioSystem::setRoutingCallback(android_media_AudioSystem_routing_callback);
}
void javaAudioFormatToNativeAudioConfig(JNIEnv *env, audio_config_t *nConfig,
const jobject jFormat, bool isInput) {
*nConfig = AUDIO_CONFIG_INITIALIZER;
nConfig->format = audioFormatToNative(env->GetIntField(jFormat, gAudioFormatFields.mEncoding));
nConfig->sample_rate = env->GetIntField(jFormat, gAudioFormatFields.mSampleRate);
jint jChannelMask = env->GetIntField(jFormat, gAudioFormatFields.mChannelMask);
if (isInput) {
nConfig->channel_mask = inChannelMaskToNative(jChannelMask);
} else {
nConfig->channel_mask = outChannelMaskToNative(jChannelMask);
}
}
static jint convertAudioMixToNative(JNIEnv *env,
AudioMix *nAudioMix,
@ -2043,13 +2057,7 @@ static jint convertAudioMixToNative(JNIEnv *env,
nAudioMix->mCbFlags = env->GetIntField(jAudioMix, gAudioMixFields.mCallbackFlags);
jobject jFormat = env->GetObjectField(jAudioMix, gAudioMixFields.mFormat);
nAudioMix->mFormat = AUDIO_CONFIG_INITIALIZER;
nAudioMix->mFormat.sample_rate = env->GetIntField(jFormat,
gAudioFormatFields.mSampleRate);
nAudioMix->mFormat.channel_mask = outChannelMaskToNative(env->GetIntField(jFormat,
gAudioFormatFields.mChannelMask));
nAudioMix->mFormat.format = audioFormatToNative(env->GetIntField(jFormat,
gAudioFormatFields.mEncoding));
javaAudioFormatToNativeAudioConfig(env, &nAudioMix->mFormat, jFormat, false /*isInput*/);
env->DeleteLocalRef(jFormat);
jobject jRule = env->GetObjectField(jAudioMix, gAudioMixFields.mRule);
@ -2712,6 +2720,58 @@ static jint android_media_AudioSystem_setVibratorInfos(JNIEnv *env, jobject thiz
return (jint)check_AudioSystem_Command(AudioSystem::setVibratorInfos(vibratorInfos));
}
static jobject android_media_AudioSystem_getSpatializer(JNIEnv *env, jobject thiz,
jobject jISpatializerCallback) {
sp<media::INativeSpatializerCallback> nISpatializerCallback
= interface_cast<media::INativeSpatializerCallback>(
ibinderForJavaObject(env, jISpatializerCallback));
sp<media::ISpatializer> nSpatializer;
status_t status = AudioSystem::getSpatializer(nISpatializerCallback,
&nSpatializer);
if (status != NO_ERROR) {
return nullptr;
}
return javaObjectForIBinder(env, IInterface::asBinder(nSpatializer));
}
static jboolean android_media_AudioSystem_canBeSpatialized(JNIEnv *env, jobject thiz,
jobject jaa, jobject jFormat,
jobjectArray jDeviceArray) {
JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique();
jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());
if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
return false;
}
AudioDeviceTypeAddrVector nDevices;
const size_t numDevices = env->GetArrayLength(jDeviceArray);
for (size_t i = 0; i < numDevices; ++i) {
AudioDeviceTypeAddr device;
jobject jDevice = env->GetObjectArrayElement(jDeviceArray, i);
if (jDevice == nullptr) {
return false;
}
jStatus = createAudioDeviceTypeAddrFromJava(env, &device, jDevice);
if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
return false;
}
nDevices.push_back(device);
}
audio_config_t nConfig;
javaAudioFormatToNativeAudioConfig(env, &nConfig, jFormat, false /*isInput*/);
bool canBeSpatialized;
status_t status =
AudioSystem::canBeSpatialized(paa.get(), &nConfig, nDevices, &canBeSpatialized);
if (status != NO_ERROR) {
ALOGW("%s native returned error %d", __func__, status);
return false;
}
return canBeSpatialized;
}
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] =
@ -2848,7 +2908,15 @@ static const JNINativeMethod gMethods[] =
(void *)android_media_AudioSystem_removeUserIdDeviceAffinities},
{"setCurrentImeUid", "(I)I", (void *)android_media_AudioSystem_setCurrentImeUid},
{"setVibratorInfos", "(Ljava/util/List;)I",
(void *)android_media_AudioSystem_setVibratorInfos}};
(void *)android_media_AudioSystem_setVibratorInfos},
{"nativeGetSpatializer",
"(Landroid/media/INativeSpatializerCallback;)Landroid/os/IBinder;",
(void *)android_media_AudioSystem_getSpatializer},
{"canBeSpatialized",
"(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;"
"[Landroid/media/AudioDeviceAttributes;)Z",
(void *)android_media_AudioSystem_canBeSpatialized}};
static const JNINativeMethod gEventHandlerMethods[] = {
{"native_setup",

View File

@ -18,6 +18,7 @@ package android.media;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.bluetooth.BluetoothCodecConfig;
@ -2000,6 +2001,46 @@ public class AudioSystem
*/
public static native int setVibratorInfos(@NonNull List<Vibrator> vibrators);
/**
* @hide
* If a spatializer effect is present on the platform, this will return an
* ISpatializer interface to control this feature.
* If no spatializer is present, a null interface is returned.
* The INativeSpatializerCallback passed must not be null.
* Only one ISpatializer interface can exist at a given time. The native audio policy
* service will reject the request if an interface was already acquired and previous owner
* did not die or call ISpatializer.release().
* @param callback the callback to receive state updates if the ISpatializer
* interface is acquired.
* @return the ISpatializer interface made available to control the
* platform spatializer
*/
@Nullable
public static ISpatializer getSpatializer(INativeSpatializerCallback callback) {
return ISpatializer.Stub.asInterface(nativeGetSpatializer(callback));
}
private static native IBinder nativeGetSpatializer(INativeSpatializerCallback callback);
/**
* @hide
* Queries if some kind of spatialization will be performed if the audio playback context
* described by the provided arguments is present.
* The context is made of:
* - The audio attributes describing the playback use case.
* - The audio configuration describing the audio format, channels, sampling rate ...
* - The devices describing the sink audio device selected for playback.
* All arguments are optional and only the specified arguments are used to match against
* supported criteria. For instance, supplying no argument will tell if spatialization is
* supported or not in general.
* @param attributes audio attributes describing the playback use case
* @param format audio configuration describing the audio format, channels, sampling rate...
* @param devices the sink audio device selected for playback
* @return true if spatialization is enabled for this context, false otherwise.
*/
public static native boolean canBeSpatialized(AudioAttributes attributes,
AudioFormat format,
AudioDeviceAttributes[] devices);
// Items shared with audio service
/**