public class

FrameMetricsAggregator

extends java.lang.Object

 java.lang.Object

↳androidx.core.app.FrameMetricsAggregator

Gradle dependencies

compile group: 'androidx.core', name: 'core', version: '1.15.0-alpha02'

  • groupId: androidx.core
  • artifactId: core
  • version: 1.15.0-alpha02

Artifact androidx.core:core:1.15.0-alpha02 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.core:core com.android.support:support-compat

Androidx class mapping:

androidx.core.app.FrameMetricsAggregator android.support.v4.app.FrameMetricsAggregator

Overview

This class can be used to record and return data about per-frame durations. It returns those results in an array per metric type, with the results indicating how many samples were recorded for each duration value. The details of the durations data are described in FrameMetricsAggregator.getMetrics().

For more information on the various metrics tracked, see the documentation for the FrameMetrics API added in API 24 as well as the GPU Profiling guide.

Summary

Fields
public static final intANIMATION_DURATION

A flag indicating that the metrics should track the animation duration.

public static final intANIMATION_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.ANIMATION_DURATION is stored.

public static final intCOMMAND_DURATION

A flag indicating that the metrics should track the command duration.

public static final intCOMMAND_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.SYNC_DURATION is stored.

public static final intDELAY_DURATION

A flag indicating that the metrics should track the delay duration.

public static final intDELAY_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.DELAY_DURATION is stored.

public static final intDRAW_DURATION

A flag indicating that the metrics should track the draw duration.

public static final intDRAW_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.DRAW_DURATION is stored.

public static final intEVERY_DURATION

A flag indicating that the metrics should track all durations.

public static final intINPUT_DURATION

A flag indicating that the metrics should track the input duration.

public static final intINPUT_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.INPUT_DURATION is stored.

public static final intLAYOUT_MEASURE_DURATION

A flag indicating that the metrics should track the layout duration.

public static final intLAYOUT_MEASURE_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.LAYOUT_MEASURE_DURATION is stored.

public static final intSWAP_DURATION

A flag indicating that the metrics should track the swap duration.

public static final intSWAP_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.COMMAND_DURATION is stored.

public static final intSYNC_DURATION

A flag indicating that the metrics should track the sync duration.

public static final intSYNC_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.SYNC_DURATION is stored.

public static final intTOTAL_DURATION

A flag indicating that the metrics should track the total duration.

public static final intTOTAL_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.TOTAL_DURATION is stored.

Constructors
publicFrameMetricsAggregator()

Constructs a FrameMetricsAggregator object that will track FrameMetricsAggregator.TOTAL_DURATION metrics.

publicFrameMetricsAggregator(int metricTypeFlags)

Constructs a FrameMetricsAggregator object that will track the metrics specified bty metricTypeFlags, which is a value derived by OR'ing together metrics constants such as FrameMetricsAggregator.TOTAL_DURATION to specify all metrics that should be tracked.

Methods
public voidadd(Activity activity)

Starts recording frame metrics for the given activity.

public SparseIntArraygetMetrics()

Returns the currently-collected metrics in an array of SparseIntArray objects.

public SparseIntArrayremove(Activity activity)

Stops recording metrics for activity and returns the collected metrics so far.

public SparseIntArrayreset()

Resets the metrics data and returns the currently-collected metrics.

public SparseIntArraystop()

Stops recording metrics for all Activities currently being tracked.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

public static final int TOTAL_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.TOTAL_DURATION is stored.

See also: FrameMetricsAggregator.getMetrics()

public static final int INPUT_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.INPUT_DURATION is stored.

See also: FrameMetricsAggregator.getMetrics()

public static final int LAYOUT_MEASURE_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.LAYOUT_MEASURE_DURATION is stored.

See also: FrameMetricsAggregator.getMetrics()

public static final int DRAW_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.DRAW_DURATION is stored.

See also: FrameMetricsAggregator.getMetrics()

public static final int SYNC_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.SYNC_DURATION is stored.

See also: FrameMetricsAggregator.getMetrics()

public static final int COMMAND_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.SYNC_DURATION is stored.

See also: FrameMetricsAggregator.getMetrics()

public static final int SWAP_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.COMMAND_DURATION is stored.

See also: FrameMetricsAggregator.getMetrics()

public static final int DELAY_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.DELAY_DURATION is stored.

See also: FrameMetricsAggregator.getMetrics()

public static final int ANIMATION_INDEX

