Merge "Track MemFree from cat proc/meminfo"
diff --git a/build/tasks/tests/instrumentation_test_list.mk b/build/tasks/tests/instrumentation_test_list.mk
index f6761d9..5ef9952 100644
--- a/build/tasks/tests/instrumentation_test_list.mk
+++ b/build/tasks/tests/instrumentation_test_list.mk
@@ -70,9 +70,6 @@
     FrameworksPrivacyLibraryTests \
     SettingsUITests \
     ExtServicesUnitTests\
-    NexusLauncherOutOfProcTests\
-    NexusLauncherDebug\
-    NexusLauncherTests\
     FrameworksNetSmokeTests\
 
 
diff --git a/build/tasks/tests/native_test_list.mk b/build/tasks/tests/native_test_list.mk
index 9e7ffcd..0d7693a 100644
--- a/build/tasks/tests/native_test_list.mk
+++ b/build/tasks/tests/native_test_list.mk
@@ -111,6 +111,7 @@
     prioritydumper_test \
     puffin_unittest \
     recovery_unit_test \
+    resolv_gold_test \
     resolv_integration_test \
     resolv_unit_test \
     scrape_mmap_addr \
@@ -129,7 +130,6 @@
     NeuralNetworksTest_mt_static \
     NeuralNetworksTest_operations \
     NeuralNetworksTest_static \
-    NeuralNetworksTest_static_asan \
     SurfaceFlinger_test \
     lmkd_unit_test \
     vrflinger_test
diff --git a/build/tasks/tests/platform_test_list.mk b/build/tasks/tests/platform_test_list.mk
index ba96337..b6a4a16 100644
--- a/build/tasks/tests/platform_test_list.mk
+++ b/build/tasks/tests/platform_test_list.mk
@@ -59,6 +59,7 @@
     InternalLocTestApp \
     JankMicroBenchmarkTests \
     libbluetooth_gd \
+    long_trace_config.textproto \
     libgrpc++_unsecure \
     MemoryUsage \
     MultiDexLegacyTestApp \
@@ -79,14 +80,13 @@
     NotificationFunctionalTests \
     NotificationStressTests \
     OverviewFunctionalTests \
+    perfetto_trace_processor_shell \
     PerformanceAppTest \
     PerformanceLaunch \
     PermissionFunctionalTests \
     PermissionTestAppMV1 \
     PermissionUtils \
-    PlatformScenarioTests \
     PowerPerfTest \
-    root-canal \
     SettingsUITests \
     SimpleTestApp \
     skia_dm \
@@ -96,6 +96,7 @@
     SmokeTestApp \
     SysAppJankTestsWear \
     TouchLatencyJankTestWear \
+    trace_config_detailed.textproto \
     UbSystemUiJankTests \
     UbWebViewJankTests \
     UiBench \
@@ -117,8 +118,12 @@
 platform_tests += perf-setup.sh
 endif
 
