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