Version 2.10.0-147.0.dev

Merge commit '51840c501aafa8cea4e4de5fabedf85dc9a4d664' into 'dev'
diff --git a/DEPS b/DEPS
index d96c4d0..8e76008 100644
--- a/DEPS
+++ b/DEPS
@@ -114,7 +114,7 @@
   "jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
   "json_rpc_2_rev": "8f189db8f0c299187a0e8fa959dba7e9b0254be5",
   "linter_tag": "0.1.119",
-  "logging_rev": "9561ba016ae607747ae69b846c0e10958ca58ed4",
+  "logging_rev": "1590ba0b648a51e7eb3895c612e4b72f72623b6f",
   "markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
   "markdown_rev": "dbeafd47759e7dd0a167602153bb9c49fb5e5fe7",
   "matcher_rev": "9cae8faa7868bf3a88a7ba45eb0bd128e66ac515",
diff --git a/pkg/analyzer/lib/dart/element/element.dart b/pkg/analyzer/lib/dart/element/element.dart
index 71b8251..9e50585 100644
--- a/pkg/analyzer/lib/dart/element/element.dart
+++ b/pkg/analyzer/lib/dart/element/element.dart
@@ -534,6 +534,9 @@
   /// Return `true` if this element has an annotation of the form `@factory`.
   bool get hasFactory;
 
+  /// Return `true` if this element has an annotation of the form `@internal`.
+  bool get hasInternal;
+
   /// Return `true` if this element has an annotation of the form `@isTest`.
   bool get hasIsTest;
 
@@ -717,6 +720,10 @@
   /// subclasses as being immutable.
   bool get isImmutable;
 
+  /// Return `true` if this annotation marks the associated element as being
+  /// internal to its package.
+  bool get isInternal;
+
   /// Return `true` if this annotation marks the associated member as running
   /// a single test.
   bool get isIsTest;
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 4f13b90..9312ea8 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -492,10 +492,12 @@
   HintCode.INFERENCE_FAILURE_ON_UNINITIALIZED_VARIABLE,
   HintCode.INFERENCE_FAILURE_ON_UNTYPED_PARAMETER,
   HintCode.INVALID_ANNOTATION_TARGET,
+  HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT,
   HintCode.INVALID_FACTORY_ANNOTATION,
   HintCode.INVALID_FACTORY_METHOD_DECL,
   HintCode.INVALID_FACTORY_METHOD_IMPL,
   HintCode.INVALID_IMMUTABLE_ANNOTATION,
+  HintCode.INVALID_INTERNAL_ANNOTATION,
   HintCode.INVALID_LANGUAGE_VERSION_OVERRIDE_AT_SIGN,
   HintCode.INVALID_LANGUAGE_VERSION_OVERRIDE_EQUALS,
   HintCode.INVALID_LANGUAGE_VERSION_OVERRIDE_GREATER,
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index ecf68fe..63df85d 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -2313,6 +2313,10 @@
   /// as being immutable.
   static const String _IMMUTABLE_VARIABLE_NAME = "immutable";
 
+  /// The name of the top-level variable used to mark an element as being
+  /// internal to its package.
+  static const String _INTERNAL_VARIABLE_NAME = "internal";
+
   /// The name of the top-level variable used to mark a constructor as being
   /// literal.
   static const String _LITERAL_VARIABLE_NAME = "literal";
@@ -2460,6 +2464,12 @@
       element.library?.name == _META_LIB_NAME;
 
   @override
+  bool get isInternal =>
+      element is PropertyAccessorElement &&
+      element.name == _INTERNAL_VARIABLE_NAME &&
+      element.library?.name == _META_LIB_NAME;
+
+  @override
   bool get isIsTest =>
       element is PropertyAccessorElement &&
       element.name == _IS_TEST_VARIABLE_NAME &&
@@ -2748,6 +2758,18 @@
   }
 
   @override
