Add initial SysUI trace support

Bug: 144854916
Test: Take a sysui trace
Change-Id: Ia8a87c8c6756ecbb77beb06a60d5b46b883ed8d9
t stat
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 577e3bb..26ef1d6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -27,7 +27,6 @@
 
     void onInitialize(in Bundle params) = 12;
 
-
     /**
      * @deprecated
      */
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 08996c3..386fe53 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -85,6 +85,8 @@
     // The notification panel is expanded and interactive (either locked or unlocked), and the
     // quick settings is not expanded
     public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11;
+    // Winscope tracing is enabled
+    public static final int SYSUI_STATE_TRACING_ENABLED = 1 << 12;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -98,7 +100,8 @@
             SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
             SYSUI_STATE_OVERVIEW_DISABLED,
             SYSUI_STATE_HOME_DISABLED,
-            SYSUI_STATE_SEARCH_DISABLED
+            SYSUI_STATE_SEARCH_DISABLED,
+            SYSUI_STATE_TRACING_ENABLED
     })
     public @interface SystemUiStateFlags {}
 
@@ -117,6 +120,7 @@
         str.add((flags & SYSUI_STATE_BOUNCER_SHOWING) != 0 ? "bouncer_visible" : "");
         str.add((flags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0 ? "a11y_click" : "");
         str.add((flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0 ? "a11y_long_click" : "");
+        str.add((flags & SYSUI_STATE_TRACING_ENABLED) != 0 ? "tracing" : "");
         return str.toString();
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java
new file mode 100644
index 0000000..557845c
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2019 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 com.android.systemui.shared.tracing;
+
+import android.os.Trace;
+import android.util.Log;
+import android.view.Choreographer;
+
+import com.android.internal.util.TraceBuffer;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.function.Consumer;
+
+/**
+ * A proto tracer implementation that can be updated directly (upon state change), or on the next
+ * scheduled frame.
+ *
+ * @param <P> The class type of the proto provider
+ * @param <S> The proto class type of the encapsulating proto
+ * @param <T> The proto class type of the individual proto entries in the buffer
+ * @param <R> The proto class type of the entry root proto in the buffer
+ */
+public class FrameProtoTracer<P, S extends P, T extends P, R>
+        implements TraceBuffer.ProtoProvider<P, S, T>, Choreographer.FrameCallback {
+
+    private static final String TAG = "FrameProtoTracer";
+    private static final int BUFFER_CAPACITY = 1024 * 1024;
+
+    private final Object mLock = new Object();
+    private final TraceBuffer<P, S, T> mBuffer;
+    private final File mTraceFile;
+    private final ProtoTraceParams<P, S, T, R> mParams;
+    private final Choreographer mChoreographer;
+    private final Queue<T> mPool = new LinkedList<>();
+    private final ArrayList<ProtoTraceable<R>> mTraceables = new ArrayList<>();
+    private final ArrayList<ProtoTraceable<R>> mTmpTraceables = new ArrayList<>();
+
+    private volatile boolean mEnabled;
+    private boolean mFrameScheduled;
+
+    public interface ProtoTraceParams<P, S, T, R> {
+        File getTraceFile();
+        S getEncapsulatingTraceProto();
+        T updateBufferProto(T reuseObj, ArrayList<ProtoTraceable<R>> traceables);
+        byte[] serializeEncapsulatingProto(S encapsulatingProto, Queue<T> buffer);
+        byte[] getProtoBytes(P proto);
+        int getProtoSize(P proto);
+    }
+
+    public FrameProtoTracer(ProtoTraceParams<P, S, T, R> params) {
+        mParams = params;
+        mBuffer = new TraceBuffer<>(BUFFER_CAPACITY, this, new Consumer<T>() {
+            @Override
+            public void accept(T t) {
+                onProtoDequeued(t);
+            }
+        });
+        mTraceFile = params.getTraceFile();
+        mChoreographer = Choreographer.getMainThreadInstance();
+    }
+
+    @Override
+    public int getItemSize(P proto) {
+        return mParams.getProtoSize(proto);
+    }
+
+    @Override
+    public byte[] getBytes(P proto) {
+        return mParams.getProtoBytes(proto);
+    }
+
+    @Override
+    public void write(S encapsulatingProto, Queue<T> buffer, OutputStream os) throws IOException {
+        os.write(mParams.serializeEncapsulatingProto(encapsulatingProto, buffer));
+    }
+
+    public void start() {
+        synchronized (mLock) {
+            if (mEnabled) {
+                return;
+            }
+            mBuffer.resetBuffer();
+            mEnabled = true;
+        }
+        logState();
+    }
+
+    public void stop() {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            mEnabled = false;
+        }
+        writeToFile();
+    }
+
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+
+    public void add(ProtoTraceable<R> traceable) {
+        synchronized (mLock) {
+            mTraceables.add(traceable);
+        }
+    }
+
+    public void remove(ProtoTraceable<R> traceable) {
+        synchronized (mLock) {
+            mTraceables.remove(traceable);
+        }
+    }
+
+    public void scheduleFrameUpdate() {
+        if (!mEnabled || mFrameScheduled) {
+            return;
+        }
+
+        // Schedule an update on the next frame
+        mChoreographer.postFrameCallback(this);
+        mFrameScheduled = true;
+    }
+
+    public void update() {
+        if (!mEnabled) {
+            return;
+        }
+
+        logState();
+    }
+
+    public float getBufferUsagePct() {
+        return (float) mBuffer.getBufferSize() / BUFFER_CAPACITY;
+    }
+
+    @Override
+    public void doFrame(long frameTimeNanos) {
+        logState();
+    }
+
+    private void onProtoDequeued(T proto) {
+        mPool.add(proto);
+    }
+
+    private void logState() {
+        synchronized (mLock) {
+            mTmpTraceables.addAll(mTraceables);
+        }
+
+        mBuffer.add(mParams.updateBufferProto(mPool.poll(), mTmpTraceables));
+        mTmpTraceables.clear();
+        mFrameScheduled = false;
+    }
+
+    private void writeToFile() {
+        try {
+            Trace.beginSection("ProtoTracer.writeToFile");
+            mBuffer.writeTraceToFile(mTraceFile, mParams.getEncapsulatingTraceProto());
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to write buffer to file", e);
+        } finally {
+            Trace.endSection();
+        }
+    }
+}
+
+
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/ProtoTraceable.java b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/ProtoTraceable.java
new file mode 100644
index 0000000..e05b0b0
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/ProtoTraceable.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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 com.android.systemui.shared.tracing;
+
+/**
+ * @see FrameProtoTracer
+ */
+public interface ProtoTraceable<T> {
+
+    /**
+     * NOTE: Implementations should update all fields in this proto.
+     */
+    void writeToProto(T proto);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index aacc2c4..a6be091 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -117,6 +117,7 @@
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.leak.GarbageMonitor;
@@ -327,6 +328,7 @@
     @Inject Lazy<SystemWindows> mSystemWindows;
     @Inject Lazy<DisplayImeController> mDisplayImeController;
     @Inject Lazy<RecordingController> mRecordingController;
+    @Inject Lazy<ProtoTracer> mProtoTracer;
 
     @Inject
     public Dependency() {
@@ -519,6 +521,7 @@
         mProviders.put(DisplayWindowController.class, mDisplayWindowController::get);
         mProviders.put(SystemWindows.class, mSystemWindows::get);
         mProviders.put(DisplayImeController.class, mDisplayImeController::get);
+        mProviders.put(ProtoTracer.class, mProtoTracer::get);
 
         // TODO(b/118592525): to support multi-display , we start to add something which is
         //                    per-display, while others may be global. I think it's time to add
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 79a33c9..573ea4d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -29,6 +29,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
 
 import android.annotation.FloatRange;
 import android.app.ActivityTaskManager;
@@ -67,6 +68,7 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NavigationBarController;
 import com.android.systemui.statusbar.phone.NavigationBarFragment;
 import com.android.systemui.statusbar.phone.NavigationBarView;
@@ -485,7 +487,8 @@
 
     @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
     @Inject
-    public OverviewProxyService(Context context, DeviceProvisionedController provisionController,
+    public OverviewProxyService(Context context, CommandQueue commandQueue,
+            DeviceProvisionedController provisionController,
             NavigationBarController navBarController, NavigationModeController navModeController,
             NotificationShadeWindowController statusBarWinController, SysUiState sysUiState,
             PipUI pipUI, Optional<Divider> dividerOptional,
@@ -530,6 +533,15 @@
         // Listen for status bar state changes
         statusBarWinController.registerCallback(mStatusBarWindowCallback);
         mScreenshotHelper = new ScreenshotHelper(context);
+
+        // Listen for tracing state changes
+        commandQueue.addCallback(new CommandQueue.Callbacks() {
+            @Override
+            public void onTracingStateChanged(boolean enabled) {
+                mSysUiState.setFlag(SYSUI_STATE_TRACING_ENABLED, enabled)
+                        .commitUpdate(mContext.getDisplayId());
+            }
+        });
     }
 
     public void notifyBackAction(boolean completed, int downX, int downY, boolean isButton,
@@ -573,14 +585,12 @@
 
     private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
             boolean bouncerShowing) {
-        int displayId = mContext.getDisplayId();
-
         mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
                         keyguardShowing && !keyguardOccluded)
                 .setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
                         keyguardShowing && keyguardOccluded)
                 .setFlag(SYSUI_STATE_BOUNCER_SHOWING, bouncerShowing)
-                .commitUpdate(displayId);
+                .commitUpdate(mContext.getDisplayId());
     }
 
     /**
@@ -601,10 +611,6 @@
         }
     }
 
-    public float getBackButtonAlpha() {
-        return mNavBarButtonAlpha;
-    }
-
     public void cleanupAfterDeath() {
         if (mInputFocusTransferStarted) {
             mHandler.post(()-> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 3af3701..64f0830 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -53,6 +53,7 @@
 import com.android.internal.view.AppearanceRegion;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
 import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.systemui.tracing.ProtoTracer;
 
 import java.util.ArrayList;
 
@@ -123,6 +124,7 @@
     private static final int MSG_DISMISS_INATTENTIVE_SLEEP_WARNING = 52 << MSG_SHIFT;
     private static final int MSG_SHOW_TOAST                        = 53 << MSG_SHIFT;
     private static final int MSG_HIDE_TOAST                        = 54 << MSG_SHIFT;
+    private static final int MSG_TRACING_STATE_CHANGED             = 55 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -143,6 +145,7 @@
      * event.
      */
     private int mLastUpdatedImeDisplayId = INVALID_DISPLAY;
+    private ProtoTracer mProtoTracer;
 
     /**
      * These methods are called back on the main thread.
@@ -325,9 +328,19 @@
          * @see IStatusBar#hideToast(String, IBinder) (String, IBinder)
          */
         default void hideToast(String packageName, IBinder token) { }
+
+        /**
+         * @param enabled
+         */
+        default void onTracingStateChanged(boolean enabled) { }
     }
 
     public CommandQueue(Context context) {
+        this(context, null);
+    }
+
+    public CommandQueue(Context context, ProtoTracer protoTracer) {
+        mProtoTracer = protoTracer;
         context.getSystemService(DisplayManager.class).registerDisplayListener(this, mHandler);
         // We always have default display.
         setDisabled(DEFAULT_DISPLAY, DISABLE_NONE, DISABLE2_NONE);
@@ -917,6 +930,26 @@
         }
     }
 
