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

Commit c9c81ef3 authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Compressing view capture data and changing the format to avoid

storing duplicate strings

Also starting the dump process early to avoid timeouts

Bug: 242868825
Test: Verified on web-hv UI tool
Change-Id: I9943e41426f820c9ab70d39b9f01896ed060cab4
parent 1b3b71f2
Loading
Loading
Loading
Loading
+25 −22
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ option java_outer_classname = "ViewCaptureData";
message ExportedData {

  repeated FrameData frameData = 1;
  repeated string classname = 2;
}

message FrameData {
@@ -31,26 +32,28 @@ message FrameData {
}

message ViewNode {
  optional string classname = 1;
  optional string id = 2;
  optional int32 left = 3;
  optional int32 top = 4;
  optional int32 width = 5;
  optional int32 height = 6;
  optional int32 scrollX = 7;
  optional int32 scrollY = 8;

  optional float translationX = 9;
  optional float translationY = 10;
  optional float scaleX = 11 [default = 1];
  optional float scaleY = 12 [default = 1];
  optional float alpha = 13 [default = 1];

  optional bool willNotDraw = 14;
  optional bool clipChildren = 15;
  optional int32 visibility = 16;

  repeated ViewNode children = 17;

  optional float elevation = 18;
  optional int32 classname_index = 1;
  optional int32 hashcode = 2;

  repeated ViewNode children = 3;

  optional string id = 4;
  optional int32 left = 5;
  optional int32 top = 6;
  optional int32 width = 7;
  optional int32 height = 8;
  optional int32 scrollX = 9;
  optional int32 scrollY = 10;

  optional float translationX = 11;
  optional float translationY = 12;
  optional float scaleX = 13 [default = 1];
  optional float scaleY = 14 [default = 1];
  optional float alpha = 15 [default = 1];

  optional bool willNotDraw = 16;
  optional bool clipChildren = 17;
  optional int32 visibility = 18;

  optional float elevation = 19;
}
+11 −9
Original line number Diff line number Diff line
@@ -1491,9 +1491,9 @@ public class Launcher extends StatefulActivity<LauncherState>
        if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
            View root = getDragLayer().getRootView();
            if (mViewCapture != null) {
                root.getViewTreeObserver().removeOnDrawListener(mViewCapture);
                mViewCapture.detach();
            }
            mViewCapture = new ViewCapture(root);
            mViewCapture = new ViewCapture(getWindow());
            mViewCapture.attach();
        }
    }
@@ -1501,6 +1501,10 @@ public class Launcher extends StatefulActivity<LauncherState>
    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mViewCapture != null) {
            mViewCapture.detach();
            mViewCapture = null;
        }
        mOverlayManager.onDetachedFromWindow();
        closeContextMenu();
    }
@@ -2981,6 +2985,7 @@ public class Launcher extends StatefulActivity<LauncherState>
     */
    @Override
    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
        SafeCloseable viewDump = mViewCapture == null ? null : mViewCapture.beginDump(writer, fd);
        super.dump(prefix, fd, writer, args);

        if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
