/* * Copyright 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 "JankTracker.h" #include #include "BackgroundExecutor.h" namespace android { namespace { constexpr size_t kJankDataBatchSize = 50; } // anonymous namespace std::atomic JankTracker::sListenerCount(0); std::atomic JankTracker::sCollectAllJankDataForTesting(false); JankTracker::~JankTracker() {} void JankTracker::addJankListener(int32_t layerId, sp listener) { // Increment right away, so that if an onJankData call comes in before the background thread has // added this listener, it will not drop the data. sListenerCount++; BackgroundExecutor::getLowPriorityInstance().sendCallbacks( {[layerId, listener = std::move(listener)]() { JankTracker& tracker = getInstance(); const std::lock_guard _l(tracker.mLock); tracker.addJankListenerLocked(layerId, listener); }}); } void JankTracker::flushJankData(int32_t layerId) { BackgroundExecutor::getLowPriorityInstance().sendCallbacks( {[layerId]() { getInstance().doFlushJankData(layerId); }}); } void JankTracker::removeJankListener(int32_t layerId, sp listener, int64_t afterVsync) { BackgroundExecutor::getLowPriorityInstance().sendCallbacks( {[layerId, listener = std::move(listener), afterVsync]() { JankTracker& tracker = getInstance(); const std::lock_guard _l(tracker.mLock); tracker.markJankListenerForRemovalLocked(layerId, listener, afterVsync); }}); } void JankTracker::onJankData(int32_t layerId, gui::JankData data) { if (sListenerCount == 0) { return; } BackgroundExecutor::getLowPriorityInstance().sendCallbacks( {[layerId, data = std::move(data)]() { JankTracker& tracker = getInstance(); tracker.mLock.lock(); bool hasListeners = tracker.mJankListeners.count(layerId) > 0; tracker.mLock.unlock(); if (!hasListeners && !sCollectAllJankDataForTesting) { return; } tracker.mJankDataLock.lock(); tracker.mJankData.emplace(layerId, data); size_t count = tracker.mJankData.count(layerId); tracker.mJankDataLock.unlock(); if (count >= kJankDataBatchSize && !sCollectAllJankDataForTesting) { tracker.doFlushJankData(layerId); } }}); } void JankTracker::addJankListenerLocked(int32_t layerId, sp listener) { for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) { if (it->second.mListener == listener) { // Undo the duplicate increment in addJankListener. sListenerCount--; return; } } mJankListeners.emplace(layerId, std::move(listener)); } void JankTracker::doFlushJankData(int32_t layerId) { std::vector jankData; int64_t maxVsync = transferAvailableJankData(layerId, jankData); std::vector> toSend; mLock.lock(); for (auto it = mJankListeners.find(layerId); it != mJankListeners.end();) { if (!jankData.empty()) { toSend.emplace_back(it->second.mListener); } int64_t removeAfter = it->second.mRemoveAfter; if (removeAfter != -1 && removeAfter <= maxVsync) { it = mJankListeners.erase(it); sListenerCount--; } else { it++; } } mLock.unlock(); for (const auto& listener : toSend) { binder::Status status = interface_cast(listener)->onJankData(jankData); if (status.exceptionCode() == binder::Status::EX_NULL_POINTER) { // Remove any listeners, where the App side has gone away, without // deregistering. dropJankListener(layerId, listener); } } } void JankTracker::markJankListenerForRemovalLocked(int32_t layerId, sp listener, int64_t afterVysnc) { for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) { if (it->second.mListener == listener) { it->second.mRemoveAfter = std::max(static_cast(0), afterVysnc); return; } } } int64_t JankTracker::transferAvailableJankData(int32_t layerId, std::vector& outJankData) { const std::lock_guard _l(mJankDataLock); int64_t maxVsync = 0; auto range = mJankData.equal_range(layerId); for (auto it = range.first; it != range.second;) { maxVsync = std::max(it->second.frameVsyncId, maxVsync); outJankData.emplace_back(std::move(it->second)); it = mJankData.erase(it); } return maxVsync; } void JankTracker::dropJankListener(int32_t layerId, sp listener) { const std::lock_guard _l(mLock); for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) { if (it->second.mListener == listener) { mJankListeners.erase(it); sListenerCount--; return; } } } void JankTracker::clearAndStartCollectingAllJankDataForTesting() { BackgroundExecutor::getLowPriorityInstance().flushQueue(); // Clear all past tracked jank data. JankTracker& tracker = getInstance(); const std::lock_guard _l(tracker.mJankDataLock); tracker.mJankData.clear(); // Pretend there's at least one listener. sListenerCount++; sCollectAllJankDataForTesting = true; } std::vector JankTracker::getCollectedJankDataForTesting(int32_t layerId) { JankTracker& tracker = getInstance(); const std::lock_guard _l(tracker.mJankDataLock); auto range = tracker.mJankData.equal_range(layerId); std::vector result; std::transform(range.first, range.second, std::back_inserter(result), [](std::pair layerIdToJankData) { return layerIdToJankData.second; }); return result; } void JankTracker::clearAndStopCollectingAllJankDataForTesting() { // Undo startCollectingAllJankDataForTesting. sListenerCount--; sCollectAllJankDataForTesting = false; // Clear all tracked jank data. JankTracker& tracker = getInstance(); const std::lock_guard _l(tracker.mJankDataLock); tracker.mJankData.clear(); } } // namespace android