blob: b42536cb3f6c2a921f4d87f2c65a0db1b8da3289 [file] [log] [blame]
/*
* Copyright 2021 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.
*/
package androidx.metrics.performance
import android.os.Handler
import android.os.HandlerThread
import android.view.FrameMetrics
import android.view.View
import android.view.Window
import androidx.annotation.RequiresApi
/**
* Subclass of JankStatsBaseImpl records frame timing data for API 24 and later,
* using FrameMetrics (which was introduced in API 24). Jank data is collected by
* setting a [Window.addOnFrameMetricsAvailableListener]
* on the Window associated with the Activity being tracked.
*/
@RequiresApi(24)
internal open class JankStatsApi24Impl(
jankStats: JankStats,
view: View,
private val window: Window
) : JankStatsApi22Impl(jankStats, view) {
// Workaround for situation like b/206956036, where platform would sometimes send completely
// duplicate events through FrameMetrics. When that occurs, simply ignore the latest event
// that has the exact same start time.
var prevStart = 0L
// Used to avoid problems with data gathered before things are set up
var listenerAddedTime: Long = 0
private val frameMetricsAvailableListener: Window.OnFrameMetricsAvailableListener =
Window.OnFrameMetricsAvailableListener { _, frameMetrics, _ ->
val startTime = getFrameStartTime(frameMetrics)
// ignore historical data gathered before we started listening
if (startTime >= listenerAddedTime && startTime != prevStart) {
val expectedDuration = getExpectedFrameDuration(frameMetrics) *
jankStats.jankHeuristicMultiplier
jankStats.logFrameData(
startTime,
getFrameDuration(frameMetrics),
expectedDuration.toLong()
)
prevStart = startTime
}
}
internal open fun getFrameDuration(frameMetrics: FrameMetrics): Long {
return frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION)
}
internal open fun getFrameStartTime(frameMetrics: FrameMetrics): Long {
return getFrameStartTime()
}
open fun getExpectedFrameDuration(metrics: FrameMetrics): Long {
return getExpectedFrameDuration(decorViewRef.get())
}
override fun setupFrameTimer(enable: Boolean) {
window.let {
if (enable) {
if (listenerAddedTime == 0L) {
if (frameMetricsHandler == null) {
val thread = HandlerThread("FrameMetricsAggregator")
thread.start()
frameMetricsHandler = Handler(thread.looper)
}
// Already added, no need to do it again
window.addOnFrameMetricsAvailableListener(
frameMetricsAvailableListener,
frameMetricsHandler
)
listenerAddedTime = System.nanoTime()
}
} else {
window.removeOnFrameMetricsAvailableListener(frameMetricsAvailableListener)
listenerAddedTime = 0
}
}
}
companion object {
private var frameMetricsHandler: Handler? = null
}
}