Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0c2752b6 authored by Andy Hung's avatar Andy Hung Committed by Android (Google) Code Review
Browse files

Merge "audioserver: add automated audio power logging" into main

parents 0ef2edb0 88a7afe4
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -19,18 +19,23 @@ cc_library {
    local_include_dirs: ["include"],
    export_include_dirs: ["include"],
    srcs: [
        "AudioPowerManager.cpp",
        "AudioToken.cpp",
        "HealthStats.cpp",
        "HealthStatsProvider.cpp",
        "PowerClientStats.cpp",
        "PowerStats.cpp",
        "PowerStatsCollector.cpp",
        "PowerStatsProvider.cpp",
    ],
    shared_libs: [
        "com.android.media.audio-aconfig-cc",
        "libaudioutils",
        "libbase",
        "libbinder_ndk",
        "libcutils",
        "liblog",
        "libmediautils",
        "libutils",
    ],
    cflags: [
+137 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "AudioToken.h"
#define LOG_TAG "AudioPowerManager"
#include <com_android_media_audioserver.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <psh_utils/AudioPowerManager.h>

namespace android::media::psh_utils {

/* static */
AudioPowerManager& AudioPowerManager::getAudioPowerManager() {
    [[clang::no_destroy]] static AudioPowerManager apm;
    return apm;
}

std::unique_ptr<Token> AudioPowerManager::startClient(pid_t pid, uid_t uid,
        const std::string& additional) {
    std::shared_ptr<PowerClientStats> powerClientStats;
    std::lock_guard l(mMutex);
    if (mPowerClientStats.count(uid) == 0) {
        const auto it = mHistoricalClients.find(uid);
        if (it == mHistoricalClients.end()) {
            powerClientStats = std::make_shared<PowerClientStats>(uid, additional);
        } else {
            powerClientStats = it->second;
            mHistoricalClients.erase(it);
        }
        mPowerClientStats[uid] = powerClientStats;
    } else {
        powerClientStats = mPowerClientStats[uid];
    }
    powerClientStats->addPid(pid);
    mPidToUid[pid] = uid;
    std::unique_ptr<Token> token =
            std::make_unique<AudioClientToken>(powerClientStats, pid, uid, additional);
    mOutstandingTokens.emplace(token.get());
    return token;
}

std::unique_ptr<Token> AudioPowerManager::startTrack(uid_t uid, const std::string& additional) {
    std::lock_guard l(mMutex);
    if (mPowerClientStats.count(uid) == 0) {
        ALOGW("%s: Cannot find uid: %d", __func__, uid);
        return {};
    }
    auto powerClientStats = mPowerClientStats[uid];
    std::unique_ptr<Token> token =
            std::make_unique<AudioTrackToken>(powerClientStats, additional);
    mOutstandingTokens.emplace(token.get());
    return token;
}

std::unique_ptr<Token> AudioPowerManager::startThread(
        pid_t pid, const std::string& wakeLockName,
        WakeFlag wakeFlag, const std::string& additional) {
    std::lock_guard l(mMutex);
    std::unique_ptr<Token> token =
            std::make_unique<AudioThreadToken>(pid, wakeLockName, wakeFlag, additional);
    mOutstandingTokens.emplace(token.get());
    return token;
}

std::string AudioPowerManager::toString() const {
    const std::string prefix("  ");
    std::string result;
    std::lock_guard l(mMutex);
    result.append("Power Tokens:\n");
    std::vector<std::string> tokenInfo;
    for (const auto& token: mOutstandingTokens) {
        tokenInfo.emplace_back(token->toString());
    }
    std::sort(tokenInfo.begin(), tokenInfo.end());
    for (const auto& info: tokenInfo) {
        result.append(prefix).append(info).append("\n");
    }
    result.append("Power Clients:\n");
    for (const auto& [uid, powerClientStats]: mPowerClientStats) {
        result.append(powerClientStats->toString(true, prefix));
    }
    result.append("Power Client History:\n");
    for (const auto& [power, powerClientStats]: mHistoricalClients) {
        result.append(powerClientStats->toString(true, prefix));
    }
    return result;
}

void AudioPowerManager::stopClient(pid_t pid) {
    std::lock_guard l(mMutex);
    const auto pidit = mPidToUid.find(pid);
    if (pidit == mPidToUid.end()) return;
    const uid_t uid = pidit->second;
    const auto it = mPowerClientStats.find(uid);
    if (it == mPowerClientStats.end()) return;

    auto powerClientStats = it->second;
    size_t count = powerClientStats->removePid(pid);
    if (count == 0) {
        mHistoricalClients[uid] = powerClientStats;
        mPowerClientStats.erase(it);
        if (mHistoricalClients.size() > kHistory) {
            mHistoricalClients.erase(mHistoricalClients.begin()); // remove oldest.
        }
    }
    mPidToUid.erase(pid);
}

void AudioPowerManager::clear_token_ptr(Token* token) {
    if (token != nullptr) {
        std::lock_guard l(mMutex);
        (void)mOutstandingTokens.erase(token);
    }
}

/* static */
bool AudioPowerManager::enabled() {
    static const bool enabled = com::android::media::audioserver::power_stats()
            && property_get_bool("persist.audio.power_stats.enabled", false);
    return enabled;
}

} // namespace android::media::psh_utils
+147 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "AudioToken"
#include <android-base/logging.h>
#include <utils/Log.h>
#include "AudioToken.h"
#include <psh_utils/AudioPowerManager.h>

namespace android::media::psh_utils {

/* static */
constinit std::atomic<size_t> AudioClientToken::sIdCounter{};

AudioClientToken::AudioClientToken(
        std::shared_ptr<PowerClientStats> powerClientStats, pid_t pid, uid_t uid,
        const std::string& additional)
    : mPowerClientStats(std::move(powerClientStats))
    , mPid(pid)
    , mAdditional(additional)
    , mId(sIdCounter.fetch_add(1, std::memory_order_relaxed)) {
        (void)uid;
}

AudioClientToken::~AudioClientToken() {
    auto& apm = AudioPowerManager::getAudioPowerManager();

    // APM has a back pointer to AudioToken, which is accessible on toString().
    // We first remove ourselves to prevent use after free.
    apm.clear_token_ptr(this);
    apm.stopClient(mPid);
}

std::string AudioClientToken::toString() const {
    std::string result("Client-");
    result.append(std::to_string(mId)).append(": ")
            .append(" pid: ").append(std::to_string(mPid));
    if (!mAdditional.empty()) {
        result.append(" ").append(mAdditional);
    }
    return result;
}

std::unique_ptr<Token> createAudioClientToken(pid_t pid, uid_t uid,
        const std::string& additional) {
    return AudioPowerManager::getAudioPowerManager().startClient(pid, uid, additional);
}

/* static */
constinit std::atomic<size_t> AudioThreadToken::sIdCounter{};

AudioThreadToken::AudioThreadToken(
        pid_t tid, const std::string& wakeLockName,
        WakeFlag wakeFlag, const std::string& additional)
    : mTid(tid)
    , mWakeLockName(wakeLockName)
    , mWakeFlag(wakeFlag)
    , mAdditional(additional)
    , mId(sIdCounter.fetch_add(1, std::memory_order_relaxed)) {
}

AudioThreadToken::~AudioThreadToken() {
    auto& apm = AudioPowerManager::getAudioPowerManager();

    // APM has a back pointer to AudioToken, which is accessible on toString().
    // We first remove ourselves to prevent use after free.
    apm.clear_token_ptr(this);
}

std::string AudioThreadToken::toString() const {
    std::string result("Thread-");
    result.append(std::to_string(mId)).append(": ")
            .append(" ThreadBase-tid: ").append(std::to_string(mTid))
            .append(" wakeLockName: ").append(mWakeLockName)
            .append(" wakeFlag: ").append(::android::media::psh_utils::toString(mWakeFlag));
    if (!mAdditional.empty()) {
        result.append(" ").append(mAdditional);
    }
    return result;
}

std::unique_ptr<Token> createAudioThreadToken(
        pid_t pid, const std::string& wakeLockName,
        WakeFlag wakeFlag, const std::string& additional) {
    return AudioPowerManager::getAudioPowerManager().startThread(
            pid, wakeLockName, wakeFlag, additional);
}

/* static */
constinit std::atomic<size_t> AudioTrackToken::sIdCounter{};

AudioTrackToken::AudioTrackToken(
        std::shared_ptr<PowerClientStats> powerClientStats, const std::string& additional)
    : mPowerClientStats(std::move(powerClientStats))
    , mAdditional(additional)
    , mId(sIdCounter.fetch_add(1, std::memory_order_relaxed)) {
        if (mPowerClientStats){
            mPowerClientStats->getCommandThread().add(
                    "start",
                    [pas = mPowerClientStats, actualNs = systemTime(SYSTEM_TIME_BOOTTIME)]() {
                        pas->start(actualNs);
                    });
        }
}

AudioTrackToken::~AudioTrackToken() {
    // APM has a back pointer to AudioToken, which is accessible on toString().
    // We first remove ourselves to prevent use after free.
    AudioPowerManager::getAudioPowerManager().clear_token_ptr(this);
    if (mPowerClientStats) {
        mPowerClientStats->getCommandThread().add(
                "stop",
                [pas = mPowerClientStats, actualNs = systemTime(SYSTEM_TIME_BOOTTIME)]() {
                    pas->stop(actualNs);
                });
    }
}

std::string AudioTrackToken::toString() const {
    std::string result("Track-");
    result.append(std::to_string(mId)).append(": ")
            .append(mPowerClientStats ? mPowerClientStats->toString() : std::string("null"));
    if (!mAdditional.empty()) {
        result.append(" ").append(mAdditional);
    }
    return result;
}

std::unique_ptr<Token> createAudioTrackToken(uid_t uid, const std::string& additional) {
    return AudioPowerManager::getAudioPowerManager().startTrack(uid, additional);
}


} // namespace android::media::psh_utils
+83 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <psh_utils/PowerClientStats.h>
#include <psh_utils/Token.h>

#include <atomic>
#include <memory>
#include <string>

namespace android::media::psh_utils {

class AudioClientToken : public Token {
public:
    AudioClientToken(std::shared_ptr<PowerClientStats> powerClientStats, pid_t pid, uid_t uid,
             const std::string& additional);
    ~AudioClientToken() override;

    // AudioPowerManager may call toString() while AudioToken is in its dtor.
    // It is safe so long as toString is final.
    std::string toString() const final;

private:
    const std::shared_ptr<PowerClientStats> mPowerClientStats;
    const pid_t mPid;
    const std::string mAdditional;
    const size_t mId;
    static constinit std::atomic<size_t> sIdCounter;
};

class AudioThreadToken : public Token {
public:
    AudioThreadToken(
            pid_t tid, const std::string& wakeLockName,
            WakeFlag wakeFlag, const std::string& additional);
    ~AudioThreadToken() override;

    // AudioPowerManager may call toString() while AudioToken is in its dtor.
    // It is safe so long as toString is final.
    std::string toString() const final;

private:
    const pid_t mTid;
    const std::string mWakeLockName;
    const WakeFlag mWakeFlag;
    const std::string mAdditional;
    const size_t mId;
    static constinit std::atomic<size_t> sIdCounter;
};

class AudioTrackToken : public Token {
public:
    AudioTrackToken(
            std::shared_ptr<PowerClientStats> powerClientStats, const std::string& additional);
    ~AudioTrackToken() override;

    // AudioPowerManager may call toString() while AudioToken is in its dtor.
    // It is safe so long as toString is final.
    std::string toString() const final;

private:
    const std::shared_ptr<PowerClientStats> mPowerClientStats;
    const std::string mAdditional;
    const size_t mId;
    static constinit std::atomic<size_t> sIdCounter;
};

} // namespace android::media::psh_utils
+101 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <psh_utils/PowerClientStats.h>
#include <mediautils/ServiceUtilities.h>

namespace android::media::psh_utils {

/* static */
audio_utils::CommandThread& PowerClientStats::getCommandThread() {
    [[clang::no_destroy]] static audio_utils::CommandThread ct;
    return ct;
}

PowerClientStats::PowerClientStats(uid_t uid, const std::string& additional)
        : mUid(uid), mAdditional(additional) {}

void PowerClientStats::start(int64_t actualNs) {
    std::lock_guard l(mMutex);
    ++mTokenCount;
    if (mStartNs == 0) mStartNs = actualNs;
    if (mStartStats) return;
    mStartStats = PowerStatsCollector::getCollector().getStats(kStatTimeToleranceNs);
}

void PowerClientStats::stop(int64_t actualNs) {
    std::lock_guard l(mMutex);
    if (--mTokenCount > 0) return;
    if (mStartNs != 0) mDeltaNs += actualNs - mStartNs;
    mStartNs = 0;
    if (!mStartStats) return;
    const auto stopStats = PowerStatsCollector::getCollector().getStats(kStatTimeToleranceNs);
    if (stopStats && stopStats != mStartStats) {
        *mDeltaStats += *stopStats - *mStartStats;
    }
    mStartStats.reset();
}

void PowerClientStats::addPid(pid_t pid) {
    std::lock_guard l(mMutex);
    mPids.emplace(pid);
}

size_t PowerClientStats::removePid(pid_t pid) {
    std::lock_guard l(mMutex);
    mPids.erase(pid);
    return mPids.size();
}

std::string PowerClientStats::toString(bool stats, const std::string& prefix) const {
    std::lock_guard l(mMutex);

    // Adjust delta time and stats if currently running.
    auto deltaStats = mDeltaStats;
    auto deltaNs = mDeltaNs;
    if (mStartNs) deltaNs += systemTime(SYSTEM_TIME_BOOTTIME) - mStartNs;
    if (mStartStats) {
        const auto stopStats = PowerStatsCollector::getCollector().getStats(kStatTimeToleranceNs);
        if (stopStats && stopStats != mStartStats) {
            auto newStats = std::make_shared<PowerStats>(*deltaStats);
            *newStats += *stopStats - *mStartStats;
            deltaStats = newStats;
        }
    }

    std::string result(prefix);
    result.append("uid: ")
            .append(std::to_string(mUid))
            .append(" ").append(mediautils::UidInfo::getInfo(mUid)->package)
            .append(" streams: ").append(std::to_string(mTokenCount))
            .append(" seconds: ").append(std::to_string(deltaNs * 1e-9));
    result.append(" {");
    for (auto pid : mPids) {
        result.append(" ").append(std::to_string(pid));
    }
    result.append(" }");
    if (!mAdditional.empty()) {
        result.append("\n").append(prefix).append(mAdditional);
    }
    if (stats) {
        std::string prefix2(prefix);
        prefix2.append("  ");
        result.append("\n").append(deltaStats->normalizedEnergy(prefix2));
    }
    return result;
}

} // namespace android::media::psh_utils
Loading