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