-ifneq ($(filter vsoc_x86 vsoc_x86_64, $(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter vsoc_arm vsoc_arm64 vsoc_x86 vsoc_x86_64, $(TARGET_BOARD_PLATFORM)),)
   platform_tests += \
     CuttlefishRilTests \
     CuttlefishWifiTests
 endif
+
+ifeq ($(HOST_OS),linux)
+platform_tests += root-canal
+endif
diff --git a/libraries/app-helpers/interfaces/common/src/android/platform/helpers/IContactsHelper.java b/libraries/app-helpers/interfaces/common/src/android/platform/helpers/IContactsHelper.java
index 0c07aa6..7ef499c 100644
--- a/libraries/app-helpers/interfaces/common/src/android/platform/helpers/IContactsHelper.java
+++ b/libraries/app-helpers/interfaces/common/src/android/platform/helpers/IContactsHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -38,6 +38,19 @@
     }
 
     /**
+     * Setup expectation: Contacts is open
+     *
+     * <p>Clicks search field and inputs contact to search. Provide alternative ways to input
+     * contact.
+     *
+     * @param contact The contact to search.
+     * @param useKeyboard Use KeyEvent to input contact if true, use UiObject2 setText otherwise.
+     */
+    public default void searchForContact(String contact, boolean useKeyboard) {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
      * Setup expectations: The search results of contact showed.
      * <p>
      * Selects the contact by the specific index.
diff --git a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IPhotosHelper.java b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IPhotosHelper.java
index 85b243d..15684fd 100644
--- a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IPhotosHelper.java
+++ b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IPhotosHelper.java
@@ -112,22 +112,22 @@
     public void openPicture(int index);
 
     /**
-     * Setup expectations: Photos is open and a picture album is open.
+     * Setup expectations: Photos is open and a picture is open.
      *
-     * This method will scroll the picture album in the specified direction.
+     * <p>This method will scroll to next or previous picture in the specified direction.
      *
      * @param direction The direction to scroll, must be LEFT or RIGHT.
-     * @return Returns whether album can be still scrolled in the given direction
+     * @return Returns whether picture can be still scrolled in the given direction
      */
-    public boolean scrollAlbum(Direction direction);
+    public boolean scrollPicture(Direction direction);
 
     /**
-     * Setup expectations: Photos is open and a picture folder is open.
+     * Setup expectations: Photos is open and a page contains pictures or albums is open.
      *
-     * This method will scroll the Photos grid view in the specified direction.
+     * <p>This method will scroll the page in the specified direction.
      *
      * @param direction The direction of the scroll, must be UP or DOWN.
      * @return Returns whether the object can still scroll in the given direction
      */
-    public boolean scrollGridView(Direction direction);
+    public boolean scrollPage(Direction direction);
 }
diff --git a/libraries/collectors-helper/jank/src/com/android/helpers/SfStatsCollectionHelper.java b/libraries/collectors-helper/jank/src/com/android/helpers/SfStatsCollectionHelper.java
index aaa5d2d..b2c0948 100644
--- a/libraries/collectors-helper/jank/src/com/android/helpers/SfStatsCollectionHelper.java
+++ b/libraries/collectors-helper/jank/src/com/android/helpers/SfStatsCollectionHelper.java
@@ -37,7 +37,8 @@
 
     private static final String LOG_TAG = SfStatsCollectionHelper.class.getSimpleName();
 
-    private static final Pattern KEY_VALUE_PATTERN = Pattern.compile("^(\\w+)\\s+=\\s+(\\S+)");
+    private static final Pattern KEY_VALUE_PATTERN =
+            Pattern.compile("^(\\w+)\\s+=\\s+(\\d+\\.?\\d*|.*).*");
     private static final Pattern HISTOGRAM_PATTERN =
             Pattern.compile("([^\\n]+)\\n((\\d+ms=\\d+\\s+)+)");
 
diff --git a/libraries/collectors-helper/jank/test/src/com/android/helpers/SfStatsCollectionHelperTest.java b/libraries/collectors-helper/jank/test/src/com/android/helpers/SfStatsCollectionHelperTest.java
index 3d851ea..c986a67 100644
--- a/libraries/collectors-helper/jank/test/src/com/android/helpers/SfStatsCollectionHelperTest.java
+++ b/libraries/collectors-helper/jank/test/src/com/android/helpers/SfStatsCollectionHelperTest.java
@@ -85,7 +85,25 @@
                     + "post2present histogram is as below:\n"
                     + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=264 9ms=0\n"
                     + "post2acquire histogram is as below:\n"
-                    + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=264 9ms=0";
+                    + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=264 9ms=0\n"
+                    + "\n"
+                    + "layerName = SurfaceView - com.mxtech.videoplayer.ad/com.mxtech.videoplayer.ad.ActivityScreen#0\n"
+                    + "packageName = \n"
+                    + "totalFrames = 2352\n"
+                    + "droppedFrames = 0\n"
+                    + "averageFPS = 59.999\n"
+                    + "present2present histogram is as below:\n"
+                    + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=2352 9ms=0\n"
+                    + "latch2present histogram is as below:\n"
+                    + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=2352 9ms=0\n"
+                    + "desired2present histogram is as below:\n"
+                    + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=2352 9ms=0\n"
+                    + "acquire2present histogram is as below:\n"
+                    + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=2352 9ms=0\n"
+                    + "post2present histogram is as below:\n"
+                    + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=2352 9ms=0\n"
+                    + "post2acquire histogram is as below:\n"
+                    + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=2352 9ms=0";
 
     private static final String LOG_TAG = SfStatsCollectionHelperTest.class.getSimpleName();
 