+  bool get hasInternal {
+    var metadata = this.metadata;
+    for (var i = 0; i < metadata.length; i++) {
+      var annotation = metadata[i];
+      if (annotation.isInternal) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @override
   bool get hasIsTest {
     var metadata = this.metadata;
     for (var i = 0; i < metadata.length; i++) {
@@ -5996,6 +6018,9 @@
   bool get hasFactory => false;
 
   @override
+  bool get hasInternal => false;
+
+  @override
   bool get hasIsTest => false;
 
   @override
diff --git a/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart b/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart
index 641f80f..d6dd68a 100644
--- a/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart
+++ b/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart
@@ -14,7 +14,6 @@
 import 'package:analyzer/src/dart/element/type.dart';
 import 'package:analyzer/src/dart/element/type_algebra.dart';
 import 'package:analyzer/src/dart/element/type_constraint_gatherer.dart';
-import 'package:analyzer/src/dart/element/type_demotion.dart';
 import 'package:analyzer/src/dart/element/type_schema.dart';
 import 'package:analyzer/src/dart/element/type_system.dart';
 import 'package:analyzer/src/error/codes.dart'
@@ -477,7 +476,7 @@
   void _nonNullifyTypes(List<DartType> types) {
     if (_typeSystem.isNonNullableByDefault) {
       for (var i = 0; i < types.length; i++) {
-        types[i] = nonNullifyType(_typeSystem, types[i]);
+        types[i] = _typeSystem.nonNullifyLegacy(types[i]);
       }
     }
   }
diff --git a/pkg/analyzer/lib/src/dart/element/member.dart b/pkg/analyzer/lib/src/dart/element/member.dart
index a37c9d4..5c08ec5 100644
--- a/pkg/analyzer/lib/src/dart/element/member.dart
+++ b/pkg/analyzer/lib/src/dart/element/member.dart
@@ -460,6 +460,9 @@
   bool get hasFactory => _declaration.hasFactory;
 
   @override
+  bool get hasInternal => _declaration.hasInternal;
+
+  @override
   bool get hasIsTest => _declaration.hasIsTest;
 
   @override
diff --git a/pkg/analyzer/lib/src/dart/element/type_demotion.dart b/pkg/analyzer/lib/src/dart/element/type_demotion.dart
index 581f22b..37757e7 100644
--- a/pkg/analyzer/lib/src/dart/element/type_demotion.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_demotion.dart
@@ -8,17 +8,16 @@
 import 'package:analyzer/dart/element/type_visitor.dart';
 import 'package:analyzer/src/dart/element/replacement_visitor.dart';
 import 'package:analyzer/src/dart/element/type.dart';
-import 'package:analyzer/src/dart/element/type_system.dart';
 
 /// Returns [type] in which all promoted type variables have been replace with
 /// their unpromoted equivalents, and, if [library] is non-nullable by default,
 /// replaces all legacy types with their non-nullable equivalents.
 DartType demoteType(LibraryElement library, DartType type) {
   if (library.isNonNullableByDefault) {
-    var visitor = const _DemotionNonNullification();
+    var visitor = const DemotionNonNullificationVisitor();
     return type.accept(visitor) ?? type;
   } else {
-    var visitor = const _DemotionNonNullification(nonNullifyTypes: false);
+    var visitor = const DemotionNonNullificationVisitor(nonNullifyTypes: false);
     return type.accept(visitor) ?? type;
   }
 }
@@ -30,25 +29,15 @@
   );
 }
 
-/// Returns [type] in which all legacy types have been replaced with
-/// non-nullable types.
-DartType nonNullifyType(TypeSystemImpl typeSystem, DartType type) {
-  if (typeSystem.isNonNullableByDefault && type != null) {
-    var visitor = const _DemotionNonNullification(demoteTypeVariables: false);
-    return type.accept(visitor) ?? type;
-  }
-  return type;
-}
-
 /// Visitor that replaces all promoted type variables the type variable itself
 /// and/or replaces all legacy types with non-nullable types.
 ///
 /// The visitor returns `null` if the type wasn't changed.
-class _DemotionNonNullification extends ReplacementVisitor {
+class DemotionNonNullificationVisitor extends ReplacementVisitor {
   final bool demoteTypeVariables;
   final bool nonNullifyTypes;
 
-  const _DemotionNonNullification({
+  const DemotionNonNullificationVisitor({
     this.demoteTypeVariables = true,
     this.nonNullifyTypes = true,
   }) : assert(demoteTypeVariables || nonNullifyTypes);
diff --git a/pkg/analyzer/lib/src/dart/element/type_system.dart b/pkg/analyzer/lib/src/dart/element/type_system.dart
index e4c683a..bb8f69f 100644
--- a/pkg/analyzer/lib/src/dart/element/type_system.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_system.dart
@@ -25,6 +25,7 @@
 import 'package:analyzer/src/dart/element/top_merge.dart';
 import 'package:analyzer/src/dart/element/type.dart';
 import 'package:analyzer/src/dart/element/type_algebra.dart';
+import 'package:analyzer/src/dart/element/type_demotion.dart';
 import 'package:analyzer/src/dart/element/type_provider.dart';
 import 'package:analyzer/src/dart/element/type_schema.dart';
 import 'package:analyzer/src/dart/element/type_schema_elimination.dart';
@@ -1181,6 +1182,17 @@
     return inferredTypes;
   }
 
+  /// Replace legacy types in [type] with non-nullable types.
+  DartType nonNullifyLegacy(DartType type) {
+    if (isNonNullableByDefault && type != null) {
+      var visitor = const DemotionNonNullificationVisitor(
+        demoteTypeVariables: false,
+      );
+      return type.accept(visitor) ?? type;
+    }
+    return type;
+  }
+
   /// Compute the canonical representation of [T].
   ///
   /// https://github.com/dart-lang/language
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
index 9d9c4eb..9be0aa2 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
@@ -577,6 +577,19 @@
       "The annotation '{0}' can only be used on {1}");
 
   /**
+   * This hint is generated anywhere where an element annotated with `@internal`
+   * is exported as a part of a package's public API.
+   *
+   * Parameters:
+   * 0: the name of the element
+   */
+  static const HintCode INVALID_EXPORT_OF_INTERNAL_ELEMENT = HintCode(
+      'INVALID_EXPORT_OF_INTERNAL_ELEMENT',
+      "The member '{0}' can't be exported as a part of a package's public "
+          "API.",
+      correction: "Try using a hide clause to hide '{0}'.");
+
+  /**
    * This hint is generated anywhere a @factory annotation is associated with
    * anything other than a method.
    */
@@ -612,6 +625,15 @@
       'INVALID_IMMUTABLE_ANNOTATION',
       "Only classes can be annotated as being immutable.");
 
+  /**
+   * This hint is generated anywhere a @internal annotation is associated with
+   * an element found in a package's public API.
+   */
+  static const HintCode INVALID_INTERNAL_ANNOTATION = HintCode(
+      'INVALID_INTERNAL_ANNOTATION',
+      "Only public elements in a package's private API can be annotated as "
+          "being internal.");
+
   /// Invalid Dart language version comments don't follow the specification [1].
   /// If a comment begins with "@dart" or "dart" (letters in any case),
   /// followed by optional whitespace, followed by optional non-alphanumeric,
@@ -922,7 +944,7 @@
 
   /**
    * This hint is generated anywhere where a member annotated with `@protected`
-   * is used outside an instance member of a subclass.
+   * is used outside of an instance member of a subclass.
    *
    * Parameters:
    * 0: the name of the member
diff --git a/pkg/analyzer/lib/src/dart/resolver/body_inference_context.dart b/pkg/analyzer/lib/src/dart/resolver/body_inference_context.dart
index 6b5aaf4..b470871 100644
--- a/pkg/analyzer/lib/src/dart/resolver/body_inference_context.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/body_inference_context.dart
@@ -7,7 +7,6 @@
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/dart/element/type_provider.dart';
 import 'package:analyzer/src/dart/element/type.dart';
-import 'package:analyzer/src/dart/element/type_demotion.dart';
 import 'package:analyzer/src/dart/element/type_system.dart';
 import 'package:meta/meta.dart';
 
@@ -137,7 +136,7 @@
     }
 
     // Otherwise, let `S` be `R`.
-    return nonNullifyType(_typeSystem, R);
+    return _typeSystem.nonNullifyLegacy(R);
   }
 
   DartType _computeActualReturnedType({
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
index f496a13..0f3a2b1 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
@@ -7,7 +7,6 @@
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/type.dart';
-import 'package:analyzer/src/dart/element/type_demotion.dart';
 import 'package:analyzer/src/dart/element/type_system.dart';
 import 'package:analyzer/src/dart/resolver/body_inference_context.dart';
 import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
@@ -112,7 +111,7 @@
           inferredType = _migrationResolutionHooks.modifyInferredParameterType(
               p, inferredType);
         } else {
-          inferredType = nonNullifyType(_typeSystem, inferredType);
+          inferredType = _typeSystem.nonNullifyLegacy(inferredType);
         }
         if (!inferredType.isDynamic) {
           p.type = inferredType;
diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
index 24c9c85..5d65e55 100644
--- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart
+++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
@@ -21,6 +21,7 @@
 import 'package:analyzer/src/dart/element/type_system.dart';
 import 'package:analyzer/src/dart/resolver/body_inference_context.dart';
 import 'package:analyzer/src/dart/resolver/exit_detector.dart';
+import 'package:analyzer/src/dart/resolver/scope.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/constant.dart';
 import 'package:analyzer/src/generated/engine.dart';
@@ -113,6 +114,11 @@
     );
   }
 
+  bool get _inPublicPackageApi {
+    return _workspacePackage != null &&
+        _workspacePackage.sourceIsInPublicApi(_currentLibrary.source);
+  }
+
   @override
   void visitAnnotation(Annotation node) {
     ElementAnnotation element = node.elementAnnotation;
@@ -132,6 +138,35 @@
         _errorReporter.reportErrorForNode(
             HintCode.INVALID_IMMUTABLE_ANNOTATION, node, []);
       }
+    } else if (element.isInternal) {
+      var parentElement = parent is Declaration ? parent.declaredElement : null;
+      if (parent is TopLevelVariableDeclaration) {
+        for (VariableDeclaration variable in parent.variables.variables) {
+          if (Identifier.isPrivateName(variable.declaredElement.name)) {
+            _errorReporter.reportErrorForNode(
+                HintCode.INVALID_INTERNAL_ANNOTATION, variable, []);
+          }
+        }
+      } else if (parent is FieldDeclaration) {
+        for (VariableDeclaration variable in parent.fields.variables) {
+          if (Identifier.isPrivateName(variable.declaredElement.name)) {
+            _errorReporter.reportErrorForNode(
+                HintCode.INVALID_INTERNAL_ANNOTATION, variable, []);
+          }
+        }
+      } else if (parent is ConstructorDeclaration) {
+        var class_ = parent.declaredElement.enclosingElement;
+        if (class_.isPrivate || (parentElement?.isPrivate ?? false)) {
+          _errorReporter.reportErrorForNode(
+              HintCode.INVALID_INTERNAL_ANNOTATION, node, []);
+        }
+      } else if (parentElement?.isPrivate ?? false) {
+        _errorReporter
+            .reportErrorForNode(HintCode.INVALID_INTERNAL_ANNOTATION, node, []);
+      } else if (_inPublicPackageApi) {
+        _errorReporter
+            .reportErrorForNode(HintCode.INVALID_INTERNAL_ANNOTATION, node, []);
+      }
     } else if (element.isLiteral == true) {
       if (parent is! ConstructorDeclaration ||
           (parent as ConstructorDeclaration).constKeyword == null) {
@@ -319,6 +354,7 @@
   @override
   void visitExportDirective(ExportDirective node) {
     _checkForDeprecatedMemberUse(node.uriElement, node);
+    _checkForInternalExport(node);
     super.visitExportDirective(node);
   }
 
@@ -994,6 +1030,31 @@
     }
   }
 
+  /// Check that the namespace exported by [node] does not include any elements
+  /// annotated with `@internal`.
+  void _checkForInternalExport(ExportDirective node) {
+    if (!_inPublicPackageApi) return;
+
+    var libraryElement = node.uriElement;
+    if (libraryElement == null) return;
+    if (libraryElement.hasInternal) {
+      _errorReporter.reportErrorForNode(
+          HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT,
+          node,
+          [libraryElement.displayName]);
+    }
+    var exportNamespace =
+        NamespaceBuilder().createExportNamespaceForDirective(node.element);
+    exportNamespace.definedNames.forEach((String name, Element element) {
+      if (element.hasInternal) {
+        _errorReporter.reportErrorForNode(
+            HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT,
+            node,
+            [element.displayName]);
+      }
+    });
+  }
+
   void _checkForInvalidFactory(MethodDeclaration decl) {
     // Check declaration.
     // Note that null return types are expected to be flagged by other analyses.
diff --git a/pkg/analyzer/lib/src/summary2/top_level_inference.dart b/pkg/analyzer/lib/src/summary2/top_level_inference.dart
index 0918c70..6639055 100644
--- a/pkg/analyzer/lib/src/summary2/top_level_inference.dart
+++ b/pkg/analyzer/lib/src/summary2/top_level_inference.dart
@@ -9,7 +9,6 @@
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/type.dart';
-import 'package:analyzer/src/dart/element/type_demotion.dart';
 import 'package:analyzer/src/dart/element/type_system.dart';
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/summary/format.dart';
@@ -450,7 +449,7 @@
     }
 
     if (_typeSystem.isNonNullableByDefault) {
-      return nonNullifyType(_typeSystem, type);
+      return _typeSystem.nonNullifyLegacy(type);
     } else {
       if (type.isBottom) {
         return DynamicTypeImpl.instance;
diff --git a/pkg/analyzer/lib/src/task/strong_mode.dart b/pkg/analyzer/lib/src/task/strong_mode.dart
index fe58576..2f72a64 100644
--- a/pkg/analyzer/lib/src/task/strong_mode.dart
+++ b/pkg/analyzer/lib/src/task/strong_mode.dart
@@ -10,7 +10,6 @@
 import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
 import 'package:analyzer/src/dart/element/type.dart';
 import 'package:analyzer/src/dart/element/type_algebra.dart';
-import 'package:analyzer/src/dart/element/type_demotion.dart';
 import 'package:analyzer/src/dart/element/type_system.dart';
 import 'package:analyzer/src/summary/format.dart';
 import 'package:analyzer/src/summary/idl.dart';
@@ -141,7 +140,7 @@
       );
       if (combinedGetter != null) {
         var returnType = combinedGetter.returnType;
-        return nonNullifyType(typeSystem, returnType);
+        return typeSystem.nonNullifyLegacy(returnType);
       }
       return DynamicTypeImpl.instance;
     }
@@ -155,7 +154,7 @@
       );
       if (combinedSetter != null) {
         var type = combinedSetter.parameters[0].type;
-        return nonNullifyType(typeSystem, type);
+        return typeSystem.nonNullifyLegacy(type);
       }
       return DynamicTypeImpl.instance;
     }
@@ -282,7 +281,7 @@
 
           if (getterType == setterType) {
             var type = getterType;
-            type = nonNullifyType(typeSystem, type);
+            type = typeSystem.nonNullifyLegacy(type);
             field.type = type;
           } else {
             LazyAst.setTypeInferenceError(
@@ -443,7 +442,7 @@
     if (element.hasImplicitReturnType && element.displayName != '[]=') {
       if (combinedSignatureType != null) {
         var returnType = combinedSignatureType.returnType;
-        returnType = nonNullifyType(typeSystem, returnType);
+        returnType = typeSystem.nonNullifyLegacy(returnType);
         element.returnType = returnType;
       } else {
         element.returnType = DynamicTypeImpl.instance;
@@ -490,7 +489,7 @@
       );
       if (matchingParameter != null) {
         var type = matchingParameter.type;
-        type = nonNullifyType(typeSystem, type);
+        type = typeSystem.nonNullifyLegacy(type);
         parameter.type = type;
       } else {
         parameter.type = DynamicTypeImpl.instance;
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_packages.dart b/pkg/analyzer/lib/src/test_utilities/mock_packages.dart
index 2f054b5..9482e66 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_packages.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_packages.dart
@@ -28,6 +28,7 @@
 const _DoNotStore doNotStore = _DoNotStore();
 const _Factory factory = const _Factory();
 const Immutable immutable = const Immutable();
+const _Internal internal = const Internal();
 const _Literal literal = const _Literal();
 const _MustCallSuper mustCallSuper = const _MustCallSuper();
 const _NonVirtual nonVirtual = const _NonVirtual();
@@ -50,6 +51,9 @@
   final String reason;
   const Immutable([this.reason]);
 }
+class _Internal {
+  const Internal();
+}
 class _Literal {
   const _Literal();
 }
diff --git a/pkg/analyzer/lib/src/workspace/basic.dart b/pkg/analyzer/lib/src/workspace/basic.dart
index fb02a62..5f3be2a 100644
--- a/pkg/analyzer/lib/src/workspace/basic.dart
+++ b/pkg/analyzer/lib/src/workspace/basic.dart
@@ -80,4 +80,12 @@
   @override
   Map<String, List<Folder>> packagesAvailableTo(String libraryPath) =>
       workspace.packageMap;
+
+  @override
+  bool sourceIsInPublicApi(Source source) {
+    // Since every source file in a BasicPackage is in the same directory, they
+    // are all in the public API of the package. A file in a subdirectory
+    // is in a separate package.
+    return true;
+  }
 }
diff --git a/pkg/analyzer/lib/src/workspace/bazel.dart b/pkg/analyzer/lib/src/workspace/bazel.dart
index 0320187..2c474e6 100644
--- a/pkg/analyzer/lib/src/workspace/bazel.dart
+++ b/pkg/analyzer/lib/src/workspace/bazel.dart
@@ -260,6 +260,19 @@
   WorkspacePackage findPackageFor(String filePath) {
     path.Context context = provider.pathContext;
     Folder folder = provider.getFolder(context.dirname(filePath));
+    if (!context.isWithin(root, folder.path)) {
+      return null;
+    }
+
+    // Handle files which are given with their location in "bazel-bin", etc.
+    // This does not typically happen during usual analysis, but it still could,
+    // and it can come up in tests.
+    if ([genfiles, ...binPaths]
+        .any((binPath) => context.isWithin(binPath, folder.path))) {
+      var relative = context.relative(filePath, from: root);
+      return findPackageFor(
+          context.joinAll([root, ...context.split(relative).skip(1)]));
+    }
 
     while (true) {
       Folder parent = folder.parent;
@@ -300,7 +313,8 @@
       // [folder]'s path, relative to [root]. For example, "foo/bar".
       String relative = context.relative(folder.path, from: root);
       for (String bin in binPaths) {
-        Folder binChild = provider.getFolder(context.join(bin, relative));
+        Folder binChild =
+            provider.getFolder(context.normalize(context.join(bin, relative)));
         if (binChild.exists &&
             binChild.getChildren().any((c) => c.path.endsWith('.packages'))) {
           // [folder]'s sister folder within [bin] contains a ".packages" file.
@@ -548,4 +562,41 @@
   //  lists.
   Map<String, List<Folder>> packagesAvailableTo(String libraryPath) =>
       <String, List<Folder>>{};
+
+  @override
+  bool sourceIsInPublicApi(Source source) {
+    var filePath = filePathFromSource(source);
+    if (filePath == null) return false;
+
+    var libFolder = workspace.provider.pathContext.join(root, 'lib');
+    if (workspace.provider.pathContext.isWithin(libFolder, filePath)) {
+      // A file in "$root/lib" is public iff it is not in "$root/lib/src".
+      var libSrcFolder = workspace.provider.pathContext.join(libFolder, 'src');
+      return !workspace.provider.pathContext.isWithin(libSrcFolder, filePath);
+    }
+
+    var relativeRoot =
+        workspace.provider.pathContext.relative(root, from: workspace.root);
+    for (var binPath in workspace.binPaths) {
+      libFolder =
+          workspace.provider.pathContext.join(binPath, relativeRoot, 'lib');
+      if (workspace.provider.pathContext.isWithin(libFolder, filePath)) {
+        // A file in "$bin/lib" is public iff it is not in "$bin/lib/src".
+        var libSrcFolder =
+            workspace.provider.pathContext.join(libFolder, 'src');
+        return !workspace.provider.pathContext.isWithin(libSrcFolder, filePath);
+      }
+    }
+
+    libFolder = workspace.provider.pathContext
+        .join(workspace.genfiles, relativeRoot, 'lib');
+    if (workspace.provider.pathContext.isWithin(libFolder, filePath)) {
+      // A file in "$genfiles/lib" is public iff it is not in
+      // "$genfiles/lib/src".
+      var libSrcFolder = workspace.provider.pathContext.join(libFolder, 'src');
+      return !workspace.provider.pathContext.isWithin(libSrcFolder, filePath);
+    }
+
+    return false;
+  }
 }
diff --git a/pkg/analyzer/lib/src/workspace/gn.dart b/pkg/analyzer/lib/src/workspace/gn.dart
index cca441f..fe78cc0 100644
--- a/pkg/analyzer/lib/src/workspace/gn.dart
+++ b/pkg/analyzer/lib/src/workspace/gn.dart
@@ -237,4 +237,17 @@
   @override
   Map<String, List<Folder>> packagesAvailableTo(String libraryPath) =>
       workspace.packageMap;
+
+  @override
+  bool sourceIsInPublicApi(Source source) {
+    var filePath = filePathFromSource(source);
+    if (filePath == null) return false;
+    var libFolder = workspace.provider.pathContext.join(root, 'lib');
+    if (workspace.provider.pathContext.isWithin(libFolder, filePath)) {
+      var libSrcFolder =
+          workspace.provider.pathContext.join(root, 'lib', 'src');
+      return !workspace.provider.pathContext.isWithin(libSrcFolder, filePath);
+    }
+    return false;
+  }
 }
diff --git a/pkg/analyzer/lib/src/workspace/package_build.dart b/pkg/analyzer/lib/src/workspace/package_build.dart
index 60eb742..7240390 100644
--- a/pkg/analyzer/lib/src/workspace/package_build.dart
+++ b/pkg/analyzer/lib/src/workspace/package_build.dart
@@ -137,6 +137,12 @@
   /// package:build does it.
   static const String _pubspecName = 'pubspec.yaml';
 
+  static const List<String> _generatedPathParts = [
+    '.dart_tool',
+    'build',
+    'generated'
+  ];
+
   /// The resource provider used to access the file system.
   final ResourceProvider provider;
 
@@ -286,7 +292,7 @@
           final yaml = loadYaml(pubspec.readAsStringSync());
           final packageName = yaml['name'] as String;
           final generatedRootPath = provider.pathContext
-              .join(folder.path, '.dart_tool', 'build', 'generated');
+              .joinAll([folder.path, ..._generatedPathParts]);
           final generatedThisPath =
               provider.pathContext.join(generatedRootPath, packageName);
           return PackageBuildWorkspace._(provider, packageMap, folder.path,
@@ -334,4 +340,26 @@
   @override
   Map<String, List<Folder>> packagesAvailableTo(String libraryPath) =>
       workspace._packageMap;
+
+  @override
+  bool sourceIsInPublicApi(Source source) {
+    var filePath = filePathFromSource(source);
+    if (filePath == null) return false;
+    var libFolder = workspace.provider.pathContext.join(root, 'lib');
+    if (workspace.provider.pathContext.isWithin(libFolder, filePath)) {
+      // A file in "$root/lib" is public iff it is not in "$root/lib/src".
+      var libSrcFolder = workspace.provider.pathContext.join(libFolder, 'src');
+      return !workspace.provider.pathContext.isWithin(libSrcFolder, filePath);
+    }
+
+    libFolder = workspace.provider.pathContext.joinAll(
+        [root, ...PackageBuildWorkspace._generatedPathParts, 'test', 'lib']);
+    if (workspace.provider.pathContext.isWithin(libFolder, filePath)) {
+      // A file in "$generated/lib" is public iff it is not in
+      // "$generated/lib/src".
+      var libSrcFolder = workspace.provider.pathContext.join(libFolder, 'src');
+      return !workspace.provider.pathContext.isWithin(libSrcFolder, filePath);
+    }
+    return false;
+  }
 }
diff --git a/pkg/analyzer/lib/src/workspace/pub.dart b/pkg/analyzer/lib/src/workspace/pub.dart
index f47327d..1fed22e 100644
--- a/pkg/analyzer/lib/src/workspace/pub.dart
+++ b/pkg/analyzer/lib/src/workspace/pub.dart
@@ -120,4 +120,20 @@
     //  [libraryPath] is inside the `lib` directory.
     return workspace.packageMap;
   }
+
+  @override
+
+  /// A Pub package's public API consists of libraries found in the top-level
+  /// "lib" directory, and any subdirectories, excluding the "src" directory
+  /// just inside the top-level "lib" directory.
+  bool sourceIsInPublicApi(Source source) {
+    var filePath = filePathFromSource(source);
+    if (filePath == null) return false;
+    var libFolder = workspace.provider.pathContext.join(root, 'lib');
+    if (!workspace.provider.pathContext.isWithin(libFolder, filePath)) {
+      return false;
+    }
+    var libSrcFolder = workspace.provider.pathContext.join(root, 'lib', 'src');
+    return !workspace.provider.pathContext.isWithin(libSrcFolder, filePath);
+  }
 }
diff --git a/pkg/analyzer/lib/src/workspace/workspace.dart b/pkg/analyzer/lib/src/workspace/workspace.dart
index d9dc17a..0b83b88 100644
--- a/pkg/analyzer/lib/src/workspace/workspace.dart
+++ b/pkg/analyzer/lib/src/workspace/workspace.dart
@@ -67,6 +67,9 @@
   /// path of the root of those packages for all of the packages that could
   /// validly be imported by the library with the given [libraryPath].
   Map<String, List<Folder>> packagesAvailableTo(String libraryPath);
+
+  /// Return whether [source] is located in this package's public API.
+  bool sourceIsInPublicApi(Source source);
 }
 
 /// An interface for a workspace that contains a default analysis options file.
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_caching_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_caching_test.dart
index 9fca7de..5a7be88 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_caching_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_caching_test.dart
@@ -23,6 +23,8 @@
   }
 
   test_lints() async {
+    useEmptyByteStore();
+
     newFile(testFilePath, content: r'''
 void f() {
   ![0].isEmpty;
diff --git a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
index 4e85656..4c28ad0 100644
--- a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
@@ -24,6 +24,7 @@
 import 'package:meta/meta.dart';
 import 'package:test/test.dart';
 
+import 'context_collection_resolution_caching.dart';
 import 'resolution.dart';
 
 class AnalysisOptionsFileConfig {
@@ -127,7 +128,7 @@
     with ResourceProviderMixin, ResolutionTest {
   static bool _lintRulesAreRegistered = false;
 
-  final ByteStore _byteStore = MemoryByteStore();
+  ByteStore _byteStore = getContextResolutionTestByteStore();
 
   Map<String, String> _declaredVariables = {};
   AnalysisContextCollection _analysisContextCollection;
@@ -218,6 +219,12 @@
     );
   }
 
+  /// Call this method if the test needs to use the empty byte store, without
+  /// any information cached.
+  void useEmptyByteStore() {
+    _byteStore = MemoryByteStore();
+  }
+
   void verifyCreatedCollection() {}
 
   /// Create all analysis contexts in [collectionIncludedPaths].
diff --git a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution_caching.dart b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution_caching.dart
new file mode 100644
index 0000000..9f2dc62
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution_caching.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/dart/analysis/byte_store.dart';
+
+final _sharedByteStore = MemoryByteStore();
+final _useSharedByteStore = false;
+
+ByteStore getContextResolutionTestByteStore() {
+  if (_useSharedByteStore) {
+    return _sharedByteStore;
+  } else {
+    return MemoryByteStore();
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_export_of_internal_element_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_export_of_internal_element_test.dart
new file mode 100644
index 0000000..40ce958
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/invalid_export_of_internal_element_test.dart
@@ -0,0 +1,512 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/dart/analysis/analysis_context.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/test_utilities/mock_packages.dart';
+import 'package:analyzer/src/workspace/bazel.dart';
+import 'package:analyzer/src/workspace/package_build.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(InvalidExportOfInternalElement_BazelPackageTest);
+    defineReflectiveTests(
+        InvalidExportOfInternalElement_PackageBuildPackageTest);
+    defineReflectiveTests(InvalidExportOfInternalElement_PubPackageTest);
+  });
+}
+
+@reflectiveTest
+class InvalidExportOfInternalElement_BazelPackageTest
+    extends BazelWorkspaceResolutionTest
+    with InvalidExportOfInternalElementTest {
+  /// A cached analysis context for resolving sources via the same [Workspace].
+  AnalysisContext analysisContext;
+
+  String get testPackageBazelBinPath => '$workspaceRootPath/bazel-bin/dart/my';
+
+  String get testPackageGenfilesPath =>
+      '$workspaceRootPath/bazel-genfiles/dart/my';
+
+  @override
+  String get testPackageLibPath => myPackageLibPath;
+
+  @override
+  Future<ResolvedUnitResult> resolveFile(String path) {
+    analysisContext ??= contextFor(path);
+    assert(analysisContext.workspace is BazelWorkspace);
+    return analysisContext.currentSession.getResolvedUnit(path);
+  }
+
+  @override
+  void setUp() async {
+    super.setUp();
+    var metaPath = '$workspaceThirdPartyDartPath/meta';
+    MockPackages.addMetaPackageFiles(
+      getFolder(metaPath),
+    );
+    newFile('$testPackageBazelBinPath/my.packages');
+    newFolder('$workspaceRootPath/bazel-out');
+  }
+
+  void test_exporterIsInBazelBinLib() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+    await resolveFile2(testPackageImplementationFilePath);
+
+    newFile('$testPackageBazelBinPath/lib/bar.dart', content: r'''
+export 'src/foo.dart';
+''');
+    await resolveFile2('$testPackageBazelBinPath/lib/bar.dart');
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 22),
+    ]);
+  }
+
+  void test_exporterIsInBazelBinLibSrc() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+    await resolveFile2(testPackageImplementationFilePath);
+
+    newFile('$testPackageBazelBinPath/lib/src/bar.dart', content: r'''
+export 'foo.dart';
+''');
+    await resolveFile2('$testPackageBazelBinPath/lib/src/bar.dart');
+
+    assertErrorsInResolvedUnit(result, []);
+  }
+
+  void test_exporterIsInGenfilesLib() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+    await resolveFile2(testPackageImplementationFilePath);
+
+    newFile('$testPackageGenfilesPath/lib/bar.dart', content: r'''
+export 'src/foo.dart';
+''');
+    await resolveFile2('$testPackageGenfilesPath/lib/bar.dart');
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 22),
+    ]);
+  }
+
+  void test_exporterIsInGenfilesLibSrc() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+    await resolveFile2(testPackageImplementationFilePath);
+
+    newFile('$testPackageGenfilesPath/lib/src/bar.dart', content: r'''
+export 'foo.dart';
+''');
+    await resolveFile2('$testPackageGenfilesPath/lib/src/bar.dart');
+
+    assertErrorsInResolvedUnit(result, []);
+  }
+
+  void test_exporterIsInLib() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+
+    newFile('$testPackageLibPath/bar.dart', content: r'''
+export 'src/foo.dart';
+''');
+    await resolveFile2('$testPackageLibPath/bar.dart');
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 22),
+    ]);
+  }
+
+  void test_exporterIsInLibSrc() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+
+    newFile('$testPackageLibPath/src/bar.dart', content: r'''
+export 'foo.dart';
+''');
+    await resolveFile2('$testPackageLibPath/src/bar.dart');
+
+    assertErrorsInResolvedUnit(result, []);
+  }
+
+  void test_exporterIsInTest() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+
+    newFile('$myPackageRootPath/test/foo_test.dart', content: r'''
+export 'package:dart.my/src/foo.dart';
+''');
+    await resolveFile2('$myPackageRootPath/test/foo_test.dart');
+
+    assertErrorsInResolvedUnit(result, []);
+  }
+
+  void test_internalIsInBazelBin() async {
+    newFile('$testPackageBazelBinPath/lib/src/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+
+    await assertErrorsInCode(r'''
+export 'package:dart.my/src/foo.dart';
+''', [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 38),
+    ]);
+  }
+
+  void test_internalIsInGenfiles() async {
+    newFile('$testPackageGenfilesPath/lib/src/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+
+    await assertErrorsInCode(r'''
+export 'package:dart.my/src/foo.dart';
+''', [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 38),
+    ]);
+  }
+
+  void test_internalIsInLibSrc() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+
+    await assertErrorsInCode(r'''
+export 'package:dart.my/src/foo.dart';
+''', [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 38),
+    ]);
+  }
+}
+
+@reflectiveTest
+class InvalidExportOfInternalElement_PackageBuildPackageTest
+    extends InvalidExportOfInternalElement_PubPackageTest {
+  /// A cached analysis context for resolving sources via the same [Workspace].
+  AnalysisContext analysisContext;
+
+  String get testPackageDartToolPath =>
+      '$testPackageRootPath/.dart_tool/build/generated/test';
+
+  @override
+  Future<ResolvedUnitResult> resolveFile(String path) {
+    analysisContext ??= contextFor(path);
+    assert(analysisContext.workspace is PackageBuildWorkspace);
+    return analysisContext.currentSession.getResolvedUnit(path);
+  }
+
+  @override
+  void setUp() async {
+    analysisContext = null;
+    super.setUp();
+    newFolder(testPackageDartToolPath);
+  }
+
+  void test_exporterInGeneratedLib() async {
+    newFile('$testPackageRootPath/lib/src/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+    await resolveFile2('$testPackageRootPath/lib/src/foo.dart');
+
+    newFile('$testPackageDartToolPath/lib/bar.dart', content: r'''
+export 'package:test/src/foo.dart';
+''');
+    await resolveFile2('$testPackageDartToolPath/lib/bar.dart');
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 35),
+    ]);
+  }
+
+  void test_exporterInGeneratedLibSrc() async {
+    newFile('$testPackageRootPath/lib/src/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+    await resolveFile2('$testPackageRootPath/lib/src/foo.dart');
+
+    newFile('$testPackageDartToolPath/lib/src/bar.dart', content: r'''
+export 'package:test/src/foo.dart';
+''');
+    await resolveFile2('$testPackageDartToolPath/lib/src/bar.dart');
+
+    assertErrorsInResolvedUnit(result, []);
+  }
+
+  void test_exporterInLib() async {
+    newFile('$testPackageRootPath/lib/src/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+    await resolveFile2('$testPackageRootPath/lib/src/foo.dart');
+
+    newFile('$testPackageRootPath/lib/bar.dart', content: r'''
+export 'package:test/src/foo.dart';
+''');
+    await resolveFile2('$testPackageRootPath/lib/bar.dart');
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 35),
+    ]);
+  }
+
+  void test_exporterInLibSrc() async {
+    newFile('$testPackageRootPath/lib/src/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+    await resolveFile2('$testPackageRootPath/lib/src/foo.dart');
+
+    newFile('$testPackageRootPath/lib/src/bar.dart', content: r'''
+export 'package:test/src/foo.dart';
+''');
+    await resolveFile2('$testPackageRootPath/lib/src/bar.dart');
+
+    assertErrorsInResolvedUnit(result, []);
+  }
+
+  void test_internalIsInGeneratedLibSrc() async {
+    newFile('$testPackageDartToolPath/lib/src/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+
+    await assertErrorsInCode(r'''
+export 'package:test/src/foo.dart';
+''', [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 35),
+    ]);
+  }
+
+  void test_internalIsLibSrc() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+
+    await assertErrorsInCode(r'''
+export 'package:test/src/foo.dart';
+''', [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 35),
+    ]);
+  }
+}
+
+@reflectiveTest
+class InvalidExportOfInternalElement_PubPackageTest
+    extends PubPackageResolutionTest with InvalidExportOfInternalElementTest {
+  @override
+  void setUp() async {
+    super.setUp();
+    writeTestPackageConfigWithMeta();
+    newFile('$testPackageRootPath/pubspec.yaml', content: r'''
+name: test
+version: 0.0.1
+''');
+  }
+
+  void test_exporterIsInLib() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+
+    newFile('$testPackageLibPath/bar.dart', content: r'''
+export 'src/foo.dart';
+''');
+    await resolveFile2('$testPackageLibPath/bar.dart');
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 22),
+    ]);
+  }
+
+  void test_exporterIsInLibSrc() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+
+    newFile('$testPackageLibPath/src/bar.dart', content: r'''
+export 'foo.dart';
+''');
+    await resolveFile2('$testPackageLibPath/src/bar.dart');
+
+    assertErrorsInResolvedUnit(result, []);
+  }
+
+  void test_exporterIsInTest() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+
+    newFile('$testPackageRootPath/test/foo_test.dart', content: r'''
+export 'package:test/src/foo.dart';
+''');
+    await resolveFile2('$testPackageRootPath/test/foo_test.dart');
+
+    assertErrorsInResolvedUnit(result, []);
+  }
+
+  void test_internalIsLibSrc() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+
+    await assertErrorsInCode(r'''
+export 'package:test/src/foo.dart';
+''', [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 35),
+    ]);
+  }
+}
+
+mixin InvalidExportOfInternalElementTest on ContextResolutionTest {
+  String get testPackageImplementationFilePath =>
+      '$testPackageLibPath/src/foo.dart';
+
+  String get testPackageLibPath;
+
+  void test_hideCombinator_internalHidden() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+class Two {}
+''');
+
+    await assertNoErrorsInCode(r'''
+export 'src/foo.dart' hide One;
+''');
+  }
+
+  void test_hideCombinator_internalNotHidden() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+class Two {}
+''');
+
+    await assertErrorsInCode(r'''
+export 'src/foo.dart' hide Two;
+''', [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 31),
+    ]);
+  }
+
+  void test_noCombinators() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+
+    await assertErrorsInCode(r'''
+export 'src/foo.dart';
+''', [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 22),
+    ]);
+  }
+
+  void test_noCombinators_indirectExport() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+export 'bar.dart';
+''');
+
+    newFile('$testPackageLibPath/src/bar.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+
+    await assertErrorsInCode(r'''
+export 'src/foo.dart';
+''', [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 22),
+    ]);
+  }
+
+  void test_noCombinators_library() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+@internal
+library foo;
+
+import 'package:meta/meta.dart';
+''');
+
+    await assertErrorsInCode(r'''
+export 'src/foo.dart';
+''', [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 22),
+    ]);
+  }
+
+  void test_noCombinators_library_notInternal() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+library foo;
+''');
+
+    await assertNoErrorsInCode(r'''
+export 'src/foo.dart';
+''');
+  }
+
+  void test_noCombinators_noInternal() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+class One {}
+''');
+
+    await assertNoErrorsInCode(r'''
+export 'src/foo.dart';
+''');
+  }
+
+  void test_showCombinator_internalNotShown() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+class Two {}
+''');
+
+    await assertNoErrorsInCode(r'''
+export 'src/foo.dart' show Two;
+''');
+  }
+
+  void test_showCombinator_internalShown() async {
+    newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+class Two {}
+''');
+
+    await assertErrorsInCode(r'''
+export 'src/foo.dart' show One;
+''', [
+      error(HintCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT, 0, 31),
+    ]);
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_internal_annotation_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_internal_annotation_test.dart
new file mode 100644
index 0000000..2dee934
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/invalid_internal_annotation_test.dart
@@ -0,0 +1,361 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(InvalidInternalAnnotationTest);
+  });
+}
+
+@reflectiveTest
+class InvalidInternalAnnotationTest extends PubPackageResolutionTest {
+  String get testPackageImplementationFilePath =>
+      '$testPackageLibPath/src/foo.dart';
+
+  @override
+  void setUp() async {
+    super.setUp();
+    writeTestPackageConfigWithMeta();
+    await newFile('$testPackageRootPath/pubspec.yaml', content: r'''
+name: test
+version: 0.0.1
+''');
+  }
+
+  void test_annotationInLib() async {
+    await newFile('$testPackageLibPath/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+    await resolveFile2('$testPackageLibPath/foo.dart');
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 33, 9),
+    ]);
+  }
+
+  void test_annotationInLib_onLibrary() async {
+    await newFile('$testPackageLibPath/foo.dart', content: r'''
+@internal
+library foo;
+import 'package:meta/meta.dart';
+''');
+    await resolveFile2('$testPackageLibPath/foo.dart');
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 0, 9),
+    ]);
+  }
+
+  void test_annotationInLibSrc() async {
+    await newFile('$testPackageLibPath/src/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+    await resolveFile2('$testPackageLibPath/src/foo.dart');
+
+    assertErrorsInResolvedUnit(result, []);
+  }
+
+  void test_annotationInLibSrcSubdirectory() async {
+    await newFile('$testPackageLibPath/src/foo/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+    await resolveFile2('$testPackageLibPath/src/foo/foo.dart');
+
+    assertErrorsInResolvedUnit(result, []);
+  }
+
+  void test_annotationInLibSubdirectory() async {
+    await newFile('$testPackageLibPath/foo/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+    await resolveFile2('$testPackageLibPath/foo/foo.dart');
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 33, 9),
+    ]);
+  }
+
+  void test_annotationInTest() async {
+    await newFile('$testPackageRootPath/test/foo_test.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal class One {}
+''');
+    await resolveFile2('$testPackageRootPath/test/foo_test.dart');
+
+    assertErrorsInResolvedUnit(result, []);
+  }
+
+  void test_privateClass() async {
+    await newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal class _One {}
+''');
+    await resolveFile2(testPackageImplementationFilePath);
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 33, 9),
+      error(HintCode.UNUSED_ELEMENT, 49, 4),
+    ]);
+  }
+
+  void test_privateClassConstructor_named() async {
+    await newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+class _C {
+  @internal _C.named();
+}
+''');
+    await resolveFile2(testPackageImplementationFilePath);
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.UNUSED_ELEMENT, 39, 2),
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 46, 9),
+    ]);
+  }
+
+  void test_privateClassConstructor_unnamed() async {
+    await newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+class _C {
+  @internal _C();
+}
+''');
+    await resolveFile2(testPackageImplementationFilePath);
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.UNUSED_ELEMENT, 39, 2),
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 46, 9),
+    ]);
+  }
+
+  void test_privateConstructor() async {
+    await newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+class C {
+  @internal C._f();
+}
+''');
+    await resolveFile2(testPackageImplementationFilePath);
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 45, 9),
+    ]);
+  }
+
+  void test_privateEnum() async {
+    await newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal enum _E {one}
+''');
+    await resolveFile2(testPackageImplementationFilePath);
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 33, 9),
+      error(HintCode.UNUSED_ELEMENT, 48, 2),
+      error(HintCode.UNUSED_FIELD, 52, 3),
+    ]);
+  }
+
+  void test_privateEnumValue() async {
+    await newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+enum E {@internal _one}
+''');
+    await resolveFile2(testPackageImplementationFilePath);
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 41, 9),
+      error(HintCode.UNUSED_FIELD, 51, 4),
+    ]);
+  }
+
+  void test_privateExtension() async {
+    await newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal extension _One on String {}
+''');
+    await resolveFile2(testPackageImplementationFilePath);
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 33, 9),
+    ]);
+  }
+
+  void test_privateExtension_unnamed() async {
+    await newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+@internal extension on String {}
+''');
+    await resolveFile2(testPackageImplementationFilePath);
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 33, 9),
+    ]);
+  }
+
+  void test_privateField_instance() async {
+    await newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+class C {
+  @internal int _i = 0;
+}
+''');
+    await resolveFile2(testPackageImplementationFilePath);
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.UNUSED_FIELD, 59, 2),
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 59, 6),
+    ]);
+  }
+
+  void test_privateField_static() async {
+    await newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+class C {
+  @internal static int _i = 0;
+}
+''');
+    await resolveFile2(testPackageImplementationFilePath);
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.UNUSED_FIELD, 66, 2),
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 66, 6),
+    ]);
+  }
+
+  void test_privateGetter() async {
+    await newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+class C {
+  @internal int get _i => 0;
+}
+''');
+    await resolveFile2(testPackageImplementationFilePath);
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 45, 9),
+      error(HintCode.UNUSED_ELEMENT, 63, 2),
+    ]);
+  }
+
+  void test_privateMethod_instance() async {
+    await newFile(testPackageImplementationFilePath, content: r'''
+import 'package:meta/meta.dart';
+class C {
+  @internal void _f() {}
+}
+''');
+    await resolveFile2(testPackageImplementationFilePath);
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 45, 9),
+      error(HintCode.UNUSED_ELEMENT, 60, 2),
+    ]);
+  }
+
+  void test_privateMethod_static() async {
+    await newFile('$testPackageLibPath/src/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+class C {
+  @internal static void _f() {}
+}
+''');
+    await resolveFile2('$testPackageLibPath/src/foo.dart');
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 45, 9),
+      error(HintCode.UNUSED_ELEMENT, 67, 2),
+    ]);
+  }
+
+  void test_privateMixin() async {
+    await newFile('$testPackageLibPath/src/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal mixin _One {}
+''');
+    await resolveFile2('$testPackageLibPath/src/foo.dart');
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 33, 9),
+      error(HintCode.UNUSED_ELEMENT, 49, 4),
+    ]);
+  }
+
+  void test_privateTopLevelFunction() async {
+    await newFile('$testPackageLibPath/src/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal void _f() {}
+''');
+    await resolveFile2('$testPackageLibPath/src/foo.dart');
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 33, 9),
+      error(HintCode.UNUSED_ELEMENT, 48, 2),
+    ]);
+  }
+
+  void test_privateTopLevelVariable() async {
+    await newFile('$testPackageLibPath/src/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal int _i = 1;
+''');
+    await resolveFile2('$testPackageLibPath/src/foo.dart');
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 47, 6),
+      error(HintCode.UNUSED_ELEMENT, 47, 2),
+    ]);
+  }
+
+  void test_privateTypedef() async {
+    await newFile('$testPackageLibPath/src/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+@internal typedef _T = void Function();
+''');
+    await resolveFile2('$testPackageLibPath/src/foo.dart');
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.INVALID_INTERNAL_ANNOTATION, 33, 9),
+      error(HintCode.UNUSED_ELEMENT, 51, 2),
+    ]);
+  }
+
+  void test_publicMethod_privateClass() async {
+    await newFile('$testPackageLibPath/src/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+class _C {
+  @internal void f() {}
+}
+''');
+    await resolveFile2('$testPackageLibPath/src/foo.dart');
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.UNUSED_ELEMENT, 39, 2),
+    ]);
+  }
+
+  void test_publicMethod_privateClass_static() async {
+    await newFile('$testPackageLibPath/src/foo.dart', content: r'''
+import 'package:meta/meta.dart';
+class _C {
+  @internal static void f() {}
+}
+''');
+    await resolveFile2('$testPackageLibPath/src/foo.dart');
+
+    assertErrorsInResolvedUnit(result, [
+      error(HintCode.UNUSED_ELEMENT, 39, 2),
+      error(HintCode.UNUSED_ELEMENT, 68, 1),
+    ]);
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 55f08d2..08a65a1 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -279,6 +279,8 @@
 import 'invalid_constant_test.dart' as invalid_constant;
 import 'invalid_constructor_name_test.dart' as invalid_constructor_name;
 import 'invalid_exception_value_test.dart' as invalid_exception_value;
+import 'invalid_export_of_internal_element_test.dart'
+    as invalid_export_of_internal_element;
 import 'invalid_extension_argument_count_test.dart'
     as invalid_extension_argument_count;
 import 'invalid_factory_annotation_test.dart' as invalid_factory_annotation;
@@ -287,6 +289,7 @@
     as invalid_factory_name_not_a_class;
 import 'invalid_field_type_in_struct_test.dart' as invalid_field_type_in_struct;
 import 'invalid_immutable_annotation_test.dart' as invalid_immutable_annotation;
+import 'invalid_internal_annotation_test.dart' as invalid_internal_annotation;
 import 'invalid_language_override_greater_test.dart'
     as invalid_language_override_greater;
 import 'invalid_language_override_test.dart' as invalid_language_override;
@@ -828,12 +831,14 @@
     invalid_constant.main();
     invalid_constructor_name.main();
     invalid_exception_value.main();
+    invalid_export_of_internal_element.main();
     invalid_extension_argument_count.main();
     invalid_factory_annotation.main();
     invalid_factory_method_impl.main();
     invalid_factory_name_not_a_class.main();
     invalid_field_type_in_struct.main();
     invalid_immutable_annotation.main();
+    invalid_internal_annotation.main();
     invalid_language_override_greater.main();
     invalid_language_override.main();
     invalid_literal_annotation.main();
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index 0f23cdf..ced7369 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -1308,40 +1308,61 @@
   @override
   HInstruction visitGetLength(HGetLength node) {
     HInstruction receiver = node.receiver;
-    if (_graph.allocatedFixedLists.contains(receiver)) {
-      // TODO(sra): How do we keep this working if we lower/inline the receiver
-      // in an optimization?
 
-      // TODO(ngeoffray): checking if the second input is an integer
-      // should not be necessary but it currently makes it easier for
-      // other optimizations to reason about a fixed length constructor
-      // that we know takes an int.
-      if (receiver.inputs[0].isInteger(_abstractValueDomain).isDefinitelyTrue) {
-        return receiver.inputs[0];
-      }
-    } else if (receiver.isConstantList()) {
+    if (receiver.isConstantList()) {
       HConstant constantReceiver = receiver;
       ListConstantValue constant = constantReceiver.constant;
       return _graph.addConstantInt(constant.length, _closedWorld);
-    } else if (receiver.isConstantString()) {
+    }
+
+    if (receiver.isConstantString()) {
       HConstant constantReceiver = receiver;
       StringConstantValue constant = constantReceiver.constant;
       return _graph.addConstantInt(constant.length, _closedWorld);
-    } else {
-      AbstractValue type = receiver.instructionType;
-      if (_abstractValueDomain.isContainer(type) &&
-          _abstractValueDomain.getContainerLength(type) != null) {
-        HInstruction constant = _graph.addConstantInt(
-            _abstractValueDomain.getContainerLength(type), _closedWorld);
-        if (_abstractValueDomain.isNull(type).isPotentiallyTrue) {
+    }
+
+    AbstractValue receiverType = receiver.instructionType;
+    if (_abstractValueDomain.isContainer(receiverType)) {
+      int /*?*/ length = _abstractValueDomain.getContainerLength(receiverType);
+      if (length != null) {
+        HInstruction constant = _graph.addConstantInt(length, _closedWorld);
+        if (_abstractValueDomain.isNull(receiverType).isPotentiallyTrue) {
           // If the container can be null, we update all uses of the length
           // access to use the constant instead, but keep the length access in
           // the graph, to ensure we still have a null check.
           node.block.rewrite(node, constant);
           return node;
-        } else {
-          return constant;
         }
+        return constant;
+      }
+    }
+
+    // Can we find the length as an input to an allocation?
+    HInstruction potentialAllocation = receiver;
+    if (receiver is HInvokeStatic &&
+        receiver.element == commonElements.setRuntimeTypeInfo) {
+      // Look through `setRuntimeTypeInfo(new Array(), ...)`
+      potentialAllocation = receiver.inputs.first;
+    }
+    if (_graph.allocatedFixedLists.contains(potentialAllocation)) {
+      // TODO(sra): How do we keep this working if we lower/inline the receiver
+      // in an optimization?
+
+      HInstruction lengthInput = potentialAllocation.inputs.first;
+
+      // We don't expect a non-integer first input to the fixed-size allocation,
+      // but checking the input is an integer ensures we do not replace a
+      // HGetlength with a reference to something with a type that will confuse
+      // bounds check eliminiation.
+      if (lengthInput.isInteger(_abstractValueDomain).isDefinitelyTrue) {
+        // TODO(sra). HGetLength may have a better type than [lengthInput] as
+        // the allocation may throw on an out-of-range input. Typically the
+        // input is an unconstrained `int` and the length is non-negative. We
+        // may have done some optimizations with the better type that we won't
+        // be able to do with the broader type of [lengthInput].  We should
+        // insert a HTypeKnown witnessed by the allocation to narrow the
+        // lengthInput.
+        return lengthInput;
       }
     }
 
diff --git a/pkg/compiler/test/analyses/api_allowed.json b/pkg/compiler/test/analyses/api_allowed.json
index e303a89..241f168 100644
--- a/pkg/compiler/test/analyses/api_allowed.json
+++ b/pkg/compiler/test/analyses/api_allowed.json
@@ -109,16 +109,7 @@
     "Dynamic access of 'dart.io::_path'.": 1,
     "Dynamic access of 'pid'.": 1,
     "Dynamic access of 'dart.io::_arguments'.": 1,
-    "Dynamic access of 'dart.io::_workingDirectory'.": 2,
-    "Dynamic access of 'isListening'.": 3,
-    "Dynamic access of 'address'.": 4,
-    "Dynamic access of 'host'.": 5,
-    "Dynamic access of 'port'.": 3,
-    "Dynamic access of 'remoteAddress'.": 2,
-    "Dynamic access of 'remotePort'.": 2,
-    "Dynamic access of 'isTcp'.": 1,
-    "Dynamic access of 'type'.": 1,
-    "Dynamic access of 'name'.": 1
+    "Dynamic access of 'dart.io::_workingDirectory'.": 2
   },
   "org-dartlang-sdk:///lib/io/link.dart": {
     "Dynamic invocation of '[]'.": 3
diff --git a/pkg/vm_service/CHANGELOG.md b/pkg/vm_service/CHANGELOG.md
index 049e01d1..0d06590 100644
--- a/pkg/vm_service/CHANGELOG.md
+++ b/pkg/vm_service/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Changelog
 
+## 5.1.0
+- Added support for `dart:io` extensions version 1.2.
+- Added `getOpenFiles`, `getOpenFileById`, `getSpawnedProcesses`, and `getSpawnedProcessById` RPCs.
+- Added `OpenFileList`, `OpenFileRef`, `OpenFile`, `SpawnedProcessList`, `SpawnedProcessRef`, and `SpawnedProcess` objects.
+
 ## 5.0.0
 
 - **breaking**: Update to version `3.39.0` of the spec.
diff --git a/pkg/vm_service/lib/src/dart_io_extensions.dart b/pkg/vm_service/lib/src/dart_io_extensions.dart
index 4297e45..ddecfc5 100644
--- a/pkg/vm_service/lib/src/dart_io_extensions.dart
+++ b/pkg/vm_service/lib/src/dart_io_extensions.dart
@@ -4,6 +4,8 @@
 
 // TODO(bkonyi): autogenerate from service_extensions.md
 
+import 'dart:collection';
+
 import 'package:meta/meta.dart';
 
 import 'vm_service.dart';
@@ -54,6 +56,42 @@
         'enable': enable,
       });
 
