Make AppContext immutable and race-free (#15984)

This updates AppContext per the recommendations in #15352

Fixes #15352
diff --git a/packages/flutter_tools/lib/src/context_runner.dart b/packages/flutter_tools/lib/src/context_runner.dart
index 5d8bc67..9abaec3 100644
--- a/packages/flutter_tools/lib/src/context_runner.dart
+++ b/packages/flutter_tools/lib/src/context_runner.dart
@@ -4,38 +4,73 @@
 
 import 'dart:async';
 
-import 'package:process/process.dart';
+import 'package:quiver/time.dart';
 
+import 'android/android_sdk.dart';
+import 'android/android_studio.dart';
+import 'android/android_workflow.dart';
+import 'artifacts.dart';
+import 'asset.dart';
+import 'base/build.dart';
 import 'base/config.dart';
 import 'base/context.dart';
-import 'base/file_system.dart';
+import 'base/flags.dart';
 import 'base/io.dart';
 import 'base/logger.dart';
 import 'base/os.dart';
 import 'base/platform.dart';
+import 'base/port_scanner.dart';
 import 'base/utils.dart';
 import 'cache.dart';
-import 'disabled_usage.dart';
+import 'devfs.dart';
+import 'device.dart';
+import 'doctor.dart';
+import 'ios/cocoapods.dart';
+import 'ios/ios_workflow.dart';
+import 'ios/mac.dart';
+import 'ios/simulators.dart';
+import 'ios/xcodeproj.dart';
+import 'run_hot.dart';
 import 'usage.dart';
+import 'version.dart';
 
-typedef Future<Null> Runner(List<String> args);
-
-Future<Null> runInContext(List<String> args, Runner runner) {
-  final AppContext executableContext = new AppContext();
-  executableContext.setVariable(Logger, new StdoutLogger());
-  return executableContext.runInZone(() {
-    // Initialize the context with some defaults.
-    // This list must be kept in sync with lib/executable.dart.
-    context.putIfAbsent(BotDetector, () => const BotDetector());
-    context.putIfAbsent(Stdio, () => const Stdio());
-    context.putIfAbsent(Platform, () => const LocalPlatform());
-    context.putIfAbsent(FileSystem, () => const LocalFileSystem());
-    context.putIfAbsent(ProcessManager, () => const LocalProcessManager());
-    context.putIfAbsent(Logger, () => new StdoutLogger());
-    context.putIfAbsent(Cache, () => new Cache());
-    context.putIfAbsent(Config, () => new Config());
-    context.putIfAbsent(OperatingSystemUtils, () => new OperatingSystemUtils());
-    context.putIfAbsent(Usage, () => new DisabledUsage());
-    return runner(args);
-  });
+Future<T> runInContext<T>(
+  FutureOr<T> runner(), {
+  Map<Type, dynamic> overrides,
+}) async {
+  return await context.run<Future<T>>(
+    name: 'global fallbacks',
+    body: () async => await runner(),
+    overrides: overrides,
+    fallbacks: <Type, Generator>{
+      AndroidSdk: AndroidSdk.locateAndroidSdk,
+      AndroidStudio: AndroidStudio.latestValid,
+      AndroidWorkflow: () => new AndroidWorkflow(),
+      Artifacts: () => new CachedArtifacts(),
+      AssetBundleFactory: () => AssetBundleFactory.defaultInstance,
+      BotDetector: () => const BotDetector(),
+      Cache: () => new Cache(),
+      Clock: () => const Clock(),
+      CocoaPods: () => const CocoaPods(),
+      Config: () => new Config(),
+      DevFSConfig: () => new DevFSConfig(),
+      DeviceManager: () => new DeviceManager(),
+      Doctor: () => new Doctor(),
+      Flags: () => const EmptyFlags(),
+      FlutterVersion: () => new FlutterVersion(const Clock()),
+      GenSnapshot: () => const GenSnapshot(),
+      HotRunnerConfig: () => new HotRunnerConfig(),
+      IMobileDevice: () => const IMobileDevice(),
+      IOSSimulatorUtils: () => new IOSSimulatorUtils(),
+      IOSWorkflow: () => const IOSWorkflow(),
+      Logger: () => platform.isWindows ? new WindowsStdoutLogger() : new StdoutLogger(),
+      OperatingSystemUtils: () => new OperatingSystemUtils(),
+      PortScanner: () => const HostPortScanner(),
+      SimControl: () => new SimControl(),
+      Stdio: () => const Stdio(),
+      Usage: () => new Usage(),
+      Xcode: () => new Xcode(),
+      XcodeProjectInterpreter: () => new XcodeProjectInterpreter(),
+    },
+  );
 }