The index in the metrics array where the data for FrameMetricsAggregator.ANIMATION_DURATION is stored.

See also: FrameMetricsAggregator.getMetrics()

public static final int TOTAL_DURATION

A flag indicating that the metrics should track the total duration. This flag may be OR'd with the other flags here when calling FrameMetricsAggregator.FrameMetricsAggregator(int) to indicate all of the metrics that should be tracked for that activity.

public static final int INPUT_DURATION

A flag indicating that the metrics should track the input duration. This flag may be OR'd with the other flags here when calling FrameMetricsAggregator.FrameMetricsAggregator(int) to indicate all of the metrics that should be tracked for that activity.

public static final int LAYOUT_MEASURE_DURATION

A flag indicating that the metrics should track the layout duration. This flag may be OR'd with the other flags here when calling FrameMetricsAggregator.FrameMetricsAggregator(int) to indicate all of the metrics that should be tracked for that activity.

public static final int DRAW_DURATION

A flag indicating that the metrics should track the draw duration. This flag may be OR'd with the other flags here when calling FrameMetricsAggregator.FrameMetricsAggregator(int) to indicate all of the metrics that should be tracked for that activity.

public static final int SYNC_DURATION

A flag indicating that the metrics should track the sync duration. This flag may be OR'd with the other flags here when calling FrameMetricsAggregator.FrameMetricsAggregator(int) to indicate all of the metrics that should be tracked for that activity.

public static final int COMMAND_DURATION

A flag indicating that the metrics should track the command duration. This flag may be OR'd with the other flags here when calling FrameMetricsAggregator.FrameMetricsAggregator(int) to indicate all of the metrics that should be tracked for that activity.

public static final int SWAP_DURATION

A flag indicating that the metrics should track the swap duration. This flag may be OR'd with the other flags here when calling FrameMetricsAggregator.FrameMetricsAggregator(int) to indicate all of the metrics that should be tracked for that activity.

public static final int DELAY_DURATION

A flag indicating that the metrics should track the delay duration. This flag may be OR'd with the other flags here when calling FrameMetricsAggregator.FrameMetricsAggregator(int) to indicate all of the metrics that should be tracked for that activity.

public static final int ANIMATION_DURATION

A flag indicating that the metrics should track the animation duration. This flag may be OR'd with the other flags here when calling FrameMetricsAggregator.FrameMetricsAggregator(int) to indicate all of the metrics that should be tracked for that activity.

public static final int EVERY_DURATION

A flag indicating that the metrics should track all durations. This is a shorthand for OR'ing all of the duration flags. This flag may be OR'd with the other flags here when calling FrameMetricsAggregator.FrameMetricsAggregator(int) to indicate the metrics that should be tracked for that activity.

Constructors

public FrameMetricsAggregator()

Constructs a FrameMetricsAggregator object that will track FrameMetricsAggregator.TOTAL_DURATION metrics. If more fine-grained metrics are needed, use FrameMetricsAggregator.FrameMetricsAggregator(int) instead.

public FrameMetricsAggregator(int metricTypeFlags)

Constructs a FrameMetricsAggregator object that will track the metrics specified bty metricTypeFlags, which is a value derived by OR'ing together metrics constants such as FrameMetricsAggregator.TOTAL_DURATION to specify all metrics that should be tracked. For example, TOTAL_DURATION | DRAW_DURATION will track both the total and draw durations for every frame.

Parameters:

metricTypeFlags: A bitwise collection of flags indicating which metrics should be recorded.

Methods

public void add(Activity activity)

Starts recording frame metrics for the given activity.

Parameters:

activity: The Activity object which will have its metrics measured.

public SparseIntArray remove(Activity activity)

Stops recording metrics for activity and returns the collected metrics so far. Recording will continue if there are still other activities being tracked. Calling remove() does not reset the metrics array; you must call FrameMetricsAggregator.reset() to clear the data.

Parameters:

activity: The Activity to stop tracking metrics for.

Returns:

An array whose index refers to the type of metric stored in that item's SparseIntArray object, e.g., data for TOTAL_DURATION is stored in the [TOTAL_INDEX] item.

See also: FrameMetricsAggregator.getMetrics()

public SparseIntArray stop()

Stops recording metrics for all Activities currently being tracked. Like FrameMetricsAggregator.remove(Activity), this method returns the currently-collected metrics. Calling stop() does not reset the metrics array; you must call FrameMetricsAggregator.reset() to clear the data.

Returns:

