1/*
2 * Copyright (C) 2015 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 "APM::AudioPolicyEngine/PFWWrapper"
18//#define LOG_NDEBUG 0
19
20#include "ParameterManagerWrapper.h"
21#include "audio_policy_criteria_conf.h"
22#include <ParameterMgrPlatformConnector.h>
23#include <SelectionCriterionTypeInterface.h>
24#include <SelectionCriterionInterface.h>
25#include <media/convert.h>
26#include <algorithm>
27#include <cutils/config_utils.h>
28#include <cutils/misc.h>
29#include <fstream>
30#include <limits>
31#include <sstream>
32#include <string>
33#include <vector>
34#include <stdint.h>
35#include <cmath>
36#include <utils/Log.h>
37
38using std::string;
39using std::map;
40using std::vector;
41
42/// PFW related definitions
43// Logger
44class ParameterMgrPlatformConnectorLogger : public CParameterMgrPlatformConnector::ILogger
45{
46public:
47    ParameterMgrPlatformConnectorLogger() {}
48
49    virtual void info(const string &log)
50    {
51        ALOGV("policy-parameter-manager: %s", log.c_str());
52    }
53    virtual void warning(const string &log)
54    {
55        ALOGW("policy-parameter-manager: %s", log.c_str());
56    }
57};
58
59namespace android
60{
61
62using utilities::convertTo;
63
64namespace audio_policy
65{
66const char *const ParameterManagerWrapper::mPolicyPfwDefaultConfFileName =
67    "/etc/parameter-framework/ParameterFrameworkConfigurationPolicy.xml";
68
69template <>
70struct ParameterManagerWrapper::parameterManagerElementSupported<ISelectionCriterionInterface> {};
71template <>
72struct ParameterManagerWrapper::parameterManagerElementSupported<ISelectionCriterionTypeInterface> {};
73
74ParameterManagerWrapper::ParameterManagerWrapper()
75    : mPfwConnectorLogger(new ParameterMgrPlatformConnectorLogger)
76{
77    // Connector
78    mPfwConnector = new CParameterMgrPlatformConnector(mPolicyPfwDefaultConfFileName);
79
80    // Logger
81    mPfwConnector->setLogger(mPfwConnectorLogger);
82
83    // Load criteria file
84    if ((loadAudioPolicyCriteriaConfig(gAudioPolicyCriteriaVendorConfFilePath) != NO_ERROR) &&
85        (loadAudioPolicyCriteriaConfig(gAudioPolicyCriteriaConfFilePath) != NO_ERROR)) {
86        ALOGE("%s: Neither vendor conf file (%s) nor system conf file (%s) could be found",
87              __FUNCTION__, gAudioPolicyCriteriaVendorConfFilePath,
88              gAudioPolicyCriteriaConfFilePath);
89    }
90}
91
92ParameterManagerWrapper::~ParameterManagerWrapper()
93{
94    // Unset logger
95    mPfwConnector->setLogger(NULL);
96    // Remove logger
97    delete mPfwConnectorLogger;
98    // Remove connector
99    delete mPfwConnector;
100}
101
102status_t ParameterManagerWrapper::start()
103{
104    ALOGD("%s: in", __FUNCTION__);
105    /// Start PFW
106    std::string error;
107    if (!mPfwConnector->start(error)) {
108        ALOGE("%s: Policy PFW start error: %s", __FUNCTION__, error.c_str());
109        return NO_INIT;
110    }
111    ALOGD("%s: Policy PFW successfully started!", __FUNCTION__);
112    return NO_ERROR;
113}
114
115
116void ParameterManagerWrapper::addCriterionType(const string &typeName, bool isInclusive)
117{
118    ALOG_ASSERT(mPolicyCriterionTypes.find(typeName) == mPolicyCriterionTypes.end(),
119                      "CriterionType %s already added", typeName.c_str());
120    ALOGD("%s: Adding new criterionType %s", __FUNCTION__, typeName.c_str());
121
122    mPolicyCriterionTypes[typeName] = mPfwConnector->createSelectionCriterionType(isInclusive);
123}
124
125void ParameterManagerWrapper::addCriterionTypeValuePair(
126    const string &typeName,
127    uint32_t numericValue,
128    const string &literalValue)
129{
130    ALOG_ASSERT(mPolicyCriterionTypes.find(typeName) != mPolicyCriterionTypes.end(),
131                      "CriterionType %s not found", typeName.c_str());
132    ALOGV("%s: Adding new value pair (%d,%s) for criterionType %s", __FUNCTION__,
133          numericValue, literalValue.c_str(), typeName.c_str());
134    ISelectionCriterionTypeInterface *criterionType = mPolicyCriterionTypes[typeName];
135    std::string error;
136    criterionType->addValuePair(numericValue, literalValue, error);
137}
138
139void ParameterManagerWrapper::loadCriterionType(cnode *root, bool isInclusive)
140{
141    ALOG_ASSERT(root != NULL, "error in parsing file");
142    cnode *node;
143    for (node = root->first_child; node != NULL; node = node->next) {
144
145        ALOG_ASSERT(node != NULL, "error in parsing file");
146        const char *typeName = node->name;
147        char *valueNames = strndup(node->value, strlen(node->value));
148
149        addCriterionType(typeName, isInclusive);
150
151        uint32_t index = 0;
152        char *ctx;
153        char *valueName = strtok_r(valueNames, ",", &ctx);
154        while (valueName != NULL) {
155            if (strlen(valueName) != 0) {
156
157                // Conf file may use or not pair, if no pair, use incremental index, else
158                // use provided index.
159                if (strchr(valueName, ':') != NULL) {
160
161                    char *first = strtok(valueName, ":");
162                    char *second = strtok(NULL, ":");
163                    ALOG_ASSERT((first != NULL) && (strlen(first) != 0) &&
164                                      (second != NULL) && (strlen(second) != 0),
165                                      "invalid value pair");
166
167                    if (!convertTo<string, uint32_t>(first, index)) {
168                        ALOGE("%s: Invalid index(%s) found", __FUNCTION__, first);
169                    }
170                    addCriterionTypeValuePair(typeName, index, second);
171                } else {
172
173                    uint32_t pfwIndex = isInclusive ? 1 << index : index;
174                    addCriterionTypeValuePair(typeName, pfwIndex, valueName);
175                    index += 1;
176                }
177            }
178            valueName = strtok_r(NULL, ",", &ctx);
179        }
180        free(valueNames);
181    }
182}
183
184void ParameterManagerWrapper::loadInclusiveCriterionType(cnode *root)
185{
186    ALOG_ASSERT(root != NULL, "error in parsing file");
187    cnode *node = config_find(root, gInclusiveCriterionTypeTag.c_str());
188    if (node == NULL) {
189        return;
190    }
191    loadCriterionType(node, true);
192}
193
194void ParameterManagerWrapper::loadExclusiveCriterionType(cnode *root)
195{
196    ALOG_ASSERT(root != NULL, "error in parsing file");
197    cnode *node = config_find(root, gExclusiveCriterionTypeTag.c_str());
198    if (node == NULL) {
199        return;
200    }
201    loadCriterionType(node, false);
202}
203
204void ParameterManagerWrapper::parseChildren(cnode *root, string &defaultValue, string &type)
205{
206    ALOG_ASSERT(root != NULL, "error in parsing file");
207    cnode *node;
208    for (node = root->first_child; node != NULL; node = node->next) {
209        ALOG_ASSERT(node != NULL, "error in parsing file");
210
211        if (string(node->name) == gDefaultTag) {
212            defaultValue = node->value;
213        } else if (string(node->name) == gTypeTag) {
214            type = node->value;
215        } else {
216             ALOGE("%s: Unrecognized %s %s node", __FUNCTION__, node->name, node->value);
217        }
218    }
219}
220
221template <typename T>
222T *ParameterManagerWrapper::getElement(const string &name, std::map<string, T *> &elementsMap)
223{
224    parameterManagerElementSupported<T>();
225    typename std::map<string, T *>::iterator it = elementsMap.find(name);
226    ALOG_ASSERT(it != elementsMap.end(), "Element %s not found", name.c_str());
227    return it != elementsMap.end() ? it->second : NULL;
228}
229
230template <typename T>
231const T *ParameterManagerWrapper::getElement(const string &name, const std::map<string, T *> &elementsMap) const
232{
233    parameterManagerElementSupported<T>();
234    typename std::map<string, T *>::const_iterator it = elementsMap.find(name);
235    ALOG_ASSERT(it != elementsMap.end(), "Element %s not found", name.c_str());
236    return it != elementsMap.end() ? it->second : NULL;
237}
238
239void ParameterManagerWrapper::loadCriteria(cnode *root)
240{
241    ALOG_ASSERT(root != NULL, "error in parsing file");
242    cnode *node = config_find(root, gCriterionTag.c_str());
243
244    if (node == NULL) {
245        ALOGW("%s: no inclusive criteria found", __FUNCTION__);
246        return;
247    }
248    for (node = node->first_child; node != NULL; node = node->next) {
249        loadCriterion(node);
250    }
251}
252
253void ParameterManagerWrapper::addCriterion(const string &name, const string &typeName,
254                              const string &defaultLiteralValue)
255{
256    ALOG_ASSERT(mPolicyCriteria.find(name) == mPolicyCriteria.end(),
257                "Route Criterion %s already added", name.c_str());
258
259    ISelectionCriterionTypeInterface *criterionType =
260            getElement<ISelectionCriterionTypeInterface>(typeName, mPolicyCriterionTypes);
261
262    ISelectionCriterionInterface *criterion =
263            mPfwConnector->createSelectionCriterion(name, criterionType);
264
265    mPolicyCriteria[name] = criterion;
266    int numericalValue = 0;
267    if (!criterionType->getNumericalValue(defaultLiteralValue.c_str(),  numericalValue)) {
268        ALOGE("%s; trying to apply invalid default literal value (%s)", __FUNCTION__,
269              defaultLiteralValue.c_str());
270    }
271    criterion->setCriterionState(numericalValue);
272}
273
274void ParameterManagerWrapper::loadCriterion(cnode *root)
275{
276    ALOG_ASSERT(root != NULL, "error in parsing file");
277    const char *criterionName = root->name;
278
279    ALOG_ASSERT(mPolicyCriteria.find(criterionName) == mPolicyCriteria.end(),
280                      "Criterion %s already added", criterionName);
281
282    string paramKeyName = "";
283    string path = "";
284    string typeName = "";
285    string defaultValue = "";
286
287    parseChildren(root, defaultValue, typeName);
288
289    addCriterion(criterionName, typeName, defaultValue);
290}
291
292void ParameterManagerWrapper::loadConfig(cnode *root)
293{
294    ALOG_ASSERT(root != NULL, "error in parsing file");
295    cnode *node = config_find(root, gPolicyConfTag.c_str());
296    if (node == NULL) {
297        ALOGW("%s: Could not find node for pfw", __FUNCTION__);
298        return;
299    }
300    ALOGD("%s: Loading conf for pfw", __FUNCTION__);
301    loadInclusiveCriterionType(node);
302    loadExclusiveCriterionType(node);
303    loadCriteria(node);
304}
305
306
307status_t ParameterManagerWrapper::loadAudioPolicyCriteriaConfig(const char *path)
308{
309    ALOG_ASSERT(path != NULL, "error in parsing file: empty path");
310    cnode *root;
311    char *data;
312    ALOGD("%s", __FUNCTION__);
313    data = (char *)load_file(path, NULL);
314    if (data == NULL) {
315        return -ENODEV;
316    }
317    root = config_node("", "");
318    ALOG_ASSERT(root != NULL, "Unable to allocate a configuration node");
319    config_load(root, data);
320
321    loadConfig(root);
322
323    config_free(root);
324    free(root);
325    free(data);
326    ALOGD("%s: loaded", __FUNCTION__);
327    return NO_ERROR;
328}
329
330bool ParameterManagerWrapper::isStarted()
331{
332    return mPfwConnector && mPfwConnector->isStarted();
333}
334
335status_t ParameterManagerWrapper::setPhoneState(audio_mode_t mode)
336{
337    ISelectionCriterionInterface *criterion =
338            getElement<ISelectionCriterionInterface>(gPhoneStateCriterionTag, mPolicyCriteria);
339    if (criterion == NULL) {
340        ALOGE("%s: no criterion found for %s", __FUNCTION__, gPhoneStateCriterionTag.c_str());
341        return BAD_VALUE;
342    }
343    if (!isValueValidForCriterion(criterion, static_cast<int>(mode))) {
344        return BAD_VALUE;
345    }
346    criterion->setCriterionState((int)(mode));
347    applyPlatformConfiguration();
348    return NO_ERROR;
349}
350
351audio_mode_t ParameterManagerWrapper::getPhoneState() const
352{
353    const ISelectionCriterionInterface *criterion =
354            getElement<ISelectionCriterionInterface>(gPhoneStateCriterionTag, mPolicyCriteria);
355    if (criterion == NULL) {
356        ALOGE("%s: no criterion found for %s", __FUNCTION__, gPhoneStateCriterionTag.c_str());
357        return AUDIO_MODE_NORMAL;
358    }
359    return static_cast<audio_mode_t>(criterion->getCriterionState());
360}
361
362status_t ParameterManagerWrapper::setForceUse(audio_policy_force_use_t usage,
363                                              audio_policy_forced_cfg_t config)
364{
365    // @todo: return an error on a unsupported value
366    if (usage > AUDIO_POLICY_FORCE_USE_CNT) {
367        return BAD_VALUE;
368    }
369
370    ISelectionCriterionInterface *criterion =
371            getElement<ISelectionCriterionInterface>(gForceUseCriterionTag[usage], mPolicyCriteria);
372    if (criterion == NULL) {
373        ALOGE("%s: no criterion found for %s", __FUNCTION__, gForceUseCriterionTag[usage].c_str());
374        return BAD_VALUE;
375    }
376    if (!isValueValidForCriterion(criterion, static_cast<int>(config))) {
377        return BAD_VALUE;
378    }
379    criterion->setCriterionState((int)config);
380    applyPlatformConfiguration();
381    return NO_ERROR;
382}
383
384audio_policy_forced_cfg_t ParameterManagerWrapper::getForceUse(audio_policy_force_use_t usage) const
385{
386    // @todo: return an error on a unsupported value
387    if (usage > AUDIO_POLICY_FORCE_USE_CNT) {
388        return AUDIO_POLICY_FORCE_NONE;
389    }
390    const ISelectionCriterionInterface *criterion =
391            getElement<ISelectionCriterionInterface>(gForceUseCriterionTag[usage], mPolicyCriteria);
392    if (criterion == NULL) {
393        ALOGE("%s: no criterion found for %s", __FUNCTION__, gForceUseCriterionTag[usage].c_str());
394        return AUDIO_POLICY_FORCE_NONE;
395    }
396    return static_cast<audio_policy_forced_cfg_t>(criterion->getCriterionState());
397}
398
399bool ParameterManagerWrapper::isValueValidForCriterion(ISelectionCriterionInterface *criterion,
400                                                       int valueToCheck)
401{
402    const ISelectionCriterionTypeInterface *interface = criterion->getCriterionType();
403    string literalValue;
404    return interface->getLiteralValue(valueToCheck, literalValue);
405}
406
407status_t ParameterManagerWrapper::setAvailableInputDevices(audio_devices_t inputDevices)
408{
409    ISelectionCriterionInterface *criterion =
410            getElement<ISelectionCriterionInterface>(gInputDeviceCriterionTag, mPolicyCriteria);
411    if (criterion == NULL) {
412        ALOGE("%s: no criterion found for %s", __FUNCTION__, gInputDeviceCriterionTag.c_str());
413        return DEAD_OBJECT;
414    }
415    criterion->setCriterionState(inputDevices & ~AUDIO_DEVICE_BIT_IN);
416    applyPlatformConfiguration();
417    return NO_ERROR;
418}
419
420status_t ParameterManagerWrapper::setAvailableOutputDevices(audio_devices_t outputDevices)
421{
422    ISelectionCriterionInterface *criterion =
423            getElement<ISelectionCriterionInterface>(gOutputDeviceCriterionTag, mPolicyCriteria);
424    if (criterion == NULL) {
425        ALOGE("%s: no criterion found for %s", __FUNCTION__, gOutputDeviceCriterionTag.c_str());
426        return DEAD_OBJECT;
427    }
428    criterion->setCriterionState(outputDevices);
429    applyPlatformConfiguration();
430    return NO_ERROR;
431}
432
433void ParameterManagerWrapper::applyPlatformConfiguration()
434{
435    mPfwConnector->applyConfigurations();
436}
437
438} // namespace audio_policy
439} // namespace android
440