AudioSystem JNI: Add audio policy custom mixes registration

Bug: 16009464.

Change-Id: I3763e79f4fa0331acb56d1d6f575c7a36e0bebd5
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index cabe200..fee1ead 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -25,6 +25,7 @@
 #include <android_runtime/AndroidRuntime.h>
 
 #include <media/AudioSystem.h>
+#include <media/AudioPolicy.h>
 
 #include <system/audio.h>
 #include <system/audio_policy.h>
@@ -40,6 +41,7 @@
 static jclass gArrayListClass;
 static struct {
     jmethodID    add;
+    jmethodID    toArray;
 } gArrayListMethods;
 
 static jclass gAudioHandleClass;
@@ -102,6 +104,42 @@
     // other fields unused by JNI
 } gAudioPatchFields;
 
+static jclass gAudioMixClass;
+static struct {
+    jfieldID    mRule;
+    jfieldID    mFormat;
+    jfieldID    mRouteFlags;
+    jfieldID    mRegistrationId;
+    jfieldID    mMixType;
+} gAudioMixFields;
+
+static jclass gAudioFormatClass;
+static struct {
+    jfieldID    mEncoding;
+    jfieldID    mSampleRate;
+    jfieldID    mChannelMask;
+    // other fields unused by JNI
+} gAudioFormatFields;
+
+static jclass gAudioMixingRuleClass;
+static struct {
+    jfieldID    mCriteria;
+    // other fields unused by JNI
+} gAudioMixingRuleFields;
+
+static jclass gAttributeMatchCriterionClass;
+static struct {
+    jfieldID    mAttr;
+    jfieldID    mRule;
+} gAttributeMatchCriterionFields;
+
+static jclass gAudioAttributesClass;
+static struct {
+    jfieldID    mUsage;
+    jfieldID    mSource;
+} gAudioAttributesFields;
+
+
 static const char* const kEventHandlerClassPathName =
         "android/media/AudioPortEventHandler";
 static jmethodID gPostEventFromNative;
@@ -1322,6 +1360,128 @@
     return (jint)AudioSystem::getAudioHwSyncForSession((audio_session_t)sessionId);
 }
 