An array whose index refers to the type of metric stored in that item's SparseIntArray object, e.g., data for TOTAL_DURATION is stored in the [TOTAL_INDEX] item.

See also: FrameMetricsAggregator.remove(Activity), FrameMetricsAggregator.getMetrics()

public SparseIntArray reset()

Resets the metrics data and returns the currently-collected metrics.

Returns:

An array whose index refers to the type of metric stored in that item's SparseIntArray object, e.g., data for TOTAL_DURATION is stored in the [TOTAL_INDEX] item.

See also: FrameMetricsAggregator.getMetrics()

public SparseIntArray getMetrics()

Returns the currently-collected metrics in an array of SparseIntArray objects. The index of the array indicates which metric's data is stored in that SparseIntArray object. For example, results for total duration will be in the [TOTAL_INDEX] item.

The return value may be null if no metrics were tracked. This is especially true on releases earlier than API 24, as the FrameMetrics system does not exist on these earlier release. If the return value is not null, any of the objects at a given index in the array may still be null, which indicates that data was not being tracked for that type of metric. For example, if the FrameMetricsAggregator was created with a call to new FrameMetricsAggregator(TOTAL_DURATION | DRAW_DURATION), then the SparseIntArray at index INPUT_INDEX will be null.

For a given non-null SparseIntArray, the results stored are the number of samples at each millisecond value (rounded). For example, if a data sample consisted of total durations of 5.1ms, 5.8ms, 6.1ms, and 8.2ms, the SparseIntArray at [TOTAL_DURATION] would have key-value pairs (5, 1), (6, 2), (8, 1).

Returns:

An array whose index refers to the type of metric stored in that item's SparseIntArray object, e.g., data for TOTAL_DURATION is stored in the [TOTAL_INDEX] item.

Source

/*
 * Copyright (C) 2017 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.core.app;


import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;

import android.app.Activity;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.SparseIntArray;
import android.view.Window;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;

/**
 * This class can be used to record and return data about per-frame durations. It returns those
 * results in an array per metric type, with the results indicating how many samples were
 * recorded for each duration value. The details of the durations data are described in
 * {@link #getMetrics()}.
 * <p>
 * For more information on the various metrics tracked, see the documentation for the
 * <a href="https://developer.android.com/reference/android/view/FrameMetrics.html">FrameMetrics
 * </a> API added in API 24 as well as the
 * <a href="https://developer.android.com/studio/profile/dev-options-rendering.html">GPU Profiling
 * guide</a>.
 */
public class FrameMetricsAggregator {

    /**
     * The index in the metrics array where the data for {@link #TOTAL_DURATION}
     * is stored.
     * @see #getMetrics()
     */
    public static final int TOTAL_INDEX          = 0;
    /**
     * The index in the metrics array where the data for {@link #INPUT_DURATION}
     * is stored.
     * @see #getMetrics()
     */
    public static final int INPUT_INDEX          = 1;
    /**
     * The index in the metrics array where the data for {@link #LAYOUT_MEASURE_DURATION}
     * is stored.
     * @see #getMetrics()
     */
    public static final int LAYOUT_MEASURE_INDEX = 2;
    /**
     * The index in the metrics array where the data for {@link #DRAW_DURATION}
     * is stored.
     * @see #getMetrics()
     */
    public static final int DRAW_INDEX           = 3;
    /**
     * The index in the metrics array where the data for {@link #SYNC_DURATION}
     * is stored.
     * @see #getMetrics()
     */
    public static final int SYNC_INDEX           = 4;
    /**
     * The index in the metrics array where the data for {@link #SYNC_DURATION}
     * is stored.
     * @see #getMetrics()
     */
    public static final int COMMAND_INDEX        = 5;
    /**
     * The index in the metrics array where the data for {@link #COMMAND_DURATION}
     * is stored.
     * @see #getMetrics()
     */
    public static final int SWAP_INDEX           = 6;
    /**
     * The index in the metrics array where the data for {@link #DELAY_DURATION}
     * is stored.
     * @see #getMetrics()
     */
    public static final int DELAY_INDEX          = 7;
    /**
     * The index in the metrics array where the data for {@link #ANIMATION_DURATION}
     * is stored.
     * @see #getMetrics()
     */
    public static final int ANIMATION_INDEX      = 8;
    private static final int LAST_INDEX          = 8;

