问题现象

在开机时没有开机铃声,音乐应用无法播放,systemui或者使用了getVolumeGroupManager接口的应用有如下日志报错甚至闪退等现象

img

分析过程

从日志中由上到下可以看到,加载默认设备超时->获取不到有效的音频组->获取音频组管理器失败,类似一个故障堆栈,那么调用顺序就应该是获取音频组管理器->获取音频组信息->加载默认设备,根据日志信息跟踪到对应代码

// foundation/multimedia/audio_framework/frameworks/js/napi/audiomanager/napi_audio_volume_group_manager.cpp
napi_value NapiAudioVolumeGroupManager::CreateAudioVolumeGroupManagerWrapper(napi_env env, int32_t groupId)
{
    // Check whether the group id is valid.
    auto groupManager = AudioSystemManager::GetInstance()->GetGroupManager(groupId);
    if (groupManager == nullptr) {
        AUDIO_ERR_LOG("Failed to get group manager!"); // GetGroupManager失败报错
        NapiAudioVolumeGroupManager::isConstructSuccess_ = NAPI_ERR_INVALID_PARAM;
        return NapiParamUtils::GetUndefinedValue(env);
    }
    ...
}
// foundation/multimedia/audio_framework/services/audio_service/client/src/audio_group_manager.cpp
int32_t AudioGroupManager::Init()
{
    // init networkId_
    std::string netWorkId;
    int32_t ret = AudioPolicyManager::GetInstance().GetNetworkIdByGroupId(groupId_, netWorkId);
    if (ret == SUCCESS) {
        netWorkId_ = netWorkId;
        connectType_ = netWorkId_ == LOCAL_NETWORK_ID ? CONNECT_TYPE_LOCAL : CONNECT_TYPE_DISTRIBUTED;
        AUDIO_INFO_LOG("AudioGroupManager::init set networkId %{public}s.", netWorkId_.c_str());
    } else {
        AUDIO_ERR_LOG("AudioGroupManager::init failed, has no valid group"); // GetNetworkIdByGroupId失败报错
        return ERROR;
    }
    ...
}
// foundation/multimedia/audio_framework/services/audio_policy/server/src/audio_policy_server.cpp
int32_t AudioPolicyServer::GetNetworkIdByGroupId(int32_t groupId, std::string &networkId)
{
    auto volumeGroupInfos = audioPolicyService_.GetVolumeGroupInfos();

    ...
    if (volumeGroupInfos.size() > 0) {
        networkId = volumeGroupInfos[0]->networkId_;
        AUDIO_INFO_LOG("GetNetworkIdByGroupId: get networkId %{public}s.", networkId.c_str());
    } else {
        AUDIO_ERR_LOG("GetNetworkIdByGroupId: has no valid group"); // 音频组信息size为0
        return ERROR;
    }

    return SUCCESS;
}
// foundation/multimedia/audio_framework/services/audio_policy/server/src/service/audio_policy_service.cpp
std::vector<sptr<VolumeGroupInfo>> AudioPolicyService::GetVolumeGroupInfos()
{
    if (!isPrimaryMicModuleInfoLoaded_.load()) {
        std::unique_lock<std::mutex> lock(defaultDeviceLoadMutex_);
        bool loadWaiting = loadDefaultDeviceCV_.wait_for(lock,
            std::chrono::milliseconds(WAIT_LOAD_DEFAULT_DEVICE_TIME_MS), // 等待默认设备加载
            [this] { return isPrimaryMicModuleInfoLoaded_.load(); }
        );
        if (!loadWaiting) {
            AUDIO_ERR_LOG("load default device time out");  // 超时
        }
    }

    std::vector<sptr<VolumeGroupInfo>> volumeGroupInfos = {};
    std::shared_lock deviceLock(deviceStatusUpdateSharedMutex_);
    for (auto& v : volumeGroups_) {
        sptr<VolumeGroupInfo> info = new(std::nothrow) VolumeGroupInfo(v->volumeGroupId_, v->mappingId_, v->groupName_,
            v->networkId_, v->connectType_);
        volumeGroupInfos.push_back(info);
    }
    return volumeGroupInfos; // 返回空数组
}

通过添加日志打印可以得知最后返回了空数组,那么为什么最后返回的是空数组?

从for循环中,volumeGroupInfos的信息来自全局变量volumeGroups_,而volumeGroups_的更新处理通过调用如下函数

// foundation/multimedia/audio_framework/services/audio_policy/server/src/service/audio_policy_service.cpp
void AudioPolicyService::UpdateGroupInfo(GroupType type, std::string groupName, int32_t& groupId, std::string networkId,
    bool connected, int32_t mappingId)
{
    ...
    if (type == GroupType::VOLUME_TYPE) {
        ...
        if (iter != volumeGroups_.end()) {
            groupId = (*iter)->volumeGroupId_;
            // if status is disconnected, remove the group that has none audio device
            std::vector<sptr<AudioDeviceDescriptor>> devsInGroup = GetDevicesForGroup(type, groupId);
            if (!connected && devsInGroup.size() == 0) {
                volumeGroups_.erase(iter);
            }
            return;
        }
        if (groupName != GROUP_NAME_NONE && connected) {
            groupId = AudioGroupHandle::GetInstance().GetNextId(type);
            sptr<VolumeGroupInfo> volumeGroupInfo = new(std::nothrow) VolumeGroupInfo(groupId,
                mappingId, groupName, networkId, connectType);
            volumeGroups_.push_back(volumeGroupInfo);
        }
    } else {
        ...
    }
}