+    @Override
+    public void startTracing() {
+        synchronized (mLock) {
+            if (mProtoTracer != null) {
+                mProtoTracer.start();
+            }
+            mHandler.obtainMessage(MSG_TRACING_STATE_CHANGED, true).sendToTarget();
+        }
+    }
+
+    @Override
+    public void stopTracing() {
+        synchronized (mLock) {
+            if (mProtoTracer != null) {
+                mProtoTracer.stop();
+            }
+            mHandler.obtainMessage(MSG_TRACING_STATE_CHANGED, false).sendToTarget();
+        }
+    }
+
     private final class H extends Handler {
         private H(Looper l) {
             super(l);
@@ -1244,6 +1277,11 @@
                     }
                     break;
                 }
+                case MSG_TRACING_STATE_CHANGED:
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).onTracingStateChanged((Boolean) msg.obj);
+                    }
+                    break;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
index ec8dbea..493482a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
@@ -19,6 +19,8 @@
 import android.content.Context;
 
 import com.android.systemui.statusbar.notification.row.NotificationRowModule;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.tracing.ProtoTracer;
 
 import javax.inject.Singleton;
 
@@ -35,8 +37,8 @@
      */
     @Provides
     @Singleton
-    public CommandQueue provideCommandQueue(Context context) {
-        return new CommandQueue(context);
+    public CommandQueue provideCommandQueue(Context context, ProtoTracer protoTracer) {
+        return new CommandQueue(context, protoTracer);
     }
 
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 6bd122d..db692c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -54,6 +54,10 @@
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.shared.tracing.ProtoTraceable;
+import com.android.systemui.tracing.ProtoTracer;
+import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
+import com.android.systemui.tracing.nano.SystemUiTraceProto;
 
 import java.io.PrintWriter;
 import java.util.concurrent.Executor;
@@ -62,7 +66,7 @@
  * Utility class to handle edge swipes for back gesture
  */
 public class EdgeBackGestureHandler implements DisplayListener,
-        PluginListener<NavigationEdgeBackPlugin> {
+        PluginListener<NavigationEdgeBackPlugin>, ProtoTraceable<SystemUiTraceProto> {
 
     private static final String TAG = "EdgeBackGestureHandler";
     private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt(
@@ -161,6 +165,7 @@
         mMainExecutor = context.getMainExecutor();
         mOverviewProxyService = overviewProxyService;
         mPluginManager = pluginManager;
+        Dependency.get(ProtoTracer.class).add(this);
 
         // Reduce the default touch slop to ensure that we can intercept the gesture
         // before the app starts to react to it.
@@ -399,6 +404,8 @@
             // forward touch
             mEdgeBackPlugin.onMotionEvent(ev);
         }
+
+        Dependency.get(ProtoTracer.class).update();
     }
 
     @Override
@@ -458,6 +465,14 @@
         pw.println("  mEdgeWidth=" + mEdgeWidth);
     }
 
+    @Override
+    public void writeToProto(SystemUiTraceProto proto) {
+        if (proto.edgeBackGestureHandler == null) {
+            proto.edgeBackGestureHandler = new EdgeBackGestureHandlerProto();
+        }
+        proto.edgeBackGestureHandler.allowGesture = mAllowGesture;
+    }
+
     class SysUiInputEventReceiver extends InputEventReceiver {
         SysUiInputEventReceiver(InputChannel channel, Looper looper) {
             super(channel, looper);
diff --git a/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java b/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java
new file mode 100644
index 0000000..3bef044
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2019 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 com.android.systemui.tracing;
+
+import static com.android.systemui.tracing.nano.SystemUiTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.systemui.tracing.nano.SystemUiTraceFileProto.MAGIC_NUMBER_L;
+
+import android.content.Context;
+import android.os.SystemClock;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.DumpController;
+import com.android.systemui.Dumpable;
+import com.android.systemui.shared.tracing.FrameProtoTracer;
+import com.android.systemui.shared.tracing.FrameProtoTracer.ProtoTraceParams;
+import com.android.systemui.shared.tracing.ProtoTraceable;
+import com.android.systemui.tracing.nano.SystemUiTraceProto;
+import com.android.systemui.tracing.nano.SystemUiTraceEntryProto;
+import com.android.systemui.tracing.nano.SystemUiTraceFileProto;
+
+import com.google.protobuf.nano.MessageNano;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Queue;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Controller for coordinating winscope proto tracing.
+ */
+@Singleton
+public class ProtoTracer implements Dumpable, ProtoTraceParams<MessageNano, SystemUiTraceFileProto,
+        SystemUiTraceEntryProto, SystemUiTraceProto> {
+
+    private static final String TAG = "ProtoTracer";
+    private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+    private final Context mContext;
+    private final FrameProtoTracer<MessageNano, SystemUiTraceFileProto, SystemUiTraceEntryProto,
+            SystemUiTraceProto> mProtoTracer;
+
+    @Inject
+    public ProtoTracer(Context context, DumpController dumpController) {
+        mContext = context;
+        mProtoTracer = new FrameProtoTracer<>(this);
+        dumpController.registerDumpable(this);
+    }
+
+    @Override
+    public File getTraceFile() {
+        return new File(mContext.getFilesDir(), "sysui_trace.pb");
+    }
+
+    @Override
+    public SystemUiTraceFileProto getEncapsulatingTraceProto() {
+        return new SystemUiTraceFileProto();
+    }
+
+    @Override
+    public SystemUiTraceEntryProto updateBufferProto(SystemUiTraceEntryProto reuseObj,
+            ArrayList<ProtoTraceable<SystemUiTraceProto>> traceables) {
+        SystemUiTraceEntryProto proto = reuseObj != null
+                ? reuseObj
+                : new SystemUiTraceEntryProto();
+        proto.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
+        proto.systemUi = proto.systemUi != null ? proto.systemUi : new SystemUiTraceProto();
+        for (ProtoTraceable t : traceables) {
+            t.writeToProto(proto.systemUi);
+        }
+        return proto;
+    }
+
+    @Override
+    public byte[] serializeEncapsulatingProto(SystemUiTraceFileProto encapsulatingProto,
+            Queue<SystemUiTraceEntryProto> buffer) {
+        encapsulatingProto.magicNumber = MAGIC_NUMBER_VALUE;
+        encapsulatingProto.entry = buffer.toArray(new SystemUiTraceEntryProto[0]);
+        return MessageNano.toByteArray(encapsulatingProto);
+    }
+
+    @Override
+    public byte[] getProtoBytes(MessageNano proto) {
+        return MessageNano.toByteArray(proto);
+    }
+
+    @Override
+    public int getProtoSize(MessageNano proto) {
+        return proto.getCachedSize();
+    }
+
+    public void start() {
+        mProtoTracer.start();
+    }
+
+    public void stop() {
+        mProtoTracer.stop();
+    }
+
+    public boolean isEnabled() {
+        return mProtoTracer.isEnabled();
+    }
+
+    public void add(ProtoTraceable<SystemUiTraceProto> traceable) {
+        mProtoTracer.add(traceable);
+    }
+
+    public void remove(ProtoTraceable<SystemUiTraceProto> traceable) {
+        mProtoTracer.remove(traceable);
+    }
+
+    public void scheduleFrameUpdate() {
+        mProtoTracer.scheduleFrameUpdate();
+    }
+
+    public void update() {
+        mProtoTracer.update();
+    }
+
+    @Override
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        pw.println("ProtoTracer:");
+        pw.print("    "); pw.println("enabled: " + mProtoTracer.isEnabled());
+        pw.print("    "); pw.println("usagePct: " + mProtoTracer.getBufferUsagePct());
+        pw.print("    "); pw.println("file: " + getTraceFile());
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto
new file mode 100644
index 0000000..08ae99c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+syntax = "proto2";
+
+package com.android.systemui.tracing;
+
+option java_multiple_files = true;
+
+message SystemUiTraceProto {
+
+    optional EdgeBackGestureHandlerProto edge_back_gesture_handler = 1;
+}
+
+message EdgeBackGestureHandlerProto {
+
+    optional bool allow_gesture = 1;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace_file.proto b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace_file.proto
new file mode 100644
index 0000000..d1523ef
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace_file.proto
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+syntax = "proto2";
+
+import "frameworks/base/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto";
+
+package com.android.systemui.tracing;
+
+option java_multiple_files = true;
+
+/* represents a file full of system ui trace entries.
+   Encoded, it should start with 0x9 0x53 0x59 0x53 0x55 0x49 0x54 0x52 0x43 (.SYSUITRC), such
+   that they can be easily identified. */
+message SystemUiTraceFileProto {
+
+    /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
+       (this is needed because enums have to be 32 bits and there's no nice way to put 64bit
+        constants into .proto files. */
+    enum MagicNumber {
+        INVALID = 0;
+        MAGIC_NUMBER_L = 0x55535953;  /* SYSU (little-endian ASCII) */
+        MAGIC_NUMBER_H = 0x43525449;  /* ITRC (little-endian ASCII) */
+    }
+
+    optional fixed64 magic_number = 1;  /* Must be the first field, set to value in MagicNumber */
+    repeated SystemUiTraceEntryProto entry = 2;
+}
+
+/* one system ui trace entry. */
+message SystemUiTraceEntryProto {
+    /* required: elapsed realtime in nanos since boot of when this entry was logged */
+    optional fixed64 elapsed_realtime_nanos = 1;
+
+    optional SystemUiTraceProto system_ui = 3;
+}