Chet Haase | 523d7a1 | 2021-01-07 15:41:42 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2021 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | package androidx.metrics.performance |
| 17 | |
Chet Haase | 523d7a1 | 2021-01-07 15:41:42 -0800 | [diff] [blame] | 18 | import android.os.Handler |
| 19 | import android.os.HandlerThread |
| 20 | import android.view.FrameMetrics |
| 21 | import android.view.View |
| 22 | import android.view.Window |
| 23 | import androidx.annotation.RequiresApi |
| 24 | |
| 25 | /** |
| 26 | * Subclass of JankStatsBaseImpl records frame timing data for API 24 and later, |
| 27 | * using FrameMetrics (which was introduced in API 24). Jank data is collected by |
| 28 | * setting a [Window.addOnFrameMetricsAvailableListener] |
| 29 | * on the Window associated with the Activity being tracked. |
| 30 | */ |
| 31 | @RequiresApi(24) |
| 32 | internal open class JankStatsApi24Impl( |
| 33 | jankStats: JankStats, |
Chet Haase | a3e2759 | 2021-11-19 11:05:12 -0800 | [diff] [blame] | 34 | view: View, |
| 35 | private val window: Window |
Chet Haase | 523d7a1 | 2021-01-07 15:41:42 -0800 | [diff] [blame] | 36 | ) : JankStatsApi22Impl(jankStats, view) { |
| 37 | |
Chet Haase | a3e2759 | 2021-11-19 11:05:12 -0800 | [diff] [blame] | 38 | // Workaround for situation like b/206956036, where platform would sometimes send completely |
| 39 | // duplicate events through FrameMetrics. When that occurs, simply ignore the latest event |
| 40 | // that has the exact same start time. |
| 41 | var prevStart = 0L |
| 42 | |
Chet Haase | eca418b | 2022-01-12 12:44:19 -0800 | [diff] [blame] | 43 | // Used to avoid problems with data gathered before things are set up |
| 44 | var listenerAddedTime: Long = 0 |
| 45 | |
Chet Haase | 523d7a1 | 2021-01-07 15:41:42 -0800 | [diff] [blame] | 46 | private val frameMetricsAvailableListener: Window.OnFrameMetricsAvailableListener = |
| 47 | Window.OnFrameMetricsAvailableListener { _, frameMetrics, _ -> |
Chet Haase | 1d34c5d | 2021-09-24 17:00:50 -0700 | [diff] [blame] | 48 | val startTime = getFrameStartTime(frameMetrics) |
Chet Haase | 523d7a1 | 2021-01-07 15:41:42 -0800 | [diff] [blame] | 49 | // ignore historical data gathered before we started listening |
Chet Haase | a3e2759 | 2021-11-19 11:05:12 -0800 | [diff] [blame] | 50 | if (startTime >= listenerAddedTime && startTime != prevStart) { |
Chet Haase | 523d7a1 | 2021-01-07 15:41:42 -0800 | [diff] [blame] | 51 | val expectedDuration = getExpectedFrameDuration(frameMetrics) * |
Chet Haase | 6890048 | 2021-10-12 10:21:30 -0700 | [diff] [blame] | 52 | jankStats.jankHeuristicMultiplier |
Chet Haase | 523d7a1 | 2021-01-07 15:41:42 -0800 | [diff] [blame] | 53 | jankStats.logFrameData( |
| 54 | startTime, |
Chet Haase | 1d34c5d | 2021-09-24 17:00:50 -0700 | [diff] [blame] | 55 | getFrameDuration(frameMetrics), |
Chet Haase | 523d7a1 | 2021-01-07 15:41:42 -0800 | [diff] [blame] | 56 | expectedDuration.toLong() |
| 57 | ) |
Chet Haase | a3e2759 | 2021-11-19 11:05:12 -0800 | [diff] [blame] | 58 | prevStart = startTime |
Chet Haase | 523d7a1 | 2021-01-07 15:41:42 -0800 | [diff] [blame] | 59 | } |
| 60 | } |
| 61 | |
Chet Haase | 1d34c5d | 2021-09-24 17:00:50 -0700 | [diff] [blame] | 62 | internal open fun getFrameDuration(frameMetrics: FrameMetrics): Long { |
| 63 | return frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION) |
| 64 | } |
| 65 | |
| 66 | internal open fun getFrameStartTime(frameMetrics: FrameMetrics): Long { |
| 67 | return getFrameStartTime() |
| 68 | } |
| 69 | |
Chet Haase | 523d7a1 | 2021-01-07 15:41:42 -0800 | [diff] [blame] | 70 | open fun getExpectedFrameDuration(metrics: FrameMetrics): Long { |
| 71 | return getExpectedFrameDuration(decorViewRef.get()) |
| 72 | } |
| 73 | |
Chet Haase | 523d7a1 | 2021-01-07 15:41:42 -0800 | [diff] [blame] | 74 | override fun setupFrameTimer(enable: Boolean) { |
Chet Haase | a3e2759 | 2021-11-19 11:05:12 -0800 | [diff] [blame] | 75 | window.let { |
Chet Haase | 523d7a1 | 2021-01-07 15:41:42 -0800 | [diff] [blame] | 76 | if (enable) { |
Chet Haase | eca418b | 2022-01-12 12:44:19 -0800 | [diff] [blame] | 77 | if (listenerAddedTime == 0L) { |
| 78 | if (frameMetricsHandler == null) { |
| 79 | val thread = HandlerThread("FrameMetricsAggregator") |
| 80 | thread.start() |
| 81 | frameMetricsHandler = Handler(thread.looper) |
| 82 | } |
| 83 | // Already added, no need to do it again |
| 84 | window.addOnFrameMetricsAvailableListener( |
| 85 | frameMetricsAvailableListener, |
| 86 | frameMetricsHandler |
| 87 | ) |
| 88 | listenerAddedTime = System.nanoTime() |
Chet Haase | 523d7a1 | 2021-01-07 15:41:42 -0800 | [diff] [blame] | 89 | } |
Chet Haase | 523d7a1 | 2021-01-07 15:41:42 -0800 | [diff] [blame] | 90 | } else { |
| 91 | window.removeOnFrameMetricsAvailableListener(frameMetricsAvailableListener) |
| 92 | listenerAddedTime = 0 |
| 93 | } |
| 94 | } |
| 95 | } |
Chet Haase | eca418b | 2022-01-12 12:44:19 -0800 | [diff] [blame] | 96 | |
| 97 | companion object { |
| 98 | private var frameMetricsHandler: Handler? = null |
| 99 | } |
Chet Haase | 523d7a1 | 2021-01-07 15:41:42 -0800 | [diff] [blame] | 100 | } |