Fix internal tests from flickerlib

The flickerlib currently crashes on its internal tests. It fails due to:
- Invalid use of Mockito's @InjectMocks
- Orphaned layers with parent Id 0 or -1
- Null pointer exception on screen recording.

Test: atest FlickerLibTest
Change-Id: I934219ea749c7d0d24ab5f738de74cb242696178
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/LayersTrace.java b/libraries/flicker/src/com/android/server/wm/flicker/LayersTrace.java
index 74faca7..38b9004 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/LayersTrace.java
+++ b/libraries/flicker/src/com/android/server/wm/flicker/LayersTrace.java
@@ -126,6 +126,33 @@
             this.mRootLayers = rootLayers;
         }
 
+        /**
+         * Determines the id of the root element.
+         *
+         * <p>On some files, such as the ones used in the FlickerLib testdata, the root nodes are
+         * those that have parent=0, on newer traces, the root nodes are those that have parent=-1
+         *
+         * <p>This function keeps compatibility with both new and older traces by searching for a
+         * known root layer (Display Root) and considering its parent Id as overall root.
+         */
+        private static Layer getRootLayer(SparseArray<Layer> layerMap) {
+            Layer knownRoot = null;
+            int numKeys = layerMap.size();
+            for (int i = 0; i < numKeys; ++i) {
+                Layer currentLayer = layerMap.valueAt(i);
+                if (currentLayer.getName().contains("Display Root")) {
+                    knownRoot = currentLayer;
+                    break;
+                }
+            }
+
+            if (knownRoot == null) {
+                throw new IllegalStateException("Display root layer not found.");
+            }
+
+            return layerMap.get(knownRoot.getParentId());
+        }
+
         /** Constructs the layer hierarchy from a flattened list of layers. */
         public static Entry fromFlattenedLayers(long timestamp, LayerProto[] protos,
                 Consumer<Layer> orphanLayerCallback) {
@@ -156,13 +183,14 @@
                 newLayer.addParent(layerMap.get(parentId));
             }
 
-            // Remove root node (id = 0)
-            orphans.remove(layerMap.get(-1));
+            // Remove root node
+            Layer rootLayer = getRootLayer(layerMap);
+            orphans.remove(rootLayer);
             // Fail if we find orphan layers.
             orphans.forEach(
                     orphan -> {
                         if (orphanLayerCallback != null) {
-                            // Workaround for b/141326137, ignore the existance of an orphan layer
+                            // Workaround for b/141326137, ignore the existence of an orphan layer
                             orphanLayerCallback.accept(orphan);
                             return;
                         }
@@ -171,7 +199,7 @@
                                         .stream()
                                         .map(node -> Integer.toString(node.getId()))
                                         .collect(Collectors.joining(", "));
-                        int orphanId = orphan.mChildren.get(0).mProto.parent;
+                        int orphanId = orphan.mChildren.get(0).getParentId();
                         throw new RuntimeException(
                                 "Failed to parse layers trace. Found orphan layers with parent "
                                         + "layer id:"
@@ -180,7 +208,7 @@
                                         + childNodes);
                     });
 
-            return new Entry(timestamp, layerMap.get(-1).mChildren);
+            return new Entry(timestamp, rootLayer.mChildren);
         }
 
         /** Extracts {@link Rect} from {@link RectProto}. */
@@ -363,6 +391,18 @@
             return mProto.id;
         }
 
