[flutter_tools] Fix crash on null Android apiVersion (#50029)

diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart
index 282465d..7bca5b0 100644
--- a/packages/flutter_tools/lib/src/android/android_device.dart
+++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -1040,7 +1040,11 @@
     ];
     // logcat -T is not supported on Android releases before Lollipop.
     const int kLollipopVersionCode = 21;
-    final int apiVersion = int.tryParse(await device._apiVersion);
+    final int apiVersion = (String v) {
+      // If the API version string isn't found, conservatively assume that the
+      // version is less recent than the one we're looking for.
+      return v == null ? kLollipopVersionCode - 1 : int.tryParse(v);
+    }(await device._apiVersion);
     if (apiVersion != null && apiVersion >= kLollipopVersionCode) {
       args.addAll(<String>[
         '-T',
diff --git a/packages/flutter_tools/test/general.shard/android/android_device_test.dart b/packages/flutter_tools/test/general.shard/android/android_device_test.dart
index b16c196..06f9533 100644
--- a/packages/flutter_tools/test/general.shard/android/android_device_test.dart
+++ b/packages/flutter_tools/test/general.shard/android/android_device_test.dart
@@ -698,67 +698,88 @@
     setUp(() {
       mockAndroidSdk = MockAndroidSdk();
       mockProcessManager = MockProcessManager();
+    });
 
+    void setupGetprop({int apiVersion}) {
       when(mockProcessManager.run(
         argThat(contains('getprop')),
         stderrEncoding: anyNamed('stderrEncoding'),
         stdoutEncoding: anyNamed('stdoutEncoding'),
       )).thenAnswer((_) {
-        final StringBuffer buf = StringBuffer()
-          ..writeln('[ro.build.version.sdk]: [28]');
-        final ProcessResult result = ProcessResult(1, 0, buf.toString(), '');
+        final String buf = apiVersion == null
+          ? ''
+          : '[ro.build.version.sdk]: [$apiVersion]\n';
+        final ProcessResult result = ProcessResult(1, 0, buf, '');
         return Future<ProcessResult>.value(result);
       });
-    });
+    }
 
-    testUsingContext('calls adb logcat with expected flags', () async {
-      const String kLastLogcatTimestamp = '11-27 15:39:04.506';
-      when(mockAndroidSdk.adbPath).thenReturn('adb');
-      when(mockProcessManager.runSync(<String>['adb', '-s', '1234', 'shell', '-x', 'logcat', '-v', 'time', '-t', '1']))
-        .thenReturn(ProcessResult(0, 0, '$kLastLogcatTimestamp I/flutter: irrelevant', ''));
+    const int kLollipopVersionCode = 21;
+    void callsAdbLogcatCorrectly({int apiVersion}) {
+      testUsingContext('calls adb logcat with expected flags, apiVersion=$apiVersion', () async {
+        const String kLastLogcatTimestamp = '11-27 15:39:04.506';
+        setupGetprop(apiVersion: apiVersion);
+        when(mockAndroidSdk.adbPath).thenReturn('adb');
+        when(mockProcessManager.runSync(<String>[
+          'adb', '-s', '1234', 'shell', '-x', 'logcat', '-v', 'time', '-t', '1',
+        ]))
+          .thenReturn(ProcessResult(0, 0, '$kLastLogcatTimestamp I/flutter: irrelevant', ''));
 
-      final Completer<void> logcatCompleter = Completer<void>();
-      when(mockProcessManager.start(argThat(contains('logcat'))))
-        .thenAnswer((_) {
-          logcatCompleter.complete();
-          return Future<Process>.value(createMockProcess());
-        });
+        final Completer<void> logcatCompleter = Completer<void>();
+        when(mockProcessManager.start(argThat(contains('logcat'))))
+          .thenAnswer((_) {
+            logcatCompleter.complete();
+            return Future<Process>.value(createMockProcess());
+          });
 
-      final AndroidDevice device = AndroidDevice('1234');
-      final DeviceLogReader logReader = device.getLogReader();
-      logReader.logLines.listen((_) {});
-      await logcatCompleter.future;
+        final AndroidDevice device = AndroidDevice('1234');
+        final DeviceLogReader logReader = device.getLogReader();
+        logReader.logLines.listen((_) {});
+        await logcatCompleter.future;
 
-      verify(mockProcessManager.start(const <String>['adb', '-s', '1234', 'logcat', '-v', 'time', '-T', kLastLogcatTimestamp]))
-        .called(1);
-    }, overrides: <Type, Generator>{
-      AndroidSdk: () => mockAndroidSdk,
-      ProcessManager: () => mockProcessManager,
-    });
+        verify(mockProcessManager.start(<String>[
+          'adb', '-s', '1234', 'logcat', '-v', 'time',
+          if (apiVersion != null && apiVersion >= kLollipopVersionCode)
+            ...<String>['-T', kLastLogcatTimestamp],
+        ])).called(1);
+      }, overrides: <Type, Generator>{
+        AndroidSdk: () => mockAndroidSdk,
+        ProcessManager: () => mockProcessManager,
+      });
 
-    testUsingContext('calls adb logcat with expected flags when the device logs are empty', () async {
-      when(mockAndroidSdk.adbPath).thenReturn('adb');
-      when(mockProcessManager.runSync(<String>['adb', '-s', '1234', 'shell', '-x', 'logcat', '-v', 'time', '-t', '1']))
-        .thenReturn(ProcessResult(0, 0, '', ''));
+      testUsingContext('calls adb logcat with expected flags when the device logs are empty, apiVersion=$apiVersion', () async {
+        setupGetprop(apiVersion: apiVersion);
+        when(mockAndroidSdk.adbPath).thenReturn('adb');
+        when(mockProcessManager.runSync(<String>[
+          'adb', '-s', '1234', 'shell', '-x', 'logcat', '-v', 'time', '-t', '1',
+        ])).thenReturn(ProcessResult(0, 0, '', ''));
 
-      final Completer<void> logcatCompleter = Completer<void>();
-      when(mockProcessManager.start(argThat(contains('logcat'))))
-        .thenAnswer((_) {
-          logcatCompleter.complete();
-          return Future<Process>.value(createMockProcess());
-        });
+        final Completer<void> logcatCompleter = Completer<void>();
+        when(mockProcessManager.start(argThat(contains('logcat'))))
+          .thenAnswer((_) {
+            logcatCompleter.complete();
+            return Future<Process>.value(createMockProcess());
+          });
 
-      final AndroidDevice device = AndroidDevice('1234');
-      final DeviceLogReader logReader = device.getLogReader();
-      logReader.logLines.listen((_) {});
-      await logcatCompleter.future;
+        final AndroidDevice device = AndroidDevice('1234');
+        final DeviceLogReader logReader = device.getLogReader();
+        logReader.logLines.listen((_) {});
+        await logcatCompleter.future;
 
-      verify(mockProcessManager.start(const <String>['adb', '-s', '1234', 'logcat', '-v', 'time', '-T', '']))
-        .called(1);
-    }, overrides: <Type, Generator>{
-      AndroidSdk: () => mockAndroidSdk,
-      ProcessManager: () => mockProcessManager,
-    });
+        verify(mockProcessManager.start(<String>[
+          'adb', '-s', '1234', 'logcat', '-v', 'time',
+          if (apiVersion != null && apiVersion >= kLollipopVersionCode)
+            ...<String>['-T', ''],
+        ])).called(1);
+      }, overrides: <Type, Generator>{
+        AndroidSdk: () => mockAndroidSdk,
+        ProcessManager: () => mockProcessManager,
+      });
+    }
+
+    callsAdbLogcatCorrectly(apiVersion: kLollipopVersionCode);
+    callsAdbLogcatCorrectly(apiVersion: kLollipopVersionCode - 1);
+    callsAdbLogcatCorrectly();
   });
 
   test('Can parse adb shell dumpsys info', () {