Inject iOS, Android workflows via context (#10750)

Eliminates the need for the device/daemon code to get at the iOS/Android
tooling indirectly via Doctor. In tests, we now inject the workflow
objects (or mocks) directly.
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart
index b4dd532..f6ed675 100644
--- a/packages/flutter_tools/lib/src/android/android_device.dart
+++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -6,6 +6,7 @@
 import 'dart:convert';
 
 import '../android/android_sdk.dart';
+import '../android/android_workflow.dart';
 import '../application_package.dart';
 import '../base/common.dart' show throwToolExit;
 import '../base/file_system.dart';
@@ -17,7 +18,6 @@
 import '../build_info.dart';
 import '../commands/build_apk.dart';
 import '../device.dart';
-import '../doctor.dart';
 import '../globals.dart';
 import '../protocol_discovery.dart';
 
@@ -45,7 +45,7 @@
   bool get supportsPlatform => true;
 
   @override
-  bool get canListAnything => doctor.androidWorkflow.canListDevices;
+  bool get canListAnything => androidWorkflow.canListDevices;
 
   @override
   List<Device> pollingGetDevices() => getAdbDevices();
diff --git a/packages/flutter_tools/lib/src/android/android_workflow.dart b/packages/flutter_tools/lib/src/android/android_workflow.dart
index aedfca7..c01d5af 100644
--- a/packages/flutter_tools/lib/src/android/android_workflow.dart
+++ b/packages/flutter_tools/lib/src/android/android_workflow.dart
@@ -4,6 +4,7 @@
 
 import 'dart:async';
 
+import '../base/context.dart';
 import '../base/file_system.dart';
 import '../base/io.dart';
 import '../base/os.dart';
@@ -15,6 +16,8 @@
 import 'android_sdk.dart';
 import 'android_studio.dart' as android_studio;
 
+AndroidWorkflow get androidWorkflow => context.putIfAbsent(AndroidWorkflow, () => new AndroidWorkflow());
+
 class AndroidWorkflow extends DoctorValidator implements Workflow {
   AndroidWorkflow() : super('Android toolchain - develop for Android devices');
 
diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart
index d01e42f..39eba57 100644
--- a/packages/flutter_tools/lib/src/doctor.dart
+++ b/packages/flutter_tools/lib/src/doctor.dart
@@ -27,18 +27,6 @@
 Doctor get doctor => context[Doctor];
 
 class Doctor {
-  Doctor() {
-    _androidWorkflow = new AndroidWorkflow();
-    _iosWorkflow = new IOSWorkflow();
-  }
-
-  IOSWorkflow _iosWorkflow;
-  AndroidWorkflow _androidWorkflow;
-
-  IOSWorkflow get iosWorkflow => _iosWorkflow;
-
-  AndroidWorkflow get androidWorkflow => _androidWorkflow;
-
   List<DoctorValidator> _validators;
 
   List<DoctorValidator> get validators {
@@ -46,11 +34,11 @@
       _validators = <DoctorValidator>[];
       _validators.add(new _FlutterValidator());
 
-      if (_androidWorkflow.appliesToHostPlatform)
-        _validators.add(_androidWorkflow);
+      if (androidWorkflow.appliesToHostPlatform)
+        _validators.add(androidWorkflow);
 
-      if (_iosWorkflow.appliesToHostPlatform)
-        _validators.add(_iosWorkflow);
+      if (iosWorkflow.appliesToHostPlatform)
+        _validators.add(iosWorkflow);
 
       final List<DoctorValidator> ideValidators = <DoctorValidator>[];
       ideValidators.addAll(AndroidStudioValidator.allValidators);
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart
index 396266f..dc170ec 100644
--- a/packages/flutter_tools/lib/src/ios/devices.dart
+++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -14,9 +14,9 @@
 import '../base/process_manager.dart';
 import '../build_info.dart';
 import '../device.dart';
-import '../doctor.dart';
 import '../globals.dart';
 import '../protocol_discovery.dart';
+import 'ios_workflow.dart';
 import 'mac.dart';
 
 const String _kIdeviceinstallerInstructions =
@@ -33,7 +33,7 @@
   bool get supportsPlatform => platform.isMacOS;
 
   @override
-  bool get canListAnything => doctor.iosWorkflow.canListDevices;
+  bool get canListAnything => iosWorkflow.canListDevices;
 
   @override
   List<Device> pollingGetDevices() => IOSDevice.getAttachedDevices();
@@ -97,7 +97,7 @@
   bool get supportsStartPaused => false;
 
   static List<IOSDevice> getAttachedDevices() {
-    if (!doctor.iosWorkflow.hasIDeviceId)
+    if (!iosWorkflow.hasIDeviceId)
       return <IOSDevice>[];
 
     final List<IOSDevice> devices = <IOSDevice>[];
diff --git a/packages/flutter_tools/lib/src/ios/ios_workflow.dart b/packages/flutter_tools/lib/src/ios/ios_workflow.dart
index 696048b..4c640aa 100644
--- a/packages/flutter_tools/lib/src/ios/ios_workflow.dart
+++ b/packages/flutter_tools/lib/src/ios/ios_workflow.dart
@@ -5,6 +5,7 @@
 import 'dart:async';
 
 import '../base/common.dart';
+import '../base/context.dart';
 import '../base/file_system.dart';
 import '../base/io.dart';
 import '../base/os.dart';
@@ -14,6 +15,8 @@
 import '../doctor.dart';
 import 'mac.dart';
 
+IOSWorkflow get iosWorkflow => context.putIfAbsent(IOSWorkflow, () => new IOSWorkflow());
+
 Xcode get xcode => Xcode.instance;
 
 class IOSWorkflow extends DoctorValidator implements Workflow {
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index 16430aa..ef9188a 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -17,12 +17,12 @@
 import '../base/process.dart';
 import '../base/process_manager.dart';
 import '../build_info.dart';
-import '../doctor.dart';
 import '../flx.dart' as flx;
 import '../globals.dart';
 import '../plugins.dart';
 import '../services.dart';
 import 'code_signing.dart';
+import 'ios_workflow.dart';
 import 'xcodeproj.dart';
 
 const int kXcodeRequiredVersionMajor = 7;
@@ -354,8 +354,8 @@
 
 Future<Null> _runPodInstall(Directory bundle, String engineDirectory) async {
   if (fs.file(fs.path.join(bundle.path, 'Podfile')).existsSync()) {
-    if (!await doctor.iosWorkflow.isCocoaPodsInstalledAndMeetsVersionCheck) {
-      final String minimumVersion = doctor.iosWorkflow.cocoaPodsMinimumVersion;
+    if (!await iosWorkflow.isCocoaPodsInstalledAndMeetsVersionCheck) {
+      final String minimumVersion = iosWorkflow.cocoaPodsMinimumVersion;
       printError(
         'Warning: CocoaPods version $minimumVersion or greater not installed. Skipping pod install.\n'
         '$noCocoaPodsConsequence\n'
@@ -365,7 +365,7 @@
       );
       return;
     }
-    if (!await doctor.iosWorkflow.isCocoaPodsInitialized) {
+    if (!await iosWorkflow.isCocoaPodsInitialized) {
       printError(
         'Warning: CocoaPods installed but not initialized. Skipping pod install.\n'
         '$noCocoaPodsConsequence\n'
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index d4ec64f..d67213b 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -16,10 +16,10 @@
 import '../base/process_manager.dart';
 import '../build_info.dart';
 import '../device.dart';
-import '../doctor.dart';
 import '../flx.dart' as flx;
 import '../globals.dart';
 import '../protocol_discovery.dart';
+import 'ios_workflow.dart';
 import 'mac.dart';
 
 const String _xcrunPath = '/usr/bin/xcrun';
@@ -34,7 +34,7 @@
   bool get supportsPlatform => platform.isMacOS;
 
   @override
-  bool get canListAnything => doctor.iosWorkflow.canListDevices;
+  bool get canListAnything => iosWorkflow.canListDevices;
 
   @override
   List<Device> pollingGetDevices() => IOSSimulatorUtils.instance.getAttachedDevices();
diff --git a/packages/flutter_tools/test/commands/daemon_test.dart b/packages/flutter_tools/test/commands/daemon_test.dart
index e55d7dd..10a864b 100644
--- a/packages/flutter_tools/test/commands/daemon_test.dart
+++ b/packages/flutter_tools/test/commands/daemon_test.dart
@@ -8,7 +8,6 @@
 import 'package:flutter_tools/src/base/context.dart';
 import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/commands/daemon.dart';
-import 'package:flutter_tools/src/doctor.dart';
 import 'package:flutter_tools/src/globals.dart';
 import 'package:flutter_tools/src/ios/ios_workflow.dart';
 import 'package:test/test.dart';
@@ -220,7 +219,8 @@
       expect(response['params'], containsPair('title', 'Unable to list devices'));
       expect(response['params'], containsPair('message', contains('Unable to discover Android devices')));
     }, overrides: <Type, Generator>{
-      Doctor: () => new MockDoctor(androidCanListDevices: false),
+      AndroidWorkflow: () => new MockAndroidWorkflow(canListDevices: false),
+      IOSWorkflow: () => new MockIOSWorkflow(),
     });
 
     testUsingContext('device.getDevices should respond with list', () async {
@@ -263,36 +263,24 @@
         commands.close();
       });
     }, overrides: <Type, Generator>{
-      Doctor: () => new MockDoctor(),
+      AndroidWorkflow: () => new MockAndroidWorkflow(),
+      IOSWorkflow: () => new MockIOSWorkflow(),
     });
   });
 }
 
 bool _notEvent(Map<String, dynamic> map) => map['event'] == null;
 
-class MockDoctor extends Doctor {
-  final bool androidCanListDevices;
-  final bool iosCanListDevices;
-
-  MockDoctor({this.androidCanListDevices: true, this.iosCanListDevices: true});
-
-  @override
-  AndroidWorkflow get androidWorkflow => new MockAndroidWorkflow(androidCanListDevices);
-
-  @override
-  IOSWorkflow get iosWorkflow => new MockIosWorkflow(iosCanListDevices);
-}
-
 class MockAndroidWorkflow extends AndroidWorkflow {
+  MockAndroidWorkflow({ this.canListDevices: true });
+
   @override
   final bool canListDevices;
-
-  MockAndroidWorkflow(this.canListDevices);
 }
 
-class MockIosWorkflow extends IOSWorkflow {
+class MockIOSWorkflow extends IOSWorkflow {
+  MockIOSWorkflow({ this.canListDevices:true });
+
   @override
   final bool canListDevices;
-
-  MockIosWorkflow(this.canListDevices);
 }