send the base uri back to debuggers (#5321)

* send the base uri back to debuggers

* add a fullRestart parameter to app.restart

* add await
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart
index d72a05f..36aeef2 100644
--- a/packages/flutter_tools/lib/src/commands/daemon.dart
+++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -339,25 +339,30 @@
       );
     }
 
+    bool supportsRestart = hotMode ? device.supportsHotMode : device.supportsRestart;
+
     AppInstance app = new AppInstance(_getNextAppId(), runner);
     _apps.add(app);
     _sendAppEvent(app, 'start', <String, dynamic>{
       'deviceId': deviceId,
       'directory': projectDirectory,
-      'supportsRestart': device.supportsRestart && hotMode
+      'supportsRestart': supportsRestart
     });
 
-    Completer<int> observatoryPortCompleter;
+    Completer<DebugConnectionInfo> connectionInfoCompleter;
 
     if (options.debuggingEnabled) {
-      observatoryPortCompleter = new Completer<int>();
-      observatoryPortCompleter.future.then((int port) {
-        _sendAppEvent(app, 'debugPort', <String, dynamic>{ 'port': port });
+      connectionInfoCompleter = new Completer<DebugConnectionInfo>();
+      connectionInfoCompleter.future.then((DebugConnectionInfo info) {
+        Map<String, dynamic> params = <String, dynamic>{ 'port': info.port };
+        if (info.baseUri != null)
+          params['baseUri'] = info.baseUri;
+        _sendAppEvent(app, 'debugPort', params);
       });
     }
 
     app._runInZone(this, () {
-      runner.run(observatoryPortCompleter: observatoryPortCompleter, route: route).then((_) {
+      runner.run(connectionInfoCompleter: connectionInfoCompleter, route: route).then((_) {
         _sendAppEvent(app, 'stop');
       }).catchError((dynamic error) {
         _sendAppEvent(app, 'stop', <String, dynamic>{ 'error' : error.toString() });
@@ -371,19 +376,20 @@
       'appId': app.id,
       'deviceId': deviceId,
       'directory': projectDirectory,
-      'supportsRestart': device.supportsRestart
+      'supportsRestart': supportsRestart
     };
   }
 
   Future<bool> restart(Map<String, dynamic> args) async {
     String appId = _getStringArg(args, 'appId', required: true);
+    bool fullRestart = _getBoolArg(args, 'fullRestart') ?? false;
 
     AppInstance app = _getApp(appId);
     if (app == null)
       throw "app '$appId' not found";
 
     return app._runInZone(this, () {
-      return app.restart();
+      return app.restart(fullRestart: fullRestart);
     });
   }
 
@@ -584,7 +590,9 @@
 
   _AppRunLogger _logger;
 
-  Future<bool> restart() => runner.restart();
+  Future<bool> restart({ bool fullRestart: false }) {
+    return runner.restart(fullRestart: fullRestart);
+  }
 
   Future<Null> stop() => runner.stop();
 
diff --git a/packages/flutter_tools/lib/src/hot.dart b/packages/flutter_tools/lib/src/hot.dart
index 56f5de9..932aca6 100644
--- a/packages/flutter_tools/lib/src/hot.dart
+++ b/packages/flutter_tools/lib/src/hot.dart
@@ -162,14 +162,14 @@
 
   @override
   Future<int> run({
-    Completer<int> observatoryPortCompleter,
+    Completer<DebugConnectionInfo> connectionInfoCompleter,
     String route,
     bool shouldBuild: true
   }) {
     // Don't let uncaught errors kill the process.
     return runZoned(() {
       return _run(
-        observatoryPortCompleter: observatoryPortCompleter,
+        connectionInfoCompleter: connectionInfoCompleter,
         route: route,
         shouldBuild: shouldBuild
       );
@@ -179,7 +179,7 @@
   }
 
   Future<int> _run({
-    Completer<int> observatoryPortCompleter,
+    Completer<DebugConnectionInfo> connectionInfoCompleter,
     String route,
     bool shouldBuild: true
   }) async {
@@ -275,19 +275,28 @@
       return 2;
     }
 
-    if (observatoryPortCompleter != null && result.hasObservatory)
-      observatoryPortCompleter.complete(result.observatoryPort);
-
     await connectToServiceProtocol(result.observatoryPort);
 
     if (device.needsDevFS) {
+      try {
+        Uri baseUri = await _initDevFS();
+        if (connectionInfoCompleter != null) {
+          connectionInfoCompleter.complete(
+            new DebugConnectionInfo(result.observatoryPort, baseUri: baseUri.toString())
+          );
+        }
+      } catch (error) {
+        printError('Error initializing DevFS: $error');
+        return 3;
+      }
       _loaderShowMessage('Connecting...', progress: 0);
-      bool result = await _updateDevFS(
+      bool devfsResult = await _updateDevFS(
         progressReporter: (int progress, int max) {
-          _loaderShowMessage('Syncing files to device...', progress: progress, max: max);
+          if (progress % 10 == 0)
+            _loaderShowMessage('Syncing files to device...', progress: progress, max: max);
         }
       );
-      if (!result) {
+      if (!devfsResult) {
         _loaderShowMessage('Failed.');
         printError('Could not perform initial file synchronization.');
         return 3;
@@ -295,6 +304,9 @@
       printStatus('Running ${getDisplayPath(_mainPath)} on ${device.name}...');
       _loaderShowMessage('Launching...');
       await _launchFromDevFS(_package, _mainPath);
+    } else {
+      if (connectionInfoCompleter != null)
+        connectionInfoCompleter.complete(new DebugConnectionInfo(result.observatoryPort));
     }
 
     _startReadingFromControlPipe();
@@ -339,22 +351,16 @@
   }
 
   DevFS _devFS;
+
+  Future<Uri> _initDevFS() {
+    String fsName = path.basename(_projectRootPath);
+    _devFS = new DevFS(serviceProtocol,
+                       fsName,
+                       new Directory(_projectRootPath));
+    return _devFS.create();
+  }
+
   Future<bool> _updateDevFS({ DevFSProgressReporter progressReporter }) async {
-    if (_devFS == null) {
-      String fsName = path.basename(_projectRootPath);
-      _devFS = new DevFS(serviceProtocol,
-                         fsName,
-                         new Directory(_projectRootPath));
-
-      try {
-        await _devFS.create();
-      } catch (error) {
-        _devFS = null;
-        printError('Error initializing DevFS: $error');
-        return false;
-      }
-    }
-
     final bool rebuildBundle = bundle.needsBuild();
     if (rebuildBundle) {
       Status bundleStatus = logger.startProgress('Updating assets...');
@@ -484,7 +490,14 @@
   }
 
   @override
-  Future<bool> restart() => _reloadSources();
+  Future<bool> restart({ bool fullRestart: false }) async {
+    if (fullRestart) {
+      await _restartFromSources();
+      return true;
+    } else {
+      return _reloadSources();
+    }
+  }
 
   Future<bool> _reloadSources() async {
     if (serviceProtocol.firstIsolateId == null)
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index 8ce55ac..0eb5aea 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -32,12 +32,12 @@
 
   /// Start the app and keep the process running during its lifetime.
   Future<int> run({
-    Completer<int> observatoryPortCompleter,
+    Completer<DebugConnectionInfo> connectionInfoCompleter,
     String route,
     bool shouldBuild: true
   });
 
-  Future<bool> restart();
+  Future<bool> restart({ bool fullRestart: false });
 
   Future<Null> stop() async {
     await stopEchoingDeviceLog();
@@ -208,3 +208,10 @@
       return null;
   }
 }
+
+class DebugConnectionInfo {
+  DebugConnectionInfo(this.port, { this.baseUri });
+
+  final int port;
+  final String baseUri;
+}
diff --git a/packages/flutter_tools/lib/src/run.dart b/packages/flutter_tools/lib/src/run.dart
index 7108ef1..4764905 100644
--- a/packages/flutter_tools/lib/src/run.dart
+++ b/packages/flutter_tools/lib/src/run.dart
@@ -39,7 +39,7 @@
 
   @override
   Future<int> run({
-    Completer<int> observatoryPortCompleter,
+    Completer<DebugConnectionInfo> connectionInfoCompleter,
     String route,
     bool shouldBuild: true
   }) {
@@ -48,7 +48,7 @@
       return _run(
         traceStartup: traceStartup,
         benchmark: benchmark,
-        observatoryPortCompleter: observatoryPortCompleter,
+        connectionInfoCompleter: connectionInfoCompleter,
         route: route,
         shouldBuild: shouldBuild
       );
@@ -58,7 +58,7 @@
   }
 
   @override
-  Future<bool> restart() async {
+  Future<bool> restart({ bool fullRestart: false }) async {
     if (serviceProtocol == null) {
       printError('Debugging is not enabled.');
       return false;
@@ -94,7 +94,7 @@
   Future<int> _run({
     bool traceStartup: false,
     bool benchmark: false,
-    Completer<int> observatoryPortCompleter,
+    Completer<DebugConnectionInfo> connectionInfoCompleter,
     String route,
     bool shouldBuild: true
   }) async {
@@ -175,8 +175,8 @@
 
     startTime.stop();
 
-    if (observatoryPortCompleter != null && _result.hasObservatory)
-      observatoryPortCompleter.complete(_result.observatoryPort);
+    if (connectionInfoCompleter != null && _result.hasObservatory)
+      connectionInfoCompleter.complete(new DebugConnectionInfo(_result.observatoryPort));
 
     // Connect to observatory.
     if (debuggingOptions.debuggingEnabled) {