Add TestApis to get currently registered AudioMixes
AudioManager#getRegisteredPolicyMixes will return all AudioMixes that
are registered in the native AudioPolicyManager.
AudioPolicy#getMixes() returns all AudioMixes that are cached in the
AudioPolicy definition.
These APIs are used to help test certain conditions in GTS.
Bug: 309080867
Test: atest AudioHostTest
Change-Id: I92fdc8121f5c60bf391616ba073b0bda3b09f1cf
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 77add41..79766ed 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1917,6 +1917,7 @@
method @FlaggedApi("android.media.audio.focus_freeze_test_api") @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFocusUnmuteDelayAfterFadeOutForTest();
method @Nullable public static android.media.AudioHalVersionInfo getHalVersion();
method public static final int[] getPublicStreamTypes();
+ method @FlaggedApi("android.media.audiopolicy.audio_mix_test_api") @NonNull public java.util.List<android.media.audiopolicy.AudioMix> getRegisteredPolicyMixes();
method @NonNull public java.util.List<java.lang.Integer> getReportedSurroundFormats();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public float getRs2Value();
method public int getStreamMinVolumeInt(int);
@@ -2090,6 +2091,10 @@
package android.media.audiopolicy {
+ public class AudioPolicy {
+ method @FlaggedApi("android.media.audiopolicy.audio_mix_test_api") @NonNull public java.util.List<android.media.audiopolicy.AudioMix> getMixes();
+ }
+
public static class AudioPolicy.Builder {
method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setIsTestFocusPolicy(boolean);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 3fc1683..240028c 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -98,6 +98,7 @@
"libminikin",
"libz",
"server_configurable_flags",
+ "android.media.audiopolicy-aconfig-cc",
],
static_libs: [
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 969e47b..070d07c 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -22,6 +22,7 @@
#include <android/media/INativeSpatializerCallback.h>
#include <android/media/ISpatializer.h>
#include <android/media/audio/common/AudioConfigBase.h>
+#include <android_media_audiopolicy.h>
#include <android_os_Parcel.h>
#include <audiomanager/AudioManager.h>
#include <jni.h>
@@ -55,6 +56,8 @@
// ----------------------------------------------------------------------------
+namespace audio_flags = android::media::audiopolicy;
+
using namespace android;
using media::audio::common::AudioConfigBase;
@@ -145,6 +148,7 @@
} gAudioPatchFields;
static jclass gAudioMixClass;
+static jmethodID gAudioMixCstor;
static struct {
jfieldID mRule;
jfieldID mFormat;
@@ -165,7 +169,15 @@
// other fields unused by JNI
} gAudioFormatFields;
+static jclass gAudioAttributesClass;
+static jmethodID gAudioAttributesCstor;
+static struct {
+ jfieldID mSource;
+ jfieldID mUsage;
+} gAudioAttributesFields;
+
static jclass gAudioMixingRuleClass;
+static jmethodID gAudioMixingRuleCstor;
static struct {
jfieldID mCriteria;
jfieldID mAllowPrivilegedPlaybackCapture;
@@ -174,6 +186,8 @@
} gAudioMixingRuleFields;
static jclass gAudioMixMatchCriterionClass;
+static jmethodID gAudioMixMatchCriterionAttrCstor;
+static jmethodID gAudioMixMatchCriterionIntPropCstor;
static struct {
jfieldID mAttr;
jfieldID mIntProp;
@@ -2087,6 +2101,39 @@
channelMask, channelIndexMask);
}
+jint nativeAudioConfigToJavaAudioFormat(JNIEnv *env, const audio_config_t *nConfigBase,
+ jobject *jAudioFormat, bool isInput) {
+ if (!audio_flags::audio_mix_test_api()) {
+ return AUDIO_JAVA_INVALID_OPERATION;
+ }
+
+ if (nConfigBase == nullptr) {
+ return AUDIO_JAVA_BAD_VALUE;
+ }
+ int propertyMask = AUDIO_FORMAT_HAS_PROPERTY_ENCODING | AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE;
+ int channelMask = 0;
+ int channelIndexMask = 0;
+ switch (audio_channel_mask_get_representation(nConfigBase->channel_mask)) {
+ case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+ channelMask = isInput ? inChannelMaskFromNative(nConfigBase->channel_mask)
+ : outChannelMaskFromNative(nConfigBase->channel_mask);
+ propertyMask |= AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK;
+ break;
+ case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+ channelIndexMask = audio_channel_mask_get_bits(nConfigBase->channel_mask);
+ propertyMask |= AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK;
+ break;
+ default:
+ // This must not happen
+ break;
+ }
+
+ *jAudioFormat = env->NewObject(gAudioFormatClass, gAudioFormatCstor, propertyMask,
+ audioFormatFromNative(nConfigBase->format),
+ nConfigBase->sample_rate, channelMask, channelIndexMask);
+ return AUDIO_JAVA_SUCCESS;
+}
+
jint convertAudioMixerAttributesToNative(JNIEnv *env, const jobject jAudioMixerAttributes,
audio_mixer_attributes_t *nMixerAttributes) {
ScopedLocalRef<jobject> jFormat(env,
@@ -2179,6 +2226,88 @@
return AUDIO_JAVA_SUCCESS;
}
+static jint nativeAudioMixToJavaAudioMixingRule(JNIEnv *env, const AudioMix &nAudioMix,
+ jobject *jAudioMixingRule) {
+ if (!audio_flags::audio_mix_test_api()) {
+ return AUDIO_JAVA_INVALID_OPERATION;
+ }
+
+ jobject jAudioMixMatchCriterionList = env->NewObject(gArrayListClass, gArrayListMethods.cstor);
+ for (const auto &criteria : nAudioMix.mCriteria) {
+ jobject jAudioAttributes = NULL;
+ jobject jMixMatchCriterion = NULL;
+ jobject jValueInteger = NULL;
+ switch (criteria.mRule) {
+ case RULE_MATCH_UID:
+ jValueInteger = env->NewObject(gIntegerClass, gIntegerCstor, criteria.mValue.mUid);
+ jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass,
+ gAudioMixMatchCriterionIntPropCstor,
+ jValueInteger, criteria.mRule);
+ break;
+ case RULE_MATCH_USERID:
+ jValueInteger =
+ env->NewObject(gIntegerClass, gIntegerCstor, criteria.mValue.mUserId);
+ jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass,
+ gAudioMixMatchCriterionIntPropCstor,
+ jValueInteger, criteria.mRule);
+ break;
+ case RULE_MATCH_AUDIO_SESSION_ID:
+ jValueInteger = env->NewObject(gIntegerClass, gIntegerCstor,
+ criteria.mValue.mAudioSessionId);
+ jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass,
+ gAudioMixMatchCriterionIntPropCstor,
+ jValueInteger, criteria.mRule);
+ break;
+ case RULE_MATCH_ATTRIBUTE_USAGE:
+ jAudioAttributes = env->NewObject(gAudioAttributesClass, gAudioAttributesCstor);
+ env->SetIntField(jAudioAttributes, gAudioAttributesFields.mUsage,
+ criteria.mValue.mUsage);
+ jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass,
+ gAudioMixMatchCriterionAttrCstor,
+ jMixMatchCriterion, criteria.mRule);
+ break;
+ case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
+ jAudioAttributes = env->NewObject(gAudioAttributesClass, gAudioAttributesCstor);
+ env->SetIntField(jAudioAttributes, gAudioAttributesFields.mSource,
+ criteria.mValue.mSource);
+ jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass,
+ gAudioMixMatchCriterionAttrCstor,
+ jMixMatchCriterion, criteria.mRule);
+ break;
+ }
+ env->CallBooleanMethod(jAudioMixMatchCriterionList, gArrayListMethods.add,
+ jMixMatchCriterion);
+ }
+
+ *jAudioMixingRule = env->NewObject(gAudioMixingRuleClass, gAudioMixingRuleCstor,
+ nAudioMix.mMixType, jAudioMixMatchCriterionList,
+ nAudioMix.mAllowPrivilegedMediaPlaybackCapture,
+ nAudioMix.mVoiceCommunicationCaptureAllowed);
+ return AUDIO_JAVA_SUCCESS;
+}
+
+static jint convertAudioMixFromNative(JNIEnv *env, jobject *jAudioMix, const AudioMix &nAudioMix) {
+ if (!audio_flags::audio_mix_test_api()) {
+ return AUDIO_JAVA_INVALID_OPERATION;
+ }
+ jobject jAudioMixingRule = NULL;
+ int status = nativeAudioMixToJavaAudioMixingRule(env, nAudioMix, &jAudioMixingRule);
+ if (status != AUDIO_JAVA_SUCCESS) {
+ return status;
+ }
+ jobject jAudioFormat = NULL;
+ status = nativeAudioConfigToJavaAudioFormat(env, &nAudioMix.mFormat, &jAudioFormat, false);
+ if (status != AUDIO_JAVA_SUCCESS) {
+ return status;
+ }
+
+ jstring deviceAddress = env->NewStringUTF(nAudioMix.mDeviceAddress.c_str());
+ *jAudioMix = env->NewObject(gAudioMixClass, gAudioMixCstor, jAudioMixingRule, jAudioFormat,
+ nAudioMix.mRouteFlags, nAudioMix.mCbFlags, nAudioMix.mDeviceType,
+ deviceAddress);
+ return AUDIO_JAVA_SUCCESS;
+}
+
static jint convertAudioMixToNative(JNIEnv *env, AudioMix *nAudioMix, const jobject jAudioMix) {
nAudioMix->mMixType = env->GetIntField(jAudioMix, gAudioMixFields.mMixType);
nAudioMix->mRouteFlags = env->GetIntField(jAudioMix, gAudioMixFields.mRouteFlags);
@@ -2252,6 +2381,34 @@
return nativeToJavaStatus(status);
}
+static jint android_media_AudioSystem_getRegisteredPolicyMixes(JNIEnv *env, jobject clazz,
+ jobject jMixes) {
+ if (!audio_flags::audio_mix_test_api()) {
+ return AUDIO_JAVA_INVALID_OPERATION;
+ }
+
+ status_t status;
+ std::vector<AudioMix> mixes;
+ ALOGV("AudioSystem::getRegisteredPolicyMixes");
+ status = AudioSystem::getRegisteredPolicyMixes(mixes);
+ ALOGV("AudioSystem::getRegisteredPolicyMixes() returned %zu mixes. Status=%d", mixes.size(),
+ status);
+ if (status != NO_ERROR) {
+ return nativeToJavaStatus(status);
+ }
+
+ for (const auto &mix : mixes) {
+ jobject jAudioMix = NULL;
+ int conversionStatus = convertAudioMixFromNative(env, &jAudioMix, mix);
+ if (conversionStatus != AUDIO_JAVA_SUCCESS) {
+ return conversionStatus;
+ }
+ env->CallBooleanMethod(jMixes, gListMethods.add, jAudioMix);
+ }
+
+ return AUDIO_JAVA_SUCCESS;
+}
+
static jint android_media_AudioSystem_updatePolicyMixes(JNIEnv *env, jobject clazz,
jobjectArray mixes,
jobjectArray updatedMixingRules) {
@@ -3251,6 +3408,8 @@
MAKE_AUDIO_SYSTEM_METHOD(getAudioHwSyncForSession),
MAKE_JNI_NATIVE_METHOD("registerPolicyMixes", "(Ljava/util/ArrayList;Z)I",
android_media_AudioSystem_registerPolicyMixes),
+ MAKE_JNI_NATIVE_METHOD("getRegisteredPolicyMixes", "(Ljava/util/List;)I",
+ android_media_AudioSystem_getRegisteredPolicyMixes),
MAKE_JNI_NATIVE_METHOD("updatePolicyMixes",
"([Landroid/media/audiopolicy/AudioMix;[Landroid/media/audiopolicy/"
"AudioMixingRule;)I",
@@ -3499,6 +3658,11 @@
jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix");
gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass);
+ if (audio_flags::audio_mix_test_api()) {
+ gAudioMixCstor = GetMethodIDOrDie(env, audioMixClass, "<init>",
+ "(Landroid/media/audiopolicy/AudioMixingRule;Landroid/"
+ "media/AudioFormat;IIILjava/lang/String;)V");
+ }
gAudioMixFields.mRule = GetFieldIDOrDie(env, audioMixClass, "mRule",
"Landroid/media/audiopolicy/AudioMixingRule;");
gAudioMixFields.mFormat = GetFieldIDOrDie(env, audioMixClass, "mFormat",
@@ -3521,6 +3685,10 @@
jclass audioMixingRuleClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMixingRule");
gAudioMixingRuleClass = MakeGlobalRefOrDie(env, audioMixingRuleClass);
+ if (audio_flags::audio_mix_test_api()) {
+ gAudioMixingRuleCstor = GetMethodIDOrDie(env, audioMixingRuleClass, "<init>",
+ "(ILjava/util/Collection;ZZ)V");
+ }
gAudioMixingRuleFields.mCriteria = GetFieldIDOrDie(env, audioMixingRuleClass, "mCriteria",
"Ljava/util/ArrayList;");
gAudioMixingRuleFields.mAllowPrivilegedPlaybackCapture =
@@ -3529,9 +3697,24 @@
gAudioMixingRuleFields.mVoiceCommunicationCaptureAllowed =
GetFieldIDOrDie(env, audioMixingRuleClass, "mVoiceCommunicationCaptureAllowed", "Z");
+ if (audio_flags::audio_mix_test_api()) {
+ jclass audioAttributesClass = FindClassOrDie(env, "android/media/AudioAttributes");
+ gAudioAttributesClass = MakeGlobalRefOrDie(env, audioAttributesClass);
+ gAudioAttributesCstor = GetMethodIDOrDie(env, gAudioAttributesClass, "<init>", "()V");
+ gAudioAttributesFields.mSource = GetFieldIDOrDie(env, gAudioAttributesClass, "mUsage", "I");
+ gAudioAttributesFields.mUsage = GetFieldIDOrDie(env, gAudioAttributesClass, "mSource", "I");
+ }
+
jclass audioMixMatchCriterionClass =
FindClassOrDie(env, "android/media/audiopolicy/AudioMixingRule$AudioMixMatchCriterion");
gAudioMixMatchCriterionClass = MakeGlobalRefOrDie(env,audioMixMatchCriterionClass);
+ if (audio_flags::audio_mix_test_api()) {
+ gAudioMixMatchCriterionAttrCstor =
+ GetMethodIDOrDie(env, gAudioMixMatchCriterionClass, "<init>",
+ "(Landroid/media/AudioAttributes;I)V");
+ gAudioMixMatchCriterionIntPropCstor = GetMethodIDOrDie(env, gAudioMixMatchCriterionClass,
+ "<init>", "(Ljava/lang/Integer;I)V");
+ }
gAudioMixMatchCriterionFields.mAttr = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mAttr",
"Landroid/media/AudioAttributes;");
gAudioMixMatchCriterionFields.mIntProp = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mIntProp",
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 69708ec..b6f1004 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -5514,6 +5514,26 @@
/**
* @hide
+ * @return All currently registered audio policy mixes.
+ */
+ @TestApi
+ @FlaggedApi(android.media.audiopolicy.Flags.FLAG_AUDIO_MIX_TEST_API)
+ @NonNull
+ public List<android.media.audiopolicy.AudioMix> getRegisteredPolicyMixes() {
+ if (!android.media.audiopolicy.Flags.audioMixTestApi()) {
+ return Collections.emptyList();
+ }
+
+ final IAudioService service = getService();
+ try {
+ return service.getRegisteredPolicyMixes();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
* @return true if an AudioPolicy was previously registered
*/
@TestApi
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index f73be35f..293c561 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1984,6 +1984,9 @@
public static native int registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register);
/** @hide */
+ public static native int getRegisteredPolicyMixes(@NonNull List<AudioMix> devices);
+
+ /** @hide */
public static native int updatePolicyMixes(
AudioMix[] mixes,
AudioMixingRule[] updatedMixingRules);
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 8dfa6be..98bd3ca 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -365,6 +365,8 @@
oneway void unregisterAudioPolicyAsync(in IAudioPolicyCallback pcb);
+ List<AudioMix> getRegisteredPolicyMixes();
+
void unregisterAudioPolicy(in IAudioPolicyCallback pcb);
int addMixForPolicy(in AudioPolicyConfig policyConfig, in IAudioPolicyCallback pcb);
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index b85decc..bbe461c 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -58,6 +58,7 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -597,6 +598,21 @@
setRegistration(null);
}
+ /**
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ @FlaggedApi(Flags.FLAG_AUDIO_MIX_TEST_API)
+ public List<AudioMix> getMixes() {
+ if (!Flags.audioMixTestApi()) {
+ return Collections.emptyList();
+ }
+ synchronized (mLock) {
+ return List.copyOf(mConfig.getMixes());
+ }
+ }
+
public void setRegistration(String regId) {
synchronized (mLock) {
mRegistrationId = regId;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index bbbba26..04deb02 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -12458,6 +12458,20 @@
return app;
}
+ /**
+ * Retrieves all audioMixes registered with the AudioPolicyManager
+ * @return list of registered audio mixes
+ */
+ public List<AudioMix> getRegisteredPolicyMixes() {
+ if (!android.media.audiopolicy.Flags.audioMixTestApi()) {
+ return Collections.emptyList();
+ }
+
+ synchronized (mAudioPolicies) {
+ return mAudioSystem.getRegisteredPolicyMixes();
+ }
+ }
+
public int addMixForPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb) {
if (DEBUG_AP) { Log.d(TAG, "addMixForPolicy for " + pcb.asBinder()
+ " with config:" + policyConfig); }
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 4f46dd1..49ab19a 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -27,6 +27,7 @@
import android.media.ISoundDoseCallback;
import android.media.audiopolicy.AudioMix;
import android.media.audiopolicy.AudioMixingRule;
+import android.media.audiopolicy.Flags;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -42,6 +43,7 @@
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -602,6 +604,23 @@
}
/**
+ * @return a list of AudioMixes that are registered in the audio policy manager.
+ */
+ public List<AudioMix> getRegisteredPolicyMixes() {
+ if (!Flags.audioMixTestApi()) {
+ return Collections.emptyList();
+ }
+
+ List<AudioMix> audioMixes = new ArrayList<>();
+ int result = AudioSystem.getRegisteredPolicyMixes(audioMixes);
+ if (result != AudioSystem.SUCCESS) {
+ throw new IllegalStateException(
+ "Cannot fetch registered policy mixes. Result: " + result);
+ }
+ return Collections.unmodifiableList(audioMixes);
+ }
+
+ /**
* Update already {@link AudioMixingRule}-s for already registered {@link AudioMix}-es.
*
* @param mixes - array of registered {@link AudioMix}-es to update.