audio policy: add audio port gain control

Implement setAudioPortConfig() API to configure an
audio port.
Currently limited to gain control.

Bug: 14815883.

Change-Id: Ic1b268e5ba5c277e9f5b8fa63c81dd7c0b250024
diff --git a/services/audiopolicy/AudioPolicyClientImpl.cpp b/services/audiopolicy/AudioPolicyClientImpl.cpp
index cbab841..c322d92 100644
--- a/services/audiopolicy/AudioPolicyClientImpl.cpp
+++ b/services/audiopolicy/AudioPolicyClientImpl.cpp
@@ -195,6 +195,13 @@
     return mAudioPolicyService->clientReleaseAudioPatch(handle, delayMs);
 }
 
+status_t AudioPolicyService::AudioPolicyClient::setAudioPortConfig(
+                                                        const struct audio_port_config *config,
+                                                        int delayMs)
+{
+    return mAudioPolicyService->clientSetAudioPortConfig(config, delayMs);
+}
+
 void AudioPolicyService::AudioPolicyClient::onAudioPortListUpdate()
 {
     mAudioPolicyService->onAudioPortListUpdate();
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index e0c7f61..c025a45 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -273,6 +273,9 @@
     virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
                                        int delayMs) = 0;
 
+    /* Set audio port configuration */
+    virtual status_t setAudioPortConfig(const struct audio_port_config *config, int delayMs) = 0;
+
     virtual void onAudioPortListUpdate() = 0;
 
     virtual void onAudioPatchListUpdate() = 0;
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index 87d7771..318f5e5 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -2026,9 +2026,93 @@
     return NO_ERROR;
 }
 
-status_t AudioPolicyManager::setAudioPortConfig(const struct audio_port_config *config __unused)
+status_t AudioPolicyManager::setAudioPortConfig(const struct audio_port_config *config)
 {
-    return NO_ERROR;
+    ALOGV("setAudioPortConfig()");
+
+    if (config == NULL) {
+        return BAD_VALUE;
+    }
+    ALOGV("setAudioPortConfig() on port handle %d", config->id);
+    // Only support gain configuration for now
+    if (config->config_mask != AUDIO_PORT_CONFIG_GAIN || config->gain.index < 0) {
+        return BAD_VALUE;
+    }
+
+    sp<AudioPort> portDesc;
+    struct audio_port_config portConfig;
+    if (config->type == AUDIO_PORT_TYPE_MIX) {
+        if (config->role == AUDIO_PORT_ROLE_SOURCE) {
+            AudioOutputDescriptor *outputDesc = getOutputFromId(config->id);
+            if (outputDesc == NULL) {
+                return BAD_VALUE;
+            }
+            portDesc = outputDesc->mProfile;
+            outputDesc->toAudioPortConfig(&portConfig);
+        } else if (config->role == AUDIO_PORT_ROLE_SINK) {
+            AudioInputDescriptor *inputDesc = getInputFromId(config->id);
+            if (inputDesc == NULL) {
+                return BAD_VALUE;
+            }
+            portDesc = inputDesc->mProfile;
+            inputDesc->toAudioPortConfig(&portConfig);
+        } else {
+            return BAD_VALUE;
+        }
+    } else if (config->type == AUDIO_PORT_TYPE_DEVICE) {
+        sp<DeviceDescriptor> deviceDesc;
+        if (config->role == AUDIO_PORT_ROLE_SOURCE) {
+            deviceDesc = mAvailableInputDevices.getDeviceFromId(config->id);
+        } else if (config->role == AUDIO_PORT_ROLE_SINK) {
+            deviceDesc = mAvailableOutputDevices.getDeviceFromId(config->id);
+        } else {
+            return BAD_VALUE;
+        }
+        if (deviceDesc == NULL) {
+            return BAD_VALUE;
+        }
+        portDesc = deviceDesc;
+        deviceDesc->toAudioPortConfig(&portConfig);
+    } else {
+        return BAD_VALUE;
+    }
+
+    if ((size_t)config->gain.index >= portDesc->mGains.size()) {
+        return INVALID_OPERATION;
+    }
+    const struct audio_gain *gain = &portDesc->mGains[config->gain.index]->mGain;
+    if ((config->gain.mode & ~gain->mode) != 0) {
+        return BAD_VALUE;
+    }
+    if ((config->gain.mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) {
+        if ((config->gain.values[0] < gain->min_value) ||
+                    (config->gain.values[0] > gain->max_value)) {
+            return BAD_VALUE;
+        }
+    } else {
+        if ((config->gain.channel_mask & ~gain->channel_mask) != 0) {
+            return BAD_VALUE;
+        }
+        size_t numValues = popcount(config->gain.channel_mask);
+        for (size_t i = 0; i < numValues; i++) {
+            if ((config->gain.values[i] < gain->min_value) ||
+                    (config->gain.values[i] > gain->max_value)) {
+                return BAD_VALUE;
+            }
+        }
+    }
+    if ((config->gain.mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) {
+        if ((config->gain.ramp_duration_ms < gain->min_ramp_ms) ||
+                    (config->gain.ramp_duration_ms > gain->max_ramp_ms)) {
+            return BAD_VALUE;
+        }
+    }
+
+    portConfig.gain = config->gain;
+
+    status_t status = mpClientInterface->setAudioPortConfig(&portConfig, 0);
+
+    return status;
 }
 
 void AudioPolicyManager::clearAudioPatches(uid_t uid)
@@ -4861,7 +4945,13 @@
         port->formats[i] = mFormats[i];
     }
     port->num_formats = i;
-    port->num_gains = 0;
+
+    ALOGV("AudioPort::toAudioPort() num gains %d", mGains.size());
+
+    for (i = 0; i < mGains.size() && i < AUDIO_PORT_MAX_GAINS; i++) {
+        port->gains[i] = mGains[i]->mGain;
+    }
+    port->num_gains = i;
 }
 
 