    /**
     * A flag indicating that the metrics should track the total duration. This
     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
     * to indicate all of the metrics that should be tracked for that activity.
     */
    public static final int TOTAL_DURATION          = 1 << TOTAL_INDEX;
    /**
     * A flag indicating that the metrics should track the input duration. This
     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
     * to indicate all of the metrics that should be tracked for that activity.
     */
    public static final int INPUT_DURATION          = 1 << INPUT_INDEX;
    /**
     * A flag indicating that the metrics should track the layout duration. This
     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
     * to indicate all of the metrics that should be tracked for that activity.
     */
    public static final int LAYOUT_MEASURE_DURATION = 1 << LAYOUT_MEASURE_INDEX;
    /**
     * A flag indicating that the metrics should track the draw duration. This
     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
     * to indicate all of the metrics that should be tracked for that activity.
     */
    public static final int DRAW_DURATION           = 1 << DRAW_INDEX;
    /**
     * A flag indicating that the metrics should track the sync duration. This
     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
     * to indicate all of the metrics that should be tracked for that activity.
     */
    public static final int SYNC_DURATION           = 1 << SYNC_INDEX;
    /**
     * A flag indicating that the metrics should track the command duration. This
     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
     * to indicate all of the metrics that should be tracked for that activity.
     */
    public static final int COMMAND_DURATION        = 1 << COMMAND_INDEX;
    /**
     * A flag indicating that the metrics should track the swap duration. This
     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
     * to indicate all of the metrics that should be tracked for that activity.
     */
    public static final int SWAP_DURATION           = 1 << SWAP_INDEX;
    /**
     * A flag indicating that the metrics should track the delay duration. This
     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
     * to indicate all of the metrics that should be tracked for that activity.
     */
    public static final int DELAY_DURATION          = 1 << DELAY_INDEX;
    /**
     * A flag indicating that the metrics should track the animation duration. This
     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
     * to indicate all of the metrics that should be tracked for that activity.
     */
    public static final int ANIMATION_DURATION      = 1 << ANIMATION_INDEX;
    /**
     * A flag indicating that the metrics should track all durations. This is
     * a shorthand for OR'ing all of the duration flags. This
     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
     * to indicate the metrics that should be tracked for that activity.
     */
    public static final int EVERY_DURATION          = 0x1ff;

    private final FrameMetricsBaseImpl mInstance;

    @RestrictTo(LIBRARY_GROUP_PREFIX)
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(
            flag = true,
            value = {
                    TOTAL_DURATION,
                    INPUT_DURATION,
                    LAYOUT_MEASURE_DURATION,
                    DRAW_DURATION,
                    SYNC_DURATION,
                    COMMAND_DURATION,
                    SWAP_DURATION,
                    DELAY_DURATION,
                    ANIMATION_DURATION,
                    EVERY_DURATION
            })
    public @interface MetricType {}

    /**
     * Constructs a FrameMetricsAggregator object that will track {@link #TOTAL_DURATION}
     * metrics. If more fine-grained metrics are needed, use {@link #FrameMetricsAggregator(int)}
     * instead.
     */
    public FrameMetricsAggregator() {
        this(TOTAL_DURATION);
    }

    /**
     * Constructs a FrameMetricsAggregator object that will track the metrics specified bty
     * {@code metricTypeFlags}, which is a value derived by OR'ing together metrics constants
     * such as {@link #TOTAL_DURATION} to specify all metrics that should be tracked. For example,
     * {@code TOTAL_DURATION | DRAW_DURATION} will track both the total and draw durations
     * for every frame.
     *
     * @param metricTypeFlags A bitwise collection of flags indicating which metrics should
     * be recorded.
     */
    public FrameMetricsAggregator(@MetricType int metricTypeFlags) {
        if (Build.VERSION.SDK_INT >= 24) {
            mInstance = new FrameMetricsApi24Impl(metricTypeFlags);
        } else {
            mInstance = new FrameMetricsBaseImpl();
        }
    }

    /**
     * Starts recording frame metrics for the given activity.
     *
     * @param activity The Activity object which will have its metrics measured.
     */
    public void add(@NonNull Activity activity) {
        mInstance.add(activity);
    }

    /**
     * Stops recording metrics for {@code activity} and returns the collected metrics so far.
     * Recording will continue if there are still other activities being tracked. Calling
     * remove() does not reset the metrics array; you must call {@link #reset()} to clear the
     * data.
     *
     * @param activity The Activity to stop tracking metrics for.
     * @return An array whose index refers to the type of metric stored in that item's
     * SparseIntArray object, e.g., data for {@code TOTAL_DURATION} is stored in
     * the {@code [TOTAL_INDEX]} item.
     * @see #getMetrics()
     */
    @Nullable
    public SparseIntArray[] remove(@NonNull Activity activity) {
        return mInstance.remove(activity);
    }

