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.