1 /*
2  * Copyright (C) 2020 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 #pragma once
18 
19 #include <set>
20 #include <string>
21 
22 #include <DeviceDescriptor.h>
23 #include <HwModule.h>
24 #include <Serializer.h>
25 #include <gtest/gtest.h>
26 #include <system/audio_config.h>
27 
28 #include "DeviceManager.h"
29 
30 using ::android::sp;
31 using ::android::status_t;
32 
33 struct PolicyConfigData {
34     android::HwModuleCollection hwModules;
35     android::DeviceVector availableOutputDevices;
36     android::DeviceVector availableInputDevices;
37     sp<android::DeviceDescriptor> defaultOutputDevice;
38 };
39 
40 class PolicyConfig : private PolicyConfigData, public android::AudioPolicyConfig {
41   public:
PolicyConfig(const std::string & configFileName)42     explicit PolicyConfig(const std::string& configFileName)
43         : android::AudioPolicyConfig(hwModules, availableOutputDevices, availableInputDevices,
44                                      defaultOutputDevice),
45           mConfigFileName{configFileName} {
46         for (const auto& location : android::audio_get_configuration_paths()) {
47             std::string path = location + '/' + mConfigFileName;
48             if (access(path.c_str(), F_OK) == 0) {
49                 mFilePath = path;
50                 break;
51             }
52         }
53         init();
54     }
PolicyConfig(const std::string & configPath,const std::string & configFileName)55     PolicyConfig(const std::string& configPath, const std::string& configFileName)
56         : android::AudioPolicyConfig(hwModules, availableOutputDevices, availableInputDevices,
57                                      defaultOutputDevice),
58           mConfigFileName{configFileName},
59           mFilePath{configPath + "/" + mConfigFileName} {
60         init();
61     }
getStatus()62     status_t getStatus() const { return mStatus; }
getError()63     std::string getError() const {
64         if (mFilePath.empty()) {
65             return std::string{"Could not find "} + mConfigFileName +
66                    " file in: " + testing::PrintToString(android::audio_get_configuration_paths());
67         } else {
68             return "Invalid config file: " + mFilePath;
69         }
70     }
getFilePath()71     const std::string& getFilePath() const { return mFilePath; }
getModuleFromName(const std::string & name)72     sp<const android::HwModule> getModuleFromName(const std::string& name) const {
73         return getHwModules().getModuleFromName(name.c_str());
74     }
getPrimaryModule()75     sp<const android::HwModule> getPrimaryModule() const { return mPrimaryModule; }
getModulesWithDevicesNames()76     const std::set<std::string>& getModulesWithDevicesNames() const {
77         return mModulesWithDevicesNames;
78     }
getAttachedSinkDeviceForMixPort(const std::string & moduleName,const std::string & mixPortName)79     std::string getAttachedSinkDeviceForMixPort(const std::string& moduleName,
80                                                 const std::string& mixPortName) const {
81         return findAttachedDevice(getAttachedDevices(moduleName),
82                                   getSinkDevicesForMixPort(moduleName, mixPortName));
83     }
getAttachedSourceDeviceForMixPort(const std::string & moduleName,const std::string & mixPortName)84     std::string getAttachedSourceDeviceForMixPort(const std::string& moduleName,
85                                                   const std::string& mixPortName) const {
86         return findAttachedDevice(getAttachedDevices(moduleName),
87                                   getSourceDevicesForMixPort(moduleName, mixPortName));
88     }
haveInputProfilesInModule(const std::string & name)89     bool haveInputProfilesInModule(const std::string& name) const {
90         auto module = getModuleFromName(name);
91         return module && !module->getInputProfiles().empty();
92     }
93 
94   private:
init()95     void init() {
96         mStatus = android::deserializeAudioPolicyFileForVts(mFilePath.c_str(), this);
97         if (mStatus == android::OK) {
98             mPrimaryModule = getModuleFromName(DeviceManager::kPrimaryDevice);
99             // Available devices are not 'attached' to modules at this moment.
100             // Need to go over available devices and find their module.
101             for (const auto& device : availableOutputDevices) {
102                 for (const auto& module : hwModules) {
103                     if (module->getDeclaredDevices().indexOf(device) >= 0) {
104                         mModulesWithDevicesNames.insert(module->getName());
105                         mAttachedDevicesPerModule[module->getName()].push_back(
106                                 device->getTagName());
107                         break;
108                     }
109                 }
110             }
111             for (const auto& device : availableInputDevices) {
112                 for (const auto& module : hwModules) {
113                     if (module->getDeclaredDevices().indexOf(device) >= 0) {
114                         mModulesWithDevicesNames.insert(module->getName());
115                         mAttachedDevicesPerModule[module->getName()].push_back(
116                                 device->getTagName());
117                         break;
118                     }
119                 }
120             }
121         }
122     }
findAttachedDevice(const std::vector<std::string> & attachedDevices,const std::set<std::string> & possibleDevices)123     std::string findAttachedDevice(const std::vector<std::string>& attachedDevices,
124                                    const std::set<std::string>& possibleDevices) const {
125         for (const auto& device : attachedDevices) {
126             if (possibleDevices.count(device)) return device;
127         }
128         return {};
129     }
getAttachedDevices(const std::string & moduleName)130     std::vector<std::string> getAttachedDevices(const std::string& moduleName) const {
131         if (auto iter = mAttachedDevicesPerModule.find(moduleName);
132             iter != mAttachedDevicesPerModule.end()) {
133             return iter->second;
134         }
135         return {};
136     }
getSinkDevicesForMixPort(const std::string & moduleName,const std::string & mixPortName)137     std::set<std::string> getSinkDevicesForMixPort(const std::string& moduleName,
138                                                    const std::string& mixPortName) const {
139         std::set<std::string> result;
140         auto module = getModuleFromName(moduleName);
141         if (module != nullptr) {
142             for (const auto& route : module->getRoutes()) {
143                 for (const auto& source : route->getSources()) {
144                     if (source->getTagName() == mixPortName) {
145                         result.insert(route->getSink()->getTagName());
146                     }
147                 }
148             }
149         }
150         return result;
151     }
getSourceDevicesForMixPort(const std::string & moduleName,const std::string & mixPortName)152     std::set<std::string> getSourceDevicesForMixPort(const std::string& moduleName,
153                                                      const std::string& mixPortName) const {
154         std::set<std::string> result;
155         auto module = getModuleFromName(moduleName);
156         if (module != nullptr) {
157             for (const auto& route : module->getRoutes()) {
158                 if (route->getSink()->getTagName() == mixPortName) {
159                     const auto& sources = route->getSources();
160                     std::transform(sources.begin(), sources.end(),
161                                    std::inserter(result, result.end()),
162                                    [](const auto& source) { return source->getTagName(); });
163                 }
164             }
165         }
166         return result;
167     }
168 
169     const std::string mConfigFileName;
170     status_t mStatus = android::NO_INIT;
171     std::string mFilePath;
172     sp<const android::HwModule> mPrimaryModule = nullptr;
173     std::set<std::string> mModulesWithDevicesNames;
174     std::map<std::string, std::vector<std::string>> mAttachedDevicesPerModule;
175 };
176