    /**
     * Stops recording metrics for all Activities currently being tracked. Like {@link
     * #remove(Activity)}, this method returns the currently-collected metrics. Calling
     * stop() does not reset the metrics array; you must call {@link #reset()} to clear the
     * data.
     *
     * @return An array whose index refers to the type of metric stored in that item's
     * SparseIntArray object, e.g., data for {@code TOTAL_DURATION} is stored in
     * the {@code [TOTAL_INDEX]} item.
     * @see #remove(Activity)
     * @see #getMetrics()
     */
    @Nullable
    public SparseIntArray[] stop() {
        return mInstance.stop();
    }

    /**
     * Resets the metrics data and returns the currently-collected metrics.
     *
     * @return An array whose index refers to the type of metric stored in that item's
     * SparseIntArray object, e.g., data for {@code TOTAL_DURATION} is stored in
     * the {@code [TOTAL_INDEX]} item.
     * @see #getMetrics()
     */
    @Nullable
    public SparseIntArray[] reset() {
        return mInstance.reset();
    }

    /**
     * Returns the currently-collected metrics in an array of SparseIntArray objects.
     * The index of the array indicates which metric's data is stored in that
     * SparseIntArray object. For example, results for total duration will be in
     * the {@code [TOTAL_INDEX]} item.
     * <p>
     * The return value may be null if no metrics were tracked. This is especially true on releases
     * earlier than API 24, as the FrameMetrics system does not exist on these earlier release.
     * If the return value is not null, any of the objects at a given index in the array
     * may still be null, which indicates that data was not being tracked for that type of metric.
     * For example, if the FrameMetricsAggregator was created with a call to
     * {@code new FrameMetricsAggregator(TOTAL_DURATION | DRAW_DURATION)}, then the SparseIntArray
     * at index {@code INPUT_INDEX} will be null.
     * <p>
     * For a given non-null SparseIntArray, the results stored are the number of samples at
     * each millisecond value (rounded). For example, if a data sample consisted of total
     * durations of 5.1ms, 5.8ms, 6.1ms, and 8.2ms, the SparseIntArray at {@code [TOTAL_DURATION]}
     * would have key-value pairs (5, 1), (6, 2), (8, 1).
     *
     * @return An array whose index refers to the type of metric stored in that item's
     * SparseIntArray object, e.g., data for {@code TOTAL_DURATION} is stored in
     * the {@code [TOTAL_INDEX]} item.
     */
    @Nullable
    public SparseIntArray[] getMetrics() {
        return mInstance.getMetrics();
    }

    /**
     * Base implementation noops everything - there's no data to return on pre-API24 releases.
     */
    private static class FrameMetricsBaseImpl {

        FrameMetricsBaseImpl() {
        }

        public void add(Activity activity) {
        }

        public SparseIntArray[] remove(Activity activity) {
            return null;
        }

        public SparseIntArray[] stop() {
            return null;
        }

        public SparseIntArray[] getMetrics() {
            return null;
        }

        public SparseIntArray[] reset() {
            return null;
        }
    }

    @RequiresApi(24)
    private static class FrameMetricsApi24Impl extends FrameMetricsBaseImpl {

        private static final int NANOS_PER_MS = 1000000;
        // rounding value adds half a millisecond, for rounding to nearest ms
        private static final int NANOS_ROUNDING_VALUE = NANOS_PER_MS / 2;
        int mTrackingFlags;
        SparseIntArray[] mMetrics = new SparseIntArray[LAST_INDEX + 1];
        private final ArrayList<WeakReference<Activity>> mActivities = new ArrayList<>();
        private static HandlerThread sHandlerThread = null;
        private static Handler sHandler = null;

        FrameMetricsApi24Impl(int trackingFlags) {
            mTrackingFlags = trackingFlags;
        }

