摘要:前言說的解析之前,先熟悉下的啟動過程,開機時會通過啟動,會啟動,而會創(chuàng)建,這樣就被初始化了。感興趣的可看下這個博客有具體的講解正文回到的函數中有兩句代碼在函數中會。此刻正式開始了的初始化。
前言
說audio_policy_configuration.xml的解析之前,先熟悉下audiopolicy的啟動過程,開機時會通過init.rc啟動audioservice,audioservice會啟動AudioPolicyService,而AudiopolicyService會創(chuàng)建AudioPolicyManager,這樣AudioPolicyManager就被初始化了。感興趣的可看下這個博客有具體的講解https://blog.csdn.net/Qidi_Hu...
正文回到AudioPolicyService的onFirstRef()函數中有兩句代碼
mAudioPolicyClient = new AudioPolicyClient(this); mAudioPolicyManager =createAudioPolicyManager(mAudioPolicyClient);
在createAudioPolicyManager函數中會 new AudioPolicyManager(clientInterface)。此刻正式開始了AudioPolicyManager的初始化。
我們看下frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp 的源碼
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface): AudioPolicyManager(clientInterface, false /*forTesting*/) { loadConfig(); initialize(); }
void AudioPolicyManager::loadConfig() { //Android7.0之后便使用此宏 #ifdef USE_XML_AUDIO_POLICY_CONF if (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) { #else if ((ConfigParsingUtils::loadConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE, getConfig()) != NO_ERROR) && (ConfigParsingUtils::loadConfig(AUDIO_POLICY_CONFIG_FILE, getConfig()) != NO_ERROR)) { #endif ALOGE("could not load audio policy configuration file, setting defaults"); getConfig().setDefault(); } }
deserializeAudioPolicyXmlConfig函數的getConfig()即AudioPolicyConfig,函數聲明在AudioPolicyManager.h文中中
AudioPolicyConfig& getConfig() { return mConfig; }
static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) { char audioPolicyXmlConfigFile[AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH]; std::vectorfileNames; status_t ret; if (property_get_bool("ro.bluetooth.a2dp_offload.supported", false) && property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) { // A2DP offload supported but disabled: try to use special XML file fileNames.push_back(AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME); } //文件名#define AUDIO_POLICY_XML_CONFIG_FILE_NAME "audio_policy_configuration.xml",位于frameworks/av/services/audiopolicy/config/目錄下。 fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME); for (const char* fileName : fileNames) { for (int i = 0; i < kConfigLocationListSize; i++) { PolicySerializer serializer; snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile), "%s/%s", kConfigLocationList[i], fileName); ret = serializer.deserialize(audioPolicyXmlConfigFile, config); if (ret == NO_ERROR) { return ret; } } } return ret; }
今天要說的重點就是這個for循環(huán)了,serializer.deserialize(audioPolicyXmlConfigFile, config)
先看下PolicySerializer位于/frameworks/av/services/audiopolicy/common/managerdefinitions/include/目錄下
以下舉例的所有標簽均來自audio_policy_configuration.x下對應的第一行標簽
status_t PolicySerializer::deserialize(const char *configFile, AudioPolicyConfig &config) { xmlDocPtr doc; doc = xmlParseFile(configFile); if (doc == NULL) { ALOGE("%s: Could not parse %s document.", __FUNCTION__, configFile); return BAD_VALUE; } xmlNodePtr cur = xmlDocGetRootElement(doc); if (cur == NULL) { ALOGE("%s: Could not parse %s document: empty.", __FUNCTION__, configFile); xmlFreeDoc(doc); return BAD_VALUE; } if (xmlXIncludeProcess(doc) < 0) { ALOGE("%s: libxml failed to resolve XIncludes on %s document.", __FUNCTION__, configFile); } if (xmlStrcmp(cur->name, (const xmlChar *) mRootElementName.c_str())) { ALOGE("%s: No %s root element found in xml data %s.", __FUNCTION__, mRootElementName.c_str(), (const char *)cur->name); xmlFreeDoc(doc); return BAD_VALUE; } string version = getXmlAttribute(cur, versionAttribute); if (version.empty()) { ALOGE("%s: No version found in root node %s", __FUNCTION__, mRootElementName.c_str()); return BAD_VALUE; } if (version != mVersion) { ALOGE("%s: Version does not match; expect %s got %s", __FUNCTION__, mVersion.c_str(), version.c_str()); return BAD_VALUE; } //上面都是解析校驗xml的一些屬性標簽啥的,此處開始才是正式加載,首先是module的加載 // Lets deserialize children // Modules ModuleTraits::Collection modules; deserializeCollection(doc, cur, modules, &config); config.setHwModules(modules); // deserialize volume section VolumeTraits::Collection volumes; deserializeCollection (doc, cur, volumes, &config); config.setVolumes(volumes); // Global Configuration GlobalConfigTraits::deserialize(cur, config); xmlFreeDoc(doc); return android::OK; }
其中這兩行代碼便開始了真正的解析
deserializeCollection(doc, cur, modules, &config); config.setHwModules(modules);
deserializeCollection是個通用方法
templatestatic status_t deserializeCollection(_xmlDoc *doc, const _xmlNode *cur, typename Trait::Collection &collection, typename Trait::PtrSerializingCtx serializingContext) { const xmlNode *root = cur->xmlChildrenNode; while (root != NULL) { if (xmlStrcmp(root->name, (const xmlChar *)Trait::collectionTag) && xmlStrcmp(root->name, (const xmlChar *)Trait::tag)) { root = root->next; continue; } const xmlNode *child = root; if (!xmlStrcmp(child->name, (const xmlChar *)Trait::collectionTag)) { child = child->xmlChildrenNode; } while (child != NULL) { if (!xmlStrcmp(child->name, (const xmlChar *)Trait::tag)) { typename Trait::PtrElement element; status_t status = Trait::deserialize(doc, child, element, serializingContext); if (status != NO_ERROR) { return status; } if (collection.add(element) < 0) { ALOGE("%s: could not add element to %s collection", __FUNCTION__, Trait::collectionTag); } } child = child->next; } if (!xmlStrcmp(root->name, (const xmlChar *)Trait::tag)) { return NO_ERROR; } root = root->next; } return NO_ERROR; }
const char *const ModuleTraits::childAttachedDevicesTag = "attachedDevices"; const char *const ModuleTraits::childAttachedDeviceTag = "item"; const char *const ModuleTraits::childDefaultOutputDeviceTag = "defaultOutputDevice"; const char *const ModuleTraits::tag = "module"; const char *const ModuleTraits::collectionTag = "modules"; const char ModuleTraits::Attributes::name[] = "name"; const char ModuleTraits::Attributes::version[] = "halVersion"; status_t ModuleTraits::deserialize(xmlDocPtr doc, const xmlNode *root, PtrElement &module, PtrSerializingCtx ctx) { //解析modules下的module標簽,我們可以看下configuration.xml下module的name是primary,當我們如果需要修改時記得module標簽里的name一定不能為空 string name = getXmlAttribute(root, Attributes::name); if (name.empty()) { ALOGE("%s: No %s found", __FUNCTION__, Attributes::name); return BAD_VALUE; } uint32_t versionMajor = 0, versionMinor = 0; string versionLiteral = getXmlAttribute(root, Attributes::version); if (!versionLiteral.empty()) { sscanf(versionLiteral.c_str(), "%u.%u", &versionMajor, &versionMinor); ALOGV("%s: mHalVersion = major %u minor %u", __FUNCTION__, versionMajor, versionMajor); } ALOGV("%s: %s %s=%s", __FUNCTION__, tag, Attributes::name, name.c_str()); //可以看下Serializer.h里關于ModuleTraits的結構體定義 typedef HwModule Element; //因此這new 了一個Hwmodule,我們先簡單看一下Hwmodule的代碼,位于frameworks/av/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp HwModule::HwModule(const char *name, uint32_t halVersionMajor, uint32_t halVersionMinor) : mName(String8(name)), mHandle(AUDIO_MODULE_HANDLE_NONE) { setHalVersion(halVersionMajor, halVersionMinor); } //其實就是把里的name和halVersion解析并初始化給了HwModule module = new Element(name.c_str(), versionMajor, versionMinor); // Deserialize childrens: Audio Mix Port, Audio Device Ports (Source/Sink), Audio Routes MixPortTraits::Collection mixPorts; //我們可以看到module下有 標簽,其實也是按著這個順序解析及的。到這里多少明白了一些audio_policy_configuration.xml的解析,那么解析完的數據又 //是如何初始化的呢,我繼續(xù)往下看 //開始解析 標簽下東西 deserializeCollection (doc, root, mixPorts, NULL); //我們繼續(xù)看下mixPoritraits const char *const MixPortTraits::collectionTag = "mixPorts"; const char *const MixPortTraits::tag = "mixPort"; const char MixPortTraits::Attributes::name[] = "name"; const char MixPortTraits::Attributes::role[] = "role"; const char MixPortTraits::Attributes::flags[] = "flags"; const char MixPortTraits::Attributes::maxOpenCount[] = "maxOpenCount"; const char MixPortTraits::Attributes::maxActiveCount[] = "maxActiveCount"; status_t MixPortTraits::deserialize(_xmlDoc *doc, const _xmlNode *child, PtrElement &mixPort, PtrSerializingCtx /*serializingContext*/) { string name = getXmlAttribute(child, Attributes::name); if (name.empty()) { ALOGE("%s: No %s found", __FUNCTION__, Attributes::name); return BAD_VALUE; } ALOGV("%s: %s %s=%s", __FUNCTION__, tag, Attributes::name, name.c_str()); string role = getXmlAttribute(child, Attributes::role); if (role.empty()) { ALOGE("%s: No %s found", __FUNCTION__, Attributes::role); return BAD_VALUE; } ALOGV("%s: Role=%s", __FUNCTION__, role.c_str()); //portRole 分為 sink和source sink可以理解為輸入設備比如mic,source可以理解為輸出設備比如speaker audio_port_role_t portRole = role == "source" ? AUDIO_PORT_ROLE_SOURCE : AUDIO_PORT_ROLE_SINK; //我們再去頭文件里看下發(fā)現其實new是IOProfile typedef IOProfile Element;其實IOProfile繼承AudioPort。 mixPort = new Element(String8(name.c_str()), portRole); //簡單看下IOProfile的初始化 IOProfile(const String8 &name, audio_port_role_t role) : AudioPort(name, AUDIO_PORT_TYPE_MIX, role), maxOpenCount((role == AUDIO_PORT_ROLE_SOURCE) ? 1 : 0), curOpenCount(0), maxActiveCount(1), curActiveCount(0) {} //以上把 (doc, child, profiles, NULL); //我們在看下AudioProfileTraits const char *const AudioProfileTraits::collectionTag = "profiles"; const char *const AudioProfileTraits::tag = "profile"; const char AudioProfileTraits::Attributes::name[] = "name"; const char AudioProfileTraits::Attributes::samplingRates[] = "samplingRates"; const char AudioProfileTraits::Attributes::format[] = "format"; const char AudioProfileTraits::Attributes::channelMasks[] = "channelMasks"; //開始解析 標簽下的samle format 和chanel status_t AudioProfileTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *root, PtrElement &profile, PtrSerializingCtx /*serializingContext*/) { string samplingRates = getXmlAttribute(root, Attributes::samplingRates); string format = getXmlAttribute(root, Attributes::format); string channels = getXmlAttribute(root, Attributes::channelMasks); //再看下頭文件的定義typedef AudioProfile Element發(fā)現new的是AudioProfile,順便看下初始化做了什么 //我們明白了是把 標簽下的samle format 和chanel //全部賦值給AudioProfile。 AudioProfile(audio_format_t format, audio_channel_mask_t channelMasks, uint32_t samplingRate) : mName(String8("")), mFormat(format) { mChannelMasks.add(channelMasks); mSamplingRates.add(samplingRate); } profile = new Element(formatFromString(format, gDynamicFormat), channelMasksFromString(channels, ","), samplingRatesFromString(samplingRates, ",")); //以下3個函數調用我們只簡單分析一個,邏輯都是一樣的 //void setDynamicFormat(bool dynamic) { mIsDynamicFormat = dynamic; }實際就是把foramte賦值給我AudioProfile下的mIsDynamicFormat profile->setDynamicFormat(profile->getFormat() == gDynamicFormat); profile->setDynamicChannels(profile->getChannels().isEmpty()); profile->setDynamicRate(profile->getSampleRates().isEmpty()); return NO_ERROR; } //如果profiles是空也會初始化個默認的,也就是每個 標簽下一定要有個 if (profiles.isEmpty()) { sp dynamicProfile = new AudioProfile(gDynamicFormat, ChannelsVector(), SampleRateVector()); dynamicProfile->setDynamicFormat(true); dynamicProfile->setDynamicChannels(true); dynamicProfile->setDynamicRate(true); profiles.add(dynamicProfile); } //mixport即IOProfile,profiles即AudioProfiles,把AudioProfiles賦值給了IOProfile mixPort->setAudioProfiles(profiles); string flags = getXmlAttribute(child, Attributes::flags); //如果flag標簽存在,再設置下flag if (!flags.empty()) { // Source role if (portRole == AUDIO_PORT_ROLE_SOURCE) { mixPort->setFlags(OutputFlagConverter::maskFromString(flags)); } else { // Sink role mixPort->setFlags(InputFlagConverter::maskFromString(flags)); } } //下邊這倆標簽一般都不會使用,解析出來賦給mixport,一般在使用時如果沒有特殊需求,一般使用的都是默認的 string maxOpenCount = getXmlAttribute(child, Attributes::maxOpenCount); if (!maxOpenCount.empty()) { convertTo(maxOpenCount, mixPort->maxOpenCount); } string maxActiveCount = getXmlAttribute(child, Attributes::maxActiveCount); if (!maxActiveCount.empty()) { convertTo(maxActiveCount, mixPort->maxActiveCount); } // Deserialize children //解析 下的 這個在mixporit下通常也是沒有的 AudioGainTraits::Collection gains; deserializeCollection (doc, child, gains, NULL); mixPort->setGains(gains); return NO_ERROR; } //moudle即HwModule,將解析的mixPorts(IOProfiles)存儲給module的setProfiles,到此 標簽里的內容就全部解析完了 module->setProfiles(mixPorts); //說下setProfiles這個函數 void HwModule::setProfiles(const IOProfileCollection &profiles) { for (size_t i = 0; i < profiles.size(); i++) { addProfile(profiles[i]); } } //調用了addprofile status_t HwModule::addProfile(const sp &profile) { switch (profile->getRole()) { case AUDIO_PORT_ROLE_SOURCE: return addOutputProfile(profile); case AUDIO_PORT_ROLE_SINK: return addInputProfile(profile); case AUDIO_PORT_ROLE_NONE: return BAD_VALUE; } return BAD_VALUE; } //又調用了addOutputProfile和addInputProfile,其實這倆函數最終就是賦值mInputProfiles和mOutputProfiles這倆集合。mixport解析結束 //解析 標簽,解析原理都相同就不再細說了,只說下每個標簽解析完都做了什么。 DevicePortTraits::Collection devicePorts; //解析的源碼由于篇幅原因我就說下重要部分,這個函數會解析 標簽下的各屬性 deserializeCollection (doc, root, devicePorts, NULL); //deserializeCollection 函數中 會 new DeviceDescriptor 并將解析的tagName和type賦值下去,這里注意role這個屬性只是在解析時做的容錯,真正對判斷這個device是sink //還是source是通過audio_is_input_device(type)和audio_is_output_device(type)判斷的 deviceDesc = new Element(type, String8(name.c_str())); //DeviceDescriptor繼承自AudioPort和AudioPortConfig簡單看下DeviceDescriptor 的初始化 DeviceDescriptor::DeviceDescriptor(audio_devices_t type, const String8 &tagName) : AudioPort(String8(""), AUDIO_PORT_TYPE_DEVICE, audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE), mAddress(""), mTagName(tagName), mDeviceType(type), mId(0) { if (type == AUDIO_DEVICE_IN_REMOTE_SUBMIX || type == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ) { mAddress = String8("0"); } } //最終解析完device標簽,同樣賦值給hwModule, module->setDeclaredDevices(devicePorts); //在這個set函數中將解析的devices分別賦值給了mDeclaredDevices和mPorts,其中mDeclaredDevices是DeviceDescriptor的集合mPorts是AudioPort的集合 void HwModule::setDeclaredDevices(const DeviceVector &devices) { mDeclaredDevices = devices; for (size_t i = 0; i < devices.size(); i++) { mPorts.add(devices[i]); } } //解析 標簽,要看到希望了哈,route很重要主要把source和sink連接起來 RouteTraits::Collection routes; deserializeCollection (doc, root, routes, module.get()); //我們來看下解析的源碼 const char *const RouteTraits::tag = "route"; const char *const RouteTraits::collectionTag = "routes"; const char RouteTraits::Attributes::type[] = "type"; const char RouteTraits::Attributes::typeMix[] = "mix"; const char RouteTraits::Attributes::sink[] = "sink"; const char RouteTraits::Attributes::sources[] = "sources"; status_t RouteTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *root, PtrElement &element, PtrSerializingCtx ctx) { string type = getXmlAttribute(root, Attributes::type); if (type.empty()) { ALOGE("%s: No %s found", __FUNCTION__, Attributes::type); return BAD_VALUE; } //首先看 sink = ctx->findPortByTagName(String8(sinkAttr.c_str())); if (sink == NULL) { ALOGE("%s: no sink found with name=%s", __FUNCTION__, sinkAttr.c_str()); return BAD_VALUE; } //找到sink屬性,將sink值即Earpiece賦值給AudioRoute的setSink 標簽 setSink(sink); //解析sources屬性 我們發(fā)現sources下有好多因此我們用循環(huán)來處理 string sourcesAttr = getXmlAttribute(root, Attributes::sources); if (sourcesAttr.empty()) { ALOGE("%s: No %s found", __FUNCTION__, Attributes::sources); return BAD_VALUE; } // Tokenize and Convert Sources name to port pointer AudioPortVector sources; char *sourcesLiteral = strndup(sourcesAttr.c_str(), strlen(sourcesAttr.c_str())); char *devTag = strtok(sourcesLiteral, ","); while (devTag != NULL) { if (strlen(devTag) != 0) { //還記得之前解析的mixport實際是IOProfile,而IOProfile繼承自AudioPort,因此這里找的便是之前的mixport。 sp source = ctx->findPortByTagName(String8(devTag)); if (source == NULL) { ALOGE("%s: no source found with name=%s", __FUNCTION__, devTag); free(sourcesLiteral); return BAD_VALUE; } sources.add(source); } devTag = strtok(NULL, ","); } free(sourcesLiteral); //將audioroute賦值到audioport中 sink->addRoute(element); for (size_t i = 0; i < sources.size(); i++) { sp source = sources.itemAt(i); source->addRoute(element); } //audioroute的setSources element->setSources(sources); return NO_ERROR; } //說下setRoutes這個函數 module->setRoutes(routes); //我們看下HwModule.cpp中的實現 void HwModule::setRoutes(const AudioRouteVector &routes) { mRoutes = routes; // Now updating the streams (aka IOProfile until now) supported devices refreshSupportedDevices(); } //繼續(xù)看refreshSupportedDevices這個函數 void HwModule::refreshSupportedDevices() { // Now updating the streams (aka IOProfile until now) supported devices //mInputProfiles就是我們解析mixport時setProfiles時賦值的,因此先遍歷所有的mInputProfiles for (const auto& stream : mInputProfiles) { DeviceVector sourceDevices; //解析route標簽時sink->addRoute(element);已添加過,這里開始遍歷這個mInputProfile下的所有route for (const auto& route : stream->getRoutes()) { //route->getSink()也是解析route標簽時element->setSink(sink)下來的,判斷這個sinkmInputProfiles中是否同一個,如果相等繼續(xù) sp sink = route->getSink(); if (sink == 0 || stream != sink) { ALOGE("%s: Invalid route attached to input stream", __FUNCTION__); continue; } //先說下getRouteSourceDevices函數,找route下的source標簽下的device,如果是source即輸出設備,就存入sourceDevices集合 DeviceVector HwModule::getRouteSourceDevices(const sp &route) const { //DeviceVector : public SortedVector > DeviceVector sourceDevices; for (const auto& source : route->getSources()) { if (source->getType() == AUDIO_PORT_TYPE_DEVICE) { sourceDevices.add(mDeclaredDevices.getDeviceFromTagName(source->getTagName())); } } return sourceDevices; } //繼續(xù)看sourceDevicesForRoute 我們知道是route標簽source屬性里所有輸出device DeviceVector sourceDevicesForRoute = getRouteSourceDevices(route); if (sourceDevicesForRoute.isEmpty()) { ALOGE("%s: invalid source devices for %s", __FUNCTION__, stream->getName().string()); continue; } sourceDevices.add(sourceDevicesForRoute); } if (sourceDevices.isEmpty()) { ALOGE("%s: invalid source devices for %s", __FUNCTION__, stream->getName().string()); continue; } //將這些輸出devices關聯到inputProfile上,作為inputProfile的支持devices stream->setSupportedDevices(sourceDevices); } //同理遍歷mOutputProfiles,找到mOutputProfiles里和routes里匹配的mOutputProfile對應的route,將route里sink標簽里是輸入的devices,作為mOutputProfile支持的輸入device for (const auto& stream : mOutputProfiles) { DeviceVector sinkDevices; for (const auto& route : stream->getRoutes()) { sp source = route->getSources().findByTagName(stream->getTagName()); if (source == 0 || stream != source) { ALOGE("%s: Invalid route attached to output stream", __FUNCTION__); continue; } sp sinkDevice = getRouteSinkDevice(route); if (sinkDevice == 0) { ALOGE("%s: invalid sink device for %s", __FUNCTION__, stream->getName().string()); continue; } sinkDevices.add(sinkDevice); } stream->setSupportedDevices(sinkDevices); } } //到此還未結束,回到module標簽的開始會發(fā)現 和 標簽還未解析,繼續(xù) const xmlNode *children = root->xmlChildrenNode; while (children != NULL) { if (!xmlStrcmp(children->name, (const xmlChar *)childAttachedDevicesTag)) { ALOGV("%s: %s %s found", __FUNCTION__, tag, childAttachedDevicesTag); const xmlNode *child = children->xmlChildrenNode; while (child != NULL) { if (!xmlStrcmp(child->name, (const xmlChar *)childAttachedDeviceTag)) { xmlChar *attachedDevice = xmlNodeListGetString(doc, child->xmlChildrenNode, 1); if (attachedDevice != NULL) { ALOGV("%s: %s %s=%s", __FUNCTION__, tag, childAttachedDeviceTag, (const char*)attachedDevice); //解析 標簽找到和device標簽下name相同的DeviceDescriptor sp device = module->getDeclaredDevices().getDeviceFromTagName(String8((const char*)attachedDevice)); //ctx即audioPolicyConfig ctx->addAvailableDevice(device); //看下addAvailableDevice這個函數,將 標簽里的device分到mAvailableOutputDevices和mAvailableInputDevices中 void addAvailableDevice(const sp &availableDevice) { if (audio_is_output_device(availableDevice->type())) { mAvailableOutputDevices.add(availableDevice); } else if (audio_is_input_device(availableDevice->type())) { mAvailableInputDevices.add(availableDevice); } } xmlFree(attachedDevice); } } child = child->next; } } //同理解析 后通過AudioPolicyConfig設置下默認的輸出設備即mDefaultOutputDevices if (!xmlStrcmp(children->name, (const xmlChar *)childDefaultOutputDeviceTag)) { xmlChar *defaultOutputDevice = xmlNodeListGetString(doc, children->xmlChildrenNode, 1);; if (defaultOutputDevice != NULL) { ALOGV("%s: %s %s=%s", __FUNCTION__, tag, childDefaultOutputDeviceTag, (const char*)defaultOutputDevice); sp device = module->getDeclaredDevices().getDeviceFromTagName(String8((const char*)defaultOutputDevice)); if (device != 0 && ctx->getDefaultOutputDevice() == 0) { ctx->setDefaultOutputDevice(device); ALOGV("%s: default is %08x", __FUNCTION__, ctx->getDefaultOutputDevice()->type()); } xmlFree(defaultOutputDevice); } } children = children->next; } return NO_ERROR; }
最終解析完的所有module, config.setHwModules(modules)設置下去。到此基本就差不多了,剩下以下的的原理一樣就不說了。
// deserialize volume section deserializeCollection總結(doc, cur, volumes, &config); //// Global Configuration GlobalConfigTraits::deserialize(cur, config);
整個xml文件就解析完成了,下一章結合具體的audio_policy_configuration.xml在說下解析過程,如果有任何問題歡迎溝通指正。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76291.html
摘要:有點需要注意這里有個其實在解析時這些的文件也會放到一起解析的,下一張分享下解析完后又做了什么,如有任何問題,歡迎指正 前言 之前通過代碼說了audio_policy_configuration的解析過程,代碼確實需要一定耐心來看,那么今天結合具體xml再來說明下audio_policy_configuration的解析過程 正文 audio_policy_configuration.x...
摘要:前言說的解析之前,先熟悉下的啟動過程,開機時會通過啟動,會啟動,而會創(chuàng)建,這樣就被初始化了。感興趣的可看下這個博客有具體的講解正文回到的函數中有兩句代碼在函數中會。此刻正式開始了的初始化。 前言 說audio_policy_configuration.xml的解析之前,先熟悉下audiopolicy的啟動過程,開機時會通過init.rc啟動audioservice,audioserv...
摘要:整個包,按照功能可以大致劃分如下鎖框架原子類框架同步器框架集合框架執(zhí)行器框架本系列將按上述順序分析,分析所基于的源碼為。后,根據一系列常見的多線程設計模式,設計了并發(fā)包,其中包下提供了一系列基礎的鎖工具,用以對等進行補充增強。 showImg(https://segmentfault.com/img/remote/1460000016012623); 本文首發(fā)于一世流云專欄:https...
摘要:讀源碼系列文章已經放到了上,歡迎源碼版本本文閱讀的源碼為改寫原有的方法模塊改寫了以上這些方法,這些方法在調用的時候,會為返回的結果添加的屬性,用來保存原來的集合。方法的分析可以看讀源碼之模塊。 Stack 模塊為 Zepto 添加了 addSelf 和 end 方法。 讀 Zepto 源碼系列文章已經放到了github上,歡迎star: reading-zepto 源碼版本 本文閱讀的...
摘要:模塊基于上的事件的封裝,利用屬性,封裝出系列事件。這個判斷需要引入設備偵測模塊。然后是監(jiān)測事件,根據這三個事件,可以組合出和事件。其中變量對象和模塊中的對象的作用差不多,可以先看看讀源碼之模塊對模塊的分析。 Gesture 模塊基于 IOS 上的 Gesture 事件的封裝,利用 scale 屬性,封裝出 pinch 系列事件。 讀 Zepto 源碼系列文章已經放到了github上,歡...
閱讀 3725·2021-09-22 10:57
閱讀 1913·2019-08-30 15:55
閱讀 2699·2019-08-30 15:44
閱讀 1731·2019-08-30 15:44
閱讀 1875·2019-08-30 15:44
閱讀 2244·2019-08-30 12:49
閱讀 1052·2019-08-29 18:47
閱讀 3134·2019-08-29 16:15