往上跟踪代码,整个调用栈为AddAudioServiceOnStart->ConnectServiceAdapter->OnServiceConnected->OpenPortAndAddDeviceOnServiceConnected->AddAudioDevice->UpdateGroupInfo

而监听到3001服务起来后会执行AddAudioServiceOnStart

// foundation/multimedia/audio_framework/services/audio_policy/server/src/audio_policy_server.cpp
void AudioPolicyServer::OnAddSystemAbility(int32_t systemAbilityId, const std::string& deviceId)
{
    AUDIO_INFO_LOG("SA Id is :%{public}d", systemAbilityId);
    int64_t stamp = ClockTime::GetCurNano();
    switch (systemAbilityId) {
        ...
        case AUDIO_DISTRIBUTED_SERVICE_ID: // AUDIO_DISTRIBUTED_SERVICE_ID                     = 3001,
            AUDIO_INFO_LOG("OnAddSystemAbility audio service start");
            AddAudioServiceOnStart();
            break;
        ...
    }
    // eg. done systemAbilityId: [3001] cost 780ms
    AUDIO_INFO_LOG("done systemAbilityId: [%{public}d] cost %{public}" PRId64 " ms", systemAbilityId,
        (ClockTime::GetCurNano() - stamp) / AUDIO_US_PER_SECOND);
}

但是日志中并无该打印(后面验证应该是日志太多或者hilog还没起来导致没打印,实际服务起来了)

img

多次重启后发现偶尔是有打印的

img

此时日志显示加载配置文件失败

img

// foundation/multimedia/audio_framework/services/audio_policy/server/src/service/audio_policy_service.cpp
bool AudioPolicyService::Init(void)
{
    AUDIO_INFO_LOG("Audio policy service init enter");
    ...

    bool ret = audioPolicyConfigParser_.LoadConfiguration();
    ...
    CHECK_AND_RETURN_RET_LOG(ret, false, "Audio Policy Config Load Configuration failed"); // 报错后return
    ...
}
// foundation/multimedia/audio_framework/services/audio_policy/server/src/service/config/audio_policy_parser.cpp
bool AudioPolicyParser::LoadConfiguration()
{
    AUDIO_INFO_LOG("Enter");
    doc_ = xmlReadFile(CHIP_PROD_CONFIG_FILE, nullptr, 0); // 优先解析/chip_prod/etc/audio/audio_policy_config.xml
    if (doc_ == nullptr) {
        doc_ = xmlReadFile(CONFIG_FILE, nullptr, 0); // 如果找不到,再解析vendor/etc/audio/audio_policy_config.xml
        if (doc_ == nullptr) {
            AUDIO_ERR_LOG("xmlReadFile Failed");
            return false;
        }
    }
    AUDIO_INFO_LOG("Done");
    return true;
}

但其实vendor/etc/audio/audio_policy_config.xml是存在对应文件的

参考https://gitcode.com/openharmony/multimedia_audio_framework/commit/8bc7712c905aaaf84793450f667a242e2a398fd9?ref=OpenHarmony-v5.1.0-Release修改无效

手动cp vendor/etc/audio/audio_policy_config.xml/chip_prod/etc/audio/audio_policy_config.xml发现可以成功解析了(暂时不清楚是什么原因导致vendor/etc/audio/audio_policy_config.xml读取失败)

但OH5.0 audio_policy_config.xml文件格式变更,导致解析出错

img

// foundation/multimedia/audio_framework/services/audio_policy/server/src/service/config/audio_policy_parser.cpp
XmlNodeType AudioPolicyParser::GetXmlNodeTypeAsInt(xmlNode &node)
{
    if (!xmlStrcmp(node.name, reinterpret_cast<const xmlChar*>("adapters"))) {
        return XmlNodeType::ADAPTERS;
    } else if (!xmlStrcmp(node.name, reinterpret_cast<const xmlChar*>("volumeGroups"))) {
        return XmlNodeType::VOLUME_GROUPS;
    } else if (!xmlStrcmp(node.name, reinterpret_cast<const xmlChar*>("interruptGroups"))) {
        return XmlNodeType::INTERRUPT_GROUPS;
    } else if (!xmlStrcmp(node.name, reinterpret_cast<const xmlChar*>("globalConfigs"))) {
        return XmlNodeType::GLOBAL_CONFIGS;
    } else {
        return XmlNodeType::XML_UNKNOWN;
    }
}

参考https://gitcode.com/openharmony/vendor_hihope/blob/OpenHarmony-v5.0.2-Release/rk3568/hals/audio/config/arm/audio_policy_config.xml进行修改后解决

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