Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit adb79c98 authored by Steven Terrell's avatar Steven Terrell Committed by Android (Google) Code Review
Browse files

Merge "API Changes and Update Activity" into main

parents 820cda72 fd6f435e
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -9109,6 +9109,46 @@ package android.app.blob {
}
package android.app.jank {
  @FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public final class AppJankStats {
    ctor public AppJankStats(int, @NonNull String, @Nullable String, @Nullable String, long, long, @NonNull android.app.jank.FrameOverrunHistogram);
    method @NonNull public android.app.jank.FrameOverrunHistogram getFrameOverrunHistogram();
    method public long getJankyFrameCount();
    method public long getTotalFrameCount();
    method public int getUid();
    method @NonNull public String getWidgetCategory();
    method @NonNull public String getWidgetId();
    method @NonNull public String getWidgetState();
    field public static final String ANIMATING = "animating";
    field public static final String ANIMATION = "animation";
    field public static final String DRAGGING = "dragging";
    field public static final String FLINGING = "flinging";
    field public static final String KEYBOARD = "keyboard";
    field public static final String MEDIA = "media";
    field public static final String NAVIGATION = "navigation";
    field public static final String NONE = "none";
    field public static final String OTHER = "other";
    field public static final String PLAYBACK = "playback";
    field public static final String PREDICTIVE_BACK = "predictive_back";
    field public static final String SCROLL = "scroll";
    field public static final String SCROLLING = "scrolling";
    field public static final String SWIPING = "swiping";
    field public static final String TAPPING = "tapping";
    field public static final String WIDGET_CATEGORY_UNSPECIFIED = "widget_category_unspecified";
    field public static final String WIDGET_STATE_UNSPECIFIED = "widget_state_unspecified";
    field public static final String ZOOMING = "zooming";
  }
  @FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public class FrameOverrunHistogram {
    ctor public FrameOverrunHistogram();
    method public void addFrameOverrunMillis(int);
    method @NonNull public int[] getBucketCounters();
    method @NonNull public int[] getBucketEndpointsMillis();
  }
}
package android.app.job {
  public class JobInfo implements android.os.Parcelable {
@@ -53738,6 +53778,7 @@ package android.view {
    method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
    method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
    method public void removeOnUnhandledKeyEventListener(android.view.View.OnUnhandledKeyEventListener);
    method @FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public void reportAppJankStats(@NonNull android.app.jank.AppJankStats);
    method public void requestApplyInsets();
    method @Deprecated public void requestFitSystemWindows();
    method public final boolean requestFocus();
+58 −0
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import android.app.VoiceInteractor.Request;
import android.app.admin.DevicePolicyManager;
import android.app.assist.AssistContent;
import android.app.compat.CompatChanges;
import android.app.jank.JankTracker;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
@@ -124,6 +125,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SuperNotCalledException;
import android.view.ActionMode;
import android.view.Choreographer;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.ContextThemeWrapper;
@@ -175,6 +177,7 @@ import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ToolbarActionBar;
import com.android.internal.app.WindowDecorActionBar;
import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneWindow;
import com.android.internal.util.dump.DumpableContainerImpl;

@@ -1145,6 +1148,9 @@ public class Activity extends ContextThemeWrapper

    };

    @Nullable
    private JankTracker mJankTracker;

    private static native String getDlWarning();

    /**
@@ -2245,6 +2251,10 @@ public class Activity extends ContextThemeWrapper
        // Notify autofill
        getAutofillClientController().onActivityPostResumed();

        if (android.app.jank.Flags.detailedAppJankMetricsApi()) {
            startAppJankTracking();
        }

        mCalled = true;
    }

@@ -9264,6 +9274,9 @@ public class Activity extends ContextThemeWrapper
        dispatchActivityPrePaused();
        mDoReportFullyDrawn = false;
        mFragments.dispatchPause();
        if (android.app.jank.Flags.detailedAppJankMetricsApi()) {
            stopAppJankTracking();
        }
        mCalled = false;
        final long startTime = SystemClock.uptimeMillis();
        onPause();
@@ -9946,4 +9959,49 @@ public class Activity extends ContextThemeWrapper
            mScreenCaptureCallbackHandler.unregisterScreenCaptureCallback(callback);
        }
    }

    /**
     * Enabling jank tracking for this activity but only if certain conditions are met. The
     * application must have an app category other than undefined and a visible view.
     */
    private void startAppJankTracking() {
        if (!android.app.jank.Flags.detailedAppJankMetricsLoggingEnabled()) {
            return;
        }
        if (mApplication.getApplicationInfo().category == ApplicationInfo.CATEGORY_UNDEFINED) {
            return;
        }
        if (getWindow() != null && getWindow().peekDecorView() != null) {
            DecorView decorView = (DecorView) getWindow().peekDecorView();
            if (decorView.getVisibility() == View.VISIBLE) {
                decorView.setAppJankStatsCallback(new DecorView.AppJankStatsCallback() {
                    @Override
                    public JankTracker getAppJankTracker() {
                        return mJankTracker;
                    }
                });
                if (mJankTracker == null) {
                    // TODO b/377960907 use the Choreographer attached to the ViewRootImpl instead.
                    mJankTracker = new JankTracker(Choreographer.getInstance(),
                            decorView);
                }
                // TODO b/377674765 confirm this is the string we want logged.
                mJankTracker.setActivityName(getComponentName().getClassName());
                mJankTracker.setAppUid(myUid());
                mJankTracker.enableAppJankTracking();
            }
        }
    }

    /**
     * Call to disable jank tracking for this activity.
     */
    private void stopAppJankTracking() {
        if (!android.app.jank.Flags.detailedAppJankMetricsLoggingEnabled()) {
            return;
        }
        if (mJankTracker != null) {
            mJankTracker.disableAppJankTracking();
        }
    }
}
+253 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 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.
 */

package android.app.jank;

import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 *  This class stores detailed jank statistics for an individual UI widget. These statistics
 *  provide performance insights for specific UI widget states by correlating the number of
 *  "Janky frames" with the total frames rendered while the widget is in that state. This class
 *  can be used by library widgets to provide the system with more detailed information about
 *  where jank is happening for diagnostic purposes.
 */
@FlaggedApi(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
public final class AppJankStats {
    // UID of the app
    private int mUid;

    // The id that has been set for the widget.
    private String mWidgetId;

    // A general category that the widget applies to.
    private String mWidgetCategory;

    // The states that the UI elements can report
    private String mWidgetState;

    // The number of frames reported during this state.
    private long mTotalFrames;

    // Total number of frames determined to be janky during the reported state.
    private long mJankyFrames;

    // Histogram of frame duration overruns encoded in predetermined buckets.
    private FrameOverrunHistogram mFrameOverrunHistogram;


    /** Used to indicate no widget category has been set. */
    public static final String WIDGET_CATEGORY_UNSPECIFIED =
            "widget_category_unspecified";

    /** UI elements that facilitate scrolling. */
    public static final String SCROLL = "scroll";

    /** UI elements that facilitate playing animations. */
    public static final String ANIMATION = "animation";

    /** UI elements that facilitate media playback. */
    public static final String MEDIA = "media";

    /** UI elements that facilitate in-app navigation. */
    public static final String NAVIGATION = "navigation";

    /** UI elements that facilitate displaying, hiding or interacting with keyboard. */
    public static final String KEYBOARD = "keyboard";

    /** UI elements that facilitate predictive back gesture navigation. */
    public static final String PREDICTIVE_BACK = "predictive_back";

    /** UI elements that don't fall in one or any of the other categories. */
    public static final String OTHER = "other";

    /** Used to indicate no widget state has been set. */
    public static final String WIDGET_STATE_UNSPECIFIED = "widget_state_unspecified";

    /** Used to indicate the UI element currently has no state and is idle. */
    public static final String NONE = "none";

    /** Used to indicate the UI element is currently scrolling. */
    public static final String SCROLLING = "scrolling";

    /** Used to indicate the UI element is currently being flung. */
    public static final String FLINGING = "flinging";

    /** Used to indicate the UI element is currently being swiped. */
    public static final String SWIPING = "swiping";

    /** Used to indicate the UI element is currently being dragged. */
    public static final String DRAGGING = "dragging";

    /** Used to indicate the UI element is currently zooming. */
    public static final String ZOOMING = "zooming";

    /** Used to indicate the UI element is currently animating. */
    public static final String ANIMATING = "animating";

    /** Used to indicate the UI element is currently playing media. */
    public static final String PLAYBACK = "playback";

    /** Used to indicate the UI element is currently being tapped on, for example on a keyboard. */
    public static final String TAPPING = "tapping";


    /**
     * @hide
     */
    @StringDef(value = {
            WIDGET_CATEGORY_UNSPECIFIED,
            SCROLL,
            ANIMATION,
            MEDIA,
            NAVIGATION,
            KEYBOARD,
            PREDICTIVE_BACK,
            OTHER
    })
    @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
    @Retention(RetentionPolicy.SOURCE)
    public @interface WidgetCategory {
    }
    /**
     * @hide
     */
    @StringDef(value = {
            WIDGET_STATE_UNSPECIFIED,
            NONE,
            SCROLLING,
            FLINGING,
            SWIPING,
            DRAGGING,
            ZOOMING,
            ANIMATING,
            PLAYBACK,
            TAPPING,
    })
    @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
    @Retention(RetentionPolicy.SOURCE)
    public @interface WidgetState {
    }


    /**
     * Creates a new AppJankStats object.
     *
     * @param appUid the Uid of the App that is collecting jank stats.
     * @param widgetId the widget id that frames will be associated to.
     * @param widgetCategory a general functionality category that the widget falls into. Must be
     *                       one of the following: SCROLL, ANIMATION, MEDIA, NAVIGATION, KEYBOARD,
     *                       PREDICTIVE_BACK, OTHER or will be set to WIDGET_CATEGORY_UNSPECIFIED
     *                       if no value is passed.
     * @param widgetState the state the widget was in while frames were counted. Must be one of
     *                    the following: NONE, SCROLLING, FLINGING, SWIPING, DRAGGING, ZOOMING,
     *                    ANIMATING, PLAYBACK, TAPPING or will be set to WIDGET_STATE_UNSPECIFIED
     *                    if no value is passed.
     * @param totalFrames the total number of frames that were counted for this stat.
     * @param jankyFrames the total number of janky frames that were counted for this stat.
     * @param frameOverrunHistogram the histogram with predefined buckets. See
     * {@link #getFrameOverrunHistogram()} for details.
     *
     */
    public AppJankStats(int appUid, @NonNull String widgetId,
            @Nullable @WidgetCategory String widgetCategory,
            @Nullable @WidgetState String widgetState, long totalFrames, long jankyFrames,
            @NonNull FrameOverrunHistogram frameOverrunHistogram) {
        mUid = appUid;
        mWidgetId = widgetId;
        mWidgetCategory = widgetCategory != null ? widgetCategory : WIDGET_CATEGORY_UNSPECIFIED;
        mWidgetState = widgetState != null ? widgetState : WIDGET_STATE_UNSPECIFIED;
        mTotalFrames = totalFrames;
        mJankyFrames = jankyFrames;
        mFrameOverrunHistogram = frameOverrunHistogram;
    }

    /**
     * Returns the app uid.
     *
     * @return the app uid.
     */
    public int getUid() {
        return mUid;
    }

    /**
     * Returns the id of the widget that reported state changes.
     *
     * @return the id of the widget that reported state changes. This value cannot be null.
     */
    public @NonNull String getWidgetId() {
        return mWidgetId;
    }

    /**
     * Returns the category that the widget's functionality generally falls into, or
     * widget_category_unspecified {@link #WIDGET_CATEGORY_UNSPECIFIED} if no value was passed in.
     *
     * @return the category that the widget's functionality generally falls into, this value cannot
     * be null.
     */
    public @NonNull @WidgetCategory String getWidgetCategory() {
        return mWidgetCategory;
    }

    /**
     * Returns the widget's state that was reported for this stat, or widget_state_unspecified
     * {@link #WIDGET_STATE_UNSPECIFIED} if no value was passed in.
     *
     * @return the widget's state that was reported for this stat. This value cannot be null.
     */
    public @NonNull @WidgetState String getWidgetState() {
        return mWidgetState;
    }

    /**
     * Returns the number of frames that were determined to be janky for this stat.
     *
     * @return the number of frames that were determined to be janky for this stat.
     */
    public long getJankyFrameCount() {
        return mJankyFrames;
    }

    /**
     * Returns the total number of frames counted for this stat.
     *
     * @return the total number of frames counted for this stat.
     */
    public long getTotalFrameCount() {
        return mTotalFrames;
    }

    /**
     * Returns a Histogram containing frame overrun times in millis grouped into predefined buckets.
     * See {@link FrameOverrunHistogram} for more information.
     *
     * @return Histogram containing frame overrun times in predefined buckets. This value cannot
     * be null.
     */
    public @NonNull FrameOverrunHistogram getFrameOverrunHistogram() {
        return mFrameOverrunHistogram;
    }
}
+107 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 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.
 */

package android.app.jank;

import android.annotation.FlaggedApi;
import android.annotation.NonNull;

import java.util.Arrays;

/**
 * This class is intended to be used when reporting {@link AppJankStats} back to the system. It's
 * intended to be used by library widgets to help facilitate the reporting of frame overrun times
 * by adding those times into predefined buckets.
 */
@FlaggedApi(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
public class FrameOverrunHistogram {
    private static int[] sBucketEndpoints = new int[]{
            Integer.MIN_VALUE, -200, -150, -100, -90, -80, -70, -60, -50, -40, -30, -25, -20, -18,
            -16, -14, -12, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 25, 30, 40,
            50, 60, 70, 80, 90, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000
    };
    private int[] mBucketCounts;

    /**
     * Create a new instance of FrameOverrunHistogram.
     */
    public FrameOverrunHistogram() {
        mBucketCounts = new int[sBucketEndpoints.length - 1];
    }

    /**
     * Increases the count by one for the bucket representing the frame overrun duration.
     *
     * @param frameOverrunMillis frame overrun duration in millis, frame overrun is the difference
     *                           between a frames deadline and when it was rendered.
     */
    public void addFrameOverrunMillis(int frameOverrunMillis) {
        int countsIndex = getIndexForCountsFromOverrunTime(frameOverrunMillis);
        mBucketCounts[countsIndex]++;
    }

    /**
     * Returns the counts for the all the frame overrun buckets.
     *
     * @return an array of integers representing the counts of frame overrun times. This value
     * cannot be null.
     */
    public @NonNull int[] getBucketCounters() {
        return Arrays.copyOf(mBucketCounts, mBucketCounts.length);
    }

    /**
     * Returns the predefined endpoints for the histogram.
     *
     * @return array of integers representing the endpoints for the predefined histogram count
     * buckets. This value cannot be null.
     */
    public @NonNull int[] getBucketEndpointsMillis() {
        return Arrays.copyOf(sBucketEndpoints, sBucketEndpoints.length);
    }

    // This takes the overrun time and returns what bucket it belongs to in the counters array.
    private int getIndexForCountsFromOverrunTime(int overrunTime) {
        if (overrunTime < 20) {
            if (overrunTime >= -20) {
                return (overrunTime + 20) / 2 + 12;
            }
            if (overrunTime >= -30) {
                return (overrunTime + 30) / 5 + 10;
            }
            if (overrunTime >= -100) {
                return (overrunTime + 100) / 10 + 3;
            }
            if (overrunTime >= -200) {
                return (overrunTime + 200) / 50 + 1;
            }
            return 0;
        }
        if (overrunTime < 30) {
            return (overrunTime - 20) / 5 + 32;
        }
        if (overrunTime < 100) {
            return (overrunTime - 30) / 10 + 34;
        }
        if (overrunTime < 200) {
            return (overrunTime - 50) / 100 + 41;
        }
        if (overrunTime < 1000) {
            return (overrunTime - 200) / 100 + 43;
        }
        return sBucketEndpoints.length - 1;
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -86,6 +86,14 @@ public class JankDataProcessor {
        jankDataProcessingComplete();
    }

    /**
     * Merges app jank stats reported by components outside the platform to the current pending
     * stats
     */
    public void mergeJankStats(AppJankStats jankStats, String activityName) {
        // TODO b/377572463 Add Merging Logic
    }

    /**
     * Returns the aggregate map of different pending jank stats.
     */
Loading