        Window.OnFrameMetricsAvailableListener mListener =
                new Window.OnFrameMetricsAvailableListener() {
            @Override
            public void onFrameMetricsAvailable(Window window,
                    android.view.FrameMetrics frameMetrics, int dropCountSinceLastInvocation) {
                if ((mTrackingFlags & TOTAL_DURATION) != 0) {
                    addDurationItem(mMetrics[TOTAL_INDEX],
                            frameMetrics.getMetric(android.view.FrameMetrics.TOTAL_DURATION));
                }
                if ((mTrackingFlags & INPUT_DURATION) != 0) {
                    addDurationItem(mMetrics[INPUT_INDEX],
                            frameMetrics.getMetric(
                                    android.view.FrameMetrics.INPUT_HANDLING_DURATION));
                }
                if ((mTrackingFlags & LAYOUT_MEASURE_DURATION) != 0) {
                    addDurationItem(mMetrics[LAYOUT_MEASURE_INDEX],
                            frameMetrics.getMetric(
                                    android.view.FrameMetrics.LAYOUT_MEASURE_DURATION));
                }
                if ((mTrackingFlags & DRAW_DURATION) != 0) {
                    addDurationItem(mMetrics[DRAW_INDEX],
                            frameMetrics.getMetric(android.view.FrameMetrics.DRAW_DURATION));
                }
                if ((mTrackingFlags & SYNC_DURATION) != 0) {
                    addDurationItem(mMetrics[SYNC_INDEX],
                            frameMetrics.getMetric(android.view.FrameMetrics.SYNC_DURATION));
                }
                if ((mTrackingFlags & SWAP_DURATION) != 0) {
                    addDurationItem(mMetrics[SWAP_INDEX],
                            frameMetrics.getMetric(
                                    android.view.FrameMetrics.SWAP_BUFFERS_DURATION));
                }
                if ((mTrackingFlags & COMMAND_DURATION) != 0) {
                    addDurationItem(mMetrics[COMMAND_INDEX],
                            frameMetrics.getMetric(
                                    android.view.FrameMetrics.COMMAND_ISSUE_DURATION));
                }
                if ((mTrackingFlags & DELAY_DURATION) != 0) {
                    addDurationItem(mMetrics[DELAY_INDEX],
                            frameMetrics.getMetric(
                                    android.view.FrameMetrics.UNKNOWN_DELAY_DURATION));
                }
                if ((mTrackingFlags & ANIMATION_DURATION) != 0) {
                    addDurationItem(mMetrics[ANIMATION_INDEX],
                            frameMetrics.getMetric(
                                    android.view.FrameMetrics.ANIMATION_DURATION));
                }
            }
        };

        void addDurationItem(SparseIntArray buckets, long duration) {
            if (buckets != null) {
                int durationMs = (int) ((duration + NANOS_ROUNDING_VALUE) / NANOS_PER_MS);
                if (duration >= 0) {
                    // ignore values < 0; something must have gone wrong
                    int oldValue = buckets.get(durationMs);
                    buckets.put(durationMs, (oldValue + 1));
                }
            }
        }

        @Override
        public void add(Activity activity) {
            if (sHandlerThread == null) {
                sHandlerThread = new HandlerThread("FrameMetricsAggregator");
                sHandlerThread.start();
                sHandler = new Handler(sHandlerThread.getLooper());
            }
            for (int i = 0; i <= LAST_INDEX; ++i) {
                if (mMetrics[i] == null && (mTrackingFlags & (1 << i)) != 0) {
                    mMetrics[i] = new SparseIntArray();
                }
            }
            activity.getWindow().addOnFrameMetricsAvailableListener(mListener, sHandler);
            mActivities.add(new WeakReference<>(activity));
        }

        @Override
        public SparseIntArray[] remove(Activity activity) {
            for (WeakReference<Activity> activityRef : mActivities) {
                if (activityRef.get() == activity) {
                    mActivities.remove(activityRef);
                    break;
                }
            }
            activity.getWindow().removeOnFrameMetricsAvailableListener(mListener);
            return mMetrics;
        }

        @Override
        public SparseIntArray[] stop() {
            int size = mActivities.size();
            for (int i = size - 1; i >= 0; i--) {
                WeakReference<Activity> ref = mActivities.get(i);
                Activity activity = ref.get();
                if (ref.get() != null) {
                    activity.getWindow().removeOnFrameMetricsAvailableListener(mListener);
                    mActivities.remove(i);
                }
            }
            return mMetrics;
        }

        @Override
        public SparseIntArray[] getMetrics() {
            return mMetrics;
        }

        @Override
        public SparseIntArray[] reset() {
            SparseIntArray[] returnVal = mMetrics;
            mMetrics = new SparseIntArray[LAST_INDEX + 1];
            return returnVal;
        }

    }

}