@@ -140,6 +158,27 @@
                                 constructKey(
                                         SFSTATS_METRICS_PREFIX,
                                         "GLOBAL",
+                                        "clientCompositionFrames".toUpperCase())))
+                .isEqualTo(Double.valueOf(0));
+        assertThat(
+                        metrics.get(
+                                constructKey(
+                                        SFSTATS_METRICS_PREFIX,
+                                        "GLOBAL",
+                                        "displayOnTime".toUpperCase())))
+                .isEqualTo(Double.valueOf(2485421));
+        assertThat(
+                        metrics.get(
+                                constructKey(
+                                        SFSTATS_METRICS_PREFIX,
+                                        "GLOBAL",
+                                        "totalP2PTime".toUpperCase())))
+                .isEqualTo(Double.valueOf(2674034));
+        assertThat(
+                        metrics.get(
+                                constructKey(
+                                        SFSTATS_METRICS_PREFIX,
+                                        "GLOBAL",
                                         "FRAME_CPU_DURATION_AVG")))
                 .isEqualTo(Double.valueOf(5.5));
         assertThat(
@@ -191,6 +230,27 @@
                                         "com.google.android.nexuslauncher.NexusLauncherActivity#0",
                                         "AVERAGE_FPS")))
                 .isEqualTo(84.318);
+        assertThat(
+                        metrics.get(
+                                constructKey(
+                                        SFSTATS_METRICS_PREFIX,
+                                        "SurfaceView - com.mxtech.videoplayer.ad/com.mxtech.videoplayer.ad.ActivityScreen#0",
+                                        "TOTAL_FRAMES")))
+                .isEqualTo(Double.valueOf(2352));
+        assertThat(
+                        metrics.get(
+                                constructKey(
+                                        SFSTATS_METRICS_PREFIX,
+                                        "SurfaceView - com.mxtech.videoplayer.ad/com.mxtech.videoplayer.ad.ActivityScreen#0",
+                                        "DROPPED_FRAMES")))
+                .isEqualTo(Double.valueOf(0));
+        assertThat(
+                        metrics.get(
+                                constructKey(
+                                        SFSTATS_METRICS_PREFIX,
+                                        "SurfaceView - com.mxtech.videoplayer.ad/com.mxtech.videoplayer.ad.ActivityScreen#0",
+                                        "AVERAGE_FPS")))
+                .isEqualTo(59.999);
         mHelper.stopCollecting();
     }
 