+
+
+
+static jint convertAudioMixToNative(JNIEnv *env,
+                                    AudioMix *nAudioMix,
+                                    const jobject jAudioMix)
+{
+    nAudioMix->mMixType = env->GetIntField(jAudioMix, gAudioMixFields.mMixType);
+    nAudioMix->mRouteFlags = env->GetIntField(jAudioMix, gAudioMixFields.mRouteFlags);
+
+    jstring jRegistrationId = (jstring)env->GetObjectField(jAudioMix,
+                                                           gAudioMixFields.mRegistrationId);
+    const char *nRegistrationId = env->GetStringUTFChars(jRegistrationId, NULL);
+    nAudioMix->mRegistrationId = String8(nRegistrationId);
+    env->ReleaseStringUTFChars(jRegistrationId, nRegistrationId);
+    env->DeleteLocalRef(jRegistrationId);
+
+    jobject jFormat = env->GetObjectField(jAudioMix, gAudioMixFields.mFormat);
+    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));
+    env->DeleteLocalRef(jFormat);
+
+    jobject jRule = env->GetObjectField(jAudioMix, gAudioMixFields.mRule);
+    jobject jRuleCriteria = env->GetObjectField(jRule, gAudioMixingRuleFields.mCriteria);
+    env->DeleteLocalRef(jRule);
+    jobjectArray jCriteria = (jobjectArray)env->CallObjectMethod(jRuleCriteria,
+                                                                 gArrayListMethods.toArray);
+    env->DeleteLocalRef(jRuleCriteria);
+
+    jint numCriteria = env->GetArrayLength(jCriteria);
+    if (numCriteria > MAX_CRITERIA_PER_MIX) {
+        numCriteria = MAX_CRITERIA_PER_MIX;
+    }
+
+    for (jint i = 0; i < numCriteria; i++) {
+        AttributeMatchCriterion nCriterion;
+
+        jobject jCriterion = env->GetObjectArrayElement(jCriteria, i);
+
+        nCriterion.mRule = env->GetIntField(jCriterion, gAttributeMatchCriterionFields.mRule);
+
+        jobject jAttributes = env->GetObjectField(jCriterion, gAttributeMatchCriterionFields.mAttr);
+        if (nCriterion.mRule == RULE_MATCH_ATTRIBUTE_USAGE ||
+                nCriterion.mRule == RULE_EXCLUDE_ATTRIBUTE_USAGE) {
+            nCriterion.mAttr.mUsage = (audio_usage_t)env->GetIntField(jAttributes,
+                                                       gAudioAttributesFields.mUsage);
+        } else {
+            nCriterion.mAttr.mSource = (audio_source_t)env->GetIntField(jAttributes,
+                                                        gAudioAttributesFields.mSource);
+        }
+        env->DeleteLocalRef(jAttributes);
+
+        nAudioMix->mCriteria.add(nCriterion);
+        env->DeleteLocalRef(jCriterion);
+    }
+
+    env->DeleteLocalRef(jCriteria);
+
+    return (jint)AUDIO_JAVA_SUCCESS;
+}
+
+static jint
+android_media_AudioSystem_registerPolicyMixes(JNIEnv *env, jobject clazz,
+                                              jobject jMixesList, jboolean registration)
+{
+    ALOGV("registerPolicyMixes");
+
+    if (jMixesList == NULL) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    if (!env->IsInstanceOf(jMixesList, gArrayListClass)) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    jobjectArray jMixes = (jobjectArray)env->CallObjectMethod(jMixesList,
+                                                              gArrayListMethods.toArray);
+    jint numMixes = env->GetArrayLength(jMixes);
+    if (numMixes > MAX_MIXES_PER_POLICY) {
+        numMixes = MAX_MIXES_PER_POLICY;
+    }
+
+    status_t status;
+    jint jStatus;
+    jobject jAudioMix = NULL;
+    Vector <AudioMix> mixes;
+    for (jint i = 0; i < numMixes; i++) {
+        jAudioMix = env->GetObjectArrayElement(jMixes, i);
+        if (!env->IsInstanceOf(jAudioMix, gAudioMixClass)) {
+            jStatus = (jint)AUDIO_JAVA_BAD_VALUE;
+            goto exit;
+        }
+        AudioMix mix;
+        jStatus = convertAudioMixToNative(env, &mix, jAudioMix);
+        env->DeleteLocalRef(jAudioMix);
+        jAudioMix = NULL;
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            goto exit;
+        }
+        mixes.add(mix);
+    }
+
+    ALOGV("AudioSystem::registerPolicyMixes numMixes %d registration %d", numMixes, registration);
+    status = AudioSystem::registerPolicyMixes(mixes, registration);
+    ALOGV("AudioSystem::registerPolicyMixes() returned %d", status);
+
+    jStatus = nativeToJavaStatus(status);
+    if (jStatus != AUDIO_JAVA_SUCCESS) {
+        goto exit;
+    }
+
+exit:
+    if (jAudioMix != NULL) {
+        env->DeleteLocalRef(jAudioMix);
+    }
+    return jStatus;
+}
+
+
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
@@ -1363,6 +1523,9 @@
                                             (void *)android_media_AudioSystem_setAudioPortConfig},
     {"getAudioHwSyncForSession", "(I)I",
                                     (void *)android_media_AudioSystem_getAudioHwSyncForSession},
+    {"registerPolicyMixes",    "(Ljava/util/ArrayList;Z)I",
+                                            (void *)android_media_AudioSystem_registerPolicyMixes},
+
 };
 
 
@@ -1381,6 +1544,7 @@
     jclass arrayListClass = env->FindClass("java/util/ArrayList");
     gArrayListClass = (jclass) env->NewGlobalRef(arrayListClass);
     gArrayListMethods.add = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
+    gArrayListMethods.toArray = env->GetMethodID(arrayListClass, "toArray", "()[Ljava/lang/Object;");
 
     jclass audioHandleClass = env->FindClass("android/media/AudioHandle");
     gAudioHandleClass = (jclass) env->NewGlobalRef(audioHandleClass);
@@ -1462,6 +1626,41 @@
                                             "(Ljava/lang/Object;IIILjava/lang/Object;)V");
 
 
