Fix flaky complex_layout_scroll_perf__memory  & flutter_gallery__memory_nav (#150368)

Initial tap is missing sometimes; either its never delivered or it is
delivered before gesture controller is hooked up.

1: Update MemoryTest to have option `requiresTapToStart` guarding the
   new paths
2: Update the two perf tests that appear to be flaky to output when
   TAPPED is received
3: Update the MemoryTest to keep tapping while waiting for TAPPED

Tested on devicelab:
* setting iterations=1
* removing the timeout before READY
* running tests in a while loop

Before this change, you could get the test to hang often. After this
change you'll see "tapping device... [x]" where x is the counter.

Fixes https://github.com/flutter/flutter/issues/150096
diff --git a/dev/benchmarks/complex_layout/test_memory/scroll_perf.dart b/dev/benchmarks/complex_layout/test_memory/scroll_perf.dart
index b06849c..46e3f2d 100644
--- a/dev/benchmarks/complex_layout/test_memory/scroll_perf.dart
+++ b/dev/benchmarks/complex_layout/test_memory/scroll_perf.dart
@@ -23,7 +23,7 @@
   final Completer<void> ready = Completer<void>();
   runApp(GestureDetector(
     onTap: () {
-      debugPrint('Received tap.');
+      debugPrint('==== MEMORY BENCHMARK ==== TAPPED ====');
       ready.complete();
     },
     behavior: HitTestBehavior.opaque,
@@ -32,16 +32,14 @@
     ),
   ));
   await SchedulerBinding.instance.endOfFrame;
-
-  /// Wait 50ms to allow the raster thread to actually put up the frame. (The
-  /// endOfFrame future ends when we send the data to the engine, before
-  /// the raster thread has had a chance to rasterize, etc.)
-  await Future<void>.delayed(const Duration(milliseconds: 50));
   debugPrint('==== MEMORY BENCHMARK ==== READY ====');
 
   await ready.future; // waits for tap sent by devicelab task
   debugPrint('Continuing...');
 
+  // Wait out any errant taps due to synchronization
+  await Future<void>.delayed(const Duration(milliseconds: 200));
+
   // remove onTap handler, enable pointer events for app
   runApp(GestureDetector(
     child: const IgnorePointer(
diff --git a/dev/devicelab/bin/tasks/complex_layout_scroll_perf__memory.dart b/dev/devicelab/bin/tasks/complex_layout_scroll_perf__memory.dart
index f3784a3..ae691b0 100644
--- a/dev/devicelab/bin/tasks/complex_layout_scroll_perf__memory.dart
+++ b/dev/devicelab/bin/tasks/complex_layout_scroll_perf__memory.dart
@@ -13,5 +13,6 @@
     '${flutterDirectory.path}/dev/benchmarks/complex_layout',
     'test_memory/scroll_perf.dart',
     'com.yourcompany.complexLayout',
+    requiresTapToStart: true,
   ).run);
 }
diff --git a/dev/devicelab/bin/tasks/flutter_gallery__memory_nav.dart b/dev/devicelab/bin/tasks/flutter_gallery__memory_nav.dart
index 3b83a78..d056d64 100644
--- a/dev/devicelab/bin/tasks/flutter_gallery__memory_nav.dart
+++ b/dev/devicelab/bin/tasks/flutter_gallery__memory_nav.dart
@@ -11,5 +11,6 @@
     '${flutterDirectory.path}/dev/integration_tests/flutter_gallery',
     'test_memory/memory_nav.dart',
     'io.flutter.demo.gallery',
+    requiresTapToStart: true,
   ).run);
 }
diff --git a/dev/devicelab/lib/tasks/perf_tests.dart b/dev/devicelab/lib/tasks/perf_tests.dart
index de9dfec..8bb4b64 100644
--- a/dev/devicelab/lib/tasks/perf_tests.dart
+++ b/dev/devicelab/lib/tasks/perf_tests.dart
@@ -1968,11 +1968,12 @@
 
 /// Measure application memory usage.
 class MemoryTest {
-  MemoryTest(this.project, this.test, this.package);
+  MemoryTest(this.project, this.test, this.package, {this.requiresTapToStart = false});
 
   final String project;
   final String test;
   final String package;
+  final bool requiresTapToStart;
 
   /// Completes when the log line specified in the last call to
   /// [prepareForNextMessage] is seen by `adb logcat`.
@@ -2061,6 +2062,38 @@
     await receivedNextMessage;
   }
 
+  /// Taps the application and looks for acknowldgement.
+  ///
+  /// This is used by several tests to ensure scrolling gestures are installed.
+  Future<void> tapNotification() async {
+    // Keep "tapping" the device till it responds with the string we expect,
+    // or throw an error instead of tying up the infrastructure for 30 minutes.
+    prepareForNextMessage('TAPPED');
+    bool tapped = false;
+    int tapCount = 0;
+    await Future.any(<Future<void>>[
+      () async {
+        while (true) {
+          if (tapped) {
+            break;
+          }
+          tapCount += 1;
+          print('tapping device... [$tapCount]');
+          await device!.tap(100, 100);
+          await Future<void>.delayed(const Duration(milliseconds: 100));
+        }
+      }(),
+      () async {
+        print('awaiting "tapped" message... (timeout: 10 seconds)');
+        try {
+          await receivedNextMessage?.timeout(const Duration(seconds: 10));
+        } finally {
+          tapped = true;
+        }
+      }(),
+    ]);
+  }
+
   /// To change the behavior of the test, override this.
   ///
   /// Make sure to call recordStart() and recordEnd() once each in that order.
@@ -2070,10 +2103,11 @@
   Future<void> useMemory() async {
     await launchApp();
     await recordStart();
+    if (requiresTapToStart) {
+      await tapNotification();
+    }
 
     prepareForNextMessage('DONE');
-    print('tapping device...');
-    await device!.tap(100, 100);
     print('awaiting "done" message...');
     await receivedNextMessage;
 
diff --git a/dev/integration_tests/flutter_gallery/test_memory/memory_nav.dart b/dev/integration_tests/flutter_gallery/test_memory/memory_nav.dart
index 5992b64..9b93de1 100644
--- a/dev/integration_tests/flutter_gallery/test_memory/memory_nav.dart
+++ b/dev/integration_tests/flutter_gallery/test_memory/memory_nav.dart
@@ -26,7 +26,7 @@
   final Completer<void> ready = Completer<void>();
   runApp(GestureDetector(
     onTap: () {
-      debugPrint('Received tap.');
+      debugPrint('==== MEMORY BENCHMARK ==== TAPPED ====');
       ready.complete();
     },
     behavior: HitTestBehavior.opaque,
@@ -35,12 +35,14 @@
     ),
   ));
   await SchedulerBinding.instance.endOfFrame;
-  await Future<void>.delayed(const Duration(milliseconds: 50));
   debugPrint('==== MEMORY BENCHMARK ==== READY ====');
 
   await ready.future;
   debugPrint('Continuing...');
 
+  // Wait out any errant taps due to synchronization
+  await Future<void>.delayed(const Duration(milliseconds: 200));
+
   // remove onTap handler, enable pointer events for app
   runApp(GestureDetector(
     child: const IgnorePointer(