diff --git a/libraries/collectors-helper/memory/src/com/android/helpers/FreeMemHelper.java b/libraries/collectors-helper/memory/src/com/android/helpers/FreeMemHelper.java
index 8217182..b2f1f8c 100644
--- a/libraries/collectors-helper/memory/src/com/android/helpers/FreeMemHelper.java
+++ b/libraries/collectors-helper/memory/src/com/android/helpers/FreeMemHelper.java
@@ -117,7 +117,7 @@
         for (String process : cachedProcList) {
             Log.i(TAG, "Cached Process" + process);
             Matcher match;
-            if (((match = matches(PID_PATTERN, process))) != null) {
+            if ((match = matches(PID_PATTERN, process)) != null) {
                 String processId = match.group(PROCESS_ID);
                 String processDumpSysMemInfo = String.format(DUMPSYS_PROCESS, processId);
                 String processInfoStr;
@@ -183,7 +183,7 @@
                 Log.i(TAG, currLine);
                 Matcher match;
                 if (!isCacheProcSection
-                        && ((match = matches(CACHE_PROC_START_PATTERN, currLine))) == null) {
+                        && (match = matches(CACHE_PROC_START_PATTERN, currLine)) == null) {
                     // Continue untill the start of cache proc section.
                     continue;
                 } else {
diff --git a/libraries/device-collectors/src/test/java/android/device/preparers/GarbageCollectionPreparerTest.java b/libraries/device-collectors/src/test/java/android/device/preparers/GarbageCollectionPreparerTest.java
index 81bad3a..2449264 100644
--- a/libraries/device-collectors/src/test/java/android/device/preparers/GarbageCollectionPreparerTest.java
+++ b/libraries/device-collectors/src/test/java/android/device/preparers/GarbageCollectionPreparerTest.java
@@ -20,13 +20,11 @@
 import static android.device.preparers.GarbageCollectionPreparer.PROCESS_NAMES_KEY;
 import static android.device.preparers.GarbageCollectionPreparer.PROCESS_SEPARATOR;
 
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.app.Instrumentation;
-import android.device.collectors.DataRecord;
 import android.os.Bundle;
 
 import com.android.helpers.GarbageCollectionHelper;
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/Assertions.java b/libraries/flicker/src/com/android/server/wm/flicker/Assertions.java
index ff2de41..82b92bd 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/Assertions.java
+++ b/libraries/flicker/src/com/android/server/wm/flicker/Assertions.java
@@ -114,9 +114,13 @@
         private String prettyTimestamp(long timestamp_ns) {
             StringBuilder prettyTimestamp = new StringBuilder();
             TimeUnit[] timeUnits = {
-                TimeUnit.HOURS, TimeUnit.MINUTES, TimeUnit.SECONDS, TimeUnit.MILLISECONDS
+                TimeUnit.DAYS,
+                TimeUnit.HOURS,
+                TimeUnit.MINUTES,
+                TimeUnit.SECONDS,
+                TimeUnit.MILLISECONDS
             };
-            String[] unitSuffixes = {"h", "m", "s", "ms"};
+            String[] unitSuffixes = {"d", "h", "m", "s", "ms"};
 
             for (int i = 0; i < timeUnits.length; i++) {
                 long convertedTime = timeUnits[i].convert(timestamp_ns, TimeUnit.NANOSECONDS);
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..b273e6b 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.isRootLayer()) {
+                    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
@@ -394,7 +434,7 @@
         }
 
         public boolean isRootLayer() {
-            return mParent == null || mParent.mProto == null;
+            return mParent != null && mParent.mProto == null;
         }
 
         public boolean isInvisible() {
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/assets/testdata/layers_trace_root.pb b/libraries/flicker/test/assets/testdata/layers_trace_root.pb
new file mode 100644
index 0000000..d961714
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/layers_trace_root.pb
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/layers_trace_root_aosp.pb b/libraries/flicker/test/assets/testdata/layers_trace_root_aosp.pb
new file mode 100644
index 0000000..666b328
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/layers_trace_root_aosp.pb
Binary files differ
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java b/libraries/flicker/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java
index 0415f81..2ccae42 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java
@@ -33,6 +33,8 @@
 import org.junit.runners.MethodSorters;
 
 import java.nio.file.Paths;
+import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * Contains {@link LayersTraceSubject} tests. To run this test: {@code atest
@@ -89,7 +91,9 @@
             assertWithMessage("Contains path to trace")
                     .that(e.getMessage())
                     .contains("layers_trace_invalid_layer_visibility.pb");
-            assertWithMessage("Contains timestamp").that(e.getMessage()).contains("70h13m14s303ms");
+            assertWithMessage("Contains timestamp")
+                    .that(e.getMessage())
+                    .contains("2d22h13m14s303ms");
             assertWithMessage("Contains assertion function")
                     .that(e.getMessage())
                     .contains("!isVisible");
@@ -100,4 +104,37 @@
                                     + ".SimpleActivity#0 is visible");
         }
     }
+
+    private void detectRootLayer(String fileName) {
+        LayersTrace layersTrace = readLayerTraceFromFile(fileName);
+
+        for (LayersTrace.Entry entry : layersTrace.getEntries()) {
+            List<LayersTrace.Layer> flattened = entry.asFlattenedLayers();
+            List<LayersTrace.Layer> rootLayers =
+                    flattened
+                            .stream()
+                            .filter(LayersTrace.Layer::isRootLayer)
+                            .collect(Collectors.toList());
+
+            assertWithMessage("Does not have any root layer")
+                    .that(rootLayers.size())
+                    .isGreaterThan(0);
+
+            int firstParentId = rootLayers.get(0).getParentId();
+
+            assertWithMessage("Has multiple root layers")
+                    .that(rootLayers.stream().allMatch(p -> p.getParentId() == firstParentId))
+                    .isTrue();
+        }
+    }
+
+    @Test
+    public void testCanDetectRootLayer() {
+        detectRootLayer("layers_trace_root.pb");
+    }
+
+    @Test
+    public void testCanDetectRootLayerAOSP() {
+        detectRootLayer("layers_trace_root_aosp.pb");
+    }
 }
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();
     }
 }
diff --git a/libraries/health/rules/tests/src/android/platform/test/rule/TracePointRuleTest.java b/libraries/health/rules/tests/src/android/platform/test/rule/TracePointRuleTest.java
index cdae78a..c3f8f14 100644
--- a/libraries/health/rules/tests/src/android/platform/test/rule/TracePointRuleTest.java
+++ b/libraries/health/rules/tests/src/android/platform/test/rule/TracePointRuleTest.java
@@ -16,13 +16,11 @@
 package android.platform.test.rule;
 
 import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
 
 import org.junit.Test;
 import org.junit.runner.Description;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
-import org.junit.runners.model.InitializationError;
 import org.junit.runners.model.Statement;
 
 import java.util.ArrayList;
diff --git a/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/ScheduledScenarioRunner.java b/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/ScheduledScenarioRunner.java
index 01e5ef6..794b576 100644
--- a/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/ScheduledScenarioRunner.java
+++ b/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/ScheduledScenarioRunner.java
@@ -18,14 +18,23 @@
 
 import static java.lang.Math.max;
 
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.Bundle;
-import android.os.SystemClock;
+import android.os.Process;
 import android.platform.test.longevity.proto.Configuration.Scenario;
 import android.platform.test.longevity.proto.Configuration.Scenario.ExtraArg;
+import android.util.Log;
 import androidx.annotation.VisibleForTesting;
 import androidx.test.InstrumentationRegistry;
 
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 import org.junit.rules.TestRule;
 import org.junit.rules.Timeout;
@@ -48,6 +57,8 @@
     // rule and also outside of it.
     @VisibleForTesting static final long TEARDOWN_LEEWAY_MS = 3000;
 
+    private static final String LOG_TAG = ScheduledScenarioRunner.class.getSimpleName();
+
     private final Scenario mScenario;
     private final long mTotalTimeoutMs;
     // Timeout after the teardown leeway is taken into account.
@@ -162,20 +173,83 @@
 
     @VisibleForTesting
     protected void performIdleBeforeTeardown(long durationMs) {
-        idleWithSystemClockSleep(durationMs);
+        suspensionAwareSleep(durationMs);
     }
 
     @VisibleForTesting
     protected void performIdleBeforeNextScenario(long durationMs) {
         // TODO (b/119386011): Change this idle method to using a sleep test; for now, using the
         // same idling logic as {@link performIdleBeforeTeardown}.
-        idleWithSystemClockSleep(durationMs);
+        suspensionAwareSleep(durationMs);
     }
 
-    private void idleWithSystemClockSleep(long durationMs) {
-        if (durationMs <= 0) {
-            return;
+    /**
+     * Idle with a sleep that will be accurate despite the device entering power-saving modes (e.g.
+     * suspend, Doze).
+     */
+    @VisibleForTesting
+    static void suspensionAwareSleep(long durationMs) {
+        // Call the testable version of this method with arguments for the intended sleep behavior.
+        suspensionAwareSleep(durationMs, durationMs);
+    }
+
+    /**
+     * A testable version of suspension-aware sleep.
+     *
+     * <p>This method sets up a {@link CountDownLatch} that waits for a wake-up event, which is
+     * triggered by an {@link AlarmManager} alarm set to fire after the sleep duration. When the
+     * device enters suspend mode, the {@link CountDownLatch} await no longer works as intended and
+     * in effect waits for much longer than expected, in which case the alarm fires and ends the
+     * sleep behavior, ensuring that the device still sleeps for the expected amount of time. If the
+     * device does not enter suspend mode, this method only waits for the {@link CountDownLatch} and
+     * functions similarly to {@code Thread.sleep()}.
+     *
+     * <p>This testable method enables tests to set a longer await timeout on the {@link
+     * CountDownLatch}, enabling that the alarm fires before the {@code CountDownLatch.await()}
+     * timeout is reached, thus simulating the case where the device goes into suspend mode.
+     */
+    @VisibleForTesting
+    static void suspensionAwareSleep(long durationMs, long countDownLatchTimeoutMs) {
+        Log.i(LOG_TAG, String.format("Starting suspension-aware sleep for %d ms", durationMs));
+
+        Context context = InstrumentationRegistry.getContext();
+        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+
+        String wakeUpAction =
+                String.format(
+                        "%s.%d.%d.ScheduledScenarioRunnerSleepWakeUp"
+                                .format(
+                                        context.getPackageName(),
+                                        Process.myPid(),
+                                        Thread.currentThread().getId()));
+
+        final CountDownLatch countDownLatch = new CountDownLatch(1);
+        IntentFilter wakeUpActionFilter = new IntentFilter(wakeUpAction);
+        BroadcastReceiver receiver =
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        Log.i(
+                                LOG_TAG,
+                                "Suspension-aware sleep ended by receiving the wake-up intent.");
+                        countDownLatch.countDown();
+                    }
+                };
+        context.registerReceiver(receiver, wakeUpActionFilter);
+        PendingIntent pendingIntent =
+                PendingIntent.getBroadcast(
+                        context, 0, new Intent(wakeUpAction), PendingIntent.FLAG_UPDATE_CURRENT);
+
+        alarmManager.setExactAndAllowWhileIdle(
+                AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + durationMs, pendingIntent);
+
+        try {
+            countDownLatch.await(countDownLatchTimeoutMs, TimeUnit.MILLISECONDS);
+            Log.i(LOG_TAG, "Suspension-aware sleep ended.");
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        } finally {
+            context.unregisterReceiver(receiver);
         }
-        SystemClock.sleep(durationMs);
     }
 }