+    jclass audioMixClass = env->FindClass("android/media/audiopolicy/AudioMix");
+    gAudioMixClass = (jclass) env->NewGlobalRef(audioMixClass);
+    gAudioMixFields.mRule = env->GetFieldID(audioMixClass, "mRule",
+                                                "Landroid/media/audiopolicy/AudioMixingRule;");
+    gAudioMixFields.mFormat = env->GetFieldID(audioMixClass, "mFormat",
+                                                "Landroid/media/AudioFormat;");
+    gAudioMixFields.mRouteFlags = env->GetFieldID(audioMixClass, "mRouteFlags", "I");
+    gAudioMixFields.mRegistrationId = env->GetFieldID(audioMixClass, "mRegistrationId",
+                                                      "Ljava/lang/String;");
+    gAudioMixFields.mMixType = env->GetFieldID(audioMixClass, "mMixType", "I");
+
+    jclass audioFormatClass = env->FindClass("android/media/AudioFormat");
+    gAudioFormatClass = (jclass) env->NewGlobalRef(audioFormatClass);
+    gAudioFormatFields.mEncoding = env->GetFieldID(audioFormatClass, "mEncoding", "I");
+    gAudioFormatFields.mSampleRate = env->GetFieldID(audioFormatClass, "mSampleRate", "I");
+    gAudioFormatFields.mChannelMask = env->GetFieldID(audioFormatClass, "mChannelMask", "I");
+
+    jclass audioMixingRuleClass = env->FindClass("android/media/audiopolicy/AudioMixingRule");
+    gAudioMixingRuleClass = (jclass) env->NewGlobalRef(audioMixingRuleClass);
+    gAudioMixingRuleFields.mCriteria = env->GetFieldID(audioMixingRuleClass, "mCriteria",
+                                                       "Ljava/util/ArrayList;");
+
+    jclass attributeMatchCriterionClass =
+                env->FindClass("android/media/audiopolicy/AudioMixingRule$AttributeMatchCriterion");
+    gAttributeMatchCriterionClass = (jclass) env->NewGlobalRef(attributeMatchCriterionClass);
+    gAttributeMatchCriterionFields.mAttr = env->GetFieldID(attributeMatchCriterionClass, "mAttr",
+                                                       "Landroid/media/AudioAttributes;");
+    gAttributeMatchCriterionFields.mRule = env->GetFieldID(attributeMatchCriterionClass, "mRule",
+                                                       "I");
+
+    jclass audioAttributesClass = env->FindClass("android/media/AudioAttributes");
+    gAudioAttributesClass = (jclass) env->NewGlobalRef(audioAttributesClass);
+    gAudioAttributesFields.mUsage = env->GetFieldID(audioAttributesClass, "mUsage", "I");
+    gAudioAttributesFields.mSource = env->GetFieldID(audioAttributesClass, "mSource", "I");
+
     AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback);
 
     int status = AndroidRuntime::registerNativeMethods(env,
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 03ecad2..ebb7380 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -4298,6 +4298,13 @@
                             }
                         }
                     }
+
+                    synchronized (mAudioPolicies) {
+                        for(AudioPolicyProxy policy : mAudioPolicies.values()) {
+                            policy.connectMixes();
+                        }
+                    }
+
                     // indicate the end of reconfiguration phase to audio HAL
                     AudioSystem.setParameters("restarting=false");
                     break;
@@ -5938,7 +5945,7 @@
             if (mHasFocusListener) {
                 mMediaFocusControl.addFocusFollower(mPolicyToken);
             }
-            updateMixes(AudioSystem.DEVICE_STATE_AVAILABLE);
+            connectMixes();
         }
 
         public void binderDied() {
@@ -5960,23 +5967,11 @@
             if (mHasFocusListener) {
                 mMediaFocusControl.removeFocusFollower(mPolicyToken);
             }
-            updateMixes(AudioSystem.DEVICE_STATE_UNAVAILABLE);
+            AudioSystem.registerPolicyMixes(mMixes, false);
         }
 
-        void updateMixes(int connectionState) {
-            for (AudioMix mix : mMixes) {
-                // TODO implement sending the mix attribute matching info to native audio policy
-                if (DEBUG_AP) {
-                    Log.v(TAG, "AudioPolicyProxy mix new connection state=" + connectionState
-                            + " addr=" + mix.getRegistration());
-                }
-                AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_REMOTE_SUBMIX,
-                        connectionState,
-                        mix.getRegistration());
-                AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX,
-                        connectionState,
-                        mix.getRegistration());
-            }
+        void connectMixes() {
+            AudioSystem.registerPolicyMixes(mMixes, true);
         }
     };
 
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index e795fa7..46ab7e0 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.media.audiopolicy.AudioMix;
 import java.util.ArrayList;
 
 /* IF YOU CHANGE ANY OF THE CONSTANTS IN THIS FILE, DO NOT FORGET
@@ -566,5 +567,7 @@
     public static final int AUDIO_HW_SYNC_INVALID = 0;
 
     public static native int getAudioHwSyncForSession(int sessionId);
+
+    public static native int registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register);
 }