+        public int getParentId() {
+            return mProto.parent;
+        }
+
+        public String getName() {
+            if (mProto != null) {
+                return mProto.name;
+            }
+
+            return "";
+        }
+
         public boolean isActiveBufferEmpty() {
             return this.mProto.activeBuffer == null
                     || this.mProto.activeBuffer.height == 0
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java b/libraries/flicker/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
index 8b138aa..feedee7 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
+++ b/libraries/flicker/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
@@ -22,8 +22,6 @@
 
 import android.util.Log;
 
-import androidx.annotation.VisibleForTesting;
-
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -31,38 +29,36 @@
 
 /** Captures screen contents and saves it as a mp4 video file. */
 public class ScreenRecorder implements ITransitionMonitor {
-    @VisibleForTesting
-    public static final Path DEFAULT_OUTPUT_PATH = OUTPUT_DIR.resolve("transition.mp4");
-
     private static final String TAG = "FLICKER";
     private int mWidth;
     private int mHeight;
+    private Path mOutputPath;
     private Thread mRecorderThread;
 
     public ScreenRecorder() {
-        this(720, 1280);
+        this(720, 1280, OUTPUT_DIR.resolve("transition.mp4"));
     }
 
-    public ScreenRecorder(int width, int height) {
+    public ScreenRecorder(int width, int height, Path outputPath) {
         mWidth = width;
         mHeight = height;
+        mOutputPath = outputPath;
     }
 
-    @VisibleForTesting
-    public static Path getPath(String testTag) {
-        return OUTPUT_DIR.resolve(testTag + ".mp4");
+    public Path getPath() {
+        return mOutputPath;
     }
 
     @Override
     public void start() {
-        OUTPUT_DIR.toFile().mkdirs();
+        mOutputPath.getParent().toFile().mkdirs();
         String command =
                 String.format(
                         Locale.getDefault(),
                         "screenrecord --size %dx%d %s",
                         mWidth,
                         mHeight,
-                        DEFAULT_OUTPUT_PATH);
+                        mOutputPath);
         mRecorderThread =
                 new Thread(
                         () -> {
@@ -87,13 +83,14 @@
 
     @Override
     public Path save(String testTag) {
-        if (!Files.exists(DEFAULT_OUTPUT_PATH)) {
-            Log.w(TAG, "No video file found on " + DEFAULT_OUTPUT_PATH);
+        if (!Files.exists(mOutputPath)) {
+            Log.w(TAG, "No video file found on " + mOutputPath);
             return null;
         }
 
         try {
-            Path targetPath = Files.move(DEFAULT_OUTPUT_PATH, getPath(testTag), REPLACE_EXISTING);
+            Path targetPath =
+                    Files.move(mOutputPath, OUTPUT_DIR.resolve(testTag + ".mp4"), REPLACE_EXISTING);
             Log.i(TAG, "Video saved to " + targetPath.toString());
             return targetPath;
         } catch (IOException e) {
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java b/libraries/flicker/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java
index d75c58a..92c1d8c 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java
@@ -58,7 +58,7 @@
     @Mock private WindowManagerTraceMonitor mWindowManagerTraceMonitorMock;
     @Mock private LayersTraceMonitor mLayersTraceMonitorMock;
     @Mock private WindowAnimationFrameStatsMonitor mWindowAnimationFrameStatsMonitor;
-    @InjectMocks private TransitionBuilder mTransitionBuilder;
+    @InjectMocks private TransitionBuilder mTransitionBuilder = TransitionRunner.newBuilder();
 
     @Before
     public void init() {
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java
index 967f565..01aa3c9 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java
@@ -18,9 +18,6 @@
 
 import static android.os.SystemClock.sleep;
 
-import static com.android.server.wm.flicker.monitor.ScreenRecorder.DEFAULT_OUTPUT_PATH;
-import static com.android.server.wm.flicker.monitor.ScreenRecorder.getPath;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -33,6 +30,7 @@
 import org.junit.runners.MethodSorters;
 
 import java.io.File;
+import java.nio.file.Path;
 
 /**
  * Contains {@link ScreenRecorder} tests. To run this test: {@code atest
@@ -43,6 +41,7 @@
 public class ScreenRecorderTest {
     private static final String TEST_VIDEO_FILENAME = "test.mp4";
     private ScreenRecorder mScreenRecorder;
+    private Path mSavedVideoPath = null;
 
     @Before
     public void setup() {
@@ -51,8 +50,10 @@
 
     @After
     public void teardown() {
-        DEFAULT_OUTPUT_PATH.toFile().delete();
-        getPath(TEST_VIDEO_FILENAME).toFile().delete();
+        mScreenRecorder.getPath().toFile().delete();
+        if (mSavedVideoPath != null) {
+            mSavedVideoPath.toFile().delete();
+        }
     }
 
     @Test
@@ -60,7 +61,7 @@
         mScreenRecorder.start();
         sleep(100);
         mScreenRecorder.stop();
-        File file = DEFAULT_OUTPUT_PATH.toFile();
+        File file = mScreenRecorder.getPath().toFile();
         assertThat(file.exists()).isTrue();
     }
 
@@ -69,8 +70,7 @@
         mScreenRecorder.start();
         sleep(100);
         mScreenRecorder.stop();
-        mScreenRecorder.save(TEST_VIDEO_FILENAME);
-        File file = getPath(TEST_VIDEO_FILENAME).toFile();
+        File file = mScreenRecorder.save(TEST_VIDEO_FILENAME).toFile();
         assertThat(file.exists()).isTrue();
     }
 }