diff --git a/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ScheduledScenarioRunnerTest.java b/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ScheduledScenarioRunnerTest.java
index 7afec46..a65fce0 100644
--- a/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ScheduledScenarioRunnerTest.java
+++ b/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ScheduledScenarioRunnerTest.java
@@ -81,8 +81,8 @@
         }
     }
 
-    // Threshold above which missing a schedule is considered a failure.
-    private static final long TIMEOUT_ERROR_MARGIN_MS = 500;
+    // Threshold above which missing the expected timing is considered a failure.
+    private static final long TIMING_LEEWAY_MS = 500;
 
     // Holds the state of the instrumentation args before each test for restoring after, as one test
     // might affect the state of another otherwise.
@@ -144,7 +144,7 @@
                                     long expectedTimeout =
                                             timeoutMs - ScheduledScenarioRunner.TEARDOWN_LEEWAY_MS;
                                     return abs(exceptionTimeout - expectedTimeout)
-                                            <= TIMEOUT_ERROR_MARGIN_MS;
+                                            <= TIMING_LEEWAY_MS;
                                 });
         Assert.assertTrue(correctTestTimedOutExceptionFired);
     }
@@ -197,8 +197,7 @@
         verify(runner, times(1))
                 .performIdleBeforeNextScenario(
                         getWithinMarginMatcher(
-                                ScheduledScenarioRunner.TEARDOWN_LEEWAY_MS,
-                                TIMEOUT_ERROR_MARGIN_MS));
+                                ScheduledScenarioRunner.TEARDOWN_LEEWAY_MS, TIMING_LEEWAY_MS));
     }
 
     /** Test that a test set to stay in the app after the test idles after its @Test method. */
