Show noop transactions in viewer

Fixes: 286517142
Test: npm run test:all
Change-Id: I8e33f72f0ae35c232f3efc251c5371f8951e9482
diff --git a/tools/winscope/src/viewers/viewer_transactions/presenter.ts b/tools/winscope/src/viewers/viewer_transactions/presenter.ts
index 7c0ef39..d4a0910 100644
--- a/tools/winscope/src/viewers/viewer_transactions/presenter.ts
+++ b/tools/winscope/src/viewers/viewer_transactions/presenter.ts
@@ -283,6 +283,26 @@
             )
           );
         }
+
+        if (
+          transactionStateProto.layerChanges.length === 0 &&
+          transactionStateProto.displayChanges.length === 0
+        ) {
+          entries.push(
+            new UiDataEntry(
+              originalIndex,
+              TimeUtils.format(entry.getTimestamp()),
+              Number(entryProto.vsyncId),
+              transactionStateProto.pid.toString(),
+              transactionStateProto.uid.toString(),
+              UiDataEntryType.NO_OP,
+              '',
+              transactionStateProto.transactionId.toString(),
+              '',
+              {}
+            )
+          );
+        }
       }
 
       for (const layerCreationArgsProto of entryProto.addedLayers) {
diff --git a/tools/winscope/src/viewers/viewer_transactions/presenter_test.ts b/tools/winscope/src/viewers/viewer_transactions/presenter_test.ts
index 1cd964c..7ea260e 100644
--- a/tools/winscope/src/viewers/viewer_transactions/presenter_test.ts
+++ b/tools/winscope/src/viewers/viewer_transactions/presenter_test.ts
@@ -32,7 +32,7 @@
   let traces: Traces;
   let presenter: Presenter;
   let outputUiData: undefined | UiData;
-  const TOTAL_OUTPUT_ENTRIES = 1504;
+  const TOTAL_OUTPUT_ENTRIES = 1647;
 
   beforeAll(async () => {
     parser = await UnitTestUtils.getParser('traces/elapsed_and_real_timestamp/Transactions.pb');
@@ -75,10 +75,11 @@
       'LAYER_CHANGED',
       'LAYER_DESTROYED',
       'LAYER_HANDLE_DESTROYED',
+      'NO_OP',
     ]);
 
-    expect(outputUiData?.allTransactionIds.length).toEqual(1152);
-    expect(outputUiData?.allLayerAndDisplayIds.length).toEqual(116);
+    expect(outputUiData?.allTransactionIds.length).toEqual(1295);
+    expect(outputUiData?.allLayerAndDisplayIds.length).toEqual(117);
 
     expect(outputUiData?.entries.length).toEqual(TOTAL_OUTPUT_ENTRIES);
 
@@ -158,6 +159,7 @@
         UiDataEntryType.LAYER_CHANGED,
         UiDataEntryType.LAYER_DESTROYED,
         UiDataEntryType.LAYER_HANDLE_DESTROYED,
+        UiDataEntryType.NO_OP,
       ])
     );
 
@@ -189,6 +191,19 @@
     );
   });
 
+  it('includes no op transitions', () => {
+    presenter.onTypeFilterChanged([UiDataEntryType.NO_OP]);
+    expect(new Set(outputUiData!.entries.map((entry) => entry.type))).toEqual(
+      new Set([UiDataEntryType.NO_OP])
+    );
+
+    for (const entry of outputUiData!.entries) {
+      expect(entry.layerOrDisplayId).toEqual('');
+      expect(entry.what).toEqual('');
+      expect(entry.propertiesTree).toEqual({});
+    }
+  });
+
   it('filters entries according to "what" search string', () => {
     expect(outputUiData!.entries.length).toEqual(TOTAL_OUTPUT_ENTRIES);
 
diff --git a/tools/winscope/src/viewers/viewer_transactions/ui_data.ts b/tools/winscope/src/viewers/viewer_transactions/ui_data.ts
index 36f2f3e..f61d86c 100644
--- a/tools/winscope/src/viewers/viewer_transactions/ui_data.ts
+++ b/tools/winscope/src/viewers/viewer_transactions/ui_data.ts
@@ -56,6 +56,7 @@
   static LAYER_DESTROYED = 'LAYER_DESTROYED';
   static LAYER_CHANGED = 'LAYER_CHANGED';
   static LAYER_HANDLE_DESTROYED = 'LAYER_HANDLE_DESTROYED';
+  static NO_OP = 'NO_OP';
 }
 
 export {UiData, UiDataEntry, UiDataEntryType};