diff options
| author | George Zacharia <george.zcharia@gmail.com> | 2023-07-02 15:00:14 +0530 |
|---|---|---|
| committer | George Zacharia <george.zcharia@gmail.com> | 2023-07-02 15:00:14 +0530 |
| commit | e1fe4bad8afa333499590808f3994c8bea0c833b (patch) | |
| tree | 84db2665b4d6f4e0ae52cecf10eef27facf72cb5 /viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java | |
| parent | 2d42f0cd9b97e656643049457e668864063a7051 (diff) | |
| parent | 313ca2b3cbaef97cb4665b24412eccf3d4cdcae3 (diff) | |
Merge tag 'android-13.0.0_r52' of https://android.googlesource.com/platform/frameworks/libs/systemui into HEADt13.0
Android 13.0.0 Release 52 (TQ3A.230605.012)
Change-Id: I1a59918e5a17f17968121c15476f31567c3d257a
Diffstat (limited to 'viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java')
| -rw-r--r-- | viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java | 171 |
1 files changed, 77 insertions, 94 deletions
diff --git a/viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java b/viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java index 70c58cb..fcd7ad8 100644 --- a/viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java +++ b/viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java @@ -35,18 +35,14 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.Window; -import android.os.Process; import androidx.annotation.Nullable; import androidx.annotation.UiThread; -import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; -import com.android.app.viewcapture.data.nano.ExportedData; -import com.android.app.viewcapture.data.nano.FrameData; -import com.android.app.viewcapture.data.nano.ViewNode; - -import com.google.protobuf.nano.MessageNano; +import com.android.app.viewcapture.data.ExportedData; +import com.android.app.viewcapture.data.FrameData; +import com.android.app.viewcapture.data.ViewNode; import java.io.FileDescriptor; import java.io.FileOutputStream; @@ -54,9 +50,8 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; import java.util.Optional; +import java.util.concurrent.Executor; import java.util.concurrent.FutureTask; import java.util.function.Consumer; import java.util.zip.GZIPOutputStream; @@ -64,7 +59,7 @@ import java.util.zip.GZIPOutputStream; /** * Utility class for capturing view data every frame */ -public class ViewCapture { +public abstract class ViewCapture { private static final String TAG = "ViewCapture"; @@ -74,55 +69,31 @@ public class ViewCapture { // Number of frames to keep in memory private final int mMemorySize; - private static final int DEFAULT_MEMORY_SIZE = 2000; + protected static final int DEFAULT_MEMORY_SIZE = 2000; // Initial size of the reference pool. This is at least be 5 * total number of views in // Launcher. This allows the first free frames avoid object allocation during view capture. - private static final int DEFAULT_INIT_POOL_SIZE = 300; + protected static final int DEFAULT_INIT_POOL_SIZE = 300; - private static ViewCapture INSTANCE; public static final LooperExecutor MAIN_EXECUTOR = new LooperExecutor(Looper.getMainLooper()); - public static ViewCapture getInstance() { - return getInstance(true, DEFAULT_MEMORY_SIZE, DEFAULT_INIT_POOL_SIZE); - } - - @VisibleForTesting - public static ViewCapture getInstance(boolean offloadToBackgroundThread, int memorySize, - int initPoolSize) { - if (INSTANCE == null) { - if (Looper.myLooper() == Looper.getMainLooper()) { - INSTANCE = new ViewCapture(offloadToBackgroundThread, memorySize, initPoolSize); - } else { - try { - return MAIN_EXECUTOR.submit(() -> - getInstance(offloadToBackgroundThread, memorySize, initPoolSize)).get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - } - } - return INSTANCE; - } - private final List<WindowListener> mListeners = new ArrayList<>(); - private final Executor mExecutor; + protected final Executor mBgExecutor; + private final Choreographer mChoreographer; // Pool used for capturing view tree on the UI thread. private ViewRef mPool = new ViewRef(); + private boolean mIsEnabled = true; - private ViewCapture(boolean offloadToBackgroundThread, int memorySize, int initPoolSize) { + protected ViewCapture(int memorySize, int initPoolSize, Choreographer choreographer, + Executor bgExecutor) { mMemorySize = memorySize; - if (offloadToBackgroundThread) { - mExecutor = createAndStartNewLooperExecutor("ViewCapture", - Process.THREAD_PRIORITY_FOREGROUND); - } else { - mExecutor = MAIN_EXECUTOR; - } - mExecutor.execute(() -> initPool(initPoolSize)); + mChoreographer = choreographer; + mBgExecutor = bgExecutor; + mBgExecutor.execute(() -> initPool(initPoolSize)); } - private static LooperExecutor createAndStartNewLooperExecutor(String name, int priority) { + public static LooperExecutor createAndStartNewLooperExecutor(String name, int priority) { HandlerThread thread = new HandlerThread(name, priority); thread.start(); return new LooperExecutor(thread.getLooper()); @@ -158,22 +129,34 @@ public class ViewCapture { } /** - * Attaches the ViewCapture to the provided window and returns a handle to detach the listener + * Attaches the ViewCapture to the provided window and returns a handle to detach the listener. + * Verifies that ViewCapture is enabled before actually attaching an onDrawListener. */ public SafeCloseable startCapture(View view, String name) { WindowListener listener = new WindowListener(view, name); - mExecutor.execute(() -> MAIN_EXECUTOR.execute(listener::attachToRoot)); + if (mIsEnabled) MAIN_EXECUTOR.execute(listener::attachToRoot); mListeners.add(listener); return () -> { mListeners.remove(listener); - listener.destroy(); + listener.detachFromRoot(); }; } + @UiThread + protected void enableOrDisableWindowListeners(boolean isEnabled) { + mIsEnabled = isEnabled; + mListeners.forEach(WindowListener::detachFromRoot); + if (mIsEnabled) mListeners.forEach(WindowListener::attachToRoot); + } + + /** * Dumps all the active view captures */ public void dump(PrintWriter writer, FileDescriptor out, Context context) { + if (!mIsEnabled) { + return; + } ViewIdProvider idProvider = new ViewIdProvider(context.getResources()); // Collect all the tasks first so that all the tasks are posted on the executor @@ -181,7 +164,7 @@ public class ViewCapture { .map(l -> { FutureTask<ExportedData> task = new FutureTask<ExportedData>(() -> l.dumpToProto(idProvider)); - mExecutor.execute(task); + mBgExecutor.execute(task); return Pair.create(l.name, task); }) .collect(toList()); @@ -196,7 +179,7 @@ public class ViewCapture { ExportedData data = pair.second.get(); OutputStream encodedOS = new GZIPOutputStream(new Base64OutputStream(os, Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP)); - encodedOS.write(MessageNano.toByteArray(data)); + data.writeTo(encodedOS); encodedOS.close(); os.flush(); } catch (Exception e) { @@ -216,7 +199,7 @@ public class ViewCapture { .map(l -> { FutureTask<ExportedData> task = new FutureTask<ExportedData>(() -> l.dumpToProto(idProvider)); - mExecutor.execute(task); + mBgExecutor.execute(task); return task; }) .findFirst(); @@ -272,16 +255,10 @@ public class ViewCapture { private final long[] mFrameTimesNanosBg = new long[mMemorySize]; private final ViewPropertyRef[] mNodesBg = new ViewPropertyRef[mMemorySize]; - private boolean mDestroyed = false; + private boolean mIsActive = true; private final Consumer<ViewRef> mCaptureCallback = this::captureViewPropertiesBg; - private Choreographer mChoreographer; WindowListener(View view, String name) { - try { - mChoreographer = MAIN_EXECUTOR.submit(Choreographer::getInstance).get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } mRoot = view; this.name = name; } @@ -300,7 +277,7 @@ public class ViewCapture { if (captured != null) { captured.callback = mCaptureCallback; captured.choreographerTimeNanos = mChoreographer.getFrameTimeNanos(); - mExecutor.execute(captured); + mBgExecutor.execute(captured); } mIsFirstFrame = false; Trace.endSection(); @@ -392,14 +369,15 @@ public class ViewCapture { } void attachToRoot() { + mIsActive = true; if (mRoot.isAttachedToWindow()) { - mRoot.getViewTreeObserver().addOnDrawListener(this); + safelyEnableOnDrawListener(); } else { mRoot.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { - if (!mDestroyed) { - mRoot.getViewTreeObserver().addOnDrawListener(WindowListener.this); + if (mIsActive) { + safelyEnableOnDrawListener(); } mRoot.removeOnAttachStateChangeListener(this); } @@ -411,29 +389,34 @@ public class ViewCapture { } } - void destroy() { + void detachFromRoot() { + mIsActive = false; mRoot.getViewTreeObserver().removeOnDrawListener(this); - mDestroyed = true; + } + + private void safelyEnableOnDrawListener() { + mRoot.getViewTreeObserver().removeOnDrawListener(this); + mRoot.getViewTreeObserver().addOnDrawListener(this); } @WorkerThread private ExportedData dumpToProto(ViewIdProvider idProvider) { int size = (mNodesBg[mMemorySize - 1] == null) ? mFrameIndexBg + 1 : mMemorySize; - ExportedData exportedData = new ExportedData(); - exportedData.frameData = new FrameData[size]; + ExportedData.Builder exportedDataBuilder = ExportedData.newBuilder(); ArrayList<Class> classList = new ArrayList<>(); for (int i = size - 1; i >= 0; i--) { int index = (mMemorySize + mFrameIndexBg - i) % mMemorySize; - ViewNode node = new ViewNode(); - mNodesBg[index].toProto(idProvider, classList, node); - FrameData frameData = new FrameData(); - frameData.node = node; - frameData.timestamp = mFrameTimesNanosBg[index]; - exportedData.frameData[size - i - 1] = frameData; + ViewNode.Builder nodeBuilder = ViewNode.newBuilder(); + mNodesBg[index].toProto(idProvider, classList, nodeBuilder); + FrameData.Builder frameDataBuilder = FrameData.newBuilder() + .setNode(nodeBuilder) + .setTimestamp(mFrameTimesNanosBg[index]); + exportedDataBuilder.addFrameData(frameDataBuilder); } - exportedData.classname = classList.stream().map(Class::getName).toArray(String[]::new); - return exportedData; + return exportedDataBuilder + .addAllClassname(classList.stream().map(Class::getName).collect(toList())) + .build(); } private ViewRef captureViewTree(View view, ViewRef start) { @@ -518,35 +501,35 @@ public class ViewCapture { * at the end of the iteration. */ public ViewPropertyRef toProto(ViewIdProvider idProvider, ArrayList<Class> classList, - ViewNode viewNode) { + ViewNode.Builder viewNode) { int classnameIndex = classList.indexOf(clazz); if (classnameIndex < 0) { classnameIndex = classList.size(); classList.add(clazz); } - viewNode.classnameIndex = classnameIndex; - viewNode.hashcode = hashCode; - viewNode.id = idProvider.getName(id); - viewNode.left = left; - viewNode.top = top; - viewNode.width = right - left; - viewNode.height = bottom - top; - viewNode.translationX = translateX; - viewNode.translationY = translateY; - viewNode.scaleX = scaleX; - viewNode.scaleY = scaleY; - viewNode.alpha = alpha; - viewNode.visibility = visibility; - viewNode.willNotDraw = willNotDraw; - viewNode.elevation = elevation; - viewNode.clipChildren = clipChildren; + + viewNode.setClassnameIndex(classnameIndex) + .setHashcode(hashCode) + .setId(idProvider.getName(id)) + .setLeft(left) + .setTop(top) + .setWidth(right - left) + .setHeight(bottom - top) + .setTranslationX(translateX) + .setTranslationY(translateY) + .setScaleX(scaleX) + .setScaleY(scaleY) + .setAlpha(alpha) + .setVisibility(visibility) + .setWillNotDraw(willNotDraw) + .setElevation(elevation) + .setClipChildren(clipChildren); ViewPropertyRef result = next; - viewNode.children = new ViewNode[childCount]; for (int i = 0; (i < childCount) && (result != null); i++) { - ViewNode childViewNode = new ViewNode(); + ViewNode.Builder childViewNode = ViewNode.newBuilder(); result = result.toProto(idProvider, classList, childViewNode); - viewNode.children[i] = childViewNode; + viewNode.addChildren(childViewNode); } return result; } |