@@ -228,7 +227,7 @@
                 .performIdleBeforeTeardown(
                         getWithinMarginMatcher(
                                 timeoutMs - 2 * ScheduledScenarioRunner.TEARDOWN_LEEWAY_MS,
-                                TIMEOUT_ERROR_MARGIN_MS));
+                                TIMING_LEEWAY_MS));
         // Test should have passed.
         verify(mRunNotifier, never()).fireTestFailure(any(Failure.class));
     }
@@ -257,8 +256,7 @@
         verify(runner, never()).performIdleBeforeTeardown(anyLong());
         // Idles before the next scenario; duration should be roughly equal to the timeout.
         verify(runner, times(1))
-                .performIdleBeforeNextScenario(
-                        getWithinMarginMatcher(timeoutMs, TIMEOUT_ERROR_MARGIN_MS));
+                .performIdleBeforeNextScenario(getWithinMarginMatcher(timeoutMs, TIMING_LEEWAY_MS));
         // Test should have passed.
         verify(mRunNotifier, never()).fireTestFailure(any(Failure.class));
     }
@@ -295,8 +293,7 @@
         verify(listener, times(1)).testIgnored(any());
         // Idles before the next scenario; duration should be roughly equal to the timeout.
         verify(runner, times(1))
-                .performIdleBeforeNextScenario(
-                        getWithinMarginMatcher(timeoutMs, TIMEOUT_ERROR_MARGIN_MS));
+                .performIdleBeforeNextScenario(getWithinMarginMatcher(timeoutMs, TIMING_LEEWAY_MS));
     }
 
     /** Test that the last test does not have idle after it, regardless of its AfterTest policy. */
