Some minor cleanup in devicelab (#36571)

diff --git a/dev/devicelab/bin/run.dart b/dev/devicelab/bin/run.dart
index 4cd54b5..2d74927 100644
--- a/dev/devicelab/bin/run.dart
+++ b/dev/devicelab/bin/run.dart
@@ -107,7 +107,7 @@
     'stage',
     abbr: 's',
     help: 'Name of the stage. Runs all tasks for that stage. '
-        'The tasks and their stages are read from manifest.yaml.',
+          'The tasks and their stages are read from manifest.yaml.',
   )
   ..addFlag(
     'all',
diff --git a/dev/devicelab/bin/tasks/dartdocs.dart b/dev/devicelab/bin/tasks/dartdocs.dart
index 65ff5f6..1833b2d 100644
--- a/dev/devicelab/bin/tasks/dartdocs.dart
+++ b/dev/devicelab/bin/tasks/dartdocs.dart
@@ -11,6 +11,7 @@
 import 'package:path/path.dart' as path;
 
 Future<void> main() async {
+  final String dot = Platform.isWindows ? '-' : '•';
   await task(() async {
     final Stopwatch clock = Stopwatch()..start();
     final Process analysis = await startProcess(
@@ -27,9 +28,9 @@
       print('analyzer stdout: $entry');
       if (entry == 'Building flutter tool...') {
         // ignore this line
-      } else if (entry.startsWith('info • Document all public members •')) {
+      } else if (entry.startsWith('info $dot Document all public members $dot')) {
         publicMembers += 1;
-      } else if (entry.startsWith('info •') || entry.startsWith('warning •') || entry.startsWith('error •')) {
+      } else if (entry.startsWith('info $dot') || entry.startsWith('warning $dot') || entry.startsWith('error $dot')) {
         otherErrors += 1;
       } else if (entry.contains(' (ran in ') && !sawFinalLine) {
         // ignore this line once
diff --git a/dev/devicelab/lib/framework/apk_utils.dart b/dev/devicelab/lib/framework/apk_utils.dart
index 62bee93..0dc28b9 100644
--- a/dev/devicelab/lib/framework/apk_utils.dart
+++ b/dev/devicelab/lib/framework/apk_utils.dart
@@ -256,9 +256,18 @@
     'app:$task',
     ...?options,
   ];
-  final String gradle = Platform.isWindows ? 'gradlew.bat' : './gradlew';
-  print('Running Gradle: ${path.join(workingDirectory, gradle)} ${args.join(' ')}');
-  print(File(path.join(workingDirectory, gradle)).readAsStringSync());
+  final String gradle = path.join(workingDirectory, Platform.isWindows ? 'gradlew.bat' : './gradlew');
+  print('┌── $gradle');
+  print('│ ' + File(path.join(workingDirectory, gradle)).readAsLinesSync().join('\n│ '));
+  print('└─────────────────────────────────────────────────────────────────────────────────────');
+  print(
+    'Running Gradle:\n'
+    '  Executable: $gradle\n'
+    '  Arguments: ${args.join(' ')}\n'
+    '  Working directory: $workingDirectory\n'
+    '  JAVA_HOME: $javaHome\n'
+    ''
+  );
   return Process.run(
     gradle,
     args,
diff --git a/dev/devicelab/lib/framework/framework.dart b/dev/devicelab/lib/framework/framework.dart
index 34d2f64..ec41d5b 100644
--- a/dev/devicelab/lib/framework/framework.dart
+++ b/dev/devicelab/lib/framework/framework.dart
@@ -14,11 +14,6 @@
 import 'running_processes.dart';
 import 'utils.dart';
 
-/// Maximum amount of time a single task is allowed to take to run.
-///
-/// If exceeded the task is considered to have failed.
-const Duration _kDefaultTaskTimeout = Duration(minutes: 15);
-
 /// Represents a unit of work performed in the CI environment that can
 /// succeed, fail and be retried independently of others.
 typedef TaskFunction = Future<TaskResult> Function();
@@ -55,7 +50,7 @@
         (String method, Map<String, String> parameters) async {
       final Duration taskTimeout = parameters.containsKey('timeoutInMinutes')
         ? Duration(minutes: int.parse(parameters['timeoutInMinutes']))
-        : _kDefaultTaskTimeout;
+        : null;
       final TaskResult result = await run(taskTimeout);
       return ServiceExtensionResponse.result(json.encode(result.toJson()));
     });
@@ -90,7 +85,10 @@
       ).toSet();
       beforeRunningDartInstances.forEach(print);
 
-      TaskResult result = await _performTask().timeout(taskTimeout);
+      Future<TaskResult> futureResult = _performTask();
+      if (taskTimeout != null)
+        futureResult = futureResult.timeout(taskTimeout);
+      TaskResult result = await futureResult;
 
       section('Checking running Dart$exe processes after task...');
       final List<RunningProcessInfo> afterRunningDartInstances = await getRunningProcesses(
diff --git a/dev/devicelab/lib/framework/runner.dart b/dev/devicelab/lib/framework/runner.dart
index dfaeb7f..642afd4 100644
--- a/dev/devicelab/lib/framework/runner.dart
+++ b/dev/devicelab/lib/framework/runner.dart
@@ -11,10 +11,6 @@
 
 import 'package:flutter_devicelab/framework/utils.dart';
 
-/// Slightly longer than task timeout that gives the task runner a chance to
-/// clean-up before forcefully quitting it.
-const Duration taskTimeoutWithGracePeriod = Duration(minutes: 26);
-
 /// Runs a task in a separate Dart VM and collects the result using the VM
 /// service protocol.
 ///
@@ -71,21 +67,11 @@
     stderr.writeln('[$taskName] [STDERR] $line');
   });
 
-  String waitingFor = 'connection';
   try {
     final VMIsolateRef isolate = await _connectToRunnerIsolate(await uri.future);
-    waitingFor = 'task completion';
-    final Map<String, dynamic> taskResult =
-        await isolate.invokeExtension('ext.cocoonRunTask').timeout(taskTimeoutWithGracePeriod);
-    waitingFor = 'task process to exit';
-    await runner.exitCode.timeout(const Duration(seconds: 60));
+    final Map<String, dynamic> taskResult = await isolate.invokeExtension('ext.cocoonRunTask');
+    await runner.exitCode;
     return taskResult;
-  } on TimeoutException catch (timeout) {
-    runner.kill(ProcessSignal.sigint);
-    return <String, dynamic>{
-      'success': false,
-      'reason': 'Timeout in runner.dart waiting for $waitingFor: ${timeout.message}',
-    };
   } finally {
     if (!runnerFinished)
       runner.kill(ProcessSignal.sigkill);
@@ -104,14 +90,7 @@
   pathSegments.add('ws');
   final String url = vmServiceUri.replace(scheme: 'ws', pathSegments:
       pathSegments).toString();
-  final DateTime started = DateTime.now();
-
-  // TODO(yjbanov): due to lack of imagination at the moment the handshake with
-  //                the task process is very rudimentary and requires this small
-  //                delay to let the task process open up the VM service port.
-  //                Otherwise we almost always hit the non-ready case first and
-  //                wait a whole 1 second, which is annoying.
-  await Future<void>.delayed(const Duration(milliseconds: 100));
+  final Stopwatch stopwatch = Stopwatch()..start();
 
   while (true) {
     try {
@@ -127,17 +106,9 @@
         throw 'not ready yet';
       return isolate;
     } catch (error) {
-      const Duration connectionTimeout = Duration(seconds: 10);
-      if (DateTime.now().difference(started) > connectionTimeout) {
-        throw TimeoutException(
-          'Failed to connect to the task runner process',
-          connectionTimeout,
-        );
-      }
-      print('VM service not ready yet: $error');
-      const Duration pauseBetweenRetries = Duration(milliseconds: 200);
-      print('Will retry in $pauseBetweenRetries.');
-      await Future<void>.delayed(pauseBetweenRetries);
+      if (stopwatch.elapsed > const Duration(seconds: 10))
+        print('VM service still not ready after ${stopwatch.elapsed}: $error\nContinuing to retry...');
+      await Future<void>.delayed(const Duration(milliseconds: 50));
     }
   }
 }