1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker * Copyright 2024 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker *
4*38e8c45fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker *
8*38e8c45fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker *
10*38e8c45fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker */
16*38e8c45fSAndroid Build Coastguard Worker
17*38e8c45fSAndroid Build Coastguard Worker #include "JankTracker.h"
18*38e8c45fSAndroid Build Coastguard Worker
19*38e8c45fSAndroid Build Coastguard Worker #include <android/gui/IJankListener.h>
20*38e8c45fSAndroid Build Coastguard Worker #include "BackgroundExecutor.h"
21*38e8c45fSAndroid Build Coastguard Worker
22*38e8c45fSAndroid Build Coastguard Worker namespace android {
23*38e8c45fSAndroid Build Coastguard Worker
24*38e8c45fSAndroid Build Coastguard Worker namespace {
25*38e8c45fSAndroid Build Coastguard Worker
26*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kJankDataBatchSize = 50;
27*38e8c45fSAndroid Build Coastguard Worker
28*38e8c45fSAndroid Build Coastguard Worker } // anonymous namespace
29*38e8c45fSAndroid Build Coastguard Worker
30*38e8c45fSAndroid Build Coastguard Worker std::atomic<size_t> JankTracker::sListenerCount(0);
31*38e8c45fSAndroid Build Coastguard Worker std::atomic<bool> JankTracker::sCollectAllJankDataForTesting(false);
32*38e8c45fSAndroid Build Coastguard Worker
~JankTracker()33*38e8c45fSAndroid Build Coastguard Worker JankTracker::~JankTracker() {}
34*38e8c45fSAndroid Build Coastguard Worker
addJankListener(int32_t layerId,sp<IBinder> listener)35*38e8c45fSAndroid Build Coastguard Worker void JankTracker::addJankListener(int32_t layerId, sp<IBinder> listener) {
36*38e8c45fSAndroid Build Coastguard Worker // Increment right away, so that if an onJankData call comes in before the background thread has
37*38e8c45fSAndroid Build Coastguard Worker // added this listener, it will not drop the data.
38*38e8c45fSAndroid Build Coastguard Worker sListenerCount++;
39*38e8c45fSAndroid Build Coastguard Worker
40*38e8c45fSAndroid Build Coastguard Worker BackgroundExecutor::getLowPriorityInstance().sendCallbacks(
41*38e8c45fSAndroid Build Coastguard Worker {[layerId, listener = std::move(listener)]() {
42*38e8c45fSAndroid Build Coastguard Worker JankTracker& tracker = getInstance();
43*38e8c45fSAndroid Build Coastguard Worker const std::lock_guard<std::mutex> _l(tracker.mLock);
44*38e8c45fSAndroid Build Coastguard Worker tracker.addJankListenerLocked(layerId, listener);
45*38e8c45fSAndroid Build Coastguard Worker }});
46*38e8c45fSAndroid Build Coastguard Worker }
47*38e8c45fSAndroid Build Coastguard Worker
flushJankData(int32_t layerId)48*38e8c45fSAndroid Build Coastguard Worker void JankTracker::flushJankData(int32_t layerId) {
49*38e8c45fSAndroid Build Coastguard Worker BackgroundExecutor::getLowPriorityInstance().sendCallbacks(
50*38e8c45fSAndroid Build Coastguard Worker {[layerId]() { getInstance().doFlushJankData(layerId); }});
51*38e8c45fSAndroid Build Coastguard Worker }
52*38e8c45fSAndroid Build Coastguard Worker
removeJankListener(int32_t layerId,sp<IBinder> listener,int64_t afterVsync)53*38e8c45fSAndroid Build Coastguard Worker void JankTracker::removeJankListener(int32_t layerId, sp<IBinder> listener, int64_t afterVsync) {
54*38e8c45fSAndroid Build Coastguard Worker BackgroundExecutor::getLowPriorityInstance().sendCallbacks(
55*38e8c45fSAndroid Build Coastguard Worker {[layerId, listener = std::move(listener), afterVsync]() {
56*38e8c45fSAndroid Build Coastguard Worker JankTracker& tracker = getInstance();
57*38e8c45fSAndroid Build Coastguard Worker const std::lock_guard<std::mutex> _l(tracker.mLock);
58*38e8c45fSAndroid Build Coastguard Worker tracker.markJankListenerForRemovalLocked(layerId, listener, afterVsync);
59*38e8c45fSAndroid Build Coastguard Worker }});
60*38e8c45fSAndroid Build Coastguard Worker }
61*38e8c45fSAndroid Build Coastguard Worker
onJankData(int32_t layerId,gui::JankData data)62*38e8c45fSAndroid Build Coastguard Worker void JankTracker::onJankData(int32_t layerId, gui::JankData data) {
63*38e8c45fSAndroid Build Coastguard Worker if (sListenerCount == 0) {
64*38e8c45fSAndroid Build Coastguard Worker return;
65*38e8c45fSAndroid Build Coastguard Worker }
66*38e8c45fSAndroid Build Coastguard Worker
67*38e8c45fSAndroid Build Coastguard Worker BackgroundExecutor::getLowPriorityInstance().sendCallbacks(
68*38e8c45fSAndroid Build Coastguard Worker {[layerId, data = std::move(data)]() {
69*38e8c45fSAndroid Build Coastguard Worker JankTracker& tracker = getInstance();
70*38e8c45fSAndroid Build Coastguard Worker
71*38e8c45fSAndroid Build Coastguard Worker tracker.mLock.lock();
72*38e8c45fSAndroid Build Coastguard Worker bool hasListeners = tracker.mJankListeners.count(layerId) > 0;
73*38e8c45fSAndroid Build Coastguard Worker tracker.mLock.unlock();
74*38e8c45fSAndroid Build Coastguard Worker
75*38e8c45fSAndroid Build Coastguard Worker if (!hasListeners && !sCollectAllJankDataForTesting) {
76*38e8c45fSAndroid Build Coastguard Worker return;
77*38e8c45fSAndroid Build Coastguard Worker }
78*38e8c45fSAndroid Build Coastguard Worker
79*38e8c45fSAndroid Build Coastguard Worker tracker.mJankDataLock.lock();
80*38e8c45fSAndroid Build Coastguard Worker tracker.mJankData.emplace(layerId, data);
81*38e8c45fSAndroid Build Coastguard Worker size_t count = tracker.mJankData.count(layerId);
82*38e8c45fSAndroid Build Coastguard Worker tracker.mJankDataLock.unlock();
83*38e8c45fSAndroid Build Coastguard Worker
84*38e8c45fSAndroid Build Coastguard Worker if (count >= kJankDataBatchSize && !sCollectAllJankDataForTesting) {
85*38e8c45fSAndroid Build Coastguard Worker tracker.doFlushJankData(layerId);
86*38e8c45fSAndroid Build Coastguard Worker }
87*38e8c45fSAndroid Build Coastguard Worker }});
88*38e8c45fSAndroid Build Coastguard Worker }
89*38e8c45fSAndroid Build Coastguard Worker
addJankListenerLocked(int32_t layerId,sp<IBinder> listener)90*38e8c45fSAndroid Build Coastguard Worker void JankTracker::addJankListenerLocked(int32_t layerId, sp<IBinder> listener) {
91*38e8c45fSAndroid Build Coastguard Worker for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) {
92*38e8c45fSAndroid Build Coastguard Worker if (it->second.mListener == listener) {
93*38e8c45fSAndroid Build Coastguard Worker // Undo the duplicate increment in addJankListener.
94*38e8c45fSAndroid Build Coastguard Worker sListenerCount--;
95*38e8c45fSAndroid Build Coastguard Worker return;
96*38e8c45fSAndroid Build Coastguard Worker }
97*38e8c45fSAndroid Build Coastguard Worker }
98*38e8c45fSAndroid Build Coastguard Worker
99*38e8c45fSAndroid Build Coastguard Worker mJankListeners.emplace(layerId, std::move(listener));
100*38e8c45fSAndroid Build Coastguard Worker }
101*38e8c45fSAndroid Build Coastguard Worker
doFlushJankData(int32_t layerId)102*38e8c45fSAndroid Build Coastguard Worker void JankTracker::doFlushJankData(int32_t layerId) {
103*38e8c45fSAndroid Build Coastguard Worker std::vector<gui::JankData> jankData;
104*38e8c45fSAndroid Build Coastguard Worker int64_t maxVsync = transferAvailableJankData(layerId, jankData);
105*38e8c45fSAndroid Build Coastguard Worker
106*38e8c45fSAndroid Build Coastguard Worker std::vector<sp<IBinder>> toSend;
107*38e8c45fSAndroid Build Coastguard Worker
108*38e8c45fSAndroid Build Coastguard Worker mLock.lock();
109*38e8c45fSAndroid Build Coastguard Worker for (auto it = mJankListeners.find(layerId); it != mJankListeners.end();) {
110*38e8c45fSAndroid Build Coastguard Worker if (!jankData.empty()) {
111*38e8c45fSAndroid Build Coastguard Worker toSend.emplace_back(it->second.mListener);
112*38e8c45fSAndroid Build Coastguard Worker }
113*38e8c45fSAndroid Build Coastguard Worker
114*38e8c45fSAndroid Build Coastguard Worker int64_t removeAfter = it->second.mRemoveAfter;
115*38e8c45fSAndroid Build Coastguard Worker if (removeAfter != -1 && removeAfter <= maxVsync) {
116*38e8c45fSAndroid Build Coastguard Worker it = mJankListeners.erase(it);
117*38e8c45fSAndroid Build Coastguard Worker sListenerCount--;
118*38e8c45fSAndroid Build Coastguard Worker } else {
119*38e8c45fSAndroid Build Coastguard Worker it++;
120*38e8c45fSAndroid Build Coastguard Worker }
121*38e8c45fSAndroid Build Coastguard Worker }
122*38e8c45fSAndroid Build Coastguard Worker mLock.unlock();
123*38e8c45fSAndroid Build Coastguard Worker
124*38e8c45fSAndroid Build Coastguard Worker for (const auto& listener : toSend) {
125*38e8c45fSAndroid Build Coastguard Worker binder::Status status = interface_cast<gui::IJankListener>(listener)->onJankData(jankData);
126*38e8c45fSAndroid Build Coastguard Worker if (status.exceptionCode() == binder::Status::EX_NULL_POINTER) {
127*38e8c45fSAndroid Build Coastguard Worker // Remove any listeners, where the App side has gone away, without
128*38e8c45fSAndroid Build Coastguard Worker // deregistering.
129*38e8c45fSAndroid Build Coastguard Worker dropJankListener(layerId, listener);
130*38e8c45fSAndroid Build Coastguard Worker }
131*38e8c45fSAndroid Build Coastguard Worker }
132*38e8c45fSAndroid Build Coastguard Worker }
133*38e8c45fSAndroid Build Coastguard Worker
markJankListenerForRemovalLocked(int32_t layerId,sp<IBinder> listener,int64_t afterVysnc)134*38e8c45fSAndroid Build Coastguard Worker void JankTracker::markJankListenerForRemovalLocked(int32_t layerId, sp<IBinder> listener,
135*38e8c45fSAndroid Build Coastguard Worker int64_t afterVysnc) {
136*38e8c45fSAndroid Build Coastguard Worker for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) {
137*38e8c45fSAndroid Build Coastguard Worker if (it->second.mListener == listener) {
138*38e8c45fSAndroid Build Coastguard Worker it->second.mRemoveAfter = std::max(static_cast<int64_t>(0), afterVysnc);
139*38e8c45fSAndroid Build Coastguard Worker return;
140*38e8c45fSAndroid Build Coastguard Worker }
141*38e8c45fSAndroid Build Coastguard Worker }
142*38e8c45fSAndroid Build Coastguard Worker }
143*38e8c45fSAndroid Build Coastguard Worker
transferAvailableJankData(int32_t layerId,std::vector<gui::JankData> & outJankData)144*38e8c45fSAndroid Build Coastguard Worker int64_t JankTracker::transferAvailableJankData(int32_t layerId,
145*38e8c45fSAndroid Build Coastguard Worker std::vector<gui::JankData>& outJankData) {
146*38e8c45fSAndroid Build Coastguard Worker const std::lock_guard<std::mutex> _l(mJankDataLock);
147*38e8c45fSAndroid Build Coastguard Worker int64_t maxVsync = 0;
148*38e8c45fSAndroid Build Coastguard Worker auto range = mJankData.equal_range(layerId);
149*38e8c45fSAndroid Build Coastguard Worker for (auto it = range.first; it != range.second;) {
150*38e8c45fSAndroid Build Coastguard Worker maxVsync = std::max(it->second.frameVsyncId, maxVsync);
151*38e8c45fSAndroid Build Coastguard Worker outJankData.emplace_back(std::move(it->second));
152*38e8c45fSAndroid Build Coastguard Worker it = mJankData.erase(it);
153*38e8c45fSAndroid Build Coastguard Worker }
154*38e8c45fSAndroid Build Coastguard Worker return maxVsync;
155*38e8c45fSAndroid Build Coastguard Worker }
156*38e8c45fSAndroid Build Coastguard Worker
dropJankListener(int32_t layerId,sp<IBinder> listener)157*38e8c45fSAndroid Build Coastguard Worker void JankTracker::dropJankListener(int32_t layerId, sp<IBinder> listener) {
158*38e8c45fSAndroid Build Coastguard Worker const std::lock_guard<std::mutex> _l(mLock);
159*38e8c45fSAndroid Build Coastguard Worker for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) {
160*38e8c45fSAndroid Build Coastguard Worker if (it->second.mListener == listener) {
161*38e8c45fSAndroid Build Coastguard Worker mJankListeners.erase(it);
162*38e8c45fSAndroid Build Coastguard Worker sListenerCount--;
163*38e8c45fSAndroid Build Coastguard Worker return;
164*38e8c45fSAndroid Build Coastguard Worker }
165*38e8c45fSAndroid Build Coastguard Worker }
166*38e8c45fSAndroid Build Coastguard Worker }
167*38e8c45fSAndroid Build Coastguard Worker
clearAndStartCollectingAllJankDataForTesting()168*38e8c45fSAndroid Build Coastguard Worker void JankTracker::clearAndStartCollectingAllJankDataForTesting() {
169*38e8c45fSAndroid Build Coastguard Worker BackgroundExecutor::getLowPriorityInstance().flushQueue();
170*38e8c45fSAndroid Build Coastguard Worker
171*38e8c45fSAndroid Build Coastguard Worker // Clear all past tracked jank data.
172*38e8c45fSAndroid Build Coastguard Worker JankTracker& tracker = getInstance();
173*38e8c45fSAndroid Build Coastguard Worker const std::lock_guard<std::mutex> _l(tracker.mJankDataLock);
174*38e8c45fSAndroid Build Coastguard Worker tracker.mJankData.clear();
175*38e8c45fSAndroid Build Coastguard Worker
176*38e8c45fSAndroid Build Coastguard Worker // Pretend there's at least one listener.
177*38e8c45fSAndroid Build Coastguard Worker sListenerCount++;
178*38e8c45fSAndroid Build Coastguard Worker sCollectAllJankDataForTesting = true;
179*38e8c45fSAndroid Build Coastguard Worker }
180*38e8c45fSAndroid Build Coastguard Worker
getCollectedJankDataForTesting(int32_t layerId)181*38e8c45fSAndroid Build Coastguard Worker std::vector<gui::JankData> JankTracker::getCollectedJankDataForTesting(int32_t layerId) {
182*38e8c45fSAndroid Build Coastguard Worker JankTracker& tracker = getInstance();
183*38e8c45fSAndroid Build Coastguard Worker const std::lock_guard<std::mutex> _l(tracker.mJankDataLock);
184*38e8c45fSAndroid Build Coastguard Worker
185*38e8c45fSAndroid Build Coastguard Worker auto range = tracker.mJankData.equal_range(layerId);
186*38e8c45fSAndroid Build Coastguard Worker std::vector<gui::JankData> result;
187*38e8c45fSAndroid Build Coastguard Worker std::transform(range.first, range.second, std::back_inserter(result),
188*38e8c45fSAndroid Build Coastguard Worker [](std::pair<int32_t, gui::JankData> layerIdToJankData) {
189*38e8c45fSAndroid Build Coastguard Worker return layerIdToJankData.second;
190*38e8c45fSAndroid Build Coastguard Worker });
191*38e8c45fSAndroid Build Coastguard Worker
192*38e8c45fSAndroid Build Coastguard Worker return result;
193*38e8c45fSAndroid Build Coastguard Worker }
194*38e8c45fSAndroid Build Coastguard Worker
clearAndStopCollectingAllJankDataForTesting()195*38e8c45fSAndroid Build Coastguard Worker void JankTracker::clearAndStopCollectingAllJankDataForTesting() {
196*38e8c45fSAndroid Build Coastguard Worker // Undo startCollectingAllJankDataForTesting.
197*38e8c45fSAndroid Build Coastguard Worker sListenerCount--;
198*38e8c45fSAndroid Build Coastguard Worker sCollectAllJankDataForTesting = false;
199*38e8c45fSAndroid Build Coastguard Worker
200*38e8c45fSAndroid Build Coastguard Worker // Clear all tracked jank data.
201*38e8c45fSAndroid Build Coastguard Worker JankTracker& tracker = getInstance();
202*38e8c45fSAndroid Build Coastguard Worker const std::lock_guard<std::mutex> _l(tracker.mJankDataLock);
203*38e8c45fSAndroid Build Coastguard Worker tracker.mJankData.clear();
204*38e8c45fSAndroid Build Coastguard Worker }
205*38e8c45fSAndroid Build Coastguard Worker
206*38e8c45fSAndroid Build Coastguard Worker } // namespace android
207