@@ -370,6 +367,28 @@
         Assert.assertTrue(bundlesContainSameStringKeyValuePairs(argsBeforeTest, argsAfterTest));
     }
 
+    /** Test that suspension-aware sleep will sleep for the expected duration. */
+    @Test
+    public void testSuspensionAwareSleep_sleepsForExpectedDuration() {
+        long expectedSleepMillis = TimeUnit.SECONDS.toMillis(5);
+        long timestampBeforeSleep = System.currentTimeMillis();
+        ScheduledScenarioRunner.suspensionAwareSleep(expectedSleepMillis);
+        long actualSleepDuration = System.currentTimeMillis() - timestampBeforeSleep;
+        Assert.assertTrue(abs(actualSleepDuration - expectedSleepMillis) <= TIMING_LEEWAY_MS);
+    }
+
+    /** Test that suspension-aware sleep will end due to alarm going off. */
+    @Test
+    public void testSuspensionAwareSleep_isWokenUpByAlarm() {
+        long expectedSleepMillis = TimeUnit.SECONDS.toMillis(5);
+        long timestampBeforeSleep = System.currentTimeMillis();
+        // Supply a longer CountDownLatch timeout so that the alarm will fire before the timeout is
+        // reached.
+        ScheduledScenarioRunner.suspensionAwareSleep(expectedSleepMillis, expectedSleepMillis * 2);
+        long actualSleepDuration = System.currentTimeMillis() - timestampBeforeSleep;
+        Assert.assertTrue(abs(actualSleepDuration - expectedSleepMillis) <= TIMING_LEEWAY_MS);
+    }
+
     /**
      * Helper method to get an argument matcher that checks whether the input value is equal to
      * expected value within a margin.
diff --git a/libraries/telephony-utility/src/android/telephony/utility/SimCardUtil.java b/libraries/telephony-utility/src/android/telephony/utility/SimCardUtil.java
index 5c2d2cd..bdce1e2 100644
--- a/libraries/telephony-utility/src/android/telephony/utility/SimCardUtil.java
+++ b/libraries/telephony-utility/src/android/telephony/utility/SimCardUtil.java
@@ -19,11 +19,9 @@
 import android.content.Context;
 import android.device.collectors.util.SendToInstrumentation;
 import android.os.Bundle;
-import android.se.omapi.Channel;
 import android.se.omapi.Reader;
 import android.se.omapi.SEService;
 import android.se.omapi.SEService.OnConnectedListener;
-import android.se.omapi.Session;
 import android.telephony.TelephonyManager;
 
 import java.util.Timer;
@@ -52,7 +50,7 @@
     private static final String SECURED_ELEMENT = "has_secured_element";
     private static final String SE_SERVICE = "has_se_service";
 
-    private final long SERVICE_CONNECTION_TIME_OUT = 3000;
+    private static final long SERVICE_CONNECTION_TIME_OUT = 3000;
 
     private SEService mSeService;
     private Object mServiceMutex = new Object();
@@ -136,6 +134,7 @@
     }
 
     private class SynchronousExecutor implements Executor {
+        @Override
         public void execute(Runnable r) {
             r.run();
         }
diff --git a/scripts/perfetto-setup/Android.mk b/scripts/perfetto-setup/Android.mk
new file mode 100644
index 0000000..180ddb2
--- /dev/null
+++ b/scripts/perfetto-setup/Android.mk
@@ -0,0 +1,42 @@
+#
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_MODULE := trace_config_detailed.textproto
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_PREBUILT_MODULE_FILE := prebuilts/tools/linux-x86_64/perfetto/configs/trace_config_detailed.textproto
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := long_trace_config.textproto
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_PREBUILT_MODULE_FILE := prebuilts/tools/linux-x86_64/perfetto/configs/long_trace_config.textproto
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := perfetto_trace_processor_shell
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_CHECK_ELF_FILES := false
+LOCAL_PREBUILT_MODULE_FILE := prebuilts/tools/linux-x86_64/perfetto/trace_processor_shell
+include $(BUILD_PREBUILT)
+
+
diff --git a/tests/health/scenarios/src/android/platform/test/scenario/sleep/Idle.java b/tests/health/scenarios/src/android/platform/test/scenario/sleep/Idle.java
index 882523c..4efbd07 100644
--- a/tests/health/scenarios/src/android/platform/test/scenario/sleep/Idle.java
+++ b/tests/health/scenarios/src/android/platform/test/scenario/sleep/Idle.java
@@ -18,8 +18,10 @@
 
 import android.os.SystemClock;
 import android.platform.test.option.LongOption;
+import android.platform.test.rule.NaturalOrientationRule;
 import android.platform.test.scenario.annotation.Scenario;
 
+import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -31,6 +33,9 @@
 @Scenario
 @RunWith(JUnit4.class)
 public class Idle {
+    // Class-level rules
+    @ClassRule public static NaturalOrientationRule orientationRule = new NaturalOrientationRule();
+
     @Rule public final LongOption mDurationMs = new LongOption("durationMs").setDefault(1000L);
 
     @Test
diff --git a/tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOff.java b/tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOff.java
index d3dc780..a2e1290 100644
--- a/tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOff.java
+++ b/tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOff.java
@@ -18,44 +18,52 @@
 
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.platform.test.option.BooleanOption;
+import android.platform.test.option.LongOption;
 import android.platform.test.scenario.annotation.Scenario;
 import android.support.test.uiautomator.UiDevice;
 import androidx.test.InstrumentationRegistry;
 
+import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-/**
- * Shuts the screen off and waits for a specified amount of time.
- */
+/** Shuts the screen off and waits for a specified amount of time. */
 @Scenario
 @RunWith(JUnit4.class)
 public class ScreenOff {
-    private static final String DURATION_OPTION = "screenOffDurationMs";
-    private static final String DURATION_DEFAULT = "1000";
+    @Rule public LongOption mDurationMs = new LongOption("screenOffDurationMs").setDefault(1000L);
 
-    private long mDurationMs = 0L;
+    @Rule
+    public BooleanOption mTurnScreenBackOn =
+            new BooleanOption("screenOffTurnScreenBackOnAfterTest").setDefault(false);
+
     private UiDevice mDevice;
 
     @Before
     public void setUp() {
         mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-        String durationMsString =
-                InstrumentationRegistry.getArguments().getString(DURATION_OPTION, DURATION_DEFAULT);
-        try {
-            mDurationMs = Long.parseLong(durationMsString);
-        } catch (NumberFormatException e) {
-            throw new IllegalArgumentException(
-                    String.format(
-                            "Failed to parse option %s: %s", DURATION_OPTION, durationMsString));
-        }
     }
 
     @Test
     public void testScreenOff() throws RemoteException {
         mDevice.sleep();
-        SystemClock.sleep(mDurationMs);
+        SystemClock.sleep(mDurationMs.get());
+    }
+
+    @After
+    public void tearDown() throws RemoteException {
+        if (mTurnScreenBackOn.get()) {
+            // Wake up the display. wakeUp() is not used here as when the duration is short, the
+            // device might register a double power button press and launch camera.
+            mDevice.pressMenu();
+            mDevice.waitForIdle();
+            // Unlock the screen.
+            mDevice.pressMenu();
+            mDevice.waitForIdle();
+        }
     }
 }