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

Commit 0a88d1d3 authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 11162593 from 2f671d2f to 24Q1-release

Change-Id: I07c596b9b43edd7154e1bb8f8d622dd43fbe37f0
parents 5ac78dc7 2f671d2f
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -71,15 +71,32 @@ class TraceUtils {
        private const val DEBUG_COROUTINE_TRACING = false
        const val DEFAULT_TRACK_NAME = "AsyncTraces"

        @JvmStatic
        inline fun <T> trace(crossinline tag: () -> String, crossinline block: () -> T): T {
            return traceSection(tag) { block() }
        }

        @JvmStatic
        inline fun <T> trace(tag: String, crossinline block: () -> T): T {
            return traceSection(tag) { block() }
        }

        @JvmStatic
        inline fun traceRunnable(tag: String, crossinline block: () -> Unit): Runnable {
            return Runnable { traceSection(tag) { block() } }
        }

        @JvmStatic
        inline fun traceRunnable(crossinline tag: () -> String, crossinline block: () -> Unit): Runnable {
            return Runnable { traceSection(tag) { block() } }
        }

        /**
         * Helper function for creating a Runnable object that implements TraceNameSupplier.
         *
         * This is useful for posting Runnables to Handlers with meaningful names.
         */
        @JvmStatic
        inline fun namedRunnable(tag: String, crossinline block: () -> Unit): Runnable {
            return object : Runnable, TraceNameSupplier {
                override fun getTraceName(): String = tag
@@ -100,6 +117,7 @@ class TraceUtils {
         * This can be used to trace coroutine code. Note that all usages of this method will appear
         * under a single track.
         */
        @JvmStatic
        inline fun <T> traceAsync(method: String, block: () -> T): T =
            traceAsync(DEFAULT_TRACK_NAME, method, block)

@@ -110,6 +128,7 @@ class TraceUtils {
         * [trackName] of the track. The track is one of the rows visible in a perfetto trace inside
         * the app process.
         */
        @JvmStatic
        inline fun <T> traceAsync(trackName: String, method: String, block: () -> T): T {
            val cookie = lastCookie.incrementAndGet()
            Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, trackName, method, cookie)
+52 −13
Original line number Diff line number Diff line
@@ -19,7 +19,9 @@ package com.android.app.viewcapture;
import static com.android.app.viewcapture.data.ExportedData.MagicNumber.MAGIC_NUMBER_H;
import static com.android.app.viewcapture.data.ExportedData.MagicNumber.MAGIC_NUMBER_L;

import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.media.permission.SafeCloseable;
import android.os.HandlerThread;
@@ -141,7 +143,12 @@ public abstract class ViewCapture {
        WindowListener listener = new WindowListener(view, name);
        if (mIsEnabled) MAIN_EXECUTOR.execute(listener::attachToRoot);
        mListeners.add(listener);

        Context context = view.getContext();
        context.registerComponentCallbacks(listener);

        return () -> {
            context.unregisterComponentCallbacks(listener);
            mListeners.remove(listener);
            listener.detachFromRoot();
        };
@@ -228,42 +235,47 @@ public abstract class ViewCapture {
     * view tree on the main thread every time onDraw is called. It then saves the state of the view
     * tree traversed in a local list of nodes, so that this list of nodes can be processed on a
     * background thread, and prepared for being dumped into a bugreport.
     *
     * <p>
     * Since some of the work needs to be done on the main thread after every draw, this piece of
     * code needs to be hyper optimized. That is why we are recycling ViewRef and ViewPropertyRef
     * objects and storing the list of nodes as a flat LinkedList, rather than as a tree. This data
     * structure allows recycling to happen in O(1) time via pointer assignment. Without this
     * optimization, a lot of time is wasted creating ViewRef objects, or finding ViewRef objects to
     * recycle.
     *
     * <p>
     * Another optimization is to only traverse view nodes on the main thread that have potentially
     * changed since the last frame was drawn. This can be determined via a combination of private
     * flags inside the View class.
     *
     * <p>
     * Another optimization is to not store or manipulate any string objects on the main thread.
     * While this might seem trivial, using Strings in any form causes the ViewCapture to hog the
     * main thread for up to an additional 6-7ms. It must be avoided at all costs.
     *
     * <p>
     * Another optimization is to only store the class names of the Views in the view hierarchy one
     * time. They are then referenced via a classNameIndex value stored in each ViewPropertyRef.
     *
     * <p>
     * TODO: b/262585897: If further memory optimization is required, an effective one would be to
     * only store the changes between frames, rather than the entire node tree for each frame.
     * The go/web-hv UX already does this, and has reaped significant memory improves because of it.
     *
     * <p>
     * TODO: b/262585897: Another memory optimization could be to store all integer, float, and
     * boolean information via single integer values via the Chinese remainder theorem, or a similar
     * algorithm, which enables multiple numerical values to be stored inside 1 number. Doing this
     * would allow each ViewProperty / ViewRef to slim down its memory footprint significantly.
     *
     * <p>
     * One important thing to remember is that bugs related to recycling will usually only appear
     * after at least 2000 frames have been rendered. If that code is changed, the tester can
     * use hard-coded logs to verify that recycling is happening, and test view capturing at least
     * ~8000 frames or so to verify the recycling functionality is working properly.
     * <p>
     * Each WindowListener is memory aware and will both stop collecting view capture information,
     * as well as delete their current stash of information upon a signal from the system that
     * memory resources are scarce. The user will need to restart the app process before
     * more ViewCapture information is captured.
     */
    private class WindowListener implements ViewTreeObserver.OnDrawListener {
    private class WindowListener implements ViewTreeObserver.OnDrawListener, ComponentCallbacks2 {

        @Nullable // Nullable in tests only
        @Nullable
        public View mRoot;
        public final String name;

@@ -271,8 +283,8 @@ public abstract class ViewCapture {

        private int mFrameIndexBg = -1;
        private boolean mIsFirstFrame = true;
        private final long[] mFrameTimesNanosBg = new long[mMemorySize];
        private final ViewPropertyRef[] mNodesBg = new ViewPropertyRef[mMemorySize];
        private long[] mFrameTimesNanosBg = new long[mMemorySize];
        private ViewPropertyRef[] mNodesBg = new ViewPropertyRef[mMemorySize];

        private boolean mIsActive = true;
        private final Consumer<ViewRef> mCaptureCallback = this::captureViewPropertiesBg;
@@ -417,9 +429,11 @@ public abstract class ViewCapture {
        }

        private void safelyEnableOnDrawListener() {
            if (mRoot != null) {
                mRoot.getViewTreeObserver().removeOnDrawListener(this);
                mRoot.getViewTreeObserver().addOnDrawListener(this);
            }
        }

        @WorkerThread
        private WindowData dumpToProto(ViewIdProvider idProvider, ArrayList<Class> classList) {
@@ -469,6 +483,30 @@ public abstract class ViewCapture {
                return ref;
            }
        }

        @Override
        public void onTrimMemory(int level) {
            if (ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL == level
                    || ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW == level) {
                mNodesBg = new ViewPropertyRef[0];
                mFrameTimesNanosBg = new long[0];
                if (mRoot != null && mRoot.getContext() != null) {
                    mRoot.getContext().unregisterComponentCallbacks(this);
                }
                detachFromRoot();
                mRoot = null;
            }
        }

        @Override
        public void onConfigurationChanged(Configuration configuration) {
            // No Operation
        }

        @Override
        public void onLowMemory() {
            onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW);
        }
    }

    private static class ViewPropertyRef {
@@ -559,6 +597,7 @@ public abstract class ViewCapture {
    private static class ViewRef implements Runnable {
        public View view;
        public int childCount = 0;
        @Nullable
        public ViewRef next;

        public Consumer<ViewRef> callback = null;