1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "AudioPolicyEffects"
18//#define LOG_NDEBUG 0
19
20#include <stdlib.h>
21#include <stdio.h>
22#include <string.h>
23#include <memory>
24#include <cutils/misc.h>
25#include <media/AudioEffect.h>
26#include <media/EffectsConfig.h>
27#include <system/audio.h>
28#include <system/audio_effects/audio_effects_conf.h>
29#include <utils/Vector.h>
30#include <utils/SortedVector.h>
31#include <cutils/config_utils.h>
32#include <binder/IPCThreadState.h>
33#include "AudioPolicyEffects.h"
34#include "ServiceUtilities.h"
35
36namespace android {
37
38// ----------------------------------------------------------------------------
39// AudioPolicyEffects Implementation
40// ----------------------------------------------------------------------------
41
42AudioPolicyEffects::AudioPolicyEffects()
43{
44    status_t loadResult = loadAudioEffectXmlConfig();
45    if (loadResult < 0) {
46        ALOGW("Failed to load XML effect configuration, fallback to .conf");
47        // load automatic audio effect modules
48        if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
49            loadAudioEffectConfig(AUDIO_EFFECT_VENDOR_CONFIG_FILE);
50        } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) {
51            loadAudioEffectConfig(AUDIO_EFFECT_DEFAULT_CONFIG_FILE);
52        }
53    } else if (loadResult > 0) {
54        ALOGE("Effect config is partially invalid, skipped %d elements", loadResult);
55    }
56}
57
58
59AudioPolicyEffects::~AudioPolicyEffects()
60{
61    size_t i = 0;
62    // release audio input processing resources
63    for (i = 0; i < mInputSources.size(); i++) {
64        delete mInputSources.valueAt(i);
65    }
66    mInputSources.clear();
67
68    for (i = 0; i < mInputSessions.size(); i++) {
69        mInputSessions.valueAt(i)->mEffects.clear();
70        delete mInputSessions.valueAt(i);
71    }
72    mInputSessions.clear();
73
74    // release audio output processing resources
75    for (i = 0; i < mOutputStreams.size(); i++) {
76        delete mOutputStreams.valueAt(i);
77    }
78    mOutputStreams.clear();
79
80    for (i = 0; i < mOutputSessions.size(); i++) {
81        mOutputSessions.valueAt(i)->mEffects.clear();
82        delete mOutputSessions.valueAt(i);
83    }
84    mOutputSessions.clear();
85}
86
87
88status_t AudioPolicyEffects::addInputEffects(audio_io_handle_t input,
89                             audio_source_t inputSource,
90                             audio_session_t audioSession)
91{
92    status_t status = NO_ERROR;
93
94    // create audio pre processors according to input source
95    audio_source_t aliasSource = (inputSource == AUDIO_SOURCE_HOTWORD) ?
96                                    AUDIO_SOURCE_VOICE_RECOGNITION : inputSource;
97
98    Mutex::Autolock _l(mLock);
99    ssize_t index = mInputSources.indexOfKey(aliasSource);
100    if (index < 0) {
101        ALOGV("addInputEffects(): no processing needs to be attached to this source");
102        return status;
103    }
104    ssize_t idx = mInputSessions.indexOfKey(audioSession);
105    EffectVector *sessionDesc;
106    if (idx < 0) {
107        sessionDesc = new EffectVector(audioSession);
108        mInputSessions.add(audioSession, sessionDesc);
109    } else {
110        // EffectVector is existing and we just need to increase ref count
111        sessionDesc = mInputSessions.valueAt(idx);
112    }
113    sessionDesc->mRefCount++;
114
115    ALOGV("addInputEffects(): input: %d, refCount: %d", input, sessionDesc->mRefCount);
116    if (sessionDesc->mRefCount == 1) {
117        int64_t token = IPCThreadState::self()->clearCallingIdentity();
118        Vector <EffectDesc *> effects = mInputSources.valueAt(index)->mEffects;
119        for (size_t i = 0; i < effects.size(); i++) {
120            EffectDesc *effect = effects[i];
121            sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, -1, 0,
122                                                 0, audioSession, input);
123            status_t status = fx->initCheck();
124            if (status != NO_ERROR && status != ALREADY_EXISTS) {
125                ALOGW("addInputEffects(): failed to create Fx %s on source %d",
126                      effect->mName, (int32_t)aliasSource);
127                // fx goes out of scope and strong ref on AudioEffect is released
128                continue;
129            }
130            for (size_t j = 0; j < effect->mParams.size(); j++) {
131                fx->setParameter(effect->mParams[j]);
132            }
133            ALOGV("addInputEffects(): added Fx %s on source: %d",
134                  effect->mName, (int32_t)aliasSource);
135            sessionDesc->mEffects.add(fx);
136        }
137        sessionDesc->setProcessorEnabled(true);
138        IPCThreadState::self()->restoreCallingIdentity(token);
139    }
140    return status;
141}
142
143
144status_t AudioPolicyEffects::releaseInputEffects(audio_io_handle_t input,
145                                                 audio_session_t audioSession)
146{
147    status_t status = NO_ERROR;
148
149    Mutex::Autolock _l(mLock);
150    ssize_t index = mInputSessions.indexOfKey(audioSession);
151    if (index < 0) {
152        return status;
153    }
154    EffectVector *sessionDesc = mInputSessions.valueAt(index);
155    sessionDesc->mRefCount--;
156    ALOGV("releaseInputEffects(): input: %d, refCount: %d", input, sessionDesc->mRefCount);
157    if (sessionDesc->mRefCount == 0) {
158        sessionDesc->setProcessorEnabled(false);
159        delete sessionDesc;
160        mInputSessions.removeItemsAt(index);
161        ALOGV("releaseInputEffects(): all effects released");
162    }
163    return status;
164}
165
166status_t AudioPolicyEffects::queryDefaultInputEffects(audio_session_t audioSession,
167                                                      effect_descriptor_t *descriptors,
168                                                      uint32_t *count)
169{
170    status_t status = NO_ERROR;
171
172    Mutex::Autolock _l(mLock);
173    size_t index;
174    for (index = 0; index < mInputSessions.size(); index++) {
175        if (mInputSessions.valueAt(index)->mSessionId == audioSession) {
176            break;
177        }
178    }
179    if (index == mInputSessions.size()) {
180        *count = 0;
181        return BAD_VALUE;
182    }
183    Vector< sp<AudioEffect> > effects = mInputSessions.valueAt(index)->mEffects;
184
185    for (size_t i = 0; i < effects.size(); i++) {
186        effect_descriptor_t desc = effects[i]->descriptor();
187        if (i < *count) {
188            descriptors[i] = desc;
189        }
190    }
191    if (effects.size() > *count) {
192        status = NO_MEMORY;
193    }
194    *count = effects.size();
195    return status;
196}
197
198
199status_t AudioPolicyEffects::queryDefaultOutputSessionEffects(audio_session_t audioSession,
200                         effect_descriptor_t *descriptors,
201                         uint32_t *count)
202{
203    status_t status = NO_ERROR;
204
205    Mutex::Autolock _l(mLock);
206    size_t index;
207    for (index = 0; index < mOutputSessions.size(); index++) {
208        if (mOutputSessions.valueAt(index)->mSessionId == audioSession) {
209            break;
210        }
211    }
212    if (index == mOutputSessions.size()) {
213        *count = 0;
214        return BAD_VALUE;
215    }
216    Vector< sp<AudioEffect> > effects = mOutputSessions.valueAt(index)->mEffects;
217
218    for (size_t i = 0; i < effects.size(); i++) {
219        effect_descriptor_t desc = effects[i]->descriptor();
220        if (i < *count) {
221            descriptors[i] = desc;
222        }
223    }
224    if (effects.size() > *count) {
225        status = NO_MEMORY;
226    }
227    *count = effects.size();
228    return status;
229}
230
231
232status_t AudioPolicyEffects::addOutputSessionEffects(audio_io_handle_t output,
233                         audio_stream_type_t stream,
234                         audio_session_t audioSession)
235{
236    status_t status = NO_ERROR;
237
238    Mutex::Autolock _l(mLock);
239    // create audio processors according to stream
240    // FIXME: should we have specific post processing settings for internal streams?
241    // default to media for now.
242    if (stream >= AUDIO_STREAM_PUBLIC_CNT) {
243        stream = AUDIO_STREAM_MUSIC;
244    }
245    ssize_t index = mOutputStreams.indexOfKey(stream);
246    if (index < 0) {
247        ALOGV("addOutputSessionEffects(): no output processing needed for this stream");
248        return NO_ERROR;
249    }
250
251    ssize_t idx = mOutputSessions.indexOfKey(audioSession);
252    EffectVector *procDesc;
253    if (idx < 0) {
254        procDesc = new EffectVector(audioSession);
255        mOutputSessions.add(audioSession, procDesc);
256    } else {
257        // EffectVector is existing and we just need to increase ref count
258        procDesc = mOutputSessions.valueAt(idx);
259    }
260    procDesc->mRefCount++;
261
262    ALOGV("addOutputSessionEffects(): session: %d, refCount: %d",
263          audioSession, procDesc->mRefCount);
264    if (procDesc->mRefCount == 1) {
265        // make sure effects are associated to audio server even if we are executing a binder call
266        int64_t token = IPCThreadState::self()->clearCallingIdentity();
267        Vector <EffectDesc *> effects = mOutputStreams.valueAt(index)->mEffects;
268        for (size_t i = 0; i < effects.size(); i++) {
269            EffectDesc *effect = effects[i];
270            sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, 0, 0, 0,
271                                                 audioSession, output);
272            status_t status = fx->initCheck();
273            if (status != NO_ERROR && status != ALREADY_EXISTS) {
274                ALOGE("addOutputSessionEffects(): failed to create Fx  %s on session %d",
275                      effect->mName, audioSession);
276                // fx goes out of scope and strong ref on AudioEffect is released
277                continue;
278            }
279            ALOGV("addOutputSessionEffects(): added Fx %s on session: %d for stream: %d",
280                  effect->mName, audioSession, (int32_t)stream);
281            procDesc->mEffects.add(fx);
282        }
283
284        procDesc->setProcessorEnabled(true);
285        IPCThreadState::self()->restoreCallingIdentity(token);
286    }
287    return status;
288}
289
290status_t AudioPolicyEffects::releaseOutputSessionEffects(audio_io_handle_t output,
291                         audio_stream_type_t stream,
292                         audio_session_t audioSession)
293{
294    status_t status = NO_ERROR;
295    (void) output; // argument not used for now
296    (void) stream; // argument not used for now
297
298    Mutex::Autolock _l(mLock);
299    ssize_t index = mOutputSessions.indexOfKey(audioSession);
300    if (index < 0) {
301        ALOGV("releaseOutputSessionEffects: no output processing was attached to this stream");
302        return NO_ERROR;
303    }
304
305    EffectVector *procDesc = mOutputSessions.valueAt(index);
306    procDesc->mRefCount--;
307    ALOGV("releaseOutputSessionEffects(): session: %d, refCount: %d",
308          audioSession, procDesc->mRefCount);
309    if (procDesc->mRefCount == 0) {
310        procDesc->setProcessorEnabled(false);
311        procDesc->mEffects.clear();
312        delete procDesc;
313        mOutputSessions.removeItemsAt(index);
314        ALOGV("releaseOutputSessionEffects(): output processing released from session: %d",
315              audioSession);
316    }
317    return status;
318}
319
320
321void AudioPolicyEffects::EffectVector::setProcessorEnabled(bool enabled)
322{
323    for (size_t i = 0; i < mEffects.size(); i++) {
324        mEffects.itemAt(i)->setEnabled(enabled);
325    }
326}
327
328
329// ----------------------------------------------------------------------------
330// Audio processing configuration
331// ----------------------------------------------------------------------------
332
333/*static*/ const char * const AudioPolicyEffects::kInputSourceNames[AUDIO_SOURCE_CNT -1] = {
334    MIC_SRC_TAG,
335    VOICE_UL_SRC_TAG,
336    VOICE_DL_SRC_TAG,
337    VOICE_CALL_SRC_TAG,
338    CAMCORDER_SRC_TAG,
339    VOICE_REC_SRC_TAG,
340    VOICE_COMM_SRC_TAG,
341    UNPROCESSED_SRC_TAG
342};
343
344// returns the audio_source_t enum corresponding to the input source name or
345// AUDIO_SOURCE_CNT is no match found
346/*static*/ audio_source_t AudioPolicyEffects::inputSourceNameToEnum(const char *name)
347{
348    int i;
349    for (i = AUDIO_SOURCE_MIC; i < AUDIO_SOURCE_CNT; i++) {
350        if (strcmp(name, kInputSourceNames[i - AUDIO_SOURCE_MIC]) == 0) {
351            ALOGV("inputSourceNameToEnum found source %s %d", name, i);
352            break;
353        }
354    }
355    return (audio_source_t)i;
356}
357
358const char *AudioPolicyEffects::kStreamNames[AUDIO_STREAM_PUBLIC_CNT+1] = {
359    AUDIO_STREAM_DEFAULT_TAG,
360    AUDIO_STREAM_VOICE_CALL_TAG,
361    AUDIO_STREAM_SYSTEM_TAG,
362    AUDIO_STREAM_RING_TAG,
363    AUDIO_STREAM_MUSIC_TAG,
364    AUDIO_STREAM_ALARM_TAG,
365    AUDIO_STREAM_NOTIFICATION_TAG,
366    AUDIO_STREAM_BLUETOOTH_SCO_TAG,
367    AUDIO_STREAM_ENFORCED_AUDIBLE_TAG,
368    AUDIO_STREAM_DTMF_TAG,
369    AUDIO_STREAM_TTS_TAG
370};
371
372// returns the audio_stream_t enum corresponding to the output stream name or
373// AUDIO_STREAM_PUBLIC_CNT is no match found
374audio_stream_type_t AudioPolicyEffects::streamNameToEnum(const char *name)
375{
376    int i;
377    for (i = AUDIO_STREAM_DEFAULT; i < AUDIO_STREAM_PUBLIC_CNT; i++) {
378        if (strcmp(name, kStreamNames[i - AUDIO_STREAM_DEFAULT]) == 0) {
379            ALOGV("streamNameToEnum found stream %s %d", name, i);
380            break;
381        }
382    }
383    return (audio_stream_type_t)i;
384}
385
386// ----------------------------------------------------------------------------
387// Audio Effect Config parser
388// ----------------------------------------------------------------------------
389
390size_t AudioPolicyEffects::growParamSize(char **param,
391                                         size_t size,
392                                         size_t *curSize,
393                                         size_t *totSize)
394{
395    // *curSize is at least sizeof(effect_param_t) + 2 * sizeof(int)
396    size_t pos = ((*curSize - 1 ) / size + 1) * size;
397
398    if (pos + size > *totSize) {
399        while (pos + size > *totSize) {
400            *totSize += ((*totSize + 7) / 8) * 4;
401        }
402        char *newParam = (char *)realloc(*param, *totSize);
403        if (newParam == NULL) {
404            ALOGE("%s realloc error for size %zu", __func__, *totSize);
405            return 0;
406        }
407        *param = newParam;
408    }
409    *curSize = pos + size;
410    return pos;
411}
412
413
414size_t AudioPolicyEffects::readParamValue(cnode *node,
415                                          char **param,
416                                          size_t *curSize,
417                                          size_t *totSize)
418{
419    size_t len = 0;
420    size_t pos;
421
422    if (strncmp(node->name, SHORT_TAG, sizeof(SHORT_TAG) + 1) == 0) {
423        pos = growParamSize(param, sizeof(short), curSize, totSize);
424        if (pos == 0) {
425            goto exit;
426        }
427        *(short *)(*param + pos) = (short)atoi(node->value);
428        ALOGV("readParamValue() reading short %d", *(short *)(*param + pos));
429        len = sizeof(short);
430    } else if (strncmp(node->name, INT_TAG, sizeof(INT_TAG) + 1) == 0) {
431        pos = growParamSize(param, sizeof(int), curSize, totSize);
432        if (pos == 0) {
433            goto exit;
434        }
435        *(int *)(*param + pos) = atoi(node->value);
436        ALOGV("readParamValue() reading int %d", *(int *)(*param + pos));
437        len = sizeof(int);
438    } else if (strncmp(node->name, FLOAT_TAG, sizeof(FLOAT_TAG) + 1) == 0) {
439        pos = growParamSize(param, sizeof(float), curSize, totSize);
440        if (pos == 0) {
441            goto exit;
442        }
443        *(float *)(*param + pos) = (float)atof(node->value);
444        ALOGV("readParamValue() reading float %f",*(float *)(*param + pos));
445        len = sizeof(float);
446    } else if (strncmp(node->name, BOOL_TAG, sizeof(BOOL_TAG) + 1) == 0) {
447        pos = growParamSize(param, sizeof(bool), curSize, totSize);
448        if (pos == 0) {
449            goto exit;
450        }
451        if (strncmp(node->value, "true", strlen("true") + 1) == 0) {
452            *(bool *)(*param + pos) = true;
453        } else {
454            *(bool *)(*param + pos) = false;
455        }
456        ALOGV("readParamValue() reading bool %s",
457              *(bool *)(*param + pos) ? "true" : "false");
458        len = sizeof(bool);
459    } else if (strncmp(node->name, STRING_TAG, sizeof(STRING_TAG) + 1) == 0) {
460        len = strnlen(node->value, EFFECT_STRING_LEN_MAX);
461        if (*curSize + len + 1 > *totSize) {
462            *totSize = *curSize + len + 1;
463            *param = (char *)realloc(*param, *totSize);
464            if (*param == NULL) {
465                len = 0;
466                ALOGE("%s realloc error for string len %zu", __func__, *totSize);
467                goto exit;
468            }
469        }
470        strncpy(*param + *curSize, node->value, len);
471        *curSize += len;
472        (*param)[*curSize] = '\0';
473        ALOGV("readParamValue() reading string %s", *param + *curSize - len);
474    } else {
475        ALOGW("readParamValue() unknown param type %s", node->name);
476    }
477exit:
478    return len;
479}
480
481effect_param_t *AudioPolicyEffects::loadEffectParameter(cnode *root)
482{
483    cnode *param;
484    cnode *value;
485    size_t curSize = sizeof(effect_param_t);
486    size_t totSize = sizeof(effect_param_t) + 2 * sizeof(int);
487    effect_param_t *fx_param = (effect_param_t *)malloc(totSize);
488
489    if (fx_param == NULL) {
490        ALOGE("%s malloc error for effect structure of size %zu",
491              __func__, totSize);
492        return NULL;
493    }
494
495    param = config_find(root, PARAM_TAG);
496    value = config_find(root, VALUE_TAG);
497    if (param == NULL && value == NULL) {
498        // try to parse simple parameter form {int int}
499        param = root->first_child;
500        if (param != NULL) {
501            // Note: that a pair of random strings is read as 0 0
502            int *ptr = (int *)fx_param->data;
503#if LOG_NDEBUG == 0
504            int *ptr2 = (int *)((char *)param + sizeof(effect_param_t));
505            ALOGV("loadEffectParameter() ptr %p ptr2 %p", ptr, ptr2);
506#endif
507            *ptr++ = atoi(param->name);
508            *ptr = atoi(param->value);
509            fx_param->psize = sizeof(int);
510            fx_param->vsize = sizeof(int);
511            return fx_param;
512        }
513    }
514    if (param == NULL || value == NULL) {
515        ALOGW("loadEffectParameter() invalid parameter description %s",
516              root->name);
517        goto error;
518    }
519
520    fx_param->psize = 0;
521    param = param->first_child;
522    while (param) {
523        ALOGV("loadEffectParameter() reading param of type %s", param->name);
524        size_t size =
525                readParamValue(param, (char **)&fx_param, &curSize, &totSize);
526        if (size == 0) {
527            goto error;
528        }
529        fx_param->psize += size;
530        param = param->next;
531    }
532
533    // align start of value field on 32 bit boundary
534    curSize = ((curSize - 1 ) / sizeof(int) + 1) * sizeof(int);
535
536    fx_param->vsize = 0;
537    value = value->first_child;
538    while (value) {
539        ALOGV("loadEffectParameter() reading value of type %s", value->name);
540        size_t size =
541                readParamValue(value, (char **)&fx_param, &curSize, &totSize);
542        if (size == 0) {
543            goto error;
544        }
545        fx_param->vsize += size;
546        value = value->next;
547    }
548
549    return fx_param;
550
551error:
552    free(fx_param);
553    return NULL;
554}
555
556void AudioPolicyEffects::loadEffectParameters(cnode *root, Vector <effect_param_t *>& params)
557{
558    cnode *node = root->first_child;
559    while (node) {
560        ALOGV("loadEffectParameters() loading param %s", node->name);
561        effect_param_t *param = loadEffectParameter(node);
562        if (param != NULL) {
563            params.add(param);
564        }
565        node = node->next;
566    }
567}
568
569
570AudioPolicyEffects::EffectDescVector *AudioPolicyEffects::loadEffectConfig(
571                                                            cnode *root,
572                                                            const Vector <EffectDesc *>& effects)
573{
574    cnode *node = root->first_child;
575    if (node == NULL) {
576        ALOGW("loadInputSource() empty element %s", root->name);
577        return NULL;
578    }
579    EffectDescVector *desc = new EffectDescVector();
580    while (node) {
581        size_t i;
582
583        for (i = 0; i < effects.size(); i++) {
584            if (strncmp(effects[i]->mName, node->name, EFFECT_STRING_LEN_MAX) == 0) {
585                ALOGV("loadEffectConfig() found effect %s in list", node->name);
586                break;
587            }
588        }
589        if (i == effects.size()) {
590            ALOGV("loadEffectConfig() effect %s not in list", node->name);
591            node = node->next;
592            continue;
593        }
594        EffectDesc *effect = new EffectDesc(*effects[i]);   // deep copy
595        loadEffectParameters(node, effect->mParams);
596        ALOGV("loadEffectConfig() adding effect %s uuid %08x",
597              effect->mName, effect->mUuid.timeLow);
598        desc->mEffects.add(effect);
599        node = node->next;
600    }
601    if (desc->mEffects.size() == 0) {
602        ALOGW("loadEffectConfig() no valid effects found in config %s", root->name);
603        delete desc;
604        return NULL;
605    }
606    return desc;
607}
608
609status_t AudioPolicyEffects::loadInputEffectConfigurations(cnode *root,
610                                                           const Vector <EffectDesc *>& effects)
611{
612    cnode *node = config_find(root, PREPROCESSING_TAG);
613    if (node == NULL) {
614        return -ENOENT;
615    }
616    node = node->first_child;
617    while (node) {
618        audio_source_t source = inputSourceNameToEnum(node->name);
619        if (source == AUDIO_SOURCE_CNT) {
620            ALOGW("loadInputSources() invalid input source %s", node->name);
621            node = node->next;
622            continue;
623        }
624        ALOGV("loadInputSources() loading input source %s", node->name);
625        EffectDescVector *desc = loadEffectConfig(node, effects);
626        if (desc == NULL) {
627            node = node->next;
628            continue;
629        }
630        mInputSources.add(source, desc);
631        node = node->next;
632    }
633    return NO_ERROR;
634}
635
636status_t AudioPolicyEffects::loadStreamEffectConfigurations(cnode *root,
637                                                            const Vector <EffectDesc *>& effects)
638{
639    cnode *node = config_find(root, OUTPUT_SESSION_PROCESSING_TAG);
640    if (node == NULL) {
641        return -ENOENT;
642    }
643    node = node->first_child;
644    while (node) {
645        audio_stream_type_t stream = streamNameToEnum(node->name);
646        if (stream == AUDIO_STREAM_PUBLIC_CNT) {
647            ALOGW("loadStreamEffectConfigurations() invalid output stream %s", node->name);
648            node = node->next;
649            continue;
650        }
651        ALOGV("loadStreamEffectConfigurations() loading output stream %s", node->name);
652        EffectDescVector *desc = loadEffectConfig(node, effects);
653        if (desc == NULL) {
654            node = node->next;
655            continue;
656        }
657        mOutputStreams.add(stream, desc);
658        node = node->next;
659    }
660    return NO_ERROR;
661}
662
663AudioPolicyEffects::EffectDesc *AudioPolicyEffects::loadEffect(cnode *root)
664{
665    cnode *node = config_find(root, UUID_TAG);
666    if (node == NULL) {
667        return NULL;
668    }
669    effect_uuid_t uuid;
670    if (AudioEffect::stringToGuid(node->value, &uuid) != NO_ERROR) {
671        ALOGW("loadEffect() invalid uuid %s", node->value);
672        return NULL;
673    }
674    return new EffectDesc(root->name, uuid);
675}
676
677status_t AudioPolicyEffects::loadEffects(cnode *root, Vector <EffectDesc *>& effects)
678{
679    cnode *node = config_find(root, EFFECTS_TAG);
680    if (node == NULL) {
681        return -ENOENT;
682    }
683    node = node->first_child;
684    while (node) {
685        ALOGV("loadEffects() loading effect %s", node->name);
686        EffectDesc *effect = loadEffect(node);
687        if (effect == NULL) {
688            node = node->next;
689            continue;
690        }
691        effects.add(effect);
692        node = node->next;
693    }
694    return NO_ERROR;
695}
696
697status_t AudioPolicyEffects::loadAudioEffectXmlConfig() {
698    auto result = effectsConfig::parse();
699    if (result.parsedConfig == nullptr) {
700        return -ENOENT;
701    }
702
703    auto loadProcessingChain = [](auto& processingChain, auto& streams) {
704        for (auto& stream : processingChain) {
705            auto effectDescs = std::make_unique<EffectDescVector>();
706            for (auto& effect : stream.effects) {
707                effectDescs->mEffects.add(
708                        new EffectDesc{effect.get().name.c_str(), effect.get().uuid});
709            }
710            streams.add(stream.type, effectDescs.release());
711        }
712    };
713    loadProcessingChain(result.parsedConfig->preprocess, mInputSources);
714    loadProcessingChain(result.parsedConfig->postprocess, mOutputStreams);
715    // Casting from ssize_t to status_t is probably safe, there should not be more than 2^31 errors
716    return result.nbSkippedElement;
717}
718
719status_t AudioPolicyEffects::loadAudioEffectConfig(const char *path)
720{
721    cnode *root;
722    char *data;
723
724    data = (char *)load_file(path, NULL);
725    if (data == NULL) {
726        return -ENODEV;
727    }
728    root = config_node("", "");
729    config_load(root, data);
730
731    Vector <EffectDesc *> effects;
732    loadEffects(root, effects);
733    loadInputEffectConfigurations(root, effects);
734    loadStreamEffectConfigurations(root, effects);
735
736    for (size_t i = 0; i < effects.size(); i++) {
737        delete effects[i];
738    }
739
740    config_free(root);
741    free(root);
742    free(data);
743
744    return NO_ERROR;
745}
746
747
748} // namespace android
749