+  /// The `getOpenFiles` RPC is used to retrieve the list of files currently
+  /// opened files by `dart:io` from a given isolate.
+  Future<OpenFileList> getOpenFiles(String isolateId) => _callHelper(
+        'ext.dart.io.getOpenFiles',
+        isolateId,
+      );
+
+  /// The `getOpenFileById` RPC is used to retrieve information about files
+  /// currently opened by `dart:io` from a given isolate.
+  Future<OpenFile> getOpenFileById(String isolateId, int id) => _callHelper(
+        'ext.dart.io.getOpenFileById',
+        isolateId,
+        args: {
+          'id': id,
+        },
+      );
+
+  /// The `getSpawnedProcesses` RPC is used to retrieve the list of processed opened
+  /// by `dart:io` from a given isolate
+  Future<SpawnedProcessList> getSpawnedProcesses(String isolateId) =>
+      _callHelper(
+        'ext.dart.io.getSpawnedProcesses',
+        isolateId,
+      );
+
+  /// The `getSpawnedProcessById` RPC is used to retrieve information about a process
+  /// spawned by `dart:io` from a given isolate.
+  Future<SpawnedProcess> getSpawnedProcessById(String isolateId, int id) =>
+      _callHelper(
+        'ext.dart.io.getSpawnedProcessById',
+        isolateId,
+        args: {
+          'id': id,
+        },
+      );
+
   Future<T> _callHelper<T>(String method, String isolateId,
       {Map args = const {}}) {
     if (!_factoriesRegistered) {
@@ -70,9 +108,15 @@
   }
 
   static void _registerFactories() {
-    addTypeFactory('SocketStatistic', SocketStatistic.parse);
-    addTypeFactory('SocketProfile', SocketProfile.parse);
+    addTypeFactory('OpenFile', OpenFile.parse);
+    addTypeFactory('OpenFileList', OpenFileList.parse);
+    addTypeFactory('@OpenFile', OpenFileRef.parse);
     addTypeFactory('HttpTimelineLoggingState', HttpTimelineLoggingState.parse);
+    addTypeFactory('SpawnedProcess', SpawnedProcess.parse);
+    addTypeFactory('SpawnedProcessList', SpawnedProcessList.parse);
+    addTypeFactory('@SpawnedProcess', SpawnedProcessRef.parse);
+    addTypeFactory('SocketProfile', SocketProfile.parse);
+    addTypeFactory('SocketStatistic', SocketStatistic.parse);
     _factoriesRegistered = true;
   }
 }