@@ -3015,19 +3020,16 @@ public class Launcher extends StatefulActivity<LauncherState>
        writer.println(prefix + "\tmRotationHelper: " + mRotationHelper);
        writer.println(prefix + "\tmAppWidgetHost.isListening: " + mAppWidgetHost.isListening());

        if (mViewCapture != null) {
            writer.print(prefix + "\tmViewCapture: ");
            writer.flush();
            mViewCapture.dump(fd);
            writer.println();
        }

        // Extra logging for general debugging
        mDragLayer.dump(prefix, writer);
        mStateManager.dump(prefix, writer);
        mPopupDataProvider.dump(prefix, writer);
        mDeviceProfile.dump(this, prefix, writer);

        if (viewDump != null) {
            viewDump.close();
        }

        try {
            FileLog.flushAll(writer);
        } catch (Exception e) {
+69 −24
Original line number Diff line number Diff line
@@ -15,18 +15,23 @@
 */
package com.android.launcher3.util;

import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;

import static java.util.stream.Collectors.toList;

import android.content.res.Resources;
import android.os.Handler;
import android.os.Message;
import android.os.Trace;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Base64OutputStream;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnDrawListener;
import android.view.Window;

import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
@@ -38,7 +43,10 @@ import com.android.launcher3.view.ViewCaptureData.ViewNode;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.concurrent.Future;
import java.util.zip.GZIPOutputStream;

/**
 * Utility class for capturing view data every frame
@@ -53,6 +61,7 @@ public class ViewCapture implements OnDrawListener {
    // Launcher. This allows the first free frames avoid object allocation during view capture.
    private static final int INIT_POOL_SIZE = 300;

    private final Window mWindow;
    private final View mRoot;
    private final Resources mResources;

@@ -67,11 +76,12 @@ public class ViewCapture implements OnDrawListener {
    private ViewRef mPool = new ViewRef();

    /**
     * @param root the root view for the capture data
     * @param window the window for the capture data
     */
    public ViewCapture(View root) {
        mRoot = root;
        mResources = root.getResources();
    public ViewCapture(Window window) {
        mWindow = window;
        mRoot = mWindow.getDecorView();
        mResources = mRoot.getResources();
        mHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::captureViewPropertiesBg);
    }

@@ -82,6 +92,14 @@ public class ViewCapture implements OnDrawListener {
        mHandler.post(this::initPool);
    }

    /**
     * Removes a previously attached ViewCapture from the root
     */
    public void detach() {
        mHandler.post(() -> MAIN_EXECUTOR.execute(
                () -> mRoot.getViewTreeObserver().removeOnDrawListener(this)));
    }

    @Override
    public void onDraw() {
        Trace.beginSection("view_capture");
@@ -139,7 +157,7 @@ public class ViewCapture implements OnDrawListener {
        }
        mNodesBg[mFrameIndexBg] = result;
        ViewRef end = last;
        Executors.MAIN_EXECUTOR.execute(() -> addToPool(start, end));
        MAIN_EXECUTOR.execute(() -> addToPool(start, end));
        return true;
    }

@@ -160,7 +178,7 @@ public class ViewCapture implements OnDrawListener {
        }

        ViewRef end = current;
        Executors.MAIN_EXECUTOR.execute(() ->  {
        MAIN_EXECUTOR.execute(() ->  {
            addToPool(start, end);
            if (mRoot.isAttachedToWindow()) {
                mRoot.getViewTreeObserver().addOnDrawListener(this);
@@ -168,38 +186,58 @@ public class ViewCapture implements OnDrawListener {
        });
    }

    private String getName() {
        String title = mWindow.getAttributes().getTitle().toString();
        return TextUtils.isEmpty(title) ? mWindow.toString() : title;
    }

    /**
     * Creates a proto of all the data captured so far.
     * Starts the dump process which is completed on closing the returned object.
     */
    public void dump(FileDescriptor out) {
    public SafeCloseable beginDump(PrintWriter writer, FileDescriptor out) {
        Future<ExportedData> task = UI_HELPER_EXECUTOR.submit(this::dumpToProto);

        return () -> {
            writer.println();
            writer.println(" ContinuousViewCapture:");
            writer.println(" window " + getName() + ":");
            writer.println("  pkg:" + mRoot.getContext().getPackageName());
            writer.print("  data:");
            writer.flush();

            try (OutputStream os = new FileOutputStream(out)) {
                ExportedData data = task.get();
            Base64OutputStream encodedOS = new Base64OutputStream(os,
                    Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP);
                OutputStream encodedOS = new GZIPOutputStream(new Base64OutputStream(os,
                        Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP));
                data.writeTo(encodedOS);
                encodedOS.close();
                os.flush();
            } catch (Exception e) {
                Log.e(TAG, "Error capturing proto", e);
            }
            writer.println();
            writer.println("--end--");
        };
    }

    @WorkerThread
    private ExportedData dumpToProto() {
        ExportedData.Builder dataBuilder = ExportedData.newBuilder();
        Resources res = mResources;
        ArrayList<Class> classList = new ArrayList<>();

        int size = (mNodesBg[MEMORY_SIZE - 1] == null) ? mFrameIndexBg + 1 : MEMORY_SIZE;
        for (int i = size - 1; i >= 0; i--) {
            int index = (MEMORY_SIZE + mFrameIndexBg - i) % MEMORY_SIZE;
            ViewNode.Builder nodeBuilder = ViewNode.newBuilder();
            mNodesBg[index].toProto(res, nodeBuilder);
            mNodesBg[index].toProto(res, classList, nodeBuilder);
            dataBuilder.addFrameData(FrameData.newBuilder()
                    .setNode(nodeBuilder)
                    .setTimestamp(mFrameTimesBg[index]));
        }
        return dataBuilder.build();
        return dataBuilder
                .addAllClassname(classList.stream().map(Class::getName).collect(toList()))
                .build();
    }

    private ViewRef captureViewTree(View view, ViewRef start) {
@@ -278,10 +316,10 @@ public class ViewCapture implements OnDrawListener {
        /**
         * Converts the data to the proto representation and returns the next property ref
         * at the end of the iteration.
         * @param res
         * @return
         */
        public ViewPropertyRef toProto(Resources res, ViewNode.Builder outBuilder) {
        public ViewPropertyRef toProto(Resources res, ArrayList<Class> classList,
                ViewNode.Builder outBuilder) {
            String resolvedId;
            if (id >= 0) {
                try {
@@ -292,7 +330,14 @@ public class ViewCapture implements OnDrawListener {
            } else {
                resolvedId = "NO_ID";
            }
            outBuilder.setClassname(clazz.getName() + "@" + hashCode)
            int classnameIndex = classList.indexOf(clazz);
            if (classnameIndex < 0) {
                classnameIndex = classList.size();
                classList.add(clazz);
            }
            outBuilder
                    .setClassnameIndex(classnameIndex)
                    .setHashcode(hashCode)
                    .setId(resolvedId)
                    .setLeft(left)
                    .setTop(top)
@@ -311,7 +356,7 @@ public class ViewCapture implements OnDrawListener {
            ViewPropertyRef result = next;
            for (int i = 0; (i < childCount) && (result != null); i++) {
                ViewNode.Builder childBuilder = ViewNode.newBuilder();
                result = result.toProto(res, childBuilder);
                result = result.toProto(res, classList, childBuilder);
                outBuilder.addChildren(childBuilder);
            }
            return result;