Remove default AppContext and cleanup globals (#6999)

diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart
index 2f1406d..90e20cd 100644
--- a/packages/flutter_tools/lib/executable.dart
+++ b/packages/flutter_tools/lib/executable.dart
@@ -40,6 +40,7 @@
 import 'src/doctor.dart';
 import 'src/globals.dart';
 import 'src/hot.dart';
+import 'src/usage.dart';
 import 'src/runner/flutter_command_runner.dart';
 
 /// Main entry point for commands.
@@ -81,8 +82,12 @@
     ..addCommand(new UpdatePackagesCommand(hidden: !verboseHelp))
     ..addCommand(new UpgradeCommand());
 
-  return Chain.capture/*<Future<Null>>*/(() async {
-    // Initialize globals.
+  // Construct a context.
+  AppContext _executableContext = new AppContext();
+
+  // Make the context current.
+  _executableContext.runInZone(() {
+    // Initialize the context with some defaults.
     if (context[Logger] == null)
       context[Logger] = new StdoutLogger();
     if (context[DeviceManager] == null)
@@ -94,56 +99,58 @@
     if (context[HotRunnerConfig] == null)
       context[HotRunnerConfig] = new HotRunnerConfig();
 
-    await runner.run(args);
-    _exit(0);
-  }, onError: (dynamic error, Chain chain) {
-    if (error is UsageException) {
-      stderr.writeln(error.message);
-      stderr.writeln();
-      stderr.writeln(
-        "Run 'flutter -h' (or 'flutter <command> -h') for available "
-        "flutter commands and options."
-      );
-      // Argument error exit code.
-      _exit(64);
-    } else if (error is ToolExit) {
-      if (error.message != null)
+    return Chain.capture/*<Future<Null>>*/(() async {
+      await runner.run(args);
+      _exit(0);
+    }, onError: (dynamic error, Chain chain) {
+      if (error is UsageException) {
         stderr.writeln(error.message);
-      if (verbose) {
         stderr.writeln();
-        stderr.writeln(chain.terse.toString());
-        stderr.writeln();
-      }
-      _exit(error.exitCode ?? 1);
-    } else if (error is ProcessExit) {
-      // We've caught an exit code.
-      _exit(error.exitCode);
-    } else {
-      // We've crashed; emit a log report.
-      stderr.writeln();
-
-      flutterUsage.sendException(error, chain);
-
-      if (isRunningOnBot) {
-        // Print the stack trace on the bots - don't write a crash report.
-        stderr.writeln('$error');
-        stderr.writeln(chain.terse.toString());
-        _exit(1);
+        stderr.writeln(
+            "Run 'flutter -h' (or 'flutter <command> -h') for available "
+                "flutter commands and options."
+        );
+        // Argument error exit code.
+        _exit(64);
+      } else if (error is ToolExit) {
+        if (error.message != null)
+          stderr.writeln(error.message);
+        if (verbose) {
+          stderr.writeln();
+          stderr.writeln(chain.terse.toString());
+          stderr.writeln();
+        }
+        _exit(error.exitCode ?? 1);
+      } else if (error is ProcessExit) {
+        // We've caught an exit code.
+        _exit(error.exitCode);
       } else {
-        if (error is String)
-          stderr.writeln('Oops; flutter has exited unexpectedly: "$error".');
-        else
-          stderr.writeln('Oops; flutter has exited unexpectedly.');
+        // We've crashed; emit a log report.
+        stderr.writeln();
 
-        _createCrashReport(args, error, chain).then((File file) {
-          stderr.writeln(
-              'Crash report written to ${file.path};\n'
-              'please let us know at https://github.com/flutter/flutter/issues.'
-          );
+        flutterUsage.sendException(error, chain);
+
+        if (isRunningOnBot) {
+          // Print the stack trace on the bots - don't write a crash report.
+          stderr.writeln('$error');
+          stderr.writeln(chain.terse.toString());
           _exit(1);
-        });
+        } else {
+          if (error is String)
+            stderr.writeln('Oops; flutter has exited unexpectedly: "$error".');
+          else
+            stderr.writeln('Oops; flutter has exited unexpectedly.');
+
+          _createCrashReport(args, error, chain).then((File file) {
+            stderr.writeln(
+                'Crash report written to ${file.path};\n'
+                    'please let us know at https://github.com/flutter/flutter/issues.'
+            );
+            _exit(1);
+          });
+        }
       }
-    }
+    });
   });
 }
 
diff --git a/packages/flutter_tools/lib/src/android/android_sdk.dart b/packages/flutter_tools/lib/src/android/android_sdk.dart
index 42ab963..caf9686 100644
--- a/packages/flutter_tools/lib/src/android/android_sdk.dart
+++ b/packages/flutter_tools/lib/src/android/android_sdk.dart
@@ -8,9 +8,12 @@
 import 'package:pub_semver/pub_semver.dart';
 
 import '../base/common.dart';
+import '../base/context.dart';
 import '../base/os.dart';
 import '../globals.dart';
 
+AndroidSdk get androidSdk => context[AndroidSdk];
+
 const String kAndroidHome = 'ANDROID_HOME';
 
 // Android SDK layout:
diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart
index d7a68cf..5ed8341 100644
--- a/packages/flutter_tools/lib/src/application_package.dart
+++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -7,6 +7,7 @@
 import 'package:path/path.dart' as path;
 import 'package:xml/xml.dart' as xml;
 
+import 'android/android_sdk.dart';
 import 'android/gradle.dart';
 import 'base/os.dart' show os;
 import 'base/process.dart';
diff --git a/packages/flutter_tools/lib/src/base/context.dart b/packages/flutter_tools/lib/src/base/context.dart
index 12d9156..3cb1189 100644
--- a/packages/flutter_tools/lib/src/base/context.dart
+++ b/packages/flutter_tools/lib/src/base/context.dart
@@ -4,16 +4,11 @@
 
 import 'dart:async';
 
-final AppContext _defaultContext = new AppContext();
-
 typedef void ErrorHandler(dynamic error, StackTrace stackTrace);
 
 /// A singleton for application functionality. This singleton can be different
 /// on a per-Zone basis.
-AppContext get context {
-  AppContext currentContext = Zone.current['context'];
-  return currentContext == null ? _defaultContext : currentContext;
-}
+AppContext get context => Zone.current['context'];
 
 class AppContext {
   Map<Type, dynamic> _instances = <Type, dynamic>{};
@@ -44,18 +39,15 @@
   void operator[]=(Type type, dynamic instance) => setVariable(type, instance);
 
   AppContext _calcParent(Zone zone) {
-    if (this == _defaultContext)
-      return null;
-
     Zone parentZone = zone.parent;
     if (parentZone == null)
-      return _defaultContext;
+      return null;
 
     AppContext deps = parentZone['context'];
     if (deps == this) {
       return _calcParent(parentZone);
     } else {
-      return deps != null ? deps : _defaultContext;
+      return deps;
     }
   }
 
diff --git a/packages/flutter_tools/lib/src/base/os.dart b/packages/flutter_tools/lib/src/base/os.dart
index 9d54555..9a4290a 100644
--- a/packages/flutter_tools/lib/src/base/os.dart
+++ b/packages/flutter_tools/lib/src/base/os.dart
@@ -13,11 +13,11 @@
 
 /// Returns [OperatingSystemUtils] active in the current app context (i.e. zone).
 OperatingSystemUtils get os {
-  return context[OperatingSystemUtils] ?? (context[OperatingSystemUtils] = new OperatingSystemUtils._());
+  return context[OperatingSystemUtils] ?? (context[OperatingSystemUtils] = new OperatingSystemUtils());
 }
 
 abstract class OperatingSystemUtils {
-  factory OperatingSystemUtils._() {
+  factory OperatingSystemUtils() {
     if (Platform.isWindows) {
       return new _WindowsUtils();
     } else {
diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart
index 45e2c62..bcadae2 100644
--- a/packages/flutter_tools/lib/src/build_info.dart
+++ b/packages/flutter_tools/lib/src/build_info.dart
@@ -6,6 +6,7 @@
 
 import 'package:path/path.dart' as path;
 
+import 'base/context.dart';
 import 'base/utils.dart';
 import 'globals.dart';
 
@@ -118,6 +119,11 @@
 
 /// Returns the top-level build output directory.
 String getBuildDirectory() {
+  // TODO(johnmccutchan): Stop calling this function as part of setting
+  // up command line argument processing.
+  if (context == null)
+    return 'build';
+
   String buildDir = config.getValue('build-dir') ?? 'build';
   if (path.isAbsolute(buildDir)) {
     throw new Exception(
diff --git a/packages/flutter_tools/lib/src/commands/config.dart b/packages/flutter_tools/lib/src/commands/config.dart
index af16a39..cc1340d 100644
--- a/packages/flutter_tools/lib/src/commands/config.dart
+++ b/packages/flutter_tools/lib/src/commands/config.dart
@@ -5,6 +5,7 @@
 import 'dart:async';
 
 import '../globals.dart';
+import '../usage.dart';
 import '../runner/flutter_command.dart';
 
 class ConfigCommand extends FlutterCommand {
diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart
index 7676e8c..3d21f16 100644
--- a/packages/flutter_tools/lib/src/commands/create.dart
+++ b/packages/flutter_tools/lib/src/commands/create.dart
@@ -12,6 +12,7 @@
 import '../base/utils.dart';
 import '../cache.dart';
 import '../dart/pub.dart';
+import '../doctor.dart';
 import '../globals.dart';
 import '../runner/flutter_command.dart';
 import '../template.dart';
diff --git a/packages/flutter_tools/lib/src/commands/devices.dart b/packages/flutter_tools/lib/src/commands/devices.dart
index 687a3ac..4547ade 100644
--- a/packages/flutter_tools/lib/src/commands/devices.dart
+++ b/packages/flutter_tools/lib/src/commands/devices.dart
@@ -7,6 +7,7 @@
 import '../base/common.dart';
 import '../base/utils.dart';
 import '../device.dart';
+import '../doctor.dart';
 import '../globals.dart';
 import '../runner/flutter_command.dart';
 
diff --git a/packages/flutter_tools/lib/src/commands/doctor.dart b/packages/flutter_tools/lib/src/commands/doctor.dart
index f1a6a17..df4da8c 100644
--- a/packages/flutter_tools/lib/src/commands/doctor.dart
+++ b/packages/flutter_tools/lib/src/commands/doctor.dart
@@ -4,7 +4,7 @@
 
 import 'dart:async';
 
-import '../globals.dart';
+import '../doctor.dart';
 import '../runner/flutter_command.dart';
 
 class DoctorCommand extends FlutterCommand {
diff --git a/packages/flutter_tools/lib/src/commands/setup.dart b/packages/flutter_tools/lib/src/commands/setup.dart
index 4f3b0cf..afc2326 100644
--- a/packages/flutter_tools/lib/src/commands/setup.dart
+++ b/packages/flutter_tools/lib/src/commands/setup.dart
@@ -7,6 +7,7 @@
 import '../base/common.dart';
 import '../base/os.dart';
 import '../base/process.dart';
+import '../doctor.dart';
 import '../globals.dart';
 import '../runner/flutter_command.dart';
 
diff --git a/packages/flutter_tools/lib/src/commands/upgrade.dart b/packages/flutter_tools/lib/src/commands/upgrade.dart
index 2e5c8d0..21295fd 100644
--- a/packages/flutter_tools/lib/src/commands/upgrade.dart
+++ b/packages/flutter_tools/lib/src/commands/upgrade.dart
@@ -7,9 +7,10 @@
 import '../base/common.dart';
 import '../base/os.dart';
 import '../base/process.dart';
+import '../cache.dart';
 import '../dart/pub.dart';
 import '../dart/summary.dart';
-import '../cache.dart';
+import '../doctor.dart';
 import '../globals.dart';
 import '../runner/flutter_command.dart';
 import '../version.dart';
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index 1f9e7a2..67354df 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -9,6 +9,7 @@
 import 'android/android_device.dart';
 import 'application_package.dart';
 import 'base/common.dart';
+import 'base/context.dart';
 import 'base/os.dart';
 import 'base/utils.dart';
 import 'build_info.dart';
@@ -16,6 +17,8 @@
 import 'ios/devices.dart';
 import 'ios/simulators.dart';
 
+DeviceManager get deviceManager => context[DeviceManager];
+
 /// A class to get all available devices.
 class DeviceManager {
   /// Constructing DeviceManagers is cheap; they only do expensive work if some
diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart
index 6161d87..fb97905 100644
--- a/packages/flutter_tools/lib/src/doctor.dart
+++ b/packages/flutter_tools/lib/src/doctor.dart
@@ -9,11 +9,14 @@
 
 import 'android/android_workflow.dart';
 import 'base/common.dart';
+import 'base/context.dart';
 import 'device.dart';
 import 'globals.dart';
 import 'ios/ios_workflow.dart';
 import 'version.dart';
 
+Doctor get doctor => context[Doctor];
+
 const Map<String, String> _osNames = const <String, String>{
   'macos': 'Mac OS',
   'linux': 'Linux',
diff --git a/packages/flutter_tools/lib/src/globals.dart b/packages/flutter_tools/lib/src/globals.dart
index 0d7f4f9..1ca818b 100644
--- a/packages/flutter_tools/lib/src/globals.dart
+++ b/packages/flutter_tools/lib/src/globals.dart
@@ -2,24 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'android/android_sdk.dart';
 import 'base/config.dart';
 import 'base/context.dart';
 import 'base/logger.dart';
 import 'cache.dart';
-import 'device.dart';
-import 'doctor.dart';
 import 'toolchain.dart';
-import 'usage.dart';
 
-DeviceManager get deviceManager => context[DeviceManager];
 Logger get logger => context[Logger];
-AndroidSdk get androidSdk => context[AndroidSdk];
 Cache get cache => Cache.instance;
 Config get config => Config.instance;
-Doctor get doctor => context[Doctor];
 ToolConfiguration get tools => ToolConfiguration.instance;
-Usage get flutterUsage => Usage.instance;
 
 /// Display an error level message to the user. Commands should use this if they
 /// fail in some way.
diff --git a/packages/flutter_tools/lib/src/hot.dart b/packages/flutter_tools/lib/src/hot.dart
index 4b3fa80..ec4f3ef 100644
--- a/packages/flutter_tools/lib/src/hot.dart
+++ b/packages/flutter_tools/lib/src/hot.dart
@@ -24,6 +24,7 @@
 import 'resident_runner.dart';
 import 'toolchain.dart';
 import 'vmservice.dart';
+import 'usage.dart';
 
 class HotRunnerConfig {
   /// Should the hot runner compute the minimal Dart dependencies?
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart
index 9970159..d2aa790 100644
--- a/packages/flutter_tools/lib/src/ios/devices.dart
+++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -11,6 +11,7 @@
 import '../base/process.dart';
 import '../build_info.dart';
 import '../device.dart';
+import '../doctor.dart';
 import '../globals.dart';
 import '../protocol_discovery.dart';
 import 'mac.dart';
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index e38c53c..747bb1e 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -14,6 +14,7 @@
 import '../dart/package_map.dart';
 import '../dart/pub.dart';
 import '../device.dart';
+import '../doctor.dart';
 import '../flx.dart' as flx;
 import '../globals.dart';
 import '../usage.dart';
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
index 8edc60b..f023058 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
@@ -15,8 +15,10 @@
 import '../base/process.dart';
 import '../cache.dart';
 import '../dart/package_map.dart';
+import '../device.dart';
 import '../globals.dart';
 import '../toolchain.dart';
+import '../usage.dart';
 import '../version.dart';
 
 const String kFlutterRootEnvironmentVariableName = 'FLUTTER_ROOT'; // should point to //flutter/ (root of flutter/flutter repo)
diff --git a/packages/flutter_tools/lib/src/services.dart b/packages/flutter_tools/lib/src/services.dart
index 91ae97a1..824dd14 100644
--- a/packages/flutter_tools/lib/src/services.dart
+++ b/packages/flutter_tools/lib/src/services.dart
@@ -10,6 +10,7 @@
 import 'package:yaml/yaml.dart';
 
 import 'dart/package_map.dart';
+import 'android/android_sdk.dart';
 import 'globals.dart';
 
 const String _kFlutterManifestPath = 'flutter.yaml';
diff --git a/packages/flutter_tools/lib/src/usage.dart b/packages/flutter_tools/lib/src/usage.dart
index cca4a7f..503a0b7 100644
--- a/packages/flutter_tools/lib/src/usage.dart
+++ b/packages/flutter_tools/lib/src/usage.dart
@@ -16,6 +16,8 @@
 
 const String _kFlutterUA = 'UA-67589403-6';
 
+Usage get flutterUsage => Usage.instance;
+
 class Usage {
   /// Create a new Usage instance; [versionOverride] is used for testing.
   Usage({ String settingsName: 'flutter', String versionOverride }) {
diff --git a/packages/flutter_tools/test/analytics_test.dart b/packages/flutter_tools/test/analytics_test.dart
index 789d7fc..99c3108 100644
--- a/packages/flutter_tools/test/analytics_test.dart
+++ b/packages/flutter_tools/test/analytics_test.dart
@@ -9,7 +9,6 @@
 import 'package:flutter_tools/src/commands/create.dart';
 import 'package:flutter_tools/src/commands/config.dart';
 import 'package:flutter_tools/src/commands/doctor.dart';
-import 'package:flutter_tools/src/globals.dart';
 import 'package:flutter_tools/src/usage.dart';
 import 'package:test/test.dart';
 
@@ -19,16 +18,13 @@
 void main() {
   group('analytics', () {
     Directory temp;
-    bool wasEnabled;
 
     setUp(() {
       Cache.flutterRoot = '../..';
-      wasEnabled = flutterUsage.enabled;
       temp = Directory.systemTemp.createTempSync('flutter_tools');
     });
 
     tearDown(() {
-      flutterUsage.enabled = wasEnabled;
       temp.deleteSync(recursive: true);
     });
 
diff --git a/packages/flutter_tools/test/analyze_duplicate_names_test.dart b/packages/flutter_tools/test/analyze_duplicate_names_test.dart
index 7fbe33e..73cd4f5 100644
--- a/packages/flutter_tools/test/analyze_duplicate_names_test.dart
+++ b/packages/flutter_tools/test/analyze_duplicate_names_test.dart
@@ -4,7 +4,6 @@
 
 import 'dart:io';
 
-import 'package:flutter_tools/src/base/os.dart';
 import 'package:flutter_tools/src/commands/analyze.dart';
 import 'package:path/path.dart' as path;
 import 'package:test/test.dart';
@@ -26,7 +25,6 @@
 
   group('analyze', () {
     testUsingContext('flutter analyze with two files with the same name', () async {
-
       File dartFileA = new File(path.join(tempDir.path, 'a.dart'));
       dartFileA.parent.createSync();
       dartFileA.writeAsStringSync('library test;');
@@ -41,8 +39,6 @@
         expect(testLogger.statusText, startsWith('Analyzing 2 files...\nNo analyzer warnings!'));
       });
 
-    }, overrides: <Type, dynamic>{
-      OperatingSystemUtils: os
     });
   });
 }
diff --git a/packages/flutter_tools/test/os_utils_test.dart b/packages/flutter_tools/test/os_utils_test.dart
index 87a7c28..ac37d56 100644
--- a/packages/flutter_tools/test/os_utils_test.dart
+++ b/packages/flutter_tools/test/os_utils_test.dart
@@ -4,6 +4,8 @@
 
 import 'dart:io';
 
+import 'src/context.dart';
+
 import 'package:flutter_tools/src/base/os.dart';
 import 'package:path/path.dart' as path;
 import 'package:test/test.dart';
@@ -20,7 +22,7 @@
       temp.deleteSync(recursive: true);
     });
 
-    test('makeExecutable', () {
+    testUsingContext('makeExecutable', () async {
       File file = new File(path.join(temp.path, 'foo.script'));
       file.writeAsStringSync('hello world');
       os.makeExecutable(file);
@@ -31,6 +33,8 @@
         // rwxr--r--
         expect(mode.substring(0, 3), endsWith('x'));
       }
+    }, overrides: <Type, dynamic> {
+      OperatingSystemUtils: new OperatingSystemUtils(),
     });
   });
 }