@@ -162,3 +206,206 @@
   /// Whether or not HttpClient.enableTimelineLogging is set to true for a given isolate.
   final bool enabled;
 }
+
+/// A [SpawnedProcessRef] contains identifying information about a spawned process.
+class SpawnedProcessRef {
+  static SpawnedProcessRef parse(Map json) =>
+      json == null ? null : SpawnedProcessRef._fromJson(json);
+
+  SpawnedProcessRef({
+    @required this.id,
+    @required this.name,
+  });
+
+  SpawnedProcessRef._fromJson(Map<String, dynamic> json)
+      :
+        // TODO(bkonyi): make this part of the vm_service.dart library so we can
+        // call super._fromJson.
+        id = json['id'],
+        name = json['name'];
+
+  static const String type = 'SpawnedProcessRef';
+
+  /// The unique ID associated with this process.
+  final int id;
+
+  /// The name of the executable.
+  final String name;
+}
+
+/// A [SpawnedProcess] contains startup information of a spawned process.
+class SpawnedProcess extends Response implements SpawnedProcessRef {
+  static SpawnedProcess parse(Map json) =>
+      json == null ? null : SpawnedProcess._fromJson(json);
+
+  SpawnedProcess({
+    @required this.id,
+    @required this.name,
+    @required this.pid,
+    @required this.startedAt,
+    @required List<String> arguments,
+    @required this.workingDirectory,
+  }) : _arguments = arguments;
+
+  SpawnedProcess._fromJson(Map<String, dynamic> json)
+      :
+        // TODO(bkonyi): make this part of the vm_service.dart library so we can
+        // call super._fromJson.
+        id = json['id'],
+        name = json['name'],
+        pid = json['pid'],
+        startedAt = json['startedAt'],
+        _arguments = List<String>.from(
+            createServiceObject(json['arguments'], const ['String']) as List ??
+                []),
+        workingDirectory = json['workingDirectory'] {
+    type = json['type'];
+  }
+
+  /// The unique ID associated with this process.
+  final int id;
+
+  /// The name of the executable.
+  final String name;
+
+  /// The process ID associated with the process.
+  final int pid;
+
+  /// The time the process was started in milliseconds since epoch.
+  final int startedAt;
+
+  /// The list of arguments provided to the process at launch.
+  List<String> get arguments => UnmodifiableListView(_arguments);
+  final List<String> _arguments;
+
+  /// The working directory of the process at launch.
+  final String workingDirectory;
+}
+
+class SpawnedProcessList extends Response {
+  static SpawnedProcessList parse(Map json) =>
+      json == null ? null : SpawnedProcessList._fromJson(json);
+
+  SpawnedProcessList({@required List<SpawnedProcessRef> processes})
+      : _processes = processes;
+
+  SpawnedProcessList._fromJson(Map<String, dynamic> json)
+      :
+        // TODO(bkonyi): make this part of the vm_service.dart library so we can
+        // call super._fromJson.
+        _processes = List<SpawnedProcessRef>.from(
+            createServiceObject(json['processes'], const ['SpawnedProcessRef'])
+                    as List ??
+                []) {
+    type = json['type'];
+  }
+
+  /// A list of processes spawned through dart:io on a given isolate.
+  List<SpawnedProcessRef> get processes => UnmodifiableListView(_processes);
+  final List<SpawnedProcessRef> _processes;
+}
+
+/// A [OpenFileRef] contains identifying information about a currently opened file.
+class OpenFileRef {
+  static OpenFileRef parse(Map json) =>
+      json == null ? null : OpenFileRef._fromJson(json);
+
+  OpenFileRef({
+    @required this.id,
+    @required this.name,
+  });
+
+  OpenFileRef._fromJson(Map<String, dynamic> json)
+      :
+        // TODO(bkonyi): make this part of the vm_service.dart library so we can
+        // call super._fromJson.
+        id = json['id'],
+        name = json['name'];
+
+  static const String type = 'OpenFileRef';
+
+  /// The unique ID associated with this file.
+  final int id;
+
+  /// The path of the file.
+  final String name;
+}
+
+/// A [File] contains information about reads and writes to a currently opened file.
+class OpenFile extends Response implements OpenFileRef {
+  static OpenFile parse(Map json) =>
+      json == null ? null : OpenFile._fromJson(json);
+
+  OpenFile({
+    @required this.id,
+    @required this.name,
+    @required this.readBytes,
+    @required this.writeBytes,
+    @required this.readCount,
+    @required this.writeCount,
+    @required this.lastReadTime,
+    @required this.lastWriteTime,
+  });
+
+  OpenFile._fromJson(Map<String, dynamic> json)
+      :
+        // TODO(bkonyi): make this part of the vm_service.dart library so we can
+        // call super._fromJson.
+        id = json['id'],
+        name = json['name'],
+        readBytes = json['readBytes'],
+        writeBytes = json['writeBytes'],
+        readCount = json['readCount'],
+        writeCount = json['writeCount'],
+        lastReadTime =
+            DateTime.fromMillisecondsSinceEpoch(json['lastReadTime']),
+        lastWriteTime =
+            DateTime.fromMillisecondsSinceEpoch(json['lastWriteTime']) {
+    type = json['type'];
+  }
+
+  /// The unique ID associated with this file.
+  final int id;
+
+  /// The path of the file.
+  final String name;
+
+  /// The total number of bytes read from this file.
+  final int readBytes;
+
+  /// The total number of bytes written to this file.
+  final int writeBytes;
+
+  /// The number of reads made from this file.
+  final int readCount;
+
+  /// The number of writes made to this file.
+  final int writeCount;
+
+  /// The time at which this file was last read by this process.
+  final DateTime lastReadTime;
+
+  /// The time at which this file was last written to by this process.
+  final DateTime lastWriteTime;
+}
+
+class OpenFileList extends Response {
+  static OpenFileList parse(Map json) =>
+      json == null ? null : OpenFileList._fromJson(json);
+
+  OpenFileList({@required List<OpenFileRef> files}) : _files = files;
+
+  OpenFileList._fromJson(Map<String, dynamic> json)
+      :
+        // TODO(bkonyi): make this part of the vm_service.dart library so we can
+        // call super._fromJson.
+        _files = List<OpenFileRef>.from(
+            createServiceObject(json['files'], const ['OpenFileRef']) as List ??
+                []) {
+    type = json['type'];
+  }
+
+  /// A list of all files opened through dart:io on a given isolate.
+  List<OpenFileRef> get files => UnmodifiableListView(_files);
+  final List<OpenFileRef> _files;
+}
diff --git a/pkg/vm_service/pubspec.yaml b/pkg/vm_service/pubspec.yaml
index 66b2910..dd8502a 100644
--- a/pkg/vm_service/pubspec.yaml
+++ b/pkg/vm_service/pubspec.yaml
@@ -2,7 +2,7 @@
 description: >-
   A library to communicate with a service implementing the Dart VM
   service protocol.
-version: 5.0.0+1
+version: 5.1.0+1
 
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/vm_service
 
diff --git a/pkg/vm_service/test/common/test_helper.dart b/pkg/vm_service/test/common/test_helper.dart
index 1b19ba1..836e323 100644
--- a/pkg/vm_service/test/common/test_helper.dart
+++ b/pkg/vm_service/test/common/test_helper.dart
@@ -6,7 +6,7 @@
 
 import 'dart:async';
 import 'dart:convert';
-import 'dart:io';
+import 'dart:io' as io;
 import 'package:vm_service/vm_service_io.dart';
 import 'package:vm_service/vm_service.dart';
 import 'package:test/test.dart';
@@ -30,21 +30,22 @@
 const String _TESTEE_ENV_KEY = 'SERVICE_TEST_TESTEE';
 const Map<String, String> _TESTEE_SPAWN_ENV = {_TESTEE_ENV_KEY: 'true'};
 bool _isTestee() {
-  return Platform.environment.containsKey(_TESTEE_ENV_KEY);
+  return io.Platform.environment.containsKey(_TESTEE_ENV_KEY);
 }
 
 Uri _getTestUri() {
-  if (Platform.script.scheme == 'data') {
+  if (io.Platform.script.scheme == 'data') {
     // If we're using pub to run these tests this value isn't a file URI.
     // We'll need to parse the actual URI out...
     final fileRegExp = RegExp(r'file:\/\/\/.*\.dart');
-    final path = fileRegExp.stringMatch(Platform.script.data.contentAsString());
+    final path =
+        fileRegExp.stringMatch(io.Platform.script.data.contentAsString());
     if (path == null) {
       throw 'Unable to determine file path for script!';
     }
     return Uri.parse(path);
   } else {
-    return Platform.script;
+    return io.Platform.script;
   }
 }
 
@@ -72,7 +73,7 @@
     if (!pause_on_exit) {
       // Wait around for the process to be killed.
       // ignore: unawaited_futures
-      stdin.first.then((_) => exit(0));
+      io.stdin.first.then((_) => io.exit(0));
     }
   }
 
@@ -92,20 +93,20 @@
     }
     if (!pause_on_exit) {
       // Wait around for the process to be killed.
-      stdin.first.then((_) => exit(0));
+      io.stdin.first.then((_) => io.exit(0));
     }
   }
 }
 
 class _ServiceTesteeLauncher {
-  Process process;
+  io.Process process;
   List<String> args;
   bool killedByTester = false;
 
   _ServiceTesteeLauncher() : args = [_getTestUri().toFilePath()];
 
   // Spawn the testee process.
-  Future<Process> _spawnProcess(
+  Future<io.Process> _spawnProcess(
     bool pause_on_start,
     bool pause_on_exit,
     bool pause_on_unhandled_exceptions,
@@ -127,14 +128,14 @@
         extraArgs);
   }
 
-  Future<Process> _spawnDartProcess(
+  Future<io.Process> _spawnDartProcess(
       bool pause_on_start,
       bool pause_on_exit,
       bool pause_on_unhandled_exceptions,
       bool testeeControlsServer,
       bool useAuthToken,
       List<String> extraArgs) {
-    String dartExecutable = Platform.executable;
+    String dartExecutable = io.Platform.executable;
 
     var fullArgs = <String>[
       '--disable-dart-dev',
@@ -143,7 +144,7 @@
       fullArgs.add('--pause-isolates-on-start');
     }
     if (pause_on_exit) {
-      fullArgs.add('--pause-isolates-on-exit');
+      fullArgs.add('--pause-isolates-on-io.exit');
     }
     if (!useAuthToken) {
       fullArgs.add('--disable-service-auth-codes');
@@ -156,7 +157,7 @@
       fullArgs.addAll(extraArgs);
     }
 
-    fullArgs.addAll(Platform.executableArguments);
+    fullArgs.addAll(io.Platform.executableArguments);
     if (!testeeControlsServer) {
       fullArgs.add('--enable-vm-service:0');
     }
@@ -165,7 +166,7 @@
     return _spawnCommon(dartExecutable, fullArgs, <String, String>{});
   }
 
-  Future<Process> _spawnCommon(String executable, List<String> arguments,
+  Future<io.Process> _spawnCommon(String executable, List<String> arguments,
       Map<String, String> dartEnvironment) {
     var environment = _TESTEE_SPAWN_ENV;
     var bashEnvironment = StringBuffer();
@@ -176,7 +177,7 @@
       });
     }
     print('** Launching $bashEnvironment$executable ${arguments.join(' ')}');
-    return Process.start(executable, arguments, environment: environment);
+    return io.Process.start(executable, arguments, environment: environment);
   }
 
   Future<Uri> launch(
@@ -217,17 +218,17 @@
           first = false;
           print('** Signaled to run test queries on $uri');
         }
