Break down flutter doctor validations and results (#34624)

diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart
index da7d33b..15526ca 100644
--- a/packages/flutter_tools/lib/src/doctor.dart
+++ b/packages/flutter_tools/lib/src/doctor.dart
@@ -33,6 +33,7 @@
 import 'macos/xcode_validator.dart';
 import 'proxy_validator.dart';
 import 'tester/flutter_tester.dart';
+import 'usage.dart';
 import 'version.dart';
 import 'vscode/vscode_validator.dart';
 import 'web/web_validator.dart';
@@ -248,6 +249,8 @@
           break;
       }
 
+      flutterUsage.sendEvent('doctorResult.${validator.runtimeType.toString()}', result.typeStr);
+
       if (result.statusInfo != null) {
         printStatus('${result.coloredLeadingBox} ${validator.title} (${result.statusInfo})',
             hangingIndent: result.leadingBox.length + 1);
@@ -327,6 +330,7 @@
 abstract class DoctorValidator {
   const DoctorValidator(this.title);
 
+  /// This is displayed in the CLI.
   final String title;
 
   String get slowWarning => 'This is taking an unexpectedly long time...';
@@ -434,6 +438,22 @@
     }
     return null;
   }
+
+  /// The string representation of the type.
+  String get typeStr {
+    assert(type != null);
+    switch (type) {
+      case ValidationType.missing:
+        return 'missing';
+      case ValidationType.installed:
+        return 'installed';
+      case ValidationType.notAvailable:
+        return 'notAvailable';
+      case ValidationType.partial:
+        return 'partial';
+    }
+    return null;
+  }
 }
 
 class ValidationMessage {
diff --git a/packages/flutter_tools/test/commands/doctor_test.dart b/packages/flutter_tools/test/commands/doctor_test.dart
index 45af935..3d38d58 100644
--- a/packages/flutter_tools/test/commands/doctor_test.dart
+++ b/packages/flutter_tools/test/commands/doctor_test.dart
@@ -18,6 +18,7 @@
 import 'package:flutter_tools/src/proxy_validator.dart';
 import 'package:flutter_tools/src/vscode/vscode.dart';
 import 'package:flutter_tools/src/vscode/vscode_validator.dart';
+import 'package:flutter_tools/src/usage.dart';
 
 import '../src/common.dart';
 import '../src/context.dart';
@@ -207,6 +208,75 @@
     });
   });
 
+  group('doctor usage params', () {
+    Usage mockUsage;
+
+    setUp(() {
+      mockUsage = MockUsage();
+      when(mockUsage.isFirstRun).thenReturn(true);
+    });
+
+    testUsingContext('contains installed', () async {
+      await doctor.diagnose(verbose: false);
+
+      expect(
+        verify(mockUsage.sendEvent('doctorResult.PassingValidator', captureAny)).captured,
+        <dynamic>['installed', 'installed', 'installed'],
+      );
+    }, overrides: <Type, Generator>{
+      DoctorValidatorsProvider: () => FakeDoctorValidatorsProvider(),
+      Platform: _kNoColorOutputPlatform,
+      Usage: () => mockUsage,
+    });
+
+    testUsingContext('contains installed and partial', () async {
+      await FakePassingDoctor().diagnose(verbose: false);
+
+      expect(
+        verify(mockUsage.sendEvent('doctorResult.PassingValidator', captureAny)).captured,
+        <dynamic>['installed', 'installed'],
+      );
+      expect(
+        verify(mockUsage.sendEvent('doctorResult.PartialValidatorWithHintsOnly', captureAny)).captured,
+        <dynamic>['partial'],
+      );
+      expect(
+        verify(mockUsage.sendEvent('doctorResult.PartialValidatorWithErrors', captureAny)).captured,
+        <dynamic>['partial'],
+      );
+    }, overrides: <Type, Generator>{
+      Platform: _kNoColorOutputPlatform,
+      Usage: () => mockUsage,
+    });
+
+    testUsingContext('contains installed, missing and partial', () async {
+      await FakeDoctor().diagnose(verbose: false);
+
+      expect(
+        verify(mockUsage.sendEvent('doctorResult.PassingValidator', captureAny)).captured,
+        <dynamic>['installed'],
+      );
+      expect(
+        verify(mockUsage.sendEvent('doctorResult.MissingValidator', captureAny)).captured,
+        <dynamic>['missing'],
+      );
+      expect(
+        verify(mockUsage.sendEvent('doctorResult.NotAvailableValidator', captureAny)).captured,
+        <dynamic>['notAvailable'],
+      );
+      expect(
+        verify(mockUsage.sendEvent('doctorResult.PartialValidatorWithHintsOnly', captureAny)).captured,
+        <dynamic>['partial'],
+      );
+      expect(
+        verify(mockUsage.sendEvent('doctorResult.PartialValidatorWithErrors', captureAny)).captured,
+        <dynamic>['partial'],
+      );
+    }, overrides: <Type, Generator>{
+      Platform: _kNoColorOutputPlatform,
+      Usage: () => mockUsage,
+    });
+  });
 
   group('doctor with fake validators', () {
     testUsingContext('validate non-verbose output format for run without issues', () async {
@@ -486,6 +556,8 @@
   });
 }
 
+class MockUsage extends Mock implements Usage {}
+
 class IntelliJValidatorTestTarget extends IntelliJValidator {
   IntelliJValidatorTestTarget(String title, String installPath) : super(title, installPath);
 
@@ -639,8 +711,6 @@
   List<Workflow> get workflows => <Workflow>[];
 }
 
-
-
 class PassingGroupedValidator extends DoctorValidator {
   PassingGroupedValidator(String name) : super(name);
 
@@ -650,7 +720,6 @@
     messages.add(ValidationMessage('A helpful message'));
     return ValidationResult(ValidationType.installed, messages);
   }
-
 }
 
 class MissingGroupedValidator extends DoctorValidator {