@@ -5417,6 +5507,7 @@
 
 void AudioPolicyManager::DeviceDescriptor::toAudioPort(struct audio_port *port) const
 {
+    ALOGV("DeviceVector::toAudioPort() handle %d type %x", mId, mDeviceType);
     AudioPort::toAudioPort(port);
     port->id = mId;
     toAudioPortConfig(&port->active_config);
diff --git a/services/audiopolicy/AudioPolicyService.cpp b/services/audiopolicy/AudioPolicyService.cpp
index f3d92ed..a2a0461 100644
--- a/services/audiopolicy/AudioPolicyService.cpp
+++ b/services/audiopolicy/AudioPolicyService.cpp
@@ -226,6 +226,12 @@
     }
 }
 
+status_t AudioPolicyService::clientSetAudioPortConfig(const struct audio_port_config *config,
+                                                      int delayMs)
+{
+    return mAudioCommandThread->setAudioPortConfigCommand(config, delayMs);
+}
+
 AudioPolicyService::NotificationClient::NotificationClient(const sp<AudioPolicyService>& service,
                                                      const sp<IAudioPolicyServiceClient>& client,
                                                      uid_t uid)
@@ -506,6 +512,16 @@
                     svc->doOnAudioPatchListUpdate();
                     mLock.lock();
                     }break;
+                case SET_AUDIOPORT_CONFIG: {
+                    SetAudioPortConfigData *data = (SetAudioPortConfigData *)command->mParam.get();
+                    ALOGV("AudioCommandThread() processing set port config");
+                    sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+                    if (af == 0) {
+                        command->mStatus = PERMISSION_DENIED;
+                    } else {
+                        command->mStatus = af->setAudioPortConfig(&data->mConfig);
+                    }
+                    } break;
                 default:
                     ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
                 }
@@ -716,6 +732,19 @@
     sendCommand(command);
 }
 
+status_t AudioPolicyService::AudioCommandThread::setAudioPortConfigCommand(
+                                            const struct audio_port_config *config, int delayMs)
+{
+    sp<AudioCommand> command = new AudioCommand();
+    command->mCommand = SET_AUDIOPORT_CONFIG;
+    SetAudioPortConfigData *data = new SetAudioPortConfigData();
+    data->mConfig = *config;
+    command->mParam = data;
+    command->mWaitStatus = true;
+    ALOGV("AudioCommandThread() adding set port config delay %d", delayMs);
+    return sendCommand(command, delayMs);
+}
+
 status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs)
 {
     {
diff --git a/services/audiopolicy/AudioPolicyService.h b/services/audiopolicy/AudioPolicyService.h
index a579d1d..40f589b 100644
--- a/services/audiopolicy/AudioPolicyService.h
+++ b/services/audiopolicy/AudioPolicyService.h
@@ -166,6 +166,9 @@
                                       int delayMs);
             status_t clientReleaseAudioPatch(audio_patch_handle_t handle,
                                              int delayMs);
+            virtual status_t clientSetAudioPortConfig(const struct audio_port_config *config,
+                                                      int delayMs);
+
             void removeNotificationClient(uid_t uid);
             void onAudioPortListUpdate();
             void doOnAudioPortListUpdate();
@@ -200,7 +203,8 @@
             CREATE_AUDIO_PATCH,
             RELEASE_AUDIO_PATCH,
             UPDATE_AUDIOPORT_LIST,
-            UPDATE_AUDIOPATCH_LIST
+            UPDATE_AUDIOPATCH_LIST,
+            SET_AUDIOPORT_CONFIG,
         };
 
         AudioCommandThread (String8 name, const wp<AudioPolicyService>& service);
@@ -234,7 +238,8 @@
                                                          int delayMs);
                     void        updateAudioPortListCommand();
                     void        updateAudioPatchListCommand();
-
+                    status_t    setAudioPortConfigCommand(const struct audio_port_config *config,
+                                                          int delayMs);
                     void        insertCommand_l(AudioCommand *command, int delayMs = 0);
 
     private:
@@ -312,6 +317,11 @@
             audio_patch_handle_t mHandle;
         };
 
+        class SetAudioPortConfigData : public AudioCommandData {
+        public:
+            struct audio_port_config mConfig;
+        };
+
         Mutex   mLock;
         Condition mWaitWorkCV;
         Vector < sp<AudioCommand> > mAudioCommands; // list of pending commands
@@ -465,6 +475,9 @@
         virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
                                            int delayMs);
 
+        /* Set audio port configuration */
+        virtual status_t setAudioPortConfig(const struct audio_port_config *config, int delayMs);
+
         virtual void onAudioPortListUpdate();
         virtual void onAudioPatchListUpdate();