-        stdout.write('>testee>out> ${line}\n');
+        io.stdout.write('>testee>out> ${line}\n');
       });
       process.stderr
           .transform(utf8.decoder)
           .transform(LineSplitter())
           .listen((line) {
-        stdout.write('>testee>err> ${line}\n');
+        io.stdout.write('>testee>err> ${line}\n');
       });
       process.exitCode.then((exitCode) {
-        if ((exitCode != 0) && !killedByTester) {
-          throw "Testee exited with $exitCode";
+        if ((io.exitCode != 0) && !killedByTester) {
+          throw "Testee io.exited with $exitCode";
         }
         print("** Process exited");
       });
@@ -275,7 +276,7 @@
           var pid = process.process.pid;
           var wait = Duration(seconds: 10);
           print("Testee has pid $pid, waiting $wait before continuing");
-          sleep(wait);
+          io.sleep(wait);
         }
         setupAddresses(serverAddress);
         vm = await vmServiceConnectUri(serviceWebsocketAddress);
diff --git a/pkg/vm_service/test/file_service_test.dart b/pkg/vm_service/test/file_service_test.dart
new file mode 100644
index 0000000..c74fdae
--- /dev/null
+++ b/pkg/vm_service/test/file_service_test.dart
@@ -0,0 +1,113 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:developer';
+import 'dart:io' as io;
+
+import 'package:vm_service/vm_service.dart';
+import 'package:test/test.dart';
+
+import 'common/test_helper.dart';
+
+Future setupFiles() async {
+  final dir = await io.Directory.systemTemp.createTemp('file_service');
+  var writingFile;
+  var readingFile;
+
+  void closeDown() {
+    if (writingFile != null) {
+      writingFile.closeSync();
+    }
+    if (readingFile != null) {
+      readingFile.closeSync();
+    }
+    dir.deleteSync(recursive: true);
+  }
+
+  Future<ServiceExtensionResponse> cleanup(ignored_a, ignored_b) {
+    closeDown();
+    final result = jsonEncode({'type': 'foobar'});
+    return Future.value(ServiceExtensionResponse.result(result));
+  }
+
+  Future<ServiceExtensionResponse> setup(ignored_a, ignored_b) async {
+    try {
+      final filePath = dir.path + io.Platform.pathSeparator + "file";
+      final f = io.File(filePath);
+      writingFile = await f.open(mode: io.FileMode.write);
+      await writingFile.writeByte(42);
+      await writingFile.writeByte(42);
+      await writingFile.writeByte(42);
+
+      final file = io.File.fromUri(io.Platform.script);
+      readingFile = await file.open();
+      await readingFile.readByte();
+      await readingFile.readByte();
+      await readingFile.readByte();
+      await readingFile.readByte();
+      await readingFile.readByte();
+
+      // The utility functions should close the files after them, so we
+      // don't expect the calls below to result in open files.
+      final writeTemp = dir.path + io.Platform.pathSeparator + "other_file";
+      final utilFile = io.File(writeTemp);
+      await utilFile.writeAsString('foobar');
+      final readTemp = io.File(writeTemp);
+      await readTemp.readAsString();
+    } catch (e) {
+      closeDown();
+      rethrow;
+    }
+    final result = jsonEncode({'type': 'foobar'});
+    return Future.value(ServiceExtensionResponse.result(result));
+  }
+
+  registerExtension('ext.dart.io.cleanup', cleanup);
+  registerExtension('ext.dart.io.setup', setup);
+}
+
+var fileTests = <IsolateTest>[
+  (VmService service, IsolateRef isolate) async {
+    await service.callServiceExtension(
+      'ext.dart.io.setup',
+      isolateId: isolate.id,
+    );
+    try {
+      final result = await service.getOpenFiles(isolate.id);
+      expect(result, isA<OpenFileList>());
+      expect(result.files.length, equals(2));
+      final writing = await service.getOpenFileById(
+        isolate.id,
+        result.files[0].id,
+      );
+
+      expect(writing.readBytes, 0);
+      expect(writing.readCount, 0);
+      expect(writing.writeCount, 3);
+      expect(writing.writeBytes, 3);
+      expect(writing.lastWriteTime.millisecondsSinceEpoch, greaterThan(0));
+      expect(writing.lastReadTime.millisecondsSinceEpoch, 0);
+
+      final reading = await service.getOpenFileById(
+        isolate.id,
+        result.files[1].id,
+      );
+      expect(reading.readBytes, 5);
+      expect(reading.readCount, 5);
+      expect(reading.writeCount, 0);
+      expect(reading.writeBytes, 0);
+      expect(reading.lastWriteTime.millisecondsSinceEpoch, 0);
+      expect(reading.lastReadTime.millisecondsSinceEpoch, greaterThan(0));
+    } finally {
+      await service.callServiceExtension(
+        'ext.dart.io.cleanup',
+        isolateId: isolate.id,
+      );
+    }
+  },
+];
+
+main(args) async => runIsolateTests(args, fileTests, testeeBefore: setupFiles);
diff --git a/pkg/vm_service/test/process_service_test.dart b/pkg/vm_service/test/process_service_test.dart
new file mode 100644
index 0000000..a47359a
--- /dev/null
+++ b/pkg/vm_service/test/process_service_test.dart
@@ -0,0 +1,151 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:developer';
+import 'dart:io' as io;
+
+import 'package:path/path.dart' as path;
+import 'package:test/test.dart';
+import 'package:vm_service/vm_service.dart';
+
+import 'common/test_helper.dart';
+
+final dartJITBinary = path.join(path.dirname(io.Platform.resolvedExecutable),
+    'dart' + path.extension(io.Platform.resolvedExecutable));
+
+Future setupProcesses() async {
+  final dir = await io.Directory.systemTemp.createTemp('file_service');
+
+  final args = [
+    ...io.Platform.executableArguments,
+    '--pause_isolates_on_start',
+    io.Platform.script.toFilePath(),
+  ];
+  io.Process process1;
+  io.Process process2;
+  io.Process process3;
+
+  void closeDown() {
+    if (process1 != null) {
+      process1.kill();
+    }
+    if (process2 != null) {
+      process2.kill();
+    }
+    if (process3 != null) {
+      process3.kill();
+    }
+    dir.deleteSync(recursive: true);
+  }
+
+  Future<ServiceExtensionResponse> cleanup(ignored_a, ignored_b) {
+    closeDown();
+    final result = jsonEncode({'type': 'foobar'});
+    return Future.value(ServiceExtensionResponse.result(result));
+  }
+
+  Future<ServiceExtensionResponse> setup(ignored_a, ignored_b) async {
+    try {
+      process1 = await io.Process.start(io.Platform.executable, args);
+      process2 =
+          await io.Process.start(io.Platform.executable, args..add('foobar'));
+      final codeFilePath = dir.path + io.Platform.pathSeparator + "other_file";
+      final codeFile = io.File(codeFilePath);
+      await codeFile.writeAsString('''
+          import "dart:io";
+
+          void main() async {
+            await stdin.drain();
+          }
+          ''');
+      process3 = await io.Process.start(
+          dartJITBinary, [...io.Platform.executableArguments, codeFilePath]);
+    } catch (_) {
+      closeDown();
+      rethrow;
+    }
+
+    final result = jsonEncode({
+      'type': 'foobar',
+      'pids': [process1.pid, process2.pid, process3.pid]
+    });
+    return Future.value(ServiceExtensionResponse.result(result));
+  }
+
+  Future<ServiceExtensionResponse> closeStdin(ignored_a, ignored_b) {
+    process3.stdin.close();
+    return process3.exitCode.then<ServiceExtensionResponse>((int exit) {
+      final result = jsonEncode({'type': 'foobar'});
+      return ServiceExtensionResponse.result(result);
+    });
+  }
+
+  registerExtension('ext.dart.io.cleanup', cleanup);
+  registerExtension('ext.dart.io.setup', setup);
+  registerExtension('ext.dart.io.closeStdin', closeStdin);
+}
+
+final processTests = <IsolateTest>[
+  // Initial.
+  (VmService service, IsolateRef isolate) async {
+    final setup = await service.callServiceExtension(
+      'ext.dart.io.setup',
+      isolateId: isolate.id,
+    );
+    try {
+      SpawnedProcessList all = await service.getSpawnedProcesses(isolate.id);
+      expect(all.processes.length, equals(3));
+
+      final first = await service.getSpawnedProcessById(
+        isolate.id,
+        all.processes[0].id,
+      );
+
+      expect(first.name, io.Platform.executable);
+      expect(first.pid, equals(setup.json['pids'][0]));
+      expect(first.arguments.contains('foobar'), isFalse);
+      expect(first.startedAt, greaterThan(0));
+
+      final second = await service.getSpawnedProcessById(
+        isolate.id,
+        all.processes[1].id,
+      );
+
+      expect(second.name, io.Platform.executable);
+      expect(second.pid, equals(setup.json['pids'][1]));
+      expect(second.arguments.contains('foobar'), isTrue);
+      expect(second.pid != first.pid, isTrue);
+      expect(second.startedAt, greaterThan(0));
+      expect(second.startedAt, greaterThanOrEqualTo(first.startedAt));
+
+      final third = await service.getSpawnedProcessById(
+        isolate.id,
+        all.processes[2].id,
+      );
+
+      expect(third.name, dartJITBinary);
+      expect(third.pid, equals(setup.json['pids'][2]));
+      expect(third.pid != first.pid, isTrue);
+      expect(third.pid != second.pid, isTrue);
+      expect(third.startedAt, greaterThanOrEqualTo(second.startedAt));
+
+      await service.callServiceExtension(
+        'ext.dart.io.closeStdin',
+        isolateId: isolate.id,
+      );
+      all = await service.getSpawnedProcesses(isolate.id);
+      expect(all.processes.length, equals(2));
+    } finally {
+      await service.callServiceExtension(
+        'ext.dart.io.cleanup',
+        isolateId: isolate.id,
+      );
+    }
+  },
+];
+
+main(args) async =>
+    runIsolateTests(args, processTests, testeeBefore: setupProcesses);
diff --git a/runtime/observatory/tests/service/file_service_test.dart b/runtime/observatory/tests/service/file_service_test.dart
index 08c6d71..b347920 100644
--- a/runtime/observatory/tests/service/file_service_test.dart
+++ b/runtime/observatory/tests/service/file_service_test.dart
@@ -12,7 +12,7 @@
 import 'test_helper.dart';
 
 Future setupFiles() async {
-  var dir = await io.Directory.systemTemp.createTemp('file_service');
+  final dir = await io.Directory.systemTemp.createTemp('file_service');
   var writingFile;
   var readingFile;
 
@@ -28,20 +28,20 @@
 
   Future<ServiceExtensionResponse> cleanup(ignored_a, ignored_b) {
     closeDown();
-    var result = jsonEncode({'type': 'foobar'});
-    return new Future.value(new ServiceExtensionResponse.result(result));
+    final result = jsonEncode({'type': 'foobar'});
+    return Future.value(ServiceExtensionResponse.result(result));
   }
 
   Future<ServiceExtensionResponse> setup(ignored_a, ignored_b) async {
     try {
-      var filePath = dir.path + io.Platform.pathSeparator + "file";
-      var f = new io.File(filePath);
+      final filePath = dir.path + io.Platform.pathSeparator + "file";
+      final f = io.File(filePath);
       writingFile = await f.open(mode: io.FileMode.write);
       await writingFile.writeByte(42);
       await writingFile.writeByte(42);
       await writingFile.writeByte(42);
 
-      var file = new io.File.fromUri(io.Platform.script);
+      final file = io.File.fromUri(io.Platform.script);
       readingFile = await file.open();
       await readingFile.readByte();
       await readingFile.readByte();
@@ -51,18 +51,18 @@
 
       // The utility functions should close the files after them, so we
       // don't expect the calls below to result in open files.
-      var writeTemp = dir.path + io.Platform.pathSeparator + "other_file";
-      var utilFile = new io.File(writeTemp);
+      final writeTemp = dir.path + io.Platform.pathSeparator + "other_file";
+      final utilFile = io.File(writeTemp);
       await utilFile.writeAsString('foobar');
-      var readTemp = new io.File(writeTemp);
-      var result = await readTemp.readAsString();
+      final readTemp = io.File(writeTemp);
+      final result = await readTemp.readAsString();
       Expect.equals(result, 'foobar');
     } catch (e) {
       closeDown();
       throw e;
     }
-    var result = jsonEncode({'type': 'foobar'});
-    return new Future.value(new ServiceExtensionResponse.result(result));
+    final result = jsonEncode({'type': 'foobar'});
+    return Future.value(ServiceExtensionResponse.result(result));
   }
 
   registerExtension('ext.dart.io.cleanup', cleanup);
@@ -73,30 +73,29 @@
   (Isolate isolate) async {
     await isolate.invokeRpcNoUpgrade('ext.dart.io.setup', {});
     try {
-      var result =
+      final result =
           await isolate.invokeRpcNoUpgrade('ext.dart.io.getOpenFiles', {});
-      expect(result['type'], equals('_openfiles'));
+      expect(result['type'], equals('OpenFileList'));
+      expect(result['files'].length, equals(2));
+      final writing = await isolate.invokeRpcNoUpgrade(
+          'ext.dart.io.getOpenFileById', {'id': result['files'][0]['id']});
 
-      expect(result['data'].length, equals(2));
-      var writing = await isolate.invokeRpcNoUpgrade(
-          'ext.dart.io.getFileByID', {'id': result['data'][0]['id']});
-
-      expect(writing['totalRead'], equals(0));
+      expect(writing['readBytes'], equals(0));
       expect(writing['readCount'], equals(0));
       expect(writing['writeCount'], equals(3));
-      expect(writing['totalWritten'], equals(3));
-      expect(writing['lastWrite'], greaterThan(0));
-      expect(writing['lastRead'], equals(0));
+      expect(writing['writeBytes'], equals(3));
+      expect(writing['lastWriteTime'], greaterThan(0));
+      expect(writing['lastReadTime'], equals(0));
 
-      var reading = await isolate.invokeRpcNoUpgrade(
-          'ext.dart.io.getFileByID', {'id': result['data'][1]['id']});
+      final reading = await isolate.invokeRpcNoUpgrade(
+          'ext.dart.io.getOpenFileById', {'id': result['files'][1]['id']});
 
-      expect(reading['totalRead'], equals(5));
+      expect(reading['readBytes'], equals(5));
       expect(reading['readCount'], equals(5));
       expect(reading['writeCount'], equals(0));
-      expect(reading['totalWritten'], equals(0));
-      expect(reading['lastWrite'], equals(0));
-      expect(reading['lastRead'], greaterThan(0));
+      expect(reading['writeBytes'], equals(0));
+      expect(reading['lastWriteTime'], equals(0));
+      expect(reading['lastReadTime'], greaterThan(0));
     } finally {
       await isolate.invokeRpcNoUpgrade('ext.dart.io.cleanup', {});
     }
diff --git a/runtime/observatory/tests/service/process_service_test.dart b/runtime/observatory/tests/service/process_service_test.dart
index ced9326..8840ef1 100644
--- a/runtime/observatory/tests/service/process_service_test.dart
+++ b/runtime/observatory/tests/service/process_service_test.dart
@@ -13,20 +13,20 @@
 
 import 'test_helper.dart';
 
-final dartJITBinary = path.join(path.dirname(io.Platform.executable),
-    'dart' + path.extension(io.Platform.executable));
+final dartJITBinary = path.join(path.dirname(io.Platform.resolvedExecutable),
+    'dart' + path.extension(io.Platform.resolvedExecutable));
 
 Future setupProcesses() async {
-  var dir = await io.Directory.systemTemp.createTemp('file_service');
+  final dir = await io.Directory.systemTemp.createTemp('file_service');
 
-  var args = [
+  final args = [
     ...io.Platform.executableArguments,
     '--pause_isolates_on_start',
     io.Platform.script.toFilePath(),
   ];
-  var process1;
-  var process2;
-  var process3;
+  io.Process process1;
+  io.Process process2;
+  io.Process process3;
 
   void closeDown() {
     if (process1 != null) {
@@ -43,8 +43,8 @@
 
   Future<ServiceExtensionResponse> cleanup(ignored_a, ignored_b) {
     closeDown();
-    var result = jsonEncode({'type': 'foobar'});
-    return new Future.value(new ServiceExtensionResponse.result(result));
+    final result = jsonEncode({'type': 'foobar'});
+    return Future.value(ServiceExtensionResponse.result(result));
   }
 
   Future<ServiceExtensionResponse> setup(ignored_a, ignored_b) async {
@@ -52,8 +52,8 @@
       process1 = await io.Process.start(io.Platform.executable, args);
       process2 =
           await io.Process.start(io.Platform.executable, args..add('foobar'));
-      var codeFilePath = dir.path + io.Platform.pathSeparator + "other_file";
-      var codeFile = new io.File(codeFilePath);
+      final codeFilePath = dir.path + io.Platform.pathSeparator + "other_file";
+      final codeFile = io.File(codeFilePath);
       await codeFile.writeAsString('''
           import "dart:io";
 
@@ -63,23 +63,23 @@
           ''');
       process3 = await io.Process.start(
           dartJITBinary, [...io.Platform.executableArguments, codeFilePath]);
-    } catch (e) {
+    } catch (_) {
       closeDown();
-      throw e;
+      rethrow;
     }
 
-    var result = jsonEncode({
+    final result = jsonEncode({
       'type': 'foobar',
       'pids': [process1.pid, process2.pid, process3.pid]
     });
-    return new Future.value(new ServiceExtensionResponse.result(result));
+    return Future.value(ServiceExtensionResponse.result(result));
   }
 
   Future<ServiceExtensionResponse> closeStdin(ignored_a, ignored_b) {
     process3.stdin.close();
     return process3.exitCode.then<ServiceExtensionResponse>((int exit) {
-      var result = jsonEncode({'type': 'foobar'});
-      return new ServiceExtensionResponse.result(result);
+      final result = jsonEncode({'type': 'foobar'});
+      return ServiceExtensionResponse.result(result);
     });
   }
 
@@ -88,26 +88,28 @@
   registerExtension('ext.dart.io.closeStdin', closeStdin);
 }
 
-var processTests = <IsolateTest>[
+final processTests = <IsolateTest>[
   // Initial.
   (Isolate isolate) async {
-    var setup = await isolate.invokeRpcNoUpgrade('ext.dart.io.setup', {});
+    final setup = await isolate.invokeRpcNoUpgrade('ext.dart.io.setup', {});
     try {
-      var all =
-          await isolate.invokeRpcNoUpgrade('ext.dart.io.getProcesses', {});
-      expect(all['type'], equals('_startedprocesses'));
+      var all = await isolate
+          .invokeRpcNoUpgrade('ext.dart.io.getSpawnedProcesses', {});
+      expect(all['type'], equals('SpawnedProcessList'));
 
-      expect(all['data'].length, equals(3));
+      expect(all['processes'].length, equals(3));
 
-      var first = await isolate.invokeRpcNoUpgrade(
-          'ext.dart.io.getProcessById', {'id': all['data'][0]['id']});
+      final first = await isolate.invokeRpcNoUpgrade(
+          'ext.dart.io.getSpawnedProcessById',
+          {'id': all['processes'][0]['id']});
       expect(first['name'], io.Platform.executable);
       expect(first['pid'], equals(setup['pids'][0]));
       expect(first['arguments'].contains('foobar'), isFalse);
       expect(first['startedAt'], greaterThan(0));
 
-      var second = await isolate.invokeRpcNoUpgrade(
-          'ext.dart.io.getProcessById', {'id': all['data'][1]['id']});
+      final second = await isolate.invokeRpcNoUpgrade(
+          'ext.dart.io.getSpawnedProcessById',
+          {'id': all['processes'][1]['id']});
       expect(second['name'], io.Platform.executable);
       expect(second['pid'], equals(setup['pids'][1]));
       expect(second['arguments'].contains('foobar'), isTrue);
@@ -115,8 +117,9 @@
       expect(second['startedAt'], greaterThan(0));
       expect(second['startedAt'], greaterThanOrEqualTo(first['startedAt']));
 
-      var third = await isolate.invokeRpcNoUpgrade(
-          'ext.dart.io.getProcessById', {'id': all['data'][2]['id']});
+      final third = await isolate.invokeRpcNoUpgrade(
+          'ext.dart.io.getSpawnedProcessById',
+          {'id': all['processes'][2]['id']});
       expect(third['name'], dartJITBinary);
       expect(third['pid'], equals(setup['pids'][2]));
       expect(third['pid'] != first['pid'], isTrue);
@@ -124,9 +127,10 @@
       expect(third['startedAt'], greaterThanOrEqualTo(second['startedAt']));
 
       await isolate.invokeRpcNoUpgrade('ext.dart.io.closeStdin', {});
-      all = await isolate.invokeRpcNoUpgrade('ext.dart.io.getProcesses', {});
-      expect(all['type'], equals('_startedprocesses'));
-      expect(all['data'].length, equals(2));
+      all = await isolate
+          .invokeRpcNoUpgrade('ext.dart.io.getSpawnedProcesses', {});
+      expect(all['type'], equals('SpawnedProcessList'));
+      expect(all['processes'].length, equals(2));
     } finally {
       await isolate.invokeRpcNoUpgrade('ext.dart.io.cleanup', {});
     }
diff --git a/runtime/observatory/tests/service/tcp_socket_closing_service_test.dart b/runtime/observatory/tests/service/tcp_socket_closing_service_test.dart
deleted file mode 100644
index 8996577..0000000
--- a/runtime/observatory/tests/service/tcp_socket_closing_service_test.dart
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'dart:async';
-import 'dart:convert';
-import 'dart:io' as io;
-import 'package:observatory/service_io.dart';
-import 'package:test/test.dart';
-import 'test_helper.dart';
-
-/// Test that we correctly remove sockets that have been closed from the list
-/// of open sockets. We explicitly leave one socket open.
-
-Future setup() async {
-  var serverSocket = await io.ServerSocket.bind('127.0.0.1', 0);
-  serverSocket.listen((s) {
-    s.drain();
-    s.close();
-  });
-  var socket = await io.Socket.connect("127.0.0.1", serverSocket.port);
-  socket.write("foobar");
-  socket.write("foobar");
-
-  await socket.flush();
-  await socket.close();
-  await socket.drain();
-
-  var socket2 = await io.Socket.connect("127.0.0.1", serverSocket.port);
-  socket2.write("foobarfoobar");
-  await socket2.flush();
-  await socket2.close();
-  await socket2.drain();
-  await serverSocket.close();
-
-  var server = await io.RawDatagramSocket.bind('127.0.0.1', 0);
-  server.listen((io.RawSocketEvent event) {
-    if (event == io.RawSocketEvent.read) {
-      io.Datagram dg = server.receive();
-      dg.data.forEach((x) => true);
-      server.close();
-    }
-  });
-  var client = await io.RawDatagramSocket.bind('127.0.0.1', 0);
-  client.send(utf8.encoder.convert('foobar'),
-      new io.InternetAddress('127.0.0.1'), server.port);
-  client.close();
-
-  // The one socket to expect.
-  await io.ServerSocket.bind('127.0.0.1', 0);
-}
-
-var tests = <IsolateTest>[
-  // Initial.
-  (Isolate isolate) async {
-    var result =
-        await isolate.invokeRpcNoUpgrade('ext.dart.io.getOpenSockets', {});
-    expect(result['type'], equals('_opensockets'));
-    // We expect only one socket to be open, the server socket create at the
-    // end of test.
-    expect(result['data'].length, equals(1));
-    var server = await isolate.invokeRpcNoUpgrade(
-        'ext.dart.io.getSocketByID', {'id': result['data'][0]['id']});
-    expect(server['listening'], isTrue);
-    expect(server['lastRead'], equals(0));
-    expect(server['totalRead'], equals(0));
-    expect(server['lastWrite'], equals(0));
-    expect(server['totalWritten'], equals(0));
-    expect(server['writeCount'], equals(0));
-    expect(server['readCount'], equals(0));
-  },
-];
-
-main(args) async => runIsolateTests(args, tests, testeeBefore: setup);
diff --git a/runtime/observatory/tests/service/tcp_socket_service_test.dart b/runtime/observatory/tests/service/tcp_socket_service_test.dart
deleted file mode 100644
index 9e61a01..0000000
--- a/runtime/observatory/tests/service/tcp_socket_service_test.dart
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'dart:async';
-import 'dart:convert';
-import 'dart:io' as io;
-import 'package:observatory/service_io.dart';
-import 'package:test/test.dart';
-import 'test_helper.dart';
-
-Future setupTCP() async {
-  // Note that we don't close after us, by design we leave the sockets opens
-  // to allow us to query them from the other isolate.
-  var serverSocket = await io.ServerSocket.bind('127.0.0.1', 0);
-  serverSocket.listen((s) {
-    utf8.decoder.bind(s).listen(print);
-    s.close();
-  });
-  var socket = await io.Socket.connect("127.0.0.1", serverSocket.port);
-  socket.write("foobar");
-  socket.write("foobar");
-  await socket.flush();
-
-  var socket2 = await io.Socket.connect("127.0.0.1", serverSocket.port);
-  socket2.write("foobarfoobar");
-  await socket2.flush();
-}
-
-var tcpTests = <IsolateTest>[
-  // Initial.
-  (Isolate isolate) async {
-    var result =
-        await isolate.invokeRpcNoUpgrade('ext.dart.io.getOpenSockets', {});
-    expect(result['type'], equals('_opensockets'));
-    // We expect 3 sockets to be open (in this order):
-    //   The server socket accepting connections, on port X
-    //   The accepted connection on the client, on port Y
-    //   The client connection, on port X
-    if (result['data'].length != 5) {
-      print(result['data']);
-    }
-    expect(result['data'].length, equals(5));
-    // The first socket will have a name like listening:127.0.0.1:X
-    // The second will have a name like 127.0.0.1:Y
-    // The third will have a name like 127.0.0.1:X
-    expect(result['data'][0]['name'].startsWith('listening:127.0.0.1'), isTrue);
-    expect(result['data'][1]['name'].startsWith('127.0.0.1:'), isTrue);
-    expect(result['data'][2]['name'].startsWith('127.0.0.1:'), isTrue);
-
-    var listening = await isolate.invokeRpcNoUpgrade(
-        'ext.dart.io.getSocketByID', {'id': result['data'][0]['id']});
-    expect(listening['id'], equals(result['data'][0]['id']));
-    expect(listening['listening'], isTrue);
-    expect(listening['socketType'], equals('TCP'));
-    expect(listening['port'], greaterThanOrEqualTo(1024));
-    expect(listening['lastRead'], greaterThan(0));
-
-    expect(listening['totalRead'], equals(2));
-    expect(listening['lastWrite'], equals(0));
-    expect(listening['totalWritten'], equals(0));
-    expect(listening['writeCount'], equals(0));
-    expect(listening['readCount'], equals(2));
-    expect(listening['remoteHost'], equals('NA'));
-    expect(listening['remotePort'], equals('NA'));
-
-    var client = await isolate.invokeRpcNoUpgrade(
-        'ext.dart.io.getSocketByID', {'id': result['data'][1]['id']});
-    expect(client['id'], equals(result['data'][1]['id']));
-
-    var server = await isolate.invokeRpcNoUpgrade(
-        'ext.dart.io.getSocketByID', {'id': result['data'][2]['id']});
-    expect(server['id'], equals(result['data'][2]['id']));
-
-    // We expect the client to be connected on the port and
-    // host of the listening socket.
-    expect(client['remotePort'], equals(listening['port']));
-    expect(client['remoteHost'], equals(listening['host']));
-    // We expect the third socket (accepted server) to be connected to the
-    // same port and host as the listening socket (the listening one).
-    expect(server['port'], equals(listening['port']));
-    expect(server['host'], equals(listening['host']));
-
-    expect(client['listening'], isFalse);
-    expect(server['listening'], isFalse);
-
-    expect(client['socketType'], equals('TCP'));
-    expect(server['socketType'], equals('TCP'));
-
-    // We are using no reserved ports.
-    expect(client['port'], greaterThanOrEqualTo(1024));
-    expect(server['port'], greaterThanOrEqualTo(1024));
-
-    // The client and server "mirror" each other in reads and writes, and the
-    // timestamps are in correct order.
-    expect(client['lastRead'], equals(0));
-    expect(server['lastRead'], greaterThan(0));
-    expect(client['totalRead'], equals(0));
-    expect(server['totalRead'], equals(12));
-    expect(client['readCount'], equals(0));
-    expect(server['readCount'], greaterThanOrEqualTo(1));
-
-    expect(client['lastWrite'], greaterThan(0));
-    expect(server['lastWrite'], equals(0));
-    expect(client['totalWritten'], equals(12));
-    expect(server['totalWritten'], equals(0));
-    expect(client['writeCount'], greaterThanOrEqualTo(2));
-    expect(server['writeCount'], equals(0));
-
-    // Order
-    // Stopwatch resolution on windows can make us have the same timestamp.
-    if (io.Platform.isWindows) {
-      expect(server['lastRead'], greaterThanOrEqualTo(client['lastWrite']));
-    } else {
-      expect(server['lastRead'], greaterThan(client['lastWrite']));
-    }
-
-    var secondClient = await isolate.invokeRpcNoUpgrade(
-        'ext.dart.io.getSocketByID', {'id': result['data'][3]['id']});
-    expect(secondClient['id'], equals(result['data'][3]['id']));
-    var secondServer = await isolate.invokeRpcNoUpgrade(
-        'ext.dart.io.getSocketByID', {'id': result['data'][4]['id']});
-    expect(secondServer['id'], equals(result['data'][4]['id']));
-
-    // We expect the client to be connected on the port and
-    // host of the listening socket.
-    expect(secondClient['remotePort'], equals(listening['port']));
-    expect(secondClient['remoteHost'], equals(listening['host']));
-    // We expect the third socket (accepted server) to be connected to the
-    // same port and host as the listening socket (the listening one).
-    expect(secondServer['port'], equals(listening['port']));
-    expect(secondServer['host'], equals(listening['host']));
-
-    expect(secondClient['listening'], isFalse);
-    expect(secondServer['listening'], isFalse);
-
-    expect(secondClient['socketType'], equals('TCP'));
-    expect(secondServer['socketType'], equals('TCP'));
-
-    // We are using no reserved ports.
-    expect(secondClient['port'], greaterThanOrEqualTo(1024));
-    expect(secondServer['port'], greaterThanOrEqualTo(1024));
-
-    // The client and server "mirror" each other in reads and writes, and the
-    // timestamps are in correct order.
-    expect(secondClient['lastRead'], equals(0));
-    expect(secondServer['lastRead'], greaterThan(0));
-    expect(secondClient['totalRead'], equals(0));
-    expect(secondServer['totalRead'], equals(12));
-    expect(secondClient['readCount'], equals(0));
-    expect(secondServer['readCount'], greaterThanOrEqualTo(1));
-
-    expect(secondClient['lastWrite'], greaterThan(0));
-    expect(secondServer['lastWrite'], equals(0));
-    expect(secondClient['totalWritten'], equals(12));
-    expect(secondServer['totalWritten'], equals(0));
-    expect(secondClient['writeCount'], greaterThanOrEqualTo(1));
-    expect(secondServer['writeCount'], equals(0));
-
-    // Order
-    // Stopwatch resolution on windows make us sometimes report the same value.
-    if (io.Platform.isWindows) {
-      expect(server['lastRead'], greaterThanOrEqualTo(client['lastWrite']));
-    } else {
-      expect(server['lastRead'], greaterThan(client['lastWrite']));
-    }
-  },
-];
-
-main(args) async => runIsolateTests(args, tcpTests, testeeBefore: setupTCP);
diff --git a/runtime/observatory/tests/service/udp_socket_service_test.dart b/runtime/observatory/tests/service/udp_socket_service_test.dart
deleted file mode 100644
index e49aa97..0000000
--- a/runtime/observatory/tests/service/udp_socket_service_test.dart
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'dart:async';
-import 'dart:convert';
-import 'dart:io' as io;
-import 'package:observatory/service_io.dart';
-import 'package:test/test.dart';
-import 'test_helper.dart';
-
-Future setupUDP() async {
-  // Service might attach to us after we completed the setup but
-  // before we actually received a datagram - if it will start inspecting
-  // IO metrics at that point then it will see that no reads happened
-  // and the test will fail. That is why we don't consider setup complete
-  // until after we received the datagram.
-  final doneCompleter = Completer<void>();
-
-  var server = await io.RawDatagramSocket.bind('127.0.0.1', 0);
-  server.listen((io.RawSocketEvent event) {
-    if (event == io.RawSocketEvent.read) {
-      io.Datagram dg = server.receive();
-      dg.data.forEach((x) => true);
-      if (!doneCompleter.isCompleted) {
-        doneCompleter.complete(null);
-      }
-    }
-  });
-  var client = await io.RawDatagramSocket.bind('127.0.0.1', 0);
-  client.send(utf8.encoder.convert('foobar'),
-      new io.InternetAddress('127.0.0.1'), server.port);
-
-  // Wait for datagram to arrive.
-  await doneCompleter.future;
-}
-
-var udpTests = <IsolateTest>[
-  // Initial.
-  (Isolate isolate) async {
-    var result =
-        await isolate.invokeRpcNoUpgrade('ext.dart.io.getOpenSockets', {});
-    expect(result['type'], equals('_opensockets'));
-    // We expect 2 sockets to be open (in this order):
-    //   The server socket accepting connections, on port X
-    //   The client socket on port Y
-    expect(result['data'].length, equals(2));
-    // The first socket will have a name like listening:127.0.0.1:X
-    // The second will have a name like 127.0.0.1:Y
-    // The third will have a name like 127.0.0.1:X
-    expect(result['data'][0]['name'].startsWith('127.0.0.1'), isTrue);
-    expect(result['data'][1]['name'].startsWith('127.0.0.1:'), isTrue);
-
-    var server = await isolate.invokeRpcNoUpgrade(
-        'ext.dart.io.getSocketByID', {'id': result['data'][0]['id']});
-    expect(server['id'], equals(result['data'][0]['id']));
-    expect(server['remotePort'], equals('NA'));
-    expect(server['remoteHost'], equals('NA'));
-    expect(server['listening'], isFalse);
-    expect(server['socketType'], equals('UDP'));
-    expect(server['port'], greaterThanOrEqualTo(1024));
-    final now = DateTime.now().millisecondsSinceEpoch;
-    expect(
-        server['lastRead'], closeTo(now, Duration(seconds: 10).inMilliseconds));
-    expect(server['totalRead'], equals(6));
-    expect(server['lastWrite'], equals(0));
-    expect(server['totalWritten'], equals(0));
-    expect(server['writeCount'], equals(0));
-    expect(server['readCount'], greaterThanOrEqualTo(1));
-
-    var client = await isolate.invokeRpcNoUpgrade(
-        'ext.dart.io.getSocketByID', {'id': result['data'][1]['id']});
-    expect(client['id'], equals(result['data'][1]['id']));
-    expect(client['remotePort'], equals('NA'));
-    expect(client['remoteHost'], equals('NA'));
-    expect(client['listening'], isFalse);
-    expect(client['socketType'], equals('UDP'));
-    expect(client['port'], greaterThanOrEqualTo(1024));
-    expect(client['lastRead'], equals(0));
-    expect(client['totalRead'], equals(0));
-    // Stopwatch resolution on windows makes us sometimes report 0;
-    if (io.Platform.isWindows) {
-      expect(client['lastWrite'], greaterThanOrEqualTo(0));
-    } else {
-      expect(client['lastWrite'], greaterThan(0));
-    }
-    expect(client['totalWritten'], equals(6));
-    expect(client['writeCount'], greaterThanOrEqualTo(1));
-    expect(client['readCount'], equals(0));
-  },
-];
-
-main(args) async => runIsolateTests(args, udpTests, testeeBefore: setupUDP);
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index a2226fb..8c96d22 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -3133,10 +3133,6 @@
 }
 
 TEST_CASE(DartAPI_WeakPersistentHandle) {
-  // GCs due to allocations or weak handle creation can cause early promotion
-  // and interfer with the scenario this test is verifying.
-  NoHeapGrowthControlScope force_growth;
-
   Dart_Handle local_new_ref = Dart_Null();
   weak_new_ref = Dart_NewWeakPersistentHandle(local_new_ref, NULL, 0,
                                               WeakPersistentHandleCallback);
@@ -3148,25 +3144,32 @@
   {
     Dart_EnterScope();
 
-    // Create an object in new space.
-    Dart_Handle new_ref = AllocateNewString("new string");
-    EXPECT_VALID(new_ref);
+    Dart_Handle new_ref, old_ref;
+    {
+      // GCs due to allocations or weak handle creation can cause early
+      // promotion and interfere with the scenario this test is verifying.
+      NoHeapGrowthControlScope force_growth;
 
-    // Create an object in old space.
-    Dart_Handle old_ref = AllocateOldString("old string");
-    EXPECT_VALID(old_ref);
+      // Create an object in new space.
+      new_ref = AllocateNewString("new string");
+      EXPECT_VALID(new_ref);
 
-    // Create a weak ref to the new space object.
-    weak_new_ref = Dart_NewWeakPersistentHandle(new_ref, NULL, 0,
-                                                WeakPersistentHandleCallback);
-    EXPECT_VALID(AsHandle(weak_new_ref));
-    EXPECT(!Dart_IsNull(AsHandle(weak_new_ref)));
+      // Create an object in old space.
+      old_ref = AllocateOldString("old string");
+      EXPECT_VALID(old_ref);
 
-    // Create a weak ref to the old space object.
-    weak_old_ref = Dart_NewWeakPersistentHandle(old_ref, NULL, 0,
-                                                WeakPersistentHandleCallback);
-    EXPECT_VALID(AsHandle(weak_old_ref));
-    EXPECT(!Dart_IsNull(AsHandle(weak_old_ref)));
+      // Create a weak ref to the new space object.
+      weak_new_ref = Dart_NewWeakPersistentHandle(new_ref, NULL, 0,
+                                                  WeakPersistentHandleCallback);
+      EXPECT_VALID(AsHandle(weak_new_ref));
+      EXPECT(!Dart_IsNull(AsHandle(weak_new_ref)));
+
+      // Create a weak ref to the old space object.
+      weak_old_ref = Dart_NewWeakPersistentHandle(old_ref, NULL, 0,
+                                                  WeakPersistentHandleCallback);
+      EXPECT_VALID(AsHandle(weak_old_ref));
+      EXPECT(!Dart_IsNull(AsHandle(weak_old_ref)));
+    }
 
     {
       TransitionNativeToVM transition(thread);
@@ -3264,10 +3267,6 @@
 }
 
 TEST_CASE(DartAPI_FinalizableHandle) {
-  // GCs due to allocations or weak handle creation can cause early promotion
-  // and interfer with the scenario this test is verifying.
-  NoHeapGrowthControlScope force_growth;
-
   void* peer = reinterpret_cast<void*>(0);
   Dart_Handle local_new_ref = Dart_Null();
   finalizable_new_ref = Dart_NewFinalizableHandle(local_new_ref, peer, 0,
@@ -3283,25 +3282,32 @@
   {
     Dart_EnterScope();
 
-    // Create an object in new space.
-    Dart_Handle new_ref = AllocateNewString("new string");
-    EXPECT_VALID(new_ref);
+    Dart_Handle new_ref, old_ref;
+    {
+      // GCs due to allocations or weak handle creation can cause early
+      // promotion and interfere with the scenario this test is verifying.
+      NoHeapGrowthControlScope force_growth;
 
-    // Create an object in old space.
-    Dart_Handle old_ref = AllocateOldString("old string");
-    EXPECT_VALID(old_ref);
+      // Create an object in new space.
+      new_ref = AllocateNewString("new string");
+      EXPECT_VALID(new_ref);
 
-    // Create a weak ref to the new space object.
-    peer = reinterpret_cast<void*>(2);
-    finalizable_new_ref =
-        Dart_NewFinalizableHandle(new_ref, peer, 0, FinalizableHandleCallback);
-    finalizable_new_ref_peer = peer;
+      // Create an object in old space.
+      old_ref = AllocateOldString("old string");
+      EXPECT_VALID(old_ref);
 
-    // Create a weak ref to the old space object.
-    peer = reinterpret_cast<void*>(3);
-    finalizable_old_ref =
-        Dart_NewFinalizableHandle(old_ref, peer, 0, FinalizableHandleCallback);
-    finalizable_old_ref_peer = peer;
+      // Create a weak ref to the new space object.
+      peer = reinterpret_cast<void*>(2);
+      finalizable_new_ref = Dart_NewFinalizableHandle(
+          new_ref, peer, 0, FinalizableHandleCallback);
+      finalizable_new_ref_peer = peer;
+
+      // Create a weak ref to the old space object.
+      peer = reinterpret_cast<void*>(3);
+      finalizable_old_ref = Dart_NewFinalizableHandle(
+          old_ref, peer, 0, FinalizableHandleCallback);
+      finalizable_old_ref_peer = peer;
+    }
 
     {
       TransitionNativeToVM transition(thread);
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index b2dd597..2acdfd3 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -111,15 +111,13 @@
 
 uword Heap::AllocateOld(intptr_t size, OldPage::PageType type) {
   ASSERT(Thread::Current()->no_safepoint_scope_depth() == 0);
-  CollectForDebugging();
-  uword addr = old_space_.TryAllocate(size, type);
-  if (addr != 0) {
-    return addr;
-  }
-  // If we are in the process of running a sweep, wait for the sweeper to free
-  // memory.
-  Thread* thread = Thread::Current();
   if (old_space_.GrowthControlState()) {
+    CollectForDebugging();
+    uword addr = old_space_.TryAllocate(size, type);
+    if (addr != 0) {
+      return addr;
+    }
+    Thread* thread = Thread::Current();
     // Wait for any GC tasks that are in progress.
     WaitForSweeperTasks(thread);
     addr = old_space_.TryAllocate(size, type);
@@ -148,7 +146,7 @@
     CollectAllGarbage(kLowMemory);
     WaitForSweeperTasks(thread);
   }
-  addr = old_space_.TryAllocate(size, type, PageSpace::kForceGrowth);
+  uword addr = old_space_.TryAllocate(size, type, PageSpace::kForceGrowth);
   if (addr != 0) {
     return addr;
   }
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index e0f10cb..3c73952 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -452,6 +452,7 @@
 
 void PageSpace::EvaluateConcurrentMarking(GrowthPolicy growth_policy) {
   if (growth_policy != kForceGrowth) {
+    ASSERT(GrowthControlState());
     if (heap_ != NULL) {  // Some unit tests.
       Thread* thread = Thread::Current();
       if (thread->CanCollectGarbage()) {
@@ -1020,6 +1021,8 @@
 }
 
 void PageSpace::CollectGarbage(bool compact, bool finalize) {
+  ASSERT(GrowthControlState());
+
   if (!finalize) {
 #if defined(TARGET_ARCH_IA32)
     return;  // Barrier not implemented.
diff --git a/runtime/vm/heap/pages_test.cc b/runtime/vm/heap/pages_test.cc
index 1db6113..fed0a88 100644
--- a/runtime/vm/heap/pages_test.cc
+++ b/runtime/vm/heap/pages_test.cc
@@ -10,6 +10,7 @@
 
 TEST_CASE(Pages) {
   PageSpace* space = new PageSpace(NULL, 4 * MBInWords);
+  space->InitGrowthControl();
   EXPECT(!space->Contains(reinterpret_cast<uword>(&space)));
   uword block = space->TryAllocate(8 * kWordSize);
   EXPECT(block != 0);
diff --git a/runtime/vm/service/service_extension.md b/runtime/vm/service/service_extension.md
index 64de58a..7bdca7f 100644
--- a/runtime/vm/service/service_extension.md
+++ b/runtime/vm/service/service_extension.md
@@ -1,4 +1,4 @@
-# Dart VM Service Protocol Extension 1.1
+# Dart VM Service Protocol Extension 1.2
 
 This protocol describes service extensions that are made available through
 the Dart core libraries, but are not part of the core
@@ -10,7 +10,7 @@
 
 ## dart:io Extensions
 
-This section describes _version 1.1_ of the dart:io service protocol extensions.
+This section describes _version 1.2_ of the dart:io service protocol extensions.
 
 ### getVersion
 
@@ -52,6 +52,50 @@
 
 See [Success](#success).
 
+### getOpenFileById
+
+```
+OpenFile getOpenFileById(string isolateId, int id);
+```
+
+The _getOpenFileById_ RPC is used to retrieve information about files currently
+opened by `dart:io` from a given isolate.
+
+See [getOpenFiles](#getopenfiles) and [File](#file).
+
+### getOpenFiles
+
+```
+FileList getOpenFiles(string isolateId);
+```
+
+The _getOpenFiles_ RPC is used to retrieve the list of files currently opened
+files by `dart:io` from a given isolate.
+
+See [FileList](#filelist) and [File](#file).
+
+### getSpawnedProcessById
+
+```
+SpawnedProcess getSpawnedProcessById(string isolateId, int id);
+```
+
+The _getSpawnedProcessById_ RPC is used to retrieve information about a process spawned
+by `dart:io` from a given isolate.
+
+See [getSpawnedProcesses](#getspawnedprocesses) and [SpawnedProcess](#spawnedprocess).
+
+### getSpawnedProcesses
+
+```
+SpawnedProcessList getSpawnedProcesses(string isolateId);
+```
+
+The _getSpawnedProcesses_ RPC is used to retrieve the list of processed opened by
+`dart:io` from a given isolate.
+
+See [SpawnedProcessList](#spawnedprocesslist) and [SpawnedProcess](#spawnedprocess).
+
 ### getSocketProfile
 
 ```
@@ -88,6 +132,108 @@
 
 ## Public Types
 
+### File
+
+```
+class @OpenFile extends Response {
+  // The unique ID associated with this file.
+  int id;
+
+  // The path of the file.
+  string name;
+}
+```
+
+_@File_ is a reference to a _File_.
+
+```
+class OpenFile extends Response {
+  // The unique ID associated with this file.
+  int id;
+
+  // The path of the file.
+  string name;
+
+  // The total number of bytes read from this file.
+  int readBytes;
+
+  // The total number of bytes written to this file.
+  int writeBytes;
+
+  // The number of reads made from this file.
+  int readCount;
+
+  // The number of writes made to this file.
+  int writeCount;
+
+  // The time at which this file was last read by this process in milliseconds
+  // since epoch.
+  int lastReadTime;
+
+  // The time at which this file was last written to by this process in
+  // milliseconds since epoch.
+  int lastWriteTime;
+}
+```
+
+A _OpenFile_ contains information about reads and writes to a currently opened file.
+
+### OpenFileList
+
+```
+class OpenFileList extends Response {
+  // A list of all files opened through dart:io.
+  @OpenFile[] files;
+}
+```
+
+### SpawnedProcess
+
+```
+class @SpawnedProcess {
+  // The unique ID associated with this process.
+  int id;
+
+  // The name of the executable.
+  string name;
+}
+```
+
+_@SpawnedProcess_ is a reference to a _SpawnedProcess_.
+
+```
+class SpawnedProcess extends Response {
+  // The unique ID associated with this process.
+  int id;
+
+  // The name of the executable.
+  string name;
+
+  // The process ID associated with the process.
+  int pid;
+
+  // The time the process was started in milliseconds since epoch.
+  int startedAt;
+
+  // The list of arguments provided to the process at launch.
+  string[] arguments;
+
+  // The working directory of the process at launch.
+  string workingDirectory;
+}
+```
+
+A _Process_ contains startup information of a spawned process.
+
+### SpawnedProcessList
+
+```
+class SpawnedProcessList extends Response {
+  // A list of processes spawned through dart:io on a given isolate.
+  @SpawnedProcess[] processes;
+}
+```
+
 ### Response
 
 ```
@@ -178,3 +324,4 @@
 ------- | --------
 1.0 | Initial revision.
 1.1 | Added `lastReadTime` and `lastWriteTime` properties to `SocketStatistic`.
+1.2 | Added `getOpenFiles`, `getOpenFileById`, `getSpawnedProcesses`, and `getSpawnedProcessById` RPCs and added `OpenFile` and `SpawnedProcess` objects.
diff --git a/runtime/vm/thread_test.cc b/runtime/vm/thread_test.cc
index a5618c6..41e3374 100644
--- a/runtime/vm/thread_test.cc
+++ b/runtime/vm/thread_test.cc
@@ -176,8 +176,6 @@
   Monitor sync[kTaskCount];
   bool done[kTaskCount];
   Isolate* isolate = thread->isolate();
-  EXPECT(isolate->heap()->GrowthControlState());
-  isolate->heap()->DisableGrowthControl();
   for (int i = 0; i < kTaskCount; i++) {
     done[i] = false;
     Dart::thread_pool()->Run<TaskWithZoneAllocation>(isolate, &sync[i],
diff --git a/sdk/lib/_internal/vm/bin/process_patch.dart b/sdk/lib/_internal/vm/bin/process_patch.dart
index bb86a0d..97426b8 100644
--- a/sdk/lib/_internal/vm/bin/process_patch.dart
+++ b/sdk/lib/_internal/vm/bin/process_patch.dart
@@ -225,10 +225,10 @@
     ArgumentError.checkNotNull(_mode, "mode");
 
     if (!connectedResourceHandler) {
-      registerExtension(
-          'ext.dart.io.getProcesses', _ProcessResourceInfo.getStartedProcesses);
-      registerExtension('ext.dart.io.getProcessById',
-          _ProcessResourceInfo.getProcessInfoMapById);
+      registerExtension('ext.dart.io.getSpawnedProcesses',
+          _SpawnedProcessResourceInfo.getStartedProcesses);
+      registerExtension('ext.dart.io.getSpawnedProcessById',
+          _SpawnedProcessResourceInfo.getProcessInfoMapById);
       connectedResourceHandler = true;
     }
 
@@ -413,7 +413,7 @@
       }
 
       _started = true;
-      final resourceInfo = new _ProcessResourceInfo(this);
+      final resourceInfo = new _SpawnedProcessResourceInfo(this);
 
       // Setup an exit handler to handle internal cleanup and possible
       // callback when a process terminates.
@@ -474,7 +474,7 @@
           _path, _arguments, status._errorMessage!, status._errorCode!);
     }
 
-    final resourceInfo = new _ProcessResourceInfo(this);
+    final resourceInfo = new _SpawnedProcessResourceInfo(this);
 
     var result = _wait(_stdinNativeSocket, _stdoutNativeSocket,
         _stderrNativeSocket, _exitHandler._nativeSocket);
diff --git a/sdk/lib/_internal/vm/bin/socket_patch.dart b/sdk/lib/_internal/vm/bin/socket_patch.dart
index abd886f..976b1f1 100644
--- a/sdk/lib/_internal/vm/bin/socket_patch.dart
+++ b/sdk/lib/_internal/vm/bin/socket_patch.dart
@@ -487,9 +487,6 @@
   bool writeEventIssued = false;
   bool writeAvailable = false;
 
-  static bool connectedResourceHandler = false;
-  _SocketResourceInfo? resourceInfo;
-
   // The owner object is the object that the Socket is being used by, e.g.
   // a HttpServer, a WebSocket connection, a process pipe, etc.
   Object? owner;
@@ -658,7 +655,6 @@
           var duration =
               address.isLoopback ? _retryDurationLoopback : _retryDuration;
           var timer = new Timer(duration, connectNext);
-          setupResourceInfo(socket);
 
           connecting[socket] = timer;
           // Setup handlers for receiving the first write event which
@@ -775,15 +771,10 @@
           osError: result, address: address, port: port);
     }
     if (port != 0) socket.localPort = port;
-    setupResourceInfo(socket);
     socket.connectToEventHandler();
     return socket;
   }
 
-  static void setupResourceInfo(_NativeSocket socket) {
-    socket.resourceInfo = new _SocketResourceInfo(socket);
-  }
-
   static Future<_NativeSocket> bindDatagram(
       host, int port, bool reuseAddress, bool reusePort, int ttl) async {
     _throwOnBadPort(port);
@@ -799,7 +790,6 @@
           osError: result, address: address, port: port);
     }
     if (port != 0) socket.localPort = port;
-    setupResourceInfo(socket);
     return socket;
   }
 
@@ -868,19 +858,6 @@
           list = builder.toBytes();
         }
       }
-      final resourceInformation = resourceInfo;
-      assert(resourceInformation != null ||
-          isPipe ||
-          isInternal ||
-          isInternalSignal);
-      if (list != null) {
-        if (resourceInformation != null) {
-          resourceInformation.totalRead += list.length;
-        }
-      }
-      if (resourceInformation != null) {
-        resourceInformation.didRead();
-      }
       if (!const bool.fromEnvironment("dart.vm.product")) {
         _SocketProfile.collectStatistic(
             nativeGetSocketId(), _SocketProfileType.readBytes, list?.length);
@@ -896,16 +873,6 @@
     if (isClosing || isClosed) return null;
     try {
       Datagram? result = nativeRecvFrom();
-      if (result != null) {
-        final resourceInformation = resourceInfo;
-        if (resourceInformation != null) {
-          resourceInformation.totalRead += result.data.length;
-        }
-      }
-      final resourceInformation = resourceInfo;
-      if (resourceInformation != null) {
-        resourceInformation.didRead();
-      }
       if (!const bool.fromEnvironment("dart.vm.product")) {
         _SocketProfile.collectStatistic(nativeGetSocketId(),
             _SocketProfileType.readBytes, result?.data.length);
@@ -955,14 +922,6 @@
       }
       // Negate the result, as stated above.
       if (result < 0) result = -result;
-      final resourceInformation = resourceInfo;
-      assert(resourceInformation != null ||
-          isPipe ||
-          isInternal ||
-          isInternalSignal);
-      if (resourceInformation != null) {
-        resourceInformation.addWrite(result);
-      }
       return result;
     } catch (e) {
       StackTrace st = StackTrace.current;
@@ -986,14 +945,6 @@
       }
       int result = nativeSendTo(bufferAndStart.buffer, bufferAndStart.start,
           bytes, (address as _InternetAddress)._in_addr, port);
-      final resourceInformation = resourceInfo;
-      assert(resourceInformation != null ||
-          isPipe ||
-          isInternal ||
-          isInternalSignal);
-      if (resourceInformation != null) {
-        resourceInformation.addWrite(result);
-      }
       return result;
     } catch (e) {
       StackTrace st = StackTrace.current;
@@ -1012,16 +963,6 @@
     var socket = new _NativeSocket.normal(address);
     if (nativeAccept(socket) != true) return null;
     socket.localPort = localPort;
-    setupResourceInfo(socket);
-    final resourceInformation = resourceInfo;
-    assert(resourceInformation != null ||
-        isPipe ||
-        isInternal ||
-        isInternalSignal);
-    if (resourceInformation != null) {
-      // We track this as read one byte.
-      resourceInformation.addRead(1);
-    }
     return socket;
   }
 
@@ -1149,14 +1090,6 @@
         if (i == destroyedEvent) {
           assert(isClosing);
           assert(!isClosed);
-          final resourceInformation = resourceInfo;
-          assert(resourceInformation != null ||
-              isPipe ||
-              isInternal ||
-              isInternalSignal);
-          if (resourceInformation != null) {
-            _SocketResourceInfo.SocketClosed(resourceInformation);
-          }
           isClosed = true;
           closeCompleter.complete();
           disconnectFromEventHandler();
@@ -1275,14 +1208,6 @@
     if (eventPort == null) {
       eventPort = new RawReceivePort(multiplex);
     }
-    if (!connectedResourceHandler) {
-      registerExtension(
-          'ext.dart.io.getOpenSockets', _SocketResourceInfo.getOpenSockets);
-      registerExtension('ext.dart.io.getSocketByID',
-          _SocketResourceInfo.getSocketInfoMapByID);
-
-      connectedResourceHandler = true;
-    }
   }
 
   void disconnectFromEventHandler() {
diff --git a/sdk/lib/_internal/vm/bin/sync_socket_patch.dart b/sdk/lib/_internal/vm/bin/sync_socket_patch.dart
index f602449..6554c11 100644
--- a/sdk/lib/_internal/vm/bin/sync_socket_patch.dart
+++ b/sdk/lib/_internal/vm/bin/sync_socket_patch.dart
@@ -60,9 +60,6 @@
   // Holds the port of the socket, 0 if not known.
   int localPort = 0;
 
-  // Always set by setupResourceInfo called by connectSync.
-  late _SocketResourceInfo resourceInfo;
-
   static _NativeSynchronousSocket connectSync(host, int port) {
     if (host == null) {
       throw new ArgumentError("Parameter host cannot be null");
@@ -108,7 +105,6 @@
           }
           return connectNext();
         }
-        setupResourceInfo(socket);
       }
       return socket;
     }
@@ -164,7 +160,6 @@
   void closeSync() {
     if (!isClosed) {
       _nativeCloseSync();
-      _SocketResourceInfo.SocketClosed(resourceInfo);
       isClosed = true;
     }
   }
@@ -210,7 +205,6 @@
     if (result is OSError) {
       throw new SocketException("readIntoSync failed", osError: result);
     }
-    resourceInfo.addRead(result);
     return result;
   }
 
@@ -230,17 +224,9 @@
     if (result is OSError) {
       throw result;
     }
-    if (result is List<int>) {
-      resourceInfo.totalRead += result.length;
-    }
-    resourceInfo.didRead();
     return result;
   }
 
-  static void setupResourceInfo(_NativeSynchronousSocket socket) {
-    socket.resourceInfo = new _SocketResourceInfo(socket);
-  }
-
   void shutdown(SocketDirection direction) {
     if (isClosed) {
       return;
@@ -303,7 +289,6 @@
     if (result is OSError) {
       throw new SocketException("writeFromSync failed", osError: result);
     }
-    resourceInfo.addWrite(result);
   }
 
   void _checkAvailable() {
diff --git a/sdk/lib/io/file_impl.dart b/sdk/lib/io/file_impl.dart
index ee34b7d..75f78b1 100644
--- a/sdk/lib/io/file_impl.dart
+++ b/sdk/lib/io/file_impl.dart
@@ -679,7 +679,7 @@
 
   void _maybePerformCleanup() {
     if (closed) {
-      _FileResourceInfo.FileClosed(_resourceInfo);
+      _FileResourceInfo.fileClosed(_resourceInfo);
     }
   }
 
@@ -691,8 +691,8 @@
       // open.
       registerExtension(
           'ext.dart.io.getOpenFiles', _FileResourceInfo.getOpenFiles);
-      registerExtension(
-          'ext.dart.io.getFileByID', _FileResourceInfo.getFileInfoMapByID);
+      registerExtension('ext.dart.io.getOpenFileById',
+          _FileResourceInfo.getOpenFileInfoMapByID);
       _connectedResourceHandler = true;
     }
   }
diff --git a/sdk/lib/io/io_resource_info.dart b/sdk/lib/io/io_resource_info.dart
index b916dd8..3ba76c9 100644
--- a/sdk/lib/io/io_resource_info.dart
+++ b/sdk/lib/io/io_resource_info.dart
@@ -10,10 +10,10 @@
   String get name;
   static int _count = 0;
 
-  static final Stopwatch _sw = new Stopwatch()..start();
-  static final _startTime = new DateTime.now().millisecondsSinceEpoch;
+  static final Stopwatch _sw = Stopwatch()..start();
+  static final _startTime = DateTime.now().millisecondsSinceEpoch;
 
-  static double get timestamp => _startTime + _sw.elapsedMicroseconds / 1000;
+  static int get timestamp => _startTime + _sw.elapsedMicroseconds ~/ 1000;
 
   _IOResourceInfo(this.type) : id = _IOResourceInfo.getNextID();
 
@@ -34,20 +34,20 @@
 }
 
 abstract class _ReadWriteResourceInfo extends _IOResourceInfo {
-  int totalRead;
-  int totalWritten;
+  int readBytes;
+  int writeBytes;
   int readCount;
   int writeCount;
-  double lastRead;
-  double lastWrite;
+  int lastReadTime;
+  int lastWriteTime;
 
   // Not all call sites use this. In some cases, e.g., a socket, a read does
   // not always mean that we actually read some bytes (we may do a read to see
   // if there are some bytes available).
   void addRead(int bytes) {
-    totalRead += bytes;
+    readBytes += bytes;
     readCount++;
-    lastRead = _IOResourceInfo.timestamp;
+    lastReadTime = _IOResourceInfo.timestamp;
   }
 
   // In cases where we read but did not necessarily get any bytes, use this to
@@ -58,102 +58,100 @@
   }
 
   void addWrite(int bytes) {
-    totalWritten += bytes;
+    writeBytes += bytes;
     writeCount++;
-    lastWrite = _IOResourceInfo.timestamp;
+    lastWriteTime = _IOResourceInfo.timestamp;
   }
 
   _ReadWriteResourceInfo(String type)
-      : totalRead = 0,
-        totalWritten = 0,
+      : readBytes = 0,
+        writeBytes = 0,
         readCount = 0,
         writeCount = 0,
-        lastRead = 0.0,
-        lastWrite = 0.0,
+        lastReadTime = 0,
+        lastWriteTime = 0,
         super(type);
 
   Map<String, dynamic> get fullValueMap => {
         'type': type,
         'id': id,
         'name': name,
-        'totalRead': totalRead,
-        'totalWritten': totalWritten,
+        'readBytes': readBytes,
+        'writeBytes': writeBytes,
         'readCount': readCount,
         'writeCount': writeCount,
-        'lastRead': lastRead,
-        'lastWrite': lastWrite
+        'lastReadTime': lastReadTime,
+        'lastWriteTime': lastWriteTime
       };
 }
 
 class _FileResourceInfo extends _ReadWriteResourceInfo {
-  static const String _type = '_file';
+  static const String _type = 'OpenFile';
 
   final file;
 
-  static Map<int, _FileResourceInfo> openFiles =
-      new Map<int, _FileResourceInfo>();
+  static Map<int, _FileResourceInfo> openFiles = {};
 
   _FileResourceInfo(this.file) : super(_type) {
-    FileOpened(this);
+    fileOpened(this);
   }
 
-  static FileOpened(_FileResourceInfo info) {
+  static fileOpened(_FileResourceInfo info) {
     assert(!openFiles.containsKey(info.id));
     openFiles[info.id] = info;
   }
 
-  static FileClosed(_FileResourceInfo info) {
+  static fileClosed(_FileResourceInfo info) {
     assert(openFiles.containsKey(info.id));
     openFiles.remove(info.id);
   }
 
   static Iterable<Map<String, dynamic>> getOpenFilesList() {
-    return new List.from(openFiles.values.map((e) => e.referenceValueMap));
+    return List.from(openFiles.values.map(
+      (e) => e.referenceValueMap,
+    ));
   }
 
   static Future<ServiceExtensionResponse> getOpenFiles(function, params) {
     assert(function == 'ext.dart.io.getOpenFiles');
-    var data = {'type': '_openfiles', 'data': getOpenFilesList()};
-    var jsonValue = json.encode(data);
-    return new Future.value(new ServiceExtensionResponse.result(jsonValue));
+    final data = {
+      'type': 'OpenFileList',
+      'files': getOpenFilesList(),
+    };
+    final jsonValue = json.encode(data);
+    return Future.value(ServiceExtensionResponse.result(jsonValue));
   }
 
-  Map<String, dynamic> getFileInfoMap() {
-    return fullValueMap;
+  Map<String, dynamic> get fileInfoMap => fullValueMap;
+
+  static Future<ServiceExtensionResponse> getOpenFileInfoMapByID(
+      function, params) {
+    final id = int.parse(params['id']!);
+    final result = openFiles.containsKey(id) ? openFiles[id]!.fileInfoMap : {};
+    final jsonValue = json.encode(result);
+    return Future.value(ServiceExtensionResponse.result(jsonValue));
   }
 
-  static Future<ServiceExtensionResponse> getFileInfoMapByID(function, params) {
-    var id = int.parse(params['id']!);
-    var result =
-        openFiles.containsKey(id) ? openFiles[id]!.getFileInfoMap() : {};
-    var jsonValue = json.encode(result);
-    return new Future.value(new ServiceExtensionResponse.result(jsonValue));
-  }
-
-  String get name {
-    return '${file.path}';
-  }
+  String get name => file.path;
 }
 
-class _ProcessResourceInfo extends _IOResourceInfo {
-  static const String _type = '_process';
+class _SpawnedProcessResourceInfo extends _IOResourceInfo {
+  static const String _type = 'SpawnedProcess';
   final process;
-  final double startedAt;
+  final int startedAt;
 
-  static Map<int, _ProcessResourceInfo> startedProcesses =
-      new Map<int, _ProcessResourceInfo>();
+  static Map<int, _SpawnedProcessResourceInfo> startedProcesses =
+      Map<int, _SpawnedProcessResourceInfo>();
 
-  _ProcessResourceInfo(this.process)
+  _SpawnedProcessResourceInfo(this.process)
       : startedAt = _IOResourceInfo.timestamp,
         super(_type) {
-    ProcessStarted(this);
+    processStarted(this);
   }
 
   String get name => process._path;
 
-  void stopped() {
-    ProcessStopped(this);
-  }
+  void stopped() => processStopped(this);
 
   Map<String, dynamic> get fullValueMap => {
         'type': type,
@@ -166,115 +164,39 @@
             process._workingDirectory == null ? '.' : process._workingDirectory,
       };
 
-  static ProcessStarted(_ProcessResourceInfo info) {
+  static processStarted(_SpawnedProcessResourceInfo info) {
     assert(!startedProcesses.containsKey(info.id));
     startedProcesses[info.id] = info;
   }
 
-  static ProcessStopped(_ProcessResourceInfo info) {
+  static processStopped(_SpawnedProcessResourceInfo info) {
     assert(startedProcesses.containsKey(info.id));
     startedProcesses.remove(info.id);
   }
 
   static Iterable<Map<String, dynamic>> getStartedProcessesList() =>
-      new List.from(startedProcesses.values.map((e) => e.referenceValueMap));
+      List.from(startedProcesses.values.map(
+        (e) => e.referenceValueMap,
+      ));
 
   static Future<ServiceExtensionResponse> getStartedProcesses(
       String function, Map<String, String> params) {
-    assert(function == 'ext.dart.io.getProcesses');
-    var data = {'type': '_startedprocesses', 'data': getStartedProcessesList()};
-    var jsonValue = json.encode(data);
-    return new Future.value(new ServiceExtensionResponse.result(jsonValue));
+    assert(function == 'ext.dart.io.getSpawnedProcesses');
+    final data = {
+      'type': 'SpawnedProcessList',
+      'processes': getStartedProcessesList(),
+    };
+    final jsonValue = json.encode(data);
+    return Future.value(ServiceExtensionResponse.result(jsonValue));
   }
 
   static Future<ServiceExtensionResponse> getProcessInfoMapById(
       String function, Map<String, String> params) {
-    var id = int.parse(params['id']!);
-    var result = startedProcesses.containsKey(id)
+    final id = int.parse(params['id']!);
+    final result = startedProcesses.containsKey(id)
         ? startedProcesses[id]!.fullValueMap
         : {};
-    var jsonValue = json.encode(result);
-    return new Future.value(new ServiceExtensionResponse.result(jsonValue));
-  }
-}
-
-class _SocketResourceInfo extends _ReadWriteResourceInfo {
-  static const String _tcpString = 'TCP';
-  static const String _udpString = 'UDP';
-  static const String _type = '_socket';
-
-  final /*_NativeSocket|*/ socket;
-
-  static Map<int, _SocketResourceInfo> openSockets =
-      new Map<int, _SocketResourceInfo>();
-
-  _SocketResourceInfo(this.socket) : super(_type) {
-    SocketOpened(this);
-  }
-
-  String get name {
-    if (socket.isListening) {
-      return 'listening:${socket.address.host}:${socket.port}';
-    }
-    var remote = '';
-    try {
-      var remoteHost = socket.remoteAddress.host;
-      var remotePort = socket.remotePort;
-      remote = ' -> $remoteHost:$remotePort';
-    } catch (e) {} // ignored if we can't get the information
-    return '${socket.address.host}:${socket.port}$remote';
-  }
-
-  static Iterable<Map<String, dynamic>> getOpenSocketsList() {
-    return new List.from(openSockets.values.map((e) => e.referenceValueMap));
-  }
-
-  Map<String, dynamic> getSocketInfoMap() {
-    var result = fullValueMap;
-    result['socketType'] = socket.isTcp ? _tcpString : _udpString;
-    result['listening'] = socket.isListening;
-    result['host'] = socket.address.host;
-    result['port'] = socket.port;
-    if (!socket.isListening) {
-      try {
-        result['remoteHost'] = socket.remoteAddress.host;
-        result['remotePort'] = socket.remotePort;
-      } catch (e) {
-        // UDP.
-        result['remotePort'] = 'NA';
-        result['remoteHost'] = 'NA';
-      }
-    } else {
-      result['remotePort'] = 'NA';
-      result['remoteHost'] = 'NA';
-    }
-    result['addressType'] = socket.address.type.name;
-    return result;
-  }
-
-  static Future<ServiceExtensionResponse> getSocketInfoMapByID(
-      String function, Map<String, String> params) {
-    var id = int.parse(params['id']!);
-    var result =
-        openSockets.containsKey(id) ? openSockets[id]!.getSocketInfoMap() : {};
-    var jsonValue = json.encode(result);
-    return new Future.value(new ServiceExtensionResponse.result(jsonValue));
-  }
-
-  static Future<ServiceExtensionResponse> getOpenSockets(function, params) {
-    assert(function == 'ext.dart.io.getOpenSockets');
-    var data = {'type': '_opensockets', 'data': getOpenSocketsList()};
-    var jsonValue = json.encode(data);
-    return new Future.value(new ServiceExtensionResponse.result(jsonValue));
-  }
-
-  static SocketOpened(_SocketResourceInfo info) {
-    assert(!openSockets.containsKey(info.id));
-    openSockets[info.id] = info;
-  }
-
-  static SocketClosed(_SocketResourceInfo info) {
-    assert(openSockets.containsKey(info.id));
-    openSockets.remove(info.id);
+    final jsonValue = json.encode(result);
+    return Future.value(ServiceExtensionResponse.result(jsonValue));
   }
 }
diff --git a/sdk/lib/io/network_profiling.dart b/sdk/lib/io/network_profiling.dart
index 5032d72..7121d3c 100644
--- a/sdk/lib/io/network_profiling.dart
+++ b/sdk/lib/io/network_profiling.dart
@@ -4,8 +4,9 @@
 
 part of dart.io;
 
+// TODO(bkonyi): refactor into io_resource_info.dart
 const int _versionMajor = 1;
-const int _versionMinor = 1;
+const int _versionMinor = 2;
 
 const String _tcpSocket = 'tcp';
 const String _udpSocket = 'udp';
diff --git a/tools/VERSION b/tools/VERSION
index c246888..7a99022 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 10
 PATCH 0
-PRERELEASE 146
+PRERELEASE 147
 PRERELEASE_PATCH 0
\ No newline at end of file