ViewCapture in system apps

ViewCapture is a software tool that captures the properties of the views (such as location, size, scale, and visibility) attached to the windows it's hooked onto. ViewCapture captures information about the various views within a window and their properties, letting you know the state of the user experience at specific moments in time and track changes over time.

Screen recordings can visualize the state of a view at a specific time and show how it changes, but they demand significant CPU resources and can impact performance. The ViewCapture tool has less resource impact and can be enabled more frequently. Additionally, ViewCapture displays visualizations frame by frame at the view level, making it more straightforward to inspect the view state at specific moments compared to screen recordings.

This page describes how to onboard ViewCapture in system apps.

Use

ViewCapture.java implements an instance of onDrawListener and collects a ViewCapture trace during the drawing process. Each frame redraw triggers a traversal of the view tree hierarchy starting at the root view of the window. ViewCapture uses public View.java getter methods to fetch and copy values to a background thread for improved performance. The ViewCapture implementation optimizes this process by checking if a view is dirty or invalidated using captureViewTree, thus avoiding traversal of the entire view hierarchy. captureViewTree is available only for system apps and is part of the UnsupportedAppUsage API. Use of this API is limited to apps based on their target SDK version.

Limitations

The following sections describe the performance and memory limitations in running ViewCapture.

Performance

The average main thread overhead for ViewCapture performance is 195 μs. However, in the worst-case scenarios, it can take approximately 5 ms. Refer to the vc#onDraw slice in the Perfetto trace.

The overhead costs are primarily due to the following actions:

  1. Traversing the hierarchy costs 50 μs, even when pruned.
  2. Pulling objects from a freelist allocator to store copies of view properties costs 20 μs.
  3. Fetching each property value through a getter function results in many additional function calls per view, costing 110 μs.

Hence, enabling ViewCapture in always on-tracing (AOT) negatively impacts system performance and leads to jank. Due to these performance and memory limitations, this approach isn't ready for AOT. We recommend ViewCapture only for lab and local debugging.

Memory

Perfetto's method for ViewCapture traces uses a single ring buffer, which has a predefined memory footprint to prevent excessive memory use. This approach prevents excessive memory consumption by avoiding using separate ring buffers for each window, but doesn't resolve the issue of storing the entire view hierarchy for every state in Perfetto for each frame. Recording a single window, such as NexusLauncher, can produce over 30 seconds of ViewCapture data in a 10 MB buffer. However, capturing over 30 windows from System UI necessitates either a larger buffer or a considerably shorter recording time window.

Instructions

Follow these instructions to onboard ViewCapture in system apps:

  1. Add the dependency to your Android.bp file, as shown in the Launcher code.

    android_library {
        name: "YourLib",
        static_libs: [
              ...
            "//frameworks/libs/systemui:view_capture",
              ...
        ],
        platform_apis: true,
        privileged: true,
    }
    
  2. Create a ViewCapture instance when creating your window, for example:

    • Example 1:

      private SafeCloseable mViewCapture;
      
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        ...
        mViewCapture = ViewCaptureFactory.getInstance(this).startCapture(getWindow());
      }
      
    • Example 2:

      private SafeCloseable mViewCapture;
      
      @Override
      protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (enableViewCaptureTracing()) {
            mViewCaptureCloseable = ViewCaptureFactory.getInstance(getContext())
              .startCapture(getRootView(), ".NotificationShadeWindowView");
        }
        ...
      }
      
  3. Close the ViewCapture instance when destroying your window, as shown in the following examples:

    • Example 1:

      @Override
      public void onDestroy() {
        ...
        if (mViewCapture != null) mViewCapture.close();
      }
      
    • Example 2:

      @Override
      protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mViewCaptureCloseable != null) {
            mViewCaptureCloseable.close();
       }
        ...
      }