Merge pull request #1972 from krisgiesing/examples

Add flutter.yaml for address_book example
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e24946f..a099d5b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -68,7 +68,7 @@
  * `./sky/tools/gn --android` to prepare your build files.
  * `ninja -C out/android_Debug` to build an Android Debug binary.
 
-To run an example with your locally built minary, switch to that example's directory, run `pub get` to make sure its dependencies have been downloaded, and use `flutter start` with an explicit `--engine-src-path` pointing at the `src` directory. Make sure you have a device connected over USB and debugging enabled on that device.
+To run an example with your locally built binary, switch to that example's directory, run `pub get` to make sure its dependencies have been downloaded, and use `flutter start` with an explicit `--engine-src-path` pointing at the `src` directory. Make sure you have a device connected over USB and debugging enabled on that device.
 
  * `cd examples/hello_world/; flutter start --engine-src-path ../../`
 
@@ -94,8 +94,7 @@
  * `./sky/tools/run_tests --debug` runs the tests on the host machine using `out/Debug`.
 
 If you want to run the run a test directly:
- * Linux: `./out/Debug/sky_shell --package-root=sky/unit/packages sky/unit/test/harness/trivial_test.dart`
- * Mac: `./sky/tools/run_tests --debug test/harness/trivial_test.dart`
+ * `./sky/tools/run_tests --debug test/harness/trivial_test.dart`
 
 Note: The tests are headless, you won't see any UI. You can use `print` to generate console output or you can interact with the DartVM via observatory at [http://localhost:8181/](http://localhost:8181/).
 
diff --git a/DEPS b/DEPS
index 0db0264..2b0c286 100644
--- a/DEPS
+++ b/DEPS
@@ -19,7 +19,7 @@
 
 vars = {
   'chromium_git': 'https://chromium.googlesource.com',
-  'mojo_sdk_revision': 'eb994bc6b0130471d17f6c934b0be4938adf20b9',
+  'mojo_sdk_revision': '160b0ca55d5e4b92ac93042ec36b891e4d709348',
   'mojo_devtools_revision': '49879d78ce4486e10c2214a101d9b2e82794b2f4',
   'skia_revision': '87ed6be0e4ea8b0a88915045f697d0fd734ed6f5',
 
diff --git a/README.md b/README.md
index e063fc9..ff58080 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,11 @@
 Flutter is optimized for today's, and tomorrow's, mobile devices. We are
 focused on low-latency input and high frame rates on Android and iOS.
 
+_Flutter is an early-stage open-source project._ We are still missing core
+features like accessibility, text input, localization, and more. However,
+you can build demos and examples today. We hope you try it out and send
+us [feedback](mailto:flutter-dev@googlegroups.com).
+
  - For information about using Flutter to build apps, please see
    the [getting started guide](http://flutter.io/getting-started/).
 
diff --git a/examples/fitness/flutter.yaml b/examples/fitness/flutter.yaml
index 3eb1406..942a5de 100644
--- a/examples/fitness/flutter.yaml
+++ b/examples/fitness/flutter.yaml
@@ -1,6 +1,6 @@
 name: fitness
 version: 0.0.1
-update-url: http://localhost:9888/examples/fitness/
+update-url: http://localhost:9888/
 material-design-icons:
   - name: action/assessment
   - name: action/help
diff --git a/examples/fitness/lib/meal.dart b/examples/fitness/lib/meal.dart
index 10e0703..945ea98 100644
--- a/examples/fitness/lib/meal.dart
+++ b/examples/fitness/lib/meal.dart
@@ -84,19 +84,15 @@
 
   Widget buildBody() {
     Meal meal = new Meal(when: new DateTime.now());
-    // TODO(ianh): Fix Block such that we could use that here instead of rolling our own
-    return new ScrollableViewport(
-      child: new Container(
-        padding: const EdgeDims.all(20.0),
-        child: new BlockBody(<Widget>[
-          new Text(meal.displayDate),
-          new Input(
-            key: descriptionKey,
-            placeholder: 'Describe meal',
-            onChanged: _handleDescriptionChanged
-          ),
-        ])
-      )
+    return new Block(<Widget>[
+        new Text(meal.displayDate),
+        new Input(
+          key: descriptionKey,
+          placeholder: 'Describe meal',
+          onChanged: _handleDescriptionChanged
+        ),
+      ],
+      padding: const EdgeDims.all(20.0)
     );
   }
 
diff --git a/examples/fitness/lib/settings.dart b/examples/fitness/lib/settings.dart
index eb4764e..21404f9 100644
--- a/examples/fitness/lib/settings.dart
+++ b/examples/fitness/lib/settings.dart
@@ -90,29 +90,25 @@
   }
 
   Widget buildSettingsPane(BuildContext context) {
-    // TODO(ianh): Make Block capable of doing this
-    return new ScrollableViewport(
-      child: new Container(
-        padding: const EdgeDims.symmetric(vertical: 20.0),
-        child: new BlockBody(<Widget>[
-          new DrawerItem(
-            onPressed: () { _handleBackupChanged(!(config.userData.backupMode == BackupMode.enabled)); },
-            child: new Row(<Widget>[
-              new Flexible(child: new Text('Back up data to the cloud')),
-              new Switch(value: config.userData.backupMode == BackupMode.enabled, onChanged: _handleBackupChanged),
-            ])
-          ),
-          new DrawerItem(
-            onPressed: () => _handleGoalWeightPressed(),
-            child: new Column(<Widget>[
-                new Text('Goal Weight'),
-                new Text(goalWeightText, style: Theme.of(context).text.caption),
-              ],
-              alignItems: FlexAlignItems.start
-            )
-          ),
-        ])
-      )
+    return new Block(<Widget>[
+        new DrawerItem(
+          onPressed: () { _handleBackupChanged(!(config.userData.backupMode == BackupMode.enabled)); },
+          child: new Row(<Widget>[
+            new Flexible(child: new Text('Back up data to the cloud')),
+            new Switch(value: config.userData.backupMode == BackupMode.enabled, onChanged: _handleBackupChanged),
+          ])
+        ),
+        new DrawerItem(
+          onPressed: () => _handleGoalWeightPressed(),
+          child: new Column(<Widget>[
+              new Text('Goal Weight'),
+              new Text(goalWeightText, style: Theme.of(context).text.caption),
+            ],
+            alignItems: FlexAlignItems.start
+          )
+        ),
+      ],
+      padding: const EdgeDims.symmetric(vertical: 20.0)
     );
   }
 
diff --git a/examples/stocks/flutter.yaml b/examples/stocks/flutter.yaml
index e71bce6..9c1ae43 100644
--- a/examples/stocks/flutter.yaml
+++ b/examples/stocks/flutter.yaml
@@ -1,6 +1,6 @@
 name: stocks
 version: 0.0.2
-update-url: http://localhost:9888/examples/stocks/
+update-url: http://localhost:9888/
 material-design-icons:
   - name: action/account_balance
   - name: action/assessment
diff --git a/examples/stocks/lib/stock_settings.dart b/examples/stocks/lib/stock_settings.dart
index 687668b..f98dc89 100644
--- a/examples/stocks/lib/stock_settings.dart
+++ b/examples/stocks/lib/stock_settings.dart
@@ -81,34 +81,31 @@
   Widget buildSettingsPane(BuildContext context) {
     // TODO(ianh): Once we have the gesture API hooked up, fix https://github.com/domokit/mojo/issues/281
     // (whereby tapping the widgets below causes both the widget and the menu item to fire their callbacks)
-    return new ScrollableViewport(
-      child: new Container(
-        padding: const EdgeDims.symmetric(vertical: 20.0),
-        child: new BlockBody(<Widget>[
-          new DrawerItem(
-            icon: 'action/thumb_up',
-            onPressed: () => _confirmOptimismChange(),
-            child: new Row(<Widget>[
-              new Flexible(child: new Text('Everything is awesome')),
-              new Checkbox(
-                value: config.optimism == StockMode.optimistic,
-                onChanged: (bool value) => _confirmOptimismChange()
-              ),
-            ])
-          ),
-          new DrawerItem(
-            icon: 'action/backup',
-            onPressed: () { _handleBackupChanged(!(config.backup == BackupMode.enabled)); },
-            child: new Row(<Widget>[
-              new Flexible(child: new Text('Back up stock list to the cloud')),
-              new Switch(
-                value: config.backup == BackupMode.enabled,
-                onChanged: _handleBackupChanged
-              ),
-            ])
-          ),
-        ])
-      )
+    return new Block(<Widget>[
+        new DrawerItem(
+          icon: 'action/thumb_up',
+          onPressed: () => _confirmOptimismChange(),
+          child: new Row(<Widget>[
+            new Flexible(child: new Text('Everything is awesome')),
+            new Checkbox(
+              value: config.optimism == StockMode.optimistic,
+              onChanged: (bool value) => _confirmOptimismChange()
+            ),
+          ])
+        ),
+        new DrawerItem(
+          icon: 'action/backup',
+          onPressed: () { _handleBackupChanged(!(config.backup == BackupMode.enabled)); },
+          child: new Row(<Widget>[
+            new Flexible(child: new Text('Back up stock list to the cloud')),
+            new Switch(
+              value: config.backup == BackupMode.enabled,
+              onChanged: _handleBackupChanged
+            ),
+          ])
+        ),
+      ],
+      padding: const EdgeDims.symmetric(vertical: 20.0)
     );
   }
 
diff --git a/examples/widgets/drag_and_drop.dart b/examples/widgets/drag_and_drop.dart
index ffbd9f5..14e6039 100644
--- a/examples/widgets/drag_and_drop.dart
+++ b/examples/widgets/drag_and_drop.dart
@@ -6,29 +6,23 @@
 import 'package:flutter/painting.dart';
 import 'package:flutter/rendering.dart';
 
-class DragData {
-  DragData(this.text);
-
-  final String text;
-}
-
 class ExampleDragTarget extends StatefulComponent {
   ExampleDragTargetState createState() => new ExampleDragTargetState();
 }
 
 class ExampleDragTargetState extends State<ExampleDragTarget> {
-  String _text = 'Drag Target';
+  Color _color = Colors.grey[500];
 
-  void _handleAccept(DragData data) {
+  void _handleAccept(Color data) {
     setState(() {
-      _text = 'dropped: ${data.text}';
+      _color = data;
     });
   }
 
   Widget build(BuildContext context) {
-    return new DragTarget<DragData>(
+    return new DragTarget<Color>(
       onAccept: _handleAccept,
-      builder: (BuildContext context, List<DragData> data, _) {
+      builder: (BuildContext context, List<Color> data, _) {
         return new Container(
           height: 100.0,
           margin: new EdgeDims.all(10.0),
@@ -37,10 +31,7 @@
               width: 3.0,
               color: data.isEmpty ? Colors.white : Colors.blue[500]
             ),
-            backgroundColor: data.isEmpty ? Colors.grey[500] : Colors.green[500]
-          ),
-          child: new Center(
-            child: new Text(_text)
+            backgroundColor: data.isEmpty ? _color : Colors.grey[200]
           )
         );
       }
@@ -49,42 +40,84 @@
 }
 
 class Dot extends StatelessComponent {
-  Dot({ Key key, this.color, this.size }) : super(key: key);
+  Dot({ Key key, this.color, this.size, this.child }) : super(key: key);
   final Color color;
   final double size;
+  final Widget child;
   Widget build(BuildContext context) {
     return new Container(
       width: size,
       height: size,
       decoration: new BoxDecoration(
-        borderRadius: 10.0,
-        backgroundColor: color
-      )
+        backgroundColor: color,
+        shape: Shape.circle
+      ),
+      child: child
     );
   }
 }
 
 class ExampleDragSource extends StatelessComponent {
-  ExampleDragSource({ Key key, this.name, this.color }) : super(key: key);
-  final String name;
-  final Color color;
+  ExampleDragSource({
+    Key key,
+    this.color,
+    this.heavy: false,
+    this.under: true,
+    this.child
+  }) : super(key: key);
 
-  static const kDotSize = 50.0;
-  static const kFingerSize = 50.0;
+  final Color color;
+  final bool heavy;
+  final bool under;
+  final Widget child;
+
+  static const double kDotSize = 50.0;
+  static const double kHeavyMultiplier = 1.5;
+  static const double kFingerSize = 50.0;
 
   Widget build(BuildContext context) {
-    return new Draggable(
-      data: new DragData(name),
-      child: new Dot(color: color, size: kDotSize),
-      feedback: new Transform(
-        transform: new Matrix4.identity()..translate(-kDotSize / 2.0, -(kDotSize / 2.0 + kFingerSize)),
-        child: new Opacity(
-          opacity: 0.75,
-          child: new Dot(color: color, size: kDotSize)
-        )
-      ),
-      feedbackOffset: const Offset(0.0, -kFingerSize),
-      dragAnchor: DragAnchor.pointer
+    double size = kDotSize;
+    DraggableConstructor<Color> constructor = new Draggable<Color>#;
+    if (heavy) {
+      size *= kHeavyMultiplier;
+      constructor = new LongPressDraggable<Color>#;
+    }
+
+    Widget contents = new DefaultTextStyle(
+      style: Theme.of(context).text.body1.copyWith(textAlign: TextAlign.center),
+      child: new Dot(
+        color: color,
+        size: size,
+        child: new Center(child: child)
+      )
+    );
+
+    Widget feedback = new Opacity(
+      opacity: 0.75,
+      child: contents
+    );
+
+    Offset feedbackOffset;
+    DragAnchor anchor;
+    if (!under) {
+      feedback = new Transform(
+        transform: new Matrix4.identity()
+                     ..translate(-size / 2.0, -(size / 2.0 + kFingerSize)),
+        child: feedback
+      );
+      feedbackOffset = const Offset(0.0, -kFingerSize);
+      anchor = DragAnchor.pointer;
+    } else {
+      feedbackOffset = Offset.zero;
+      anchor = DragAnchor.child;
+    }
+
+    return constructor(
+      data: color,
+      child: contents,
+      feedback: feedback,
+      feedbackOffset: feedbackOffset,
+      dragAnchor: anchor
     );
   }
 }
@@ -95,25 +128,37 @@
       toolBar: new ToolBar(
         center: new Text('Drag and Drop Flutter Demo')
       ),
-      body: new DefaultTextStyle(
-        style: Theme.of(context).text.body1.copyWith(textAlign: TextAlign.center),
-        child: new Column(<Widget>[
-          new Flexible(child: new Row(<Widget>[
-              new ExampleDragSource(name: 'Orange', color: const Color(0xFFFF9000)),
-              new ExampleDragSource(name: 'Teal', color: const Color(0xFF00FFFF)),
-              new ExampleDragSource(name: 'Yellow', color: const Color(0xFFFFF000)),
-            ],
-            alignItems: FlexAlignItems.center,
-            justifyContent: FlexJustifyContent.spaceAround
-          )),
-          new Flexible(child: new Row(<Widget>[
-            new Flexible(child: new ExampleDragTarget()),
-            new Flexible(child: new ExampleDragTarget()),
-            new Flexible(child: new ExampleDragTarget()),
-            new Flexible(child: new ExampleDragTarget()),
-          ])),
-        ])
-      )
+      body: new Column(<Widget>[
+        new Flexible(child: new Row(<Widget>[
+            new ExampleDragSource(
+              color: const Color(0xFFFFF000),
+              under: true,
+              heavy: false,
+              child: new Text('under')
+            ),
+            new ExampleDragSource(
+              color: const Color(0xFF0FFF00),
+              under: false,
+              heavy: true,
+              child: new Text('long-press above')
+            ),
+            new ExampleDragSource(
+              color: const Color(0xFF00FFF0),
+              under: false,
+              heavy: false,
+              child: new Text('above')
+            ),
+          ],
+          alignItems: FlexAlignItems.center,
+          justifyContent: FlexJustifyContent.spaceAround
+        )),
+        new Flexible(child: new Row(<Widget>[
+          new Flexible(child: new ExampleDragTarget()),
+          new Flexible(child: new ExampleDragTarget()),
+          new Flexible(child: new ExampleDragTarget()),
+          new Flexible(child: new ExampleDragTarget()),
+        ])),
+      ])
     );
   }
 }
diff --git a/examples/widgets/scrollbar.dart b/examples/widgets/scrollbar.dart
index 4e3ecc2..dcbdc59 100644
--- a/examples/widgets/scrollbar.dart
+++ b/examples/widgets/scrollbar.dart
@@ -33,7 +33,8 @@
     Widget scrollable = new Container(
       margin: new EdgeDims.symmetric(horizontal: 6.0), // TODO(hansmuller) 6.0 should be based on _kScrollbarThumbWidth
       child: new Center(
-        shrinkWrap: ShrinkWrap.both,
+        widthFactor: 1.0,
+        heightFactor: 1.0,
         child: new Container(
           width: 80.0,
           height: _itemExtent * 5.0,
diff --git a/mojo/android/BUILD.gn b/mojo/android/BUILD.gn
index ccb4823..5c8381d 100644
--- a/mojo/android/BUILD.gn
+++ b/mojo/android/BUILD.gn
@@ -129,7 +129,6 @@
     "//mojo/message_pump",
     "//mojo/public/cpp/bindings/tests:mojo_public_bindings_test_utils",
     "//mojo/public/cpp/environment",
-    "//mojo/public/cpp/test_support:test_utils",
   ]
   defines = [ "UNIT_TEST" ]
 }
diff --git a/mojo/common/BUILD.gn b/mojo/common/BUILD.gn
index cdc4b64..adac158 100644
--- a/mojo/common/BUILD.gn
+++ b/mojo/common/BUILD.gn
@@ -9,8 +9,7 @@
   sources = [
     "binding_set.h",
     "interface_ptr_set.h",
-    "task_tracker.cc",
-    "task_tracker.h",
+    "strong_binding_set.h",
   ]
 
   deps = [
@@ -19,34 +18,38 @@
   ]
 }
 
-test("mojo_common_unittests") {
+source_set("tests") {
+  testonly = true
+
   sources = [
     "binding_set_unittest.cc",
     "callback_binding_unittest.cc",
     "interface_ptr_set_unittest.cc",
-    "task_tracker_unittest.cc",
+    "strong_binding_set_unittest.cc",
   ]
 
   deps = [
     ":common",
     ":test_interfaces",
     "//base",
-    "//base/test:test_support",
+    "//mojo/message_pump",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/bindings:callback",
+    "//mojo/public/cpp/system",
+    "//testing/gtest",
+  ]
+}
+
+test("mojo_common_unittests") {
+  deps = [
+    ":tests",
     "//mojo/converters/array_string:tests",
     "//mojo/converters/base:tests",
     "//mojo/converters/url:tests",
     "//mojo/data_pipe_utils:tests",
     "//mojo/edk/test:run_all_unittests",
-    "//mojo/edk/test:test_support",
     "//mojo/environment:chromium",
-    "//mojo/message_pump",
     "//mojo/message_pump:tests",
-    "//mojo/public/cpp/bindings",
-    "//mojo/public/cpp/bindings:callback",
-    "//mojo/public/cpp/system",
-    "//mojo/public/cpp/test_support:test_utils",
-    "//testing/gtest",
-    "//url",
   ]
 }
 
diff --git a/mojo/common/binding_set.h b/mojo/common/binding_set.h
index 6e81e76..4439f32 100644
--- a/mojo/common/binding_set.h
+++ b/mojo/common/binding_set.h
@@ -6,9 +6,11 @@
 #define MOJO_COMMON_BINDING_SET_H_
 
 #include <algorithm>
+#include <memory>
 #include <vector>
 
-#include "base/memory/weak_ptr.h"
+#include "base/logging.h"
+#include "base/macros.h"
 #include "mojo/public/cpp/bindings/binding.h"
 
 namespace mojo {
@@ -21,6 +23,9 @@
   BindingSet() {}
   ~BindingSet() { CloseAllBindings(); }
 
+  // Adds a binding to the list and arranges for it to be removed when
+  // a connection error occurs.  Does not take ownership of |impl|, which
+  // must outlive the binding set.
   void AddBinding(Interface* impl, InterfaceRequest<Interface> request) {
     bindings_.emplace_back(new Binding<Interface>(impl, request.Pass()));
     auto* binding = bindings_.back().get();
diff --git a/mojo/common/strong_binding_set.h b/mojo/common/strong_binding_set.h
new file mode 100644
index 0000000..95467d8
--- /dev/null
+++ b/mojo/common/strong_binding_set.h
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_COMMON_STRONG_BINDING_SET_H_
+#define MOJO_COMMON_STRONG_BINDING_SET_H_
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace mojo {
+
+// Use this class to manage a set of strong bindings each of which is
+// owned by the pipe it is bound to.  The set takes ownership of the
+// interfaces and will delete them when the bindings are closed.
+template <typename Interface>
+class StrongBindingSet {
+ public:
+  StrongBindingSet() {}
+  ~StrongBindingSet() { CloseAllBindings(); }
+
+  // Adds a binding to the list and arranges for it to be removed when
+  // a connection error occurs.  Takes ownership of |impl|, which
+  // will be deleted when the binding is closed.
+  void AddBinding(Interface* impl, InterfaceRequest<Interface> request) {
+    bindings_.emplace_back(new Binding<Interface>(impl, request.Pass()));
+    auto* binding = bindings_.back().get();
+    // Set the connection error handler for the newly added Binding to be a
+    // function that will erase it from the vector.
+    binding->set_connection_error_handler([this, binding]() {
+      auto it =
+          std::find_if(bindings_.begin(), bindings_.end(),
+                       [binding](const std::unique_ptr<Binding<Interface>>& b) {
+                         return (b.get() == binding);
+                       });
+      DCHECK(it != bindings_.end());
+      delete binding->impl();
+      bindings_.erase(it);
+    });
+  }
+
+  // Closes all bindings and deletes their associated interfaces.
+  void CloseAllBindings() {
+    for (auto it = bindings_.begin(); it != bindings_.end(); ++it) {
+      delete (*it)->impl();
+    }
+    bindings_.clear();
+  }
+
+  size_t size() const { return bindings_.size(); }
+
+ private:
+  std::vector<std::unique_ptr<Binding<Interface>>> bindings_;
+
+  DISALLOW_COPY_AND_ASSIGN(StrongBindingSet);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_COMMON_STRONG_BINDING_SET_H_
diff --git a/mojo/common/strong_binding_set_unittest.cc b/mojo/common/strong_binding_set_unittest.cc
new file mode 100644
index 0000000..59a7aa4
--- /dev/null
+++ b/mojo/common/strong_binding_set_unittest.cc
@@ -0,0 +1,115 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/common/strong_binding_set.h"
+
+#include "base/message_loop/message_loop.h"
+#include "mojo/common/test_interfaces.mojom.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace common {
+namespace {
+
+class DummyImpl : public tests::Dummy {
+ public:
+  explicit DummyImpl(bool* deleted_flag) : deleted_flag_(deleted_flag) {}
+  ~DummyImpl() override { *deleted_flag_ = true; }
+
+  void Foo() override { call_count_++; }
+
+  int call_count() const { return call_count_; }
+
+ private:
+  bool* deleted_flag_;
+  int call_count_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(DummyImpl);
+};
+
+// Tests all of the functionality of StrongBindingSet.
+TEST(StrongBindingSetTest, FullLifeCycle) {
+  base::MessageLoop loop(MessagePumpMojo::Create());
+
+  // Create 10 InterfacePtrs and DummyImpls.
+  const size_t kNumObjects = 10;
+  InterfacePtr<tests::Dummy> intrfc_ptrs[kNumObjects];
+  DummyImpl* impls[kNumObjects];
+  bool deleted_flags[kNumObjects] = {};
+
+  // Create 10 message pipes, bind everything together, and add the
+  // bindings to binding_set.
+  StrongBindingSet<tests::Dummy> binding_set;
+  EXPECT_EQ(0u, binding_set.size());
+  for (size_t i = 0; i < kNumObjects; i++) {
+    impls[i] = new DummyImpl(&deleted_flags[i]);
+    binding_set.AddBinding(impls[i], GetProxy(&intrfc_ptrs[i]));
+  }
+  EXPECT_EQ(kNumObjects, binding_set.size());
+
+  // Check that initially all call counts are zero.
+  for (const auto& impl : impls) {
+    EXPECT_EQ(0, impl->call_count());
+  }
+
+  // Invoke method foo() on all 10 InterfacePointers.
+  for (InterfacePtr<tests::Dummy>& ptr : intrfc_ptrs) {
+    ptr->Foo();
+  }
+
+  // Check that now all call counts are one.
+  loop.RunUntilIdle();
+  for (const auto& impl : impls) {
+    EXPECT_EQ(1, impl->call_count());
+  }
+
+  // Close the first 5 message pipes and destroy the first five
+  // InterfacePtrs.
+  for (size_t i = 0; i < kNumObjects / 2; i++) {
+    intrfc_ptrs[i].reset();
+  }
+
+  // Check that the set contains only five elements now.
+  loop.RunUntilIdle();
+  EXPECT_EQ(kNumObjects / 2, binding_set.size());
+
+  // Check that the first 5 interfaces have all been deleted.
+  for (size_t i = 0; i < kNumObjects; i++) {
+    bool expected = (i < kNumObjects / 2);
+    EXPECT_EQ(expected, deleted_flags[i]);
+  }
+
+  // Invoke method foo() on the second five InterfacePointers.
+  for (size_t i = kNumObjects / 2; i < kNumObjects; i++) {
+    intrfc_ptrs[i]->Foo();
+  }
+  loop.RunUntilIdle();
+
+  // Check that now the second five counts are two.
+  for (size_t i = kNumObjects / 2; i < kNumObjects; i++) {
+    EXPECT_EQ(2, impls[i]->call_count());
+  }
+
+  // Invoke CloseAllBindings
+  binding_set.CloseAllBindings();
+  EXPECT_EQ(0u, binding_set.size());
+
+  // Invoke method foo() on the second five InterfacePointers.
+  for (size_t i = kNumObjects / 2; i < kNumObjects; i++) {
+    intrfc_ptrs[i]->Foo();
+  }
+  loop.RunUntilIdle();
+
+  // Check that all interfaces have all been deleted.
+  for (size_t i = 0; i < kNumObjects; i++) {
+    EXPECT_TRUE(deleted_flags[i]);
+  }
+}
+
+}  // namespace
+}  // namespace common
+}  // namespace mojo
diff --git a/mojo/common/task_tracker.cc b/mojo/common/task_tracker.cc
deleted file mode 100644
index 8665799..0000000
--- a/mojo/common/task_tracker.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/common/task_tracker.h"
-
-#include <vector>
-
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/threading/thread_local.h"
-#include "base/tracked_objects.h"
-
-namespace mojo {
-namespace common {
-
-namespace {
-
-class TrackingActivation {
- public:
-  TrackingActivation() : birth_(nullptr) {}
-
-  bool Start(const char* function_name,
-             const char* file_name,
-             int line_number,
-             const void* program_counter);
-  void End();
-  bool IsAlive() const { return birth_ != nullptr; }
-
- private:
-  tracked_objects::TaskStopwatch* stopwatch() {
-    return reinterpret_cast<tracked_objects::TaskStopwatch*>(stopwatch_heap_);
-  }
-
-  tracked_objects::Births* birth_;
-  // TaskStopwatch isn't copyable, but we don't want to allocate it on the heap
-  // so as not to slow things down, hence replacement new.
-  char stopwatch_heap_[sizeof(tracked_objects::TaskStopwatch)];
-
-  DISALLOW_COPY_AND_ASSIGN(TrackingActivation);
-};
-
-bool TrackingActivation::Start(const char* function_name,
-                               const char* file_name,
-                               int line_number,
-                               const void* program_counter) {
-  // So far we don't track nested invocations.
-  if (IsAlive())
-    return false;
-  birth_ = tracked_objects::ThreadData::TallyABirthIfActive(
-      tracked_objects::Location(function_name, file_name, line_number,
-                                program_counter));
-  if (!birth_)
-    return false;
-
-  (new (stopwatch()) tracked_objects::TaskStopwatch())->Start();
-  return true;
-}
-
-void TrackingActivation::End() {
-  DCHECK(IsAlive());
-  stopwatch()->Stop();
-  tracked_objects::ThreadData::TallyRunInAScopedRegionIfTracking(birth_,
-                                                                 *stopwatch());
-  stopwatch()->tracked_objects::TaskStopwatch::~TaskStopwatch();
-  birth_ = nullptr;
-}
-
-base::ThreadLocalPointer<TrackingActivation> g_activation;
-
-}  // namespace
-
-// static
-intptr_t TaskTracker::StartTracking(const char* function_name,
-                                    const char* file_name,
-                                    int line_number,
-                                    const void* program_counter) {
-  TrackingActivation* activation = g_activation.Get();
-  if (!activation) {
-    // Leak this.
-    activation = new TrackingActivation();
-    g_activation.Set(activation);
-  }
-
-  if (!activation->Start(function_name, file_name, line_number,
-                         program_counter))
-    return 0;
-  return reinterpret_cast<intptr_t>(activation);
-}
-
-// static
-void TaskTracker::EndTracking(intptr_t id) {
-  if (0 == id)
-    return;
-  // |EndTracking()| must be called from the same thread of |StartTracking()|.
-  DCHECK_EQ(reinterpret_cast<TrackingActivation*>(id), g_activation.Get());
-  reinterpret_cast<TrackingActivation*>(id)->End();
-}
-
-}  // namespace common
-}  // namespace mojo
diff --git a/mojo/common/task_tracker.h b/mojo/common/task_tracker.h
deleted file mode 100644
index 7886636..0000000
--- a/mojo/common/task_tracker.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MOJO_COMMON_TASK_TRACKER_H_
-#define MOJO_COMMON_TASK_TRACKER_H_
-
-#include <stdint.h>
-
-namespace mojo {
-namespace common {
-
-class TaskTracker {
- public:
-  static intptr_t StartTracking(const char* function_name,
-                                const char* file_name,
-                                int line_number,
-                                const void* program_counter);
-  static void EndTracking(intptr_t id);
-  static void Enable();
-};
-
-}  // namespace common
-}  // namespace mojo
-
-#endif  // MOJO_COMMON_TASK_TRACKER_H_
diff --git a/mojo/common/task_tracker_unittest.cc b/mojo/common/task_tracker_unittest.cc
deleted file mode 100644
index 090e586..0000000
--- a/mojo/common/task_tracker_unittest.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/common/task_tracker.h"
-
-#include "base/tracked_objects.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace mojo {
-namespace common {
-namespace test {
-
-class TaskTrackerTest : public testing::Test {
- public:
-  void SetUp() override {
-    tracked_objects::ThreadData::InitializeAndSetTrackingStatus(
-        tracked_objects::ThreadData::PROFILING_ACTIVE);
-  }
-
-  void TearDown() override {
-    tracked_objects::ThreadData::InitializeAndSetTrackingStatus(
-        tracked_objects::ThreadData::DEACTIVATED);
-  }
-};
-
-TEST_F(TaskTrackerTest, Nesting) {
-  intptr_t id0 = TaskTracker::StartTracking("Foo", "foo.cc", 1, nullptr);
-  intptr_t id1 = TaskTracker::StartTracking("Bar", "bar.cc", 1, nullptr);
-  TaskTracker::EndTracking(id1);
-  TaskTracker::EndTracking(id0);
-
-  tracked_objects::ProcessDataSnapshot snapshot;
-  tracked_objects::ThreadData::Snapshot(0, &snapshot);
-
-  // Nested one is ignored.
-  EXPECT_EQ(1U, snapshot.phased_snapshots[0].tasks.size());
-}
-
-TEST_F(TaskTrackerTest, Twice) {
-  intptr_t id0 = TaskTracker::StartTracking("Foo", "foo.cc", 1, nullptr);
-  TaskTracker::EndTracking(id0);
-  intptr_t id1 = TaskTracker::StartTracking("Bar", "bar.cc", 1, nullptr);
-  TaskTracker::EndTracking(id1);
-
-  tracked_objects::ProcessDataSnapshot snapshot;
-  tracked_objects::ThreadData::Snapshot(0, &snapshot);
-
-  EXPECT_EQ(2U, snapshot.phased_snapshots[0].tasks.size());
-}
-
-}  // namespace test
-}  // namespace common
-}  // namespace mojo
diff --git a/mojo/converters/array_string/array_string_type_converters.cc b/mojo/converters/array_string/array_string_type_converters.cc
index 84d3c2a..9f99460 100644
--- a/mojo/converters/array_string/array_string_type_converters.cc
+++ b/mojo/converters/array_string/array_string_type_converters.cc
@@ -23,7 +23,7 @@
     const std::string& input) {
   auto result = Array<uint8_t>::New(input.size());
   memcpy(&result.front(), input.c_str(), input.size());
-  return result.Pass();
+  return result;
 }
 
 }  // namespace mojo
diff --git a/mojo/converters/geometry/geometry_type_converters.cc b/mojo/converters/geometry/geometry_type_converters.cc
index 40d8d89..ad66c11 100644
--- a/mojo/converters/geometry/geometry_type_converters.cc
+++ b/mojo/converters/geometry/geometry_type_converters.cc
@@ -11,7 +11,7 @@
   PointPtr point(Point::New());
   point->x = input.x();
   point->y = input.y();
-  return point.Pass();
+  return point;
 }
 
 // static
@@ -27,7 +27,7 @@
   PointFPtr point(PointF::New());
   point->x = input.x();
   point->y = input.y();
-  return point.Pass();
+  return point;
 }
 
 // static
@@ -43,7 +43,7 @@
   SizePtr size(Size::New());
   size->width = input.width();
   size->height = input.height();
-  return size.Pass();
+  return size;
 }
 
 // static
@@ -60,7 +60,7 @@
   rect->y = input.y();
   rect->width = input.width();
   rect->height = input.height();
-  return rect.Pass();
+  return rect;
 }
 
 // static
@@ -77,7 +77,7 @@
   rect->y = input.y();
   rect->width = input.width();
   rect->height = input.height();
-  return rect.Pass();
+  return rect;
 }
 
 // static
@@ -96,7 +96,7 @@
   matrix.Swap(&storage);
   TransformPtr transform(Transform::New());
   transform->matrix = matrix.Pass();
-  return transform.Pass();
+  return transform;
 }
 
 // static
diff --git a/mojo/converters/input_events/input_events_type_converters.cc b/mojo/converters/input_events/input_events_type_converters.cc
index e7b0f15..61137b3 100644
--- a/mojo/converters/input_events/input_events_type_converters.cc
+++ b/mojo/converters/input_events/input_events_type_converters.cc
@@ -225,7 +225,7 @@
     }
     event->key_data = key_data.Pass();
   }
-  return event.Pass();
+  return event;
 }
 
 // static
diff --git a/mojo/converters/input_events/input_events_type_converters.h b/mojo/converters/input_events/input_events_type_converters.h
index df3d01d..7afff26 100644
--- a/mojo/converters/input_events/input_events_type_converters.h
+++ b/mojo/converters/input_events/input_events_type_converters.h
@@ -14,28 +14,28 @@
 // NOTE: the mojo input events do not necessarily provide a 1-1 mapping with
 // ui::Event types. Be careful in using them!
 template <>
-struct TypeConverter<EventType, ui::EventType> {
-  static EventType Convert(ui::EventType type);
+struct TypeConverter<EventType, ::ui::EventType> {
+  static EventType Convert(::ui::EventType type);
 };
 
 template <>
-struct TypeConverter<EventPtr, ui::Event> {
-  static EventPtr Convert(const ui::Event& input);
+struct TypeConverter<EventPtr, ::ui::Event> {
+  static EventPtr Convert(const ::ui::Event& input);
 };
 
 template <>
-struct TypeConverter<EventPtr, ui::KeyEvent> {
-  static EventPtr Convert(const ui::KeyEvent& input);
+struct TypeConverter<EventPtr, ::ui::KeyEvent> {
+  static EventPtr Convert(const ::ui::KeyEvent& input);
 };
 
 template <>
-struct TypeConverter<EventPtr, ui::GestureEvent> {
-  static EventPtr Convert(const ui::GestureEvent& input);
+struct TypeConverter<EventPtr, ::ui::GestureEvent> {
+  static EventPtr Convert(const ::ui::GestureEvent& input);
 };
 
 template <>
-struct TypeConverter<scoped_ptr<ui::Event>, EventPtr> {
-  static scoped_ptr<ui::Event> Convert(const EventPtr& input);
+struct TypeConverter<scoped_ptr<::ui::Event>, EventPtr> {
+  static scoped_ptr<::ui::Event> Convert(const EventPtr& input);
 };
 
 }  // namespace mojo
diff --git a/mojo/converters/ozone_drm_gpu/ozone_drm_gpu_type_converters.cc b/mojo/converters/ozone_drm_gpu/ozone_drm_gpu_type_converters.cc
index 51639a5..3f31e63 100644
--- a/mojo/converters/ozone_drm_gpu/ozone_drm_gpu_type_converters.cc
+++ b/mojo/converters/ozone_drm_gpu/ozone_drm_gpu_type_converters.cc
@@ -26,7 +26,7 @@
   out->size = Size::From<gfx::Size>(in.size);
   out->is_interlaced = in.is_interlaced;
   out->refresh_rate = in.refresh_rate;
-  return out.Pass();
+  return out;
 }
 
 static_assert(static_cast<int>(ui::DISPLAY_CONNECTION_TYPE_NONE) ==
@@ -102,7 +102,7 @@
   out->native_mode = DisplayMode::From<ui::DisplayMode_Params>(in.native_mode);
   out->product_id = in.product_id;
   out->string_representation = in.string_representation;
-  return out.Pass();
+  return out;
 }
 
 }  // namespace mojo
diff --git a/mojo/converters/surfaces/surfaces_type_converters.cc b/mojo/converters/surfaces/surfaces_type_converters.cc
index 2c273dd..f16dac1 100644
--- a/mojo/converters/surfaces/surfaces_type_converters.cc
+++ b/mojo/converters/surfaces/surfaces_type_converters.cc
@@ -198,7 +198,7 @@
   SurfaceIdPtr id(SurfaceId::New());
   id->local = static_cast<uint32_t>(input.id);
   id->id_namespace = cc::SurfaceIdAllocator::NamespaceForId(input);
-  return id.Pass();
+  return id;
 }
 
 // static
@@ -214,7 +214,7 @@
 ColorPtr TypeConverter<ColorPtr, SkColor>::Convert(const SkColor& input) {
   ColorPtr color(Color::New());
   color->rgba = input;
-  return color.Pass();
+  return color;
 }
 
 // static
@@ -228,7 +228,7 @@
   RenderPassIdPtr pass_id(RenderPassId::New());
   pass_id->layer_id = input.layer_id;
   pass_id->index = input.index;
-  return pass_id.Pass();
+  return pass_id;
 }
 
 // static
@@ -337,7 +337,7 @@
     default:
       NOTREACHED() << "Unsupported material " << input.material;
   }
-  return quad.Pass();
+  return quad;
 }
 
 // static
@@ -354,7 +354,7 @@
   state->opacity = input.opacity;
   state->blend_mode = static_cast<SkXfermode>(input.blend_mode);
   state->sorting_context_id = input.sorting_context_id;
-  return state.Pass();
+  return state;
 }
 
 // static
@@ -391,7 +391,7 @@
   DCHECK_EQ(next_sqs_iter.index(), shared_quad_state.size());
   pass->quads = quads.Pass();
   pass->shared_quad_states = shared_quad_state.Pass();
-  return pass.Pass();
+  return pass;
 }
 
 // static
@@ -416,9 +416,9 @@
       ++sqs_iter;
     }
     if (!ConvertDrawQuad(quad, *sqs_iter, pass.get()))
-      return scoped_ptr<cc::RenderPass>();
+      return nullptr;
   }
-  return pass.Pass();
+  return pass;
 }
 
 // static
@@ -430,7 +430,7 @@
   }
   MailboxPtr mailbox(Mailbox::New());
   mailbox->name = name.Pass();
-  return mailbox.Pass();
+  return mailbox;
 }
 
 // static
@@ -449,7 +449,7 @@
   holder->mailbox = Mailbox::From<gpu::Mailbox>(input.mailbox);
   holder->texture_target = input.texture_target;
   holder->sync_point = input.sync_point;
-  return holder.Pass();
+  return holder;
 }
 
 // static
@@ -474,7 +474,7 @@
   transferable->mailbox_holder = MailboxHolder::From(input.mailbox_holder);
   transferable->is_repeated = input.is_repeated;
   transferable->is_software = input.is_software;
-  return transferable.Pass();
+  return transferable;
 }
 
 // static
@@ -501,7 +501,7 @@
   for (size_t i = 0; i < input.size(); ++i) {
     resources[i] = TransferableResource::From(input[i]);
   }
-  return resources.Pass();
+  return resources;
 }
 
 // static
@@ -524,7 +524,7 @@
   returned->sync_point = input.sync_point;
   returned->count = input.count;
   returned->lost = input.lost;
-  return returned.Pass();
+  return returned;
 }
 
 // static
@@ -547,7 +547,7 @@
   for (size_t i = 0; i < input.size(); ++i) {
     resources[i] = ReturnedResource::From(input[i]);
   }
-  return resources.Pass();
+  return resources;
 }
 
 // static
@@ -563,7 +563,7 @@
   for (size_t i = 0; i < pass_list.size(); ++i) {
     frame->passes[i] = Pass::From(*pass_list[i]);
   }
-  return frame.Pass();
+  return frame;
 }
 
 // static
@@ -584,7 +584,7 @@
   }
   scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame);
   frame->delegated_frame_data = frame_data.Pass();
-  return frame.Pass();
+  return frame;
 }
 
 }  // namespace mojo
diff --git a/mojo/edk/embedder/BUILD.gn b/mojo/edk/embedder/BUILD.gn
index 3916de3..f80f11b 100644
--- a/mojo/edk/embedder/BUILD.gn
+++ b/mojo/edk/embedder/BUILD.gn
@@ -26,8 +26,6 @@
     "test_embedder.h",
   ]
 
-  defines = [ "MOJO_SYSTEM_IMPLEMENTATION" ]
-
   mojo_edk_configs = [ "mojo/edk/system:system_config" ]
 
   public_deps = [
@@ -51,17 +49,13 @@
   # mojo_system_impl component.
   visibility = [ ":embedder" ]
 
-  mojo_edk_visibility = [
-    "mojo/edk/system",
-    "mojo/edk/system:test_utils",
-  ]
+  mojo_edk_visibility = [ "mojo/edk/system/*" ]
 
   sources = [
     "platform_channel_pair.cc",
     "platform_channel_pair.h",
-    "platform_channel_pair_posix.cc",
-    "platform_channel_utils_posix.cc",
-    "platform_channel_utils_posix.h",
+    "platform_channel_utils.cc",
+    "platform_channel_utils.h",
     "platform_handle.cc",
     "platform_handle.h",
     "platform_handle_utils.h",
@@ -75,8 +69,6 @@
     "scoped_platform_handle.h",
     "simple_platform_shared_buffer.cc",
     "simple_platform_shared_buffer.h",
-    "simple_platform_shared_buffer_android.cc",
-    "simple_platform_shared_buffer_posix.cc",
     "simple_platform_support.cc",
     "simple_platform_support.h",
   ]
@@ -116,26 +108,25 @@
   mojo_sdk_public_deps = [ "mojo/public/cpp/system" ]
 }
 
-mojo_edk_source_set("embedder_unittests") {
+mojo_edk_source_set("unittests") {
   testonly = true
   mojo_edk_visibility = [ "mojo/edk/system:mojo_system_unittests" ]
 
   sources = [
     "embedder_unittest.cc",
-    "platform_channel_pair_posix_unittest.cc",
+    "platform_channel_pair_unittest.cc",
     "simple_platform_shared_buffer_unittest.cc",
   ]
 
   deps = [
     "//base",
-    "//base/test:test_support",
     "//testing/gtest",
   ]
 
   mojo_edk_deps = [
     "mojo/edk/test:test_support",
     "mojo/edk/system",
-    "mojo/edk/system:test_utils",
+    "mojo/edk/system/test",
     "mojo/edk/util",
   ]
 }
diff --git a/mojo/edk/embedder/embedder.cc b/mojo/edk/embedder/embedder.cc
index b6712d3..9e1e462 100644
--- a/mojo/edk/embedder/embedder.cc
+++ b/mojo/edk/embedder/embedder.cc
@@ -25,7 +25,9 @@
 #include "mojo/edk/system/message_pipe_dispatcher.h"
 #include "mojo/edk/system/platform_handle_dispatcher.h"
 #include "mojo/edk/system/raw_channel.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/ref_ptr.h"
+
+using mojo::util::RefPtr;
 
 namespace mojo {
 namespace embedder {
@@ -134,8 +136,7 @@
 
   *platform_handle =
       static_cast<system::PlatformHandleDispatcher*>(dispatcher.get())
-          ->PassPlatformHandle()
-          .Pass();
+          ->PassPlatformHandle();
   return MOJO_RESULT_OK;
 }
 
@@ -185,7 +186,7 @@
       internal::g_ipc_support->GenerateConnectionIdentifier();
   *platform_connection_id = connection_id.ToString();
   system::ChannelId channel_id = system::kInvalidChannelId;
-  system::RefPtr<system::MessagePipeDispatcher> dispatcher =
+  RefPtr<system::MessagePipeDispatcher> dispatcher =
       internal::g_ipc_support->ConnectToSlave(
           connection_id, slave_info, platform_handle.Pass(),
           did_connect_to_slave_callback, std::move(did_connect_to_slave_runner),
@@ -212,7 +213,7 @@
   CHECK(ok);
 
   system::ChannelId channel_id = system::kInvalidChannelId;
-  system::RefPtr<system::MessagePipeDispatcher> dispatcher =
+  RefPtr<system::MessagePipeDispatcher> dispatcher =
       internal::g_ipc_support->ConnectToMaster(
           connection_id, did_connect_to_master_callback,
           std::move(did_connect_to_master_runner), &channel_id);
@@ -236,7 +237,7 @@
       internal::g_ipc_support->channel_manager();
 
   *channel_info = new ChannelInfo(MakeChannelId());
-  system::RefPtr<system::MessagePipeDispatcher> dispatcher =
+  RefPtr<system::MessagePipeDispatcher> dispatcher =
       channel_manager->CreateChannelOnIOThread((*channel_info)->channel_id,
                                                platform_handle.Pass());
 
@@ -259,7 +260,7 @@
 
   system::ChannelId channel_id = MakeChannelId();
   std::unique_ptr<ChannelInfo> channel_info(new ChannelInfo(channel_id));
-  system::RefPtr<system::MessagePipeDispatcher> dispatcher =
+  RefPtr<system::MessagePipeDispatcher> dispatcher =
       channel_manager->CreateChannel(
           channel_id, platform_handle.Pass(),
           base::Bind(did_create_channel_callback,
diff --git a/mojo/edk/embedder/embedder.h b/mojo/edk/embedder/embedder.h
index c7c3dc0..8899aea 100644
--- a/mojo/edk/embedder/embedder.h
+++ b/mojo/edk/embedder/embedder.h
@@ -9,7 +9,6 @@
 #include <string>
 
 #include "base/callback.h"
-#include "base/memory/ref_counted.h"
 #include "base/task_runner.h"
 #include "mojo/edk/embedder/channel_info_forward.h"
 #include "mojo/edk/embedder/platform_task_runner.h"
@@ -101,7 +100,7 @@
 void ShutdownIPCSupportOnIOThread();
 
 // Like |ShutdownIPCSupportOnIOThread()|, but may be called from any thread,
-// signalling shutdown completion via the process delegate's
+// signaling shutdown completion via the process delegate's
 // |OnShutdownComplete()|.
 void ShutdownIPCSupport();
 
diff --git a/mojo/edk/embedder/embedder_unittest.cc b/mojo/edk/embedder/embedder_unittest.cc
index e1d4421..415c34f 100644
--- a/mojo/edk/embedder/embedder_unittest.cc
+++ b/mojo/edk/embedder/embedder_unittest.cc
@@ -9,28 +9,30 @@
 #include <memory>
 
 #include "base/bind.h"
-#include "base/command_line.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/test/test_timeouts.h"
 #include "mojo/edk/embedder/platform_channel_pair.h"
 #include "mojo/edk/embedder/test_embedder.h"
-#include "mojo/edk/system/mutex.h"
-#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/test/test_command_line.h"
+#include "mojo/edk/system/test/test_io_thread.h"
+#include "mojo/edk/system/test/timeouts.h"
+#include "mojo/edk/system/waitable_event.h"
 #include "mojo/edk/test/multiprocess_test_helper.h"
 #include "mojo/edk/test/scoped_ipc_support.h"
-#include "mojo/edk/test/test_io_thread.h"
+#include "mojo/edk/util/command_line.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/c/system/core.h"
 #include "mojo/public/cpp/system/handle.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using mojo::system::test::TestIOThread;
+using mojo::util::Mutex;
+using mojo::util::MutexLocker;
+
 namespace mojo {
-
-using test::TestIOThread;
-
 namespace embedder {
 namespace {
 
@@ -55,7 +57,6 @@
   // object is alive).
   explicit ScopedTestChannel(ScopedPlatformHandle platform_handle)
       : bootstrap_message_pipe_(MOJO_HANDLE_INVALID),
-        event_(true, false),  // Manual reset.
         channel_info_(nullptr),
         wait_on_shutdown_(true) {
     bootstrap_message_pipe_ =
@@ -72,7 +73,7 @@
   // the I/O thread must be alive and pumping messages.)
   ~ScopedTestChannel() {
     // |WaitForChannelCreationCompletion()| must be called before destruction.
-    CHECK(event_.IsSignaled());
+    CHECK(event_.IsSignaledForTest());
     event_.Reset();
     if (wait_on_shutdown_) {
       DestroyChannel(channel_info_,
@@ -117,7 +118,7 @@
   // Set after channel creation has been completed (i.e., the callback to
   // |CreateChannel()| has been called). Also used in the destructor to wait for
   // |DestroyChannel()| completion.
-  base::WaitableEvent event_;
+  mojo::system::ManualResetWaitableEvent event_;
 
   // Valid after channel creation completion until destruction.
   ChannelInfo* channel_info_;
@@ -196,25 +197,27 @@
 
 class TestAsyncWaiter {
  public:
-  TestAsyncWaiter() : event_(true, false), wait_result_(MOJO_RESULT_UNKNOWN) {}
+  TestAsyncWaiter() : wait_result_(MOJO_RESULT_UNKNOWN) {}
 
   void Awake(MojoResult result) {
-    system::MutexLocker l(&wait_result_mutex_);
+    MutexLocker l(&wait_result_mutex_);
     wait_result_ = result;
     event_.Signal();
   }
 
-  bool TryWait() { return event_.TimedWait(TestTimeouts::action_timeout()); }
+  bool TryWait() {
+    return !event_.WaitWithTimeout(mojo::system::test::ActionTimeout());
+  }
 
   MojoResult wait_result() const {
-    system::MutexLocker l(&wait_result_mutex_);
+    MutexLocker l(&wait_result_mutex_);
     return wait_result_;
   }
 
  private:
-  base::WaitableEvent event_;
+  mojo::system::ManualResetWaitableEvent event_;
 
-  mutable system::Mutex wait_result_mutex_;
+  mutable Mutex wait_result_mutex_;
   MojoResult wait_result_ MOJO_GUARDED_BY(wait_result_mutex_);
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(TestAsyncWaiter);
@@ -406,11 +409,12 @@
 
   mojo::test::MultiprocessTestHelper multiprocess_test_helper;
   std::string connection_id;
-  base::WaitableEvent event(true, false);
+  mojo::system::ManualResetWaitableEvent event;
   ChannelInfo* channel_info = nullptr;
   ScopedMessagePipeHandle mp = ConnectToSlave(
       nullptr, multiprocess_test_helper.server_platform_handle.Pass(),
-      base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event)),
+      base::Bind(&mojo::system::ManualResetWaitableEvent::Signal,
+                 base::Unretained(&event)),
       nullptr, &connection_id, &channel_info);
   ASSERT_TRUE(mp.is_valid());
   EXPECT_TRUE(channel_info);
@@ -424,9 +428,8 @@
                                             MOJO_WRITE_MESSAGE_FLAG_NONE));
 
   // Wait for a response.
-  EXPECT_EQ(MOJO_RESULT_OK,
-            Wait(mp.get(), MOJO_HANDLE_SIGNAL_READABLE,
-                 mojo::system::test::ActionDeadline(), nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, Wait(mp.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                                 mojo::system::test::ActionTimeout(), nullptr));
 
   // The response message should say "world".
   char buffer[100];
@@ -441,7 +444,7 @@
 
   EXPECT_TRUE(multiprocess_test_helper.WaitForChildTestShutdown());
 
-  EXPECT_TRUE(event.TimedWait(TestTimeouts::action_timeout()));
+  EXPECT_FALSE(event.WaitWithTimeout(mojo::system::test::ActionTimeout()));
   test_io_thread().PostTaskAndWait(
       base::Bind(&DestroyChannelOnIOThread, base::Unretained(channel_info)));
 }
@@ -480,17 +483,16 @@
     mojo::test::ScopedSlaveIPCSupport ipc_support(
         test_io_thread.task_runner(), client_platform_handle.Pass());
 
-    const base::CommandLine& command_line =
-        *base::CommandLine::ForCurrentProcess();
-    ASSERT_TRUE(command_line.HasSwitch(kConnectionIdFlag));
-    std::string connection_id =
-        command_line.GetSwitchValueASCII(kConnectionIdFlag);
+    std::string connection_id;
+    ASSERT_TRUE(mojo::system::test::GetTestCommandLine()->GetOptionValue(
+        kConnectionIdFlag, &connection_id));
     ASSERT_FALSE(connection_id.empty());
-    base::WaitableEvent event(true, false);
+    mojo::system::ManualResetWaitableEvent event;
     ChannelInfo* channel_info = nullptr;
     ScopedMessagePipeHandle mp = ConnectToMaster(
         connection_id,
-        base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event)),
+        base::Bind(&mojo::system::ManualResetWaitableEvent::Signal,
+                   base::Unretained(&event)),
         nullptr, &channel_info);
     ASSERT_TRUE(mp.is_valid());
     EXPECT_TRUE(channel_info);
@@ -498,7 +500,7 @@
     // Wait for the master to send us a message.
     EXPECT_EQ(MOJO_RESULT_OK,
               Wait(mp.get(), MOJO_HANDLE_SIGNAL_READABLE,
-                   mojo::system::test::ActionDeadline(), nullptr));
+                   mojo::system::test::ActionTimeout(), nullptr));
 
     // It should say "hello".
     char buffer[100];
@@ -515,7 +517,7 @@
 
     mp.reset();
 
-    EXPECT_TRUE(event.TimedWait(TestTimeouts::action_timeout()));
+    EXPECT_FALSE(event.WaitWithTimeout(mojo::system::test::ActionTimeout()));
     test_io_thread.PostTaskAndWait(
         base::Bind(&DestroyChannelOnIOThread, base::Unretained(channel_info)));
   }
diff --git a/mojo/edk/embedder/platform_channel_pair.cc b/mojo/edk/embedder/platform_channel_pair.cc
index 4e88bc9..1eaf30b 100644
--- a/mojo/edk/embedder/platform_channel_pair.cc
+++ b/mojo/edk/embedder/platform_channel_pair.cc
@@ -4,14 +4,63 @@
 
 #include "mojo/edk/embedder/platform_channel_pair.h"
 
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/command_line.h"
 #include "base/logging.h"
+#include "base/posix/global_descriptors.h"
+#include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
+#include "mojo/edk/embedder/platform_handle.h"
 
 namespace mojo {
 namespace embedder {
 
+namespace {
+
+bool IsTargetDescriptorUsed(
+    const base::FileHandleMappingVector& file_handle_mapping,
+    int target_fd) {
+  for (size_t i = 0; i < file_handle_mapping.size(); i++) {
+    if (file_handle_mapping[i].second == target_fd)
+      return true;
+  }
+  return false;
+}
+
+}  // namespace
+
 const char PlatformChannelPair::kMojoPlatformChannelHandleSwitch[] =
     "mojo-platform-channel-handle";
 
+PlatformChannelPair::PlatformChannelPair() {
+  // Create the Unix domain socket and set the ends to nonblocking.
+  int fds[2];
+  // TODO(vtl): Maybe fail gracefully if |socketpair()| fails.
+  PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
+  PCHECK(fcntl(fds[0], F_SETFL, O_NONBLOCK) == 0);
+  PCHECK(fcntl(fds[1], F_SETFL, O_NONBLOCK) == 0);
+
+#if defined(OS_MACOSX)
+  // This turns off |SIGPIPE| when writing to a closed socket (causing it to
+  // fail with |EPIPE| instead). On Linux, we have to use |send...()| with
+  // |MSG_NOSIGNAL| -- which is not supported on Mac -- instead.
+  int no_sigpipe = 1;
+  PCHECK(setsockopt(fds[0], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
+                    sizeof(no_sigpipe)) == 0);
+  PCHECK(setsockopt(fds[1], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
+                    sizeof(no_sigpipe)) == 0);
+#endif  // defined(OS_MACOSX)
+
+  server_handle_.reset(PlatformHandle(fds[0]));
+  DCHECK(server_handle_.is_valid());
+  client_handle_.reset(PlatformHandle(fds[1]));
+  DCHECK(client_handle_.is_valid());
+}
+
 PlatformChannelPair::~PlatformChannelPair() {
 }
 
@@ -23,6 +72,55 @@
   return client_handle_.Pass();
 }
 
+// static
+ScopedPlatformHandle PlatformChannelPair::PassClientHandleFromParentProcess(
+    const base::CommandLine& command_line) {
+  std::string client_fd_string =
+      command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
+  int client_fd = -1;
+  if (client_fd_string.empty() ||
+      !base::StringToInt(client_fd_string, &client_fd) ||
+      client_fd < base::GlobalDescriptors::kBaseDescriptor) {
+    LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch;
+    return ScopedPlatformHandle();
+  }
+
+  return ScopedPlatformHandle(PlatformHandle(client_fd));
+}
+
+void PlatformChannelPair::PrepareToPassClientHandleToChildProcess(
+    base::CommandLine* command_line,
+    base::FileHandleMappingVector* handle_passing_info) const {
+  DCHECK(command_line);
+  DCHECK(handle_passing_info);
+  // This is an arbitrary sanity check. (Note that this guarantees that the loop
+  // below will terminate sanely.)
+  CHECK_LT(handle_passing_info->size(), 1000u);
+
+  DCHECK(client_handle_.is_valid());
+
+  // Find a suitable FD to map our client handle to in the child process.
+  // This has quadratic time complexity in the size of |*handle_passing_info|,
+  // but |*handle_passing_info| should be very small (usually/often empty).
+  int target_fd = base::GlobalDescriptors::kBaseDescriptor;
+  while (IsTargetDescriptorUsed(*handle_passing_info, target_fd))
+    target_fd++;
+
+  handle_passing_info->push_back(
+      std::pair<int, int>(client_handle_.get().fd, target_fd));
+  // Log a warning if the command line already has the switch, but "clobber" it
+  // anyway, since it's reasonably likely that all the switches were just copied
+  // from the parent.
+  LOG_IF(WARNING, command_line->HasSwitch(kMojoPlatformChannelHandleSwitch))
+      << "Child command line already has switch --"
+      << kMojoPlatformChannelHandleSwitch << "="
+      << command_line->GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
+  // (Any existing switch won't actually be removed from the command line, but
+  // the last one appended takes precedence.)
+  command_line->AppendSwitchASCII(kMojoPlatformChannelHandleSwitch,
+                                  base::IntToString(target_fd));
+}
+
 void PlatformChannelPair::ChildProcessLaunched() {
   DCHECK(client_handle_.is_valid());
   client_handle_.reset();
diff --git a/mojo/edk/embedder/platform_channel_pair.h b/mojo/edk/embedder/platform_channel_pair.h
index 5f44ef4..8e1771a 100644
--- a/mojo/edk/embedder/platform_channel_pair.h
+++ b/mojo/edk/embedder/platform_channel_pair.h
@@ -37,9 +37,9 @@
 // implementations.
 //
 // Note: On POSIX platforms, to write to the "pipe", use
-// |PlatformChannel{Write,Writev}()| (from platform_channel_utils_posix.h)
-// instead of |write()|, |writev()|, etc. Otherwise, you have to worry about
-// platform differences in suppressing |SIGPIPE|.
+// |PlatformChannel{Write,Writev}()| (from platform_channel_utils.h) instead of
+// |write()|, |writev()|, etc. Otherwise, you have to worry about platform
+// differences in suppressing |SIGPIPE|.
 class PlatformChannelPair {
  public:
   PlatformChannelPair();
diff --git a/mojo/edk/embedder/platform_channel_pair_posix.cc b/mojo/edk/embedder/platform_channel_pair_posix.cc
deleted file mode 100644
index 2242cce..0000000
--- a/mojo/edk/embedder/platform_channel_pair_posix.cc
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/edk/embedder/platform_channel_pair.h"
-
-#include <fcntl.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "base/command_line.h"
-#include "base/logging.h"
-#include "base/posix/global_descriptors.h"
-#include "base/strings/string_number_conversions.h"
-#include "build/build_config.h"
-#include "mojo/edk/embedder/platform_handle.h"
-
-namespace mojo {
-namespace embedder {
-
-namespace {
-
-bool IsTargetDescriptorUsed(
-    const base::FileHandleMappingVector& file_handle_mapping,
-    int target_fd) {
-  for (size_t i = 0; i < file_handle_mapping.size(); i++) {
-    if (file_handle_mapping[i].second == target_fd)
-      return true;
-  }
-  return false;
-}
-
-}  // namespace
-
-PlatformChannelPair::PlatformChannelPair() {
-  // Create the Unix domain socket and set the ends to nonblocking.
-  int fds[2];
-  // TODO(vtl): Maybe fail gracefully if |socketpair()| fails.
-  PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
-  PCHECK(fcntl(fds[0], F_SETFL, O_NONBLOCK) == 0);
-  PCHECK(fcntl(fds[1], F_SETFL, O_NONBLOCK) == 0);
-
-#if defined(OS_MACOSX)
-  // This turns off |SIGPIPE| when writing to a closed socket (causing it to
-  // fail with |EPIPE| instead). On Linux, we have to use |send...()| with
-  // |MSG_NOSIGNAL| -- which is not supported on Mac -- instead.
-  int no_sigpipe = 1;
-  PCHECK(setsockopt(fds[0], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
-                    sizeof(no_sigpipe)) == 0);
-  PCHECK(setsockopt(fds[1], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
-                    sizeof(no_sigpipe)) == 0);
-#endif  // defined(OS_MACOSX)
-
-  server_handle_.reset(PlatformHandle(fds[0]));
-  DCHECK(server_handle_.is_valid());
-  client_handle_.reset(PlatformHandle(fds[1]));
-  DCHECK(client_handle_.is_valid());
-}
-
-// static
-ScopedPlatformHandle PlatformChannelPair::PassClientHandleFromParentProcess(
-    const base::CommandLine& command_line) {
-  std::string client_fd_string =
-      command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
-  int client_fd = -1;
-  if (client_fd_string.empty() ||
-      !base::StringToInt(client_fd_string, &client_fd) ||
-      client_fd < base::GlobalDescriptors::kBaseDescriptor) {
-    LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch;
-    return ScopedPlatformHandle();
-  }
-
-  return ScopedPlatformHandle(PlatformHandle(client_fd));
-}
-
-void PlatformChannelPair::PrepareToPassClientHandleToChildProcess(
-    base::CommandLine* command_line,
-    base::FileHandleMappingVector* handle_passing_info) const {
-  DCHECK(command_line);
-  DCHECK(handle_passing_info);
-  // This is an arbitrary sanity check. (Note that this guarantees that the loop
-  // below will terminate sanely.)
-  CHECK_LT(handle_passing_info->size(), 1000u);
-
-  DCHECK(client_handle_.is_valid());
-
-  // Find a suitable FD to map our client handle to in the child process.
-  // This has quadratic time complexity in the size of |*handle_passing_info|,
-  // but |*handle_passing_info| should be very small (usually/often empty).
-  int target_fd = base::GlobalDescriptors::kBaseDescriptor;
-  while (IsTargetDescriptorUsed(*handle_passing_info, target_fd))
-    target_fd++;
-
-  handle_passing_info->push_back(
-      std::pair<int, int>(client_handle_.get().fd, target_fd));
-  // Log a warning if the command line already has the switch, but "clobber" it
-  // anyway, since it's reasonably likely that all the switches were just copied
-  // from the parent.
-  LOG_IF(WARNING, command_line->HasSwitch(kMojoPlatformChannelHandleSwitch))
-      << "Child command line already has switch --"
-      << kMojoPlatformChannelHandleSwitch << "="
-      << command_line->GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
-  // (Any existing switch won't actually be removed from the command line, but
-  // the last one appended takes precedence.)
-  command_line->AppendSwitchASCII(kMojoPlatformChannelHandleSwitch,
-                                  base::IntToString(target_fd));
-}
-
-}  // namespace embedder
-}  // namespace mojo
diff --git a/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc b/mojo/edk/embedder/platform_channel_pair_unittest.cc
similarity index 90%
rename from mojo/edk/embedder/platform_channel_pair_posix_unittest.cc
rename to mojo/edk/embedder/platform_channel_pair_unittest.cc
index 5787e53..20b9a7a 100644
--- a/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc
+++ b/mojo/edk/embedder/platform_channel_pair_unittest.cc
@@ -14,14 +14,15 @@
 #include <unistd.h>
 
 #include <deque>
+#include <utility>
 
 #include "base/logging.h"
 #include "build/build_config.h"
-#include "mojo/edk/embedder/platform_channel_utils_posix.h"
+#include "mojo/edk/embedder/platform_channel_utils.h"
 #include "mojo/edk/embedder/platform_handle.h"
 #include "mojo/edk/embedder/platform_handle_vector.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
-#include "mojo/edk/test/scoped_test_dir.h"
+#include "mojo/edk/system/test/scoped_test_dir.h"
 #include "mojo/edk/test/test_utils.h"
 #include "mojo/edk/util/scoped_file.h"
 #include "mojo/public/cpp/system/macros.h"
@@ -38,10 +39,10 @@
   CHECK_EQ(poll(&pfds, 1, -1), 1);
 }
 
-class PlatformChannelPairPosixTest : public testing::Test {
+class PlatformChannelPairTest : public testing::Test {
  public:
-  PlatformChannelPairPosixTest() {}
-  ~PlatformChannelPairPosixTest() override {}
+  PlatformChannelPairTest() {}
+  ~PlatformChannelPairTest() override {}
 
   void SetUp() override {
     // Make sure |SIGPIPE| isn't being ignored.
@@ -58,13 +59,13 @@
  private:
   struct sigaction old_action_;
 
-  MOJO_DISALLOW_COPY_AND_ASSIGN(PlatformChannelPairPosixTest);
+  MOJO_DISALLOW_COPY_AND_ASSIGN(PlatformChannelPairTest);
 };
 
-TEST_F(PlatformChannelPairPosixTest, NoSigPipe) {
+TEST_F(PlatformChannelPairTest, NoSigPipe) {
   PlatformChannelPair channel_pair;
-  ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass();
-  ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass();
+  ScopedPlatformHandle server_handle = channel_pair.PassServerHandle();
+  ScopedPlatformHandle client_handle = channel_pair.PassClientHandle();
 
   // Write to the client.
   static const char kHello[] = "hello";
@@ -102,10 +103,10 @@
     PLOG(WARNING) << "write (expected EPIPE)";
 }
 
-TEST_F(PlatformChannelPairPosixTest, SendReceiveData) {
+TEST_F(PlatformChannelPairTest, SendReceiveData) {
   PlatformChannelPair channel_pair;
-  ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass();
-  ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass();
+  ScopedPlatformHandle server_handle = channel_pair.PassServerHandle();
+  ScopedPlatformHandle client_handle = channel_pair.PassClientHandle();
 
   for (size_t i = 0; i < 10; i++) {
     std::string send_string(1 << i, 'A' + i);
@@ -126,14 +127,14 @@
   }
 }
 
-TEST_F(PlatformChannelPairPosixTest, SendReceiveFDs) {
-  mojo::test::ScopedTestDir test_dir;
+TEST_F(PlatformChannelPairTest, SendReceiveFDs) {
+  mojo::system::test::ScopedTestDir test_dir;
 
   static const char kHello[] = "hello";
 
   PlatformChannelPair channel_pair;
-  ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass();
-  ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass();
+  ScopedPlatformHandle server_handle = channel_pair.PassServerHandle();
+  ScopedPlatformHandle client_handle = channel_pair.PassClientHandle();
 
 // Reduce the number of FDs opened on OS X to avoid test flake.
 #if defined(OS_MACOSX)
@@ -152,7 +153,7 @@
       ASSERT_TRUE(fp);
       ASSERT_EQ(j, fwrite(std::string(j, c).data(), 1, j, fp.get()));
       platform_handles->push_back(
-          test::PlatformHandleFromFILE(fp.Pass()).release());
+          test::PlatformHandleFromFILE(std::move(fp)).release());
       ASSERT_TRUE(platform_handles->back().is_valid());
     }
 
@@ -189,14 +190,14 @@
   }
 }
 
-TEST_F(PlatformChannelPairPosixTest, AppendReceivedFDs) {
-  mojo::test::ScopedTestDir test_dir;
+TEST_F(PlatformChannelPairTest, AppendReceivedFDs) {
+  mojo::system::test::ScopedTestDir test_dir;
 
   static const char kHello[] = "hello";
 
   PlatformChannelPair channel_pair;
-  ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass();
-  ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass();
+  ScopedPlatformHandle server_handle = channel_pair.PassServerHandle();
+  ScopedPlatformHandle client_handle = channel_pair.PassClientHandle();
 
   const std::string file_contents("hello world");
 
@@ -207,7 +208,7 @@
               fwrite(file_contents.data(), 1, file_contents.size(), fp.get()));
     ScopedPlatformHandleVectorPtr platform_handles(new PlatformHandleVector);
     platform_handles->push_back(
-        test::PlatformHandleFromFILE(fp.Pass()).release());
+        test::PlatformHandleFromFILE(std::move(fp)).release());
     ASSERT_TRUE(platform_handles->back().is_valid());
 
     // Send the FD (+ "hello").
diff --git a/mojo/edk/embedder/platform_channel_utils_posix.cc b/mojo/edk/embedder/platform_channel_utils.cc
similarity index 98%
rename from mojo/edk/embedder/platform_channel_utils_posix.cc
rename to mojo/edk/embedder/platform_channel_utils.cc
index 61b573b..71c1159 100644
--- a/mojo/edk/embedder/platform_channel_utils_posix.cc
+++ b/mojo/edk/embedder/platform_channel_utils.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "mojo/edk/embedder/platform_channel_utils_posix.h"
+#include "mojo/edk/embedder/platform_channel_utils.h"
 
 #include <sys/socket.h>
 #include <sys/uio.h>
diff --git a/mojo/edk/embedder/platform_channel_utils_posix.h b/mojo/edk/embedder/platform_channel_utils.h
similarity index 94%
rename from mojo/edk/embedder/platform_channel_utils_posix.h
rename to mojo/edk/embedder/platform_channel_utils.h
index 429cffa..8458b06 100644
--- a/mojo/edk/embedder/platform_channel_utils_posix.h
+++ b/mojo/edk/embedder/platform_channel_utils.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_UTILS_POSIX_H_
-#define MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_UTILS_POSIX_H_
+#ifndef MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_UTILS_H_
+#define MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_UTILS_H_
 
 #include <stddef.h>
 #include <sys/types.h>  // For |ssize_t|.
@@ -71,4 +71,4 @@
 }  // namespace embedder
 }  // namespace mojo
 
-#endif  // MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_UTILS_POSIX_H_
+#endif  // MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_UTILS_H_
diff --git a/mojo/edk/embedder/platform_shared_buffer.h b/mojo/edk/embedder/platform_shared_buffer.h
index 331440f..8907366 100644
--- a/mojo/edk/embedder/platform_shared_buffer.h
+++ b/mojo/edk/embedder/platform_shared_buffer.h
@@ -9,8 +9,8 @@
 
 #include <memory>
 
-#include "base/memory/ref_counted.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/util/ref_counted.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -34,7 +34,7 @@
 //
 // TODO(vtl): Rectify this with |base::SharedMemory|.
 class PlatformSharedBuffer
-    : public base::RefCountedThreadSafe<PlatformSharedBuffer> {
+    : public util::RefCountedThreadSafe<PlatformSharedBuffer> {
  public:
   // Gets the size of shared buffer (in number of bytes).
   virtual size_t GetNumBytes() const = 0;
@@ -65,7 +65,7 @@
   virtual ScopedPlatformHandle PassPlatformHandle() = 0;
 
  protected:
-  friend class base::RefCountedThreadSafe<PlatformSharedBuffer>;
+  friend class util::RefCountedThreadSafe<PlatformSharedBuffer>;
 
   PlatformSharedBuffer() {}
   virtual ~PlatformSharedBuffer() {}
diff --git a/mojo/edk/embedder/platform_support.h b/mojo/edk/embedder/platform_support.h
index 40b404c..ae02504 100644
--- a/mojo/edk/embedder/platform_support.h
+++ b/mojo/edk/embedder/platform_support.h
@@ -8,6 +8,7 @@
 #include <stddef.h>
 
 #include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/c/system/types.h"
 #include "mojo/public/cpp/system/macros.h"
 
@@ -42,8 +43,9 @@
   // Gets cryptographically-secure (pseudo)random bytes.
   virtual void GetCryptoRandomBytes(void* bytes, size_t num_bytes) = 0;
 
-  virtual PlatformSharedBuffer* CreateSharedBuffer(size_t num_bytes) = 0;
-  virtual PlatformSharedBuffer* CreateSharedBufferFromHandle(
+  virtual util::RefPtr<PlatformSharedBuffer> CreateSharedBuffer(
+      size_t num_bytes) = 0;
+  virtual util::RefPtr<PlatformSharedBuffer> CreateSharedBufferFromHandle(
       size_t num_bytes,
       ScopedPlatformHandle platform_handle) = 0;
 
diff --git a/mojo/edk/embedder/simple_platform_shared_buffer.cc b/mojo/edk/embedder/simple_platform_shared_buffer.cc
index cb4ae3d..d23e1ee 100644
--- a/mojo/edk/embedder/simple_platform_shared_buffer.cc
+++ b/mojo/edk/embedder/simple_platform_shared_buffer.cc
@@ -4,44 +4,62 @@
 
 #include "mojo/edk/embedder/simple_platform_shared_buffer.h"
 
+#include <stdint.h>
+#include <stdio.h>     // For |fileno()|.
+#include <sys/mman.h>  // For |mmap()|/|munmap()|.
+#include <sys/stat.h>
+#include <sys/types.h>  // For |off_t|.
+#include <unistd.h>
+
+#include <limits>
+#include <utility>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
 #include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/sys_info.h"
+#include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
 #include "mojo/edk/embedder/platform_handle_utils.h"
+#include "mojo/edk/util/scoped_file.h"
+
+#if defined(OS_ANDROID)
+#include "third_party/ashmem/ashmem.h"
+#endif  // defined(OS_ANDROID)
+
+using mojo::util::RefPtr;
+
+// We assume that |size_t| and |off_t| (type for |ftruncate()|) fits in a
+// |uint64_t|.
+static_assert(sizeof(size_t) <= sizeof(uint64_t), "size_t too big");
+static_assert(sizeof(off_t) <= sizeof(uint64_t), "off_t too big");
 
 namespace mojo {
 namespace embedder {
 
+// SimplePlatformSharedBuffer --------------------------------------------------
+
 // static
-SimplePlatformSharedBuffer* SimplePlatformSharedBuffer::Create(
+RefPtr<SimplePlatformSharedBuffer> SimplePlatformSharedBuffer::Create(
     size_t num_bytes) {
   DCHECK_GT(num_bytes, 0u);
 
-  SimplePlatformSharedBuffer* rv = new SimplePlatformSharedBuffer(num_bytes);
-  if (!rv->Init()) {
-    // We can't just delete it directly, due to the "in destructor" (debug)
-    // check.
-    scoped_refptr<SimplePlatformSharedBuffer> deleter(rv);
-    return nullptr;
-  }
-
-  return rv;
+  RefPtr<SimplePlatformSharedBuffer> rv(
+      AdoptRef(new SimplePlatformSharedBuffer(num_bytes)));
+  return rv->Init() ? rv : nullptr;
 }
 
 // static
-SimplePlatformSharedBuffer*
+RefPtr<SimplePlatformSharedBuffer>
 SimplePlatformSharedBuffer::CreateFromPlatformHandle(
     size_t num_bytes,
     ScopedPlatformHandle platform_handle) {
   DCHECK_GT(num_bytes, 0u);
 
-  SimplePlatformSharedBuffer* rv = new SimplePlatformSharedBuffer(num_bytes);
-  if (!rv->InitFromPlatformHandle(platform_handle.Pass())) {
-    // We can't just delete it directly, due to the "in destructor" (debug)
-    // check.
-    scoped_refptr<SimplePlatformSharedBuffer> deleter(rv);
-    return nullptr;
-  }
-
-  return rv;
+  RefPtr<SimplePlatformSharedBuffer> rv(
+      AdoptRef(new SimplePlatformSharedBuffer(num_bytes)));
+  return rv->InitFromPlatformHandle(std::move(platform_handle)) ? rv : nullptr;
 }
 
 size_t SimplePlatformSharedBuffer::GetNumBytes() const {
@@ -72,7 +90,32 @@
 std::unique_ptr<PlatformSharedBufferMapping>
 SimplePlatformSharedBuffer::MapNoCheck(size_t offset, size_t length) {
   DCHECK(IsValidMap(offset, length));
-  return MapImpl(offset, length);
+
+  size_t offset_rounding = offset % base::SysInfo::VMAllocationGranularity();
+  size_t real_offset = offset - offset_rounding;
+  size_t real_length = length + offset_rounding;
+
+  // This should hold (since we checked |num_bytes| versus the maximum value of
+  // |off_t| on creation, but it never hurts to be paranoid.
+  DCHECK_LE(static_cast<uint64_t>(real_offset),
+            static_cast<uint64_t>(std::numeric_limits<off_t>::max()));
+
+  void* real_base =
+      mmap(nullptr, real_length, PROT_READ | PROT_WRITE, MAP_SHARED,
+           handle_.get().fd, static_cast<off_t>(real_offset));
+  // |mmap()| should return |MAP_FAILED| (a.k.a. -1) on error. But it shouldn't
+  // return null either.
+  if (real_base == MAP_FAILED || !real_base) {
+    PLOG(ERROR) << "mmap";
+    return nullptr;
+  }
+
+  void* base = static_cast<char*>(real_base) + offset_rounding;
+  // Note: We can't use |MakeUnique| here, since it's not a friend of
+  // |SimplePlatformSharedBufferMapping| (only we are).
+  return std::unique_ptr<SimplePlatformSharedBufferMapping>(
+      new SimplePlatformSharedBufferMapping(base, length, real_base,
+                                            real_length));
 }
 
 ScopedPlatformHandle SimplePlatformSharedBuffer::DuplicatePlatformHandle() {
@@ -81,7 +124,7 @@
 
 ScopedPlatformHandle SimplePlatformSharedBuffer::PassPlatformHandle() {
   DCHECK(HasOneRef());
-  return handle_.Pass();
+  return std::move(handle_);
 }
 
 SimplePlatformSharedBuffer::SimplePlatformSharedBuffer(size_t num_bytes)
@@ -103,5 +146,125 @@
   return length_;
 }
 
+bool SimplePlatformSharedBuffer::Init() {
+  DCHECK(!handle_.is_valid());
+
+  if (static_cast<uint64_t>(num_bytes_) >
+      static_cast<uint64_t>(std::numeric_limits<off_t>::max())) {
+    return false;
+  }
+
+  ScopedPlatformHandle handle;
+
+// Use ashmem on Android.
+#if defined(OS_ANDROID)
+  handle.reset(PlatformHandle(ashmem_create_region(nullptr, num_bytes_)));
+  if (!handle.is_valid()) {
+    DPLOG(ERROR) << "ashmem_create_region()";
+    return false;
+  }
+
+  if (ashmem_set_prot_region(handle.get().fd, PROT_READ | PROT_WRITE) < 0) {
+    DPLOG(ERROR) << "ashmem_set_prot_region()";
+    return false;
+  }
+#else
+  base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+  // TODO(vtl): This is stupid. The implementation of
+  // |CreateAndOpenTemporaryFileInDir()| starts with an FD, |fdopen()|s to get a
+  // |FILE*|, and then we have to |dup(fileno(fp))| to get back to an FD that we
+  // can own. (base/memory/shared_memory_posix.cc does this too, with more
+  // |fstat()|s thrown in for good measure.)
+  base::FilePath shared_buffer_dir;
+  if (!base::GetShmemTempDir(false, &shared_buffer_dir)) {
+    LOG(ERROR) << "Failed to get temporary directory for shared memory";
+    return false;
+  }
+  base::FilePath shared_buffer_file;
+  util::ScopedFILE fp(base::CreateAndOpenTemporaryFileInDir(
+      shared_buffer_dir, &shared_buffer_file));
+  if (!fp) {
+    LOG(ERROR) << "Failed to create/open temporary file for shared memory";
+    return false;
+  }
+  // Note: |unlink()| is not interruptible.
+  if (unlink(shared_buffer_file.value().c_str()) != 0) {
+    PLOG(WARNING) << "unlink";
+    // This isn't "fatal" (e.g., someone else may have unlinked the file first),
+    // so we may as well continue.
+  }
+
+  // Note: |dup()| is not interruptible (but |dup2()|/|dup3()| are).
+  handle.reset(PlatformHandle(dup(fileno(fp.get()))));
+  if (!handle.is_valid()) {
+    PLOG(ERROR) << "dup";
+    return false;
+  }
+
+  if (HANDLE_EINTR(
+          ftruncate(handle.get().fd, static_cast<off_t>(num_bytes_))) != 0) {
+    PLOG(ERROR) << "ftruncate";
+    return false;
+  }
+#endif  // defined(OS_ANDROID)
+
+  handle_ = std::move(handle);
+  return true;
+}
+
+bool SimplePlatformSharedBuffer::InitFromPlatformHandle(
+    ScopedPlatformHandle platform_handle) {
+  DCHECK(!handle_.is_valid());
+
+  if (static_cast<uint64_t>(num_bytes_) >
+      static_cast<uint64_t>(std::numeric_limits<off_t>::max())) {
+    return false;
+  }
+
+// Use ashmem on Android.
+#if defined(OS_ANDROID)
+  int size = ashmem_get_size_region(platform_handle.get().fd);
+  if (size < 0) {
+    DPLOG(ERROR) << "ashmem_get_size_region()";
+    return false;
+  }
+
+  if (static_cast<size_t>(size) != num_bytes_) {
+    LOG(ERROR) << "Shared memory region has the wrong size";
+    return false;
+  }
+#else
+  struct stat sb = {};
+  // Note: |fstat()| isn't interruptible.
+  if (fstat(platform_handle.get().fd, &sb) != 0) {
+    PLOG(ERROR) << "fstat";
+    return false;
+  }
+
+  if (!S_ISREG(sb.st_mode)) {
+    LOG(ERROR) << "Platform handle not to a regular file";
+    return false;
+  }
+
+  if (sb.st_size != static_cast<off_t>(num_bytes_)) {
+    LOG(ERROR) << "Shared memory file has the wrong size";
+    return false;
+  }
+
+  // TODO(vtl): More checks?
+#endif  // defined(OS_ANDROID)
+
+  handle_ = platform_handle.Pass();
+  return true;
+}
+
+// SimplePlatformSharedBufferMapping -------------------------------------------
+
+void SimplePlatformSharedBufferMapping::Unmap() {
+  int result = munmap(real_base_, real_length_);
+  PLOG_IF(ERROR, result != 0) << "munmap";
+}
+
 }  // namespace embedder
 }  // namespace mojo
diff --git a/mojo/edk/embedder/simple_platform_shared_buffer.h b/mojo/edk/embedder/simple_platform_shared_buffer.h
index 199826d..75dfa82 100644
--- a/mojo/edk/embedder/simple_platform_shared_buffer.h
+++ b/mojo/edk/embedder/simple_platform_shared_buffer.h
@@ -8,6 +8,7 @@
 #include <stddef.h>
 
 #include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -18,9 +19,9 @@
  public:
   // Creates a shared buffer of size |num_bytes| bytes (initially zero-filled).
   // |num_bytes| must be nonzero. Returns null on failure.
-  static SimplePlatformSharedBuffer* Create(size_t num_bytes);
+  static util::RefPtr<SimplePlatformSharedBuffer> Create(size_t num_bytes);
 
-  static SimplePlatformSharedBuffer* CreateFromPlatformHandle(
+  static util::RefPtr<SimplePlatformSharedBuffer> CreateFromPlatformHandle(
       size_t num_bytes,
       ScopedPlatformHandle platform_handle);
 
@@ -39,8 +40,6 @@
   explicit SimplePlatformSharedBuffer(size_t num_bytes);
   ~SimplePlatformSharedBuffer() override;
 
-  // Implemented in simple_platform_shared_buffer_{posix,win}.cc:
-
   // This is called by |Create()| before this object is given to anyone.
   bool Init();
 
@@ -49,10 +48,6 @@
   // claimed |num_bytes_|.)
   bool InitFromPlatformHandle(ScopedPlatformHandle platform_handle);
 
-  // The platform-dependent part of |Map()|; doesn't check arguments.
-  std::unique_ptr<PlatformSharedBufferMapping> MapImpl(size_t offset,
-                                                       size_t length);
-
   const size_t num_bytes_;
 
   // This is set in |Init()|/|InitFromPlatformHandle()| and never modified
diff --git a/mojo/edk/embedder/simple_platform_shared_buffer_android.cc b/mojo/edk/embedder/simple_platform_shared_buffer_android.cc
deleted file mode 100644
index 6ee24e7..0000000
--- a/mojo/edk/embedder/simple_platform_shared_buffer_android.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/edk/embedder/simple_platform_shared_buffer.h"
-
-#include <stdint.h>
-#include <sys/mman.h>   // For |PROT_...|.
-#include <sys/types.h>  // For |off_t|.
-
-#include <limits>
-
-#include "base/logging.h"
-#include "third_party/ashmem/ashmem.h"
-
-namespace mojo {
-namespace embedder {
-
-// SimplePlatformSharedBuffer --------------------------------------------------
-
-bool SimplePlatformSharedBuffer::Init() {
-  DCHECK(!handle_.is_valid());
-
-  if (static_cast<uint64_t>(num_bytes_) >
-      static_cast<uint64_t>(std::numeric_limits<off_t>::max())) {
-    return false;
-  }
-
-  ScopedPlatformHandle handle(
-      PlatformHandle(ashmem_create_region(nullptr, num_bytes_)));
-  if (!handle.is_valid()) {
-    DPLOG(ERROR) << "ashmem_create_region()";
-    return false;
-  }
-
-  if (ashmem_set_prot_region(handle.get().fd, PROT_READ | PROT_WRITE) < 0) {
-    DPLOG(ERROR) << "ashmem_set_prot_region()";
-    return false;
-  }
-
-  handle_ = handle.Pass();
-  return true;
-}
-
-bool SimplePlatformSharedBuffer::InitFromPlatformHandle(
-    ScopedPlatformHandle platform_handle) {
-  DCHECK(!handle_.is_valid());
-
-  if (static_cast<uint64_t>(num_bytes_) >
-      static_cast<uint64_t>(std::numeric_limits<off_t>::max())) {
-    return false;
-  }
-
-  int size = ashmem_get_size_region(platform_handle.get().fd);
-
-  if (size < 0) {
-    DPLOG(ERROR) << "ashmem_get_size_region()";
-    return false;
-  }
-
-  if (static_cast<size_t>(size) != num_bytes_) {
-    LOG(ERROR) << "Shared memory region has the wrong size";
-    return false;
-  }
-
-  handle_ = platform_handle.Pass();
-  return true;
-}
-
-}  // namespace embedder
-}  // namespace mojo
diff --git a/mojo/edk/embedder/simple_platform_shared_buffer_posix.cc b/mojo/edk/embedder/simple_platform_shared_buffer_posix.cc
deleted file mode 100644
index ca3f013..0000000
--- a/mojo/edk/embedder/simple_platform_shared_buffer_posix.cc
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/edk/embedder/simple_platform_shared_buffer.h"
-
-#include <stdint.h>
-#include <stdio.h>     // For |fileno()|.
-#include <sys/mman.h>  // For |mmap()|/|munmap()|.
-#include <sys/stat.h>
-#include <sys/types.h>  // For |off_t|.
-#include <unistd.h>
-
-#include <limits>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/logging.h"
-#include "base/posix/eintr_wrapper.h"
-#include "base/sys_info.h"
-#include "base/threading/thread_restrictions.h"
-#include "mojo/edk/util/scoped_file.h"
-
-// We assume that |size_t| and |off_t| (type for |ftruncate()|) fits in a
-// |uint64_t|.
-static_assert(sizeof(size_t) <= sizeof(uint64_t), "size_t too big");
-static_assert(sizeof(off_t) <= sizeof(uint64_t), "off_t too big");
-
-namespace mojo {
-namespace embedder {
-
-// SimplePlatformSharedBuffer --------------------------------------------------
-
-// The implementation for android uses ashmem to generate the file descriptor
-// for the shared memory. See simple_platform_shared_buffer_android.cc
-#if !defined(OS_ANDROID)
-
-bool SimplePlatformSharedBuffer::Init() {
-  DCHECK(!handle_.is_valid());
-
-  base::ThreadRestrictions::ScopedAllowIO allow_io;
-
-  if (static_cast<uint64_t>(num_bytes_) >
-      static_cast<uint64_t>(std::numeric_limits<off_t>::max())) {
-    return false;
-  }
-
-  // TODO(vtl): This is stupid. The implementation of
-  // |CreateAndOpenTemporaryFileInDir()| starts with an FD, |fdopen()|s to get a
-  // |FILE*|, and then we have to |dup(fileno(fp))| to get back to an FD that we
-  // can own. (base/memory/shared_memory_posix.cc does this too, with more
-  // |fstat()|s thrown in for good measure.)
-  base::FilePath shared_buffer_dir;
-  if (!base::GetShmemTempDir(false, &shared_buffer_dir)) {
-    LOG(ERROR) << "Failed to get temporary directory for shared memory";
-    return false;
-  }
-  base::FilePath shared_buffer_file;
-  util::ScopedFILE fp(base::CreateAndOpenTemporaryFileInDir(
-      shared_buffer_dir, &shared_buffer_file));
-  if (!fp) {
-    LOG(ERROR) << "Failed to create/open temporary file for shared memory";
-    return false;
-  }
-  // Note: |unlink()| is not interruptible.
-  if (unlink(shared_buffer_file.value().c_str()) != 0) {
-    PLOG(WARNING) << "unlink";
-    // This isn't "fatal" (e.g., someone else may have unlinked the file first),
-    // so we may as well continue.
-  }
-
-  // Note: |dup()| is not interruptible (but |dup2()|/|dup3()| are).
-  ScopedPlatformHandle handle(PlatformHandle(dup(fileno(fp.get()))));
-  if (!handle.is_valid()) {
-    PLOG(ERROR) << "dup";
-    return false;
-  }
-
-  if (HANDLE_EINTR(
-          ftruncate(handle.get().fd, static_cast<off_t>(num_bytes_))) != 0) {
-    PLOG(ERROR) << "ftruncate";
-    return false;
-  }
-
-  handle_ = handle.Pass();
-  return true;
-}
-
-bool SimplePlatformSharedBuffer::InitFromPlatformHandle(
-    ScopedPlatformHandle platform_handle) {
-  DCHECK(!handle_.is_valid());
-
-  if (static_cast<uint64_t>(num_bytes_) >
-      static_cast<uint64_t>(std::numeric_limits<off_t>::max())) {
-    return false;
-  }
-
-  struct stat sb = {};
-  // Note: |fstat()| isn't interruptible.
-  if (fstat(platform_handle.get().fd, &sb) != 0) {
-    PLOG(ERROR) << "fstat";
-    return false;
-  }
-
-  if (!S_ISREG(sb.st_mode)) {
-    LOG(ERROR) << "Platform handle not to a regular file";
-    return false;
-  }
-
-  if (sb.st_size != static_cast<off_t>(num_bytes_)) {
-    LOG(ERROR) << "Shared memory file has the wrong size";
-    return false;
-  }
-
-  // TODO(vtl): More checks?
-
-  handle_ = platform_handle.Pass();
-  return true;
-}
-
-#endif  // !defined(OS_ANDROID)
-
-std::unique_ptr<PlatformSharedBufferMapping>
-SimplePlatformSharedBuffer::MapImpl(size_t offset, size_t length) {
-  size_t offset_rounding = offset % base::SysInfo::VMAllocationGranularity();
-  size_t real_offset = offset - offset_rounding;
-  size_t real_length = length + offset_rounding;
-
-  // This should hold (since we checked |num_bytes| versus the maximum value of
-  // |off_t| on creation, but it never hurts to be paranoid.
-  DCHECK_LE(static_cast<uint64_t>(real_offset),
-            static_cast<uint64_t>(std::numeric_limits<off_t>::max()));
-
-  void* real_base =
-      mmap(nullptr, real_length, PROT_READ | PROT_WRITE, MAP_SHARED,
-           handle_.get().fd, static_cast<off_t>(real_offset));
-  // |mmap()| should return |MAP_FAILED| (a.k.a. -1) on error. But it shouldn't
-  // return null either.
-  if (real_base == MAP_FAILED || !real_base) {
-    PLOG(ERROR) << "mmap";
-    return nullptr;
-  }
-
-  void* base = static_cast<char*>(real_base) + offset_rounding;
-  // Note: We can't use |MakeUnique| here, since it's not a friend of
-  // |SimplePlatformSharedBufferMapping| (only we are).
-  return std::unique_ptr<SimplePlatformSharedBufferMapping>(
-      new SimplePlatformSharedBufferMapping(base, length, real_base,
-                                            real_length));
-}
-
-// SimplePlatformSharedBufferMapping -------------------------------------------
-
-void SimplePlatformSharedBufferMapping::Unmap() {
-  int result = munmap(real_base_, real_length_);
-  PLOG_IF(ERROR, result != 0) << "munmap";
-}
-
-}  // namespace embedder
-}  // namespace mojo
diff --git a/mojo/edk/embedder/simple_platform_shared_buffer_unittest.cc b/mojo/edk/embedder/simple_platform_shared_buffer_unittest.cc
index 368604f..bb71d98 100644
--- a/mojo/edk/embedder/simple_platform_shared_buffer_unittest.cc
+++ b/mojo/edk/embedder/simple_platform_shared_buffer_unittest.cc
@@ -6,7 +6,6 @@
 
 #include <limits>
 
-#include "base/memory/ref_counted.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -21,8 +20,7 @@
   const int kFudge = 1234567890;
 
   // Make some memory.
-  scoped_refptr<SimplePlatformSharedBuffer> buffer(
-      SimplePlatformSharedBuffer::Create(kNumBytes));
+  auto buffer = SimplePlatformSharedBuffer::Create(kNumBytes);
   ASSERT_TRUE(buffer);
 
   // Map it all, scribble some stuff, and then unmap it.
@@ -98,8 +96,7 @@
 // TODO(vtl): Bigger buffers.
 
 TEST(SimplePlatformSharedBufferTest, InvalidMappings) {
-  scoped_refptr<SimplePlatformSharedBuffer> buffer(
-      SimplePlatformSharedBuffer::Create(100));
+  auto buffer = SimplePlatformSharedBuffer::Create(100);
   ASSERT_TRUE(buffer);
 
   // Zero length not allowed.
@@ -129,8 +126,7 @@
   // If |size_t| is 32-bit, it's quite possible/likely that |Create()| succeeds
   // (since it only involves creating a 4 GB file).
   const size_t kMaxSizeT = std::numeric_limits<size_t>::max();
-  scoped_refptr<SimplePlatformSharedBuffer> buffer(
-      SimplePlatformSharedBuffer::Create(kMaxSizeT));
+  auto buffer = SimplePlatformSharedBuffer::Create(kMaxSizeT);
   // But, assuming |sizeof(size_t) == sizeof(void*)|, mapping all of it should
   // always fail.
   if (buffer)
@@ -142,8 +138,7 @@
 // and reuse the same address, in which case we'd have to be more careful about
 // using the address as the key for unmapping.
 TEST(SimplePlatformSharedBufferTest, MappingsDistinct) {
-  scoped_refptr<SimplePlatformSharedBuffer> buffer(
-      SimplePlatformSharedBuffer::Create(100));
+  auto buffer = SimplePlatformSharedBuffer::Create(100);
   std::unique_ptr<PlatformSharedBufferMapping> mapping1(buffer->Map(0, 100));
   std::unique_ptr<PlatformSharedBufferMapping> mapping2(buffer->Map(0, 100));
   EXPECT_NE(mapping1->GetBase(), mapping2->GetBase());
@@ -152,8 +147,7 @@
 TEST(SimplePlatformSharedBufferTest, BufferZeroInitialized) {
   static const size_t kSizes[] = {10, 100, 1000, 10000, 100000};
   for (size_t i = 0; i < MOJO_ARRAYSIZE(kSizes); i++) {
-    scoped_refptr<SimplePlatformSharedBuffer> buffer(
-        SimplePlatformSharedBuffer::Create(kSizes[i]));
+    auto buffer = SimplePlatformSharedBuffer::Create(kSizes[i]);
     std::unique_ptr<PlatformSharedBufferMapping> mapping(
         buffer->Map(0, kSizes[i]));
     for (size_t j = 0; j < kSizes[i]; j++) {
@@ -170,8 +164,7 @@
   std::unique_ptr<PlatformSharedBufferMapping> mapping2;
 
   {
-    scoped_refptr<SimplePlatformSharedBuffer> buffer(
-        SimplePlatformSharedBuffer::Create(100));
+    auto buffer = SimplePlatformSharedBuffer::Create(100);
     mapping1 = buffer->Map(0, 100);
     mapping2 = buffer->Map(50, 50);
     static_cast<char*>(mapping1->GetBase())[50] = 'x';
diff --git a/mojo/edk/embedder/simple_platform_support.cc b/mojo/edk/embedder/simple_platform_support.cc
index f314323..d0b3110 100644
--- a/mojo/edk/embedder/simple_platform_support.cc
+++ b/mojo/edk/embedder/simple_platform_support.cc
@@ -8,6 +8,8 @@
 #include "base/time/time.h"
 #include "mojo/edk/embedder/simple_platform_shared_buffer.h"
 
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace embedder {
 
@@ -20,12 +22,13 @@
   base::RandBytes(bytes, num_bytes);
 }
 
-PlatformSharedBuffer* SimplePlatformSupport::CreateSharedBuffer(
+RefPtr<PlatformSharedBuffer> SimplePlatformSupport::CreateSharedBuffer(
     size_t num_bytes) {
   return SimplePlatformSharedBuffer::Create(num_bytes);
 }
 
-PlatformSharedBuffer* SimplePlatformSupport::CreateSharedBufferFromHandle(
+RefPtr<PlatformSharedBuffer>
+SimplePlatformSupport::CreateSharedBufferFromHandle(
     size_t num_bytes,
     ScopedPlatformHandle platform_handle) {
   return SimplePlatformSharedBuffer::CreateFromPlatformHandle(
diff --git a/mojo/edk/embedder/simple_platform_support.h b/mojo/edk/embedder/simple_platform_support.h
index 19c56a3..41c1602 100644
--- a/mojo/edk/embedder/simple_platform_support.h
+++ b/mojo/edk/embedder/simple_platform_support.h
@@ -23,8 +23,9 @@
 
   MojoTimeTicks GetTimeTicksNow() override;
   void GetCryptoRandomBytes(void* bytes, size_t num_bytes) override;
-  PlatformSharedBuffer* CreateSharedBuffer(size_t num_bytes) override;
-  PlatformSharedBuffer* CreateSharedBufferFromHandle(
+  util::RefPtr<PlatformSharedBuffer> CreateSharedBuffer(
+      size_t num_bytes) override;
+  util::RefPtr<PlatformSharedBuffer> CreateSharedBufferFromHandle(
       size_t num_bytes,
       ScopedPlatformHandle platform_handle) override;
 
diff --git a/mojo/edk/embedder/system_impl_private_entrypoints.cc b/mojo/edk/embedder/system_impl_private_entrypoints.cc
index e2a369b..e772165 100644
--- a/mojo/edk/embedder/system_impl_private_entrypoints.cc
+++ b/mojo/edk/embedder/system_impl_private_entrypoints.cc
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/logging.h"
 #include "mojo/edk/embedder/embedder_internal.h"
 #include "mojo/edk/system/core.h"
 #include "mojo/edk/system/dispatcher.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/c/system/buffer.h"
 #include "mojo/public/c/system/data_pipe.h"
 #include "mojo/public/c/system/functions.h"
@@ -16,7 +17,7 @@
 using mojo::system::Core;
 using mojo::system::Dispatcher;
 using mojo::system::MakeUserPointer;
-using mojo::system::RefPtr;
+using mojo::util::RefPtr;
 
 // Definitions of the system functions, but with an explicit parameter for the
 // core object rather than using the default singleton. Also includes functions
diff --git a/mojo/edk/mojo_edk.gni b/mojo/edk/mojo_edk.gni
index 21ad315..6fbaafc 100644
--- a/mojo/edk/mojo_edk.gni
+++ b/mojo/edk/mojo_edk.gni
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("../public/mojo_sdk.gni")
+import("//testing/test.gni")
 
 # A mojo_edk_source_set is a mojo_sdk_source_set that does not restrict
 # external dependencies and understands the following additional variables, all
@@ -96,6 +97,78 @@
   }
 }
 
+template("mojo_edk_unittests") {
+  test(target_name) {
+    deps = [
+      rebase_path("mojo/edk/system/test:run_all_unittests", ".", mojo_root),
+    ]
+    if (defined(invoker.sources)) {
+      sources = invoker.sources
+    }
+    if (defined(invoker.deps)) {
+      foreach(dep, invoker.deps) {
+        # The only deps that are not specified relative to the location of the
+        # Mojo EDK should be on targets within the same file or on a whitelisted
+        # set of external dependencies.
+        # TODO(vtl): Get rid of //base dependencies (and stop allowing it).
+        assert(get_path_info(dep, "dir") == "." || dep == "//testing/gtest" ||
+               dep == "//base")
+        deps += [ dep ]
+      }
+    }
+    if (defined(invoker.mojo_sdk_deps)) {
+      foreach(sdk_dep, invoker.mojo_sdk_deps) {
+        # Check that the SDK dep was not mistakenly given as an absolute path.
+        assert(get_path_info(sdk_dep, "abspath") != sdk_dep)
+        deps += [ rebase_path(sdk_dep, ".", mojo_root) ]
+      }
+    }
+    if (defined(invoker.mojo_edk_deps)) {
+      foreach(edk_dep, invoker.mojo_edk_deps) {
+        # Check that the EDK dep was not mistakenly given as an absolute path.
+        assert(get_path_info(edk_dep, "abspath") != edk_dep)
+        deps += [ rebase_path(edk_dep, ".", mojo_root) ]
+      }
+    }
+  }
+}
+
+template("mojo_edk_perftests") {
+  test(target_name) {
+    deps = [
+      rebase_path("mojo/edk/system/test:run_all_perftests", ".", mojo_root),
+    ]
+    if (defined(invoker.sources)) {
+      sources = invoker.sources
+    }
+    if (defined(invoker.deps)) {
+      foreach(dep, invoker.deps) {
+        # The only deps that are not specified relative to the location of the
+        # Mojo EDK should be on targets within the same file or on a whitelisted
+        # set of external dependencies.
+        # TODO(vtl): Get rid of //base dependencies (and stop allowing it).
+        assert(get_path_info(dep, "dir") == "." || dep == "//testing/gtest" ||
+               dep == "//base")
+        deps += [ dep ]
+      }
+    }
+    if (defined(invoker.mojo_sdk_deps)) {
+      foreach(sdk_dep, invoker.mojo_sdk_deps) {
+        # Check that the SDK dep was not mistakenly given as an absolute path.
+        assert(get_path_info(sdk_dep, "abspath") != sdk_dep)
+        deps += [ rebase_path(sdk_dep, ".", mojo_root) ]
+      }
+    }
+    if (defined(invoker.mojo_edk_deps)) {
+      foreach(edk_dep, invoker.mojo_edk_deps) {
+        # Check that the EDK dep was not mistakenly given as an absolute path.
+        assert(get_path_info(edk_dep, "abspath") != edk_dep)
+        deps += [ rebase_path(edk_dep, ".", mojo_root) ]
+      }
+    }
+  }
+}
+
 # Build EDK things with static thread annotation analysis enabled.
 # TODO(vtl): Should we set this at a higher level?
 if (is_clang) {
diff --git a/mojo/edk/system/BUILD.gn b/mojo/edk/system/BUILD.gn
index 7800a59..0139346 100644
--- a/mojo/edk/system/BUILD.gn
+++ b/mojo/edk/system/BUILD.gn
@@ -10,11 +10,8 @@
   import("//build/config/android/rules.gni")
 }
 
+# TODO(vtl): Should we get rid of this?
 config("system_config") {
-  defines = [
-    # Ensures that dependent projects import the core functions on Windows.
-    "MOJO_USE_SYSTEM_IMPL",
-  ]
 }
 
 component("system") {
@@ -83,8 +80,6 @@
     "message_pipe_dispatcher.h",
     "message_pipe_endpoint.cc",
     "message_pipe_endpoint.h",
-    "mutex.cc",
-    "mutex.h",
     "options_validation.h",
     "platform_handle_dispatcher.cc",
     "platform_handle_dispatcher.h",
@@ -94,10 +89,6 @@
     "raw_channel.cc",
     "raw_channel.h",
     "raw_channel_posix.cc",
-    "ref_counted.h",
-    "ref_counted_internal.h",
-    "ref_ptr.h",
-    "ref_ptr_internal.h",
     "remote_consumer_data_pipe_impl.cc",
     "remote_consumer_data_pipe_impl.h",
     "remote_data_pipe_ack.h",
@@ -109,17 +100,16 @@
     "simple_dispatcher.h",
     "slave_connection_manager.cc",
     "slave_connection_manager.h",
-    "thread_annotations.h",
     "transport_data.cc",
     "transport_data.h",
     "unique_identifier.cc",
     "unique_identifier.h",
+    "waitable_event.cc",
+    "waitable_event.h",
     "waiter.cc",
     "waiter.h",
   ]
 
-  defines = [ "MOJO_SYSTEM_IMPLEMENTATION" ]
-
   all_dependent_configs = [ ":system_config" ]
 
   public_deps = [
@@ -147,29 +137,9 @@
   ]
 }
 
-mojo_edk_source_set("test_utils") {
-  testonly = true
-
+mojo_edk_unittests("mojo_system_unittests") {
   sources = [
-    "test_utils.cc",
-    "test_utils.h",
-  ]
-
-  mojo_sdk_public_deps = [
-    "mojo/public/c/system",
-    "mojo/public/cpp/system",
-  ]
-
-  deps = [
-    "//base",
-    "//base/test:test_support",
-  ]
-
-  mojo_edk_deps = [ "mojo/edk/embedder:platform" ]
-}
-
-test("mojo_system_unittests") {
-  sources = [
+    # TODO(vtl): This should be in its own mojo_edk_unittests target.
     "../test/multiprocess_test_helper_unittest.cc",
     "awakable_list_unittest.cc",
     "channel_endpoint_id_unittest.cc",
@@ -196,21 +166,17 @@
     "message_pipe_test_utils.h",
     "message_pipe_unittest.cc",
     "multiprocess_message_pipe_unittest.cc",
-    "mutex_unittest.cc",
     "options_validation_unittest.cc",
     "platform_handle_dispatcher_unittest.cc",
     "raw_channel_unittest.cc",
-    "ref_counted_unittest.cc",
     "remote_data_pipe_impl_unittest.cc",
     "remote_message_pipe_unittest.cc",
-    "run_all_unittests.cc",
     "shared_buffer_dispatcher_unittest.cc",
     "simple_dispatcher_unittest.cc",
     "test_channel_endpoint_client.cc",
     "test_channel_endpoint_client.h",
-    "test_utils_unittest.cc",
-    "thread_annotations_unittest.cc",
     "unique_identifier_unittest.cc",
+    "waitable_event_unittest.cc",
     "waiter_test_utils.cc",
     "waiter_test_utils.h",
     "waiter_unittest.cc",
@@ -218,33 +184,41 @@
 
   deps = [
     ":system",
-    ":test_utils",
-    "../embedder:embedder_unittests",
-    "../test:test_support",
-    "../util",
     "//base",
-    "//base/test:test_support",
     "//testing/gtest",
   ]
 
-  allow_circular_includes_from = [ "../embedder:embedder_unittests" ]
+  mojo_edk_deps = [
+    # TODO(vtl): Add separate mojo_edk_unittests targets for these.
+    "mojo/edk/embedder:unittests",
+    "mojo/edk/system/test:unittests",
+    "mojo/edk/util:unittests",
+
+    "mojo/edk/system/test",
+    "mojo/edk/test:test_support",
+    "mojo/edk/util",
+  ]
 }
 
-test("mojo_system_perftests") {
+mojo_edk_perftests("mojo_system_perftests") {
   sources = [
     "message_pipe_perftest.cc",
     "message_pipe_test_utils.cc",
     "message_pipe_test_utils.h",
-    "ref_counted_perftest.cc",
   ]
 
   deps = [
     ":system",
-    ":test_utils",
-    "../test:test_support",
     "//base",
-    "//base/test:test_support",
-    "//base/test:test_support_perf",
     "//testing/gtest",
   ]
+
+  mojo_edk_deps = [
+    # TODO(vtl): Add separate test targets for this.
+    "mojo/edk/util:perftests",
+
+    "mojo/edk/system/test",
+    "mojo/edk/system/test:perf",
+    "mojo/edk/test:test_support",
+  ]
 }
diff --git a/mojo/edk/system/awakable_list_unittest.cc b/mojo/edk/system/awakable_list_unittest.cc
index 728dbc3..c2a1fb1 100644
--- a/mojo/edk/system/awakable_list_unittest.cc
+++ b/mojo/edk/system/awakable_list_unittest.cc
@@ -3,14 +3,15 @@
 // found in the LICENSE file.
 
 // NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a
-// heavily-loaded system). Sorry. |test::EpsilonDeadline()| may be increased to
+// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to
 // increase tolerance and reduce observed flakiness (though doing so reduces the
 // meaningfulness of the test).
 
 #include "mojo/edk/system/awakable_list.h"
 
 #include "mojo/edk/system/handle_signals_state.h"
-#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/test/sleep.h"
+#include "mojo/edk/system/test/timeouts.h"
 #include "mojo/edk/system/waiter.h"
 #include "mojo/edk/system/waiter_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -53,7 +54,7 @@
     test::SimpleWaiterThread thread(&result, &context);
     awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3);
     thread.Start();
-    test::Sleep(2 * test::EpsilonDeadline());
+    test::Sleep(2 * test::EpsilonTimeout());
     awakable_list.CancelAll();
   }  // Join |thread|.
   EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
@@ -100,7 +101,7 @@
     test::SimpleWaiterThread thread(&result, &context);
     awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3);
     thread.Start();
-    test::Sleep(2 * test::EpsilonDeadline());
+    test::Sleep(2 * test::EpsilonTimeout());
     awakable_list.AwakeForStateChange(HandleSignalsState(
         MOJO_HANDLE_SIGNAL_READABLE,
         MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE));
@@ -146,7 +147,7 @@
     test::SimpleWaiterThread thread(&result, &context);
     awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3);
     thread.Start();
-    test::Sleep(2 * test::EpsilonDeadline());
+    test::Sleep(2 * test::EpsilonTimeout());
     awakable_list.AwakeForStateChange(HandleSignalsState(
         MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_WRITABLE));
     awakable_list.Remove(thread.waiter());
@@ -176,7 +177,7 @@
     test::SimpleWaiterThread thread2(&result2, &context2);
     awakable_list.Add(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2);
     thread2.Start();
-    test::Sleep(2 * test::EpsilonDeadline());
+    test::Sleep(2 * test::EpsilonTimeout());
     awakable_list.CancelAll();
   }  // Join threads.
   EXPECT_EQ(MOJO_RESULT_CANCELLED, result1);
@@ -193,7 +194,7 @@
     test::SimpleWaiterThread thread2(&result2, &context2);
     awakable_list.Add(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 4);
     thread2.Start();
-    test::Sleep(2 * test::EpsilonDeadline());
+    test::Sleep(2 * test::EpsilonTimeout());
     awakable_list.AwakeForStateChange(HandleSignalsState(
         MOJO_HANDLE_SIGNAL_READABLE,
         MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE));
@@ -214,7 +215,7 @@
     test::SimpleWaiterThread thread2(&result2, &context2);
     awakable_list.Add(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 6);
     thread2.Start();
-    test::Sleep(2 * test::EpsilonDeadline());
+    test::Sleep(2 * test::EpsilonTimeout());
     awakable_list.AwakeForStateChange(HandleSignalsState(
         MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_READABLE));
     awakable_list.Remove(thread2.waiter());
@@ -232,7 +233,7 @@
     awakable_list.Add(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 7);
     thread1.Start();
 
-    test::Sleep(1 * test::EpsilonDeadline());
+    test::Sleep(1 * test::EpsilonTimeout());
 
     // Should do nothing.
     awakable_list.AwakeForStateChange(HandleSignalsState(
@@ -243,7 +244,7 @@
     awakable_list.Add(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 8);
     thread2.Start();
 
-    test::Sleep(1 * test::EpsilonDeadline());
+    test::Sleep(1 * test::EpsilonTimeout());
 
     // Awake #1.
     awakable_list.AwakeForStateChange(HandleSignalsState(
@@ -251,7 +252,7 @@
         MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE));
     awakable_list.Remove(thread1.waiter());
 
-    test::Sleep(1 * test::EpsilonDeadline());
+    test::Sleep(1 * test::EpsilonTimeout());
 
     test::SimpleWaiterThread thread3(&result3, &context3);
     awakable_list.Add(thread3.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 9);
@@ -261,7 +262,7 @@
     awakable_list.Add(thread4.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 10);
     thread4.Start();
 
-    test::Sleep(1 * test::EpsilonDeadline());
+    test::Sleep(1 * test::EpsilonTimeout());
 
     // Awake #2 and #3 for unsatisfiability.
     awakable_list.AwakeForStateChange(HandleSignalsState(
diff --git a/mojo/edk/system/channel.cc b/mojo/edk/system/channel.cc
index ae8b1c4..61fd80f 100644
--- a/mojo/edk/system/channel.cc
+++ b/mojo/edk/system/channel.cc
@@ -14,6 +14,10 @@
 #include "mojo/edk/system/endpoint_relayer.h"
 #include "mojo/edk/system/transport_data.h"
 
+using mojo::util::MakeRefCounted;
+using mojo::util::MutexLocker;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/channel.h b/mojo/edk/system/channel.h
index 3857a14..1828d43 100644
--- a/mojo/edk/system/channel.h
+++ b/mojo/edk/system/channel.h
@@ -16,10 +16,11 @@
 #include "mojo/edk/system/channel_endpoint_id.h"
 #include "mojo/edk/system/incoming_endpoint.h"
 #include "mojo/edk/system/message_in_transit.h"
-#include "mojo/edk/system/mutex.h"
 #include "mojo/edk/system/raw_channel.h"
-#include "mojo/edk/system/ref_counted.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/edk/util/ref_counted.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/c/system/types.h"
 #include "mojo/public/cpp/system/macros.h"
 
@@ -50,10 +51,10 @@
 // |ChannelEndpointClient| (e.g., |MessagePipe|), |ChannelEndpoint|, |Channel|.
 // Thus |Channel| may not call into |ChannelEndpoint| with |Channel|'s lock
 // held.
-class Channel final : public RefCountedThreadSafe<Channel>,
+class Channel final : public util::RefCountedThreadSafe<Channel>,
                       public RawChannel::Delegate {
  public:
-  // Note: Use |MakeRefCounted<Channel>()|.
+  // Note: Use |util::MakeRefCounted<Channel>()|.
 
   // This must be called on the creation thread before any other methods are
   // called, and before references to this object are given to any other
@@ -85,7 +86,7 @@
   //
   // (Bootstrapping is symmetric: Both sides call this, which will establish the
   // first connection across a channel.)
-  void SetBootstrapEndpoint(RefPtr<ChannelEndpoint>&& endpoint);
+  void SetBootstrapEndpoint(util::RefPtr<ChannelEndpoint>&& endpoint);
 
   // Like |SetBootstrapEndpoint()|, but with explicitly-specified local and
   // remote IDs.
@@ -93,7 +94,7 @@
   // (Bootstrapping is still symmetric, though the sides should obviously
   // interchange local and remote IDs. This can be used to allow multiple
   // "bootstrap" endpoints, though this is really most useful for testing.)
-  void SetBootstrapEndpointWithIds(RefPtr<ChannelEndpoint>&& endpoint,
+  void SetBootstrapEndpointWithIds(util::RefPtr<ChannelEndpoint>&& endpoint,
                                    ChannelEndpointId local_id,
                                    ChannelEndpointId remote_id);
 
@@ -149,14 +150,15 @@
                                        MessageInTransitQueue* message_queue);
   // This one returns the |ChannelEndpoint| for the serialized endpoint (which
   // can be used by, e.g., a |ProxyMessagePipeEndpoint|.
-  RefPtr<ChannelEndpoint> SerializeEndpointWithLocalPeer(
+  util::RefPtr<ChannelEndpoint> SerializeEndpointWithLocalPeer(
       void* destination,
       MessageInTransitQueue* message_queue,
-      RefPtr<ChannelEndpointClient>&& endpoint_client,
+      util::RefPtr<ChannelEndpointClient>&& endpoint_client,
       unsigned endpoint_client_port);
-  void SerializeEndpointWithRemotePeer(void* destination,
-                                       MessageInTransitQueue* message_queue,
-                                       RefPtr<ChannelEndpoint>&& peer_endpoint);
+  void SerializeEndpointWithRemotePeer(
+      void* destination,
+      MessageInTransitQueue* message_queue,
+      util::RefPtr<ChannelEndpoint>&& peer_endpoint);
 
   // Deserializes an endpoint that was sent from the peer |Channel| (using
   // |SerializeEndpoint...()|. |source| should be (a copy of) the data that
@@ -164,7 +166,7 @@
   // |GetSerializedEndpointSize()| bytes. This returns the deserialized
   // |IncomingEndpoint| (which can be converted into a |MessagePipe|) or null on
   // error.
-  RefPtr<IncomingEndpoint> DeserializeEndpoint(const void* source);
+  util::RefPtr<IncomingEndpoint> DeserializeEndpoint(const void* source);
 
   // See |RawChannel::GetSerializedPlatformHandleSize()|.
   size_t GetSerializedPlatformHandleSize() const;
@@ -224,7 +226,8 @@
   // for which |is_remote()| returns true).
   //
   // TODO(vtl): Maybe limit the number of attached message pipes.
-  ChannelEndpointId AttachAndRunEndpoint(RefPtr<ChannelEndpoint>&& endpoint);
+  ChannelEndpointId AttachAndRunEndpoint(
+      util::RefPtr<ChannelEndpoint>&& endpoint);
 
   // Helper to send channel control messages. Returns true on success. Callable
   // from any thread.
@@ -246,7 +249,7 @@
   // TODO(vtl): Annotate the above rule using |MOJO_ACQUIRED_{BEFORE,AFTER}()|,
   // once clang actually checks such annotations.
   // https://github.com/domokit/mojo/issues/313
-  mutable Mutex mutex_;
+  mutable util::Mutex mutex_;
 
   std::unique_ptr<RawChannel> raw_channel_ MOJO_GUARDED_BY(mutex_);
   bool is_running_ MOJO_GUARDED_BY(mutex_);
@@ -257,7 +260,7 @@
   ChannelManager* channel_manager_ MOJO_GUARDED_BY(mutex_);
 
   using IdToEndpointMap =
-      std::unordered_map<ChannelEndpointId, RefPtr<ChannelEndpoint>>;
+      std::unordered_map<ChannelEndpointId, util::RefPtr<ChannelEndpoint>>;
   // Map from local IDs to endpoints. If the endpoint is null, this means that
   // we're just waiting for the remove ack before removing the entry.
   IdToEndpointMap local_id_to_endpoint_map_ MOJO_GUARDED_BY(mutex_);
@@ -265,7 +268,7 @@
   LocalChannelEndpointIdGenerator local_id_generator_ MOJO_GUARDED_BY(mutex_);
 
   using IdToIncomingEndpointMap =
-      std::unordered_map<ChannelEndpointId, RefPtr<IncomingEndpoint>>;
+      std::unordered_map<ChannelEndpointId, util::RefPtr<IncomingEndpoint>>;
   // Map from local IDs to incoming endpoints (i.e., those received inside other
   // messages, but not yet claimed via |DeserializeEndpoint()|).
   IdToIncomingEndpointMap incoming_endpoints_ MOJO_GUARDED_BY(mutex_);
diff --git a/mojo/edk/system/channel_endpoint.cc b/mojo/edk/system/channel_endpoint.cc
index b2ced62..8d8ecc1 100644
--- a/mojo/edk/system/channel_endpoint.cc
+++ b/mojo/edk/system/channel_endpoint.cc
@@ -12,6 +12,9 @@
 #include "mojo/edk/system/channel_endpoint_client.h"
 #include "mojo/public/cpp/system/macros.h"
 
+using mojo::util::MutexLocker;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/channel_endpoint.h b/mojo/edk/system/channel_endpoint.h
index af537e5..fca0e9e 100644
--- a/mojo/edk/system/channel_endpoint.h
+++ b/mojo/edk/system/channel_endpoint.h
@@ -9,9 +9,10 @@
 
 #include "mojo/edk/system/channel_endpoint_id.h"
 #include "mojo/edk/system/message_in_transit_queue.h"
-#include "mojo/edk/system/mutex.h"
-#include "mojo/edk/system/ref_counted.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/edk/util/ref_counted.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -109,9 +110,10 @@
 //         simultaneously, and both sides send "remove" messages). In that
 //         case, it must still remain alive until it receives the "remove
 //         ack" (and it must ack the "remove" message that it received).
-class ChannelEndpoint final : public RefCountedThreadSafe<ChannelEndpoint> {
+class ChannelEndpoint final
+    : public util::RefCountedThreadSafe<ChannelEndpoint> {
  public:
-  // Note: Use |MakeRefCounted<ChannelEndpoint>()|.
+  // Note: Use |util::MakeRefCounted<ChannelEndpoint>()|.
 
   // Methods called by |ChannelEndpointClient|:
 
@@ -127,7 +129,7 @@
   // This returns true in the typical case, and false if this endpoint has been
   // detached from the channel, in which case the caller should probably call
   // its (new) client's |OnDetachFromChannel()|.
-  bool ReplaceClient(RefPtr<ChannelEndpointClient>&& client,
+  bool ReplaceClient(util::RefPtr<ChannelEndpointClient>&& client,
                      unsigned client_port);
 
   // Called before the |ChannelEndpointClient| gives up its reference to this
@@ -161,7 +163,7 @@
   // in which case |message_queue| should not be null. In that case, this
   // endpoint will simply send queued messages upon being attached to a
   // |Channel| and immediately detach itself.
-  ChannelEndpoint(RefPtr<ChannelEndpointClient>&& client,
+  ChannelEndpoint(util::RefPtr<ChannelEndpointClient>&& client,
                   unsigned client_port,
                   MessageInTransitQueue* message_queue = nullptr);
 
@@ -177,7 +179,7 @@
   // this does not call |channel_->DetachEndpoint()|.
   void DieNoLock() MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
 
-  Mutex mutex_;
+  util::Mutex mutex_;
 
   enum class State {
     // |AttachAndRun()| has not been called yet (|channel_| is null).
@@ -192,9 +194,9 @@
 
   // |client_| must be valid whenever it is non-null. Before |*client_| gives up
   // its reference to this object, it must call |DetachFromClient()|.
-  // NOTE: This is a |RefPtr<>|, rather than a raw pointer, since the |Channel|
-  // needs to keep the client (e.g., |MessagePipe|) alive for the "proxy-proxy"
-  // case.
+  // NOTE: This is a |util:RefPtr<>|, rather than a raw pointer, since the
+  // |Channel| needs to keep the client (e.g., |MessagePipe|) alive for the
+  // "proxy-proxy" case.
   // WARNING: |ChannelEndpointClient| methods must not be called under |mutex_|.
   // Thus to make such a call, a reference must first be taken under |mutex_|
   // and the lock released.
@@ -204,7 +206,7 @@
   // WARNING: Beware of interactions with |ReplaceClient()|. By the time the
   // call is made, the client may have changed. This must be detected and dealt
   // with.
-  RefPtr<ChannelEndpointClient> client_ MOJO_GUARDED_BY(mutex_);
+  util::RefPtr<ChannelEndpointClient> client_ MOJO_GUARDED_BY(mutex_);
   unsigned client_port_ MOJO_GUARDED_BY(mutex_);
 
   // |channel_| must be valid whenever it is non-null. Before |*channel_| gives
diff --git a/mojo/edk/system/channel_endpoint_client.h b/mojo/edk/system/channel_endpoint_client.h
index fea942d..5b4a21b 100644
--- a/mojo/edk/system/channel_endpoint_client.h
+++ b/mojo/edk/system/channel_endpoint_client.h
@@ -5,7 +5,7 @@
 #ifndef MOJO_EDK_SYSTEM_CHANNEL_ENDPOINT_CLIENT_H_
 #define MOJO_EDK_SYSTEM_CHANNEL_ENDPOINT_CLIENT_H_
 
-#include "mojo/edk/system/ref_counted.h"
+#include "mojo/edk/util/ref_counted.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -30,7 +30,7 @@
 // |ChannelEndpoint| has apparently relinquished its pointer to the
 // |ChannelEndpointClient|).
 class ChannelEndpointClient
-    : public RefCountedThreadSafe<ChannelEndpointClient> {
+    : public util::RefCountedThreadSafe<ChannelEndpointClient> {
  public:
   // Called by |ChannelEndpoint| in response to its |OnReadMessage()|, which is
   // called by |Channel| when it receives a message for the |ChannelEndpoint|.
diff --git a/mojo/edk/system/channel_endpoint_unittest.cc b/mojo/edk/system/channel_endpoint_unittest.cc
index 19cc237..5c93fb2 100644
--- a/mojo/edk/system/channel_endpoint_unittest.cc
+++ b/mojo/edk/system/channel_endpoint_unittest.cc
@@ -7,15 +7,17 @@
 #include <memory>
 #include <utility>
 
-#include "base/synchronization/waitable_event.h"
-#include "base/test/test_timeouts.h"
 #include "mojo/edk/system/channel_test_base.h"
 #include "mojo/edk/system/message_in_transit_queue.h"
 #include "mojo/edk/system/message_in_transit_test_utils.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/system/test/timeouts.h"
 #include "mojo/edk/system/test_channel_endpoint_client.h"
+#include "mojo/edk/system/waitable_event.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/cpp/system/macros.h"
 
+using mojo::util::MakeRefCounted;
+
 namespace mojo {
 namespace system {
 namespace {
@@ -59,7 +61,7 @@
   channel(1)->SetBootstrapEndpoint(endpoint1.Clone());
 
   // We'll receive a message on channel/client 0.
-  base::WaitableEvent read_event(true, false);
+  ManualResetWaitableEvent read_event;
   client0->SetReadEvent(&read_event);
 
   // Make a test message.
@@ -69,14 +71,14 @@
   // Check that our test utility works (at least in one direction).
   test::VerifyTestMessage(send_message.get(), message_id);
 
-  // Event shouldn't be signalled yet.
-  EXPECT_FALSE(read_event.IsSignaled());
+  // Event shouldn't be signaled yet.
+  EXPECT_FALSE(read_event.IsSignaledForTest());
 
   // Send it through channel/endpoint 1.
   EXPECT_TRUE(endpoint1->EnqueueMessage(std::move(send_message)));
 
   // Wait to receive it.
-  EXPECT_TRUE(read_event.TimedWait(TestTimeouts::tiny_timeout()));
+  EXPECT_FALSE(read_event.WaitWithTimeout(test::TinyTimeout()));
   client0->SetReadEvent(nullptr);
 
   // Check the received message.
@@ -113,10 +115,10 @@
   EXPECT_TRUE(endpoint1->EnqueueMessage(test::MakeTestMessage(6)));
 
   // Wait for the messages.
-  base::WaitableEvent read_event(true, false);
+  ManualResetWaitableEvent read_event;
   client0->SetReadEvent(&read_event);
   for (size_t i = 0; client0->NumMessages() < 6 && i < 6; i++) {
-    EXPECT_TRUE(read_event.TimedWait(TestTimeouts::tiny_timeout()));
+    EXPECT_FALSE(read_event.WaitWithTimeout(test::TinyTimeout()));
     read_event.Reset();
   }
   client0->SetReadEvent(nullptr);
diff --git a/mojo/edk/system/channel_manager.cc b/mojo/edk/system/channel_manager.cc
index b2f7388..ca0c270 100644
--- a/mojo/edk/system/channel_manager.cc
+++ b/mojo/edk/system/channel_manager.cc
@@ -12,6 +12,10 @@
 #include "mojo/edk/system/channel_endpoint.h"
 #include "mojo/edk/system/message_pipe_dispatcher.h"
 
+using mojo::util::MakeRefCounted;
+using mojo::util::MutexLocker;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/channel_manager.h b/mojo/edk/system/channel_manager.h
index af2e28e..1ef217b 100644
--- a/mojo/edk/system/channel_manager.h
+++ b/mojo/edk/system/channel_manager.h
@@ -10,12 +10,12 @@
 #include <unordered_map>
 
 #include "base/callback_forward.h"
-#include "base/memory/ref_counted.h"
 #include "mojo/edk/embedder/platform_task_runner.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
 #include "mojo/edk/system/channel_id.h"
-#include "mojo/edk/system/mutex.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace base {
@@ -71,7 +71,7 @@
   // constructor). |channel_id| should be a valid |ChannelId| (i.e., nonzero)
   // not "assigned" to any other |Channel| being managed by this
   // |ChannelManager|.
-  RefPtr<MessagePipeDispatcher> CreateChannelOnIOThread(
+  util::RefPtr<MessagePipeDispatcher> CreateChannelOnIOThread(
       ChannelId channel_id,
       embedder::ScopedPlatformHandle platform_handle);
 
@@ -79,7 +79,7 @@
   // pipe. Returns the newly-created |Channel|.
   // TODO(vtl): Maybe get rid of the others (and bootstrap message pipes in
   // general).
-  RefPtr<Channel> CreateChannelWithoutBootstrapOnIOThread(
+  util::RefPtr<Channel> CreateChannelWithoutBootstrapOnIOThread(
       ChannelId channel_id,
       embedder::ScopedPlatformHandle platform_handle);
 
@@ -87,14 +87,14 @@
   // completion, will call |callback| (using |callback_thread_task_runner| if it
   // is non-null, else on the I/O thread). Note: This will always post a task to
   // the I/O thread, even if called from that thread.
-  RefPtr<MessagePipeDispatcher> CreateChannel(
+  util::RefPtr<MessagePipeDispatcher> CreateChannel(
       ChannelId channel_id,
       embedder::ScopedPlatformHandle platform_handle,
       const base::Closure& callback,
       embedder::PlatformTaskRunnerRefPtr callback_thread_task_runner);
 
   // Gets the |Channel| with the given ID (which must exist).
-  RefPtr<Channel> GetChannel(ChannelId channel_id) const;
+  util::RefPtr<Channel> GetChannel(ChannelId channel_id) const;
 
   // Informs the channel manager (and thus channel) that it will be shutdown
   // soon (by calling |ShutdownChannel()|). Calling this is optional (and may in
@@ -129,10 +129,10 @@
   // Used by |CreateChannelOnIOThread()| and |CreateChannelHelper()|. Called on
   // the I/O thread. |bootstrap_channel_endpoint| is optional and may be null.
   // Returns the newly-created |Channel|.
-  RefPtr<Channel> CreateChannelOnIOThreadHelper(
+  util::RefPtr<Channel> CreateChannelOnIOThreadHelper(
       ChannelId channel_id,
       embedder::ScopedPlatformHandle platform_handle,
-      RefPtr<ChannelEndpoint>&& bootstrap_channel_endpoint);
+      util::RefPtr<ChannelEndpoint>&& bootstrap_channel_endpoint);
 
   // Used by |CreateChannel()|. Called on the I/O thread.
   // TODO(vtl): |bootstrap_channel_endpoint| should be an rvalue reference, but
@@ -140,7 +140,7 @@
   void CreateChannelHelper(
       ChannelId channel_id,
       embedder::ScopedPlatformHandle platform_handle,
-      RefPtr<ChannelEndpoint> bootstrap_channel_endpoint,
+      util::RefPtr<ChannelEndpoint> bootstrap_channel_endpoint,
       const base::Closure& callback,
       embedder::PlatformTaskRunnerRefPtr callback_thread_task_runner);
 
@@ -153,9 +153,10 @@
   // TODO(vtl): Annotate the above rule using |MOJO_ACQUIRED_{BEFORE,AFTER}()|,
   // once clang actually checks such annotations.
   // https://github.com/domokit/mojo/issues/313
-  mutable Mutex mutex_;
+  mutable util::Mutex mutex_;
 
-  using ChannelIdToChannelMap = std::unordered_map<ChannelId, RefPtr<Channel>>;
+  using ChannelIdToChannelMap =
+      std::unordered_map<ChannelId, util::RefPtr<Channel>>;
   ChannelIdToChannelMap channels_ MOJO_GUARDED_BY(mutex_);
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(ChannelManager);
diff --git a/mojo/edk/system/channel_manager_unittest.cc b/mojo/edk/system/channel_manager_unittest.cc
index b5467e7..9b9e6ac 100644
--- a/mojo/edk/system/channel_manager_unittest.cc
+++ b/mojo/edk/system/channel_manager_unittest.cc
@@ -14,10 +14,12 @@
 #include "mojo/edk/system/channel.h"
 #include "mojo/edk/system/channel_endpoint.h"
 #include "mojo/edk/system/message_pipe_dispatcher.h"
-#include "mojo/edk/test/simple_test_thread.h"
+#include "mojo/edk/system/test/simple_test_thread.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 namespace {
@@ -53,12 +55,16 @@
 
   RefPtr<Channel> ch = channel_manager().GetChannel(id);
   EXPECT_TRUE(ch);
+  // |ChannelManager| should have a ref.
+  EXPECT_FALSE(ch->HasOneRef());
 
   channel_manager().WillShutdownChannel(id);
+  // |ChannelManager| should still have a ref.
+  EXPECT_FALSE(ch->HasOneRef());
 
   channel_manager().ShutdownChannelOnIOThread(id);
   // |ChannelManager| should have given up its ref.
-  ch->AssertHasOneRef();
+  EXPECT_TRUE(ch->HasOneRef());
 
   EXPECT_EQ(MOJO_RESULT_OK, d->Close());
 }
@@ -83,18 +89,19 @@
   // Calling |WillShutdownChannel()| multiple times (on |id1|) is okay.
   channel_manager().WillShutdownChannel(id1);
   channel_manager().WillShutdownChannel(id1);
+  EXPECT_FALSE(ch1->HasOneRef());
   // Not calling |WillShutdownChannel()| (on |id2|) is okay too.
 
   channel_manager().ShutdownChannelOnIOThread(id1);
-  ch1->AssertHasOneRef();
+  EXPECT_TRUE(ch1->HasOneRef());
   channel_manager().ShutdownChannelOnIOThread(id2);
-  ch2->AssertHasOneRef();
+  EXPECT_TRUE(ch2->HasOneRef());
 
   EXPECT_EQ(MOJO_RESULT_OK, d1->Close());
   EXPECT_EQ(MOJO_RESULT_OK, d2->Close());
 }
 
-class OtherThread : public mojo::test::SimpleTestThread {
+class OtherThread : public test::SimpleTestThread {
  public:
   // Note: There should be no other refs to the channel identified by
   // |channel_id| outside the channel manager.
@@ -115,8 +122,12 @@
 
     // You can use any unique, nonzero value as the ID.
     RefPtr<Channel> ch = channel_manager_->GetChannel(channel_id_);
+    // |ChannelManager| should have a ref.
+    EXPECT_FALSE(ch->HasOneRef());
 
     channel_manager_->WillShutdownChannel(channel_id_);
+    // |ChannelManager| should still have a ref.
+    EXPECT_FALSE(ch->HasOneRef());
 
     {
       base::MessageLoop message_loop;
diff --git a/mojo/edk/system/channel_test_base.cc b/mojo/edk/system/channel_test_base.cc
index 1069e79..ecb7d1b 100644
--- a/mojo/edk/system/channel_test_base.cc
+++ b/mojo/edk/system/channel_test_base.cc
@@ -11,12 +11,14 @@
 #include "mojo/edk/embedder/platform_channel_pair.h"
 #include "mojo/edk/system/raw_channel.h"
 
+using mojo::util::MakeRefCounted;
+
 namespace mojo {
 namespace system {
 namespace test {
 
 ChannelTestBase::ChannelTestBase()
-    : io_thread_(mojo::test::TestIOThread::StartMode::AUTO) {}
+    : io_thread_(TestIOThread::StartMode::AUTO) {}
 
 ChannelTestBase::~ChannelTestBase() {
 }
diff --git a/mojo/edk/system/channel_test_base.h b/mojo/edk/system/channel_test_base.h
index 8b1b932..4ca931c 100644
--- a/mojo/edk/system/channel_test_base.h
+++ b/mojo/edk/system/channel_test_base.h
@@ -10,8 +10,8 @@
 #include "base/bind.h"
 #include "mojo/edk/embedder/simple_platform_support.h"
 #include "mojo/edk/system/channel.h"
-#include "mojo/edk/system/ref_ptr.h"
-#include "mojo/edk/test/test_io_thread.h"
+#include "mojo/edk/system/test/test_io_thread.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -43,17 +43,17 @@
   void ShutdownChannelOnIOThread(unsigned i);
   void ShutdownAndReleaseChannelOnIOThread(unsigned i);
 
-  mojo::test::TestIOThread* io_thread() { return &io_thread_; }
+  TestIOThread* io_thread() { return &io_thread_; }
   Channel* channel(unsigned i) { return channels_[i].get(); }
-  RefPtr<Channel>* mutable_channel(unsigned i) { return &channels_[i]; }
+  util::RefPtr<Channel>* mutable_channel(unsigned i) { return &channels_[i]; }
 
  private:
   void SetUpOnIOThread();
 
   embedder::SimplePlatformSupport platform_support_;
-  mojo::test::TestIOThread io_thread_;
+  TestIOThread io_thread_;
   std::unique_ptr<RawChannel> raw_channels_[2];
-  RefPtr<Channel> channels_[2];
+  util::RefPtr<Channel> channels_[2];
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(ChannelTestBase);
 };
diff --git a/mojo/edk/system/channel_unittest.cc b/mojo/edk/system/channel_unittest.cc
index b90c884..d4894fe 100644
--- a/mojo/edk/system/channel_unittest.cc
+++ b/mojo/edk/system/channel_unittest.cc
@@ -10,9 +10,10 @@
 #include "mojo/edk/system/channel_endpoint_id.h"
 #include "mojo/edk/system/channel_test_base.h"
 #include "mojo/edk/system/message_pipe.h"
-#include "mojo/edk/system/ref_ptr.h"
-#include "mojo/edk/system/test_utils.h"
 #include "mojo/edk/system/waiter.h"
+#include "mojo/edk/util/ref_ptr.h"
+
+using mojo::util::RefPtr;
 
 namespace mojo {
 namespace system {
@@ -29,7 +30,7 @@
   PostMethodToIOThreadAndWait(&ChannelTest::ShutdownChannelOnIOThread, 0);
 
   // Okay to destroy |Channel| on not-the-I/O-thread.
-  channel(0)->AssertHasOneRef();
+  EXPECT_TRUE(channel(0)->HasOneRef());
   *mutable_channel(0) = nullptr;
 }
 
@@ -47,7 +48,7 @@
 
   PostMethodToIOThreadAndWait(&ChannelTest::ShutdownChannelOnIOThread, 0);
 
-  channel(0)->AssertHasOneRef();
+  EXPECT_TRUE(channel(0)->HasOneRef());
 }
 
 // ChannelTest.ShutdownAfterAttachAndRun ---------------------------------------
@@ -79,7 +80,7 @@
 
   mp->Close(0);
 
-  channel(0)->AssertHasOneRef();
+  EXPECT_TRUE(channel(0)->HasOneRef());
 }
 
 // ChannelTest.WaitAfterAttachRunAndShutdown -----------------------------------
@@ -105,7 +106,7 @@
 
   mp->Close(0);
 
-  channel(0)->AssertHasOneRef();
+  EXPECT_TRUE(channel(0)->HasOneRef());
 }
 
 // ChannelTest.EndpointChannelShutdownRace -------------------------------------
diff --git a/mojo/edk/system/connection_manager.h b/mojo/edk/system/connection_manager.h
index 7cc0801..13d7584 100644
--- a/mojo/edk/system/connection_manager.h
+++ b/mojo/edk/system/connection_manager.h
@@ -9,7 +9,7 @@
 
 #include "mojo/edk/system/connection_identifier.h"
 #include "mojo/edk/system/process_identifier.h"
-#include "mojo/edk/system/thread_annotations.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc
index ae729a9..8e1340f 100644
--- a/mojo/edk/system/core.cc
+++ b/mojo/edk/system/core.cc
@@ -26,6 +26,9 @@
 #include "mojo/public/c/system/macros.h"
 #include "mojo/public/cpp/system/macros.h"
 
+using mojo::util::MutexLocker;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h
index 8c79421..c8a47e0 100644
--- a/mojo/edk/system/core.h
+++ b/mojo/edk/system/core.h
@@ -11,8 +11,9 @@
 #include "mojo/edk/system/handle_table.h"
 #include "mojo/edk/system/mapping_table.h"
 #include "mojo/edk/system/memory.h"
-#include "mojo/edk/system/mutex.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/c/system/buffer.h"
 #include "mojo/public/c/system/data_pipe.h"
 #include "mojo/public/c/system/message_pipe.h"
@@ -48,7 +49,7 @@
 
   // Looks up the dispatcher for the given handle. Returns null if the handle is
   // invalid.
-  RefPtr<Dispatcher> GetDispatcher(MojoHandle handle);
+  util::RefPtr<Dispatcher> GetDispatcher(MojoHandle handle);
 
   // Like |GetDispatcher()|, but also removes the handle from the handle table.
   // On success, gets the dispatcher for a given handle (which should not be
@@ -57,7 +58,7 @@
   // |MOJO_RESULT_INVALID_ARGUMENT| if there's no dispatcher for the given
   // handle or |MOJO_RESULT_BUSY| if the handle is marked as busy.)
   MojoResult GetAndRemoveDispatcher(MojoHandle handle,
-                                    RefPtr<Dispatcher>* dispatcher);
+                                    util::RefPtr<Dispatcher>* dispatcher);
 
   // Watches on the given handle for the given signals, calling |callback| when
   // a signal is satisfied or when all signals become unsatisfiable. |callback|
@@ -175,10 +176,10 @@
 
   // TODO(vtl): |handle_table_mutex_| should be a reader-writer lock (if only we
   // had them).
-  Mutex handle_table_mutex_;
+  util::Mutex handle_table_mutex_;
   HandleTable handle_table_ MOJO_GUARDED_BY(handle_table_mutex_);
 
-  Mutex mapping_table_mutex_;
+  util::Mutex mapping_table_mutex_;
   MappingTable mapping_table_ MOJO_GUARDED_BY(mapping_table_mutex_);
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(Core);
diff --git a/mojo/edk/system/core_test_base.cc b/mojo/edk/system/core_test_base.cc
index 29b569e..9048070 100644
--- a/mojo/edk/system/core_test_base.cc
+++ b/mojo/edk/system/core_test_base.cc
@@ -11,9 +11,12 @@
 #include "mojo/edk/system/core.h"
 #include "mojo/edk/system/dispatcher.h"
 #include "mojo/edk/system/memory.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/cpp/system/macros.h"
 
+using mojo::util::MutexLocker;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 namespace test {
diff --git a/mojo/edk/system/core_test_base.h b/mojo/edk/system/core_test_base.h
index e2375cd..0e9c14c 100644
--- a/mojo/edk/system/core_test_base.h
+++ b/mojo/edk/system/core_test_base.h
@@ -6,7 +6,8 @@
 #define MOJO_EDK_SYSTEM_CORE_TEST_BASE_H_
 
 #include "mojo/edk/embedder/simple_platform_support.h"
-#include "mojo/edk/system/mutex.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/c/system/types.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -88,7 +89,7 @@
   void AwakableWasAdded(Awakable*);
 
  private:
-  mutable Mutex mutex_;
+  mutable util::Mutex mutex_;
   unsigned ctor_call_count_ MOJO_GUARDED_BY(mutex_);
   unsigned dtor_call_count_ MOJO_GUARDED_BY(mutex_);
   unsigned close_call_count_ MOJO_GUARDED_BY(mutex_);
diff --git a/mojo/edk/system/core_unittest.cc b/mojo/edk/system/core_unittest.cc
index 57495d8..89f9856 100644
--- a/mojo/edk/system/core_unittest.cc
+++ b/mojo/edk/system/core_unittest.cc
@@ -11,7 +11,7 @@
 #include "base/bind.h"
 #include "mojo/edk/system/awakable.h"
 #include "mojo/edk/system/core_test_base.h"
-#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/test/sleep.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -30,7 +30,7 @@
   const MojoTimeTicks start = core()->GetTimeTicksNow();
   EXPECT_NE(static_cast<MojoTimeTicks>(0), start)
       << "GetTimeTicksNow should return nonzero value";
-  test::Sleep(test::DeadlineFromMilliseconds(15));
+  test::SleepMilliseconds(15u);
   const MojoTimeTicks finish = core()->GetTimeTicksNow();
   // Allow for some fuzz in sleep.
   EXPECT_GE((finish - start), static_cast<MojoTimeTicks>(8000))
diff --git a/mojo/edk/system/data_pipe.cc b/mojo/edk/system/data_pipe.cc
index ffe3469..7cfd2a5 100644
--- a/mojo/edk/system/data_pipe.cc
+++ b/mojo/edk/system/data_pipe.cc
@@ -25,6 +25,9 @@
 #include "mojo/edk/system/remote_producer_data_pipe_impl.h"
 #include "mojo/edk/util/make_unique.h"
 
+using mojo::util::MutexLocker;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/data_pipe.h b/mojo/edk/system/data_pipe.h
index 9eb567a..62fe5aa 100644
--- a/mojo/edk/system/data_pipe.h
+++ b/mojo/edk/system/data_pipe.h
@@ -14,9 +14,9 @@
 #include "mojo/edk/system/channel_endpoint_client.h"
 #include "mojo/edk/system/handle_signals_state.h"
 #include "mojo/edk/system/memory.h"
-#include "mojo/edk/system/mutex.h"
-#include "mojo/edk/system/ref_ptr.h"
-#include "mojo/edk/system/thread_annotations.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/c/system/data_pipe.h"
 #include "mojo/public/c/system/types.h"
 #include "mojo/public/cpp/system/macros.h"
@@ -58,7 +58,7 @@
   // |ValidateOptions()|. In particular: |struct_size| is ignored (so
   // |validated_options| must be the current version of the struct) and
   // |capacity_num_bytes| must be nonzero.
-  static RefPtr<DataPipe> CreateLocal(
+  static util::RefPtr<DataPipe> CreateLocal(
       const MojoCreateDataPipeOptions& validated_options);
 
   // Creates a data pipe with a remote producer and a local consumer, using an
@@ -67,10 +67,10 @@
   // |channel_endpoint| is null, this will create a "half-open" data pipe (with
   // only the consumer open). Note that this may fail, in which case it returns
   // null.
-  static RefPtr<DataPipe> CreateRemoteProducerFromExisting(
+  static util::RefPtr<DataPipe> CreateRemoteProducerFromExisting(
       const MojoCreateDataPipeOptions& validated_options,
       MessageInTransitQueue* message_queue,
-      RefPtr<ChannelEndpoint>&& channel_endpoint);
+      util::RefPtr<ChannelEndpoint>&& channel_endpoint);
 
   // Creates a data pipe with a local producer and a remote consumer, using an
   // existing |ChannelEndpoint| (whose |ReplaceClient()| it'll call) and taking
@@ -78,11 +78,11 @@
   // (|message_queue| may be null). If |channel_endpoint| is null, this will
   // create a "half-open" data pipe (with only the producer open). Note that
   // this may fail, in which case it returns null.
-  static RefPtr<DataPipe> CreateRemoteConsumerFromExisting(
+  static util::RefPtr<DataPipe> CreateRemoteConsumerFromExisting(
       const MojoCreateDataPipeOptions& validated_options,
       size_t consumer_num_bytes,
       MessageInTransitQueue* message_queue,
-      RefPtr<ChannelEndpoint>&& channel_endpoint);
+      util::RefPtr<ChannelEndpoint>&& channel_endpoint);
 
   // Used by |DataPipeProducerDispatcher::Deserialize()|. Returns true on
   // success (in which case, |*data_pipe| is set appropriately) and false on
@@ -90,7 +90,7 @@
   static bool ProducerDeserialize(Channel* channel,
                                   const void* source,
                                   size_t size,
-                                  RefPtr<DataPipe>* data_pipe);
+                                  util::RefPtr<DataPipe>* data_pipe);
 
   // Used by |DataPipeConsumerDispatcher::Deserialize()|. Returns true on
   // success (in which case, |*data_pipe| is set appropriately) and false on
@@ -98,7 +98,7 @@
   static bool ConsumerDeserialize(Channel* channel,
                                   const void* source,
                                   size_t size,
-                                  RefPtr<DataPipe>* data_pipe);
+                                  util::RefPtr<DataPipe>* data_pipe);
 
   // These are called by the producer dispatcher to implement its methods of
   // corresponding names.
@@ -265,7 +265,7 @@
   MSVC_SUPPRESS_WARNING(4324)
   const MojoCreateDataPipeOptions validated_options_;
 
-  mutable Mutex mutex_;
+  mutable util::Mutex mutex_;
   // *Known* state of producer or consumer.
   bool producer_open_ MOJO_GUARDED_BY(mutex_);
   bool consumer_open_ MOJO_GUARDED_BY(mutex_);
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.cc b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
index 74121dc..490b917 100644
--- a/mojo/edk/system/data_pipe_consumer_dispatcher.cc
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
@@ -10,6 +10,9 @@
 #include "mojo/edk/system/data_pipe.h"
 #include "mojo/edk/system/memory.h"
 
+using mojo::util::MutexLocker;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.h b/mojo/edk/system/data_pipe_consumer_dispatcher.h
index 6c7b2ea..9f2389d 100644
--- a/mojo/edk/system/data_pipe_consumer_dispatcher.h
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.h
@@ -6,7 +6,8 @@
 #define MOJO_EDK_SYSTEM_DATA_PIPE_CONSUMER_DISPATCHER_H_
 
 #include "mojo/edk/system/dispatcher.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -19,21 +20,20 @@
 // thread-safe.
 class DataPipeConsumerDispatcher final : public Dispatcher {
  public:
-  static RefPtr<DataPipeConsumerDispatcher> Create() {
+  static util::RefPtr<DataPipeConsumerDispatcher> Create() {
     return AdoptRef(new DataPipeConsumerDispatcher());
   }
 
   // Must be called before any other methods.
-  void Init(RefPtr<DataPipe>&& data_pipe) MOJO_NOT_THREAD_SAFE;
+  void Init(util::RefPtr<DataPipe>&& data_pipe) MOJO_NOT_THREAD_SAFE;
 
   // |Dispatcher| public methods:
   Type GetType() const override;
 
   // The "opposite" of |SerializeAndClose()|. (Typically this is called by
   // |Dispatcher::Deserialize()|.)
-  static RefPtr<DataPipeConsumerDispatcher> Deserialize(Channel* channel,
-                                                        const void* source,
-                                                        size_t size);
+  static util::RefPtr<DataPipeConsumerDispatcher>
+  Deserialize(Channel* channel, const void* source, size_t size);
 
   // Get access to the |DataPipe| for testing.
   DataPipe* GetDataPipeForTest();
@@ -45,7 +45,8 @@
   // |Dispatcher| protected methods:
   void CancelAllAwakablesNoLock() override;
   void CloseImplNoLock() override;
-  RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock() override;
+  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock()
+      override;
   MojoResult ReadDataImplNoLock(UserPointer<void> elements,
                                 UserPointer<uint32_t> num_bytes,
                                 MojoReadDataFlags flags) override;
@@ -73,7 +74,7 @@
   bool IsBusyNoLock() const override;
 
   // This will be null if closed.
-  RefPtr<DataPipe> data_pipe_ MOJO_GUARDED_BY(mutex());
+  util::RefPtr<DataPipe> data_pipe_ MOJO_GUARDED_BY(mutex());
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(DataPipeConsumerDispatcher);
 };
diff --git a/mojo/edk/system/data_pipe_impl.h b/mojo/edk/system/data_pipe_impl.h
index 6dfddc7..87464c8 100644
--- a/mojo/edk/system/data_pipe_impl.h
+++ b/mojo/edk/system/data_pipe_impl.h
@@ -13,7 +13,7 @@
 #include "mojo/edk/system/data_pipe.h"
 #include "mojo/edk/system/handle_signals_state.h"
 #include "mojo/edk/system/memory.h"
-#include "mojo/edk/system/thread_annotations.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/c/system/data_pipe.h"
 #include "mojo/public/c/system/macros.h"
 #include "mojo/public/c/system/types.h"
diff --git a/mojo/edk/system/data_pipe_impl_unittest.cc b/mojo/edk/system/data_pipe_impl_unittest.cc
index cbacf0e..46e6a18 100644
--- a/mojo/edk/system/data_pipe_impl_unittest.cc
+++ b/mojo/edk/system/data_pipe_impl_unittest.cc
@@ -25,13 +25,17 @@
 #include "mojo/edk/system/memory.h"
 #include "mojo/edk/system/message_pipe.h"
 #include "mojo/edk/system/raw_channel.h"
-#include "mojo/edk/system/ref_ptr.h"
-#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/test/sleep.h"
+#include "mojo/edk/system/test/test_io_thread.h"
+#include "mojo/edk/system/test/timeouts.h"
 #include "mojo/edk/system/waiter.h"
-#include "mojo/edk/test/test_io_thread.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using mojo::util::MakeRefCounted;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 namespace {
@@ -223,7 +227,7 @@
 class RemoteDataPipeImplTestHelper : public DataPipeImplTestHelper {
  public:
   RemoteDataPipeImplTestHelper()
-      : io_thread_(mojo::test::TestIOThread::StartMode::AUTO) {}
+      : io_thread_(test::TestIOThread::StartMode::AUTO) {}
   ~RemoteDataPipeImplTestHelper() override {}
 
   void SetUp() override {
@@ -279,7 +283,7 @@
       transport.End();
     }
     uint32_t context = 0;
-    ASSERT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionDeadline(), &context));
+    ASSERT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionTimeout(), &context));
     EXPECT_EQ(987u, context);
     HandleSignalsState hss = HandleSignalsState();
     message_pipe(dest_i)->RemoveAwakable(0, &waiter, &hss);
@@ -299,7 +303,7 @@
     ASSERT_EQ(1u, read_dispatchers.size());
     ASSERT_EQ(1u, read_num_dispatchers);
     ASSERT_TRUE(read_dispatchers[0]);
-    read_dispatchers[0]->AssertHasOneRef();
+    EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
 
     *to_receive = read_dispatchers[0];
   }
@@ -344,7 +348,7 @@
   }
 
   embedder::SimplePlatformSupport platform_support_;
-  mojo::test::TestIOThread io_thread_;
+  test::TestIOThread io_thread_;
   RefPtr<Channel> channels_[2];
   RefPtr<MessagePipe> message_pipes_[2];
 
@@ -372,7 +376,7 @@
     SendDispatcher(0, to_send, &to_receive);
     // |to_send| should have been closed. This is |DCHECK()|ed when it is
     // destroyed.
-    to_send->AssertHasOneRef();
+    EXPECT_TRUE(to_send->HasOneRef());
     to_send = nullptr;
 
     ASSERT_EQ(Dispatcher::Type::DATA_PIPE_PRODUCER, to_receive->GetType());
@@ -421,7 +425,7 @@
     SendDispatcher(0, to_send, &to_receive);
     // |to_send| should have been closed. This is |DCHECK()|ed when it is
     // destroyed.
-    to_send->AssertHasOneRef();
+    EXPECT_TRUE(to_send->HasOneRef());
     to_send = nullptr;
 
     ASSERT_EQ(Dispatcher::Type::DATA_PIPE_CONSUMER, to_receive->GetType());
@@ -475,7 +479,7 @@
     SendDispatcher(0, to_send, &to_receive);
     // |to_send| should have been closed. This is |DCHECK()|ed when it is
     // destroyed.
-    to_send->AssertHasOneRef();
+    EXPECT_TRUE(to_send->HasOneRef());
     to_send = nullptr;
     ASSERT_EQ(Dispatcher::Type::DATA_PIPE_PRODUCER, to_receive->GetType());
     to_send = RefPtr<DataPipeProducerDispatcher>(
@@ -486,7 +490,7 @@
     SendDispatcher(1, to_send, &to_receive);
     // |producer_dispatcher_| should have been closed. This is |DCHECK()|ed when
     // it is destroyed.
-    to_send->AssertHasOneRef();
+    EXPECT_TRUE(to_send->HasOneRef());
     to_send = nullptr;
 
     ASSERT_EQ(Dispatcher::Type::DATA_PIPE_PRODUCER, to_receive->GetType());
@@ -522,7 +526,7 @@
     SendDispatcher(0, to_send, &to_receive);
     // |to_send| should have been closed. This is |DCHECK()|ed when it is
     // destroyed.
-    to_send->AssertHasOneRef();
+    EXPECT_TRUE(to_send->HasOneRef());
     to_send = nullptr;
     ASSERT_EQ(Dispatcher::Type::DATA_PIPE_CONSUMER, to_receive->GetType());
     to_send = RefPtr<DataPipeConsumerDispatcher>(
@@ -533,7 +537,7 @@
     SendDispatcher(1, to_send, &to_receive);
     // |consumer_dispatcher_| should have been closed. This is |DCHECK()|ed when
     // it is destroyed.
-    to_send->AssertHasOneRef();
+    EXPECT_TRUE(to_send->HasOneRef());
     to_send = nullptr;
 
     ASSERT_EQ(Dispatcher::Type::DATA_PIPE_CONSUMER, to_receive->GetType());
@@ -652,7 +656,7 @@
 
   // Wait.
   context = 0;
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionDeadline(), &context));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionTimeout(), &context));
   EXPECT_EQ(123u, context);
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
@@ -805,7 +809,7 @@
 
   // Wait for data to become available to the consumer.
   context = 0;
-  EXPECT_EQ(MOJO_RESULT_OK, cwaiter.Wait(test::TinyDeadline(), &context));
+  EXPECT_EQ(MOJO_RESULT_OK, cwaiter.Wait(test::TinyTimeout(), &context));
   EXPECT_EQ(1234u, context);
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&cwaiter, &hss);
@@ -856,7 +860,7 @@
 
   // Waiting should now succeed.
   context = 0;
-  EXPECT_EQ(MOJO_RESULT_OK, pwaiter.Wait(test::TinyDeadline(), &context));
+  EXPECT_EQ(MOJO_RESULT_OK, pwaiter.Wait(test::TinyTimeout(), &context));
   EXPECT_EQ(78u, context);
   hss = HandleSignalsState();
   this->ProducerRemoveAwakable(&pwaiter, &hss);
@@ -900,7 +904,7 @@
 
   // Waiting should succeed.
   context = 0;
-  EXPECT_EQ(MOJO_RESULT_OK, pwaiter.Wait(test::TinyDeadline(), &context));
+  EXPECT_EQ(MOJO_RESULT_OK, pwaiter.Wait(test::TinyTimeout(), &context));
   EXPECT_EQ(90u, context);
   hss = HandleSignalsState();
   this->ProducerRemoveAwakable(&pwaiter, &hss);
@@ -928,7 +932,7 @@
   // It should now be never-writable.
   context = 0;
   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            pwaiter.Wait(test::TinyDeadline(), &context));
+            pwaiter.Wait(test::TinyTimeout(), &context));
   EXPECT_EQ(12u, context);
   hss = HandleSignalsState();
   this->ProducerRemoveAwakable(&pwaiter, &hss);
@@ -963,7 +967,7 @@
 
   // It should be signaled.
   context = 0;
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), &context));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyTimeout(), &context));
   EXPECT_EQ(12u, context);
   hss = HandleSignalsState();
   this->ProducerRemoveAwakable(&waiter, &hss);
@@ -998,7 +1002,7 @@
 
   // It should be signaled.
   context = 0;
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), &context));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyTimeout(), &context));
   EXPECT_EQ(12u, context);
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
@@ -1048,7 +1052,7 @@
 
   // Wait for readability (needed for remote cases).
   context = 0;
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), &context));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyTimeout(), &context));
   EXPECT_EQ(34u, context);
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
@@ -1120,7 +1124,7 @@
 
   // Waiting should now succeed.
   context = 0;
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), &context));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyTimeout(), &context));
   EXPECT_EQ(90u, context);
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
@@ -1152,7 +1156,7 @@
 
   // Wait for the peer closed signal.
   context = 0;
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), &context));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyTimeout(), &context));
   EXPECT_EQ(12u, context);
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
@@ -1223,7 +1227,7 @@
 
   // Wait for readability (needed for remote cases).
   context = 0;
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), &context));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyTimeout(), &context));
   EXPECT_EQ(12u, context);
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
@@ -1280,7 +1284,7 @@
   // Should be never-readable.
   context = 0;
   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            waiter.Wait(test::TinyDeadline(), &context));
+            waiter.Wait(test::TinyTimeout(), &context));
   EXPECT_EQ(56u, context);
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
@@ -1356,7 +1360,7 @@
             hss.satisfiable_signals);
 
   // It should become readable.
-  EXPECT_EQ(MOJO_RESULT_OK, cwaiter.Wait(test::TinyDeadline(), nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, cwaiter.Wait(test::TinyTimeout(), nullptr));
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&cwaiter, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
@@ -1485,7 +1489,7 @@
   // available at once (except that in current implementations, with reasonable
   // limits, it will). Eventually, we'll be able to wait for a specified amount
   // of data to become available.
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyTimeout(), nullptr));
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
@@ -1545,7 +1549,7 @@
     if (num_bytes >= 10u * sizeof(int32_t))
       break;
 
-    test::Sleep(test::EpsilonDeadline());
+    test::Sleep(test::EpsilonTimeout());
   }
   EXPECT_EQ(10u * sizeof(int32_t), num_bytes);
 
@@ -1596,7 +1600,7 @@
   this->ProducerClose();
 
   // Wait.
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyTimeout(), nullptr));
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
@@ -1686,7 +1690,7 @@
 
   // Wait for data.
   // TODO(vtl): (See corresponding TODO in AllOrNone.)
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyTimeout(), nullptr));
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
@@ -1732,7 +1736,7 @@
       EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, result);
     }
 
-    test::Sleep(test::EpsilonDeadline());
+    test::Sleep(test::EpsilonTimeout());
   }
   EXPECT_EQ(90u, total_num_bytes);
 
@@ -1745,7 +1749,7 @@
     if (num_bytes >= 100u)
       break;
 
-    test::Sleep(test::EpsilonDeadline());
+    test::Sleep(test::EpsilonTimeout());
   }
   EXPECT_EQ(100u, num_bytes);
 
@@ -1823,7 +1827,7 @@
     if (num_bytes >= 2u * kTestDataSize)
       break;
 
-    test::Sleep(test::EpsilonDeadline());
+    test::Sleep(test::EpsilonTimeout());
   }
   EXPECT_EQ(2u * kTestDataSize, num_bytes);
 
@@ -1898,7 +1902,7 @@
 
   // Wait for data.
   // TODO(vtl): (See corresponding TODO in AllOrNone.)
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyTimeout(), nullptr));
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
@@ -1924,7 +1928,7 @@
   this->ConsumerClose();
 
   // Wait for producer to know that the consumer is closed.
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyTimeout(), nullptr));
   hss = HandleSignalsState();
   this->ProducerRemoveAwakable(&waiter, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
@@ -2016,7 +2020,7 @@
 
   // Wait. (Note that once the consumer knows that the producer is closed, it
   // must also know about all the data that was sent.)
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyTimeout(), nullptr));
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
@@ -2095,7 +2099,7 @@
   EXPECT_EQ(kTestDataSize, num_bytes);
 
   // Wait.
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyTimeout(), nullptr));
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
@@ -2122,7 +2126,7 @@
 
   // Wait.
   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            waiter.Wait(test::TinyDeadline(), nullptr));
+            waiter.Wait(test::TinyTimeout(), nullptr));
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
@@ -2164,7 +2168,7 @@
   EXPECT_EQ(kTestDataSize, num_bytes);
 
   // Wait.
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyTimeout(), nullptr));
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
@@ -2190,7 +2194,7 @@
   this->ProducerClose();
 
   // Wait for producer close to be detected.
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyTimeout(), nullptr));
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
@@ -2208,7 +2212,7 @@
 
   // Wait.
   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            waiter.Wait(test::TinyDeadline(), nullptr));
+            waiter.Wait(test::TinyTimeout(), nullptr));
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
@@ -2260,7 +2264,7 @@
   // Note: If we didn't wait for the consumer close to be detected before
   // completing the two-phase write, wait might succeed (in the remote cases).
   // This is because the first |Awake()| "wins".
-  EXPECT_EQ(MOJO_RESULT_OK, waiter1.Wait(test::TinyDeadline(), nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter1.Wait(test::TinyTimeout(), nullptr));
   hss = HandleSignalsState();
   this->ProducerRemoveAwakable(&waiter1, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
@@ -2271,7 +2275,7 @@
 
   // Wait.
   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            waiter2.Wait(test::TinyDeadline(), nullptr));
+            waiter2.Wait(test::TinyTimeout(), nullptr));
   hss = HandleSignalsState();
   this->ProducerRemoveAwakable(&waiter2, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
@@ -2307,7 +2311,7 @@
 
   // Wait a bit, to make sure that if a signal were (incorrectly) sent, it'd
   // have time to propagate.
-  test::Sleep(test::EpsilonDeadline());
+  test::Sleep(test::EpsilonTimeout());
 
   // Still no data.
   num_bytes = 1000u;
@@ -2329,7 +2333,7 @@
   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, this->ProducerEndWriteData(0u));
 
   // Wait a bit (as above).
-  test::Sleep(test::EpsilonDeadline());
+  test::Sleep(test::EpsilonTimeout());
 
   // Still no data.
   num_bytes = 1000u;
@@ -2351,7 +2355,7 @@
   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, this->ProducerEndWriteData(0u));
 
   // Wait a bit (as above).
-  test::Sleep(test::EpsilonDeadline());
+  test::Sleep(test::EpsilonTimeout());
 
   // Still no data.
   num_bytes = 1000u;
@@ -2374,7 +2378,7 @@
 
   // Wait for data.
   // TODO(vtl): (See corresponding TODO in AllOrNone.)
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyTimeout(), nullptr));
   hss = HandleSignalsState();
   this->ConsumerRemoveAwakable(&waiter, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
@@ -2467,7 +2471,7 @@
     if (num_bytes >= kTestDataSize)
       break;
 
-    test::Sleep(test::EpsilonDeadline());
+    test::Sleep(test::EpsilonTimeout());
   }
   EXPECT_EQ(kTestDataSize, num_bytes);
 
diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.cc b/mojo/edk/system/data_pipe_producer_dispatcher.cc
index 55ee735..814fa59 100644
--- a/mojo/edk/system/data_pipe_producer_dispatcher.cc
+++ b/mojo/edk/system/data_pipe_producer_dispatcher.cc
@@ -10,6 +10,9 @@
 #include "mojo/edk/system/data_pipe.h"
 #include "mojo/edk/system/memory.h"
 
+using mojo::util::MutexLocker;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.h b/mojo/edk/system/data_pipe_producer_dispatcher.h
index 77c09aa..9251ce1 100644
--- a/mojo/edk/system/data_pipe_producer_dispatcher.h
+++ b/mojo/edk/system/data_pipe_producer_dispatcher.h
@@ -6,7 +6,8 @@
 #define MOJO_EDK_SYSTEM_DATA_PIPE_PRODUCER_DISPATCHER_H_
 
 #include "mojo/edk/system/dispatcher.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -19,21 +20,20 @@
 // thread-safe.
 class DataPipeProducerDispatcher final : public Dispatcher {
  public:
-  static RefPtr<DataPipeProducerDispatcher> Create() {
+  static util::RefPtr<DataPipeProducerDispatcher> Create() {
     return AdoptRef(new DataPipeProducerDispatcher());
   }
 
   // Must be called before any other methods.
-  void Init(RefPtr<DataPipe>&& data_pipe) MOJO_NOT_THREAD_SAFE;
+  void Init(util::RefPtr<DataPipe>&& data_pipe) MOJO_NOT_THREAD_SAFE;
 
   // |Dispatcher| public methods:
   Type GetType() const override;
 
   // The "opposite" of |SerializeAndClose()|. (Typically this is called by
   // |Dispatcher::Deserialize()|.)
-  static RefPtr<DataPipeProducerDispatcher> Deserialize(Channel* channel,
-                                                        const void* source,
-                                                        size_t size);
+  static util::RefPtr<DataPipeProducerDispatcher>
+  Deserialize(Channel* channel, const void* source, size_t size);
 
   // Get access to the |DataPipe| for testing.
   DataPipe* GetDataPipeForTest();
@@ -45,7 +45,8 @@
   // |Dispatcher| protected methods:
   void CancelAllAwakablesNoLock() override;
   void CloseImplNoLock() override;
-  RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock() override;
+  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock()
+      override;
   MojoResult WriteDataImplNoLock(UserPointer<const void> elements,
                                  UserPointer<uint32_t> num_bytes,
                                  MojoWriteDataFlags flags) override;
@@ -73,7 +74,7 @@
   bool IsBusyNoLock() const override;
 
   // This will be null if closed.
-  RefPtr<DataPipe> data_pipe_ MOJO_GUARDED_BY(mutex());
+  util::RefPtr<DataPipe> data_pipe_ MOJO_GUARDED_BY(mutex());
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(DataPipeProducerDispatcher);
 };
diff --git a/mojo/edk/system/dispatcher.cc b/mojo/edk/system/dispatcher.cc
index a39647d..ea88870 100644
--- a/mojo/edk/system/dispatcher.cc
+++ b/mojo/edk/system/dispatcher.cc
@@ -12,6 +12,9 @@
 #include "mojo/edk/system/platform_handle_dispatcher.h"
 #include "mojo/edk/system/shared_buffer_dispatcher.h"
 
+using mojo::util::MutexLocker;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/dispatcher.h b/mojo/edk/system/dispatcher.h
index 9b8dafd..e425bb3 100644
--- a/mojo/edk/system/dispatcher.h
+++ b/mojo/edk/system/dispatcher.h
@@ -15,9 +15,10 @@
 #include "mojo/edk/embedder/platform_handle_vector.h"
 #include "mojo/edk/system/handle_signals_state.h"
 #include "mojo/edk/system/memory.h"
-#include "mojo/edk/system/mutex.h"
-#include "mojo/edk/system/ref_counted.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/edk/util/ref_counted.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/c/system/buffer.h"
 #include "mojo/public/c/system/data_pipe.h"
 #include "mojo/public/c/system/message_pipe.h"
@@ -42,7 +43,7 @@
 class TransportData;
 class Awakable;
 
-using DispatcherVector = std::vector<RefPtr<Dispatcher>>;
+using DispatcherVector = std::vector<util::RefPtr<Dispatcher>>;
 
 namespace test {
 
@@ -56,7 +57,7 @@
 // object is thread-safe, with its state being protected by a single mutex
 // |mutex_|, which is also made available to implementation subclasses (via the
 // |mutex()| method).
-class Dispatcher : public RefCountedThreadSafe<Dispatcher> {
+class Dispatcher : public util::RefCountedThreadSafe<Dispatcher> {
  public:
   enum class Type {
     UNKNOWN = 0,
@@ -114,7 +115,7 @@
   // new handle on success).
   MojoResult DuplicateBufferHandle(
       UserPointer<const MojoDuplicateBufferHandleOptions> options,
-      RefPtr<Dispatcher>* new_dispatcher);
+      util::RefPtr<Dispatcher>* new_dispatcher);
   MojoResult MapBuffer(
       uint64_t offset,
       uint64_t num_bytes,
@@ -204,7 +205,7 @@
     // Deserialization API.
     // Note: This "clears" (i.e., reset to the invalid handle) any platform
     // handles that it takes ownership of.
-    static RefPtr<Dispatcher> Deserialize(
+    static util::RefPtr<Dispatcher> Deserialize(
         Channel* channel,
         int32_t type,
         const void* source,
@@ -222,7 +223,8 @@
   virtual void CancelAllAwakablesNoLock() MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
   virtual void CloseImplNoLock() MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
 
-  virtual RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock()
+  virtual util::RefPtr<Dispatcher>
+  CreateEquivalentDispatcherAndCloseImplNoLock()
       MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_) = 0;
 
   // These are to be overridden by subclasses (if necessary). They are never
@@ -261,7 +263,8 @@
       MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
   virtual MojoResult DuplicateBufferHandleImplNoLock(
       UserPointer<const MojoDuplicateBufferHandleOptions> options,
-      RefPtr<Dispatcher>* new_dispatcher) MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+      util::RefPtr<Dispatcher>* new_dispatcher)
+      MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
   virtual MojoResult MapBufferImplNoLock(
       uint64_t offset,
       uint64_t num_bytes,
@@ -308,7 +311,7 @@
   // handle from being sent over a message pipe (with status "busy").
   virtual bool IsBusyNoLock() const MOJO_SHARED_LOCKS_REQUIRED(mutex_);
 
-  Mutex& mutex() const MOJO_LOCK_RETURNED(mutex_) { return mutex_; }
+  util::Mutex& mutex() const MOJO_LOCK_RETURNED(mutex_) { return mutex_; }
 
  private:
   FRIEND_REF_COUNTED_THREAD_SAFE(Dispatcher);
@@ -325,7 +328,7 @@
   // dispatcher will look as though it was closed, but the resource it
   // represents will be assigned to the new dispatcher. This must be called
   // under the dispatcher's lock.
-  RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseNoLock()
+  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseNoLock()
       MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
 
   // API to serialize dispatchers to a |Channel|, exposed to only
@@ -373,7 +376,7 @@
 
   // This protects the following members as well as any state added by
   // subclasses.
-  mutable Mutex mutex_;
+  mutable util::Mutex mutex_;
   bool is_closed_ MOJO_GUARDED_BY(mutex_);
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(Dispatcher);
@@ -396,7 +399,8 @@
     return dispatcher_->IsBusyNoLock();
   }
   void Close() MOJO_NOT_THREAD_SAFE { dispatcher_->CloseNoLock(); }
-  RefPtr<Dispatcher> CreateEquivalentDispatcherAndClose() MOJO_NOT_THREAD_SAFE {
+  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndClose()
+      MOJO_NOT_THREAD_SAFE {
     return dispatcher_->CreateEquivalentDispatcherAndCloseNoLock();
   }
 
diff --git a/mojo/edk/system/dispatcher_unittest.cc b/mojo/edk/system/dispatcher_unittest.cc
index 827b1f4..43a8376 100644
--- a/mojo/edk/system/dispatcher_unittest.cc
+++ b/mojo/edk/system/dispatcher_unittest.cc
@@ -7,16 +7,20 @@
 #include <memory>
 #include <vector>
 
-#include "base/synchronization/waitable_event.h"
+#include "base/logging.h"
 #include "mojo/edk/embedder/platform_shared_buffer.h"
 #include "mojo/edk/system/memory.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/system/test/simple_test_thread.h"
+#include "mojo/edk/system/waitable_event.h"
 #include "mojo/edk/system/waiter.h"
-#include "mojo/edk/test/simple_test_thread.h"
 #include "mojo/edk/util/make_unique.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using mojo::util::MakeRefCounted;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 namespace {
@@ -117,7 +121,7 @@
   EXPECT_EQ(0u, hss.satisfiable_signals);
 }
 
-class ThreadSafetyStressThread : public mojo::test::SimpleTestThread {
+class ThreadSafetyStressThread : public test::SimpleTestThread {
  public:
   enum DispatcherOp {
     CLOSE = 0,
@@ -136,7 +140,7 @@
     DISPATCHER_OP_COUNT
   };
 
-  ThreadSafetyStressThread(base::WaitableEvent* event,
+  ThreadSafetyStressThread(ManualResetWaitableEvent* event,
                            RefPtr<Dispatcher> dispatcher,
                            DispatcherOp op)
       : event_(event), dispatcher_(dispatcher), op_(op) {
@@ -240,7 +244,7 @@
     EXPECT_EQ(0u, hss.satisfiable_signals);
   }
 
-  base::WaitableEvent* const event_;
+  ManualResetWaitableEvent* const event_;
   const RefPtr<Dispatcher> dispatcher_;
   const DispatcherOp op_;
 
@@ -254,8 +258,8 @@
   static const size_t kNumThreads = 100;
 
   for (size_t i = 0; i < kRepeatCount; i++) {
-    // Manual reset, not initially signalled.
-    base::WaitableEvent event(true, false);
+    // Manual reset, not initially signaled.
+    ManualResetWaitableEvent event;
     auto d = MakeRefCounted<TrivialDispatcher>();
 
     {
@@ -282,8 +286,8 @@
   static const size_t kNumThreads = 100;
 
   for (size_t i = 0; i < kRepeatCount; i++) {
-    // Manual reset, not initially signalled.
-    base::WaitableEvent event(true, false);
+    // Manual reset, not initially signaled.
+    ManualResetWaitableEvent event;
     auto d = MakeRefCounted<TrivialDispatcher>();
 
     {
diff --git a/mojo/edk/system/endpoint_relayer.cc b/mojo/edk/system/endpoint_relayer.cc
index 0cf4efa..3d7b8d1 100644
--- a/mojo/edk/system/endpoint_relayer.cc
+++ b/mojo/edk/system/endpoint_relayer.cc
@@ -10,6 +10,9 @@
 #include "mojo/edk/system/channel_endpoint.h"
 #include "mojo/edk/system/message_in_transit.h"
 
+using mojo::util::MutexLocker;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/endpoint_relayer.h b/mojo/edk/system/endpoint_relayer.h
index f41de96..bb06894 100644
--- a/mojo/edk/system/endpoint_relayer.h
+++ b/mojo/edk/system/endpoint_relayer.h
@@ -8,8 +8,9 @@
 #include <memory>
 
 #include "mojo/edk/system/channel_endpoint_client.h"
-#include "mojo/edk/system/mutex.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -61,14 +62,14 @@
     MOJO_DISALLOW_COPY_AND_ASSIGN(Filter);
   };
 
-  // Note: Use |MakeRefCounted<EndpointRelayer>()|.
+  // Note: Use |util::MakeRefCounted<EndpointRelayer>()|.
 
   // Gets the other port number (i.e., 0 -> 1, 1 -> 0).
   static unsigned GetPeerPort(unsigned port);
 
   // Initialize this object. This must be called before any other method.
-  void Init(RefPtr<ChannelEndpoint>&& endpoint0,
-            RefPtr<ChannelEndpoint>&& endpoint1) MOJO_NOT_THREAD_SAFE;
+  void Init(util::RefPtr<ChannelEndpoint>&& endpoint0,
+            util::RefPtr<ChannelEndpoint>&& endpoint1) MOJO_NOT_THREAD_SAFE;
 
   // Sets (or resets) the filter, which can (optionally) handle/filter
   // |Type::ENDPOINT_CLIENT| messages (see |Filter| above).
@@ -84,8 +85,8 @@
   EndpointRelayer();
   ~EndpointRelayer() override;
 
-  Mutex mutex_;
-  RefPtr<ChannelEndpoint> endpoints_[2] MOJO_GUARDED_BY(mutex_);
+  util::Mutex mutex_;
+  util::RefPtr<ChannelEndpoint> endpoints_[2] MOJO_GUARDED_BY(mutex_);
   std::unique_ptr<Filter> filter_ MOJO_GUARDED_BY(mutex_);
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(EndpointRelayer);
diff --git a/mojo/edk/system/endpoint_relayer_unittest.cc b/mojo/edk/system/endpoint_relayer_unittest.cc
index 1b8b440..3ebdaf3 100644
--- a/mojo/edk/system/endpoint_relayer_unittest.cc
+++ b/mojo/edk/system/endpoint_relayer_unittest.cc
@@ -5,17 +5,20 @@
 #include "mojo/edk/system/endpoint_relayer.h"
 
 #include "base/logging.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/test/test_timeouts.h"
 #include "mojo/edk/system/channel_endpoint_id.h"
 #include "mojo/edk/system/channel_test_base.h"
 #include "mojo/edk/system/message_in_transit_queue.h"
 #include "mojo/edk/system/message_in_transit_test_utils.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/system/test/timeouts.h"
 #include "mojo/edk/system/test_channel_endpoint_client.h"
+#include "mojo/edk/system/waitable_event.h"
 #include "mojo/edk/util/make_unique.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/cpp/system/macros.h"
 
+using mojo::util::MakeRefCounted;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 namespace {
@@ -90,13 +93,13 @@
 };
 
 TEST_F(EndpointRelayerTest, Basic) {
-  base::WaitableEvent read_event(true, false);
+  ManualResetWaitableEvent read_event;
   client1b()->SetReadEvent(&read_event);
   EXPECT_EQ(0u, client1b()->NumMessages());
 
   EXPECT_TRUE(endpoint1a()->EnqueueMessage(test::MakeTestMessage(12345)));
 
-  EXPECT_TRUE(read_event.TimedWait(TestTimeouts::tiny_timeout()));
+  EXPECT_FALSE(read_event.WaitWithTimeout(test::TinyTimeout()));
   client1b()->SetReadEvent(nullptr);
 
   ASSERT_EQ(1u, client1b()->NumMessages());
@@ -111,7 +114,7 @@
 
   EXPECT_TRUE(endpoint1b()->EnqueueMessage(test::MakeTestMessage(67890)));
 
-  EXPECT_TRUE(read_event.TimedWait(TestTimeouts::tiny_timeout()));
+  EXPECT_FALSE(read_event.WaitWithTimeout(test::TinyTimeout()));
   client1a()->SetReadEvent(nullptr);
 
   ASSERT_EQ(1u, client1a()->NumMessages());
@@ -127,10 +130,10 @@
   EXPECT_TRUE(endpoint1a()->EnqueueMessage(test::MakeTestMessage(4)));
   EXPECT_TRUE(endpoint1a()->EnqueueMessage(test::MakeTestMessage(5)));
 
-  base::WaitableEvent read_event(true, false);
+  ManualResetWaitableEvent read_event;
   client1b()->SetReadEvent(&read_event);
   for (size_t i = 0; client1b()->NumMessages() < 5 && i < 5; i++) {
-    EXPECT_TRUE(read_event.TimedWait(TestTimeouts::tiny_timeout()));
+    EXPECT_FALSE(read_event.WaitWithTimeout(test::TinyTimeout()));
     read_event.Reset();
   }
   client1b()->SetReadEvent(nullptr);
@@ -198,10 +201,10 @@
   EXPECT_TRUE(endpoint1a()->EnqueueMessage(test::MakeTestMessage(1003)));
   EXPECT_TRUE(endpoint1a()->EnqueueMessage(test::MakeTestMessage(5)));
 
-  base::WaitableEvent read_event(true, false);
+  ManualResetWaitableEvent read_event;
   client1b()->SetReadEvent(&read_event);
   for (size_t i = 0; client1b()->NumMessages() < 5 && i < 5; i++) {
-    EXPECT_TRUE(read_event.TimedWait(TestTimeouts::tiny_timeout()));
+    EXPECT_FALSE(read_event.WaitWithTimeout(test::TinyTimeout()));
     read_event.Reset();
   }
   client1b()->SetReadEvent(nullptr);
diff --git a/mojo/edk/system/handle_table.cc b/mojo/edk/system/handle_table.cc
index 19d0763..19d8087 100644
--- a/mojo/edk/system/handle_table.cc
+++ b/mojo/edk/system/handle_table.cc
@@ -11,6 +11,8 @@
 #include "mojo/edk/system/configuration.h"
 #include "mojo/edk/system/dispatcher.h"
 
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/handle_table.h b/mojo/edk/system/handle_table.h
index 91d3669..26c9c99 100644
--- a/mojo/edk/system/handle_table.h
+++ b/mojo/edk/system/handle_table.h
@@ -9,7 +9,7 @@
 #include <utility>
 #include <vector>
 
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/c/system/types.h"
 #include "mojo/public/cpp/system/macros.h"
 
@@ -20,7 +20,7 @@
 class Dispatcher;
 class DispatcherTransport;
 
-using DispatcherVector = std::vector<RefPtr<Dispatcher>>;
+using DispatcherVector = std::vector<util::RefPtr<Dispatcher>>;
 
 // Test-only function (defined/used in embedder/test_embedder.cc). Declared here
 // so it can be friended.
@@ -46,7 +46,7 @@
   // handle.
   // WARNING: For efficiency, this returns a dumb pointer. If you're going to
   // use the result outside |Core|'s lock, you MUST take a reference (e.g., by
-  // storing the result inside a |RefPtr|).
+  // storing the result inside a |util::RefPtr|).
   Dispatcher* GetDispatcher(MojoHandle handle);
 
   // On success, gets the dispatcher for a given handle (which should not be
@@ -55,7 +55,7 @@
   // |MOJO_RESULT_INVALID_ARGUMENT| if there's no dispatcher for the given
   // handle or |MOJO_RESULT_BUSY| if the handle is marked as busy.)
   MojoResult GetAndRemoveDispatcher(MojoHandle handle,
-                                    RefPtr<Dispatcher>* dispatcher);
+                                    util::RefPtr<Dispatcher>* dispatcher);
 
   // Adds a dispatcher (which must be valid), returning the handle for it.
   // Returns |MOJO_HANDLE_INVALID| on failure (if the handle table is full).
@@ -118,16 +118,16 @@
   // closed (or learning about this too late).
   struct Entry {
     Entry();
-    explicit Entry(RefPtr<Dispatcher>&& dispatcher);
+    explicit Entry(util::RefPtr<Dispatcher>&& dispatcher);
     ~Entry();
 
-    RefPtr<Dispatcher> dispatcher;
+    util::RefPtr<Dispatcher> dispatcher;
     bool busy;
   };
   using HandleToEntryMap = std::unordered_map<MojoHandle, Entry>;
 
   // Adds the given dispatcher to the handle table, not doing any size checks.
-  MojoHandle AddDispatcherNoSizeCheck(RefPtr<Dispatcher>&& dispatcher);
+  MojoHandle AddDispatcherNoSizeCheck(util::RefPtr<Dispatcher>&& dispatcher);
 
   HandleToEntryMap handle_to_entry_map_;
   MojoHandle next_handle_;  // Invariant: never |MOJO_HANDLE_INVALID|.
diff --git a/mojo/edk/system/incoming_endpoint.cc b/mojo/edk/system/incoming_endpoint.cc
index 4fd8789..d1d5715 100644
--- a/mojo/edk/system/incoming_endpoint.cc
+++ b/mojo/edk/system/incoming_endpoint.cc
@@ -13,6 +13,10 @@
 #include "mojo/edk/system/message_pipe.h"
 #include "mojo/edk/system/remote_producer_data_pipe_impl.h"
 
+using mojo::util::MakeRefCounted;
+using mojo::util::MutexLocker;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/incoming_endpoint.h b/mojo/edk/system/incoming_endpoint.h
index 6dbf9a0..1ad708e 100644
--- a/mojo/edk/system/incoming_endpoint.h
+++ b/mojo/edk/system/incoming_endpoint.h
@@ -9,8 +9,9 @@
 
 #include "mojo/edk/system/channel_endpoint_client.h"
 #include "mojo/edk/system/message_in_transit_queue.h"
-#include "mojo/edk/system/mutex.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/cpp/system/macros.h"
 
 struct MojoCreateDataPipeOptions;
@@ -27,16 +28,16 @@
 // |MessagePipe|s or |DataPipe|s.
 class IncomingEndpoint final : public ChannelEndpointClient {
  public:
-  // Note: Use |MakeRefCounted<IncomingEndpoint>()|.
+  // Note: Use |util::MakeRefCounted<IncomingEndpoint>()|.
 
   // Must be called before any other method.
-  RefPtr<ChannelEndpoint> Init() MOJO_NOT_THREAD_SAFE;
+  util::RefPtr<ChannelEndpoint> Init() MOJO_NOT_THREAD_SAFE;
 
-  RefPtr<MessagePipe> ConvertToMessagePipe();
-  RefPtr<DataPipe> ConvertToDataPipeProducer(
+  util::RefPtr<MessagePipe> ConvertToMessagePipe();
+  util::RefPtr<DataPipe> ConvertToDataPipeProducer(
       const MojoCreateDataPipeOptions& validated_options,
       size_t consumer_num_bytes);
-  RefPtr<DataPipe> ConvertToDataPipeConsumer(
+  util::RefPtr<DataPipe> ConvertToDataPipeConsumer(
       const MojoCreateDataPipeOptions& validated_options);
 
   // Must be called before destroying this object if |ConvertToMessagePipe()|
@@ -53,8 +54,8 @@
   IncomingEndpoint();
   ~IncomingEndpoint() override;
 
-  Mutex mutex_;
-  RefPtr<ChannelEndpoint> endpoint_ MOJO_GUARDED_BY(mutex_);
+  util::Mutex mutex_;
+  util::RefPtr<ChannelEndpoint> endpoint_ MOJO_GUARDED_BY(mutex_);
   MessageInTransitQueue message_queue_ MOJO_GUARDED_BY(mutex_);
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(IncomingEndpoint);
diff --git a/mojo/edk/system/ipc_support.cc b/mojo/edk/system/ipc_support.cc
index fbd8136..c3778de 100644
--- a/mojo/edk/system/ipc_support.cc
+++ b/mojo/edk/system/ipc_support.cc
@@ -14,6 +14,8 @@
 #include "mojo/edk/system/message_pipe_dispatcher.h"
 #include "mojo/edk/system/slave_connection_manager.h"
 
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/ipc_support.h b/mojo/edk/system/ipc_support.h
index ed5ab02..26b70e5 100644
--- a/mojo/edk/system/ipc_support.h
+++ b/mojo/edk/system/ipc_support.h
@@ -9,7 +9,6 @@
 
 #include "base/callback_forward.h"
 #include "base/gtest_prod_util.h"
-#include "base/memory/ref_counted.h"
 #include "mojo/edk/embedder/platform_task_runner.h"
 #include "mojo/edk/embedder/process_type.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
@@ -17,7 +16,7 @@
 #include "mojo/edk/system/channel_id.h"
 #include "mojo/edk/system/connection_identifier.h"
 #include "mojo/edk/system/process_identifier.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -100,7 +99,7 @@
   //
   // TODO(vtl): Add some more channel management functionality to this class.
   // Maybe make this callback interface more sane.
-  RefPtr<MessagePipeDispatcher> ConnectToSlave(
+  util::RefPtr<MessagePipeDispatcher> ConnectToSlave(
       const ConnectionIdentifier& connection_id,
       embedder::SlaveInfo slave_info,
       embedder::ScopedPlatformHandle platform_handle,
@@ -116,7 +115,7 @@
   // |ConnectToSlave()|.
   //
   // TODO(vtl): |ConnectToSlave()|'s channel management TODO also applies here.
-  RefPtr<MessagePipeDispatcher> ConnectToMaster(
+  util::RefPtr<MessagePipeDispatcher> ConnectToMaster(
       const ConnectionIdentifier& connection_id,
       const base::Closure& callback,
       embedder::PlatformTaskRunnerRefPtr callback_thread_task_runner,
diff --git a/mojo/edk/system/ipc_support_unittest.cc b/mojo/edk/system/ipc_support_unittest.cc
index 2fb3418..5c8316e 100644
--- a/mojo/edk/system/ipc_support_unittest.cc
+++ b/mojo/edk/system/ipc_support_unittest.cc
@@ -9,10 +9,7 @@
 #include <vector>
 
 #include "base/bind.h"
-#include "base/command_line.h"
 #include "base/logging.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/test/test_timeouts.h"
 #include "mojo/edk/embedder/master_process_delegate.h"
 #include "mojo/edk/embedder/platform_channel_pair.h"
 #include "mojo/edk/embedder/simple_platform_support.h"
@@ -23,18 +20,20 @@
 #include "mojo/edk/system/message_pipe.h"
 #include "mojo/edk/system/message_pipe_dispatcher.h"
 #include "mojo/edk/system/process_identifier.h"
-#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/test/test_command_line.h"
+#include "mojo/edk/system/test/test_io_thread.h"
+#include "mojo/edk/system/test/timeouts.h"
+#include "mojo/edk/system/waitable_event.h"
 #include "mojo/edk/system/waiter.h"
 #include "mojo/edk/test/multiprocess_test_helper.h"
-#include "mojo/edk/test/test_io_thread.h"
 #include "mojo/edk/test/test_utils.h"
+#include "mojo/edk/util/command_line.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using mojo::util::RefPtr;
+
 namespace mojo {
-
-using test::TestIOThread;
-
 namespace system {
 namespace {
 
@@ -58,7 +57,7 @@
                                    MOJO_WRITE_MESSAGE_FLAG_NONE));
 
   // Wait for it to arrive.
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionDeadline(), nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionTimeout(), nullptr));
   read_mp->RemoveAwakable(&waiter, nullptr);
 
   // Read the message from the read end.
@@ -102,7 +101,7 @@
   mp_to_send = nullptr;
 
   // Wait for it to arrive.
-  CHECK_EQ(waiter.Wait(test::ActionDeadline(), nullptr), MOJO_RESULT_OK);
+  CHECK_EQ(waiter.Wait(test::ActionTimeout(), nullptr), MOJO_RESULT_OK);
   read_mp->RemoveAwakable(&waiter, nullptr);
 
   // Read the message from the read end.
@@ -121,14 +120,13 @@
 
 class TestMasterProcessDelegate : public embedder::MasterProcessDelegate {
  public:
-  TestMasterProcessDelegate()
-      : on_slave_disconnect_event_(false, false) {}  // Auto reset.
+  TestMasterProcessDelegate() {}
   ~TestMasterProcessDelegate() override {}
 
   // Warning: There's only one slave disconnect event (which resets
   // automatically).
   bool TryWaitForOnSlaveDisconnect() {
-    return on_slave_disconnect_event_.TimedWait(TestTimeouts::action_timeout());
+    return !on_slave_disconnect_event_.WaitWithTimeout(test::ActionTimeout());
   }
 
  private:
@@ -139,7 +137,7 @@
     on_slave_disconnect_event_.Signal();
   }
 
-  base::WaitableEvent on_slave_disconnect_event_;
+  AutoResetWaitableEvent on_slave_disconnect_event_;
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(TestMasterProcessDelegate);
 };
@@ -161,13 +159,12 @@
 // Represents the master's side of its connection to a slave.
 class TestSlaveConnection {
  public:
-  TestSlaveConnection(TestIOThread* test_io_thread,
+  TestSlaveConnection(test::TestIOThread* test_io_thread,
                       IPCSupport* master_ipc_support)
       : test_io_thread_(test_io_thread),
         master_ipc_support_(master_ipc_support),
         connection_id_(master_ipc_support_->GenerateConnectionIdentifier()),
-        slave_id_(kInvalidProcessIdentifier),
-        event_(true, false) {}
+        slave_id_(kInvalidProcessIdentifier) {}
   ~TestSlaveConnection() {}
 
   // After this is called, |ShutdownChannelToSlave()| must be called (possibly
@@ -177,7 +174,8 @@
     // Note: |ChannelId|s and |ProcessIdentifier|s are interchangeable.
     RefPtr<MessagePipeDispatcher> mp = master_ipc_support_->ConnectToSlave(
         connection_id_, nullptr, channel_pair.PassServerHandle(),
-        base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event_)),
+        base::Bind(&ManualResetWaitableEvent::Signal,
+                   base::Unretained(&event_)),
         nullptr, &slave_id_);
     EXPECT_TRUE(mp);
     EXPECT_NE(slave_id_, kInvalidProcessIdentifier);
@@ -187,7 +185,7 @@
   }
 
   void WaitForChannelToSlave() {
-    EXPECT_TRUE(event_.TimedWait(TestTimeouts::action_timeout()));
+    EXPECT_FALSE(event_.WaitWithTimeout(test::ActionTimeout()));
   }
 
   void ShutdownChannelToSlave() {
@@ -207,13 +205,13 @@
   const ConnectionIdentifier& connection_id() const { return connection_id_; }
 
  private:
-  TestIOThread* const test_io_thread_;
+  test::TestIOThread* const test_io_thread_;
   IPCSupport* const master_ipc_support_;
   const ConnectionIdentifier connection_id_;
   // The master's message pipe dispatcher.
   RefPtr<MessagePipeDispatcher> message_pipe_;
   ProcessIdentifier slave_id_;
-  base::WaitableEvent event_;
+  ManualResetWaitableEvent event_;
   embedder::ScopedPlatformHandle slave_platform_handle_;
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(TestSlaveConnection);
@@ -225,7 +223,7 @@
  public:
   // Note: Before destruction, |ShutdownIPCSupport()| must be called.
   TestSlave(embedder::PlatformSupport* platform_support,
-            TestIOThread* test_io_thread,
+            test::TestIOThread* test_io_thread,
             embedder::ScopedPlatformHandle platform_handle)
       : test_io_thread_(test_io_thread),
         slave_ipc_support_(platform_support,
@@ -233,8 +231,7 @@
                            test_io_thread->task_runner(),
                            &slave_process_delegate_,
                            test_io_thread->task_runner(),
-                           platform_handle.Pass()),
-        event_(true, false) {}
+                           platform_handle.Pass()) {}
   ~TestSlave() {}
 
   // After this is called, |ShutdownChannelToMaster()| must be called (possibly
@@ -243,8 +240,8 @@
       const ConnectionIdentifier& connection_id) {
     ProcessIdentifier master_id = kInvalidProcessIdentifier;
     RefPtr<MessagePipeDispatcher> mp = slave_ipc_support_.ConnectToMaster(
-        connection_id,
-        base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event_)),
+        connection_id, base::Bind(&ManualResetWaitableEvent::Signal,
+                                  base::Unretained(&event_)),
         nullptr, &master_id);
     EXPECT_TRUE(mp);
     EXPECT_EQ(kMasterProcessIdentifier, master_id);
@@ -252,7 +249,7 @@
   }
 
   void WaitForChannelToMaster() {
-    EXPECT_TRUE(event_.TimedWait(TestTimeouts::action_timeout()));
+    EXPECT_FALSE(event_.WaitWithTimeout(test::ActionTimeout()));
   }
 
   void ShutdownChannelToMaster() {
@@ -273,10 +270,10 @@
   }
 
  private:
-  TestIOThread* const test_io_thread_;
+  test::TestIOThread* const test_io_thread_;
   TestSlaveProcessDelegate slave_process_delegate_;
   IPCSupport slave_ipc_support_;
-  base::WaitableEvent event_;
+  ManualResetWaitableEvent event_;
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(TestSlave);
 };
@@ -285,7 +282,7 @@
 class TestSlaveSetup {
  public:
   TestSlaveSetup(embedder::SimplePlatformSupport* platform_support,
-                 TestIOThread* test_io_thread,
+                 test::TestIOThread* test_io_thread,
                  TestMasterProcessDelegate* master_process_delegate,
                  IPCSupport* master_ipc_support)
       : platform_support_(platform_support),
@@ -351,7 +348,7 @@
 
  private:
   embedder::SimplePlatformSupport* const platform_support_;
-  TestIOThread* const test_io_thread_;
+  test::TestIOThread* const test_io_thread_;
   TestMasterProcessDelegate* const master_process_delegate_;
   IPCSupport* const master_ipc_support_;
 
@@ -368,7 +365,7 @@
  public:
   // Note: Run master process delegate methods on the I/O thread.
   IPCSupportTest()
-      : test_io_thread_(TestIOThread::StartMode::AUTO),
+      : test_io_thread_(test::TestIOThread::StartMode::AUTO),
         master_ipc_support_(&platform_support_,
                             embedder::ProcessType::MASTER,
                             test_io_thread_.task_runner(),
@@ -394,7 +391,7 @@
   embedder::SimplePlatformSupport& platform_support() {
     return platform_support_;
   }
-  TestIOThread& test_io_thread() { return test_io_thread_; }
+  test::TestIOThread& test_io_thread() { return test_io_thread_; }
   TestMasterProcessDelegate& master_process_delegate() {
     return master_process_delegate_;
   }
@@ -402,7 +399,7 @@
 
  private:
   embedder::SimplePlatformSupport platform_support_;
-  TestIOThread test_io_thread_;
+  test::TestIOThread test_io_thread_;
 
   // All tests require a master.
   TestMasterProcessDelegate master_process_delegate_;
@@ -436,7 +433,7 @@
 
   // A message was sent through the message pipe, |Channel|s must have been
   // established on both sides. The events have thus almost certainly been
-  // signalled, but we'll wait just to be sure.
+  // signaled, but we'll wait just to be sure.
   s->slave_connection()->WaitForChannelToSlave();
   s->slave()->WaitForChannelToMaster();
 
@@ -675,7 +672,7 @@
   ASSERT_TRUE(client_platform_handle.is_valid());
 
   embedder::SimplePlatformSupport platform_support;
-  TestIOThread test_io_thread(TestIOThread::StartMode::AUTO);
+  test::TestIOThread test_io_thread(test::TestIOThread::StartMode::AUTO);
   TestSlaveProcessDelegate slave_process_delegate;
   // Note: Run process delegate methods on the I/O thread.
   IPCSupport ipc_support(&platform_support, embedder::ProcessType::SLAVE,
@@ -683,12 +680,12 @@
                          test_io_thread.task_runner(),
                          client_platform_handle.Pass());
 
-  const base::CommandLine& command_line =
-      *base::CommandLine::ForCurrentProcess();
-  ASSERT_TRUE(command_line.HasSwitch(kConnectionIdFlag));
+  std::string connection_id_string;
+  ASSERT_TRUE(test::GetTestCommandLine()->GetOptionValue(
+      kConnectionIdFlag, &connection_id_string));
   bool ok = false;
-  ConnectionIdentifier connection_id = ConnectionIdentifier::FromString(
-      command_line.GetSwitchValueASCII(kConnectionIdFlag), &ok);
+  ConnectionIdentifier connection_id =
+      ConnectionIdentifier::FromString(connection_id_string, &ok);
   ASSERT_TRUE(ok);
 
   embedder::ScopedPlatformHandle second_platform_handle =
diff --git a/mojo/edk/system/local_data_pipe_impl.cc b/mojo/edk/system/local_data_pipe_impl.cc
index 1d1f205..d4330ae 100644
--- a/mojo/edk/system/local_data_pipe_impl.cc
+++ b/mojo/edk/system/local_data_pipe_impl.cc
@@ -25,6 +25,8 @@
 #include "mojo/edk/system/remote_producer_data_pipe_impl.h"
 #include "mojo/edk/util/make_unique.h"
 
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/master_connection_manager.cc b/mojo/edk/system/master_connection_manager.cc
index 0976a5d..84f838a 100644
--- a/mojo/edk/system/master_connection_manager.cc
+++ b/mojo/edk/system/master_connection_manager.cc
@@ -13,7 +13,6 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
-#include "base/synchronization/waitable_event.h"
 #include "mojo/edk/embedder/master_process_delegate.h"
 #include "mojo/edk/embedder/platform_channel_pair.h"
 #include "mojo/edk/embedder/platform_handle.h"
@@ -22,9 +21,12 @@
 #include "mojo/edk/system/message_in_transit.h"
 #include "mojo/edk/system/raw_channel.h"
 #include "mojo/edk/system/transport_data.h"
+#include "mojo/edk/system/waitable_event.h"
 #include "mojo/edk/util/make_unique.h"
 #include "mojo/public/cpp/system/macros.h"
 
+using mojo::util::MutexLocker;
+
 namespace mojo {
 namespace system {
 
@@ -371,7 +373,7 @@
 
   // We have to wait for the task to be executed, in case someone calls
   // |AddSlave()| followed immediately by |Shutdown()|.
-  base::WaitableEvent event(false, false);
+  AutoResetWaitableEvent event;
   private_thread_.message_loop()->PostTask(
       FROM_HERE,
       base::Bind(&MasterConnectionManager::AddSlaveOnPrivateThread,
@@ -671,7 +673,7 @@
     embedder::SlaveInfo slave_info,
     embedder::ScopedPlatformHandle platform_handle,
     ProcessIdentifier slave_process_identifier,
-    base::WaitableEvent* event) {
+    AutoResetWaitableEvent* event) {
   DCHECK(platform_handle.is_valid());
   DCHECK(event);
   AssertOnPrivateThread();
diff --git a/mojo/edk/system/master_connection_manager.h b/mojo/edk/system/master_connection_manager.h
index efbe43c..e751e4d 100644
--- a/mojo/edk/system/master_connection_manager.h
+++ b/mojo/edk/system/master_connection_manager.h
@@ -9,17 +9,16 @@
 
 #include <unordered_map>
 
-#include "base/memory/ref_counted.h"
 #include "base/threading/thread.h"
 #include "mojo/edk/embedder/platform_task_runner.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
 #include "mojo/edk/system/connection_manager.h"
-#include "mojo/edk/system/mutex.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace base {
 class TaskRunner;
-class WaitableEvent;
 }
 
 namespace mojo {
@@ -31,6 +30,8 @@
 
 namespace system {
 
+class AutoResetWaitableEvent;
+
 // The |ConnectionManager| implementation for the master process.
 //
 // This class is thread-safe (except that no public methods may be called from
@@ -115,7 +116,7 @@
   void AddSlaveOnPrivateThread(embedder::SlaveInfo slave_info,
                                embedder::ScopedPlatformHandle platform_handle,
                                ProcessIdentifier slave_process_identifier,
-                               base::WaitableEvent* event);
+                               AutoResetWaitableEvent* event);
   // Called by |Helper::OnError()|.
   void OnError(ProcessIdentifier process_identifier);
   // Posts a call to |master_process_delegate_->OnSlaveDisconnect()|.
@@ -147,7 +148,7 @@
 
   // Note: |mutex_| is not needed in the constructor, |Init()|,
   // |Shutdown()|/|ShutdownOnPrivateThread()|, or the destructor
-  Mutex mutex_;
+  util::Mutex mutex_;
 
   ProcessIdentifier next_process_identifier_ MOJO_GUARDED_BY(mutex_);
 
diff --git a/mojo/edk/system/memory.h b/mojo/edk/system/memory.h
index 427dbb3..b996e9c 100644
--- a/mojo/edk/system/memory.h
+++ b/mojo/edk/system/memory.h
@@ -58,8 +58,8 @@
 class UserPointerWriter;
 template <typename Type>
 class UserPointerReaderWriter;
-template <class Options>
-class UserOptionsReader;
+template <typename Type>
+class UserPointerPartialReader;
 
 // Provides a convenient way to implicitly get null |UserPointer<Type>|s.
 struct NullUserPointer {};
@@ -75,6 +75,8 @@
   using NonVoidType = typename internal::VoidToChar<Type>::type;
 
  public:
+  static_assert(!std::is_volatile<Type>::value, "Type must not be volatile");
+
   // Instead of explicitly using these constructors, you can often use
   // |MakeUserPointer()| (or |NullUserPointer()| for null pointers). (The common
   // exception is when you have, e.g., a |char*| and want to get a
@@ -235,13 +237,25 @@
   using Writer = UserPointerWriter<Type>;
   using ReaderWriter = UserPointerReaderWriter<Type>;
 
+  // This is like |Reader| above, but for partially reading the memory of a
+  // single object (usually a struct). The pointer it provides will be to a full
+  // |Type| (no more, no less), with unavailable bytes set to zero.
+  //
+  // Note: It isn't safe to just use |UserPointer<const char>::Reader| and
+  // reinterpret cast the pointer to a struct pointer. Even if before accessing
+  // a field you check that it's within the available size, the compiler may
+  // read beyond the extent of the field itself, so long as the read is still
+  // within the struct.
+  //
+  // TODO(vtl): Add writer and reader-writer versions of this if/when necessary.
+  using PartialReader = UserPointerPartialReader<Type>;
+
  private:
   friend class UserPointerReader<Type>;
   friend class UserPointerReader<const Type>;
   friend class UserPointerWriter<Type>;
   friend class UserPointerReaderWriter<Type>;
-  template <class Options>
-  friend class UserOptionsReader;
+  friend class UserPointerPartialReader<Type>;
 
   Type* pointer_;
   // Allow copy and assignment.
@@ -260,37 +274,29 @@
   using TypeNoConst = typename std::remove_const<Type>::type;
 
  public:
+  static_assert(!std::is_volatile<Type>::value, "Type must not be volatile");
+
   // Note: If |count| is zero, |GetPointer()| will always return null.
-  UserPointerReader(UserPointer<const Type> user_pointer, size_t count) {
-    Init(user_pointer.pointer_, count, true);
+  UserPointerReader(UserPointer<const TypeNoConst> user_pointer, size_t count) {
+    Init(user_pointer.pointer_, count);
   }
   UserPointerReader(UserPointer<TypeNoConst> user_pointer, size_t count) {
-    Init(user_pointer.pointer_, count, true);
+    Init(user_pointer.pointer_, count);
   }
 
-  const Type* GetPointer() const { return buffer_.get(); }
+  const TypeNoConst* GetPointer() const { return buffer_.get(); }
 
  private:
-  template <class Options>
-  friend class UserOptionsReader;
-
-  struct NoCheck {};
-  UserPointerReader(NoCheck,
-                    UserPointer<const Type> user_pointer,
-                    size_t count) {
-    Init(user_pointer.pointer_, count, false);
-  }
-
-  void Init(const Type* user_pointer, size_t count, bool check) {
+  void Init(const TypeNoConst* user_pointer, size_t count) {
     if (count == 0)
       return;
 
-    if (check) {
-      internal::CheckUserPointerWithCount<sizeof(Type), MOJO_ALIGNOF(Type)>(
-          user_pointer, count);
-    }
+    internal::CheckUserPointerWithCount<sizeof(TypeNoConst),
+                                        MOJO_ALIGNOF(TypeNoConst)>(user_pointer,
+                                                                   count);
+
     buffer_.reset(new TypeNoConst[count]);
-    memcpy(buffer_.get(), user_pointer, count * sizeof(Type));
+    memcpy(buffer_.get(), user_pointer, count * sizeof(TypeNoConst));
   }
 
   std::unique_ptr<TypeNoConst[]> buffer_;
@@ -302,6 +308,9 @@
 template <typename Type>
 class UserPointerWriter {
  public:
+  static_assert(!std::is_volatile<Type>::value, "Type must not be volatile");
+  static_assert(!std::is_const<Type>::value, "Type must not be const");
+
   // Note: If |count| is zero, |GetPointer()| will always return null.
   UserPointerWriter(UserPointer<Type> user_pointer, size_t count)
       : user_pointer_(user_pointer), count_(count) {
@@ -331,6 +340,9 @@
 template <typename Type>
 class UserPointerReaderWriter {
  public:
+  static_assert(!std::is_volatile<Type>::value, "Type must not be volatile");
+  static_assert(!std::is_const<Type>::value, "Type must not be const");
+
   // Note: If |count| is zero, |GetPointer()| will always return null.
   UserPointerReaderWriter(UserPointer<Type> user_pointer, size_t count)
       : user_pointer_(user_pointer), count_(count) {
@@ -359,6 +371,47 @@
   MOJO_DISALLOW_COPY_AND_ASSIGN(UserPointerReaderWriter);
 };
 
+// Implementation of |UserPointer<Type>::PartialReader|.
+template <typename Type>
+class UserPointerPartialReader {
+ private:
+  using TypeNoConst = typename std::remove_cv<Type>::type;
+
+ public:
+  static_assert(!std::is_volatile<Type>::value, "Type must not be volatile");
+
+  // Note: If |count| is zero, |GetPointer()| will always return null.
+  UserPointerPartialReader(UserPointer<const TypeNoConst> user_pointer,
+                           size_t num_bytes) {
+    Init(user_pointer.pointer_, num_bytes);
+  }
+  UserPointerPartialReader(UserPointer<TypeNoConst> user_pointer,
+                           size_t num_bytes) {
+    Init(user_pointer.pointer_, num_bytes);
+  }
+
+  const TypeNoConst* GetPointer() const { return &storage_; }
+
+ private:
+  void Init(const TypeNoConst* user_pointer, size_t num_bytes) {
+    // Check that all |num_bytes| are valid.
+    internal::CheckUserPointerWithSize<MOJO_ALIGNOF(TypeNoConst)>(user_pointer,
+                                                                  num_bytes);
+
+    // But only copy up to |num_bytes|.
+    if (num_bytes >= sizeof(TypeNoConst))
+      num_bytes = sizeof(TypeNoConst);
+
+    memcpy(&storage_, user_pointer, num_bytes);
+    memset(reinterpret_cast<char*>(&storage_) + num_bytes, 0,
+           sizeof(TypeNoConst) - num_bytes);
+  }
+
+  TypeNoConst storage_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(UserPointerPartialReader);
+};
+
 }  // namespace system
 }  // namespace mojo
 
diff --git a/mojo/edk/system/message_pipe.cc b/mojo/edk/system/message_pipe.cc
index 49fd5a0..5256bd3 100644
--- a/mojo/edk/system/message_pipe.cc
+++ b/mojo/edk/system/message_pipe.cc
@@ -19,6 +19,10 @@
 #include "mojo/edk/system/proxy_message_pipe_endpoint.h"
 #include "mojo/edk/util/make_unique.h"
 
+using mojo::util::MakeRefCounted;
+using mojo::util::MutexLocker;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/message_pipe.h b/mojo/edk/system/message_pipe.h
index d7eefc6..99b1200 100644
--- a/mojo/edk/system/message_pipe.h
+++ b/mojo/edk/system/message_pipe.h
@@ -11,7 +11,6 @@
 #include <memory>
 #include <vector>
 
-#include "base/memory/ref_counted.h"
 #include "mojo/edk/embedder/platform_handle_vector.h"
 #include "mojo/edk/system/channel_endpoint_client.h"
 #include "mojo/edk/system/dispatcher.h"
@@ -19,8 +18,9 @@
 #include "mojo/edk/system/memory.h"
 #include "mojo/edk/system/message_in_transit.h"
 #include "mojo/edk/system/message_pipe_endpoint.h"
-#include "mojo/edk/system/mutex.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/c/system/message_pipe.h"
 #include "mojo/public/c/system/types.h"
 #include "mojo/public/cpp/system/macros.h"
@@ -39,21 +39,21 @@
 class MessagePipe final : public ChannelEndpointClient {
  public:
   // Creates a |MessagePipe| with two new |LocalMessagePipeEndpoint|s.
-  static RefPtr<MessagePipe> CreateLocalLocal();
+  static util::RefPtr<MessagePipe> CreateLocalLocal();
 
   // Creates a |MessagePipe| with a |LocalMessagePipeEndpoint| on port 0 and a
   // |ProxyMessagePipeEndpoint| on port 1. |*channel_endpoint| is set to the
   // (newly-created) |ChannelEndpoint| for the latter.
-  static RefPtr<MessagePipe> CreateLocalProxy(
-      RefPtr<ChannelEndpoint>* channel_endpoint);
+  static util::RefPtr<MessagePipe> CreateLocalProxy(
+      util::RefPtr<ChannelEndpoint>* channel_endpoint);
 
   // Similar to |CreateLocalProxy()|, except that it'll do so from an existing
   // |ChannelEndpoint| (whose |ReplaceClient()| it'll call) and take
   // |message_queue|'s contents as already-received incoming messages. If
   // |channel_endpoint| is null, this will create a "half-open" message pipe.
-  static RefPtr<MessagePipe> CreateLocalProxyFromExisting(
+  static util::RefPtr<MessagePipe> CreateLocalProxyFromExisting(
       MessageInTransitQueue* message_queue,
-      RefPtr<ChannelEndpoint>&& channel_endpoint);
+      util::RefPtr<ChannelEndpoint>&& channel_endpoint);
 
   // Creates a |MessagePipe| with a |ProxyMessagePipeEndpoint| on port 0 and a
   // |LocalMessagePipeEndpoint| on port 1. |*channel_endpoint| is set to the
@@ -61,8 +61,8 @@
   // Note: This is really only needed in tests (outside of tests, this
   // configuration arises from a local message pipe having its port 0
   // "converted" using |ConvertLocalToProxy()|).
-  static RefPtr<MessagePipe> CreateProxyLocal(
-      RefPtr<ChannelEndpoint>* channel_endpoint);
+  static util::RefPtr<MessagePipe> CreateProxyLocal(
+      util::RefPtr<ChannelEndpoint>* channel_endpoint);
 
   // Gets the other port number (i.e., 0 -> 1, 1 -> 0).
   static unsigned GetPeerPort(unsigned port);
@@ -73,7 +73,7 @@
   static bool Deserialize(Channel* channel,
                           const void* source,
                           size_t size,
-                          RefPtr<MessagePipe>* message_pipe,
+                          util::RefPtr<MessagePipe>* message_pipe,
                           unsigned* port);
 
   // Gets the type of the endpoint (used for assertions, etc.).
@@ -138,7 +138,7 @@
       std::vector<DispatcherTransport>* transports)
       MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
 
-  mutable Mutex mutex_;
+  mutable util::Mutex mutex_;
   std::unique_ptr<MessagePipeEndpoint> endpoints_[2] MOJO_GUARDED_BY(mutex_);
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(MessagePipe);
diff --git a/mojo/edk/system/message_pipe_dispatcher.cc b/mojo/edk/system/message_pipe_dispatcher.cc
index 8ba0927..704fd13 100644
--- a/mojo/edk/system/message_pipe_dispatcher.cc
+++ b/mojo/edk/system/message_pipe_dispatcher.cc
@@ -14,6 +14,8 @@
 #include "mojo/edk/system/options_validation.h"
 #include "mojo/edk/system/proxy_message_pipe_endpoint.h"
 
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/message_pipe_dispatcher.h b/mojo/edk/system/message_pipe_dispatcher.h
index 24b262f..da64649 100644
--- a/mojo/edk/system/message_pipe_dispatcher.h
+++ b/mojo/edk/system/message_pipe_dispatcher.h
@@ -7,7 +7,8 @@
 
 #include "mojo/edk/system/dispatcher.h"
 #include "mojo/edk/system/memory.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -26,7 +27,7 @@
   // this is exposed directly for testing convenience.)
   static const MojoCreateMessagePipeOptions kDefaultCreateOptions;
 
-  static RefPtr<MessagePipeDispatcher> Create(
+  static util::RefPtr<MessagePipeDispatcher> Create(
       const MojoCreateMessagePipeOptions& /*validated_options*/) {
     return AdoptRef(new MessagePipeDispatcher());
   }
@@ -41,7 +42,7 @@
       MojoCreateMessagePipeOptions* out_options);
 
   // Must be called before any other methods. (This method is not thread-safe.)
-  void Init(RefPtr<MessagePipe>&& message_pipe,
+  void Init(util::RefPtr<MessagePipe>&& message_pipe,
             unsigned port) MOJO_NOT_THREAD_SAFE;
 
   // |Dispatcher| public methods:
@@ -52,14 +53,14 @@
   // the message pipe, port 0).
   // TODO(vtl): This currently uses |kDefaultCreateOptions|, which is okay since
   // there aren't any options, but eventually options should be plumbed through.
-  static RefPtr<MessagePipeDispatcher> CreateRemoteMessagePipe(
-      RefPtr<ChannelEndpoint>* channel_endpoint);
+  static util::RefPtr<MessagePipeDispatcher> CreateRemoteMessagePipe(
+      util::RefPtr<ChannelEndpoint>* channel_endpoint);
 
   // The "opposite" of |SerializeAndClose()|. (Typically this is called by
   // |Dispatcher::Deserialize()|.)
-  static RefPtr<MessagePipeDispatcher> Deserialize(Channel* channel,
-                                                   const void* source,
-                                                   size_t size);
+  static util::RefPtr<MessagePipeDispatcher> Deserialize(Channel* channel,
+                                                         const void* source,
+                                                         size_t size);
 
  private:
   friend class MessagePipeDispatcherTransport;
@@ -78,7 +79,8 @@
   // |Dispatcher| protected methods:
   void CancelAllAwakablesNoLock() override;
   void CloseImplNoLock() override;
-  RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock() override;
+  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock()
+      override;
   MojoResult WriteMessageImplNoLock(
       UserPointer<const void> bytes,
       uint32_t num_bytes,
@@ -108,7 +110,7 @@
       MOJO_NOT_THREAD_SAFE;
 
   // This will be null if closed.
-  RefPtr<MessagePipe> message_pipe_ MOJO_GUARDED_BY(mutex());
+  util::RefPtr<MessagePipe> message_pipe_ MOJO_GUARDED_BY(mutex());
   unsigned port_ MOJO_GUARDED_BY(mutex());
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(MessagePipeDispatcher);
diff --git a/mojo/edk/system/message_pipe_dispatcher_unittest.cc b/mojo/edk/system/message_pipe_dispatcher_unittest.cc
index b51899b..3d852c2 100644
--- a/mojo/edk/system/message_pipe_dispatcher_unittest.cc
+++ b/mojo/edk/system/message_pipe_dispatcher_unittest.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a
-// heavily-loaded system). Sorry. |test::EpsilonDeadline()| may be increased to
+// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to
 // increase tolerance and reduce observed flakiness (though doing so reduces the
 // meaningfulness of the test).
 
@@ -17,15 +17,20 @@
 #include <vector>
 
 #include "mojo/edk/system/message_pipe.h"
-#include "mojo/edk/system/ref_ptr.h"
-#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/test/random.h"
+#include "mojo/edk/system/test/simple_test_thread.h"
+#include "mojo/edk/system/test/sleep.h"
+#include "mojo/edk/system/test/stopwatch.h"
+#include "mojo/edk/system/test/timeouts.h"
 #include "mojo/edk/system/waiter.h"
 #include "mojo/edk/system/waiter_test_utils.h"
-#include "mojo/edk/test/simple_test_thread.h"
 #include "mojo/edk/util/make_unique.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 namespace {
@@ -77,7 +82,7 @@
     stopwatch.Start();
     EXPECT_EQ(MOJO_RESULT_OK, w.Wait(MOJO_DEADLINE_INDEFINITE, &context));
     EXPECT_EQ(1u, context);
-    EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+    EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
     hss = HandleSignalsState();
     d0->RemoveAwakable(&w, &hss);
     EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
@@ -110,7 +115,7 @@
               d0->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 3, nullptr));
     stopwatch.Start();
     EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, w.Wait(0, nullptr));
-    EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+    EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
     hss = HandleSignalsState();
     d0->RemoveAwakable(&w, &hss);
     EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
@@ -122,10 +127,10 @@
               d0->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 3, nullptr));
     stopwatch.Start();
     EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
-              w.Wait(2 * test::EpsilonDeadline(), nullptr));
+              w.Wait(2 * test::EpsilonTimeout(), nullptr));
     MojoDeadline elapsed = stopwatch.Elapsed();
-    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
-    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
     hss = HandleSignalsState();
     d0->RemoveAwakable(&w, &hss);
     EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
@@ -140,7 +145,7 @@
     EXPECT_EQ(MOJO_RESULT_OK, d1->Close());
 
     // It should be signaled.
-    EXPECT_EQ(MOJO_RESULT_OK, w.Wait(test::TinyDeadline(), &context));
+    EXPECT_EQ(MOJO_RESULT_OK, w.Wait(test::TinyTimeout(), &context));
     EXPECT_EQ(12u, context);
     hss = HandleSignalsState();
     d0->RemoveAwakable(&w, &hss);
@@ -371,7 +376,7 @@
                                 &context, &hss);
       stopwatch.Start();
       thread.Start();
-      test::Sleep(2 * test::EpsilonDeadline());
+      test::Sleep(2 * test::EpsilonTimeout());
       // Wake it up by writing to |d0|.
       buffer[0] = 123456789;
       EXPECT_EQ(MOJO_RESULT_OK,
@@ -379,8 +384,8 @@
                                  nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE));
     }  // Joins the thread.
     elapsed = stopwatch.Elapsed();
-    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
-    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
     EXPECT_TRUE(did_wait);
     EXPECT_EQ(MOJO_RESULT_OK, result);
     EXPECT_EQ(1u, context);
@@ -396,7 +401,7 @@
       stopwatch.Start();
       thread.Start();
     }  // Joins the thread.
-    EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+    EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
     EXPECT_FALSE(did_wait);
     EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, result);
     EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
@@ -421,12 +426,12 @@
                                 &context, &hss);
       stopwatch.Start();
       thread.Start();
-      test::Sleep(2 * test::EpsilonDeadline());
+      test::Sleep(2 * test::EpsilonTimeout());
       EXPECT_EQ(MOJO_RESULT_OK, d0->Close());
     }  // Joins the thread.
     elapsed = stopwatch.Elapsed();
-    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
-    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
     EXPECT_TRUE(did_wait);
     EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
     EXPECT_EQ(3u, context);
@@ -455,12 +460,12 @@
                                 &context, &hss);
       stopwatch.Start();
       thread.Start();
-      test::Sleep(2 * test::EpsilonDeadline());
+      test::Sleep(2 * test::EpsilonTimeout());
       EXPECT_EQ(MOJO_RESULT_OK, d1->Close());
     }  // Joins the thread.
     elapsed = stopwatch.Elapsed();
-    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
-    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
     EXPECT_TRUE(did_wait);
     EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
     EXPECT_EQ(4u, context);
@@ -475,7 +480,7 @@
 
 const size_t kMaxMessageSize = 2000;
 
-class WriterThread : public mojo::test::SimpleTestThread {
+class WriterThread : public test::SimpleTestThread {
  public:
   // |*messages_written| and |*bytes_written| belong to the thread while it's
   // alive.
@@ -525,7 +530,7 @@
   MOJO_DISALLOW_COPY_AND_ASSIGN(WriterThread);
 };
 
-class ReaderThread : public mojo::test::SimpleTestThread {
+class ReaderThread : public test::SimpleTestThread {
  public:
   // |*messages_read| and |*bytes_read| belong to the thread while it's alive.
   ReaderThread(RefPtr<Dispatcher> read_dispatcher,
diff --git a/mojo/edk/system/message_pipe_endpoint.h b/mojo/edk/system/message_pipe_endpoint.h
index c488ca3..560ad62 100644
--- a/mojo/edk/system/message_pipe_endpoint.h
+++ b/mojo/edk/system/message_pipe_endpoint.h
@@ -10,7 +10,6 @@
 #include <memory>
 #include <vector>
 
-#include "base/memory/ref_counted.h"
 #include "mojo/edk/system/dispatcher.h"
 #include "mojo/edk/system/memory.h"
 #include "mojo/edk/system/message_in_transit.h"
diff --git a/mojo/edk/system/message_pipe_perftest.cc b/mojo/edk/system/message_pipe_perftest.cc
index 9a5d569..7716938 100644
--- a/mojo/edk/system/message_pipe_perftest.cc
+++ b/mojo/edk/system/message_pipe_perftest.cc
@@ -3,30 +3,27 @@
 // found in the LICENSE file.
 
 #include <stdint.h>
-#include <stdio.h>
-#include <string.h>
 
-#include <memory>
 #include <string>
 #include <utility>
-#include <vector>
 
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
-#include "base/test/perf_time_logger.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
-#include "mojo/edk/system/channel.h"
 #include "mojo/edk/system/local_message_pipe_endpoint.h"
 #include "mojo/edk/system/message_pipe.h"
 #include "mojo/edk/system/message_pipe_test_utils.h"
 #include "mojo/edk/system/proxy_message_pipe_endpoint.h"
-#include "mojo/edk/system/raw_channel.h"
-#include "mojo/edk/system/ref_ptr.h"
-#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/test/perf_log.h"
+#include "mojo/edk/system/test/stopwatch.h"
 #include "mojo/edk/test/test_utils.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/public/cpp/system/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 namespace {
@@ -73,12 +70,12 @@
     std::string test_name =
         base::StringPrintf("IPC_Perf_%dx_%u", message_count_,
                            static_cast<unsigned>(message_size_));
-    base::PerfTimeLogger logger(test_name.c_str());
+    test::Stopwatch stopwatch;
 
+    stopwatch.Start();
     for (int i = 0; i < message_count_; ++i)
       WriteWaitThenRead(mp);
-
-    logger.Done();
+    test::LogPerfResult(test_name.c_str(), stopwatch.Elapsed() / 1000.0, "ms");
   }
 
  private:
@@ -86,7 +83,8 @@
   size_t message_size_;
   std::string payload_;
   std::string read_buffer_;
-  std::unique_ptr<base::PerfTimeLogger> perf_logger_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(MultiprocessMessagePipePerfTest);
 };
 
 // For each message received, sends a reply message with the same contents
diff --git a/mojo/edk/system/message_pipe_test_utils.cc b/mojo/edk/system/message_pipe_test_utils.cc
index 18bdf5d..4521575 100644
--- a/mojo/edk/system/message_pipe_test_utils.cc
+++ b/mojo/edk/system/message_pipe_test_utils.cc
@@ -10,9 +10,13 @@
 #include "mojo/edk/system/channel.h"
 #include "mojo/edk/system/channel_endpoint.h"
 #include "mojo/edk/system/message_pipe.h"
-#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/test/sleep.h"
+#include "mojo/edk/system/test/timeouts.h"
 #include "mojo/edk/system/waiter.h"
 
+using mojo::util::MakeRefCounted;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 namespace test {
@@ -37,7 +41,7 @@
 
 ChannelThread::ChannelThread(embedder::PlatformSupport* platform_support)
     : platform_support_(platform_support),
-      test_io_thread_(mojo::test::TestIOThread::StartMode::MANUAL) {}
+      test_io_thread_(TestIOThread::StartMode::MANUAL) {}
 
 ChannelThread::~ChannelThread() {
   Stop();
@@ -58,7 +62,7 @@
     // TODO(vtl): Remove this once |Channel| has a
     // |FlushWriteBufferAndShutdown()| (or whatever).
     while (!channel_->IsWriteBufferEmpty())
-      test::Sleep(test::DeadlineFromMilliseconds(20));
+      test::Sleep(test::EpsilonTimeout());
 
     test_io_thread_.PostTaskAndWait(base::Bind(
         &ChannelThread::ShutdownChannelOnIOThread, base::Unretained(this)));
diff --git a/mojo/edk/system/message_pipe_test_utils.h b/mojo/edk/system/message_pipe_test_utils.h
index 9982dca..29bf709 100644
--- a/mojo/edk/system/message_pipe_test_utils.h
+++ b/mojo/edk/system/message_pipe_test_utils.h
@@ -7,10 +7,9 @@
 
 #include "mojo/edk/embedder/simple_platform_support.h"
 #include "mojo/edk/system/channel.h"
-#include "mojo/edk/system/ref_ptr.h"
-#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/test/test_io_thread.h"
 #include "mojo/edk/test/multiprocess_test_helper.h"
-#include "mojo/edk/test/test_io_thread.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -32,19 +31,19 @@
   ~ChannelThread();
 
   void Start(embedder::ScopedPlatformHandle platform_handle,
-             RefPtr<ChannelEndpoint>&& channel_endpoint);
+             util::RefPtr<ChannelEndpoint>&& channel_endpoint);
   void Stop();
 
  private:
   // TODO(vtl): |channel_endpoint| should be an rvalue reference, but that
   // doesn't currently work correctly with base::Bind.
   void InitChannelOnIOThread(embedder::ScopedPlatformHandle platform_handle,
-                             RefPtr<ChannelEndpoint> channel_endpoint);
+                             util::RefPtr<ChannelEndpoint> channel_endpoint);
   void ShutdownChannelOnIOThread();
 
   embedder::PlatformSupport* const platform_support_;
-  mojo::test::TestIOThread test_io_thread_;
-  RefPtr<Channel> channel_;
+  TestIOThread test_io_thread_;
+  util::RefPtr<Channel> channel_;
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(ChannelThread);
 };
@@ -56,7 +55,7 @@
   ~MultiprocessMessagePipeTestBase() override;
 
  protected:
-  void Init(RefPtr<ChannelEndpoint>&& ep);
+  void Init(util::RefPtr<ChannelEndpoint>&& ep);
 
   embedder::PlatformSupport* platform_support() { return &platform_support_; }
   mojo::test::MultiprocessTestHelper* helper() { return &helper_; }
diff --git a/mojo/edk/system/multiprocess_message_pipe_unittest.cc b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
index 88e73ed..0e4de3a 100644
--- a/mojo/edk/system/multiprocess_message_pipe_unittest.cc
+++ b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
@@ -22,14 +22,15 @@
 #include "mojo/edk/system/message_pipe_test_utils.h"
 #include "mojo/edk/system/platform_handle_dispatcher.h"
 #include "mojo/edk/system/raw_channel.h"
-#include "mojo/edk/system/ref_ptr.h"
 #include "mojo/edk/system/shared_buffer_dispatcher.h"
-#include "mojo/edk/system/test_utils.h"
-#include "mojo/edk/test/scoped_test_dir.h"
+#include "mojo/edk/system/test/scoped_test_dir.h"
 #include "mojo/edk/test/test_utils.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/edk/util/scoped_file.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 namespace {
@@ -338,7 +339,7 @@
                              MOJO_WRITE_MESSAGE_FLAG_NONE));
   transport.End();
 
-  dispatcher->AssertHasOneRef();
+  EXPECT_TRUE(dispatcher->HasOneRef());
   dispatcher = nullptr;
 
   // Wait for a message from the child.
@@ -426,7 +427,7 @@
 
     RefPtr<PlatformHandleDispatcher> dispatcher(
         static_cast<PlatformHandleDispatcher*>(dispatchers[i].get()));
-    embedder::ScopedPlatformHandle h = dispatcher->PassPlatformHandle().Pass();
+    embedder::ScopedPlatformHandle h = dispatcher->PassPlatformHandle();
     CHECK(h.is_valid());
     dispatcher->Close();
 
@@ -447,7 +448,7 @@
       public testing::WithParamInterface<size_t> {};
 
 TEST_P(MultiprocessMessagePipeTestWithPipeCount, PlatformHandlePassing) {
-  mojo::test::ScopedTestDir test_dir;
+  test::ScopedTestDir test_dir;
 
   helper()->StartChild("CheckPlatformHandleFile");
 
@@ -468,7 +469,7 @@
 
     auto dispatcher =
         PlatformHandleDispatcher::Create(embedder::ScopedPlatformHandle(
-            mojo::test::PlatformHandleFromFILE(fp.Pass())));
+            mojo::test::PlatformHandleFromFILE(std::move(fp))));
     dispatchers.push_back(dispatcher);
     DispatcherTransport transport(
         test::DispatcherTryStartTransport(dispatcher.get()));
@@ -485,7 +486,7 @@
 
   for (size_t i = 0; i < pipe_count; ++i) {
     transports[i].End();
-    dispatchers[i]->AssertHasOneRef();
+    EXPECT_TRUE(dispatchers[i]->HasOneRef());
   }
 
   dispatchers.clear();
diff --git a/mojo/edk/system/mutex.cc b/mojo/edk/system/mutex.cc
deleted file mode 100644
index 216b35a..0000000
--- a/mojo/edk/system/mutex.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/edk/system/mutex.h"
-
-#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
-
-#include "base/logging.h"
-
-namespace mojo {
-namespace system {
-
-Mutex::Mutex() : lock_() {
-}
-
-Mutex::~Mutex() {
-  DCHECK(owning_thread_ref_.is_null());
-}
-
-void Mutex::AssertHeld() const {
-  DCHECK(owning_thread_ref_ == base::PlatformThread::CurrentRef());
-}
-
-void Mutex::CheckHeldAndUnmark() {
-  DCHECK(owning_thread_ref_ == base::PlatformThread::CurrentRef());
-  owning_thread_ref_ = base::PlatformThreadRef();
-}
-
-void Mutex::CheckUnheldAndMark() {
-  DCHECK(owning_thread_ref_.is_null());
-  owning_thread_ref_ = base::PlatformThread::CurrentRef();
-}
-
-}  // namespace system
-}  // namespace mojo
-
-#endif  // !NDEBUG || DCHECK_ALWAYS_ON
diff --git a/mojo/edk/system/mutex.h b/mojo/edk/system/mutex.h
deleted file mode 100644
index 859f6a3..0000000
--- a/mojo/edk/system/mutex.h
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// A mutex class, with support for thread annotations.
-//
-// TODO(vtl): Currently, this is a fork of Chromium's
-// base/synchronization/lock.h (with names changed and minor modifications; it
-// still cheats and uses Chromium's lock_impl.*), but eventually we'll want our
-// own and, e.g., add support for non-exclusive (reader) locks.
-
-#ifndef MOJO_EDK_SYSTEM_MUTEX_H_
-#define MOJO_EDK_SYSTEM_MUTEX_H_
-
-#include "base/synchronization/lock_impl.h"
-#include "base/threading/platform_thread.h"
-#include "mojo/edk/system/thread_annotations.h"
-#include "mojo/public/cpp/system/macros.h"
-
-namespace mojo {
-namespace system {
-
-// Mutex -----------------------------------------------------------------------
-
-class MOJO_LOCKABLE Mutex {
- public:
-#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
-  Mutex() : lock_() {}
-  ~Mutex() {}
-
-  void Lock() MOJO_EXCLUSIVE_LOCK_FUNCTION() { lock_.Lock(); }
-  void Unlock() MOJO_UNLOCK_FUNCTION() { lock_.Unlock(); }
-
-  bool TryLock() MOJO_EXCLUSIVE_TRYLOCK_FUNCTION(true) { return lock_.Try(); }
-
-  void AssertHeld() const MOJO_ASSERT_EXCLUSIVE_LOCK() {}
-#else
-  Mutex();
-  ~Mutex();
-
-  void Lock() MOJO_EXCLUSIVE_LOCK_FUNCTION() {
-    lock_.Lock();
-    CheckUnheldAndMark();
-  }
-  void Unlock() MOJO_UNLOCK_FUNCTION() {
-    CheckHeldAndUnmark();
-    lock_.Unlock();
-  }
-
-  bool TryLock() MOJO_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
-    bool rv = lock_.Try();
-    if (rv)
-      CheckUnheldAndMark();
-    return rv;
-  }
-
-  void AssertHeld() const MOJO_ASSERT_EXCLUSIVE_LOCK();
-#endif  // NDEBUG && !DCHECK_ALWAYS_ON
-
- private:
-#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
-  void CheckHeldAndUnmark();
-  void CheckUnheldAndMark();
-
-  base::PlatformThreadRef owning_thread_ref_;
-#endif  // !NDEBUG || DCHECK_ALWAYS_ON
-
-  base::internal::LockImpl lock_;
-
-  MOJO_DISALLOW_COPY_AND_ASSIGN(Mutex);
-};
-
-// MutexLocker -----------------------------------------------------------------
-
-class MOJO_SCOPED_LOCKABLE MutexLocker {
- public:
-  explicit MutexLocker(Mutex* mutex) MOJO_EXCLUSIVE_LOCK_FUNCTION(mutex)
-      : mutex_(mutex) {
-    this->mutex_->Lock();
-  }
-  ~MutexLocker() MOJO_UNLOCK_FUNCTION() { this->mutex_->Unlock(); }
-
- private:
-  Mutex* const mutex_;
-
-  MOJO_DISALLOW_COPY_AND_ASSIGN(MutexLocker);
-};
-
-}  // namespace system
-}  // namespace mojo
-
-#endif  // MOJO_EDK_SYSTEM_MUTEX_H_
diff --git a/mojo/edk/system/mutex_unittest.cc b/mojo/edk/system/mutex_unittest.cc
deleted file mode 100644
index 9b51653..0000000
--- a/mojo/edk/system/mutex_unittest.cc
+++ /dev/null
@@ -1,250 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/edk/system/mutex.h"
-
-#include <stdlib.h>
-
-#include "base/threading/platform_thread.h"
-#include "mojo/edk/system/test_utils.h"
-#include "mojo/public/cpp/system/macros.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace mojo {
-namespace system {
-namespace {
-
-// Sleeps for a "very small" amount of time.
-void EpsilonRandomSleep() {
-  test::Sleep(test::DeadlineFromMilliseconds(rand() % 20));
-}
-
-// Basic test to make sure that Lock()/Unlock()/TryLock() don't crash ----------
-
-class BasicMutexTestThread : public base::PlatformThread::Delegate {
- public:
-  explicit BasicMutexTestThread(Mutex* mutex) : mutex_(mutex), acquired_(0) {}
-
-  void ThreadMain() override {
-    for (int i = 0; i < 10; i++) {
-      mutex_->Lock();
-      mutex_->AssertHeld();
-      acquired_++;
-      mutex_->Unlock();
-    }
-    for (int i = 0; i < 10; i++) {
-      mutex_->Lock();
-      mutex_->AssertHeld();
-      acquired_++;
-      EpsilonRandomSleep();
-      mutex_->Unlock();
-    }
-    for (int i = 0; i < 10; i++) {
-      if (mutex_->TryLock()) {
-        mutex_->AssertHeld();
-        acquired_++;
-        EpsilonRandomSleep();
-        mutex_->Unlock();
-      }
-    }
-  }
-
-  int acquired() const { return acquired_; }
-
- private:
-  Mutex* mutex_;
-  int acquired_;
-
-  MOJO_DISALLOW_COPY_AND_ASSIGN(BasicMutexTestThread);
-};
-
-TEST(MutexTest, Basic) {
-  Mutex mutex;
-  BasicMutexTestThread thread(&mutex);
-  base::PlatformThreadHandle handle;
-
-  ASSERT_TRUE(base::PlatformThread::Create(0, &thread, &handle));
-
-  int acquired = 0;
-  for (int i = 0; i < 5; i++) {
-    mutex.Lock();
-    mutex.AssertHeld();
-    acquired++;
-    mutex.Unlock();
-  }
-  for (int i = 0; i < 10; i++) {
-    mutex.Lock();
-    mutex.AssertHeld();
-    acquired++;
-    EpsilonRandomSleep();
-    mutex.Unlock();
-  }
-  for (int i = 0; i < 10; i++) {
-    if (mutex.TryLock()) {
-      mutex.AssertHeld();
-      acquired++;
-      EpsilonRandomSleep();
-      mutex.Unlock();
-    }
-  }
-  for (int i = 0; i < 5; i++) {
-    mutex.Lock();
-    mutex.AssertHeld();
-    acquired++;
-    EpsilonRandomSleep();
-    mutex.Unlock();
-  }
-
-  base::PlatformThread::Join(handle);
-
-  EXPECT_GE(acquired, 20);
-  EXPECT_GE(thread.acquired(), 20);
-}
-
-// Test that TryLock() works as expected ---------------------------------------
-
-class TryLockTestThread : public base::PlatformThread::Delegate {
- public:
-  explicit TryLockTestThread(Mutex* mutex) : mutex_(mutex), got_lock_(false) {}
-
-  void ThreadMain() override MOJO_NO_THREAD_SAFETY_ANALYSIS {
-    got_lock_ = mutex_->TryLock();
-    if (got_lock_) {
-      mutex_->AssertHeld();
-      mutex_->Unlock();
-    }
-  }
-
-  bool got_lock() const { return got_lock_; }
-
- private:
-  Mutex* mutex_;
-  bool got_lock_;
-
-  MOJO_DISALLOW_COPY_AND_ASSIGN(TryLockTestThread);
-};
-
-TEST(MutexTest, TryLock) MOJO_NO_THREAD_SAFETY_ANALYSIS {
-  Mutex mutex;
-
-  ASSERT_TRUE(mutex.TryLock());
-  // We now have the mutex....
-
-  // This thread will not be able to get the mutex.
-  {
-    TryLockTestThread thread(&mutex);
-    base::PlatformThreadHandle handle;
-
-    ASSERT_TRUE(base::PlatformThread::Create(0, &thread, &handle));
-
-    base::PlatformThread::Join(handle);
-
-    ASSERT_FALSE(thread.got_lock());
-  }
-
-  mutex.Unlock();
-
-  // This thread will....
-  {
-    TryLockTestThread thread(&mutex);
-    base::PlatformThreadHandle handle;
-
-    ASSERT_TRUE(base::PlatformThread::Create(0, &thread, &handle));
-
-    base::PlatformThread::Join(handle);
-
-    ASSERT_TRUE(thread.got_lock());
-    // But it released it....
-    ASSERT_TRUE(mutex.TryLock());
-  }
-
-  mutex.Unlock();
-}
-
-// Tests that mutexes actually exclude -----------------------------------------
-
-class MutexLockTestThread : public base::PlatformThread::Delegate {
- public:
-  MutexLockTestThread(Mutex* mutex, int* value)
-      : mutex_(mutex), value_(value) {}
-
-  // Static helper which can also be called from the main thread.
-  static void DoStuff(Mutex* mutex, int* value) {
-    for (int i = 0; i < 40; i++) {
-      mutex->Lock();
-      int v = *value;
-      EpsilonRandomSleep();
-      *value = v + 1;
-      mutex->Unlock();
-    }
-  }
-
-  void ThreadMain() override { DoStuff(mutex_, value_); }
-
- private:
-  Mutex* mutex_;
-  int* value_;
-
-  MOJO_DISALLOW_COPY_AND_ASSIGN(MutexLockTestThread);
-};
-
-TEST(MutexTest, MutexTwoThreads) {
-  Mutex mutex;
-  int value = 0;
-
-  MutexLockTestThread thread(&mutex, &value);
-  base::PlatformThreadHandle handle;
-
-  ASSERT_TRUE(base::PlatformThread::Create(0, &thread, &handle));
-
-  MutexLockTestThread::DoStuff(&mutex, &value);
-
-  base::PlatformThread::Join(handle);
-
-  EXPECT_EQ(2 * 40, value);
-}
-
-TEST(MutexTest, MutexFourThreads) {
-  Mutex mutex;
-  int value = 0;
-
-  MutexLockTestThread thread1(&mutex, &value);
-  MutexLockTestThread thread2(&mutex, &value);
-  MutexLockTestThread thread3(&mutex, &value);
-  base::PlatformThreadHandle handle1;
-  base::PlatformThreadHandle handle2;
-  base::PlatformThreadHandle handle3;
-
-  ASSERT_TRUE(base::PlatformThread::Create(0, &thread1, &handle1));
-  ASSERT_TRUE(base::PlatformThread::Create(0, &thread2, &handle2));
-  ASSERT_TRUE(base::PlatformThread::Create(0, &thread3, &handle3));
-
-  MutexLockTestThread::DoStuff(&mutex, &value);
-
-  base::PlatformThread::Join(handle1);
-  base::PlatformThread::Join(handle2);
-  base::PlatformThread::Join(handle3);
-
-  EXPECT_EQ(4 * 40, value);
-}
-
-// MutexLocker -----------------------------------------------------------------
-
-TEST(MutexTest, MutexLocker) {
-  Mutex mutex;
-
-  {
-    MutexLocker locker(&mutex);
-    mutex.AssertHeld();
-  }
-
-  // The destruction of |locker| should unlock |mutex|.
-  ASSERT_TRUE(mutex.TryLock());
-  mutex.AssertHeld();
-  mutex.Unlock();
-}
-
-}  // namespace
-}  // namespace system
-}  // namespace mojo
diff --git a/mojo/edk/system/options_validation.h b/mojo/edk/system/options_validation.h
index 35d81f9..bd904f5 100644
--- a/mojo/edk/system/options_validation.h
+++ b/mojo/edk/system/options_validation.h
@@ -14,7 +14,6 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <algorithm>
 #include <type_traits>
 
 #include "base/logging.h"
@@ -38,15 +37,15 @@
   // Note: We initialize |options_reader_| without checking, since we do a check
   // in |GetSizeForReader()|.
   explicit UserOptionsReader(UserPointer<const Options> options)
-      : options_reader_(UserPointer<const char>::Reader::NoCheck(),
-                        options.template ReinterpretCast<const char>(),
-                        GetSizeForReader(options)) {}
+      : options_reader_(options, GetSizeForReader(options)) {}
 
-  bool is_valid() const { return !!options_reader_.GetPointer(); }
+  bool is_valid() const {
+    return options_reader_.GetPointer()->struct_size >= sizeof(uint32_t);
+  }
 
   const Options& options() const {
     DCHECK(is_valid());
-    return *reinterpret_cast<const Options*>(options_reader_.GetPointer());
+    return *options_reader_.GetPointer();
   }
 
   // Checks that the given (variable-size) |options| passed to the constructor
@@ -64,20 +63,17 @@
   static inline size_t GetSizeForReader(UserPointer<const Options> options) {
     uint32_t struct_size =
         options.template ReinterpretCast<const uint32_t>().Get();
+    // Note: |PartialReader| will clear memory, so |is_valid()| will return
+    // false in this case.
     if (struct_size < sizeof(uint32_t))
       return 0;
 
-    // Check the full requested size.
-    // Note: Use |MOJO_ALIGNOF()| here to match the exact macro used in the
-    // declaration of Options structs.
-    internal::CheckUserPointerWithSize<MOJO_ALIGNOF(Options)>(options.pointer_,
-                                                              struct_size);
-    options.template ReinterpretCast<const char>().CheckArray(struct_size);
-    // But we'll never look at more than |sizeof(Options)| bytes.
-    return std::min(static_cast<size_t>(struct_size), sizeof(Options));
+    // |PartialReader|'s constructor will automatically limit the amount copied
+    // to |sizeof(Options)|.
+    return struct_size;
   }
 
-  UserPointer<const char>::Reader options_reader_;
+  typename UserPointer<const Options>::PartialReader options_reader_;
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(UserOptionsReader);
 };
diff --git a/mojo/edk/system/platform_handle_dispatcher.cc b/mojo/edk/system/platform_handle_dispatcher.cc
index f9a9f96..91c64fd 100644
--- a/mojo/edk/system/platform_handle_dispatcher.cc
+++ b/mojo/edk/system/platform_handle_dispatcher.cc
@@ -8,6 +8,9 @@
 
 #include "base/logging.h"
 
+using mojo::util::MutexLocker;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/platform_handle_dispatcher.h b/mojo/edk/system/platform_handle_dispatcher.h
index db2f0cf..d9b809f 100644
--- a/mojo/edk/system/platform_handle_dispatcher.h
+++ b/mojo/edk/system/platform_handle_dispatcher.h
@@ -6,8 +6,9 @@
 #define MOJO_EDK_SYSTEM_PLATFORM_HANDLE_DISPATCHER_H_
 
 #include "mojo/edk/embedder/scoped_platform_handle.h"
-#include "mojo/edk/system/ref_ptr.h"
 #include "mojo/edk/system/simple_dispatcher.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -17,7 +18,7 @@
 // the embedder).
 class PlatformHandleDispatcher final : public SimpleDispatcher {
  public:
-  static RefPtr<PlatformHandleDispatcher> Create(
+  static util::RefPtr<PlatformHandleDispatcher> Create(
       embedder::ScopedPlatformHandle platform_handle) {
     return AdoptRef(new PlatformHandleDispatcher(platform_handle.Pass()));
   }
@@ -29,7 +30,7 @@
 
   // The "opposite" of |SerializeAndClose()|. (Typically this is called by
   // |Dispatcher::Deserialize()|.)
-  static RefPtr<PlatformHandleDispatcher> Deserialize(
+  static util::RefPtr<PlatformHandleDispatcher> Deserialize(
       Channel* channel,
       const void* source,
       size_t size,
@@ -42,7 +43,8 @@
 
   // |Dispatcher| protected methods:
   void CloseImplNoLock() override;
-  RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock() override;
+  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock()
+      override;
   void StartSerializeImplNoLock(Channel* channel,
                                 size_t* max_size,
                                 size_t* max_platform_handles) override
diff --git a/mojo/edk/system/platform_handle_dispatcher_unittest.cc b/mojo/edk/system/platform_handle_dispatcher_unittest.cc
index 7d70d75..6564e7c 100644
--- a/mojo/edk/system/platform_handle_dispatcher_unittest.cc
+++ b/mojo/edk/system/platform_handle_dispatcher_unittest.cc
@@ -6,18 +6,21 @@
 
 #include <stdio.h>
 
-#include "base/memory/ref_counted.h"
-#include "mojo/edk/test/scoped_test_dir.h"
+#include <utility>
+
+#include "mojo/edk/system/test/scoped_test_dir.h"
 #include "mojo/edk/test/test_utils.h"
 #include "mojo/edk/util/scoped_file.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 namespace {
 
 TEST(PlatformHandleDispatcherTest, Basic) {
-  mojo::test::ScopedTestDir test_dir;
+  test::ScopedTestDir test_dir;
 
   static const char kHelloWorld[] = "hello world";
 
@@ -27,7 +30,7 @@
             fwrite(kHelloWorld, 1, sizeof(kHelloWorld), fp.get()));
 
   embedder::ScopedPlatformHandle h(
-      mojo::test::PlatformHandleFromFILE(fp.Pass()));
+      mojo::test::PlatformHandleFromFILE(std::move(fp)));
   EXPECT_FALSE(fp);
   ASSERT_TRUE(h.is_valid());
 
@@ -35,10 +38,10 @@
   EXPECT_FALSE(h.is_valid());
   EXPECT_EQ(Dispatcher::Type::PLATFORM_HANDLE, dispatcher->GetType());
 
-  h = dispatcher->PassPlatformHandle().Pass();
+  h = dispatcher->PassPlatformHandle();
   EXPECT_TRUE(h.is_valid());
 
-  fp = mojo::test::FILEFromPlatformHandle(h.Pass(), "rb").Pass();
+  fp = mojo::test::FILEFromPlatformHandle(h.Pass(), "rb");
   EXPECT_FALSE(h.is_valid());
   EXPECT_TRUE(fp);
 
@@ -49,14 +52,14 @@
   EXPECT_STREQ(kHelloWorld, read_buffer);
 
   // Try getting the handle again. (It should fail cleanly.)
-  h = dispatcher->PassPlatformHandle().Pass();
+  h = dispatcher->PassPlatformHandle();
   EXPECT_FALSE(h.is_valid());
 
   EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
 }
 
 TEST(PlatformHandleDispatcherTest, CreateEquivalentDispatcherAndClose) {
-  mojo::test::ScopedTestDir test_dir;
+  test::ScopedTestDir test_dir;
 
   static const char kFooBar[] = "foo bar";
 
@@ -64,7 +67,7 @@
   EXPECT_EQ(sizeof(kFooBar), fwrite(kFooBar, 1, sizeof(kFooBar), fp.get()));
 
   auto dispatcher = PlatformHandleDispatcher::Create(
-      mojo::test::PlatformHandleFromFILE(fp.Pass()));
+      mojo::test::PlatformHandleFromFILE(std::move(fp)));
 
   DispatcherTransport transport(
       test::DispatcherTryStartTransport(dispatcher.get()));
@@ -76,7 +79,7 @@
   ASSERT_TRUE(generic_dispatcher);
 
   transport.End();
-  dispatcher->AssertHasOneRef();
+  EXPECT_TRUE(dispatcher->HasOneRef());
   dispatcher = nullptr;
 
   ASSERT_EQ(Dispatcher::Type::PLATFORM_HANDLE, generic_dispatcher->GetType());
@@ -84,7 +87,7 @@
       static_cast<PlatformHandleDispatcher*>(generic_dispatcher.get()));
 
   fp = mojo::test::FILEFromPlatformHandle(dispatcher->PassPlatformHandle(),
-                                          "rb").Pass();
+                                          "rb");
   EXPECT_TRUE(fp);
 
   rewind(fp.get());
diff --git a/mojo/edk/system/proxy_message_pipe_endpoint.cc b/mojo/edk/system/proxy_message_pipe_endpoint.cc
index c8a6961..00937a7 100644
--- a/mojo/edk/system/proxy_message_pipe_endpoint.cc
+++ b/mojo/edk/system/proxy_message_pipe_endpoint.cc
@@ -13,6 +13,8 @@
 #include "mojo/edk/system/local_message_pipe_endpoint.h"
 #include "mojo/edk/system/message_pipe_dispatcher.h"
 
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/proxy_message_pipe_endpoint.h b/mojo/edk/system/proxy_message_pipe_endpoint.h
index ac760cd..24d06e3 100644
--- a/mojo/edk/system/proxy_message_pipe_endpoint.h
+++ b/mojo/edk/system/proxy_message_pipe_endpoint.h
@@ -7,7 +7,7 @@
 
 #include "mojo/edk/system/message_in_transit.h"
 #include "mojo/edk/system/message_pipe_endpoint.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -28,7 +28,8 @@
 // a |MessagePipeDispatcher|.
 class ProxyMessagePipeEndpoint final : public MessagePipeEndpoint {
  public:
-  explicit ProxyMessagePipeEndpoint(RefPtr<ChannelEndpoint>&& channel_endpoint);
+  explicit ProxyMessagePipeEndpoint(
+      util::RefPtr<ChannelEndpoint>&& channel_endpoint);
   ~ProxyMessagePipeEndpoint() override;
 
   // Returns |channel_endpoint_| and resets |channel_endpoint_| to null. This
@@ -37,7 +38,7 @@
   // Note: The returned |ChannelEndpoint| must have its client changed while
   // still under |MessagePipe|'s lock (which this must have also been called
   // under).
-  RefPtr<ChannelEndpoint> ReleaseChannelEndpoint();
+  util::RefPtr<ChannelEndpoint> ReleaseChannelEndpoint();
 
   // |MessagePipeEndpoint| implementation:
   Type GetType() const override;
@@ -48,7 +49,7 @@
  private:
   void DetachIfNecessary();
 
-  RefPtr<ChannelEndpoint> channel_endpoint_;
+  util::RefPtr<ChannelEndpoint> channel_endpoint_;
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(ProxyMessagePipeEndpoint);
 };
diff --git a/mojo/edk/system/raw_channel.cc b/mojo/edk/system/raw_channel.cc
index 9c1aa24..16f924d 100644
--- a/mojo/edk/system/raw_channel.cc
+++ b/mojo/edk/system/raw_channel.cc
@@ -16,6 +16,8 @@
 #include "mojo/edk/system/message_in_transit.h"
 #include "mojo/edk/system/transport_data.h"
 
+using mojo::util::MutexLocker;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/raw_channel.h b/mojo/edk/system/raw_channel.h
index 6a78395..beb0194 100644
--- a/mojo/edk/system/raw_channel.h
+++ b/mojo/edk/system/raw_channel.h
@@ -13,8 +13,8 @@
 #include "mojo/edk/embedder/scoped_platform_handle.h"
 #include "mojo/edk/system/message_in_transit.h"
 #include "mojo/edk/system/message_in_transit_queue.h"
-#include "mojo/edk/system/mutex.h"
-#include "mojo/edk/system/thread_annotations.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace base {
@@ -211,7 +211,9 @@
                         size_t bytes_written) MOJO_LOCKS_EXCLUDED(write_mutex_);
 
   base::MessageLoopForIO* message_loop_for_io() { return message_loop_for_io_; }
-  Mutex& write_mutex() MOJO_LOCK_RETURNED(write_mutex_) { return write_mutex_; }
+  util::Mutex& write_mutex() MOJO_LOCK_RETURNED(write_mutex_) {
+    return write_mutex_;
+  }
 
   // Should only be called on the I/O thread.
   ReadBuffer* read_buffer() { return read_buffer_.get(); }
@@ -319,7 +321,7 @@
   bool* set_on_shutdown_;
   std::unique_ptr<ReadBuffer> read_buffer_;
 
-  Mutex write_mutex_;  // Protects the following members.
+  util::Mutex write_mutex_;  // Protects the following members.
   bool write_stopped_ MOJO_GUARDED_BY(write_mutex_);
   std::unique_ptr<WriteBuffer> write_buffer_ MOJO_GUARDED_BY(write_mutex_);
 
diff --git a/mojo/edk/system/raw_channel_posix.cc b/mojo/edk/system/raw_channel_posix.cc
index d740516..096d0d7 100644
--- a/mojo/edk/system/raw_channel_posix.cc
+++ b/mojo/edk/system/raw_channel_posix.cc
@@ -18,13 +18,15 @@
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop.h"
-#include "mojo/edk/embedder/platform_channel_utils_posix.h"
+#include "mojo/edk/embedder/platform_channel_utils.h"
 #include "mojo/edk/embedder/platform_handle.h"
 #include "mojo/edk/embedder/platform_handle_vector.h"
 #include "mojo/edk/system/transport_data.h"
 #include "mojo/edk/util/make_unique.h"
 #include "mojo/public/cpp/system/macros.h"
 
+using mojo::util::MutexLocker;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/raw_channel_unittest.cc b/mojo/edk/system/raw_channel_unittest.cc
index 53e8688..0c18226 100644
--- a/mojo/edk/system/raw_channel_unittest.cc
+++ b/mojo/edk/system/raw_channel_unittest.cc
@@ -13,23 +13,27 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
-#include "base/synchronization/waitable_event.h"
 #include "mojo/edk/embedder/platform_channel_pair.h"
 #include "mojo/edk/embedder/platform_handle.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
 #include "mojo/edk/system/message_in_transit.h"
-#include "mojo/edk/system/mutex.h"
-#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/test/random.h"
+#include "mojo/edk/system/test/scoped_test_dir.h"
+#include "mojo/edk/system/test/simple_test_thread.h"
+#include "mojo/edk/system/test/sleep.h"
+#include "mojo/edk/system/test/test_io_thread.h"
 #include "mojo/edk/system/transport_data.h"
-#include "mojo/edk/test/scoped_test_dir.h"
-#include "mojo/edk/test/simple_test_thread.h"
-#include "mojo/edk/test/test_io_thread.h"
+#include "mojo/edk/system/waitable_event.h"
 #include "mojo/edk/test/test_utils.h"
 #include "mojo/edk/util/make_unique.h"
+#include "mojo/edk/util/mutex.h"
 #include "mojo/edk/util/scoped_file.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using mojo::util::Mutex;
+using mojo::util::MutexLocker;
+
 namespace mojo {
 namespace system {
 namespace {
@@ -71,7 +75,7 @@
 
 class RawChannelTest : public testing::Test {
  public:
-  RawChannelTest() : io_thread_(mojo::test::TestIOThread::StartMode::MANUAL) {}
+  RawChannelTest() : io_thread_(test::TestIOThread::StartMode::MANUAL) {}
   ~RawChannelTest() override {}
 
   void SetUp() override {
@@ -88,12 +92,12 @@
   }
 
  protected:
-  mojo::test::TestIOThread* io_thread() { return &io_thread_; }
+  test::TestIOThread* io_thread() { return &io_thread_; }
 
   embedder::ScopedPlatformHandle handles[2];
 
  private:
-  mojo::test::TestIOThread io_thread_;
+  test::TestIOThread io_thread_;
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(RawChannelTest);
 };
@@ -170,7 +174,7 @@
 
       if (static_cast<size_t>(read_size) < sizeof(buffer)) {
         i++;
-        test::Sleep(test::DeadlineFromMilliseconds(kMessageReaderSleepMs));
+        test::SleepMilliseconds(kMessageReaderSleepMs);
       }
     }
 
@@ -215,7 +219,7 @@
 
 class ReadCheckerRawChannelDelegate : public RawChannel::Delegate {
  public:
-  ReadCheckerRawChannelDelegate() : done_event_(false, false), position_(0) {}
+  ReadCheckerRawChannelDelegate() : position_(0) {}
   ~ReadCheckerRawChannelDelegate() override {}
 
   // |RawChannel::Delegate| implementation (called on the I/O thread):
@@ -263,7 +267,7 @@
   }
 
  private:
-  base::WaitableEvent done_event_;
+  AutoResetWaitableEvent done_event_;
 
   Mutex mutex_;
   std::vector<uint32_t> expected_sizes_ MOJO_GUARDED_BY(mutex_);
@@ -304,7 +308,7 @@
 
 // RawChannelTest.WriteMessageAndOnReadMessage ---------------------------------
 
-class RawChannelWriterThread : public mojo::test::SimpleTestThread {
+class RawChannelWriterThread : public test::SimpleTestThread {
  public:
   RawChannelWriterThread(RawChannel* raw_channel, size_t write_count)
       : raw_channel_(raw_channel), left_to_write_(write_count) {}
@@ -330,7 +334,7 @@
 class ReadCountdownRawChannelDelegate : public RawChannel::Delegate {
  public:
   explicit ReadCountdownRawChannelDelegate(size_t expected_count)
-      : done_event_(false, false), expected_count_(expected_count), count_(0) {}
+      : expected_count_(expected_count), count_(0) {}
   ~ReadCountdownRawChannelDelegate() override {}
 
   // |RawChannel::Delegate| implementation (called on the I/O thread):
@@ -357,7 +361,7 @@
   void Wait() { done_event_.Wait(); }
 
  private:
-  base::WaitableEvent done_event_;
+  AutoResetWaitableEvent done_event_;
   size_t expected_count_;
   size_t count_;
 
@@ -391,7 +395,7 @@
 
   // Sleep a bit, to let any extraneous reads be processed. (There shouldn't be
   // any, but we want to know about them.)
-  test::Sleep(test::DeadlineFromMilliseconds(100));
+  test::SleepMilliseconds(100u);
 
   // Wait for reading to finish.
   reader_delegate.Wait();
@@ -412,8 +416,6 @@
                                    bool expect_read_error,
                                    bool expect_write_error)
       : ReadCountdownRawChannelDelegate(expected_read_count),
-        got_read_error_event_(false, false),
-        got_write_error_event_(false, false),
         expecting_read_error_(expect_read_error),
         expecting_write_error_(expect_write_error) {}
 
@@ -450,8 +452,8 @@
   void WaitForWriteError() { got_write_error_event_.Wait(); }
 
  private:
-  base::WaitableEvent got_read_error_event_;
-  base::WaitableEvent got_write_error_event_;
+  AutoResetWaitableEvent got_read_error_event_;
+  AutoResetWaitableEvent got_write_error_event_;
 
   bool expecting_read_error_;
   bool expecting_write_error_;
@@ -481,7 +483,7 @@
 
   // Sleep a bit, to make sure we don't get another |OnError()|
   // notification. (If we actually get another one, |OnError()| crashes.)
-  test::Sleep(test::DeadlineFromMilliseconds(20));
+  test::SleepMilliseconds(20u);
 
   io_thread()->PostTaskAndWait(
       base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get())));
@@ -546,7 +548,6 @@
                                                    bool should_destroy)
       : raw_channel_(raw_channel),
         should_destroy_(should_destroy),
-        done_event_(false, false),
         did_shutdown_(false) {}
   ~ShutdownOnReadMessageRawChannelDelegate() override {}
 
@@ -577,7 +578,7 @@
  private:
   RawChannel* const raw_channel_;
   const bool should_destroy_;
-  base::WaitableEvent done_event_;
+  AutoResetWaitableEvent done_event_;
   bool did_shutdown_;
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(ShutdownOnReadMessageRawChannelDelegate);
@@ -621,7 +622,6 @@
       : raw_channel_(raw_channel),
         should_destroy_(should_destroy),
         shutdown_on_error_type_(shutdown_on_error_type),
-        done_event_(false, false),
         did_shutdown_(false) {}
   ~ShutdownOnErrorRawChannelDelegate() override {}
 
@@ -652,7 +652,7 @@
   RawChannel* const raw_channel_;
   const bool should_destroy_;
   const Error shutdown_on_error_type_;
-  base::WaitableEvent done_event_;
+  AutoResetWaitableEvent done_event_;
   bool did_shutdown_;
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(ShutdownOnErrorRawChannelDelegate);
@@ -723,7 +723,7 @@
 class ReadPlatformHandlesCheckerRawChannelDelegate
     : public RawChannel::Delegate {
  public:
-  ReadPlatformHandlesCheckerRawChannelDelegate() : done_event_(false, false) {}
+  ReadPlatformHandlesCheckerRawChannelDelegate() {}
   ~ReadPlatformHandlesCheckerRawChannelDelegate() override {}
 
   // |RawChannel::Delegate| implementation (called on the I/O thread):
@@ -772,13 +772,13 @@
   void Wait() { done_event_.Wait(); }
 
  private:
-  base::WaitableEvent done_event_;
+  AutoResetWaitableEvent done_event_;
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(ReadPlatformHandlesCheckerRawChannelDelegate);
 };
 
 TEST_F(RawChannelTest, ReadWritePlatformHandles) {
-  mojo::test::ScopedTestDir test_dir;
+  test::ScopedTestDir test_dir;
 
   WriteOnlyRawChannelDelegate write_delegate;
   std::unique_ptr<RawChannel> rc_write(RawChannel::Create(handles[0].Pass()));
@@ -800,9 +800,9 @@
     embedder::ScopedPlatformHandleVectorPtr platform_handles(
         new embedder::PlatformHandleVector());
     platform_handles->push_back(
-        mojo::test::PlatformHandleFromFILE(fp1.Pass()).release());
+        mojo::test::PlatformHandleFromFILE(std::move(fp1)).release());
     platform_handles->push_back(
-        mojo::test::PlatformHandleFromFILE(fp2.Pass()).release());
+        mojo::test::PlatformHandleFromFILE(std::move(fp2)).release());
 
     std::unique_ptr<MessageInTransit> message(
         new MessageInTransit(MessageInTransit::Type::ENDPOINT_CLIENT,
diff --git a/mojo/edk/system/remote_consumer_data_pipe_impl.cc b/mojo/edk/system/remote_consumer_data_pipe_impl.cc
index d71a3de..b54b0fa 100644
--- a/mojo/edk/system/remote_consumer_data_pipe_impl.cc
+++ b/mojo/edk/system/remote_consumer_data_pipe_impl.cc
@@ -18,6 +18,8 @@
 #include "mojo/edk/system/message_in_transit.h"
 #include "mojo/edk/system/remote_data_pipe_ack.h"
 
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/remote_consumer_data_pipe_impl.h b/mojo/edk/system/remote_consumer_data_pipe_impl.h
index f05de1b..20a3f04 100644
--- a/mojo/edk/system/remote_consumer_data_pipe_impl.h
+++ b/mojo/edk/system/remote_consumer_data_pipe_impl.h
@@ -10,7 +10,7 @@
 #include "base/memory/aligned_memory.h"
 #include "mojo/edk/system/channel_endpoint.h"
 #include "mojo/edk/system/data_pipe_impl.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -25,7 +25,7 @@
   // is nonzero (i.e., if we're in the middle of a two-phase write when the
   // consumer handle is transferred); |start_index| is ignored if it is zero.
   RemoteConsumerDataPipeImpl(
-      RefPtr<ChannelEndpoint>&& channel_endpoint,
+      util::RefPtr<ChannelEndpoint>&& channel_endpoint,
       size_t consumer_num_bytes,
       std::unique_ptr<char, base::AlignedFreeDeleter> buffer,
       size_t start_index);
@@ -95,7 +95,7 @@
   void Disconnect();
 
   // Should be valid if and only if |consumer_open()| returns true.
-  RefPtr<ChannelEndpoint> channel_endpoint_;
+  util::RefPtr<ChannelEndpoint> channel_endpoint_;
 
   // The number of bytes we've sent the consumer, but don't *know* have been
   // consumed.
diff --git a/mojo/edk/system/remote_data_pipe_impl_unittest.cc b/mojo/edk/system/remote_data_pipe_impl_unittest.cc
index 60ceb45..242bf0e 100644
--- a/mojo/edk/system/remote_data_pipe_impl_unittest.cc
+++ b/mojo/edk/system/remote_data_pipe_impl_unittest.cc
@@ -22,13 +22,16 @@
 #include "mojo/edk/system/memory.h"
 #include "mojo/edk/system/message_pipe.h"
 #include "mojo/edk/system/raw_channel.h"
-#include "mojo/edk/system/ref_ptr.h"
-#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/test/test_io_thread.h"
+#include "mojo/edk/system/test/timeouts.h"
 #include "mojo/edk/system/waiter.h"
-#include "mojo/edk/test/test_io_thread.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using mojo::util::MakeRefCounted;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 namespace {
@@ -39,8 +42,7 @@
 
 class RemoteDataPipeImplTest : public testing::Test {
  public:
-  RemoteDataPipeImplTest()
-      : io_thread_(mojo::test::TestIOThread::StartMode::AUTO) {}
+  RemoteDataPipeImplTest() : io_thread_(test::TestIOThread::StartMode::AUTO) {}
   ~RemoteDataPipeImplTest() override {}
 
   void SetUp() override {
@@ -114,7 +116,7 @@
   }
 
   embedder::SimplePlatformSupport platform_support_;
-  mojo::test::TestIOThread io_thread_;
+  test::TestIOThread io_thread_;
   RefPtr<Channel> channels_[2];
   RefPtr<MessagePipe> message_pipes_[2];
 
@@ -145,7 +147,7 @@
             message_pipe(0)->WriteMessage(0, UserPointer<const void>(kHello),
                                           sizeof(kHello), nullptr,
                                           MOJO_WRITE_MESSAGE_FLAG_NONE));
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionDeadline(), &context));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionTimeout(), &context));
   EXPECT_EQ(123u, context);
   hss = HandleSignalsState();
   message_pipe(1)->RemoveAwakable(0, &waiter, &hss);
@@ -204,10 +206,10 @@
 
     // |consumer| should have been closed. This is |DCHECK()|ed when it is
     // destroyed.
-    consumer->AssertHasOneRef();
+    EXPECT_TRUE(consumer->HasOneRef());
     consumer = nullptr;
   }
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionDeadline(), &context));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionTimeout(), &context));
   EXPECT_EQ(123u, context);
   hss = HandleSignalsState();
   message_pipe(1)->RemoveAwakable(0, &waiter, &hss);
@@ -223,7 +225,7 @@
   EXPECT_EQ(1u, read_dispatchers.size());
   EXPECT_EQ(1u, read_num_dispatchers);
   ASSERT_TRUE(read_dispatchers[0]);
-  read_dispatchers[0]->AssertHasOneRef();
+  EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
 
   EXPECT_EQ(Dispatcher::Type::DATA_PIPE_CONSUMER,
             read_dispatchers[0]->GetType());
@@ -237,7 +239,7 @@
       consumer->AddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 456, &hss);
   if (result == MOJO_RESULT_OK) {
     context = 0;
-    EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionDeadline(), &context));
+    EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionTimeout(), &context));
     EXPECT_EQ(456u, context);
     consumer->RemoveAwakable(&waiter, &hss);
   } else {
@@ -265,7 +267,7 @@
   if (result == MOJO_RESULT_OK) {
     context = 0;
     EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-              waiter.Wait(test::ActionDeadline(), &context));
+              waiter.Wait(test::ActionTimeout(), &context));
     EXPECT_EQ(789u, context);
     consumer->RemoveAwakable(&waiter, &hss);
   } else {
@@ -319,10 +321,10 @@
 
     // |consumer| should have been closed. This is |DCHECK()|ed when it is
     // destroyed.
-    consumer->AssertHasOneRef();
+    EXPECT_TRUE(consumer->HasOneRef());
     consumer = nullptr;
   }
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionDeadline(), &context));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionTimeout(), &context));
   EXPECT_EQ(123u, context);
   hss = HandleSignalsState();
   message_pipe(1)->RemoveAwakable(0, &waiter, &hss);
@@ -338,7 +340,7 @@
   EXPECT_EQ(1u, read_dispatchers.size());
   EXPECT_EQ(1u, read_num_dispatchers);
   ASSERT_TRUE(read_dispatchers[0]);
-  read_dispatchers[0]->AssertHasOneRef();
+  EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
 
   EXPECT_EQ(Dispatcher::Type::DATA_PIPE_CONSUMER,
             read_dispatchers[0]->GetType());
@@ -360,7 +362,7 @@
       consumer->AddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 456, &hss);
   if (result == MOJO_RESULT_OK) {
     context = 0;
-    EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionDeadline(), &context));
+    EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionTimeout(), &context));
     EXPECT_EQ(456u, context);
     consumer->RemoveAwakable(&waiter, &hss);
   } else {
@@ -438,10 +440,10 @@
 
     // |consumer| should have been closed. This is |DCHECK()|ed when it is
     // destroyed.
-    consumer->AssertHasOneRef();
+    EXPECT_TRUE(consumer->HasOneRef());
     consumer = nullptr;
   }
-  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionDeadline(), &context));
+  EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionTimeout(), &context));
   EXPECT_EQ(123u, context);
   hss = HandleSignalsState();
   message_pipe(1)->RemoveAwakable(0, &waiter, &hss);
@@ -457,7 +459,7 @@
   EXPECT_EQ(1u, read_dispatchers.size());
   EXPECT_EQ(1u, read_num_dispatchers);
   ASSERT_TRUE(read_dispatchers[0]);
-  read_dispatchers[0]->AssertHasOneRef();
+  EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
 
   EXPECT_EQ(Dispatcher::Type::DATA_PIPE_CONSUMER,
             read_dispatchers[0]->GetType());
@@ -479,7 +481,7 @@
       consumer->AddAwakable(&waiter, MOJO_HANDLE_SIGNAL_PEER_CLOSED, 456, &hss);
   if (result == MOJO_RESULT_OK) {
     context = 0;
-    EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionDeadline(), &context));
+    EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::ActionTimeout(), &context));
     EXPECT_EQ(456u, context);
     consumer->RemoveAwakable(&waiter, &hss);
   } else {
diff --git a/mojo/edk/system/remote_message_pipe_unittest.cc b/mojo/edk/system/remote_message_pipe_unittest.cc
index 32043ff..a29e382 100644
--- a/mojo/edk/system/remote_message_pipe_unittest.cc
+++ b/mojo/edk/system/remote_message_pipe_unittest.cc
@@ -25,17 +25,21 @@
 #include "mojo/edk/system/message_pipe_dispatcher.h"
 #include "mojo/edk/system/platform_handle_dispatcher.h"
 #include "mojo/edk/system/raw_channel.h"
-#include "mojo/edk/system/ref_ptr.h"
 #include "mojo/edk/system/shared_buffer_dispatcher.h"
-#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/test/scoped_test_dir.h"
+#include "mojo/edk/system/test/sleep.h"
+#include "mojo/edk/system/test/test_io_thread.h"
+#include "mojo/edk/system/test/timeouts.h"
 #include "mojo/edk/system/waiter.h"
-#include "mojo/edk/test/scoped_test_dir.h"
-#include "mojo/edk/test/test_io_thread.h"
 #include "mojo/edk/test/test_utils.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/edk/util/scoped_file.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using mojo::util::MakeRefCounted;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 namespace {
@@ -46,8 +50,7 @@
 
 class RemoteMessagePipeTest : public testing::Test {
  public:
-  RemoteMessagePipeTest()
-      : io_thread_(mojo::test::TestIOThread::StartMode::AUTO) {}
+  RemoteMessagePipeTest() : io_thread_(test::TestIOThread::StartMode::AUTO) {}
   ~RemoteMessagePipeTest() override {}
 
   void SetUp() override {
@@ -89,7 +92,7 @@
   }
 
   embedder::PlatformSupport* platform_support() { return &platform_support_; }
-  mojo::test::TestIOThread* io_thread() { return &io_thread_; }
+  test::TestIOThread* io_thread() { return &io_thread_; }
   // Warning: It's up to the caller to ensure that the returned channel
   // is/remains valid.
   Channel* channels(size_t i) { return channels_[i].get(); }
@@ -160,7 +163,7 @@
   }
 
   embedder::SimplePlatformSupport platform_support_;
-  mojo::test::TestIOThread io_thread_;
+  test::TestIOThread io_thread_;
   embedder::ScopedPlatformHandle platform_handles_[2];
   RefPtr<Channel> channels_[2];
 
@@ -643,7 +646,7 @@
 
     // |dispatcher| should have been closed. This is |DCHECK()|ed when the
     // |dispatcher| is destroyed.
-    dispatcher->AssertHasOneRef();
+    EXPECT_TRUE(dispatcher->HasOneRef());
     dispatcher = nullptr;
   }
 
@@ -671,7 +674,7 @@
   EXPECT_EQ(1u, read_dispatchers.size());
   EXPECT_EQ(1u, read_num_dispatchers);
   ASSERT_TRUE(read_dispatchers[0]);
-  read_dispatchers[0]->AssertHasOneRef();
+  EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
 
   EXPECT_EQ(Dispatcher::Type::MESSAGE_PIPE, read_dispatchers[0]->GetType());
   dispatcher = RefPtr<MessagePipeDispatcher>(
@@ -818,7 +821,7 @@
 
     // |dispatcher| should have been closed. This is |DCHECK()|ed when the
     // |dispatcher| is destroyed.
-    dispatcher->AssertHasOneRef();
+    EXPECT_TRUE(dispatcher->HasOneRef());
     dispatcher = nullptr;
   }
 
@@ -846,7 +849,7 @@
   EXPECT_EQ(1u, read_dispatchers.size());
   EXPECT_EQ(1u, read_num_dispatchers);
   ASSERT_TRUE(read_dispatchers[0]);
-  read_dispatchers[0]->AssertHasOneRef();
+  EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
 
   EXPECT_EQ(Dispatcher::Type::MESSAGE_PIPE, read_dispatchers[0]->GetType());
   dispatcher = RefPtr<MessagePipeDispatcher>(
@@ -947,7 +950,7 @@
 
     // |dispatcher| should have been closed. This is |DCHECK()|ed when the
     // |dispatcher| is destroyed.
-    dispatcher->AssertHasOneRef();
+    EXPECT_TRUE(dispatcher->HasOneRef());
     dispatcher = nullptr;
   }
 
@@ -975,7 +978,7 @@
   EXPECT_EQ(1u, read_dispatchers.size());
   EXPECT_EQ(1u, read_num_dispatchers);
   ASSERT_TRUE(read_dispatchers[0]);
-  read_dispatchers[0]->AssertHasOneRef();
+  EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
 
   EXPECT_EQ(Dispatcher::Type::SHARED_BUFFER, read_dispatchers[0]->GetType());
   dispatcher = RefPtr<SharedBufferDispatcher>(
@@ -1013,7 +1016,7 @@
 }
 
 TEST_F(RemoteMessagePipeTest, PlatformHandlePassing) {
-  mojo::test::ScopedTestDir test_dir;
+  test::ScopedTestDir test_dir;
 
   static const char kHello[] = "hello";
   static const char kWorld[] = "world";
@@ -1032,7 +1035,7 @@
   // We'll try to pass this dispatcher, which will cause a |PlatformHandle| to
   // be passed.
   auto dispatcher = PlatformHandleDispatcher::Create(
-      mojo::test::PlatformHandleFromFILE(fp.Pass()));
+      mojo::test::PlatformHandleFromFILE(std::move(fp)));
 
   // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do
   // it later, it might already be readable.)
@@ -1057,7 +1060,7 @@
 
     // |dispatcher| should have been closed. This is |DCHECK()|ed when the
     // |dispatcher| is destroyed.
-    dispatcher->AssertHasOneRef();
+    EXPECT_TRUE(dispatcher->HasOneRef());
     dispatcher = nullptr;
   }
 
@@ -1085,16 +1088,16 @@
   EXPECT_EQ(1u, read_dispatchers.size());
   EXPECT_EQ(1u, read_num_dispatchers);
   ASSERT_TRUE(read_dispatchers[0]);
-  read_dispatchers[0]->AssertHasOneRef();
+  EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
 
   EXPECT_EQ(Dispatcher::Type::PLATFORM_HANDLE, read_dispatchers[0]->GetType());
   dispatcher = RefPtr<PlatformHandleDispatcher>(
       static_cast<PlatformHandleDispatcher*>(read_dispatchers[0].get()));
 
-  embedder::ScopedPlatformHandle h = dispatcher->PassPlatformHandle().Pass();
+  embedder::ScopedPlatformHandle h = dispatcher->PassPlatformHandle();
   EXPECT_TRUE(h.is_valid());
 
-  fp = mojo::test::FILEFromPlatformHandle(h.Pass(), "rb").Pass();
+  fp = mojo::test::FILEFromPlatformHandle(h.Pass(), "rb");
   EXPECT_FALSE(h.is_valid());
   EXPECT_TRUE(fp);
 
@@ -1115,7 +1118,7 @@
 // itself (not in the test). Also, any logged warnings/errors would also
 // probably be indicative of bugs.
 TEST_F(RemoteMessagePipeTest, RacingClosesStress) {
-  MojoDeadline delay = test::DeadlineFromMilliseconds(5);
+  MojoDeadline delay = test::DeadlineFromMilliseconds(5u);
 
   for (unsigned i = 0; i < 256; i++) {
     DVLOG(2) << "---------------------------------------- " << i;
@@ -1192,7 +1195,7 @@
 
     // |dispatcher| should have been closed. This is |DCHECK()|ed when the
     // |dispatcher| is destroyed.
-    dispatcher->AssertHasOneRef();
+    EXPECT_TRUE(dispatcher->HasOneRef());
     dispatcher = nullptr;
   }
 
@@ -1220,7 +1223,7 @@
   EXPECT_EQ(1u, read_dispatchers.size());
   EXPECT_EQ(1u, read_num_dispatchers);
   ASSERT_TRUE(read_dispatchers[0]);
-  read_dispatchers[0]->AssertHasOneRef();
+  EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
 
   EXPECT_EQ(Dispatcher::Type::MESSAGE_PIPE, read_dispatchers[0]->GetType());
   dispatcher = RefPtr<MessagePipeDispatcher>(
@@ -1252,7 +1255,7 @@
 
     // |dispatcher| should have been closed. This is |DCHECK()|ed when the
     // |dispatcher| is destroyed.
-    dispatcher->AssertHasOneRef();
+    EXPECT_TRUE(dispatcher->HasOneRef());
     dispatcher = nullptr;
   }
 
@@ -1278,7 +1281,7 @@
   EXPECT_EQ(1u, read_dispatchers.size());
   EXPECT_EQ(1u, read_num_dispatchers);
   ASSERT_TRUE(read_dispatchers[0]);
-  read_dispatchers[0]->AssertHasOneRef();
+  EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
 
   EXPECT_EQ(Dispatcher::Type::MESSAGE_PIPE, read_dispatchers[0]->GetType());
   dispatcher = RefPtr<MessagePipeDispatcher>(
diff --git a/mojo/edk/system/remote_producer_data_pipe_impl.cc b/mojo/edk/system/remote_producer_data_pipe_impl.cc
index 6cd9b2d..772277b 100644
--- a/mojo/edk/system/remote_producer_data_pipe_impl.cc
+++ b/mojo/edk/system/remote_producer_data_pipe_impl.cc
@@ -20,6 +20,8 @@
 #include "mojo/edk/system/remote_consumer_data_pipe_impl.h"
 #include "mojo/edk/system/remote_data_pipe_ack.h"
 
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
diff --git a/mojo/edk/system/remote_producer_data_pipe_impl.h b/mojo/edk/system/remote_producer_data_pipe_impl.h
index 2f0a3a9..1210b73 100644
--- a/mojo/edk/system/remote_producer_data_pipe_impl.h
+++ b/mojo/edk/system/remote_producer_data_pipe_impl.h
@@ -10,7 +10,7 @@
 #include "base/memory/aligned_memory.h"
 #include "mojo/edk/system/channel_endpoint.h"
 #include "mojo/edk/system/data_pipe_impl.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -24,9 +24,9 @@
 class RemoteProducerDataPipeImpl final : public DataPipeImpl {
  public:
   explicit RemoteProducerDataPipeImpl(
-      RefPtr<ChannelEndpoint>&& channel_endpoint);
+      util::RefPtr<ChannelEndpoint>&& channel_endpoint);
   RemoteProducerDataPipeImpl(
-      RefPtr<ChannelEndpoint>&& channel_endpoint,
+      util::RefPtr<ChannelEndpoint>&& channel_endpoint,
       std::unique_ptr<char, base::AlignedFreeDeleter> buffer,
       size_t start_index,
       size_t current_num_bytes);
@@ -106,7 +106,7 @@
   void Disconnect();
 
   // Should be valid if and only if |producer_open()| returns true.
-  RefPtr<ChannelEndpoint> channel_endpoint_;
+  util::RefPtr<ChannelEndpoint> channel_endpoint_;
 
   std::unique_ptr<char, base::AlignedFreeDeleter> buffer_;
   // Circular buffer.
diff --git a/mojo/edk/system/shared_buffer_dispatcher.cc b/mojo/edk/system/shared_buffer_dispatcher.cc
index 38d7faa..8b86d0a 100644
--- a/mojo/edk/system/shared_buffer_dispatcher.cc
+++ b/mojo/edk/system/shared_buffer_dispatcher.cc
@@ -5,6 +5,7 @@
 #include "mojo/edk/system/shared_buffer_dispatcher.h"
 
 #include <limits>
+#include <utility>
 
 #include "base/logging.h"
 #include "mojo/edk/embedder/platform_support.h"
@@ -14,6 +15,8 @@
 #include "mojo/edk/system/options_validation.h"
 #include "mojo/public/c/system/macros.h"
 
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 
@@ -75,15 +78,15 @@
     return nullptr;
   }
 
-  scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer(
-      platform_support->CreateSharedBuffer(static_cast<size_t>(num_bytes)));
+  auto shared_buffer =
+      platform_support->CreateSharedBuffer(static_cast<size_t>(num_bytes));
   if (!shared_buffer) {
     *result = MOJO_RESULT_RESOURCE_EXHAUSTED;
     return nullptr;
   }
 
   *result = MOJO_RESULT_OK;
-  return CreateInternal(shared_buffer.Pass());
+  return CreateInternal(std::move(shared_buffer));
 }
 
 Dispatcher::Type SharedBufferDispatcher::GetType() const {
@@ -144,21 +147,21 @@
 
   // Wrapping |platform_handle| in a |ScopedPlatformHandle| means that it'll be
   // closed even if creation fails.
-  scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer(
+  auto shared_buffer =
       channel->platform_support()->CreateSharedBufferFromHandle(
-          num_bytes, embedder::ScopedPlatformHandle(platform_handle)));
+          num_bytes, embedder::ScopedPlatformHandle(platform_handle));
   if (!shared_buffer) {
     LOG(ERROR)
         << "Invalid serialized shared buffer dispatcher (invalid num_bytes?)";
     return nullptr;
   }
 
-  return CreateInternal(shared_buffer.Pass());
+  return CreateInternal(std::move(shared_buffer));
 }
 
 SharedBufferDispatcher::SharedBufferDispatcher(
-    scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer)
-    : shared_buffer_(shared_buffer) {
+    RefPtr<embedder::PlatformSharedBuffer>&& shared_buffer)
+    : shared_buffer_(std::move(shared_buffer)) {
   DCHECK(shared_buffer_);
 }
 
@@ -207,7 +210,7 @@
 SharedBufferDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() {
   mutex().AssertHeld();
   DCHECK(shared_buffer_);
-  return CreateInternal(shared_buffer_.Pass());
+  return CreateInternal(std::move(shared_buffer_));
 }
 
 MojoResult SharedBufferDispatcher::DuplicateBufferHandleImplNoLock(
@@ -221,7 +224,7 @@
     return result;
 
   // Note: Since this is "duplicate", we keep our ref to |shared_buffer_|.
-  *new_dispatcher = CreateInternal(shared_buffer_);
+  *new_dispatcher = CreateInternal(shared_buffer_.Clone());
   return MOJO_RESULT_OK;
 }
 
diff --git a/mojo/edk/system/shared_buffer_dispatcher.h b/mojo/edk/system/shared_buffer_dispatcher.h
index c61bd5c..9f153cf 100644
--- a/mojo/edk/system/shared_buffer_dispatcher.h
+++ b/mojo/edk/system/shared_buffer_dispatcher.h
@@ -5,10 +5,13 @@
 #ifndef MOJO_EDK_SYSTEM_SHARED_BUFFER_DISPATCHER_H_
 #define MOJO_EDK_SYSTEM_SHARED_BUFFER_DISPATCHER_H_
 
+#include <utility>
+
 #include "mojo/edk/embedder/platform_shared_buffer.h"
 #include "mojo/edk/system/memory.h"
-#include "mojo/edk/system/ref_ptr.h"
 #include "mojo/edk/system/simple_dispatcher.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -41,7 +44,7 @@
   // Static factory method: |validated_options| must be validated (obviously).
   // Returns null on error; |*result| will be set to an appropriate result
   // code).
-  static RefPtr<SharedBufferDispatcher> Create(
+  static util::RefPtr<SharedBufferDispatcher> Create(
       embedder::PlatformSupport* platform_support,
       const MojoCreateSharedBufferOptions& validated_options,
       uint64_t num_bytes,
@@ -52,20 +55,20 @@
 
   // The "opposite" of |SerializeAndClose()|. (Typically this is called by
   // |Dispatcher::Deserialize()|.)
-  static RefPtr<SharedBufferDispatcher> Deserialize(
+  static util::RefPtr<SharedBufferDispatcher> Deserialize(
       Channel* channel,
       const void* source,
       size_t size,
       embedder::PlatformHandleVector* platform_handles);
 
  private:
-  static RefPtr<SharedBufferDispatcher> CreateInternal(
-      scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer) {
-    return AdoptRef(new SharedBufferDispatcher(shared_buffer.Pass()));
+  static util::RefPtr<SharedBufferDispatcher> CreateInternal(
+      util::RefPtr<embedder::PlatformSharedBuffer>&& shared_buffer) {
+    return AdoptRef(new SharedBufferDispatcher(std::move(shared_buffer)));
   }
 
   explicit SharedBufferDispatcher(
-      scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer);
+      util::RefPtr<embedder::PlatformSharedBuffer>&& shared_buffer);
   ~SharedBufferDispatcher() override;
 
   // Validates and/or sets default options for
@@ -79,10 +82,11 @@
 
   // |Dispatcher| protected methods:
   void CloseImplNoLock() override;
-  RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock() override;
+  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock()
+      override;
   MojoResult DuplicateBufferHandleImplNoLock(
       UserPointer<const MojoDuplicateBufferHandleOptions> options,
-      RefPtr<Dispatcher>* new_dispatcher) override;
+      util::RefPtr<Dispatcher>* new_dispatcher) override;
   MojoResult MapBufferImplNoLock(
       uint64_t offset,
       uint64_t num_bytes,
@@ -99,7 +103,7 @@
       embedder::PlatformHandleVector* platform_handles) override
       MOJO_NOT_THREAD_SAFE;
 
-  scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer_
+  util::RefPtr<embedder::PlatformSharedBuffer> shared_buffer_
       MOJO_GUARDED_BY(mutex());
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(SharedBufferDispatcher);
diff --git a/mojo/edk/system/shared_buffer_dispatcher_unittest.cc b/mojo/edk/system/shared_buffer_dispatcher_unittest.cc
index 571e431..f4998bb 100644
--- a/mojo/edk/system/shared_buffer_dispatcher_unittest.cc
+++ b/mojo/edk/system/shared_buffer_dispatcher_unittest.cc
@@ -6,13 +6,14 @@
 
 #include <limits>
 
-#include "base/memory/ref_counted.h"
 #include "mojo/edk/embedder/platform_shared_buffer.h"
 #include "mojo/edk/embedder/simple_platform_support.h"
 #include "mojo/edk/system/dispatcher.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 namespace {
diff --git a/mojo/edk/system/simple_dispatcher.h b/mojo/edk/system/simple_dispatcher.h
index 8a8e83e..c305d5f 100644
--- a/mojo/edk/system/simple_dispatcher.h
+++ b/mojo/edk/system/simple_dispatcher.h
@@ -9,6 +9,7 @@
 
 #include "mojo/edk/system/awakable_list.h"
 #include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
@@ -16,8 +17,8 @@
 
 // A base class for simple dispatchers. "Simple" means that there's a one-to-one
 // correspondence between handles and dispatchers (see the explanatory comment
-// in core.cc). This class implements the standard waiter-signalling mechanism
-// in that case.
+// in core.cc). This class implements the standard waiter-signaling mechanism in
+// that case.
 class SimpleDispatcher : public Dispatcher {
  protected:
   SimpleDispatcher();
diff --git a/mojo/edk/system/simple_dispatcher_unittest.cc b/mojo/edk/system/simple_dispatcher_unittest.cc
index 8b5fb5c..998801b 100644
--- a/mojo/edk/system/simple_dispatcher_unittest.cc
+++ b/mojo/edk/system/simple_dispatcher_unittest.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a
-// heavily-loaded system). Sorry. |test::EpsilonDeadline()| may be increased to
+// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to
 // increase tolerance and reduce observed flakiness (though doing so reduces the
 // meaningfulness of the test).
 
@@ -13,15 +13,21 @@
 #include <vector>
 
 #include "base/logging.h"
-#include "mojo/edk/system/ref_ptr.h"
-#include "mojo/edk/system/test_utils.h"
-#include "mojo/edk/system/thread_annotations.h"
+#include "mojo/edk/system/test/sleep.h"
+#include "mojo/edk/system/test/stopwatch.h"
+#include "mojo/edk/system/test/timeouts.h"
 #include "mojo/edk/system/waiter.h"
 #include "mojo/edk/system/waiter_test_utils.h"
 #include "mojo/edk/util/make_unique.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using mojo::util::MakeRefCounted;
+using mojo::util::MutexLocker;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 namespace {
@@ -114,7 +120,7 @@
   d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE);
   stopwatch.Start();
   EXPECT_EQ(MOJO_RESULT_OK, w.Wait(MOJO_DEADLINE_INDEFINITE, &context));
-  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
   EXPECT_EQ(1u, context);
   hss = HandleSignalsState();
   d->RemoveAwakable(&w, &hss);
@@ -130,7 +136,7 @@
   d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE);
   stopwatch.Start();
   EXPECT_EQ(MOJO_RESULT_OK, w.Wait(0, &context));
-  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
   EXPECT_EQ(2u, context);
   hss = HandleSignalsState();
   d->RemoveAwakable(&w, &hss);
@@ -145,8 +151,8 @@
             d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 3, nullptr));
   d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE);
   stopwatch.Start();
-  EXPECT_EQ(MOJO_RESULT_OK, w.Wait(2 * test::EpsilonDeadline(), &context));
-  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+  EXPECT_EQ(MOJO_RESULT_OK, w.Wait(2 * test::EpsilonTimeout(), &context));
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
   EXPECT_EQ(3u, context);
   hss = HandleSignalsState();
   d->RemoveAwakable(&w, &hss);
@@ -161,7 +167,7 @@
             d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 4, nullptr));
   stopwatch.Start();
   EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, w.Wait(0, nullptr));
-  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
   hss = HandleSignalsState();
   d->RemoveAwakable(&w, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
@@ -176,10 +182,10 @@
             d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 5, nullptr));
   stopwatch.Start();
   EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
-            w.Wait(2 * test::EpsilonDeadline(), nullptr));
+            w.Wait(2 * test::EpsilonTimeout(), nullptr));
   MojoDeadline elapsed = stopwatch.Elapsed();
-  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
-  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
   hss = HandleSignalsState();
   d->RemoveAwakable(&w, &hss);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
@@ -218,7 +224,7 @@
   stopwatch.Start();
   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
             w.Wait(MOJO_DEADLINE_INDEFINITE, &context));
-  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
   EXPECT_EQ(2u, context);
   hss = HandleSignalsState();
   d->RemoveAwakable(&w, &hss);
@@ -234,7 +240,7 @@
   d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
   stopwatch.Start();
   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, w.Wait(0, &context));
-  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
   EXPECT_EQ(3u, context);
   hss = HandleSignalsState();
   d->RemoveAwakable(&w, &hss);
@@ -251,8 +257,8 @@
   d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
   stopwatch.Start();
   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            w.Wait(2 * test::EpsilonDeadline(), &context));
-  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+            w.Wait(2 * test::EpsilonTimeout(), &context));
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
   EXPECT_EQ(4u, context);
   hss = HandleSignalsState();
   d->RemoveAwakable(&w, &hss);
@@ -289,7 +295,7 @@
   EXPECT_EQ(MOJO_RESULT_OK, d->Close());
   stopwatch.Start();
   EXPECT_EQ(MOJO_RESULT_CANCELLED, w.Wait(MOJO_DEADLINE_INDEFINITE, &context));
-  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
   EXPECT_EQ(2u, context);
   // Don't need to remove waiters from closed dispatchers.
 
@@ -301,7 +307,7 @@
   EXPECT_EQ(MOJO_RESULT_OK, d->Close());
   stopwatch.Start();
   EXPECT_EQ(MOJO_RESULT_CANCELLED, w.Wait(0, &context));
-  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
   EXPECT_EQ(3u, context);
   // Don't need to remove waiters from closed dispatchers.
 
@@ -314,8 +320,8 @@
   EXPECT_EQ(MOJO_RESULT_OK, d->Close());
   stopwatch.Start();
   EXPECT_EQ(MOJO_RESULT_CANCELLED,
-            w.Wait(2 * test::EpsilonDeadline(), &context));
-  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+            w.Wait(2 * test::EpsilonTimeout(), &context));
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
   EXPECT_EQ(4u, context);
   // Don't need to remove waiters from closed dispatchers.
 }
@@ -341,7 +347,7 @@
     // If we closed earlier, then probably we'd get a |MOJO_RESULT_CANCELLED|.
     EXPECT_EQ(MOJO_RESULT_OK, d->Close());
   }
-  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+  EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
   EXPECT_FALSE(did_wait);
   EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, result);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
@@ -357,14 +363,14 @@
                                 &context, &hss);
       stopwatch.Start();
       thread.Start();
-      test::Sleep(2 * test::EpsilonDeadline());
+      test::Sleep(2 * test::EpsilonTimeout());
       d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
     }  // Joins the thread.
     EXPECT_EQ(MOJO_RESULT_OK, d->Close());
   }
   MojoDeadline elapsed = stopwatch.Elapsed();
-  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
-  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
   EXPECT_TRUE(did_wait);
   EXPECT_EQ(MOJO_RESULT_OK, result);
   EXPECT_EQ(2u, context);
@@ -381,14 +387,14 @@
                                 &context, &hss);
       stopwatch.Start();
       thread.Start();
-      test::Sleep(2 * test::EpsilonDeadline());
+      test::Sleep(2 * test::EpsilonTimeout());
       d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_NONE);
     }  // Joins the thread.
     EXPECT_EQ(MOJO_RESULT_OK, d->Close());
   }
   elapsed = stopwatch.Elapsed();
-  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
-  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
   EXPECT_TRUE(did_wait);
   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
   EXPECT_EQ(3u, context);
@@ -403,12 +409,12 @@
                               &context, &hss);
     stopwatch.Start();
     thread.Start();
-    test::Sleep(2 * test::EpsilonDeadline());
+    test::Sleep(2 * test::EpsilonTimeout());
     EXPECT_EQ(MOJO_RESULT_OK, d->Close());
   }  // Joins the thread.
   elapsed = stopwatch.Elapsed();
-  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
-  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
   EXPECT_TRUE(did_wait);
   EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
   EXPECT_EQ(4u, context);
@@ -420,11 +426,11 @@
     auto d = MakeRefCounted<MockSimpleDispatcher>();
     {
       test::WaiterThread thread(d, MOJO_HANDLE_SIGNAL_READABLE,
-                                2 * test::EpsilonDeadline(), 5, &did_wait,
+                                2 * test::EpsilonTimeout(), 5, &did_wait,
                                 &result, &context, &hss);
       stopwatch.Start();
       thread.Start();
-      test::Sleep(1 * test::EpsilonDeadline());
+      test::Sleep(1 * test::EpsilonTimeout());
       // Not what we're waiting for.
       d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE);
     }  // Joins the thread (after its wait times out).
@@ -432,8 +438,8 @@
     EXPECT_EQ(MOJO_RESULT_OK, d->Close());
   }
   elapsed = stopwatch.Elapsed();
-  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
-  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
   EXPECT_TRUE(did_wait);
   EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
@@ -459,7 +465,7 @@
           &did_wait[i], &result[i], &context[i], &hss[i]));
       threads.back()->Start();
     }
-    test::Sleep(2 * test::EpsilonDeadline());
+    test::Sleep(2 * test::EpsilonTimeout());
     d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
     EXPECT_EQ(MOJO_RESULT_OK, d->Close());
   }  // Joins the threads.
@@ -488,7 +494,7 @@
           &did_wait[i], &result[i], &context[i], &hss[i]));
       threads.back()->Start();
     }
-    test::Sleep(2 * test::EpsilonDeadline());
+    test::Sleep(2 * test::EpsilonTimeout());
     d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
     // This will wake up the ones waiting to write.
     EXPECT_EQ(MOJO_RESULT_OK, d->Close());
@@ -525,9 +531,9 @@
           &did_wait[i], &result[i], &context[i], &hss[i]));
       threads.back()->Start();
     }
-    test::Sleep(1 * test::EpsilonDeadline());
+    test::Sleep(1 * test::EpsilonTimeout());
     d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
-    test::Sleep(1 * test::EpsilonDeadline());
+    test::Sleep(1 * test::EpsilonTimeout());
     d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
     EXPECT_EQ(MOJO_RESULT_OK, d->Close());
   }  // Joins the threads.
@@ -553,17 +559,17 @@
     std::vector<std::unique_ptr<test::WaiterThread>> threads;
     for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
       threads.push_back(util::MakeUnique<test::WaiterThread>(
-          d, MOJO_HANDLE_SIGNAL_READABLE, 3 * test::EpsilonDeadline(), i,
+          d, MOJO_HANDLE_SIGNAL_READABLE, 3 * test::EpsilonTimeout(), i,
           &did_wait[i], &result[i], &context[i], &hss[i]));
       threads.back()->Start();
     }
     for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
       threads.push_back(util::MakeUnique<test::WaiterThread>(
-          d, MOJO_HANDLE_SIGNAL_WRITABLE, 1 * test::EpsilonDeadline(), i,
+          d, MOJO_HANDLE_SIGNAL_WRITABLE, 1 * test::EpsilonTimeout(), i,
           &did_wait[i], &result[i], &context[i], &hss[i]));
       threads.back()->Start();
     }
-    test::Sleep(2 * test::EpsilonDeadline());
+    test::Sleep(2 * test::EpsilonTimeout());
     d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
     // All those waiting for writable should have timed out.
     EXPECT_EQ(MOJO_RESULT_OK, d->Close());
diff --git a/mojo/edk/system/slave_connection_manager.cc b/mojo/edk/system/slave_connection_manager.cc
index 08aaf9d..749597a 100644
--- a/mojo/edk/system/slave_connection_manager.cc
+++ b/mojo/edk/system/slave_connection_manager.cc
@@ -13,6 +13,8 @@
 #include "mojo/edk/system/message_in_transit.h"
 #include "mojo/edk/util/make_unique.h"
 
+using mojo::util::MutexLocker;
+
 namespace mojo {
 namespace system {
 
@@ -27,9 +29,7 @@
       ack_result_(nullptr),
       ack_peer_process_identifier_(nullptr),
       ack_is_first_(nullptr),
-      ack_platform_handle_(nullptr),
-      event_(false, false) {  // Auto-reset, not initially signalled.
-}
+      ack_platform_handle_(nullptr) {}
 
 SlaveConnectionManager::~SlaveConnectionManager() {
   DCHECK(!delegate_thread_task_runner_);
diff --git a/mojo/edk/system/slave_connection_manager.h b/mojo/edk/system/slave_connection_manager.h
index 894f67d..bd66534 100644
--- a/mojo/edk/system/slave_connection_manager.h
+++ b/mojo/edk/system/slave_connection_manager.h
@@ -8,14 +8,14 @@
 #include <memory>
 
 #include "base/memory/ref_counted.h"
-#include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
 #include "mojo/edk/embedder/platform_task_runner.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
 #include "mojo/edk/embedder/slave_process_delegate.h"
 #include "mojo/edk/system/connection_manager.h"
-#include "mojo/edk/system/mutex.h"
 #include "mojo/edk/system/raw_channel.h"
+#include "mojo/edk/system/waitable_event.h"
+#include "mojo/edk/util/mutex.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace base {
@@ -146,8 +146,8 @@
   //
   // TODO(vtl): This is all a hack. It'd really suffice to have a version of
   // |RawChannel| with fully synchronous reading and writing.
-  Mutex mutex_;
-  base::WaitableEvent event_;
+  util::Mutex mutex_;
+  AutoResetWaitableEvent event_;
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(SlaveConnectionManager);
 };
diff --git a/mojo/edk/system/test/BUILD.gn b/mojo/edk/system/test/BUILD.gn
new file mode 100644
index 0000000..dd33b9a
--- /dev/null
+++ b/mojo/edk/system/test/BUILD.gn
@@ -0,0 +1,106 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../mojo_edk.gni")
+
+# Utilties for use by EDK internal (unit and perf) tests.
+mojo_edk_source_set("test") {
+  testonly = true
+  mojo_edk_visibility = [ "mojo/edk/*" ]
+
+  sources = [
+    "random.cc",
+    "random.h",
+    "scoped_test_dir.cc",
+    "scoped_test_dir.h",
+    "simple_test_thread.cc",
+    "simple_test_thread.h",
+    "sleep.cc",
+    "sleep.h",
+    "stopwatch.cc",
+    "stopwatch.h",
+    "test_command_line.cc",
+    "test_command_line.h",
+    "test_io_thread.cc",
+    "test_io_thread.h",
+    "timeouts.cc",
+    "timeouts.h",
+  ]
+
+  mojo_sdk_public_deps = [
+    "mojo/public/c/system",
+    "mojo/public/cpp/system",
+  ]
+
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//testing/gtest",
+  ]
+
+  mojo_edk_deps = [
+    "mojo/edk/embedder:platform",
+    "mojo/edk/util",
+  ]
+}
+
+# Utilities for use by EDK internal perf tests (for use with
+# :run_all_perftests).
+mojo_edk_source_set("perf") {
+  testonly = true
+  mojo_edk_visibility = [ "mojo/edk/*" ]
+
+  sources = [
+    "perf_log.cc",
+    "perf_log.h",
+  ]
+
+  deps = [
+    "//base/test:test_support",
+  ]
+}
+
+mojo_edk_source_set("run_all_unittests") {
+  testonly = true
+  mojo_edk_visibility = [ "mojo/edk/*" ]
+
+  sources = [
+    "run_all_unittests.cc",
+  ]
+
+  deps = [
+    ":test",
+    "//base",
+    "//base/test:test_support",
+    "//testing/gtest",
+  ]
+}
+
+mojo_edk_source_set("run_all_perftests") {
+  testonly = true
+  mojo_edk_visibility = [ "mojo/edk/*" ]
+
+  sources = [
+    "run_all_perftests.cc",
+  ]
+
+  deps = [
+    ":test",
+    "//base/test:test_support",
+  ]
+}
+
+mojo_edk_source_set("unittests") {
+  testonly = true
+  mojo_edk_visibility = [ "mojo/edk/system:mojo_system_unittests" ]
+
+  sources = [
+    "random_unittest.cc",
+  ]
+
+  deps = [
+    ":test",
+    "//testing/gtest",
+  ]
+}
diff --git a/mojo/edk/system/test/perf_log.cc b/mojo/edk/system/test/perf_log.cc
new file mode 100644
index 0000000..5b9d738
--- /dev/null
+++ b/mojo/edk/system/test/perf_log.cc
@@ -0,0 +1,19 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/system/test/perf_log.h"
+
+#include "base/test/perf_log.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+void LogPerfResult(const char* test_name, double value, const char* units) {
+  base::LogPerfResult(test_name, value, units);
+}
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/test/perf_log.h b/mojo/edk/system/test/perf_log.h
new file mode 100644
index 0000000..93affff
--- /dev/null
+++ b/mojo/edk/system/test/perf_log.h
@@ -0,0 +1,29 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Functions for logging perf test results.
+
+#ifndef MOJO_EDK_SYSTEM_TEST_PERF_LOG_H_
+#define MOJO_EDK_SYSTEM_TEST_PERF_LOG_H_
+
+//#include "mojo/public/c/system/types.h"
+//#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+// TODO(vtl): Possibly should have our own "InitPerfLog()" and
+// "FinalizePerfLog()" functions, but we can't do that until we stop using
+// |base::PerfTestSuite()|. Currently,
+
+// Logs the result of a perf test. You may only call this while running inside a
+// perf test suite (using the :run_all_perftests from this directory).
+void LogPerfResult(const char* test_name, double value, const char* units);
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_TEST_PERF_LOG_H_
diff --git a/mojo/edk/system/test/random.cc b/mojo/edk/system/test/random.cc
new file mode 100644
index 0000000..9463d27
--- /dev/null
+++ b/mojo/edk/system/test/random.cc
@@ -0,0 +1,36 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/system/test/random.h"
+
+#include <stdint.h>
+
+#include <limits>
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+// TODO(vtl): Replace all of this implementation with suitable use of C++11
+// <random> when we can.
+int RandomInt(int min, int max) {
+  DCHECK_LE(min, max);
+  DCHECK_LE(static_cast<int64_t>(max) - min, RAND_MAX);
+  DCHECK_LT(static_cast<int64_t>(max) - min, std::numeric_limits<int>::max());
+
+  // This is in-range for an |int| due to the above.
+  int range = max - min + 1;
+  int max_valid = (RAND_MAX / range) * range - 1;
+  int value;
+  do {
+    value = rand();
+  } while (value > max_valid);
+  return min + (value % range);
+}
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/test/random.h b/mojo/edk/system/test/random.h
new file mode 100644
index 0000000..cae4b9a
--- /dev/null
+++ b/mojo/edk/system/test/random.h
@@ -0,0 +1,23 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Pseudorandom number generation for tests.
+
+#ifndef MOJO_EDK_SYSTEM_TEST_RANDOM_H_
+#define MOJO_EDK_SYSTEM_TEST_RANDOM_H_
+
+namespace mojo {
+namespace system {
+namespace test {
+
+// Returns a (uniformly) (pseudo)random integer in the interval [min, max].
+// Currently, |max - min| must be at most |RAND_MAX| and must also be (strictly)
+// less than |INT_MAX|.
+int RandomInt(int min, int max);
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_TEST_RANDOM_H_
diff --git a/mojo/edk/system/test_utils_unittest.cc b/mojo/edk/system/test/random_unittest.cc
similarity index 92%
rename from mojo/edk/system/test_utils_unittest.cc
rename to mojo/edk/system/test/random_unittest.cc
index 5a3eda5..d1743ca 100644
--- a/mojo/edk/system/test_utils_unittest.cc
+++ b/mojo/edk/system/test/random_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/test/random.h"
 
 #include <limits>
 
@@ -13,7 +13,7 @@
 namespace test {
 namespace {
 
-TEST(TestUtilsTest, RandomInt) {
+TEST(RandomTest, RandomInt) {
   static const int kMin = -3;
   static const int kMax = 6;
   static const unsigned kNumBuckets = kMax - kMin + 1;
@@ -37,7 +37,7 @@
   }
 }
 
-TEST(TestUtilsTest, RandomIntSameValues) {
+TEST(RandomTest, RandomIntSameValues) {
   static const int kIntMin = std::numeric_limits<int>::min();
   EXPECT_EQ(kIntMin, RandomInt(kIntMin, kIntMin));
 
diff --git a/mojo/edk/system/test/run_all_perftests.cc b/mojo/edk/system/test/run_all_perftests.cc
new file mode 100644
index 0000000..aba386d
--- /dev/null
+++ b/mojo/edk/system/test/run_all_perftests.cc
@@ -0,0 +1,11 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/perf_test_suite.h"
+#include "mojo/edk/system/test/test_command_line.h"
+
+int main(int argc, char** argv) {
+  mojo::system::test::InitializeTestCommandLine(argc, argv);
+  return base::PerfTestSuite(argc, argv).Run();
+}
diff --git a/mojo/edk/system/run_all_unittests.cc b/mojo/edk/system/test/run_all_unittests.cc
similarity index 81%
rename from mojo/edk/system/run_all_unittests.cc
rename to mojo/edk/system/test/run_all_unittests.cc
index cd61337..72a9ca2 100644
--- a/mojo/edk/system/run_all_unittests.cc
+++ b/mojo/edk/system/test/run_all_unittests.cc
@@ -2,12 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// This defines the main() for EDK internal implementation unit test binaries.
+
 #include "base/bind.h"
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/test_suite.h"
+#include "mojo/edk/system/test/test_command_line.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 int main(int argc, char** argv) {
+  mojo::system::test::InitializeTestCommandLine(argc, argv);
+
 // Silence death test thread warnings on Linux. We can afford to run our death
 // tests a little more slowly (< 10 ms per death test on a Z620).
 // On android, we need to run in the default mode, as the threadsafe mode
diff --git a/mojo/edk/test/scoped_test_dir.cc b/mojo/edk/system/test/scoped_test_dir.cc
similarity index 87%
rename from mojo/edk/test/scoped_test_dir.cc
rename to mojo/edk/system/test/scoped_test_dir.cc
index 5bd768b..7f7f2bc 100644
--- a/mojo/edk/test/scoped_test_dir.cc
+++ b/mojo/edk/system/test/scoped_test_dir.cc
@@ -2,13 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "mojo/edk/test/scoped_test_dir.h"
+#include "mojo/edk/system/test/scoped_test_dir.h"
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
 
 namespace mojo {
+namespace system {
 namespace test {
 
 ScopedTestDir::ScopedTestDir() {
@@ -24,4 +25,5 @@
 }
 
 }  // namespace test
+}  // namespace system
 }  // namespace mojo
diff --git a/mojo/edk/test/scoped_test_dir.h b/mojo/edk/system/test/scoped_test_dir.h
similarity index 79%
rename from mojo/edk/test/scoped_test_dir.h
rename to mojo/edk/system/test/scoped_test_dir.h
index 4db372f..9f4db1b 100644
--- a/mojo/edk/test/scoped_test_dir.h
+++ b/mojo/edk/system/test/scoped_test_dir.h
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MOJO_EDK_TEST_SCOPED_TEST_DIR_H_
-#define MOJO_EDK_TEST_SCOPED_TEST_DIR_H_
+#ifndef MOJO_EDK_SYSTEM_TEST_SCOPED_TEST_DIR_H_
+#define MOJO_EDK_SYSTEM_TEST_SCOPED_TEST_DIR_H_
 
 #include "base/files/scoped_temp_dir.h"
 #include "mojo/edk/util/scoped_file.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
+namespace system {
 namespace test {
 
 // Creates/destroyes a temporary directory for test purposes. (Unlike
@@ -28,6 +29,7 @@
 };
 
 }  // namespace test
+}  // namespace system
 }  // namespace mojo
 
-#endif  // MOJO_EDK_TEST_SCOPED_TEST_DIR_H_
+#endif  // MOJO_EDK_SYSTEM_TEST_SCOPED_TEST_DIR_H_
diff --git a/mojo/edk/system/test/simple_test_thread.cc b/mojo/edk/system/test/simple_test_thread.cc
new file mode 100644
index 0000000..dd4c91a
--- /dev/null
+++ b/mojo/edk/system/test/simple_test_thread.cc
@@ -0,0 +1,31 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/system/test/simple_test_thread.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+SimpleTestThread::~SimpleTestThread() {
+  DCHECK(!thread_.joinable());
+}
+
+void SimpleTestThread::Start() {
+  DCHECK(!thread_.joinable());
+  thread_ = std::thread([this]() { Run(); });
+}
+
+void SimpleTestThread::Join() {
+  DCHECK(thread_.joinable());
+  thread_.join();
+}
+
+SimpleTestThread::SimpleTestThread() {}
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/test/simple_test_thread.h b/mojo/edk/system/test/simple_test_thread.h
new file mode 100644
index 0000000..e258d18
--- /dev/null
+++ b/mojo/edk/system/test/simple_test_thread.h
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EDK_SYSTEM_TEST_SIMPLE_TEST_THREAD_
+#define MOJO_EDK_SYSTEM_TEST_SIMPLE_TEST_THREAD_
+
+#include <thread>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+// Class to help simple threads (with no message loops) in tests.
+class SimpleTestThread {
+ public:
+  virtual ~SimpleTestThread();
+
+  // Starts the thread.
+  void Start();
+
+  // Joins the thread; this must be called if the thread was started.
+  void Join();
+
+ protected:
+  SimpleTestThread();
+
+  // Code to run in the thread.
+  virtual void Run() = 0;
+
+ private:
+  std::thread thread_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(SimpleTestThread);
+};
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_TEST_SYSTEM_SIMPLE_TEST_THREAD_
diff --git a/mojo/edk/system/test/sleep.cc b/mojo/edk/system/test/sleep.cc
new file mode 100644
index 0000000..e909f3d
--- /dev/null
+++ b/mojo/edk/system/test/sleep.cc
@@ -0,0 +1,31 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/system/test/sleep.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/threading/platform_thread.h"  // For |Sleep()|.
+#include "base/time/time.h"
+#include "mojo/edk/system/test/timeouts.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+void Sleep(MojoDeadline duration) {
+  CHECK_LE(duration,
+           static_cast<MojoDeadline>(std::numeric_limits<int64_t>::max()));
+  base::PlatformThread::Sleep(
+      base::TimeDelta::FromMicroseconds(static_cast<int64_t>(duration)));
+}
+
+void SleepMilliseconds(unsigned duration_milliseconds) {
+  Sleep(DeadlineFromMilliseconds(duration_milliseconds));
+}
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/test/sleep.h b/mojo/edk/system/test/sleep.h
new file mode 100644
index 0000000..ff8e4f0
--- /dev/null
+++ b/mojo/edk/system/test/sleep.h
@@ -0,0 +1,24 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Function for sleeping in tests. (Please use sparingly.)
+
+#ifndef MOJO_EDK_SYSTEM_TEST_SLEEP_H_
+#define MOJO_EDK_SYSTEM_TEST_SLEEP_H_
+
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+// Sleeps for at least the specified duration.
+void Sleep(MojoDeadline duration);
+void SleepMilliseconds(unsigned duration_milliseconds);
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_TEST_SLEEP_H_
diff --git a/mojo/edk/system/test/stopwatch.cc b/mojo/edk/system/test/stopwatch.cc
new file mode 100644
index 0000000..5aad8fd
--- /dev/null
+++ b/mojo/edk/system/test/stopwatch.cc
@@ -0,0 +1,32 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/system/test/stopwatch.h"
+
+#include <stdint.h>
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+Stopwatch::Stopwatch() {}
+
+Stopwatch::~Stopwatch() {}
+
+void Stopwatch::Start() {
+  start_time_ = platform_support_.GetTimeTicksNow();
+}
+
+MojoDeadline Stopwatch::Elapsed() {
+  int64_t result = platform_support_.GetTimeTicksNow() - start_time_;
+  // |DCHECK_GE|, not |CHECK_GE|, since this may be performance-important.
+  DCHECK_GE(result, 0);
+  return static_cast<MojoDeadline>(result);
+}
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/test/stopwatch.h b/mojo/edk/system/test/stopwatch.h
new file mode 100644
index 0000000..ed8d149
--- /dev/null
+++ b/mojo/edk/system/test/stopwatch.h
@@ -0,0 +1,43 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A simple "stopwatch" for measuring elapsed time.
+
+#ifndef MOJO_EDK_SYSTEM_TEST_STOPWATCH_H_
+#define MOJO_EDK_SYSTEM_TEST_STOPWATCH_H_
+
+#include "mojo/edk/embedder/simple_platform_support.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+// A simple "stopwatch" for measuring time elapsed from a given starting point.
+class Stopwatch {
+ public:
+  Stopwatch();
+  ~Stopwatch();
+
+  void Start();
+  // Returns the amount of time elapsed since the last call to |Start()| (in
+  // microseconds).
+  MojoDeadline Elapsed();
+
+ private:
+  // TODO(vtl): We need this for |GetTimeTicksNow()|. Maybe we should have a
+  // singleton for tests instead? Or maybe it should be injected?
+  embedder::SimplePlatformSupport platform_support_;
+
+  MojoTimeTicks start_time_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(Stopwatch);
+};
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_TEST_STOPWATCH_H_
diff --git a/mojo/edk/system/test/test_command_line.cc b/mojo/edk/system/test/test_command_line.cc
new file mode 100644
index 0000000..cf6f327
--- /dev/null
+++ b/mojo/edk/system/test/test_command_line.cc
@@ -0,0 +1,29 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/system/test/test_command_line.h"
+
+#include "base/logging.h"
+#include "mojo/edk/util/command_line.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+static util::CommandLine* g_test_command_line = nullptr;
+
+void InitializeTestCommandLine(int argc, const char* const* argv) {
+  CHECK(!g_test_command_line);
+  // TODO(vtl): May have to annotate the following "leak", if we run with LSan.
+  g_test_command_line =
+      new util::CommandLine(util::CommandLineFromArgcArgv(argc, argv));
+}
+
+const util::CommandLine* GetTestCommandLine() {
+  return g_test_command_line;
+}
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/test/test_command_line.h b/mojo/edk/system/test/test_command_line.h
new file mode 100644
index 0000000..0acc1ca
--- /dev/null
+++ b/mojo/edk/system/test/test_command_line.h
@@ -0,0 +1,34 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Provides a command line singleton for tests. (This is mainly useful for
+// multiprocess tests which start their own binary as a child process. Having
+// the singleton makes the command line accessible without lots of plumbing.)
+
+#ifndef MOJO_EDK_SYSTEM_TEST_TEST_COMMAND_LINE_H_
+#define MOJO_EDK_SYSTEM_TEST_TEST_COMMAND_LINE_H_
+
+namespace mojo {
+
+namespace util {
+class CommandLine;
+}  // namespace util
+
+namespace system {
+namespace test {
+
+// Initializes the command line singleton (made accessible via
+// |GetTestCommandLine()| below. This should be called at most once (typically
+// in |main()|).
+void InitializeTestCommandLine(int argc, const char* const* argv);
+
+// Gets the "command line" that the test binary was run with.
+const util::CommandLine* GetTestCommandLine();
+
+}  // namespace test
+}  // namespace system
+
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_TEST_TEST_COMMAND_LINE_H_
diff --git a/mojo/edk/test/test_io_thread.cc b/mojo/edk/system/test/test_io_thread.cc
similarity index 94%
rename from mojo/edk/test/test_io_thread.cc
rename to mojo/edk/system/test/test_io_thread.cc
index b8b4745..ee11ff3 100644
--- a/mojo/edk/test/test_io_thread.cc
+++ b/mojo/edk/system/test/test_io_thread.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "mojo/edk/test/test_io_thread.h"
+#include "mojo/edk/system/test/test_io_thread.h"
 
 #include "base/bind.h"
 #include "base/callback.h"
@@ -10,6 +10,7 @@
 #include "base/synchronization/waitable_event.h"
 
 namespace mojo {
+namespace system {
 namespace test {
 
 namespace {
@@ -63,4 +64,5 @@
 }
 
 }  // namespace test
+}  // namespace system
 }  // namespace mojo
diff --git a/mojo/edk/test/test_io_thread.h b/mojo/edk/system/test/test_io_thread.h
similarity index 89%
rename from mojo/edk/test/test_io_thread.h
rename to mojo/edk/system/test/test_io_thread.h
index e85672c..8b0d6e3 100644
--- a/mojo/edk/test/test_io_thread.h
+++ b/mojo/edk/system/test/test_io_thread.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MOJO_EDK_TEST_TEST_IO_THREAD_H_
-#define MOJO_EDK_TEST_TEST_IO_THREAD_H_
+#ifndef MOJO_EDK_SYSTEM_TEST_TEST_IO_THREAD_H_
+#define MOJO_EDK_SYSTEM_TEST_TEST_IO_THREAD_H_
 
 #include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
@@ -12,6 +12,7 @@
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
+namespace system {
 namespace test {
 
 // Class to help create/run threads with I/O |MessageLoop|s in tests.
@@ -50,6 +51,7 @@
 };
 
 }  // namespace test
+}  // namespace system
 }  // namespace mojo
 
-#endif  // MOJO_EDK_TEST_TEST_IO_THREAD_H_
+#endif  // MOJO_EDK_SYSTEM_TEST_TEST_IO_THREAD_H_
diff --git a/mojo/edk/system/test/timeouts.cc b/mojo/edk/system/test/timeouts.cc
new file mode 100644
index 0000000..f23b12d
--- /dev/null
+++ b/mojo/edk/system/test/timeouts.cc
@@ -0,0 +1,42 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/system/test/timeouts.h"
+
+#include "base/test/test_timeouts.h"
+#include "build/build_config.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+MojoDeadline DeadlineFromMilliseconds(unsigned milliseconds) {
+  return static_cast<MojoDeadline>(milliseconds) * 1000;
+}
+
+MojoDeadline EpsilonTimeout() {
+// Currently, |tiny_timeout()| is usually 100 ms (possibly scaled under ASAN,
+// etc.). Based on this, set it to (usually be) 30 ms on Android and 20 ms
+// elsewhere. (We'd like this to be as small as possible, without making things
+// flaky)
+#if defined(OS_ANDROID)
+  return (TinyTimeout() * 3) / 10;
+#else
+  return (TinyTimeout() * 2) / 10;
+#endif
+}
+
+MojoDeadline TinyTimeout() {
+  return static_cast<MojoDeadline>(
+      TestTimeouts::tiny_timeout().InMicroseconds());
+}
+
+MojoDeadline ActionTimeout() {
+  return static_cast<MojoDeadline>(
+      TestTimeouts::action_timeout().InMicroseconds());
+}
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/test/timeouts.h b/mojo/edk/system/test/timeouts.h
new file mode 100644
index 0000000..aa69ec2
--- /dev/null
+++ b/mojo/edk/system/test/timeouts.h
@@ -0,0 +1,36 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Test timeouts.
+
+#ifndef MOJO_EDK_SYSTEM_TEST_TIMEOUTS_H_
+#define MOJO_EDK_SYSTEM_TEST_TIMEOUTS_H_
+
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+MojoDeadline DeadlineFromMilliseconds(unsigned milliseconds);
+
+// A timeout smaller than |TestTimeouts::tiny_timeout()|, as a |MojoDeadline|.
+// Warning: This may lead to flakiness, but this is unavoidable if, e.g., you're
+// trying to ensure that functions with timeouts are reasonably accurate. We
+// want this to be as small as possible without causing too much flakiness.
+MojoDeadline EpsilonTimeout();
+
+// |TestTimeouts::tiny_timeout()|, as a |MojoDeadline|. (Expect this to be on
+// the order of 100 ms.)
+MojoDeadline TinyTimeout();
+
+// |TestTimeouts::action_timeout()|, as a |MojoDeadline|. (Expect this to be on
+// the order of 10 s.)
+MojoDeadline ActionTimeout();
+
+}  // namespace test
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_TEST_TIMEOUTS_H_
diff --git a/mojo/edk/system/test_channel_endpoint_client.cc b/mojo/edk/system/test_channel_endpoint_client.cc
index 957fa96..cc7b25d 100644
--- a/mojo/edk/system/test_channel_endpoint_client.cc
+++ b/mojo/edk/system/test_channel_endpoint_client.cc
@@ -6,10 +6,13 @@
 
 #include <utility>
 
-#include "base/synchronization/waitable_event.h"
 #include "mojo/edk/system/message_in_transit.h"
+#include "mojo/edk/system/waitable_event.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using mojo::util::MutexLocker;
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 namespace test {
@@ -40,7 +43,8 @@
   return messages_.GetMessage();
 }
 
-void TestChannelEndpointClient::SetReadEvent(base::WaitableEvent* read_event) {
+void TestChannelEndpointClient::SetReadEvent(
+    ManualResetWaitableEvent* read_event) {
   MutexLocker locker(&mutex_);
   read_event_ = read_event;
 }
diff --git a/mojo/edk/system/test_channel_endpoint_client.h b/mojo/edk/system/test_channel_endpoint_client.h
index 592e8ae..420d0b8 100644
--- a/mojo/edk/system/test_channel_endpoint_client.h
+++ b/mojo/edk/system/test_channel_endpoint_client.h
@@ -10,24 +10,24 @@
 #include "mojo/edk/system/channel_endpoint.h"
 #include "mojo/edk/system/channel_endpoint_client.h"
 #include "mojo/edk/system/message_in_transit_queue.h"
-#include "mojo/edk/system/mutex.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/edk/util/ref_ptr.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/cpp/system/macros.h"
 
-namespace base {
-class WaitableEvent;
-}
-
 namespace mojo {
 namespace system {
+
+class ManualResetWaitableEvent;
+
 namespace test {
 
 class TestChannelEndpointClient final : public ChannelEndpointClient {
  public:
-  // Note: Use |MakeRefCounted<TestChannelEndpointClient>()|.
+  // Note: Use |util::MakeRefCounted<TestChannelEndpointClient>()|.
 
   // Initializes with the given port and endpoint.
-  void Init(unsigned port, RefPtr<ChannelEndpoint>&& endpoint);
+  void Init(unsigned port, util::RefPtr<ChannelEndpoint>&& endpoint);
 
   // Returns true if we're detached from the |ChannelEndpoint|.
   bool IsDetached() const;
@@ -41,7 +41,7 @@
 
   // Sets an event to signal when we receive a message. (|read_event| must live
   // until this object is destroyed or the read event is reset to null.)
-  void SetReadEvent(base::WaitableEvent* read_event);
+  void SetReadEvent(ManualResetWaitableEvent* read_event);
 
   // |ChannelEndpointClient| implementation:
   bool OnReadMessage(unsigned port, MessageInTransit* message) override;
@@ -53,15 +53,15 @@
   TestChannelEndpointClient();
   ~TestChannelEndpointClient() override;
 
-  mutable Mutex mutex_;
+  mutable util::Mutex mutex_;
 
   unsigned port_ MOJO_GUARDED_BY(mutex_);
-  RefPtr<ChannelEndpoint> endpoint_ MOJO_GUARDED_BY(mutex_);
+  util::RefPtr<ChannelEndpoint> endpoint_ MOJO_GUARDED_BY(mutex_);
 
   MessageInTransitQueue messages_ MOJO_GUARDED_BY(mutex_);
 
   // Event to trigger if we read a message (may be null).
-  base::WaitableEvent* read_event_ MOJO_GUARDED_BY(mutex_);
+  ManualResetWaitableEvent* read_event_ MOJO_GUARDED_BY(mutex_);
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(TestChannelEndpointClient);
 };
diff --git a/mojo/edk/system/test_utils.cc b/mojo/edk/system/test_utils.cc
deleted file mode 100644
index e25adc9..0000000
--- a/mojo/edk/system/test_utils.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/edk/system/test_utils.h"
-
-#include <stdint.h>
-#include <stdlib.h>
-
-#include <limits>
-
-#include "base/logging.h"
-#include "base/test/test_timeouts.h"
-#include "base/threading/platform_thread.h"  // For |Sleep()|.
-#include "build/build_config.h"
-
-namespace mojo {
-namespace system {
-namespace test {
-
-MojoDeadline DeadlineFromMilliseconds(unsigned milliseconds) {
-  return static_cast<MojoDeadline>(milliseconds) * 1000;
-}
-
-MojoDeadline EpsilonDeadline() {
-// Currently, |tiny_timeout()| is usually 100 ms (possibly scaled under ASAN,
-// etc.). Based on this, set it to (usually be) 30 ms on Android and 20 ms
-// elsewhere. (We'd like this to be as small as possible, without making things
-// flaky)
-#if defined(OS_ANDROID)
-  return (TinyDeadline() * 3) / 10;
-#else
-  return (TinyDeadline() * 2) / 10;
-#endif
-}
-
-MojoDeadline TinyDeadline() {
-  return static_cast<MojoDeadline>(
-      TestTimeouts::tiny_timeout().InMicroseconds());
-}
-
-MojoDeadline ActionDeadline() {
-  return static_cast<MojoDeadline>(
-      TestTimeouts::action_timeout().InMicroseconds());
-}
-
-void Sleep(MojoDeadline deadline) {
-  CHECK_LE(deadline,
-           static_cast<MojoDeadline>(std::numeric_limits<int64_t>::max()));
-  base::PlatformThread::Sleep(
-      base::TimeDelta::FromMicroseconds(static_cast<int64_t>(deadline)));
-}
-
-// TODO(vtl): Replace all of this implementation with suitable use of C++11
-// <random> when we can.
-int RandomInt(int min, int max) {
-  DCHECK_LE(min, max);
-  DCHECK_LE(static_cast<int64_t>(max) - min, RAND_MAX);
-  DCHECK_LT(static_cast<int64_t>(max) - min, std::numeric_limits<int>::max());
-
-  // This is in-range for an |int| due to the above.
-  int range = max - min + 1;
-  int max_valid = (RAND_MAX / range) * range - 1;
-  int value;
-  do {
-    value = rand();
-  } while (value > max_valid);
-  return min + (value % range);
-}
-
-Stopwatch::Stopwatch() {
-}
-
-Stopwatch::~Stopwatch() {
-}
-
-void Stopwatch::Start() {
-  start_time_ = platform_support_.GetTimeTicksNow();
-}
-
-MojoDeadline Stopwatch::Elapsed() {
-  int64_t result = platform_support_.GetTimeTicksNow() - start_time_;
-  // |DCHECK_GE|, not |CHECK_GE|, since this may be performance-important.
-  DCHECK_GE(result, 0);
-  return static_cast<MojoDeadline>(result);
-}
-
-}  // namespace test
-}  // namespace system
-}  // namespace mojo
diff --git a/mojo/edk/system/test_utils.h b/mojo/edk/system/test_utils.h
deleted file mode 100644
index 8d424cc..0000000
--- a/mojo/edk/system/test_utils.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MOJO_EDK_SYSTEM_TEST_UTILS_H_
-#define MOJO_EDK_SYSTEM_TEST_UTILS_H_
-
-#include "mojo/edk/embedder/simple_platform_support.h"
-#include "mojo/public/c/system/types.h"
-#include "mojo/public/cpp/system/macros.h"
-
-namespace mojo {
-namespace system {
-namespace test {
-
-// Deadlines/timeouts and sleeping ---------------------------------------------
-
-MojoDeadline DeadlineFromMilliseconds(unsigned milliseconds);
-
-// A timeout smaller than |TestTimeouts::tiny_timeout()|, as a |MojoDeadline|.
-// Warning: This may lead to flakiness, but this is unavoidable if, e.g., you're
-// trying to ensure that functions with timeouts are reasonably accurate. We
-// want this to be as small as possible without causing too much flakiness.
-MojoDeadline EpsilonDeadline();
-
-// |TestTimeouts::tiny_timeout()|, as a |MojoDeadline|. (Expect this to be on
-// the order of 100 ms.)
-MojoDeadline TinyDeadline();
-
-// |TestTimeouts::action_timeout()|, as a |MojoDeadline|. (Expect this to be on
-// the order of 10 s.)
-MojoDeadline ActionDeadline();
-
-// Sleeps for at least the specified duration.
-void Sleep(MojoDeadline deadline);
-
-// Pseudorandom numbers for testing --------------------------------------------
-
-// Returns a (uniformly) (pseudo)random integer in the interval [min, max].
-// Currently, |max - min| must be at most |RAND_MAX| and must also be (strictly)
-// less than |INT_MAX|.
-int RandomInt(int min, int max);
-
-// Stopwatch -------------------------------------------------------------------
-
-// A simple "stopwatch" for measuring time elapsed from a given starting point.
-class Stopwatch {
- public:
-  Stopwatch();
-  ~Stopwatch();
-
-  void Start();
-  // Returns the amount of time elapsed since the last call to |Start()| (in
-  // microseconds).
-  MojoDeadline Elapsed();
-
- private:
-  // TODO(vtl): We need this for |GetTimeTicksNow()|. Maybe we should have a
-  // singleton for tests instead? Or maybe it should be injected?
-  embedder::SimplePlatformSupport platform_support_;
-
-  MojoTimeTicks start_time_;
-
-  MOJO_DISALLOW_COPY_AND_ASSIGN(Stopwatch);
-};
-
-}  // namespace test
-}  // namespace system
-}  // namespace mojo
-
-#endif  // MOJO_EDK_SYSTEM_TEST_UTILS_H_
diff --git a/mojo/edk/system/waitable_event.cc b/mojo/edk/system/waitable_event.cc
new file mode 100644
index 0000000..c05d038
--- /dev/null
+++ b/mojo/edk/system/waitable_event.cc
@@ -0,0 +1,168 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/system/waitable_event.h"
+
+#include "base/logging.h"
+#include "base/time/time.h"
+
+using mojo::util::CondVar;
+using mojo::util::Mutex;
+using mojo::util::MutexLocker;
+
+namespace mojo {
+namespace system {
+
+namespace {
+
+// Waits with a timeout on |condition()|. Returns true on timeout, or false if
+// |condition()| ever returns true. |condition()| should have no side effects
+// (and will always be called with |*mutex| held).
+template <typename ConditionFn>
+bool WaitWithTimeoutImpl(Mutex* mutex,
+                         CondVar* cv,
+                         ConditionFn condition,
+                         uint64_t timeout_microseconds)
+    MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex) {
+  mutex->AssertHeld();
+
+  if (condition())
+    return false;
+
+  // We may get spurious wakeups.
+  uint64_t wait_remaining = timeout_microseconds;
+  auto start = base::TimeTicks::Now();
+  while (true) {
+    if (cv->WaitWithTimeout(mutex, wait_remaining))
+      return true;  // Definitely timed out.
+
+    // We may have been awoken.
+    if (condition())
+      return false;
+
+    // Or the wakeup may have been spurious.
+    auto now = base::TimeTicks::Now();
+    DCHECK_GE(now, start);
+    uint64_t elapsed = static_cast<uint64_t>((now - start).InMicroseconds());
+    // It's possible that we may have timed out anyway.
+    if (elapsed >= timeout_microseconds)
+      return true;
+
+    // Otherwise, recalculate the amount that we have left to wait.
+    wait_remaining = timeout_microseconds - elapsed;
+  }
+}
+
+}  // namespace
+
+// AutoResetWaitableEvent ------------------------------------------------------
+
+void AutoResetWaitableEvent::Signal() {
+  MutexLocker locker(&mutex_);
+  signaled_ = true;
+  cv_.Signal();
+}
+
+void AutoResetWaitableEvent::Reset() {
+  MutexLocker locker(&mutex_);
+  signaled_ = false;
+}
+
+void AutoResetWaitableEvent::Wait() {
+  MutexLocker locker(&mutex_);
+  while (!signaled_)
+    cv_.Wait(&mutex_);
+  signaled_ = false;
+}
+
+bool AutoResetWaitableEvent::WaitWithTimeout(uint64_t timeout_microseconds) {
+  MutexLocker locker(&mutex_);
+
+  if (signaled_) {
+    signaled_ = false;
+    return false;
+  }
+
+  // We may get spurious wakeups.
+  uint64_t wait_remaining = timeout_microseconds;
+  auto start = base::TimeTicks::Now();
+  while (true) {
+    if (cv_.WaitWithTimeout(&mutex_, wait_remaining))
+      return true;  // Definitely timed out.
+
+    // We may have been awoken.
+    if (signaled_)
+      break;
+
+    // Or the wakeup may have been spurious.
+    auto now = base::TimeTicks::Now();
+    DCHECK_GE(now, start);
+    uint64_t elapsed = static_cast<uint64_t>((now - start).InMicroseconds());
+    // It's possible that we may have timed out anyway.
+    if (elapsed >= timeout_microseconds)
+      return true;
+
+    // Otherwise, recalculate the amount that we have left to wait.
+    wait_remaining = timeout_microseconds - elapsed;
+  }
+
+  signaled_ = false;
+  return false;
+}
+
+bool AutoResetWaitableEvent::IsSignaledForTest() {
+  MutexLocker locker(&mutex_);
+  return signaled_;
+}
+
+// ManualResetWaitableEvent ----------------------------------------------------
+
+void ManualResetWaitableEvent::Signal() {
+  MutexLocker locker(&mutex_);
+  signaled_ = true;
+  signal_id_++;
+  cv_.SignalAll();
+}
+
+void ManualResetWaitableEvent::Reset() {
+  MutexLocker locker(&mutex_);
+  signaled_ = false;
+}
+
+void ManualResetWaitableEvent::Wait() {
+  MutexLocker locker(&mutex_);
+
+  if (signaled_)
+    return;
+
+  auto last_signal_id = signal_id_;
+  do {
+    cv_.Wait(&mutex_);
+  } while (signal_id_ == last_signal_id);
+}
+
+bool ManualResetWaitableEvent::WaitWithTimeout(uint64_t timeout_microseconds) {
+  MutexLocker locker(&mutex_);
+
+  auto last_signal_id = signal_id_;
+  // Disable thread-safety analysis for the lambda: We could annotate it with
+  // |MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_)|, but then the analyzer currently
+  // isn't able to figure out that |WaitWithTimeoutImpl()| calls it while
+  // holding |mutex_|.
+  bool rv = WaitWithTimeoutImpl(
+      &mutex_, &cv_, [this, last_signal_id]() MOJO_NO_THREAD_SAFETY_ANALYSIS {
+        // Also check |signaled_| in case we're already signaled.
+        return signaled_ || signal_id_ != last_signal_id;
+      }, timeout_microseconds);
+  DCHECK(rv || signaled_ || signal_id_ != last_signal_id);
+  return rv;
+}
+
+bool ManualResetWaitableEvent::IsSignaledForTest() {
+  MutexLocker locker(&mutex_);
+  return signaled_;
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/waitable_event.h b/mojo/edk/system/waitable_event.h
new file mode 100644
index 0000000..38c75e0
--- /dev/null
+++ b/mojo/edk/system/waitable_event.h
@@ -0,0 +1,127 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Provides classes with functionality analogous to (but much more limited than)
+// Chromium's |base::WaitableEvent|, which in turn provides functionality
+// analogous to Windows's Event. (Unlike these two, we have separate types for
+// the manual- and auto-reset versions.)
+
+#ifndef MOJO_EDK_SYSTEM_WAITABLE_EVENT_H_
+#define MOJO_EDK_SYSTEM_WAITABLE_EVENT_H_
+
+#include "mojo/edk/util/cond_var.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/edk/util/thread_annotations.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace system {
+
+// AutoResetWaitableEvent ------------------------------------------------------
+
+// An event that can be signaled and waited on. This version automatically
+// returns to the unsignaled state after unblocking one waiter. (This is similar
+// to Windows's auto-reset Event, which is also imitated by Chromium's
+// auto-reset |base::WaitableEvent|. However, there are some limitations -- see
+// |Signal()|.) This class is thread-safe.
+class AutoResetWaitableEvent {
+ public:
+  AutoResetWaitableEvent() {}
+  ~AutoResetWaitableEvent() {}
+
+  // Put the event in the signaled state. Exactly one |Wait()| will be unblocked
+  // and the event will be returned to the unsignaled state.
+  //
+  // Notes (these are arguably bugs, but not worth working around):
+  // * That |Wait()| may be one that occurs on the calling thread, *after* the
+  //   call to |Signal()|.
+  // * A |Signal()|, followed by a |Reset()|, may cause *no* waiting thread to
+  //   be unblocked.
+  // * We rely on pthreads's queueing for picking which waiting thread to
+  //   unblock, rather than enforcing FIFO ordering.
+  void Signal();
+
+  // Put the event into the unsignaled state. Generally, this is not recommended
+  // on an auto-reset event (see notes above).
+  void Reset();
+
+  // Blocks the calling thread until the event is signaled. Upon unblocking, the
+  // event is returned to the unsignaled state, so that (unless |Reset()| is
+  // called) each |Signal()| unblocks exactly one |Wait()|.
+  void Wait();
+
+  // Like |Wait()|, but with a timeout. Also unblocks if |timeout_microseconds|
+  // without being signaled in which case it returns true (otherwise, it returns
+  // false).
+  bool WaitWithTimeout(uint64_t timeout_microseconds);
+
+  // Returns whether this event is in a signaled state or not. For use in tests
+  // only (in general, this is racy). Note: Unlike
+  // |base::WaitableEvent::IsSignaled()|, this doesn't reset the signaled state.
+  bool IsSignaledForTest();
+
+ private:
+  util::CondVar cv_;
+  util::Mutex mutex_;
+
+  // True if this event is in the signaled state.
+  bool signaled_ MOJO_GUARDED_BY(mutex_) = false;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(AutoResetWaitableEvent);
+};
+
+// ManualResetWaitableEvent ----------------------------------------------------
+
+// An event that can be signaled and waited on. This version remains signaled
+// until explicitly reset. (This is similar to Windows's manual-reset Event,
+// which is also imitated by Chromium's manual-reset |base::WaitableEvent|.)
+// This class is thread-safe.
+class ManualResetWaitableEvent {
+ public:
+  ManualResetWaitableEvent() {}
+  ~ManualResetWaitableEvent() {}
+
+  // Put the event into the unsignaled state.
+  void Reset();
+
+  // Put the event in the signaled state. If this is a manual-reset event, it
+  // wakes all waiting threads (blocked on |Wait()| or |WaitWithTimeout()|).
+  // Otherwise, it wakes a single waiting thread (and returns to the unsignaled
+  // state), if any; if there are none, it remains signaled.
+  void Signal();
+
+  // Blocks the calling thread until the event is signaled.
+  void Wait();
+
+  // Like |Wait()|, but with a timeout. Also unblocks if |timeout_microseconds|
+  // without being signaled in which case it returns true (otherwise, it returns
+  // false).
+  bool WaitWithTimeout(uint64_t timeout_microseconds);
+
+  // Returns whether this event is in a signaled state or not. For use in tests
+  // only (in general, this is racy).
+  bool IsSignaledForTest();
+
+ private:
+  util::CondVar cv_;
+  util::Mutex mutex_;
+
+  // True if this event is in the signaled state.
+  bool signaled_ MOJO_GUARDED_BY(mutex_) = false;
+
+  // While |CondVar::SignalAll()| (|pthread_cond_broadcast()|) will wake all
+  // waiting threads, one has to deal with spurious wake-ups. Checking
+  // |signaled_| isn't sufficient, since another thread may have been awoken and
+  // (manually) reset |signaled_|. This is a counter that is incremented in
+  // |Signal()| before calling |CondVar::SignalAll()|. A waiting thread knows it
+  // was awoken if |signal_id_| is different from when it started waiting.
+  unsigned signal_id_ MOJO_GUARDED_BY(mutex_) = 0u;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ManualResetWaitableEvent);
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_WAITABLE_EVENT_H_
diff --git a/mojo/edk/system/waitable_event_unittest.cc b/mojo/edk/system/waitable_event_unittest.cc
new file mode 100644
index 0000000..80e4c9b
--- /dev/null
+++ b/mojo/edk/system/waitable_event_unittest.cc
@@ -0,0 +1,257 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/system/waitable_event.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <atomic>
+#include <thread>
+#include <type_traits>
+#include <vector>
+
+#include "mojo/edk/system/test/sleep.h"
+#include "mojo/edk/system/test/stopwatch.h"
+#include "mojo/edk/system/test/timeouts.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+// Sleeps for a "very small" amount of time.
+void EpsilonRandomSleep() {
+  test::SleepMilliseconds(static_cast<unsigned>(rand()) % 20u);
+}
+
+// We'll use |MojoDeadline| with |uint64_t| (for |WaitWithTimeout()|'s timeout
+// argument), though note that |WaitWithTimeout()| doesn't support
+// |MOJO_DEADLINE_INDEFINITE|.
+static_assert(std::is_same<uint64_t, MojoDeadline>::value,
+              "MojoDeadline isn't uint64_t!");
+
+// AutoResetWaitableEvent ------------------------------------------------------
+
+TEST(AutoResetWaitableEventTest, Basic) {
+  AutoResetWaitableEvent ev;
+  EXPECT_FALSE(ev.IsSignaledForTest());
+  ev.Signal();
+  EXPECT_TRUE(ev.IsSignaledForTest());
+  ev.Wait();
+  EXPECT_FALSE(ev.IsSignaledForTest());
+  ev.Reset();
+  EXPECT_FALSE(ev.IsSignaledForTest());
+  ev.Signal();
+  EXPECT_TRUE(ev.IsSignaledForTest());
+  ev.Reset();
+  EXPECT_FALSE(ev.IsSignaledForTest());
+  EXPECT_TRUE(ev.WaitWithTimeout(0));
+  EXPECT_FALSE(ev.IsSignaledForTest());
+  EXPECT_TRUE(ev.WaitWithTimeout(test::DeadlineFromMilliseconds(1)));
+  EXPECT_FALSE(ev.IsSignaledForTest());
+  ev.Signal();
+  EXPECT_TRUE(ev.IsSignaledForTest());
+  EXPECT_FALSE(ev.WaitWithTimeout(0));
+  EXPECT_FALSE(ev.IsSignaledForTest());
+  EXPECT_TRUE(ev.WaitWithTimeout(test::DeadlineFromMilliseconds(1)));
+  EXPECT_FALSE(ev.IsSignaledForTest());
+  ev.Signal();
+  EXPECT_FALSE(ev.WaitWithTimeout(test::DeadlineFromMilliseconds(1)));
+  EXPECT_FALSE(ev.IsSignaledForTest());
+}
+
+TEST(AutoResetWaitableEventTest, MultipleWaiters) {
+  AutoResetWaitableEvent ev;
+
+  for (size_t i = 0u; i < 5u; i++) {
+    std::atomic_uint wake_count(0u);
+    std::vector<std::thread> threads;
+    for (size_t j = 0u; j < 4u; j++) {
+      threads.push_back(std::thread([&ev, &wake_count]() {
+        if (rand() % 2 == 0)
+          ev.Wait();
+        else
+          EXPECT_FALSE(ev.WaitWithTimeout(test::ActionTimeout()));
+        wake_count.fetch_add(1u);
+        // Note: We can't say anything about the signaled state of |ev| here,
+        // since the main thread may have already signaled it again.
+      }));
+    }
+
+    // Unfortunately, we can't really wait for the threads to be waiting, so we
+    // just sleep for a bit, and count on them having started and advanced to
+    // waiting.
+    test::Sleep(2 * test::TinyTimeout());
+
+    for (size_t j = 0u; j < threads.size(); j++) {
+      unsigned old_wake_count = wake_count.load();
+      EXPECT_EQ(j, old_wake_count);
+
+      // Each |Signal()| should wake exactly one thread.
+      ev.Signal();
+
+      // Poll for |wake_count| to change.
+      while (wake_count.load() == old_wake_count)
+        test::Sleep(test::EpsilonTimeout());
+
+      EXPECT_FALSE(ev.IsSignaledForTest());
+
+      // And once it's changed, wait a little longer, to see if any other
+      // threads are awoken (they shouldn't be).
+      test::Sleep(test::EpsilonTimeout());
+
+      EXPECT_EQ(old_wake_count + 1u, wake_count.load());
+
+      EXPECT_FALSE(ev.IsSignaledForTest());
+    }
+
+    // Having done that, if we signal |ev| now, it should stay signaled.
+    ev.Signal();
+    test::Sleep(test::EpsilonTimeout());
+    EXPECT_TRUE(ev.IsSignaledForTest());
+
+    for (auto& thread : threads)
+      thread.join();
+
+    ev.Reset();
+  }
+}
+
+TEST(AutoResetWaitableEventTest, Timeouts) {
+  static const unsigned kTestTimeoutsMs[] = {0, 10, 20, 40, 80};
+
+  test::Stopwatch stopwatch;
+
+  AutoResetWaitableEvent ev;
+
+  for (size_t i = 0u; i < MOJO_ARRAYSIZE(kTestTimeoutsMs); i++) {
+    uint64_t timeout = test::DeadlineFromMilliseconds(kTestTimeoutsMs[i]);
+
+    stopwatch.Start();
+    EXPECT_TRUE(ev.WaitWithTimeout(timeout));
+    MojoDeadline elapsed = stopwatch.Elapsed();
+
+    // It should time out after *at least* the specified amount of time.
+    EXPECT_GE(elapsed, timeout);
+    // But we expect that it should time out soon after that amount of time.
+    EXPECT_LT(elapsed, timeout + test::EpsilonTimeout());
+  }
+}
+
+// ManualResetWaitableEvent ----------------------------------------------------
+
+TEST(ManualResetWaitableEventTest, Basic) {
+  ManualResetWaitableEvent ev;
+  EXPECT_FALSE(ev.IsSignaledForTest());
+  ev.Signal();
+  EXPECT_TRUE(ev.IsSignaledForTest());
+  ev.Wait();
+  EXPECT_TRUE(ev.IsSignaledForTest());
+  ev.Reset();
+  EXPECT_FALSE(ev.IsSignaledForTest());
+  EXPECT_TRUE(ev.WaitWithTimeout(0));
+  EXPECT_FALSE(ev.IsSignaledForTest());
+  EXPECT_TRUE(ev.WaitWithTimeout(test::DeadlineFromMilliseconds(1)));
+  EXPECT_FALSE(ev.IsSignaledForTest());
+  ev.Signal();
+  EXPECT_TRUE(ev.IsSignaledForTest());
+  EXPECT_FALSE(ev.WaitWithTimeout(0));
+  EXPECT_TRUE(ev.IsSignaledForTest());
+  EXPECT_FALSE(ev.WaitWithTimeout(test::DeadlineFromMilliseconds(1)));
+  EXPECT_TRUE(ev.IsSignaledForTest());
+}
+
+TEST(ManualResetWaitableEventTest, SignalMultiple) {
+  ManualResetWaitableEvent ev;
+
+  for (size_t i = 0u; i < 10u; i++) {
+    for (size_t num_waiters = 1u; num_waiters < 5u; num_waiters++) {
+      std::vector<std::thread> threads;
+      for (size_t j = 0u; j < num_waiters; j++) {
+        threads.push_back(std::thread([&ev]() {
+          EpsilonRandomSleep();
+
+          if (rand() % 2 == 0)
+            ev.Wait();
+          else
+            EXPECT_FALSE(ev.WaitWithTimeout(test::ActionTimeout()));
+        }));
+      }
+
+      EpsilonRandomSleep();
+
+      ev.Signal();
+
+      // The threads will only terminate once they've successfully waited (or
+      // timed out).
+      for (auto& thread : threads)
+        thread.join();
+
+      ev.Reset();
+    }
+  }
+}
+
+// Tries to test that threads that are awoken may immediately call |Reset()|
+// without affecting other threads that are awoken.
+TEST(ManualResetWaitableEventTest, SignalMultipleWaitReset) {
+  ManualResetWaitableEvent ev;
+
+  for (size_t i = 0u; i < 5u; i++) {
+    std::vector<std::thread> threads;
+    for (size_t j = 0u; j < 4u; j++) {
+      threads.push_back(std::thread([&ev]() {
+        if (rand() % 2 == 0)
+          ev.Wait();
+        else
+          EXPECT_FALSE(ev.WaitWithTimeout(test::ActionTimeout()));
+        ev.Reset();
+      }));
+    }
+
+    // Unfortunately, we can't really wait for the threads to be waiting, so we
+    // just sleep for a bit, and count on them having started and advanced to
+    // waiting.
+    test::Sleep(2 * test::TinyTimeout());
+
+    ev.Signal();
+
+    // In fact, we may ourselves call |Reset()| immediately.
+    ev.Reset();
+
+    // The threads will only terminate once they've successfully waited (or
+    // timed out).
+    for (auto& thread : threads)
+      thread.join();
+
+    ASSERT_FALSE(ev.IsSignaledForTest());
+  }
+}
+
+TEST(ManualResetWaitableEventTest, Timeouts) {
+  static const unsigned kTestTimeoutsMs[] = {0, 10, 20, 40, 80};
+
+  test::Stopwatch stopwatch;
+
+  ManualResetWaitableEvent ev;
+
+  for (size_t i = 0u; i < MOJO_ARRAYSIZE(kTestTimeoutsMs); i++) {
+    uint64_t timeout = test::DeadlineFromMilliseconds(kTestTimeoutsMs[i]);
+
+    stopwatch.Start();
+    EXPECT_TRUE(ev.WaitWithTimeout(timeout));
+    MojoDeadline elapsed = stopwatch.Elapsed();
+
+    // It should time out after *at least* the specified amount of time.
+    EXPECT_GE(elapsed, timeout);
+    // But we expect that it should time out soon after that amount of time.
+    EXPECT_LT(elapsed, timeout + test::EpsilonTimeout());
+  }
+}
+
+}  // namespace
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/waiter.cc b/mojo/edk/system/waiter.cc
index f18edc8..540dcb6 100644
--- a/mojo/edk/system/waiter.cc
+++ b/mojo/edk/system/waiter.cc
@@ -4,16 +4,16 @@
 
 #include "mojo/edk/system/waiter.h"
 
-#include <limits>
-
 #include "base/logging.h"
 #include "base/time/time.h"
 
+using mojo::util::MutexLocker;
+
 namespace mojo {
 namespace system {
 
 Waiter::Waiter()
-    : cv_(&lock_),
+    :
 #ifndef NDEBUG
       initialized_(false),
 #endif
@@ -37,7 +37,7 @@
 
 // TODO(vtl): Fast-path the |deadline == 0| case?
 MojoResult Waiter::Wait(MojoDeadline deadline, uint32_t* context) {
-  base::AutoLock locker(lock_);
+  MutexLocker locker(&mutex_);
 
 #ifndef NDEBUG
   DCHECK(initialized_);
@@ -53,27 +53,36 @@
     return awake_result_;
   }
 
-  // |MojoDeadline| is actually a |uint64_t|, but we need a signed quantity.
-  // Treat any out-of-range deadline as "forever" (which is wrong, but okay
-  // since 2^63 microseconds is ~300000 years). Note that this also takes care
-  // of the |MOJO_DEADLINE_INDEFINITE| (= 2^64 - 1) case.
-  if (deadline > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
+  if (deadline == MOJO_DEADLINE_INDEFINITE) {
     do {
-      cv_.Wait();
+      cv_.Wait(&mutex_);
     } while (!awoken_);
   } else {
-    // NOTE(vtl): This is very inefficient on POSIX, since pthreads condition
-    // variables take an absolute deadline.
-    const base::TimeTicks end_time =
-        base::TimeTicks::Now() +
-        base::TimeDelta::FromMicroseconds(static_cast<int64_t>(deadline));
-    do {
-      base::TimeTicks now_time = base::TimeTicks::Now();
-      if (now_time >= end_time)
+    // We may get spurious wakeups, so record the start time and track the
+    // remaining timeout.
+    uint64_t wait_remaining = deadline;
+    auto start = base::TimeTicks::Now();
+    while (true) {
+      // NOTE(vtl): Possibly, we should add a version of |WaitWithTimeout()|
+      // that takes an absolute deadline, since that's what pthreads takes.
+      if (cv_.WaitWithTimeout(&mutex_, wait_remaining))
+        return MOJO_RESULT_DEADLINE_EXCEEDED;  // Definitely timed out.
+
+      // Otherwise, we may have been awoken.
+      if (awoken_)
+        break;
+
+      // Or the wakeup may have been spurious.
+      auto now = base::TimeTicks::Now();
+      DCHECK_GE(now, start);
+      uint64_t elapsed = static_cast<uint64_t>((now - start).InMicroseconds());
+      // It's possible that the deadline has passed anyway.
+      if (elapsed >= deadline)
         return MOJO_RESULT_DEADLINE_EXCEEDED;
 
-      cv_.TimedWait(end_time - now_time);
-    } while (!awoken_);
+      // Otherwise, recalculate the amount that we have left to wait.
+      wait_remaining = deadline - elapsed;
+    }
   }
 
   DCHECK_NE(awake_result_, MOJO_RESULT_INTERNAL);
@@ -83,7 +92,7 @@
 }
 
 bool Waiter::Awake(MojoResult result, uintptr_t context) {
-  base::AutoLock locker(lock_);
+  MutexLocker locker(&mutex_);
 
   if (awoken_)
     return true;
@@ -92,7 +101,8 @@
   awake_result_ = result;
   awake_context_ = context;
   cv_.Signal();
-  // |cv_.Wait()|/|cv_.TimedWait()| will return after |lock_| is released.
+  // |cv_.Wait()|/|cv_.WaitWithTimeout()| will return after |mutex_| is
+  // released.
   return true;
 }
 
diff --git a/mojo/edk/system/waiter.h b/mojo/edk/system/waiter.h
index d676789..0a2f7ee 100644
--- a/mojo/edk/system/waiter.h
+++ b/mojo/edk/system/waiter.h
@@ -7,9 +7,10 @@
 
 #include <stdint.h>
 
-#include "base/synchronization/condition_variable.h"
-#include "base/synchronization/lock.h"
 #include "mojo/edk/system/awakable.h"
+#include "mojo/edk/util/cond_var.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/edk/util/thread_annotations.h"
 #include "mojo/public/c/system/types.h"
 #include "mojo/public/cpp/system/macros.h"
 
@@ -27,7 +28,7 @@
 
   // A |Waiter| can be used multiple times; |Init()| should be called before
   // each time it's used.
-  void Init();
+  void Init() MOJO_NOT_THREAD_SAFE;
 
   // Waits until a suitable |Awake()| is called. (|context| may be null, in
   // which case, obviously no context is ever returned.)
@@ -60,14 +61,14 @@
   bool Awake(MojoResult result, uintptr_t context) override;
 
  private:
-  base::ConditionVariable cv_;  // Associated to |lock_|.
-  base::Lock lock_;             // Protects the following members.
+  util::CondVar cv_;  // Associated to |mutex_|.
+  util::Mutex mutex_;
 #ifndef NDEBUG
-  bool initialized_;
+  bool initialized_ MOJO_GUARDED_BY(mutex_);
 #endif
-  bool awoken_;
-  MojoResult awake_result_;
-  uintptr_t awake_context_;
+  bool awoken_ MOJO_GUARDED_BY(mutex_);
+  MojoResult awake_result_ MOJO_GUARDED_BY(mutex_);
+  uintptr_t awake_context_ MOJO_GUARDED_BY(mutex_);
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(Waiter);
 };
diff --git a/mojo/edk/system/waiter_test_utils.cc b/mojo/edk/system/waiter_test_utils.cc
index 9b54909..be2e9a3 100644
--- a/mojo/edk/system/waiter_test_utils.cc
+++ b/mojo/edk/system/waiter_test_utils.cc
@@ -6,6 +6,8 @@
 
 #include <utility>
 
+using mojo::util::RefPtr;
+
 namespace mojo {
 namespace system {
 namespace test {
diff --git a/mojo/edk/system/waiter_test_utils.h b/mojo/edk/system/waiter_test_utils.h
index b45ba09..585cc5b 100644
--- a/mojo/edk/system/waiter_test_utils.h
+++ b/mojo/edk/system/waiter_test_utils.h
@@ -9,9 +9,9 @@
 
 #include "mojo/edk/system/dispatcher.h"
 #include "mojo/edk/system/handle_signals_state.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/system/test/simple_test_thread.h"
 #include "mojo/edk/system/waiter.h"
-#include "mojo/edk/test/simple_test_thread.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/c/system/types.h"
 #include "mojo/public/cpp/system/macros.h"
 
@@ -43,7 +43,7 @@
 // code). (We accept this unrealism for simplicity, since |AwakableList| is
 // thread-unsafe so making it more realistic would require adding nontrivial
 // synchronization machinery.)
-class SimpleWaiterThread : public mojo::test::SimpleTestThread {
+class SimpleWaiterThread : public test::SimpleTestThread {
  public:
   // For the duration of the lifetime of this object, |*result| belongs to it
   // (in the sense that it will write to it whenever it wants).
@@ -65,12 +65,12 @@
 // This is a more complex and realistic thread that has a |Waiter|, on which it
 // waits for the given deadline (with the given flags). Unlike
 // |SimpleWaiterThread|, it requires the machinery of |Dispatcher|.
-class WaiterThread : public mojo::test::SimpleTestThread {
+class WaiterThread : public test::SimpleTestThread {
  public:
   // Note: |*did_wait_out|, |*result_out|, |*context_out| and
   // |*signals_state_out| "belong" to this object (i.e., may be modified by, on
   // some other thread) while it's alive.
-  WaiterThread(RefPtr<Dispatcher>&& dispatcher,
+  WaiterThread(util::RefPtr<Dispatcher>&& dispatcher,
                MojoHandleSignals handle_signals,
                MojoDeadline deadline,
                uint32_t context,
@@ -83,7 +83,7 @@
  private:
   void Run() override;
 
-  const RefPtr<Dispatcher> dispatcher_;
+  const util::RefPtr<Dispatcher> dispatcher_;
   const MojoHandleSignals handle_signals_;
   const MojoDeadline deadline_;
   const uint32_t context_;
diff --git a/mojo/edk/system/waiter_unittest.cc b/mojo/edk/system/waiter_unittest.cc
index 7406e06..9a344f5 100644
--- a/mojo/edk/system/waiter_unittest.cc
+++ b/mojo/edk/system/waiter_unittest.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a
-// heavily-loaded system). Sorry. |test::EpsilonDeadline()| may be increased to
+// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to
 // increase tolerance and reduce observed flakiness (though doing so reduces the
 // meaningfulness of the test).
 
@@ -11,19 +11,24 @@
 
 #include <stdint.h>
 
-#include "mojo/edk/system/mutex.h"
-#include "mojo/edk/system/test_utils.h"
-#include "mojo/edk/test/simple_test_thread.h"
+#include "mojo/edk/system/test/simple_test_thread.h"
+#include "mojo/edk/system/test/sleep.h"
+#include "mojo/edk/system/test/stopwatch.h"
+#include "mojo/edk/system/test/timeouts.h"
+#include "mojo/edk/util/mutex.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using mojo::util::Mutex;
+using mojo::util::MutexLocker;
+
 namespace mojo {
 namespace system {
 namespace {
 
 const unsigned kPollTimeMs = 10;
 
-class WaitingThread : public mojo::test::SimpleTestThread {
+class WaitingThread : public test::SimpleTestThread {
  public:
   explicit WaitingThread(MojoDeadline deadline)
       : deadline_(deadline),
@@ -49,7 +54,7 @@
         }
       }
 
-      test::Sleep(test::DeadlineFromMilliseconds(kPollTimeMs));
+      test::SleepMilliseconds(kPollTimeMs);
     }
   }
 
@@ -96,61 +101,61 @@
 
   // Awake immediately after thread start.
   {
-    WaitingThread thread(10 * test::EpsilonDeadline());
+    WaitingThread thread(10 * test::EpsilonTimeout());
     thread.Start();
     thread.waiter()->Awake(MOJO_RESULT_OK, 1);
     thread.WaitUntilDone(&result, &context, &elapsed);
     EXPECT_EQ(MOJO_RESULT_OK, result);
     EXPECT_EQ(1u, context);
-    EXPECT_LT(elapsed, test::EpsilonDeadline());
+    EXPECT_LT(elapsed, test::EpsilonTimeout());
   }
 
   // Awake before after thread start.
   {
-    WaitingThread thread(10 * test::EpsilonDeadline());
+    WaitingThread thread(10 * test::EpsilonTimeout());
     thread.waiter()->Awake(MOJO_RESULT_CANCELLED, 2);
     thread.Start();
     thread.WaitUntilDone(&result, &context, &elapsed);
     EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
     EXPECT_EQ(2u, context);
-    EXPECT_LT(elapsed, test::EpsilonDeadline());
+    EXPECT_LT(elapsed, test::EpsilonTimeout());
   }
 
   // Awake some time after thread start.
   {
-    WaitingThread thread(10 * test::EpsilonDeadline());
+    WaitingThread thread(10 * test::EpsilonTimeout());
     thread.Start();
-    test::Sleep(2 * test::EpsilonDeadline());
+    test::Sleep(2 * test::EpsilonTimeout());
     thread.waiter()->Awake(1, 3);
     thread.WaitUntilDone(&result, &context, &elapsed);
     EXPECT_EQ(1u, result);
     EXPECT_EQ(3u, context);
-    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
-    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
   }
 
   // Awake some longer time after thread start.
   {
-    WaitingThread thread(10 * test::EpsilonDeadline());
+    WaitingThread thread(10 * test::EpsilonTimeout());
     thread.Start();
-    test::Sleep(5 * test::EpsilonDeadline());
+    test::Sleep(5 * test::EpsilonTimeout());
     thread.waiter()->Awake(2, 4);
     thread.WaitUntilDone(&result, &context, &elapsed);
     EXPECT_EQ(2u, result);
     EXPECT_EQ(4u, context);
-    EXPECT_GT(elapsed, (5 - 1) * test::EpsilonDeadline());
-    EXPECT_LT(elapsed, (5 + 1) * test::EpsilonDeadline());
+    EXPECT_GT(elapsed, (5 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (5 + 1) * test::EpsilonTimeout());
   }
 
   // Don't awake -- time out (on another thread).
   {
-    WaitingThread thread(2 * test::EpsilonDeadline());
+    WaitingThread thread(2 * test::EpsilonTimeout());
     thread.Start();
     thread.WaitUntilDone(&result, &context, &elapsed);
     EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result);
     EXPECT_EQ(static_cast<uint32_t>(-1), context);
-    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
-    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
   }
 
   // No (indefinite) deadline.
@@ -163,7 +168,7 @@
     thread.WaitUntilDone(&result, &context, &elapsed);
     EXPECT_EQ(MOJO_RESULT_OK, result);
     EXPECT_EQ(5u, context);
-    EXPECT_LT(elapsed, test::EpsilonDeadline());
+    EXPECT_LT(elapsed, test::EpsilonTimeout());
   }
 
   // Awake before after thread start.
@@ -174,33 +179,33 @@
     thread.WaitUntilDone(&result, &context, &elapsed);
     EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
     EXPECT_EQ(6u, context);
-    EXPECT_LT(elapsed, test::EpsilonDeadline());
+    EXPECT_LT(elapsed, test::EpsilonTimeout());
   }
 
   // Awake some time after thread start.
   {
     WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
     thread.Start();
-    test::Sleep(2 * test::EpsilonDeadline());
+    test::Sleep(2 * test::EpsilonTimeout());
     thread.waiter()->Awake(1, 7);
     thread.WaitUntilDone(&result, &context, &elapsed);
     EXPECT_EQ(1u, result);
     EXPECT_EQ(7u, context);
-    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
-    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
   }
 
   // Awake some longer time after thread start.
   {
     WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
     thread.Start();
-    test::Sleep(5 * test::EpsilonDeadline());
+    test::Sleep(5 * test::EpsilonTimeout());
     thread.waiter()->Awake(2, 8);
     thread.WaitUntilDone(&result, &context, &elapsed);
     EXPECT_EQ(2u, result);
     EXPECT_EQ(8u, context);
-    EXPECT_GT(elapsed, (5 - 1) * test::EpsilonDeadline());
-    EXPECT_LT(elapsed, (5 + 1) * test::EpsilonDeadline());
+    EXPECT_GT(elapsed, (5 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (5 + 1) * test::EpsilonTimeout());
   }
 }
 
@@ -215,25 +220,25 @@
   stopwatch.Start();
   EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, &context));
   elapsed = stopwatch.Elapsed();
-  EXPECT_LT(elapsed, test::EpsilonDeadline());
+  EXPECT_LT(elapsed, test::EpsilonTimeout());
   EXPECT_EQ(123u, context);
 
   waiter.Init();
   stopwatch.Start();
   EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
-            waiter.Wait(2 * test::EpsilonDeadline(), &context));
+            waiter.Wait(2 * test::EpsilonTimeout(), &context));
   elapsed = stopwatch.Elapsed();
-  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
-  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
+  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
   EXPECT_EQ(123u, context);
 
   waiter.Init();
   stopwatch.Start();
   EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
-            waiter.Wait(5 * test::EpsilonDeadline(), &context));
+            waiter.Wait(5 * test::EpsilonTimeout(), &context));
   elapsed = stopwatch.Elapsed();
-  EXPECT_GT(elapsed, (5 - 1) * test::EpsilonDeadline());
-  EXPECT_LT(elapsed, (5 + 1) * test::EpsilonDeadline());
+  EXPECT_GT(elapsed, (5 - 1) * test::EpsilonTimeout());
+  EXPECT_LT(elapsed, (5 + 1) * test::EpsilonTimeout());
   EXPECT_EQ(123u, context);
 }
 
@@ -251,7 +256,7 @@
     thread.WaitUntilDone(&result, &context, &elapsed);
     EXPECT_EQ(MOJO_RESULT_OK, result);
     EXPECT_EQ(1u, context);
-    EXPECT_LT(elapsed, test::EpsilonDeadline());
+    EXPECT_LT(elapsed, test::EpsilonTimeout());
   }
 
   {
@@ -262,33 +267,33 @@
     thread.WaitUntilDone(&result, &context, &elapsed);
     EXPECT_EQ(1u, result);
     EXPECT_EQ(3u, context);
-    EXPECT_LT(elapsed, test::EpsilonDeadline());
+    EXPECT_LT(elapsed, test::EpsilonTimeout());
   }
 
   {
     WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
     thread.Start();
     thread.waiter()->Awake(10, 5);
-    test::Sleep(2 * test::EpsilonDeadline());
+    test::Sleep(2 * test::EpsilonTimeout());
     thread.waiter()->Awake(20, 6);
     thread.WaitUntilDone(&result, &context, &elapsed);
     EXPECT_EQ(10u, result);
     EXPECT_EQ(5u, context);
-    EXPECT_LT(elapsed, test::EpsilonDeadline());
+    EXPECT_LT(elapsed, test::EpsilonTimeout());
   }
 
   {
-    WaitingThread thread(10 * test::EpsilonDeadline());
+    WaitingThread thread(10 * test::EpsilonTimeout());
     thread.Start();
-    test::Sleep(1 * test::EpsilonDeadline());
+    test::Sleep(1 * test::EpsilonTimeout());
     thread.waiter()->Awake(MOJO_RESULT_FAILED_PRECONDITION, 7);
-    test::Sleep(2 * test::EpsilonDeadline());
+    test::Sleep(2 * test::EpsilonTimeout());
     thread.waiter()->Awake(MOJO_RESULT_OK, 8);
     thread.WaitUntilDone(&result, &context, &elapsed);
     EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
     EXPECT_EQ(7u, context);
-    EXPECT_GT(elapsed, (1 - 1) * test::EpsilonDeadline());
-    EXPECT_LT(elapsed, (1 + 1) * test::EpsilonDeadline());
+    EXPECT_GT(elapsed, (1 - 1) * test::EpsilonTimeout());
+    EXPECT_LT(elapsed, (1 + 1) * test::EpsilonTimeout());
   }
 }
 
diff --git a/mojo/edk/test/BUILD.gn b/mojo/edk/test/BUILD.gn
index 6a905ee..8fab4ee 100644
--- a/mojo/edk/test/BUILD.gn
+++ b/mojo/edk/test/BUILD.gn
@@ -3,8 +3,6 @@
 # found in the LICENSE file.
 
 import("../mojo_edk.gni")
-import("../../public/mojo.gni")
-import("//testing/test.gni")
 
 mojo_edk_source_set("test_support") {
   testonly = true
@@ -13,14 +11,8 @@
     "multiprocess_test_helper.h",
     "scoped_ipc_support.cc",
     "scoped_ipc_support.h",
-    "scoped_test_dir.cc",
-    "scoped_test_dir.h",
-    "simple_test_thread.cc",
-    "simple_test_thread.h",
-    "test_io_thread.cc",
-    "test_io_thread.h",
+    "test_utils.cc",
     "test_utils.h",
-    "test_utils_posix.cc",
   ]
 
   deps = [
@@ -51,8 +43,6 @@
   ]
 
   mojo_edk_deps = [ "mojo/edk/system" ]
-
-  mojo_sdk_deps = [ "mojo/public/c/test_support" ]
 }
 
 mojo_edk_source_set("run_all_perftests") {
@@ -65,8 +55,6 @@
 
   mojo_edk_deps = [ "mojo/edk/system" ]
 
-  mojo_sdk_deps = [ "mojo/public/c/test_support" ]
-
   sources = [
     "run_all_perftests.cc",
   ]
@@ -79,92 +67,16 @@
     "//base/test:test_support",
   ]
 
-  mojo_sdk_deps = [ "mojo/public/c/test_support" ]
+  mojo_sdk_deps = [ "mojo/public/cpp/test_support" ]
 
   mojo_sdk_public_deps = [ "mojo/public/cpp/system" ]
 
   sources = [
     "test_support_impl.cc",
-    "test_support_impl.h",
-  ]
-}
-
-# Public SDK test targets follow. These targets are not defined within the
-# public SDK itself as running the unittests requires the EDK.
-# TODO(vtl): These don't really belong here. (They should be converted to
-# apptests, but even apart from that these targets belong somewhere else.)
-
-group("public_tests") {
-  testonly = true
-  deps = [
-    ":mojo_public_bindings_perftests",
-    ":mojo_public_bindings_unittests",
-    ":mojo_public_environment_unittests",
-    ":mojo_public_system_perftests",
-    ":mojo_public_system_unittests",
-    ":mojo_public_utility_unittests",
-    ":mojo_system_impl_private_unittests",
   ]
 
-  if (mojo_use_application_in_sdk) {
-    deps += [ ":mojo_public_application_unittests" ]
-  }
-}
-
-if (mojo_use_application_in_sdk) {
-  test("mojo_public_application_unittests") {
-    deps = [
-      ":run_all_unittests",
-      "../../public/cpp/application/tests",
-    ]
-  }
-}
-
-test("mojo_public_bindings_unittests") {
-  deps = [
+  visibility = [
     ":run_all_unittests",
-    "../../public/cpp/bindings/tests",
-  ]
-}
-
-test("mojo_public_bindings_perftests") {
-  deps = [
     ":run_all_perftests",
-    "../../public/cpp/bindings/tests:perftests",
-  ]
-}
-
-test("mojo_public_environment_unittests") {
-  deps = [
-    ":run_all_unittests",
-    "../../public/cpp/environment/tests",
-  ]
-}
-
-test("mojo_public_system_perftests") {
-  deps = [
-    ":run_all_perftests",
-    "../../public/c/system/tests:perftests",
-  ]
-}
-
-test("mojo_public_system_unittests") {
-  deps = [
-    ":run_all_unittests",
-    "../../public/cpp/system/tests",
-  ]
-}
-
-test("mojo_public_utility_unittests") {
-  deps = [
-    ":run_all_unittests",
-    "../../public/cpp/utility/tests",
-  ]
-}
-
-test("mojo_system_impl_private_unittests") {
-  deps = [
-    ":run_all_unittests",
-    "../../public/platform/native:system_impl_private_tests",
   ]
 }
diff --git a/mojo/edk/test/multiprocess_test_helper.cc b/mojo/edk/test/multiprocess_test_helper.cc
index 81fbdcc..65265a3 100644
--- a/mojo/edk/test/multiprocess_test_helper.cc
+++ b/mojo/edk/test/multiprocess_test_helper.cc
@@ -6,6 +6,7 @@
 
 #include "base/command_line.h"
 #include "base/logging.h"
+#include "base/test/test_timeouts.h"
 #include "mojo/edk/embedder/platform_channel_pair.h"
 
 namespace mojo {
diff --git a/mojo/edk/test/multiprocess_test_helper.h b/mojo/edk/test/multiprocess_test_helper.h
index 2d02796..952df04 100644
--- a/mojo/edk/test/multiprocess_test_helper.h
+++ b/mojo/edk/test/multiprocess_test_helper.h
@@ -10,7 +10,6 @@
 
 #include "base/process/process.h"
 #include "base/test/multiprocess_test.h"
-#include "base/test/test_timeouts.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "testing/multiprocess_func_list.h"
diff --git a/mojo/edk/test/run_all_perftests.cc b/mojo/edk/test/run_all_perftests.cc
index f0eacc6..998dffb 100644
--- a/mojo/edk/test/run_all_perftests.cc
+++ b/mojo/edk/test/run_all_perftests.cc
@@ -4,11 +4,8 @@
 
 #include "base/test/perf_test_suite.h"
 #include "mojo/edk/embedder/test_embedder.h"
-#include "mojo/edk/test/test_support_impl.h"
-#include "mojo/public/tests/test_support_private.h"
 
 int main(int argc, char** argv) {
   mojo::embedder::test::InitWithSimplePlatformSupport();
-  mojo::test::TestSupport::Init(new mojo::test::TestSupportImpl());
   return base::PerfTestSuite(argc, argv).Run();
 }
diff --git a/mojo/edk/test/run_all_unittests.cc b/mojo/edk/test/run_all_unittests.cc
index 2059347..d5cea6f 100644
--- a/mojo/edk/test/run_all_unittests.cc
+++ b/mojo/edk/test/run_all_unittests.cc
@@ -8,8 +8,6 @@
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/test_suite.h"
 #include "mojo/edk/embedder/test_embedder.h"
-#include "mojo/edk/test/test_support_impl.h"
-#include "mojo/public/tests/test_support_private.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 int main(int argc, char** argv) {
@@ -31,7 +29,6 @@
   base::TestSuite test_suite(argc, argv);
 
   mojo::embedder::test::InitWithSimplePlatformSupport();
-  mojo::test::TestSupport::Init(new mojo::test::TestSupportImpl());
 
   return base::LaunchUnitTests(
       argc, argv,
diff --git a/mojo/edk/test/simple_test_thread.cc b/mojo/edk/test/simple_test_thread.cc
deleted file mode 100644
index 6cf9f11..0000000
--- a/mojo/edk/test/simple_test_thread.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/edk/test/simple_test_thread.h"
-
-#include "base/logging.h"
-
-namespace mojo {
-namespace test {
-
-SimpleTestThread::SimpleTestThread() : thread_(this, "SimpleTestThread") {}
-
-SimpleTestThread::~SimpleTestThread() {}
-
-void SimpleTestThread::Start() {
-  thread_.Start();
-}
-
-void SimpleTestThread::Join() {
-  thread_.Join();
-}
-
-}  // namespace test
-}  // namespace mojo
diff --git a/mojo/edk/test/simple_test_thread.h b/mojo/edk/test/simple_test_thread.h
deleted file mode 100644
index 36ec7cd..0000000
--- a/mojo/edk/test/simple_test_thread.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MOJO_EDK_TEST_SIMPLE_TEST_THREAD_
-#define MOJO_EDK_TEST_SIMPLE_TEST_THREAD_
-
-#include "base/threading/simple_thread.h"
-#include "mojo/public/cpp/system/macros.h"
-
-namespace mojo {
-namespace test {
-
-// Class to help simple threads (with no message loops) in tests.
-class SimpleTestThread : public base::DelegateSimpleThread::Delegate {
- public:
-  SimpleTestThread();
-  ~SimpleTestThread() override;
-
-  // Starts the thread.
-  void Start();
-
-  // Joins the thread; this must be called if the thread was started.
-  void Join();
-
-  // Note: Subclasses must implement:
-  //   virtual void Run() = 0;
-  // TODO(vtl): When we stop using |base::DelegateSimpleThread|, this will
-  // directly become part of our interface.
-
- private:
-  base::DelegateSimpleThread thread_;
-
-  MOJO_DISALLOW_COPY_AND_ASSIGN(SimpleTestThread);
-};
-
-}  // namespace test
-}  // namespace mojo
-
-#endif  // MOJO_EDK_TEST_SIMPLE_TEST_THREAD_
diff --git a/mojo/edk/test/test_support_impl.cc b/mojo/edk/test/test_support_impl.cc
index 4bf11bd..3f84b8f 100644
--- a/mojo/edk/test/test_support_impl.cc
+++ b/mojo/edk/test/test_support_impl.cc
@@ -2,12 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "mojo/edk/test/test_support_impl.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
+#include "mojo/public/cpp/test_support/test_support.h"
 
 #include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
@@ -20,7 +15,7 @@
 namespace test {
 namespace {
 
-base::FilePath ResolveSourceRootRelativePath(const char* relative_path) {
+base::FilePath ResolveSourceRootRelativePath(const std::string& relative_path) {
   // TODO(vtl): Have someone inject the source root instead.
   base::FilePath path;
   if (!PathService::Get(base::DIR_SOURCE_ROOT, &path))
@@ -30,16 +25,10 @@
 
 }  // namespace
 
-TestSupportImpl::TestSupportImpl() {
-}
-
-TestSupportImpl::~TestSupportImpl() {
-}
-
-void TestSupportImpl::LogPerfResult(const char* test_name,
-                                    const char* sub_test_name,
-                                    double value,
-                                    const char* units) {
+void LogPerfResult(const char* test_name,
+                   const char* sub_test_name,
+                   double value,
+                   const char* units) {
   DCHECK(test_name);
   if (sub_test_name) {
     std::string name = std::string(test_name) + "/" + sub_test_name;
@@ -49,23 +38,18 @@
   }
 }
 
-FILE* TestSupportImpl::OpenSourceRootRelativeFile(const char* relative_path) {
+FILE* OpenSourceRootRelativeFile(const std::string& relative_path) {
   return base::OpenFile(ResolveSourceRootRelativePath(relative_path), "rb");
 }
 
-char** TestSupportImpl::EnumerateSourceRootRelativeDirectory(
-    const char* relative_path) {
+std::vector<std::string> EnumerateSourceRootRelativeDirectory(
+    const std::string& relative_path) {
   std::vector<std::string> names;
   base::FileEnumerator e(ResolveSourceRootRelativePath(relative_path), false,
                          base::FileEnumerator::FILES);
   for (base::FilePath name = e.Next(); !name.empty(); name = e.Next())
     names.push_back(name.BaseName().AsUTF8Unsafe());
-
-  // |names.size() + 1| for null terminator.
-  char** rv = static_cast<char**>(calloc(names.size() + 1, sizeof(char*)));
-  for (size_t i = 0; i < names.size(); ++i)
-    rv[i] = strdup(names[i].c_str());
-  return rv;
+  return names;
 }
 
 }  // namespace test
diff --git a/mojo/edk/test/test_support_impl.h b/mojo/edk/test/test_support_impl.h
deleted file mode 100644
index 589253a0..0000000
--- a/mojo/edk/test/test_support_impl.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MOJO_EDK_TEST_TEST_SUPPORT_IMPL_H_
-#define MOJO_EDK_TEST_TEST_SUPPORT_IMPL_H_
-
-#include <stdio.h>
-
-#include "mojo/public/cpp/system/macros.h"
-#include "mojo/public/tests/test_support_private.h"
-
-namespace mojo {
-namespace test {
-
-class TestSupportImpl : public TestSupport {
- public:
-  TestSupportImpl();
-  ~TestSupportImpl() override;
-
-  void LogPerfResult(const char* test_name,
-                     const char* sub_test_name,
-                     double value,
-                     const char* units) override;
-  FILE* OpenSourceRootRelativeFile(const char* relative_path) override;
-  char** EnumerateSourceRootRelativeDirectory(
-      const char* relative_path) override;
-
- private:
-  MOJO_DISALLOW_COPY_AND_ASSIGN(TestSupportImpl);
-};
-
-}  // namespace test
-}  // namespace mojo
-
-#endif  // MOJO_EDK_TEST_TEST_SUPPORT_IMPL_H_
diff --git a/mojo/edk/test/test_utils_posix.cc b/mojo/edk/test/test_utils.cc
similarity index 98%
rename from mojo/edk/test/test_utils_posix.cc
rename to mojo/edk/test/test_utils.cc
index 70c1bde..1a04962 100644
--- a/mojo/edk/test/test_utils_posix.cc
+++ b/mojo/edk/test/test_utils.cc
@@ -86,7 +86,7 @@
   CHECK(h.is_valid());
   util::ScopedFILE rv(fdopen(h.release().fd, mode));
   PCHECK(rv) << "fdopen";
-  return rv.Pass();
+  return rv;
 }
 
 }  // namespace test
diff --git a/mojo/edk/util/BUILD.gn b/mojo/edk/util/BUILD.gn
index 380255d..1725b61 100644
--- a/mojo/edk/util/BUILD.gn
+++ b/mojo/edk/util/BUILD.gn
@@ -6,15 +6,65 @@
 
 mojo_edk_source_set("util") {
   sources = [
+    "command_line.cc",
+    "command_line.h",
+    "cond_var.cc",
+    "cond_var.h",
+    "logging_internal.cc",
+    "logging_internal.h",
     "make_unique.h",
+    "mutex.cc",
+    "mutex.h",
+    "ref_counted.h",
+    "ref_counted_internal.h",
+    "ref_ptr.h",
+    "ref_ptr_internal.h",
     "scoped_file.h",
+    "thread_annotations.h",
   ]
 
-  defines = [ "MOJO_SYSTEM_IMPLEMENTATION" ]
-
   mojo_edk_configs = [ "mojo/edk/system:system_config" ]
 
+  mojo_sdk_public_deps = [ "mojo/public/cpp/system" ]
+}
+
+mojo_edk_source_set("unittests") {
+  testonly = true
+  mojo_edk_visibility = [ "mojo/edk/system:mojo_system_unittests" ]
+
+  sources = [
+    "command_line_unittest.cc",
+    "cond_var_unittest.cc",
+    "mutex_unittest.cc",
+    "ref_counted_unittest.cc",
+    "thread_annotations_unittest.cc",
+  ]
+
   deps = [
-    "//base",
+    ":util",
+    "//testing/gtest",
+  ]
+
+  mojo_sdk_deps = [ "mojo/public/cpp/system" ]
+
+  mojo_edk_deps = [ "mojo/edk/system/test" ]
+}
+
+mojo_edk_source_set("perftests") {
+  testonly = true
+  mojo_edk_visibility = [ "mojo/edk/system:mojo_system_perftests" ]
+
+  sources = [
+    "ref_counted_perftest.cc",
+  ]
+
+  deps = [
+    ":util",
+    "//testing/gtest",
+  ]
+
+  mojo_edk_deps = [
+    "mojo/edk/system/test",
+    "mojo/edk/system/test:perf",
   ]
 }
diff --git a/mojo/edk/util/command_line.cc b/mojo/edk/util/command_line.cc
new file mode 100644
index 0000000..3f7701d
--- /dev/null
+++ b/mojo/edk/util/command_line.cc
@@ -0,0 +1,158 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/util/command_line.h"
+
+namespace mojo {
+namespace util {
+
+// CommandLine -----------------------------------------------------------------
+
+CommandLine::Option::Option(const std::string& name) : name(name) {}
+
+CommandLine::Option::Option(const std::string& name, const std::string& value)
+    : name(name), value(value) {}
+
+CommandLine::CommandLine() = default;
+
+CommandLine::CommandLine(const CommandLine& from) = default;
+
+CommandLine::CommandLine(CommandLine&& from) = default;
+
+CommandLine::CommandLine(const std::string& argv0,
+                         const std::vector<Option>& options,
+                         const std::vector<std::string>& positional_args)
+    : has_argv0_(true),
+      argv0_(argv0),
+      options_(options),
+      positional_args_(positional_args) {
+  for (size_t i = 0; i < options_.size(); i++)
+    option_index_[options_[i].name] = i;
+}
+
+CommandLine::~CommandLine() = default;
+
+CommandLine& CommandLine::operator=(const CommandLine& from) = default;
+
+CommandLine& CommandLine::operator=(CommandLine&& from) = default;
+
+bool CommandLine::HasOption(const std::string& name, size_t* index) const {
+  auto it = option_index_.find(name);
+  if (it == option_index_.end())
+    return false;
+  if (index)
+    *index = it->second;
+  return true;
+}
+
+bool CommandLine::GetOptionValue(const std::string& name,
+                                 std::string* value) const {
+  size_t index;
+  if (!HasOption(name, &index))
+    return false;
+  *value = options_[index].value;
+  return true;
+}
+
+std::string CommandLine::GetOptionValueWithDefault(
+    const std::string& name,
+    const std::string& default_value) const {
+  size_t index;
+  if (!HasOption(name, &index))
+    return default_value;
+  return options_[index].value;
+}
+
+// Factory functions (etc.) ----------------------------------------------------
+
+namespace internal {
+
+CommandLineBuilder::CommandLineBuilder() {}
+CommandLineBuilder::~CommandLineBuilder() {}
+
+bool CommandLineBuilder::ProcessArg(const std::string& arg) {
+  if (!has_argv0_) {
+    has_argv0_ = true;
+    argv0_ = arg;
+    return false;
+  }
+
+  // If we've seen a positional argument, then the remaining arguments are also
+  // positional.
+  if (started_positional_args_) {
+    bool rv = positional_args_.empty();
+    positional_args_.push_back(arg);
+    return rv;
+  }
+
+  // Anything that doesn't start with "--" is a positional argument.
+  if (arg.size() < 2u || arg[0] != '-' || arg[1] != '-') {
+    bool rv = positional_args_.empty();
+    started_positional_args_ = true;
+    positional_args_.push_back(arg);
+    return rv;
+  }
+
+  // "--" ends option processing, but isn't stored as a positional argument.
+  if (arg.size() == 2u) {
+    started_positional_args_ = true;
+    return false;
+  }
+
+  // Note: The option name *must* be at least one character, so start at
+  // position 3 -- "--=foo" will yield a name of "=foo" and no value. (Passing a
+  // starting |pos| that's "too big" is OK.)
+  size_t equals_pos = arg.find('=', 3u);
+  if (equals_pos == std::string::npos) {
+    options_.push_back(CommandLine::Option(arg.substr(2u)));
+    return false;
+  }
+
+  options_.push_back(CommandLine::Option(arg.substr(2u, equals_pos - 2u),
+                                         arg.substr(equals_pos + 1u)));
+  return false;
+}
+
+CommandLine CommandLineBuilder::Build() const {
+  if (!has_argv0_)
+    return CommandLine();
+  return CommandLine(argv0_, options_, positional_args_);
+}
+
+}  // namespace internal
+
+std::vector<std::string> CommandLineToArgv(const CommandLine& command_line) {
+  if (!command_line.has_argv0())
+    return std::vector<std::string>();
+
+  std::vector<std::string> argv;
+  const std::vector<CommandLine::Option>& options = command_line.options();
+  const std::vector<std::string>& positional_args =
+      command_line.positional_args();
+  // Reserve space for argv[0], options, maybe a "--" (if needed), and the
+  // positional arguments.
+  argv.reserve(1u + options.size() + 1u + positional_args.size());
+
+  argv.push_back(command_line.argv0());
+  for (const auto& option : options) {
+    if (option.value.empty())
+      argv.push_back("--" + option.name);
+    else
+      argv.push_back("--" + option.name + "=" + option.value);
+  }
+
+  if (!positional_args.empty()) {
+    // Insert a "--" if necessary.
+    if (positional_args[0].size() >= 2u && positional_args[0][0] == '-' &&
+        positional_args[0][1] == '-')
+      argv.push_back("--");
+
+    argv.insert(argv.end(), positional_args.begin(), positional_args.end());
+  }
+
+  return argv;
+}
+
+}  // namespace util
+}  // namespace mojo
diff --git a/mojo/edk/util/command_line.h b/mojo/edk/util/command_line.h
new file mode 100644
index 0000000..4f989b0
--- /dev/null
+++ b/mojo/edk/util/command_line.h
@@ -0,0 +1,224 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Provides a simple class, |CommandLine|, for dealing with command lines (and
+// flags and positional arguments).
+//
+// * Options (a.k.a. flags or switches) are all of the form "--name=<value>" (or
+//   "--name", but this is indistinguishable from "--name="), where <value> is a
+//   string. Not supported: "-name", "-n", "--name <value>", "-n <value>", etc.
+// * Option order is preserved.
+// * Option processing is stopped after the first positional argument[*]. Thus
+//   in the command line "my_program --foo bar --baz", only "--foo" is an option
+//   ("bar" and "--baz" are positional arguments).
+// * Options can be looked up by name. If the same option occurs multiple times,
+//   convention is to use the last occurrence (and the provided look-up
+//   functions behave this way).
+// * "--" may also be used to separate options from positional arguments. Thus
+//   in the command line "my_program --foo -- --bar", "--bar" is a positional
+//   argument.
+// * |CommandLine|s store |argv[0]| and distinguish between not having |argv[0]|
+//   and |argv[0]| being empty.
+// * Apart from being copyable and movable, |CommandLine|s are immutable.
+//
+// There are factory functions to turn raw arguments into |CommandLine|s, in
+// accordance with the above rules. However, |CommandLine|s may be used more
+// generically (with the user transforming arguments using different rules,
+// e.g., accepting "-name" as an option), subject to certain limitations (e.g.,
+// not being able to distinguish "no value" from "empty value").
+//
+// [*] This is somewhat annoying for users, but: a. it's standard Unix behavior
+// for most command line parsers, b. it makes "my_program *" (etc.) safer (which
+// mostly explains a.), c. it makes parsing "subcommands", like "my_program
+// --flag_for_my_program subcommand --flag_for_subcommand" saner.
+
+#ifndef MOJO_EDK_UTIL_COMMAND_LINE_H_
+#define MOJO_EDK_UTIL_COMMAND_LINE_H_
+
+#include <cstddef>
+#include <initializer_list>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace util {
+
+// CommandLine -----------------------------------------------------------------
+
+// Class that stores processed command lines ("argv[0]", options, and positional
+// arguments) and provides access to them. For more details, see the file-level
+// comment above. This class is thread-safe.
+class CommandLine {
+ private:
+  class ConstructionHelper;
+
+ public:
+  struct Option {
+    Option() {}
+    explicit Option(const std::string& name);
+    Option(const std::string& name, const std::string& value);
+
+    bool operator==(const Option& other) const {
+      return name == other.name && value == other.value;
+    }
+    bool operator!=(const Option& other) const { return !operator==(other); }
+
+    std::string name;
+    std::string value;
+  };
+
+  // Default, copy, and move constructors (to be out-of-lined).
+  CommandLine();
+  CommandLine(const CommandLine& from);
+  CommandLine(CommandLine&& from);
+
+  // Constructs a |CommandLine| from its "components". This is especially useful
+  // for creating a new |CommandLine| based on an existing |CommandLine| (e.g.,
+  // adding options or arguments).
+  explicit CommandLine(const std::string& argv0,
+                       const std::vector<Option>& options,
+                       const std::vector<std::string>& positional_args);
+
+  ~CommandLine();
+
+  // Copy and move assignment (to be out-of-lined).
+  CommandLine& operator=(const CommandLine& from);
+  CommandLine& operator=(CommandLine&& from);
+
+  bool has_argv0() const { return has_argv0_; }
+  const std::string& argv0() const { return argv0_; }
+  const std::vector<Option> options() const { return options_; }
+  const std::vector<std::string> positional_args() const {
+    return positional_args_;
+  }
+
+  bool operator==(const CommandLine& other) const {
+    // No need to compare |option_index_|.
+    return has_argv0_ == other.has_argv0_ && argv0_ == other.argv0_ &&
+           options_ == other.options_ &&
+           positional_args_ == other.positional_args_;
+  }
+  bool operator!=(const CommandLine& other) const { return !operator==(other); }
+
+  // Returns true if this command line has the option |name| (and if |index| is
+  // non-null, sets |*index| to the index of the *last* occurrence of the given
+  // option in |options()|) and false if not.
+  bool HasOption(const std::string& name, size_t* index) const;
+
+  // Gets the value of the option |name|. Returns true (and sets |*value|) on
+  // success and false (leaving |*value| alone) on failure.
+  bool GetOptionValue(const std::string& name, std::string* value) const;
+
+  // Gets the value of the option |name|, with a default if the option is not
+  // specified. (Note: This doesn't return a const reference, since this would
+  // make the |default_value| argument inconvenient/dangerous.)
+  std::string GetOptionValueWithDefault(const std::string& name,
+                                        const std::string& default_value) const;
+
+ private:
+  bool has_argv0_ = false;
+  // The following should all be empty if |has_argv0_| is false.
+  std::string argv0_;
+  std::vector<Option> options_;
+  std::vector<std::string> positional_args_;
+
+  // Maps option names to position in |options_|. If a given name occurs
+  // multiple times, the index will be to the *last* occurrence.
+  std::unordered_map<std::string, size_t> option_index_;
+
+  // Allow copy and assignment.
+};
+
+// Factory functions (etc.) ----------------------------------------------------
+
+namespace internal {
+
+// Helper class for building command lines (finding options, etc.) from raw
+// arguments.
+class CommandLineBuilder {
+ public:
+  CommandLineBuilder();
+  ~CommandLineBuilder();
+
+  // Processes an additional argument in the command line. Returns true if |arg|
+  // is the *first* positional argument.
+  bool ProcessArg(const std::string& arg);
+
+  // Builds a |CommandLine| from the arguments processed so far.
+  CommandLine Build() const;
+
+ private:
+  bool has_argv0_ = false;
+  std::string argv0_;
+  std::vector<CommandLine::Option> options_;
+  std::vector<std::string> positional_args_;
+
+  // True if we've started processing positional arguments.
+  bool started_positional_args_ = false;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(CommandLineBuilder);
+};
+
+}  // namespace internal
+
+// The following factory functions create |CommandLine|s from raw arguments in
+// accordance with the rules outlined at the top of this file. (Other ways of
+// transforming raw arguments into options and positional arguments are
+// possible.)
+
+// Like |CommandLineFromIterators()| (see below), but sets
+// |*first_positional_arg| to point to the first positional argument seen (or
+// |last| if none are seen). This is useful for processing "subcommands".
+template <typename InputIterator>
+inline CommandLine CommandLineFromIteratorsFindFirstPositionalArg(
+    InputIterator first,
+    InputIterator last,
+    InputIterator* first_positional_arg) {
+  if (first_positional_arg)
+    *first_positional_arg = last;
+  internal::CommandLineBuilder builder;
+  for (auto it = first; it < last; ++it) {
+    if (builder.ProcessArg(*it)) {
+      if (first_positional_arg)
+        *first_positional_arg = it;
+    }
+  }
+  return builder.Build();
+}
+
+// Builds a |CommandLine| from first/last iterators (where |last| is really
+// one-past-the-last, as usual) to |std::string|s or things that implicitly
+// convert to |std::string|.
+template <typename InputIterator>
+inline CommandLine CommandLineFromIterators(InputIterator first,
+                                            InputIterator last) {
+  return CommandLineFromIteratorsFindFirstPositionalArg<InputIterator>(
+      first, last, nullptr);
+}
+
+// Builds a |CommandLine| from the usual argc/argv.
+inline CommandLine CommandLineFromArgcArgv(int argc, const char* const* argv) {
+  return CommandLineFromIterators(argv, argv + argc);
+}
+
+// Builds a |CommandLine| from an initializer list of |std::string|s or things
+// that implicitly convert to |std::string|.
+template <typename StringType>
+inline CommandLine CommandLineFromInitializerList(
+    std::initializer_list<StringType> argv) {
+  return CommandLineFromIterators(argv.begin(), argv.end());
+}
+
+// This is the "opposite" of the above factory functions, transforming a
+// |CommandLine| into a vector of argument strings according to the rules
+// outlined at the top of this file.
+std::vector<std::string> CommandLineToArgv(const CommandLine& command_line);
+
+}  // namespace util
+}  // namespace mojo
+
+#endif  // MOJO_EDK_UTIL_COMMAND_LINE_H_
diff --git a/mojo/edk/util/command_line_unittest.cc b/mojo/edk/util/command_line_unittest.cc
new file mode 100644
index 0000000..a460c20
--- /dev/null
+++ b/mojo/edk/util/command_line_unittest.cc
@@ -0,0 +1,442 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/util/command_line.h"
+
+#include <utility>
+
+#include "mojo/public/cpp/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace util {
+namespace {
+
+TEST(CommandLineTest, Basic) {
+  // Making this const verifies that the methods called are const.
+  const auto cl = CommandLineFromInitializerList(
+      {"my_program", "--flag1", "--flag2=value2", "arg1", "arg2", "arg3"});
+
+  EXPECT_TRUE(cl.has_argv0());
+  EXPECT_EQ("my_program", cl.argv0());
+
+  EXPECT_EQ(2u, cl.options().size());
+  EXPECT_EQ("flag1", cl.options()[0].name);
+  EXPECT_EQ(std::string(), cl.options()[0].value);
+  EXPECT_EQ("flag2", cl.options()[1].name);
+  EXPECT_EQ("value2", cl.options()[1].value);
+
+  EXPECT_EQ(3u, cl.positional_args().size());
+  EXPECT_EQ("arg1", cl.positional_args()[0]);
+  EXPECT_EQ("arg2", cl.positional_args()[1]);
+  EXPECT_EQ("arg3", cl.positional_args()[2]);
+
+  EXPECT_TRUE(cl.HasOption("flag1", nullptr));
+  size_t index = static_cast<size_t>(-1);
+  EXPECT_TRUE(cl.HasOption("flag2", &index));
+  EXPECT_EQ(1u, index);
+  EXPECT_FALSE(cl.HasOption("flag3", nullptr));
+
+  std::string value = "nonempty";
+  EXPECT_TRUE(cl.GetOptionValue("flag1", &value));
+  EXPECT_EQ(std::string(), value);
+  EXPECT_TRUE(cl.GetOptionValue("flag2", &value));
+  EXPECT_EQ("value2", value);
+  EXPECT_FALSE(cl.GetOptionValue("flag3", &value));
+
+  EXPECT_EQ(std::string(), cl.GetOptionValueWithDefault("flag1", "nope"));
+  EXPECT_EQ("value2", cl.GetOptionValueWithDefault("flag2", "nope"));
+  EXPECT_EQ("nope", cl.GetOptionValueWithDefault("flag3", "nope"));
+}
+
+TEST(CommandLineTest, DefaultConstructor) {
+  CommandLine cl;
+  EXPECT_FALSE(cl.has_argv0());
+  EXPECT_EQ(std::string(), cl.argv0());
+  EXPECT_EQ(std::vector<CommandLine::Option>(), cl.options());
+  EXPECT_EQ(std::vector<std::string>(), cl.positional_args());
+}
+
+TEST(CommandLineTest, ComponentConstructor) {
+  const std::string argv0 = "my_program";
+  const std::vector<CommandLine::Option> options = {
+      CommandLine::Option("flag", "value")};
+  const std::vector<std::string> positional_args = {"arg"};
+
+  CommandLine cl(argv0, options, positional_args);
+  EXPECT_TRUE(cl.has_argv0());
+  EXPECT_EQ(argv0, cl.argv0());
+  EXPECT_EQ(options, cl.options());
+  EXPECT_EQ(positional_args, cl.positional_args());
+  EXPECT_EQ("value", cl.GetOptionValueWithDefault("flag", "nope"));
+}
+
+TEST(CommandLineTest, CommandLineFromIteratorsFindFirstPositionalArg) {
+  // This shows how one might process subcommands.
+  {
+    static std::vector<std::string> argv = {"my_program", "--flag1",
+                                            "--flag2",    "subcommand",
+                                            "--subflag",  "subarg"};
+    auto first = argv.cbegin();
+    auto last = argv.cend();
+    std::vector<std::string>::const_iterator sub_first;
+    auto cl =
+        CommandLineFromIteratorsFindFirstPositionalArg(first, last, &sub_first);
+    EXPECT_TRUE(cl.has_argv0());
+    EXPECT_EQ(argv[0], cl.argv0());
+    std::vector<CommandLine::Option> expected_options = {
+        CommandLine::Option("flag1"), CommandLine::Option("flag2")};
+    EXPECT_EQ(expected_options, cl.options());
+    std::vector<std::string> expected_positional_args = {argv[3], argv[4],
+                                                         argv[5]};
+    EXPECT_EQ(expected_positional_args, cl.positional_args());
+    EXPECT_TRUE(cl.HasOption("flag1", nullptr));
+    EXPECT_TRUE(cl.HasOption("flag2", nullptr));
+    EXPECT_FALSE(cl.HasOption("subflag", nullptr));
+
+    EXPECT_EQ(first + 3, sub_first);
+    auto sub_cl = CommandLineFromIterators(sub_first, last);
+    EXPECT_TRUE(sub_cl.has_argv0());
+    EXPECT_EQ(argv[3], sub_cl.argv0());
+    std::vector<CommandLine::Option> expected_sub_options = {
+        CommandLine::Option("subflag")};
+    EXPECT_EQ(expected_sub_options, sub_cl.options());
+    std::vector<std::string> expected_sub_positional_args = {argv[5]};
+    EXPECT_EQ(expected_sub_positional_args, sub_cl.positional_args());
+    EXPECT_FALSE(sub_cl.HasOption("flag1", nullptr));
+    EXPECT_FALSE(sub_cl.HasOption("flag2", nullptr));
+    EXPECT_TRUE(sub_cl.HasOption("subflag", nullptr));
+  }
+
+  // No positional argument.
+  {
+    static std::vector<std::string> argv = {"my_program", "--flag"};
+    std::vector<std::string>::const_iterator sub_first;
+    auto cl = CommandLineFromIteratorsFindFirstPositionalArg(
+        argv.cbegin(), argv.cend(), &sub_first);
+    EXPECT_EQ(argv.cend(), sub_first);
+  }
+
+  // Multiple positional arguments.
+  {
+    static std::vector<std::string> argv = {"my_program", "arg1", "arg2"};
+    std::vector<std::string>::const_iterator sub_first;
+    auto cl = CommandLineFromIteratorsFindFirstPositionalArg(
+        argv.cbegin(), argv.cend(), &sub_first);
+    EXPECT_EQ(argv.cbegin() + 1, sub_first);
+  }
+
+  // "--".
+  {
+    static std::vector<std::string> argv = {"my_program", "--", "--arg"};
+    std::vector<std::string>::const_iterator sub_first;
+    auto cl = CommandLineFromIteratorsFindFirstPositionalArg(
+        argv.cbegin(), argv.cend(), &sub_first);
+    EXPECT_EQ(argv.cbegin() + 2, sub_first);
+  }
+}
+
+TEST(CommandLineTest, CommmandLineFromIterators) {
+  {
+    // Note (here and below): The |const| ensures that the factory method can
+    // accept const iterators.
+    const std::vector<std::string> argv = {"my_program", "--flag=value", "arg"};
+
+    auto cl = CommandLineFromIterators(argv.begin(), argv.end());
+    EXPECT_TRUE(cl.has_argv0());
+    EXPECT_EQ(argv[0], cl.argv0());
+    std::vector<CommandLine::Option> expected_options = {
+        CommandLine::Option("flag", "value")};
+    EXPECT_EQ(expected_options, cl.options());
+    std::vector<std::string> expected_positional_args = {argv[2]};
+    EXPECT_EQ(expected_positional_args, cl.positional_args());
+    EXPECT_EQ("value", cl.GetOptionValueWithDefault("flag", "nope"));
+  }
+
+  // Can handle empty argv.
+  {
+    const std::vector<std::string> argv;
+
+    auto cl = CommandLineFromIterators(argv.begin(), argv.end());
+    EXPECT_FALSE(cl.has_argv0());
+    EXPECT_EQ(std::string(), cl.argv0());
+    EXPECT_EQ(std::vector<CommandLine::Option>(), cl.options());
+    EXPECT_EQ(std::vector<std::string>(), cl.positional_args());
+  }
+
+  // Can handle empty |argv[0]|.
+  {
+    const std::vector<std::string> argv = {""};
+
+    auto cl = CommandLineFromIterators(argv.begin(), argv.end());
+    EXPECT_TRUE(cl.has_argv0());
+    EXPECT_EQ(std::string(), cl.argv0());
+    EXPECT_EQ(std::vector<CommandLine::Option>(), cl.options());
+    EXPECT_EQ(std::vector<std::string>(), cl.positional_args());
+  }
+
+  // Can also take a vector of |const char*|s.
+  {
+    const std::vector<const char*> argv = {"my_program", "--flag=value", "arg"};
+
+    auto cl = CommandLineFromIterators(argv.begin(), argv.end());
+    EXPECT_TRUE(cl.has_argv0());
+    EXPECT_EQ(argv[0], cl.argv0());
+    std::vector<CommandLine::Option> expected_options = {
+        CommandLine::Option("flag", "value")};
+    EXPECT_EQ(expected_options, cl.options());
+    std::vector<std::string> expected_positional_args = {argv[2]};
+    EXPECT_EQ(expected_positional_args, cl.positional_args());
+    EXPECT_EQ("value", cl.GetOptionValueWithDefault("flag", "nope"));
+  }
+
+  // Or a plain old array.
+  {
+    static const char* const argv[] = {"my_program", "--flag=value", "arg"};
+
+    auto cl = CommandLineFromIterators(argv, argv + MOJO_ARRAYSIZE(argv));
+    EXPECT_TRUE(cl.has_argv0());
+    EXPECT_EQ(argv[0], cl.argv0());
+    std::vector<CommandLine::Option> expected_options = {
+        CommandLine::Option("flag", "value")};
+    EXPECT_EQ(expected_options, cl.options());
+    std::vector<std::string> expected_positional_args = {argv[2]};
+    EXPECT_EQ(expected_positional_args, cl.positional_args());
+    EXPECT_EQ("value", cl.GetOptionValueWithDefault("flag", "nope"));
+  }
+}
+
+TEST(CommandLineTest, CommandLineFromArgcArgv) {
+  static const char* const argv[] = {"my_program", "--flag=value", "arg"};
+  const int argc = static_cast<int>(MOJO_ARRAYSIZE(argv));
+
+  auto cl = CommandLineFromArgcArgv(argc, argv);
+  EXPECT_TRUE(cl.has_argv0());
+  EXPECT_EQ(argv[0], cl.argv0());
+  std::vector<CommandLine::Option> expected_options = {
+      CommandLine::Option("flag", "value")};
+  EXPECT_EQ(expected_options, cl.options());
+  std::vector<std::string> expected_positional_args = {argv[2]};
+  EXPECT_EQ(expected_positional_args, cl.positional_args());
+  EXPECT_EQ("value", cl.GetOptionValueWithDefault("flag", "nope"));
+}
+
+TEST(CommandLineTest, CommandLineFromInitializerList) {
+  {
+    std::initializer_list<const char*> il = {"my_program", "--flag=value",
+                                             "arg"};
+    auto cl = CommandLineFromInitializerList(il);
+    EXPECT_TRUE(cl.has_argv0());
+    EXPECT_EQ("my_program", cl.argv0());
+    std::vector<CommandLine::Option> expected_options = {
+        CommandLine::Option("flag", "value")};
+    EXPECT_EQ(expected_options, cl.options());
+    std::vector<std::string> expected_positional_args = {"arg"};
+    EXPECT_EQ(expected_positional_args, cl.positional_args());
+    EXPECT_EQ("value", cl.GetOptionValueWithDefault("flag", "nope"));
+  }
+
+  {
+    std::initializer_list<std::string> il = {"my_program", "--flag=value",
+                                             "arg"};
+    auto cl = CommandLineFromInitializerList(il);
+    EXPECT_TRUE(cl.has_argv0());
+    EXPECT_EQ("my_program", cl.argv0());
+    std::vector<CommandLine::Option> expected_options = {
+        CommandLine::Option("flag", "value")};
+    EXPECT_EQ(expected_options, cl.options());
+    std::vector<std::string> expected_positional_args = {"arg"};
+    EXPECT_EQ(expected_positional_args, cl.positional_args());
+    EXPECT_EQ("value", cl.GetOptionValueWithDefault("flag", "nope"));
+  }
+}
+
+TEST(CommandLineTest, OddArguments) {
+  {
+    // Except for "arg", these are all options.
+    auto cl =
+        CommandLineFromInitializerList({"my_program", "--=", "--=foo", "--bar=",
+                                        "--==", "--===", "--==x", "arg"});
+    EXPECT_TRUE(cl.has_argv0());
+    EXPECT_EQ("my_program", cl.argv0());
+    std::vector<CommandLine::Option> expected_options = {
+        CommandLine::Option("="),      CommandLine::Option("=foo"),
+        CommandLine::Option("bar"),    CommandLine::Option("="),
+        CommandLine::Option("=", "="), CommandLine::Option("=", "x")};
+    EXPECT_EQ(expected_options, cl.options());
+    std::vector<std::string> expected_positional_args = {"arg"};
+    EXPECT_EQ(expected_positional_args, cl.positional_args());
+  }
+
+  // "-x" is an argument, not an options.
+  {
+    auto cl = CommandLineFromInitializerList({"", "-x"});
+    EXPECT_TRUE(cl.has_argv0());
+    EXPECT_EQ(std::string(), cl.argv0());
+    EXPECT_EQ(std::vector<CommandLine::Option>(), cl.options());
+    std::vector<std::string> expected_positional_args = {"-x"};
+    EXPECT_EQ(expected_positional_args, cl.positional_args());
+  }
+
+  // Ditto for "-".
+  {
+    auto cl = CommandLineFromInitializerList({"", "-"});
+    EXPECT_TRUE(cl.has_argv0());
+    EXPECT_EQ(std::string(), cl.argv0());
+    EXPECT_EQ(std::vector<CommandLine::Option>(), cl.options());
+    std::vector<std::string> expected_positional_args = {"-"};
+    EXPECT_EQ(expected_positional_args, cl.positional_args());
+  }
+
+  // "--" terminates option processing, but isn't an argument in the first
+  // occurrence.
+  {
+    auto cl = CommandLineFromInitializerList(
+        {"", "--flag=value", "--", "--not-a-flag", "arg", "--"});
+    EXPECT_TRUE(cl.has_argv0());
+    EXPECT_EQ(std::string(), cl.argv0());
+    std::vector<CommandLine::Option> expected_options = {
+        CommandLine::Option("flag", "value")};
+    std::vector<std::string> expected_positional_args = {"--not-a-flag", "arg",
+                                                         "--"};
+    EXPECT_EQ(expected_positional_args, cl.positional_args());
+  }
+}
+
+TEST(CommandLineTest, MultipleOccurrencesOfOption) {
+  auto cl = CommandLineFromInitializerList(
+      {"my_program", "--flag1=value1", "--flag2=value2", "--flag1=value3"});
+  std::vector<CommandLine::Option> expected_options = {
+      CommandLine::Option("flag1", "value1"),
+      CommandLine::Option("flag2", "value2"),
+      CommandLine::Option("flag1", "value3")};
+  EXPECT_EQ("value3", cl.GetOptionValueWithDefault("flag1", "nope"));
+  EXPECT_EQ("value2", cl.GetOptionValueWithDefault("flag2", "nope"));
+}
+
+// |cl1| and |cl2| should be not equal.
+void ExpectNotEqual(const char* message,
+                    std::initializer_list<std::string> c1,
+                    std::initializer_list<std::string> c2) {
+  SCOPED_TRACE(message);
+
+  const auto cl1 = CommandLineFromInitializerList(c1);
+  const auto cl2 = CommandLineFromInitializerList(c2);
+
+  // These are tautological.
+  EXPECT_TRUE(cl1 == cl1);
+  EXPECT_FALSE(cl1 != cl1);
+  EXPECT_TRUE(cl2 == cl2);
+  EXPECT_FALSE(cl2 != cl2);
+
+  // These rely on |cl1| not being equal to |cl2|.
+  EXPECT_FALSE(cl1 == cl2);
+  EXPECT_TRUE(cl1 != cl2);
+  EXPECT_FALSE(cl2 == cl1);
+  EXPECT_TRUE(cl2 != cl1);
+}
+
+void ExpectEqual(const char* message,
+                 std::initializer_list<std::string> c1,
+                 std::initializer_list<std::string> c2) {
+  SCOPED_TRACE(message);
+
+  const auto cl1 = CommandLineFromInitializerList(c1);
+  const auto cl2 = CommandLineFromInitializerList(c2);
+
+  // These are tautological.
+  EXPECT_TRUE(cl1 == cl1);
+  EXPECT_FALSE(cl1 != cl1);
+  EXPECT_TRUE(cl2 == cl2);
+  EXPECT_FALSE(cl2 != cl2);
+
+  // These rely on |cl1| being equal to |cl2|.
+  EXPECT_TRUE(cl1 == cl2);
+  EXPECT_FALSE(cl1 != cl2);
+  EXPECT_TRUE(cl2 == cl1);
+  EXPECT_FALSE(cl2 != cl1);
+}
+
+TEST(CommandLineTest, ComparisonOperators) {
+  ExpectNotEqual("1", {}, {""});
+  ExpectNotEqual("2", {"abc"}, {"def"});
+  ExpectNotEqual("3", {"abc", "--flag"}, {"abc"});
+  ExpectNotEqual("4", {"abc", "--flag1"}, {"abc", "--flag2"});
+  ExpectNotEqual("5", {"abc", "--flag1", "--flag2"}, {"abc", "--flag1"});
+  ExpectNotEqual("6", {"abc", "arg"}, {"abc"});
+  ExpectNotEqual("7", {"abc", "arg1"}, {"abc", "arg2"});
+  ExpectNotEqual("8", {"abc", "arg1", "arg2"}, {"abc", "arg1"});
+  ExpectNotEqual("9", {"abc", "--flag", "arg1"}, {"abc", "--flag", "arg2"});
+
+  // However, the presence of an unnecessary "--" shouldn't affect what's
+  // constructed.
+  ExpectEqual("10", {"abc", "--flag", "arg"}, {"abc", "--flag", "--", "arg"});
+}
+
+TEST(CommandLineTest, MoveAndCopy) {
+  const auto cl = CommandLineFromInitializerList(
+      {"my_program", "--flag1=value1", "--flag2", "arg"});
+
+  // Copy constructor.
+  CommandLine cl2(cl);
+  EXPECT_EQ(cl, cl2);
+  // Check that |option_index_| gets copied too.
+  EXPECT_EQ("value1", cl2.GetOptionValueWithDefault("flag1", "nope"));
+
+  // Move constructor.
+  CommandLine cl3(std::move(cl2));
+  EXPECT_EQ(cl, cl3);
+  EXPECT_EQ("value1", cl3.GetOptionValueWithDefault("flag1", "nope"));
+
+  // Copy assignment.
+  CommandLine cl4;
+  EXPECT_NE(cl, cl4);
+  cl4 = cl;
+  EXPECT_EQ(cl, cl4);
+  EXPECT_EQ("value1", cl4.GetOptionValueWithDefault("flag1", "nope"));
+
+  // Move assignment.
+  CommandLine cl5;
+  EXPECT_NE(cl, cl5);
+  cl5 = std::move(cl4);
+  EXPECT_EQ(cl, cl5);
+  EXPECT_EQ("value1", cl5.GetOptionValueWithDefault("flag1", "nope"));
+}
+
+void ToArgvHelper(const char* message, std::initializer_list<std::string> c) {
+  SCOPED_TRACE(message);
+  std::vector<std::string> argv = c;
+  auto cl = CommandLineFromInitializerList(c);
+  EXPECT_EQ(argv, CommandLineToArgv(cl));
+}
+
+TEST(CommandLineTest, CommandLineToArgv) {
+  ToArgvHelper("1", {});
+  ToArgvHelper("2", {""});
+  ToArgvHelper("3", {"my_program"});
+  ToArgvHelper("4", {"my_program", "--flag"});
+  ToArgvHelper("5", {"my_program", "--flag1", "--flag2=value"});
+  ToArgvHelper("6", {"my_program", "arg"});
+  ToArgvHelper("7", {"my_program", "arg1", "arg2"});
+  ToArgvHelper("8", {"my_program", "--flag1", "--flag2=value", "arg1", "arg2"});
+  ToArgvHelper("9", {"my_program", "--flag", "--", "--not-a-flag"});
+  ToArgvHelper("10", {"my_program", "--flag", "arg", "--"});
+
+  // However, |CommandLineToArgv()| will "strip" an unneeded "--".
+  {
+    auto cl = CommandLineFromInitializerList({"my_program", "--"});
+    std::vector<std::string> argv = {"my_program"};
+    EXPECT_EQ(argv, CommandLineToArgv(cl));
+  }
+  {
+    auto cl =
+        CommandLineFromInitializerList({"my_program", "--flag", "--", "arg"});
+    std::vector<std::string> argv = {"my_program", "--flag", "arg"};
+    EXPECT_EQ(argv, CommandLineToArgv(cl));
+  }
+}
+
+}  // namespace
+}  // namespace util
+}  // namespace mojo
diff --git a/mojo/edk/util/cond_var.cc b/mojo/edk/util/cond_var.cc
new file mode 100644
index 0000000..b675bf0
--- /dev/null
+++ b/mojo/edk/util/cond_var.cc
@@ -0,0 +1,147 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/util/cond_var.h"
+
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+
+#include <limits>
+
+#include "build/build_config.h"
+#include "mojo/edk/util/logging_internal.h"
+#include "mojo/edk/util/mutex.h"
+
+namespace mojo {
+namespace util {
+
+namespace {
+
+// Helper for |CondVar::WaitWithTimeout()|. Returns true on (definite) time-out.
+bool RelativeTimedWait(const struct timespec& timeout_rel,
+                       pthread_cond_t* posix_cond_var,
+                       pthread_mutex_t* posix_mutex) {
+// Mac has a function to do a relative timed wait directly.
+#if defined(OS_MACOSX)
+  int error = pthread_cond_timedwait_relative_np(posix_cond_var, posix_mutex,
+                                                 &timeout_rel);
+  INTERNAL_DCHECK_WITH_ERRNO(error == 0 || error == ETIMEDOUT || error == EINTR,
+                             "pthread_cond_timedwait_relative_np", error);
+  return error == ETIMEDOUT;
+#else
+  static const long kNanosecondsPerSecond = 1000000000L;
+
+// NaCl's |pthread_condattr_setclock()| only supports |CLOCK_REALTIME| (which is
+// the default, which is why we don't bother setting it in |CondVar|'s
+// constructor).
+#if defined(OS_NACL)
+  static const clockid_t kClockType = CLOCK_REALTIME;
+#else
+  static const clockid_t kClockType = CLOCK_MONOTONIC;
+#endif  // defined(OS_NACL)
+
+  struct timespec timeout_abs;
+  int error = clock_gettime(kClockType, &timeout_abs);
+  // Note: The return value of |clock_gettime()| is *not* an error code, unlike
+  // the pthreads functions (however, it sets errno).
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "clock_gettime", errno);
+
+  timeout_abs.tv_sec += timeout_rel.tv_sec;
+  timeout_abs.tv_nsec += timeout_rel.tv_nsec;
+  if (timeout_abs.tv_nsec >= kNanosecondsPerSecond) {
+    timeout_abs.tv_sec++;
+    timeout_abs.tv_nsec -= kNanosecondsPerSecond;
+    INTERNAL_DCHECK(timeout_abs.tv_nsec < kNanosecondsPerSecond);
+  }
+
+// Older Android doesn't have |pthread_condattr_setclock()|, but they have
+// |pthread_cond_timedwait_monotonic_np()|.
+#if defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
+  error = pthread_cond_timedwait_monotonic_np(posix_cond_var, posix_mutex,
+                                              &timeout_abs);
+  INTERNAL_DCHECK_WITH_ERRNO(error == 0 || error == ETIMEDOUT || error == EINTR,
+                             "pthread_cond_timedwait_monotonic_np", error);
+#else
+  error = pthread_cond_timedwait(posix_cond_var, posix_mutex, &timeout_abs);
+  INTERNAL_DCHECK_WITH_ERRNO(error == 0 || error == ETIMEDOUT || error == EINTR,
+                             "pthread_cond_timedwait", error);
+#endif  // defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
+  return error == ETIMEDOUT;
+#endif  // defined(OS_MACOSX)
+}
+
+}  // namespace
+
+CondVar::CondVar() {
+// Mac and older Android don't have |pthread_condattr_setclock()| (but they have
+// other timed wait functions we can use) and NaCl doesn't have a useful one.
+#if !defined(OS_MACOSX) && !defined(OS_NACL) && \
+    !(defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC))
+  pthread_condattr_t attr;
+  int error = pthread_condattr_init(&attr);
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_condattr_init", error);
+  error = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_condattr_setclock", error);
+  error = pthread_cond_init(&impl_, &attr);
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_cond_init", error);
+  error = pthread_condattr_destroy(&attr);
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_condattr_destroy", error);
+#else
+  int error = pthread_cond_init(&impl_, nullptr);
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_cond_init", error);
+#endif  // !defined(OS_MACOSX) && !defined(OS_NACL) && !(defined(OS_ANDROID)...)
+}
+
+CondVar::~CondVar() {
+  int error = pthread_cond_destroy(&impl_);
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_cond_destroy", error);
+}
+
+void CondVar::Wait(Mutex* mutex) {
+  INTERNAL_DCHECK(mutex);
+  mutex->AssertHeld();
+
+  int error = pthread_cond_wait(&impl_, &mutex->impl_);
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_cond_wait", error);
+}
+
+bool CondVar::WaitWithTimeout(Mutex* mutex, uint64_t timeout_microseconds) {
+  static const uint64_t kMicrosecondsPerSecond = 1000000ULL;
+  static const uint64_t kNanosecondsPerMicrosecond = 1000ULL;
+
+  // Turn very long waits into "forever". This isn't a huge concern if |time_t|
+  // is 64-bit, but overflowing |time_t| is a real risk if it's only 32-bit.
+  // (2^31 / 16 seconds = ~4.25 years, so we won't risk overflowing until 2033.)
+  constexpr uint64_t kForeverThresholdSeconds =
+      std::numeric_limits<time_t>::max() / 16;
+  uint64_t timeout_seconds = timeout_microseconds / kMicrosecondsPerSecond;
+  if (timeout_seconds >= kForeverThresholdSeconds) {
+    Wait(mutex);
+    return false;  // Did *not* time out.
+  }
+
+  INTERNAL_DCHECK(mutex);
+  mutex->AssertHeld();
+
+  struct timespec timeout_rel = {};
+  timeout_rel.tv_sec = static_cast<time_t>(timeout_seconds);
+  timeout_rel.tv_nsec =
+      static_cast<long>((timeout_microseconds % kMicrosecondsPerSecond) *
+                        kNanosecondsPerMicrosecond);
+  return RelativeTimedWait(timeout_rel, &impl_, &mutex->impl_);
+}
+
+void CondVar::Signal() {
+  int error = pthread_cond_signal(&impl_);
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_cond_signal", error);
+}
+
+void CondVar::SignalAll() {
+  int error = pthread_cond_broadcast(&impl_);
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_cond_broadcast", error);
+}
+
+}  // namespace util
+}  // namespace mojo
diff --git a/mojo/edk/util/cond_var.h b/mojo/edk/util/cond_var.h
new file mode 100644
index 0000000..68b0fdd
--- /dev/null
+++ b/mojo/edk/util/cond_var.h
@@ -0,0 +1,66 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A condition variable class (to be used with |mojo::util::Mutex|).
+
+#ifndef MOJO_EDK_UTIL_COND_VAR_H_
+#define MOJO_EDK_UTIL_COND_VAR_H_
+
+#include <pthread.h>
+#include <stdint.h>
+
+#include "mojo/edk/util/thread_annotations.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace util {
+
+class Mutex;
+
+class CondVar {
+ public:
+  CondVar();
+  ~CondVar();
+
+  // Atomically releases |*mutex| (which must be held) and blocks on this
+  // condition variable, unlocking and reacquiring |*mutex| when:
+  //   * |SignalAll()| is called,
+  //   * |Signal()| is called and this thread is scheduled to be the next to be
+  //     unblocked, or
+  //   * whenever (spuriously, e.g., due to |EINTR|).
+  // To deal with spurious wakeups, wait using a loop (with |my_mutex| held):
+  //   while (!<my_condition>)
+  //     cv.Wait(&my_mutex);
+  void Wait(Mutex* mutex) MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex);
+
+  // Like |Wait()|, but will also unblock when |timeout_microseconds| have
+  // elapsed without this condition variable being signaled. Returns true on
+  // timeout; this is somewhat counterintuitive, but the false case is
+  // non-specific: the condition variable may or may not have been signaled and
+  // |timeout_microseconds| may or may not have already elapsed (spurious
+  // wakeups are possible).
+  // TODO(vtl): A version with an absolute deadline time would be more efficient
+  // for users who want to wait to be signaled or a timeout to have definitely
+  // elapsed. With this API, users have to recalculate the timeout when they
+  // detect a spurious wakeup.
+  bool WaitWithTimeout(Mutex* mutex, uint64_t timeout_microseconds)
+      MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex);
+
+  // Signals this condition variable, waking at least one waiting thread if
+  // there are any.
+  void Signal();
+
+  // Signals this condition variable, waking all waiting threads.
+  void SignalAll();
+
+ private:
+  pthread_cond_t impl_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(CondVar);
+};
+
+}  // namespace util
+}  // namespace mojo
+
+#endif  // MOJO_EDK_UTIL_COND_VAR_H_
diff --git a/mojo/edk/util/cond_var_unittest.cc b/mojo/edk/util/cond_var_unittest.cc
new file mode 100644
index 0000000..930765b
--- /dev/null
+++ b/mojo/edk/util/cond_var_unittest.cc
@@ -0,0 +1,173 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/util/cond_var.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <thread>
+#include <type_traits>
+#include <vector>
+
+#include "mojo/edk/system/test/sleep.h"
+#include "mojo/edk/system/test/stopwatch.h"
+#include "mojo/edk/system/test/timeouts.h"
+#include "mojo/edk/util/mutex.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace util {
+namespace {
+
+// Sleeps for a "very small" amount of time.
+void EpsilonRandomSleep() {
+  system::test::SleepMilliseconds(static_cast<unsigned>(rand()) % 20u);
+}
+
+// We'll use |MojoDeadline| with |uint64_t| (for |CondVar::WaitWithTimeout()|'s
+// timeout argument), though note that |WaitWithTimeout()| doesn't support
+// |MOJO_DEADLINE_INDEFINITE|.
+static_assert(std::is_same<uint64_t, MojoDeadline>::value,
+              "MojoDeadline isn't uint64_t!");
+
+TEST(CondVarTest, Basic) {
+  // Create/destroy.
+  { CondVar cv; }
+
+  // Signal with no waiter.
+  {
+    CondVar cv;
+    cv.Signal();
+    cv.SignalAll();
+  }
+
+  // Wait with a zero and with very short timeout.
+  {
+    Mutex mu;
+    CondVar cv;
+
+    MutexLocker locker(&mu);
+
+    // Note: Theoretically, pthreads is allowed to wake us up spuriously, in
+    // which case |WaitWithTimeout()| would return false. (This would also
+    // happen if we're interrupted, e.g., by ^Z.)
+    EXPECT_TRUE(cv.WaitWithTimeout(&mu, 0));
+    mu.AssertHeld();
+    EXPECT_TRUE(
+        cv.WaitWithTimeout(&mu, system::test::DeadlineFromMilliseconds(1)));
+    mu.AssertHeld();
+  }
+
+  // Wait using |Wait()| or |WaitWithTimeout()|, to be signaled by |Signal()| or
+  // |SignalAll()|.
+  for (size_t i = 0; i < 30; i++) {
+    Mutex mu;
+    CondVar cv;
+    bool condition = false;
+
+    auto thread = std::thread([&mu, &cv, &condition]() {
+      EpsilonRandomSleep();
+
+      MutexLocker locker(&mu);
+      condition = true;
+      if (rand() % 2 == 0)
+        cv.Signal();
+      else
+        cv.SignalAll();
+    });
+
+    EpsilonRandomSleep();
+
+    MutexLocker locker(&mu);
+    if (rand() % 2 == 0) {
+      while (!condition) {
+        cv.Wait(&mu);
+        mu.AssertHeld();
+      }
+    } else {
+      while (!condition) {
+        EXPECT_FALSE(cv.WaitWithTimeout(&mu, system::test::TinyTimeout()));
+        mu.AssertHeld();
+      }
+    }
+
+    thread.join();
+  }
+}
+
+TEST(CondVarTest, SignalAll) {
+  Mutex mu;
+  CondVar cv;
+  bool condition = false;
+
+  for (size_t i = 0; i < 10; i++) {
+    for (size_t num_waiters = 1; num_waiters < 5; num_waiters++) {
+      std::vector<std::thread> threads;
+      for (size_t j = 0; j < num_waiters; j++) {
+        threads.push_back(std::thread([&mu, &cv, &condition]() {
+          EpsilonRandomSleep();
+
+          MutexLocker locker(&mu);
+          if (rand() % 2 == 0) {
+            while (!condition) {
+              cv.Wait(&mu);
+              mu.AssertHeld();
+            }
+          } else {
+            while (!condition) {
+              EXPECT_FALSE(
+                  cv.WaitWithTimeout(&mu, system::test::TinyTimeout()));
+              mu.AssertHeld();
+            }
+          }
+        }));
+      }
+
+      EpsilonRandomSleep();
+
+      {
+        MutexLocker locker(&mu);
+        condition = true;
+        cv.SignalAll();
+      }
+
+      for (auto& thread : threads)
+        thread.join();
+    }
+  }
+}
+
+TEST(CondVarTest, Timeouts) {
+  static const unsigned kTestTimeoutsMs[] = {0, 10, 20, 40, 80, 160};
+
+  system::test::Stopwatch stopwatch;
+
+  Mutex mu;
+  CondVar cv;
+
+  MutexLocker locker(&mu);
+
+  for (size_t i = 0; i < MOJO_ARRAYSIZE(kTestTimeoutsMs); i++) {
+    uint64_t timeout =
+        system::test::DeadlineFromMilliseconds(kTestTimeoutsMs[i]);
+
+    stopwatch.Start();
+    // See note in CondVarTest.Basic about spurious wakeups.
+    EXPECT_TRUE(cv.WaitWithTimeout(&mu, timeout));
+    MojoDeadline elapsed = stopwatch.Elapsed();
+
+    // It should time out after *at least* the specified amount of time.
+    EXPECT_GE(elapsed, timeout);
+    // But we expect that it should time out soon after that amount of time.
+    EXPECT_LT(elapsed, timeout + system::test::EpsilonTimeout());
+  }
+}
+
+// TODO(vtl): Test that |Signal()| (usually) wakes only one waiter.
+
+}  // namespace
+}  // namespace util
+}  // namespace mojo
diff --git a/mojo/edk/util/logging_internal.cc b/mojo/edk/util/logging_internal.cc
new file mode 100644
index 0000000..c24e4dc
--- /dev/null
+++ b/mojo/edk/util/logging_internal.cc
@@ -0,0 +1,33 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/util/mutex.h"
+
+#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+namespace mojo {
+namespace util {
+namespace internal {
+
+void DcheckHelper(const char* file, int line, const char* condition_string) {
+  fprintf(stderr, "%s:%d: Check failed: %s\n", file, line, condition_string);
+  abort();
+}
+
+void DcheckWithErrnoHelper(const char* file,
+                           int line,
+                           const char* fn,
+                           int error) {
+  fprintf(stderr, "%s:%d: %s: %s\n", file, line, fn, strerror(error));
+  abort();
+}
+
+}  // namespace internal
+}  // namespace util
+}  // namespace mojo
+
+#endif  // !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
diff --git a/mojo/edk/util/logging_internal.h b/mojo/edk/util/logging_internal.h
new file mode 100644
index 0000000..59e501e
--- /dev/null
+++ b/mojo/edk/util/logging_internal.h
@@ -0,0 +1,61 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Internal logging macros, to avoid external dependencies.
+
+#ifndef MOJO_EDK_UTIL_LOGGING_INTERNAL_H_
+#define MOJO_EDK_UTIL_LOGGING_INTERNAL_H_
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+
+#define INTERNAL_DCHECK(condition) \
+  do {                             \
+  } while (false && (condition))
+
+#define INTERNAL_DCHECK_WITH_ERRNO(condition, fn, error) \
+  do {                                                   \
+  } while (false && (condition) && (error))
+
+#else
+
+// Our own simplified "DCHECK". Asserts that |condition| is true. If not, "logs"
+// the failing condition and aborts.
+#define INTERNAL_DCHECK(condition)                                          \
+  do {                                                                      \
+    if (!(condition)) {                                                     \
+      ::mojo::util::internal::DcheckHelper(__FILE__, __LINE__, #condition); \
+    }                                                                       \
+  } while (false)
+
+// Our own simplified "DCHECK"/"DPCHECK" hybrid. Asserts that |condition| is
+// true. If not, "logs" |fn| with errno value |error| and aborts. (This doesn't
+// just use |errno| since some APIs, like pthreads, don't set errno.)
+#define INTERNAL_DCHECK_WITH_ERRNO(condition, fn, error)                    \
+  do {                                                                      \
+    if (!(condition)) {                                                     \
+      ::mojo::util::internal::DcheckWithErrnoHelper(__FILE__, __LINE__, fn, \
+                                                    error);                 \
+    }                                                                       \
+  } while (false)
+
+namespace mojo {
+namespace util {
+namespace internal {
+
+// Helper for |INTERNAL_DCHECK_WITH_ERRNO()| above.
+void DcheckHelper(const char* file, int line, const char* condition_string);
+
+// Helper for |INTERNAL_DCHECK_WITH_ERRNO()| above.
+void DcheckWithErrnoHelper(const char* file,
+                           int line,
+                           const char* fn,
+                           int error);
+
+}  // namespace internal
+}  // namespace util
+}  // namespace mojo
+
+#endif  // defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+
+#endif  // MOJO_EDK_UTIL_LOGGING_INTERNAL_H_
diff --git a/mojo/edk/util/mutex.cc b/mojo/edk/util/mutex.cc
new file mode 100644
index 0000000..1f006df
--- /dev/null
+++ b/mojo/edk/util/mutex.cc
@@ -0,0 +1,57 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/util/mutex.h"
+
+#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
+#include <errno.h>
+
+#include "mojo/edk/util/logging_internal.h"
+
+namespace mojo {
+namespace util {
+
+Mutex::Mutex() {
+  pthread_mutexattr_t attr;
+  int error = pthread_mutexattr_init(&attr);
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_mutexattr_init", error);
+  error = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_mutexattr_settype", error);
+  error = pthread_mutex_init(&impl_, &attr);
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_mutex_init", error);
+  error = pthread_mutexattr_destroy(&attr);
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_mutexattr_destroy", error);
+}
+
+Mutex::~Mutex() {
+  int error = pthread_mutex_destroy(&impl_);
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_mutex_destroy", error);
+}
+
+void Mutex::Lock() MOJO_EXCLUSIVE_LOCK_FUNCTION() {
+  int error = pthread_mutex_lock(&impl_);
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_mutex_lock", error);
+}
+
+void Mutex::Unlock() MOJO_UNLOCK_FUNCTION() {
+  int error = pthread_mutex_unlock(&impl_);
+  INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_mutex_unlock", error);
+}
+
+bool Mutex::TryLock() MOJO_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
+  int error = pthread_mutex_trylock(&impl_);
+  INTERNAL_DCHECK_WITH_ERRNO(!error || error == EBUSY, "pthread_mutex_trylock",
+                             error);
+  return !error;
+}
+
+void Mutex::AssertHeld() MOJO_ASSERT_EXCLUSIVE_LOCK() {
+  int error = pthread_mutex_lock(&impl_);
+  INTERNAL_DCHECK_WITH_ERRNO(error == EDEADLK, "pthread_mutex_lock", error);
+}
+
+}  // namespace util
+}  // namespace mojo
+
+#endif  // !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
diff --git a/mojo/edk/util/mutex.h b/mojo/edk/util/mutex.h
new file mode 100644
index 0000000..381ce7a
--- /dev/null
+++ b/mojo/edk/util/mutex.h
@@ -0,0 +1,83 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A mutex class, with support for thread annotations.
+//
+// TODO(vtl): Add support for non-exclusive (reader) locks.
+
+#ifndef MOJO_EDK_UTIL_MUTEX_H_
+#define MOJO_EDK_UTIL_MUTEX_H_
+
+#include <pthread.h>
+
+#include "mojo/edk/util/thread_annotations.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace util {
+
+// Mutex -----------------------------------------------------------------------
+
+class CondVar;
+
+class MOJO_LOCKABLE Mutex {
+ public:
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+  Mutex() { pthread_mutex_init(&impl_, nullptr); }
+  ~Mutex() { pthread_mutex_destroy(&impl_); }
+
+  // Takes an exclusive lock.
+  void Lock() MOJO_EXCLUSIVE_LOCK_FUNCTION() { pthread_mutex_lock(&impl_); }
+
+  // Releases a lock.
+  void Unlock() MOJO_UNLOCK_FUNCTION() { pthread_mutex_unlock(&impl_); }
+
+  // Tries to take an exclusive lock, returning true if successful.
+  bool TryLock() MOJO_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
+    return !pthread_mutex_trylock(&impl_);
+  }
+
+  // Asserts that an exclusive lock is held by the calling thread. (Does nothing
+  // for non-Debug builds.)
+  void AssertHeld() MOJO_ASSERT_EXCLUSIVE_LOCK() {}
+#else
+  Mutex();
+  ~Mutex();
+
+  void Lock() MOJO_EXCLUSIVE_LOCK_FUNCTION();
+  void Unlock() MOJO_UNLOCK_FUNCTION();
+
+  bool TryLock() MOJO_EXCLUSIVE_TRYLOCK_FUNCTION(true);
+
+  void AssertHeld() MOJO_ASSERT_EXCLUSIVE_LOCK();
+#endif  // defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+
+ private:
+  friend class CondVar;
+
+  pthread_mutex_t impl_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(Mutex);
+};
+
+// MutexLocker -----------------------------------------------------------------
+
+class MOJO_SCOPED_LOCKABLE MutexLocker {
+ public:
+  explicit MutexLocker(Mutex* mutex) MOJO_EXCLUSIVE_LOCK_FUNCTION(mutex)
+      : mutex_(mutex) {
+    this->mutex_->Lock();
+  }
+  ~MutexLocker() MOJO_UNLOCK_FUNCTION() { this->mutex_->Unlock(); }
+
+ private:
+  Mutex* const mutex_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(MutexLocker);
+};
+
+}  // namespace util
+}  // namespace mojo
+
+#endif  // MOJO_EDK_UTIL_MUTEX_H_
diff --git a/mojo/edk/util/mutex_unittest.cc b/mojo/edk/util/mutex_unittest.cc
new file mode 100644
index 0000000..6730aba
--- /dev/null
+++ b/mojo/edk/util/mutex_unittest.cc
@@ -0,0 +1,201 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/util/mutex.h"
+
+#include <stdlib.h>
+
+#include <thread>
+
+#include "mojo/edk/system/test/sleep.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace util {
+namespace {
+
+// Sleeps for a "very small" amount of time.
+void EpsilonRandomSleep() {
+  system::test::SleepMilliseconds(static_cast<unsigned>(rand()) % 20u);
+}
+
+// Basic test to make sure that Lock()/Unlock()/TryLock() don't crash ----------
+
+TEST(MutexTest, Basic) {
+  Mutex mutex;
+
+  int thread_acquired = 0;
+  auto thread = std::thread([&mutex, &thread_acquired]() {
+    for (int i = 0; i < 10; i++) {
+      mutex.Lock();
+      mutex.AssertHeld();
+      thread_acquired++;
+      mutex.Unlock();
+    }
+    for (int i = 0; i < 10; i++) {
+      mutex.Lock();
+      mutex.AssertHeld();
+      thread_acquired++;
+      EpsilonRandomSleep();
+      mutex.Unlock();
+    }
+    for (int i = 0; i < 10; i++) {
+      if (mutex.TryLock()) {
+        mutex.AssertHeld();
+        thread_acquired++;
+        EpsilonRandomSleep();
+        mutex.Unlock();
+      }
+    }
+  });
+
+  int acquired = 0;
+  for (int i = 0; i < 5; i++) {
+    mutex.Lock();
+    mutex.AssertHeld();
+    acquired++;
+    mutex.Unlock();
+  }
+  for (int i = 0; i < 10; i++) {
+    mutex.Lock();
+    mutex.AssertHeld();
+    acquired++;
+    EpsilonRandomSleep();
+    mutex.Unlock();
+  }
+  for (int i = 0; i < 10; i++) {
+    if (mutex.TryLock()) {
+      mutex.AssertHeld();
+      acquired++;
+      EpsilonRandomSleep();
+      mutex.Unlock();
+    }
+  }
+  for (int i = 0; i < 5; i++) {
+    mutex.Lock();
+    mutex.AssertHeld();
+    acquired++;
+    EpsilonRandomSleep();
+    mutex.Unlock();
+  }
+
+  thread.join();
+
+  EXPECT_GE(acquired, 20);
+  EXPECT_GE(thread_acquired, 20);
+}
+
+TEST(MutexTest, AssertHeld) {
+  Mutex mutex;
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+  // For non-Debug builds, |AssertHeld()| should do nothing.
+  mutex.AssertHeld();
+#else
+  EXPECT_DEATH_IF_SUPPORTED({ mutex.AssertHeld(); }, "pthread_mutex_lock");
+#endif  // defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+
+  // TODO(vtl): Should also test the case when the mutex is held by another
+  // thread, though this is more annoying since it requires synchronization.
+}
+
+// Test that TryLock() works as expected ---------------------------------------
+
+TEST(MutexTest, TryLock) MOJO_NO_THREAD_SAFETY_ANALYSIS {
+  Mutex mutex;
+
+  ASSERT_TRUE(mutex.TryLock());
+  // We now have the mutex....
+
+  {
+    // This thread will not be able to get the mutex.
+    auto thread = std::thread([&mutex]() { EXPECT_FALSE(mutex.TryLock()); });
+    thread.join();
+  }
+
+  mutex.Unlock();
+  // And now we don't.
+
+  {
+    // This thread will be able to get it (and then release it).
+    auto thread = std::thread([&mutex]() {
+      EXPECT_TRUE(mutex.TryLock());
+      mutex.AssertHeld();
+      mutex.Unlock();
+    });
+    thread.join();
+
+    // And we can take it again.
+    ASSERT_TRUE(mutex.TryLock());
+  }
+
+  mutex.Unlock();
+}
+
+// Tests that mutexes actually exclude -----------------------------------------
+
+// We'll call this from both the main thread and secondary threads.
+void DoStuffWithMutex(Mutex* mutex, int* value) {
+  for (int i = 0; i < 40; i++) {
+    mutex->Lock();
+    int v = *value;
+    EpsilonRandomSleep();
+    *value = v + 1;
+    mutex->Unlock();
+  }
+}
+
+std::thread MakeMutexTestThread(Mutex* mutex, int* value) {
+  return std::thread([mutex, value]() { DoStuffWithMutex(mutex, value); });
+}
+
+TEST(MutexTest, MutexTwoThreads) {
+  Mutex mutex;
+  int value = 0;
+
+  std::thread thread = MakeMutexTestThread(&mutex, &value);
+
+  DoStuffWithMutex(&mutex, &value);
+
+  thread.join();
+
+  EXPECT_EQ(2 * 40, value);
+}
+
+TEST(MutexTest, MutexFourThreads) {
+  Mutex mutex;
+  int value = 0;
+
+  std::thread thread1 = MakeMutexTestThread(&mutex, &value);
+  std::thread thread2 = MakeMutexTestThread(&mutex, &value);
+  std::thread thread3 = MakeMutexTestThread(&mutex, &value);
+
+  DoStuffWithMutex(&mutex, &value);
+
+  thread1.join();
+  thread2.join();
+  thread3.join();
+
+  EXPECT_EQ(4 * 40, value);
+}
+
+// MutexLocker -----------------------------------------------------------------
+
+TEST(MutexTest, MutexLocker) {
+  Mutex mutex;
+
+  {
+    MutexLocker locker(&mutex);
+    mutex.AssertHeld();
+  }
+
+  // The destruction of |locker| should unlock |mutex|.
+  ASSERT_TRUE(mutex.TryLock());
+  mutex.AssertHeld();
+  mutex.Unlock();
+}
+
+}  // namespace
+}  // namespace util
+}  // namespace mojo
diff --git a/mojo/edk/system/ref_counted.h b/mojo/edk/util/ref_counted.h
similarity index 77%
rename from mojo/edk/system/ref_counted.h
rename to mojo/edk/util/ref_counted.h
index 8c9a16c..7f8cbb7 100644
--- a/mojo/edk/system/ref_counted.h
+++ b/mojo/edk/util/ref_counted.h
@@ -4,20 +4,20 @@
 
 // Provides a base class for reference-counted classes.
 
-#ifndef MOJO_EDK_SYSTEM_REF_COUNTED_H_
-#define MOJO_EDK_SYSTEM_REF_COUNTED_H_
+#ifndef MOJO_EDK_UTIL_REF_COUNTED_H_
+#define MOJO_EDK_UTIL_REF_COUNTED_H_
 
 #include <assert.h>
 
 #include <cstddef>
 #include <utility>
 
-#include "mojo/edk/system/ref_counted_internal.h"
-#include "mojo/edk/system/ref_ptr.h"
+#include "mojo/edk/util/ref_counted_internal.h"
+#include "mojo/edk/util/ref_ptr.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
-namespace system {
+namespace util {
 
 // A base class for (thread-safe) reference-counted classes. Use like:
 //
@@ -77,6 +77,24 @@
       delete static_cast<const T*>(this);
   }
 
+  // Returns true if there is exactly one reference to this object. Use of this
+  // is subtle, and should usually be avoided. To assert that there is only one
+  // reference (typically held by the calling thread, possibly in a local
+  // variable), use |AssertHasOneRef()| instead. However, a use is:
+  //
+  //   if (foo->HasOneRef()) {
+  //     // Do something "fast", since |foo| is the only reference and no other
+  //     // thread can get another reference.
+  //     ...
+  //   } else {
+  //     // Do something slower, but still valid even if there were only one
+  //     // reference.
+  //     ...
+  //   }
+  //
+  // Inherited from the internal superclass:
+  //   bool HasOneRef();
+
   // Asserts that there is exactly one reference to this object; does nothing in
   // Release builds (when |NDEBUG| is defined).
   // Inherited from the internal superclass:
@@ -109,15 +127,15 @@
 // If you subclass |RefCountedThreadSafe| and want to keep your destructor
 // private, use this. (See the example above |RefCountedThreadSafe|.)
 #define FRIEND_REF_COUNTED_THREAD_SAFE(T) \
-  friend class ::mojo::system::RefCountedThreadSafe<T>
+  friend class ::mojo::util::RefCountedThreadSafe<T>
 
 // If you want to keep your constructor(s) private and still want to use
 // |MakeRefCounted<T>()|, use this. (See the example above
 // |RefCountedThreadSafe|.)
 #define FRIEND_MAKE_REF_COUNTED(T) \
-  friend class ::mojo::system::internal::MakeRefCountedHelper<T>
+  friend class ::mojo::util::internal::MakeRefCountedHelper<T>
 
-}  // namespace system
+}  // namespace util
 }  // namespace mojo
 
-#endif  // MOJO_EDK_SYSTEM_REF_COUNTED_H_
+#endif  // MOJO_EDK_UTIL_REF_COUNTED_H_
diff --git a/mojo/edk/system/ref_counted_internal.h b/mojo/edk/util/ref_counted_internal.h
similarity index 86%
rename from mojo/edk/system/ref_counted_internal.h
rename to mojo/edk/util/ref_counted_internal.h
index 6a596f3..3407c5f 100644
--- a/mojo/edk/system/ref_counted_internal.h
+++ b/mojo/edk/util/ref_counted_internal.h
@@ -4,8 +4,8 @@
 
 // Internal implementation details for ref_counted.h.
 
-#ifndef MOJO_EDK_SYSTEM_REF_COUNTED_INTERNAL_H_
-#define MOJO_EDK_SYSTEM_REF_COUNTED_INTERNAL_H_
+#ifndef MOJO_EDK_UTIL_REF_COUNTED_INTERNAL_H_
+#define MOJO_EDK_UTIL_REF_COUNTED_INTERNAL_H_
 
 #include <assert.h>
 
@@ -14,9 +14,10 @@
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
-namespace system {
+namespace util {
 namespace internal {
 
+// See ref_counted.h for comments on the public methods.
 class RefCountedThreadSafeBase {
  public:
   void AddRef() const {
@@ -25,10 +26,12 @@
     ref_count_.fetch_add(1u, std::memory_order_relaxed);
   }
 
-  void AssertHasOneRef() const {
-    assert(ref_count_.load(std::memory_order_acquire) == 1u);
+  bool HasOneRef() const {
+    return ref_count_.load(std::memory_order_acquire) == 1u;
   }
 
+  void AssertHasOneRef() const { assert(HasOneRef()); }
+
  protected:
   RefCountedThreadSafeBase();
   ~RefCountedThreadSafeBase();
@@ -95,7 +98,7 @@
 }
 
 }  // namespace internal
-}  // namespace system
+}  // namespace util
 }  // namespace mojo
 
-#endif  // MOJO_EDK_SYSTEM_REF_COUNTED_INTERNAL_H_
+#endif  // MOJO_EDK_UTIL_REF_COUNTED_INTERNAL_H_
diff --git a/mojo/edk/system/ref_counted_perftest.cc b/mojo/edk/util/ref_counted_perftest.cc
similarity index 63%
rename from mojo/edk/system/ref_counted_perftest.cc
rename to mojo/edk/util/ref_counted_perftest.cc
index 265e077..3c270e5 100644
--- a/mojo/edk/system/ref_counted_perftest.cc
+++ b/mojo/edk/util/ref_counted_perftest.cc
@@ -4,13 +4,14 @@
 
 #include <stdint.h>
 
-#include "base/test/perf_log.h"
-#include "mojo/edk/system/ref_counted.h"
-#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/test/perf_log.h"
+#include "mojo/edk/system/test/stopwatch.h"
+#include "mojo/edk/system/test/timeouts.h"
+#include "mojo/edk/util/ref_counted.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace mojo {
-namespace system {
+namespace util {
 namespace {
 
 class MyClass : public RefCountedThreadSafe<MyClass> {
@@ -28,7 +29,7 @@
 
 TEST(RefCountedPerfTest, OneThreadCreateAdoptDestroy) {
   uint64_t iterations = 0;
-  test::Stopwatch stopwatch;
+  system::test::Stopwatch stopwatch;
   stopwatch.Start();
   do {
     for (size_t i = 0; i < 1000; i++, iterations++) {
@@ -36,32 +37,32 @@
       x = nullptr;
     }
     iterations++;
-  } while (stopwatch.Elapsed() < test::DeadlineFromMilliseconds(1000));
+  } while (stopwatch.Elapsed() < system::test::DeadlineFromMilliseconds(1000));
   double elapsed = stopwatch.Elapsed() / 1000000.0;
 
-  base::LogPerfResult("OneThreadCreateAdoptDestroy", iterations / elapsed,
-                      "iterations/s");
+  system::test::LogPerfResult("OneThreadCreateAdoptDestroy",
+                              iterations / elapsed, "iterations/s");
 }
 
 TEST(RefCountedPerfTest, OneThreadAssignRefPtr) {
   RefPtr<MyClass> x = MyClass::Create();
   uint64_t iterations = 0;
-  test::Stopwatch stopwatch;
+  system::test::Stopwatch stopwatch;
   stopwatch.Start();
   do {
     for (size_t i = 0; i < 1000; i++, iterations++) {
       RefPtr<MyClass> y = x;
     }
     iterations++;
-  } while (stopwatch.Elapsed() < test::DeadlineFromMilliseconds(1000));
+  } while (stopwatch.Elapsed() < system::test::DeadlineFromMilliseconds(1000));
   double elapsed = stopwatch.Elapsed() / 1000000.0;
 
-  base::LogPerfResult("OneThreadAssignRefPtr", iterations / elapsed,
-                      "iterations/s");
+  system::test::LogPerfResult("OneThreadAssignRefPtr", iterations / elapsed,
+                              "iterations/s");
 }
 
 // TODO(vtl): Add threaded perf tests.
 
 }  // namespace
-}  // namespace system
+}  // namespace util
 }  // namespace mojo
diff --git a/mojo/edk/system/ref_counted_unittest.cc b/mojo/edk/util/ref_counted_unittest.cc
similarity index 97%
rename from mojo/edk/system/ref_counted_unittest.cc
rename to mojo/edk/util/ref_counted_unittest.cc
index 084bfc4..6795083 100644
--- a/mojo/edk/system/ref_counted_unittest.cc
+++ b/mojo/edk/util/ref_counted_unittest.cc
@@ -6,7 +6,7 @@
 // TODO(vtl): Possibly we could separate these tests out better, since a lot of
 // it is actually testing |RefPtr|.
 
-#include "mojo/edk/system/ref_counted.h"
+#include "mojo/edk/util/ref_counted.h"
 
 #include "build/build_config.h"
 #include "mojo/public/cpp/system/macros.h"
@@ -31,7 +31,7 @@
 #endif
 
 namespace mojo {
-namespace system {
+namespace util {
 namespace {
 
 class MyClass : public RefCountedThreadSafe<MyClass> {
@@ -497,10 +497,12 @@
   bool was_destroyed = false;
   RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(&created, &was_destroyed));
   ASSERT_FALSE(was_destroyed);
+  EXPECT_TRUE(created->HasOneRef());
   created->AssertHasOneRef();
 
   RefPtr<MySubclass> r2 = r1;
   ASSERT_FALSE(was_destroyed);
+  EXPECT_FALSE(created->HasOneRef());
 
   r1 = nullptr;
   ASSERT_FALSE(was_destroyed);
@@ -508,18 +510,23 @@
 
   {
     RefPtr<MyClass> r3 = r2;
+    EXPECT_FALSE(created->HasOneRef());
     {
       RefPtr<MyClass> r4(r3);
       r2 = nullptr;
       ASSERT_FALSE(was_destroyed);
+      EXPECT_FALSE(created->HasOneRef());
     }
     ASSERT_FALSE(was_destroyed);
+    EXPECT_TRUE(created->HasOneRef());
     created->AssertHasOneRef();
 
     r1 = RefPtr<MySubclass>(static_cast<MySubclass*>(r3.get()));
     ASSERT_FALSE(was_destroyed);
+    EXPECT_FALSE(created->HasOneRef());
   }
   ASSERT_FALSE(was_destroyed);
+  EXPECT_TRUE(created->HasOneRef());
   created->AssertHasOneRef();
 
   EXPECT_EQ(created, r1.get());
@@ -606,5 +613,5 @@
 // TODO(vtl): Add (threaded) stress tests.
 
 }  // namespace
-}  // namespace system
+}  // namespace util
 }  // namespace mojo
diff --git a/mojo/edk/system/ref_ptr.h b/mojo/edk/util/ref_ptr.h
similarity index 96%
rename from mojo/edk/system/ref_ptr.h
rename to mojo/edk/util/ref_ptr.h
index 493aa98..9d8bf85 100644
--- a/mojo/edk/system/ref_ptr.h
+++ b/mojo/edk/util/ref_ptr.h
@@ -4,19 +4,19 @@
 
 // Provides a smart pointer class for intrusively reference-counted objects.
 
-#ifndef MOJO_EDK_SYSTEM_REF_PTR_H_
-#define MOJO_EDK_SYSTEM_REF_PTR_H_
+#ifndef MOJO_EDK_UTIL_REF_PTR_H_
+#define MOJO_EDK_UTIL_REF_PTR_H_
 
 #include <assert.h>
 
 #include <cstddef>
 #include <utility>
 
-#include "mojo/edk/system/ref_ptr_internal.h"
+#include "mojo/edk/util/ref_ptr_internal.h"
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
-namespace system {
+namespace util {
 
 // A smart pointer class for intrusively reference-counted objects (e.g., those
 // subclassing |RefCountedThreadSafe| -- see ref_counted.h).
@@ -227,7 +227,7 @@
       std::forward<Args>(args)...);
 }
 
-}  // namespace system
+}  // namespace util
 }  // namespace mojo
 
-#endif  // MOJO_EDK_SYSTEM_REF_PTR_H_
+#endif  // MOJO_EDK_UTIL_REF_PTR_H_
diff --git a/mojo/edk/system/ref_ptr_internal.h b/mojo/edk/util/ref_ptr_internal.h
similarity index 83%
rename from mojo/edk/system/ref_ptr_internal.h
rename to mojo/edk/util/ref_ptr_internal.h
index 8684694..5c416e9 100644
--- a/mojo/edk/system/ref_ptr_internal.h
+++ b/mojo/edk/util/ref_ptr_internal.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MOJO_EDK_SYSTEM_REF_PTR_INTERNAL_H_
-#define MOJO_EDK_SYSTEM_REF_PTR_INTERNAL_H_
+#ifndef MOJO_EDK_UTIL_REF_PTR_INTERNAL_H_
+#define MOJO_EDK_UTIL_REF_PTR_INTERNAL_H_
 
 #include <utility>
 
 #include "mojo/public/cpp/system/macros.h"
 
 namespace mojo {
-namespace system {
+namespace util {
 
 template <typename T>
 class RefPtr;
@@ -34,7 +34,7 @@
 };
 
 }  // namespace internal
-}  // namespace system
+}  // namespace util
 }  // namespace mojo
 
-#endif  // MOJO_EDK_SYSTEM_REF_PTR_INTERNAL_H_
+#endif  // MOJO_EDK_UTIL_REF_PTR_INTERNAL_H_
diff --git a/mojo/edk/util/scoped_file.h b/mojo/edk/util/scoped_file.h
index 7aeb868..821babb 100644
--- a/mojo/edk/util/scoped_file.h
+++ b/mojo/edk/util/scoped_file.h
@@ -7,7 +7,7 @@
 
 #include <stdio.h>
 
-#include "base/memory/scoped_ptr.h"
+#include <memory>
 
 namespace mojo {
 namespace util {
@@ -24,7 +24,7 @@
 }  // namespace internal
 
 // Automatically closes |FILE*|s.
-using ScopedFILE = scoped_ptr<FILE, internal::ScopedFILECloser>;
+using ScopedFILE = std::unique_ptr<FILE, internal::ScopedFILECloser>;
 
 }  // namespace util
 }  // namespace mojo
diff --git a/mojo/edk/system/thread_annotations.h b/mojo/edk/util/thread_annotations.h
similarity index 95%
rename from mojo/edk/system/thread_annotations.h
rename to mojo/edk/util/thread_annotations.h
index ffac558..0cc0e01 100644
--- a/mojo/edk/system/thread_annotations.h
+++ b/mojo/edk/util/thread_annotations.h
@@ -12,8 +12,8 @@
 // particular, |TRY_ACQUIRE()| doesn't work: b/19264527).
 // https://github.com/domokit/mojo/issues/314
 
-#ifndef MOJO_EDK_SYSTEM_THREAD_ANNOTATIONS_H_
-#define MOJO_EDK_SYSTEM_THREAD_ANNOTATIONS_H_
+#ifndef MOJO_EDK_UTIL_THREAD_ANNOTATIONS_H_
+#define MOJO_EDK_UTIL_THREAD_ANNOTATIONS_H_
 
 // Enable thread-safety attributes only with clang.
 // The attributes can be safely erased when compiling with other compilers.
@@ -82,4 +82,4 @@
 // implementations.
 #define MOJO_NOT_THREAD_SAFE MOJO_NO_THREAD_SAFETY_ANALYSIS
 
-#endif  // MOJO_EDK_SYSTEM_THREAD_ANNOTATIONS_H_
+#endif  // MOJO_EDK_UTIL_THREAD_ANNOTATIONS_H_
diff --git a/mojo/edk/system/thread_annotations_unittest.cc b/mojo/edk/util/thread_annotations_unittest.cc
similarity index 95%
rename from mojo/edk/system/thread_annotations_unittest.cc
rename to mojo/edk/util/thread_annotations_unittest.cc
index d56814f..838dda5 100644
--- a/mojo/edk/system/thread_annotations_unittest.cc
+++ b/mojo/edk/util/thread_annotations_unittest.cc
@@ -10,9 +10,9 @@
 // So instead we have some cheesy macros that you can define to enable
 // individual compilation failures.
 
-#include "mojo/edk/system/thread_annotations.h"
+#include "mojo/edk/util/thread_annotations.h"
 
-#include "mojo/edk/system/mutex.h"
+#include "mojo/edk/util/mutex.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -24,7 +24,7 @@
 // #define NC_ACQUIRED_BEFORE
 
 namespace mojo {
-namespace system {
+namespace util {
 namespace {
 
 // Test MOJO_GUARDED_BY --------------------------------------------------------
@@ -120,5 +120,5 @@
 // TODO(vtl): Test more things.
 
 }  // namespace
-}  // namespace system
+}  // namespace util
 }  // namespace mojo
diff --git a/mojo/environment/BUILD.gn b/mojo/environment/BUILD.gn
index 5c7a9f0..2bf0c58 100644
--- a/mojo/environment/BUILD.gn
+++ b/mojo/environment/BUILD.gn
@@ -8,16 +8,12 @@
     "default_async_waiter.h",
     "default_logger.cc",
     "default_logger.h",
-    "default_task_tracker.cc",
-    "default_task_tracker.h",
     "environment.cc",
 
     # TODO(vtl): This is kind of ugly. (See TODO in logging.h.)
     "../public/cpp/environment/async_waiter.h",
     "../public/cpp/environment/lib/async_waiter.cc",
     "../public/cpp/environment/lib/logging.cc",
-    "../public/cpp/environment/lib/scoped_task_tracking.cc",
-    "../public/cpp/environment/lib/scoped_task_tracking.h",
     "../public/cpp/environment/logging.h",
   ]
 
diff --git a/mojo/environment/default_task_tracker.cc b/mojo/environment/default_task_tracker.cc
deleted file mode 100644
index eb7cccf..0000000
--- a/mojo/environment/default_task_tracker.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/environment/default_task_tracker.h"
-
-#include "base/logging.h"
-#include "mojo/common/task_tracker.h"
-#include "mojo/public/cpp/environment/task_tracker.h"
-
-namespace mojo {
-namespace internal {
-namespace {
-
-bool g_enabled = false;
-
-TaskTrackingId StartTracking(const char* function_name,
-                             const char* file_name,
-                             int line_number,
-                             const void* program_counter) {
-  if (!g_enabled)
-    return TaskTrackingId(0);
-
-  return common::TaskTracker::StartTracking(function_name, file_name,
-                                            line_number, program_counter);
-}
-
-void EndTracking(const TaskTrackingId id) {
-  if (!g_enabled) {
-    DCHECK_EQ(0, id);
-    return;
-  }
-
-  common::TaskTracker::EndTracking(id);
-}
-
-void SetEnabled(bool enabled) {
-  g_enabled = enabled;
-}
-
-}  // namespace
-
-const TaskTracker kDefaultTaskTracker = {&StartTracking,
-                                         &EndTracking,
-                                         &SetEnabled};
-
-}  // namespace internal
-}  // namespace mojo
diff --git a/mojo/environment/default_task_tracker.h b/mojo/environment/default_task_tracker.h
deleted file mode 100644
index f1f43e5..0000000
--- a/mojo/environment/default_task_tracker.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MOJO_ENVIRONMENT_DEFAULT_TASK_TRACKER_H_
-#define MOJO_ENVIRONMENT_DEFAULT_TASK_TRACKER_H_
-
-namespace mojo {
-
-struct TaskTracker;
-
-namespace internal {
-
-extern const TaskTracker kDefaultTaskTracker;
-
-}  // namespace internal
-}  // namespace mojo
-
-#endif  // MOJO_ENVIRONMENT_DEFAULT_TASK_TRACKER_H_
diff --git a/mojo/environment/environment.cc b/mojo/environment/environment.cc
index a5a424c..562bfe4 100644
--- a/mojo/environment/environment.cc
+++ b/mojo/environment/environment.cc
@@ -7,7 +7,6 @@
 #include "base/message_loop/message_loop.h"
 #include "mojo/environment/default_async_waiter.h"
 #include "mojo/environment/default_logger.h"
-#include "mojo/environment/default_task_tracker.h"
 
 namespace mojo {
 
@@ -36,11 +35,6 @@
 }
 
 // static
-const TaskTracker* Environment::GetDefaultTaskTracker() {
-  return &internal::kDefaultTaskTracker;
-}
-
-// static
 void Environment::InstantiateDefaultRunLoop() {
   CHECK(!base::MessageLoop::current());
   // Not leaked: accessible from |base::MessageLoop::current()|.
diff --git a/mojo/message_pump/handle_watcher_unittest.cc b/mojo/message_pump/handle_watcher_unittest.cc
index 36f15ac..671cfee 100644
--- a/mojo/message_pump/handle_watcher_unittest.cc
+++ b/mojo/message_pump/handle_watcher_unittest.cc
@@ -54,7 +54,7 @@
     loop.reset(new base::MessageLoop());
   else
     loop.reset(new base::MessageLoop(MessagePumpMojo::Create()));
-  return loop.Pass();
+  return loop;
 }
 
 // Helper class to manage the callback and running the message loop waiting for
diff --git a/mojo/services/files/c/tests/test_utils.cc b/mojo/services/files/c/tests/test_utils.cc
index 518186f..908c7dd 100644
--- a/mojo/services/files/c/tests/test_utils.cc
+++ b/mojo/services/files/c/tests/test_utils.cc
@@ -40,7 +40,7 @@
   if (error != mojo::files::Error::OK)
     return nullptr;
 
-  return file.Pass();
+  return file;
 }
 
 void CreateTestFileAt(mojo::files::DirectoryPtr* root,
diff --git a/mojo/services/files/interfaces/file.mojom b/mojo/services/files/interfaces/file.mojom
index e1e1db8..c2f2bdd 100644
--- a/mojo/services/files/interfaces/file.mojom
+++ b/mojo/services/files/interfaces/file.mojom
@@ -38,7 +38,7 @@
   // want it to be signed (this is consistent with |size| values, but
   // inconsistent with 32-bit |num_bytes_to_read| values)? Do we want to have
   // separate "read to end" versus "tail" (i.e., keep on reading as more data is
-  // appended) modes, and how would those be signalled?
+  // appended) modes, and how would those be signaled?
   ReadToStream(handle<data_pipe_producer> source,
                int64 offset,
                Whence whence,
diff --git a/mojo/services/http_server/cpp/lib/http_server_util.cc b/mojo/services/http_server/cpp/lib/http_server_util.cc
index 2e28dd8..36274d3 100644
--- a/mojo/services/http_server/cpp/lib/http_server_util.cc
+++ b/mojo/services/http_server/cpp/lib/http_server_util.cc
@@ -27,7 +27,7 @@
   MOJO_DCHECK(MOJO_RESULT_OK == result);
   response->status_code = status_code;
   response->content_length = num_bytes;
-  return response.Pass();
+  return response;
 }
 
 }  // namespace http_server
diff --git a/mojo/services/mojo_services.gni b/mojo/services/mojo_services.gni
index f2bdddc..e55533d 100644
--- a/mojo/services/mojo_services.gni
+++ b/mojo/services/mojo_services.gni
@@ -40,6 +40,7 @@
   "//mojo/services/surfaces/interfaces",
   "//mojo/services/terminal/interfaces",
   "//mojo/services/tracing/interfaces",
+  "//mojo/services/ui/views/interfaces",
   "//mojo/services/url_response_disk_cache/interfaces",
   "//mojo/services/vanadium/security/interfaces",
   "//mojo/services/view_manager/interfaces",
diff --git a/mojo/services/surfaces/cpp/surfaces_utils.cc b/mojo/services/surfaces/cpp/surfaces_utils.cc
index b675f73..d37dcbb 100644
--- a/mojo/services/surfaces/cpp/surfaces_utils.cc
+++ b/mojo/services/surfaces/cpp/surfaces_utils.cc
@@ -16,7 +16,7 @@
   transform->matrix[5] = 1.f;
   transform->matrix[10] = 1.f;
   transform->matrix[15] = 1.f;
-  return transform.Pass();
+  return transform;
 }
 }
 
@@ -33,7 +33,7 @@
   sqs->opacity = 1.f;
   sqs->blend_mode = mojo::SkXfermode::kSrc_Mode;
   sqs->sorting_context_id = 0;
-  return sqs.Pass();
+  return sqs;
 }
 
 PassPtr CreateDefaultPass(int id, const Rect& rect) {
@@ -43,7 +43,7 @@
   pass->damage_rect = rect.Clone();
   pass->transform_to_root_target = GetIdentityTransform();
   pass->has_transparent_background = false;
-  return pass.Pass();
+  return pass;
 }
 
 }  // namespace mojo
diff --git a/mojo/services/ui/views/interfaces/BUILD.gn b/mojo/services/ui/views/interfaces/BUILD.gn
new file mode 100644
index 0000000..07ac010
--- /dev/null
+++ b/mojo/services/ui/views/interfaces/BUILD.gn
@@ -0,0 +1,21 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/module_args/mojo.gni")
+import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+  sources = [
+    "layouts.mojom",
+    "view_manager.mojom",
+    "view_provider.mojom",
+    "view_trees.mojom",
+    "views.mojom",
+  ]
+
+  deps = [
+    "../../../geometry/interfaces",
+    "../../../surfaces/interfaces:surface_id",
+  ]
+}
diff --git a/mojo/services/ui/views/interfaces/layouts.mojom b/mojo/services/ui/views/interfaces/layouts.mojom
new file mode 100644
index 0000000..7a6a177
--- /dev/null
+++ b/mojo/services/ui/views/interfaces/layouts.mojom
@@ -0,0 +1,73 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[DartPackage="mojo_services"]
+module mojo.ui;
+
+import "mojo/services/geometry/interfaces/geometry.mojom";
+import "mojo/services/surfaces/interfaces/surface_id.mojom";
+
+// Box constraints for layout.
+//
+// A box constraint allows a parent view to determine the size of its child
+// by constraining its width and height.  Each dimension is considered
+// independently.  If a constraint only admits a single value in one or both
+// dimensions (because the minimum and maximum values are equal) then it is
+// said to be "tight" in those dimension.
+//
+// A |Size| respects this constraint if, and only if, all of the following
+// relations hold:
+//
+//   size.width >= constraints.min_width
+//   size.width <= constraints.max_width
+//   size.height >= constraints.min_height
+//   size.height <= constraints.max_height
+//
+// The view manager validates all constraints before delivering them to a
+// child view; it will close its connection if the parent view supplies
+// constraints which are ill-formed.
+struct BoxConstraints {
+  // The minimum width of the view in device pixels.
+  // Must be >= 0.
+  int32 min_width;
+
+  // The maximum width of the view in device pixels.
+  // Must be >= |min_width|.
+  int32 max_width;
+
+  // The minimum height of the view in device pixels.
+  // Must be >= 0.
+  int32 min_height;
+
+  // The maximum height of the view in device pixels.
+  // Must be >= |min_height|.
+  int32 max_height;
+};
+
+// Layout parameters provided by a parent view to one of its children.
+//
+// TODO(jeffbrown): We will eventually need to pass a bunch more information
+// such as theme colors, virtual light sources for shadows, elevation, and
+// more.  It is unclear whether we'll want to put that information here
+// or somewhere else but it may be convenient to stash it here because we
+// will already have an invalidation mechanism in place.
+struct ViewLayoutParams {
+  // The size constraints for the child.
+  mojo.ui.BoxConstraints constraints;
+
+  // The ratio between the size of one display device pixel to the size
+  // of one logical pixel, assuming pixels are square.  This value changes
+  // in relation to display density and zoom level.
+  // Must be > 0.
+  float device_pixel_ratio = 1.0;
+};
+
+// Layout information for a view.
+struct ViewLayoutInfo {
+  // The view's surface id for composition by the parent.
+  mojo.SurfaceId surface_id;
+
+  // The actual size of the view in device pixels.
+  mojo.Size size;
+};
diff --git a/mojo/services/ui/views/interfaces/view_manager.mojom b/mojo/services/ui/views/interfaces/view_manager.mojom
new file mode 100644
index 0000000..0e45fe6
--- /dev/null
+++ b/mojo/services/ui/views/interfaces/view_manager.mojom
@@ -0,0 +1,49 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[DartPackage="mojo_services"]
+module mojo.ui;
+
+import "mojo/services/ui/views/interfaces/views.mojom";
+import "mojo/services/ui/views/interfaces/view_trees.mojom";
+
+// The view manager is a service which manages trees of views.
+//
+// Before a view can be added to the view tree, it must first be registered
+// with the view manager.  Once registered, the view receives a token as a
+// transferable reference to be provided to the view's intended container.
+interface ViewManager {
+  // Registers a view with the view manager.
+  //
+  // When a view is registered, it receives its own host and a token
+  // to identify it.
+  //
+  // The |view_host| is used to configure the view and interact with its
+  // local environment.  The view host is private to the view and should
+  // not be shared with anyone else.
+  //
+  // The |view_token| is used as a transferable reference which can
+  // be passed to the view's intended container as part of a request to
+  // add the view as a child.  The view manager itself does not describe
+  // how this interaction should take place, only that the token should
+  // eventually be passed back through the container's view host interface
+  // as an argument to AddChild().
+  //
+  // To unregister the view and cause it to be removed from the view tree,
+  // simply close the |view| and/or |view_host| message pipes.
+  RegisterView(mojo.ui.View view,
+               mojo.ui.ViewHost& view_host) =>
+                   (mojo.ui.ViewToken view_token);
+
+  // Registers a view tree with the view manager.
+  //
+  // The |view_tree_host| is used to configure the view tree and interact
+  // with the views it contains.  The view tree host is private to the view
+  // and should not be shared with anyone else.
+  //
+  // To unregister the view tree simply close the |view_tree| and/or
+  // |view_tree_host| message pipes.
+  RegisterViewTree(mojo.ui.ViewTree view_tree,
+                   mojo.ui.ViewTreeHost& view_tree_host) => ();
+};
diff --git a/mojo/services/ui/views/interfaces/view_provider.mojom b/mojo/services/ui/views/interfaces/view_provider.mojom
new file mode 100644
index 0000000..b09ec92
--- /dev/null
+++ b/mojo/services/ui/views/interfaces/view_provider.mojom
@@ -0,0 +1,30 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[DartPackage="mojo_services"]
+module mojo.ui;
+
+import "mojo/public/interfaces/application/service_provider.mojom";
+import "mojo/services/ui/views/interfaces/views.mojom";
+
+// Provides a View upon request.
+//
+// Applications should implement and expose this service so that they can
+// expose views to be embedded into other applications.
+interface ViewProvider {
+  // Creates and registers a view with the view manager and returns its
+  // view token (as provided by |ViewManager.RegisterView()|).
+  //
+  // Having received the view token, the caller should attach the view to
+  // a view tree and lay it out.
+  //
+  // The caller may provide services to the view via the |services|
+  // service provider.
+  //
+  // The caller may receive services from the view via the |exposed_services|
+  // service provider.
+  CreateView(mojo.ServiceProvider&? services,
+             mojo.ServiceProvider? exposed_services) =>
+                 (ViewToken view_token);
+};
diff --git a/mojo/services/ui/views/interfaces/view_trees.mojom b/mojo/services/ui/views/interfaces/view_trees.mojom
new file mode 100644
index 0000000..ac3439c
--- /dev/null
+++ b/mojo/services/ui/views/interfaces/view_trees.mojom
@@ -0,0 +1,92 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[DartPackage="mojo_services"]
+module mojo.ui;
+
+import "mojo/services/ui/views/interfaces/layouts.mojom";
+import "mojo/services/ui/views/interfaces/views.mojom";
+
+// A view tree is a top-level container for a hierarchy of views.
+//
+// A view tree must registered with the view manager before it can be shown.
+interface ViewTree {
+  // Called when the tree needs to update its layout.
+  //
+  // This method may be called for one or more of the following reasons:
+  //
+  //   1. The root was just set.
+  //   2. The root produced different layout information during its last
+  //      layout pass causing a recursive layout to occur.
+  //
+  // Layout requests are coalesced for efficiency.  Certain intermediate
+  // updates may be dropped if the view tree is unable to keep up with them
+  // in a timely manner.  Do nothing updates are always dropped.
+  //
+  // The implementation should invoke the callback once the event has
+  // been handled and the view tree is ready to be shown in its new aspect.
+  OnLayout() => ();
+
+  // Called when the root view has become unavailable.
+  //
+  // The root may become unavailable for many reasons such being unregistered
+  // by its application, abnormal termination of its application, or
+  // being reparented into a different view tree.
+  //
+  // The implementation should invoke the callback once the event has
+  // been handled.
+  OnRootUnavailable(uint32 root_key) => ();
+};
+
+// The view tree host provides an interface for a view tree to configure itself
+// and interact with its views.
+//
+// Each view tree obtains its own view tree host when registered with the
+// ViewManager.  To unregister the view tree, close its view tree
+// and/or view tree host message pipes.
+interface ViewTreeHost {
+  // Requests that the view tree's OnLayout() method be called to compute a
+  // new layout due to a change in the view tree's layout information.
+  RequestLayout();
+
+  // Sets the root of the view tree and assigns it the provided |root_key|
+  // to distinguish it from any other roots this view tree has had.
+  //
+  // It is a good idea to provide a distinct |root_key| each time a new root
+  // is set so that callbacks related to the root can be clearly distinguished
+  // across these changes.
+  //
+  // If |root_view_token| refers to a view which is already unavailable
+  // then the call proceeds as if it succeeded but an OnChildUnavailable()
+  // message will be sent.
+  //
+  // If |root_view_token| refers to a view which already has a parent or is
+  // the root of a view tree then an OnChildUnavailable() or OnRootUnavailable()
+  // message will be sent to its old parent or root and the the view will be
+  // used as the root of the new view tree as usual.  This special case also
+  // applies when the specified view is already the root of this view tree, in
+  // which case the behavior is similar to the view having been transferred to
+  // some other view tree and then back again.
+  SetRoot(uint32 root_key, mojo.ui.ViewToken root_view_token);
+
+  // Removes the root of the view tree.
+  //
+  // Does nothing if the view tree currently does not have a root.
+  ResetRoot();
+
+  // Sets the layout parameters of the root of the view tree and retrieves
+  // its layout information.
+  //
+  // The returned |info| is null if this layout request was canceled either
+  // because it has been superceded by a subsequently issued layout request
+  // or because the root has become unavailable.
+  //
+  // It is an error to call this function if the view tree does not currently
+  // have a root; the connection will be closed.
+  //
+  // It is an error to specify malformed |root_layout_params| such
+  // as invalid size constraints; the connection will be closed.
+  LayoutRoot(mojo.ui.ViewLayoutParams root_layout_params) =>
+      (mojo.ui.ViewLayoutInfo? info);
+};
diff --git a/mojo/services/ui/views/interfaces/views.mojom b/mojo/services/ui/views/interfaces/views.mojom
new file mode 100644
index 0000000..49ca841
--- /dev/null
+++ b/mojo/services/ui/views/interfaces/views.mojom
@@ -0,0 +1,167 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[DartPackage="mojo_services"]
+module mojo.ui;
+
+import "mojo/public/interfaces/application/service_provider.mojom";
+import "mojo/services/ui/views/interfaces/layouts.mojom";
+
+// A view token is an opaque transferable reference to a view.
+//
+// The ViewManager provides each view with a unique view token when
+// it is registered.  The token can subsequently be passed to other
+// applications and may be used to add the view as a child of some
+// other view or to set it as the root of a view tree.
+//
+// View tokens should be kept secret and should only be shared with
+// the view's intended container.
+//
+// TODO(jeffbrown): This implementation is a temporary placeholder until
+// we extend Mojo to provide a way to create tokens which cannot be forged.
+struct ViewToken {
+  uint32 value;
+};
+
+// A view is a graphical user interface component which is responsible
+// for drawing and supporting user interactions in the area of the screen
+// that it occupies.
+//
+// A view may also act as a container for other views (known as the
+// view's children) which it may freely layout and position anywhere
+// within its bounds to form a composite user interface.  The hierarchy
+// of views thus formed is called a view tree.
+//
+// A view must registered with the view manager before it can be shown.
+interface View {
+  // Called when the view needs to update its layout and provide its size.
+  //
+  // This method may be called for one or more of the following reasons:
+  //
+  //   1. The view called RequestLayout() to mark itself as needing layout.
+  //   2. The view's parent called LayoutChild() for the first time to
+  //      provide layout parameters to this view.
+  //   3. The view's parent called LayoutChild() and provided a
+  //      set of layout parameters which differ from its prior call to
+  //      OnLayout().
+  //   4. One or more of the view's children were just added to the view
+  //      tree using AddChild() or removed from the tree using RemoveChild().
+  //   5. One or more of the view's children produced different layout
+  //      information during their last layout pass causing a recursive
+  //      layout to occur.
+  //
+  // The |children_needing_layout| array includes the keys of all children
+  // which require a layout.  The view is responsible for calling LayoutChild()
+  // at least once for each child in the array in addition to any other
+  // children which might also need to be updated.
+  //
+  // Layout requests are coalesced for efficiency.  Certain intermediate
+  // updates may be dropped if the view is unable to keep up with them
+  // in a timely manner.  Do nothing updates are always dropped.
+  //
+  // The implementation should invoke the callback once the event has
+  // been handled and the view is ready to be shown in its new aspect.
+  //
+  // The result of the layout may cause the parent's layout to be invalidated.
+  // When this happens, the parent's own OnLayout() method will be called
+  // and will be informed that this child needs layout.
+  //
+  // Recursive layout happens in any of the following circumstances:
+  //
+  //   1. If the resulting surface has changed since the last layout.
+  //   2. If the resulting size has changed since the last layout.
+  //
+  // It is an error to return a malformed |info| which does not satisfy
+  // the requested |layout_params|, such as by returning a size which
+  // exceeds the requested constraints; the view's connection will be closed.
+  OnLayout(mojo.ui.ViewLayoutParams layout_params,
+    array<uint32> children_needing_layout) => (mojo.ui.ViewLayoutInfo info);
+
+  // Called when a child view has become unavailable.
+  //
+  // A child may become unavailable for many reasons such being unregistered
+  // by its application, abnormal termination of its application, or
+  // cycles being introduced in the view tree.
+  //
+  // To complete removal of an unavailable child, this view component must
+  // call RemoveChild() on its view host with |child_key|.
+  //
+  // The implementation should invoke the callback once the event has
+  // been handled.
+  OnChildUnavailable(uint32 child_key) => ();
+};
+
+// The view host provides an interface for a view to configure itself and
+// interact with its local environment, such as adding and removing
+// children and specifying layout constraints.
+//
+// Each view obtains its own view host when registered with the ViewManager.
+// To unregister the view, close its view host message pipe.
+interface ViewHost {
+  // Gets a service provider to access services which are associated with
+  // the view such as input, accessibility and editing capabilities.
+  // The view service provider is private to the view and should not be
+  // shared with anyone else.
+  GetServiceProvider(mojo.ServiceProvider& service_provider);
+
+  // Requests that the view's OnLayout() method be called to compute a
+  // new layout due to a change in the view's layout information.
+  RequestLayout();
+
+  // Adds the view referenced by |child_view_token| as a child and assigns
+  // it the provided |child_key| to identify it among its children.
+  // The parent may remove the child later by passing the same |child_key|
+  // to RemoveChild().
+  //
+  // It is important for the parent to choose locally unique values for
+  // |child_key| to ensure that each child can be distinguished even as
+  // more children are added or removed.  We recommend using a simple
+  // counter which is incremented on each (re-)addition.
+  //
+  // If the child becomes unavailable at any time prior to being removed
+  // then an OnChildUnavailable() message will be sent.
+  //
+  // If |child_view_token| refers to a view which is already unavailable or
+  // if adding the view would create a cycle in the view tree then the
+  // call proceeds as if it succeeded but an OnChildUnavailable() message
+  // will be sent.
+  //
+  // If |child_view_token| refers to a view which already has a parent or is
+  // the root of a view tree then an OnChildUnavailable() or OnRootUnavailable()
+  // message will be sent to its old parent or root and the the view will be
+  // (re-)added to its new parent as usual.  This special case also applies
+  // when the specified view is already a child of this view, in which
+  // case the behavior is similar to the view having been transferred to
+  // some other parent and then back again.
+  //
+  // Note that an unavailable child will remain in its parent's list of
+  // children until its parent explicitly calls RemoveChild() to remove
+  // it.
+  //
+  // It is an error to add a view whose |child_key| already appears
+  // in the view's list of children; the connection will be closed.
+  AddChild(uint32 child_key, mojo.ui.ViewToken child_view_token);
+
+  // Removes the view referenced by |child_key| from the view's
+  // list of children.
+  //
+  // It is an error to remove a view whose |child_key| does not appear
+  // in the parent's list of children; the connection will be closed.
+  RemoveChild(uint32 child_key);
+
+  // Sets the layout parameters of the child view referenced by |child_key|
+  // and retrieves its layout information.
+  //
+  // The returned |info| is null if this layout request was canceled either
+  // because it has been superceded by a subsequently issued layout request
+  // or because the child has become unavailable.
+  //
+  // It is an error to specify a |child_key| that does not appear in
+  // the parent's list of children; the connection will be closed.
+  //
+  // It is an error to specify malformed |child_layout_params| such
+  // as invalid size constraints; the connection will be closed.
+  LayoutChild(uint32 child_key, mojo.ui.ViewLayoutParams child_layout_params)
+      => (mojo.ui.ViewLayoutInfo? info);
+};
diff --git a/mojo/services/vanadium/security/interfaces/principal.mojom b/mojo/services/vanadium/security/interfaces/principal.mojom
index 9c58834..d2493a3 100644
--- a/mojo/services/vanadium/security/interfaces/principal.mojom
+++ b/mojo/services/vanadium/security/interfaces/principal.mojom
@@ -1,49 +1,75 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
+// Copyright 2015 The Chromium Authors. All rights reserved.  Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
 
 module vanadium;
 
-// Represents the name of an application. |url| is the url of the
-// application. |qualifier| is a string that allows to tie a specific
-// instance of an application to another.
+// Represents the name of an application. |url| is the url of the application.
+// |qualifier| is a string that allows to tie a specific instance of an
+// application to another.
 struct AppInstanceName {
   string url;
   string? qualifier;
 };
 
-// Certificate represents a human-readable name and public-key (DER encoded) pair.
-// The private-key for a certificate is only available for signing operations
-// within the principal service application.
-struct Certificate {
-   string extension;
-   array<uint8>? publickey;
+// Represents a user identity obtained for an application instance.
+//
+// |email| is the email address of the user, which may be obtained through a
+// third-party authentication flow (e.g., oauth2).
+//
+// |blessing| is a JSON-encoded Vanadium blessing binding a human-readable
+// name (that includes |email|) to the public key of the application instance.
+// More specifically, it represents a JSON-encoded WireBlessings object defined
+// here: https://github.com/vanadium/go.v23/blob/master/security/types.vdl#L136
+// A detailed decription of blessings can be found here:
+// https://github.com/vanadium/docs/blob/master/concepts/security.md
+struct User {
+  string email;
+  array<uint8> blessing;
+  // TODO(ataly, ukode): Include the name of the identity provider?
+  // TODO(ataly, ukode): Include the first and last name of the user?
+  // TODO(ataly, ukode): Include any unique ids assigned to the user by the
+  // identity provider?
 };
 
-// Blessing is a credential binding a user identity to a public key. The corresponding
-// private key is only available for signing within the PrincipalService application.
-struct Blessing {
-   array<Certificate> chain;
-};
-
-// ChainSeparator is the separator used to join name extensions in a certificate chain.
-const string ChainSeparator = "/";
-
-// A service that binds user identities to an application instance running in Mojo
+// A service that binds user identities to an application instance running in
+// Mojo. An application instance may have multiple user identities with one of
+// them set as the current identity.
 interface PrincipalService {
   // Login is called by an application instance (requestor_url/qualifier) that
-  // wants to get a user blessing. The service may obtain the user blessing
-  // through a third-party authentication flow (eg:oauth2). The user blessing
-  // is bound to a public/private key-pair that this service generates and
-  // persists for this application instance. Returns null if login fails.
-  Login() => (Blessing? user_blessing);
+  // wants to get a new user identity. The service may obtain the user identity
+  // through a third-party authentication flow (e.g., oauth2) which may involve
+  // user intervention. The obtained identity is added to the set of
+  // authenticated user identities of the application instance, and is also set
+  // as the current user identity for the application instance.
+  //
+  // Additionally, the service creates a user blessing that binds the obtained
+  // email address of the user to the unique public/private key-pair of the
+  // application instance.
+  //
+  // Returns the user identity or null if an error is encountered at any stage.
+  Login() => (User? user);
 
-  // Removes the user blessing for the application instance that invokes the
-  // Logout method.
+  // Logout sets the current user identity of the calling application instance
+  // to null.
   Logout();
 
-  // GetUserBlessing returns the user blessing for a given application instance.
-  // It returns an error if the application instance has not invoked Login().
-  GetUserBlessing(AppInstanceName app) => (Blessing? user_blessing);
-};
+  // GetUser returns the current user identity for a given application
+  // instance.  If a null application instance is provided then the current
+  // user identity of the calling application instance is returned.
+  //
+  // Returns null if the application instance has not invoked Login or if the
+  // instance is in logged out state (see 'Logout').
+  GetUser(AppInstanceName? app) => (User? user);
 
+  // SetUser sets the current user identity of the calling application
+  // instance. The provided identity must be present in the set of logged-in
+  // user identities for the application instance, otherwise an error is
+  // returned.
+  SetUser(User user) => (string? error);
+
+  // GetLoggedInUsers returns all authenticated user identities of the calling
+  // application instance. The user identities are a result of previous Login
+  // calls by the application instance.
+  GetLoggedInUsers() => (array<User> ids);
+};
diff --git a/mojo/services/view_manager/cpp/lib/view.cc b/mojo/services/view_manager/cpp/lib/view.cc
index a42fa12..aa4b30e 100644
--- a/mojo/services/view_manager/cpp/lib/view.cc
+++ b/mojo/services/view_manager/cpp/lib/view.cc
@@ -377,9 +377,7 @@
 ViewportMetricsPtr CreateEmptyViewportMetrics() {
   ViewportMetricsPtr metrics = ViewportMetrics::New();
   metrics->size = Size::New();
-  // TODO(vtl): The |.Pass()| below is only needed due to an MSVS bug; remove it
-  // once that's fixed.
-  return metrics.Pass();
+  return metrics;
 }
 
 }  // namespace
diff --git a/sky/build/PackagerInvoke b/sky/build/PackagerInvoke
new file mode 100755
index 0000000..b2bf658
--- /dev/null
+++ b/sky/build/PackagerInvoke
@@ -0,0 +1,82 @@
+#!/bin/sh
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+RunCommand() {
+  $@ >/dev/null
+  return $?
+}
+
+EchoError() {
+  echo "$@" 1>&2
+}
+
+AssertExists() {
+  RunCommand ls $1
+  if [ $? -ne 0 ]; then
+    EchoError "The path $1 does not exist"
+    exit -1
+  fi
+  return 0
+}
+
+PackageProject() {
+  # Check that the project path was specified
+  if [[ -z "$1" ]]; then
+    EchoError "Project path not specified"
+    exit -1
+  fi
+
+  # Check if pub exists
+  RunCommand which pub
+  if [[ $? -ne 0 ]]; then
+    EchoError "Could not find 'pub'."
+    EchoError "Did you install the Dart SDK?"
+    exit -1
+  fi
+
+  local pub_path="$(which pub)"
+
+  AssertExists $1
+  local project_path=$1
+
+  local derived_dir=${SOURCE_ROOT}/FlutterApplication/Generated
+  RunCommand mkdir -p $derived_dir
+  AssertExists $derived_dir
+
+  local dart_main=${project_path}/lib/main.dart
+  AssertExists $dart_main
+
+  local package_root=${project_path}/packages
+  AssertExists $package_root
+
+  local icons_path=${project_path}/packages/material_design_icons/icons
+  AssertExists $icons_path
+
+  # Remove old build artifacts
+  RunCommand rm -f ${derived_dir}/app.flx
+
+  # Generate the new FLX file. The pub command must be run from the directory
+  # containing the pubspec
+  RunCommand pushd ${project_path}
+
+  RunCommand ${pub_path} run sky_tools build                                   \
+      --asset-base ${icons_path}                                               \
+      --main ${dart_main}                                                      \
+      --output-file ${derived_dir}/app.flx                                     \
+      --package-root ${package_root}                                           \
+      --precompiled
+
+  if [[ $? -ne 0 ]]; then
+    EchoError "Failed to package $1 ..."
+    exit -1
+  fi
+
+  RunCommand popd
+
+  echo "Project $1 successfully packaged..."
+  return 0
+}
+
+PackageProject $1
diff --git a/sky/build/SnapshotterInvoke b/sky/build/SnapshotterInvoke
new file mode 100755
index 0000000..81b27b6
--- /dev/null
+++ b/sky/build/SnapshotterInvoke
@@ -0,0 +1,156 @@
+#!/bin/sh
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+RunCommand() {
+  $@ >/dev/null
+  return $?
+}
+
+EchoError() {
+  echo "$@" 1>&2
+}
+
+AssertExists() {
+  RunCommand ls $1
+  if [ $? -ne 0 ]; then
+    EchoError "The path $1 does not exist"
+    exit -1
+  fi
+  return 0
+}
+
+GenerateBinaryInclude() {
+  local input=$1
+  local output=$2
+
+  AssertExists ${input}
+
+  # Check that an input file was specified
+  if [[ -z "${output}" ]]; then
+    EchoError "Isolate buffer output file unspecified"
+    exit -1
+  fi
+
+  # Check that xxd is available on the system
+  RunCommand which xxd
+  if [[ $? -ne 0 ]]; then
+    EchoError "Could not find |xxd| to generate buffers for ${input}"
+    exit -1
+  fi
+
+  # Remove old build artifacts
+  RunCommand rm -f #{output}
+
+  # Since there is no flag to specify the symbol name to xxd, we change to the
+  # directory of the input file and invoke xxd from there
+  RunCommand pushd $(dirname ${input})
+
+  local input_basename=${input##*/}
+  RunCommand xxd --include ${input_basename} ${output}
+  if [[ $? -ne 0 ]]; then
+    EchoError "|xxd| invocation failed to generate ${output}"
+  fi
+
+  RunCommand popd
+
+  AssertExists $2
+}
+
+SnapshotProject() {
+  # Check that the caller has provided a project path
+  if [[ -z "$1" ]]; then
+    EchoError "The path to the dart project must be specified"
+    exit -1
+  fi
+
+  local project_path="$1"
+
+  # Check if 'pub get' has been run
+  RunCommand ls ${project_path}/packages
+  if [ $? -ne 0 ]; then
+    EchoError "The project does not have a packages directory"
+    EchoError "Did you forget to run 'pub get'?"
+    exit -1
+  fi
+
+  local packages=${project_path}/packages
+
+  # Resolve symlinks to point to the url mapping values
+
+  # For dart:mojo.internal
+  local mojo_internal_path="$(readlink ${packages}/mojo)/../sdk_ext/internal.dart"
+  AssertExists $mojo_internal_path
+
+  # For dart:ui_internals
+  local ui_internals_path="$(readlink ${packages}/sky_engine)/../sdk_ext/internals.dart"
+  AssertExists $ui_internals_path
+
+  # For dart:ui
+  local ui_path="$(readlink ${packages}/sky_engine)/../sdk_ext/dart_ui.dart"
+  AssertExists $ui_path
+
+  # For dart:vmservice_sky
+  local vm_service_path="$(readlink ${packages}/sky_engine)/../sdk_ext/core/script/dart_service_isolate/main.dart"
+  AssertExists $vm_service_path
+
+  local main_path="${project_path}/lib/main.dart"
+  AssertExists $main_path
+
+  local src_dir=${SOURCE_ROOT}/Tools
+  local derived_dir=${SOURCE_ROOT}/FlutterApplication/Generated
+
+  RunCommand mkdir -p $derived_dir
+
+  AssertExists $src_dir
+  AssertExists $derived_dir
+
+  # Remove old build artifacts
+  RunCommand rm -f ${derived_dir}/kDartVmIsolateSnapshotBuffer.c
+  RunCommand rm -f ${derived_dir}/kDartIsolateSnapshotBuffer.c
+  RunCommand rm -f ${derived_dir}/InstructionsSnapshot.S
+
+  # Finally! Generate the snapshot. The instructions buffer is already in an
+  # assembly file which can be directly used by the linker. For the VM isolate
+  # snapshot buffer and isolate snapshot buffer, we name the file to match
+  # the name of the symbol the VM expects at runtime. On these binary files,
+  # we invoke xxd.
+  RunCommand ${src_dir}/Snapshotter                                            \
+      --vm_isolate_snapshot=${derived_dir}/kDartVmIsolateSnapshotBuffer        \
+      --isolate_snapshot=${derived_dir}/kDartIsolateSnapshotBuffer             \
+      --instructions_snapshot=${derived_dir}/InstructionsSnapshot.S            \
+      --embedder_entry_points_manifest=${src_dir}/EmbedderEntryPoints          \
+      --package_root=${packages}                                               \
+      --url_mapping=dart:mojo.internal,${mojo_internal_path}                   \
+      --url_mapping=dart:ui,${ui_path}                                         \
+      --url_mapping=dart:ui_internals,${ui_internals_path}                     \
+      --url_mapping=dart:vmservice_sky,$vm_service_path                        \
+      $main_path
+
+  if [[ $? -ne 0 ]]; then
+    EchoError "Snapshotter failed for $1 ..."
+    exit -1
+  fi
+
+  # The instruction buffer is already generated, the isolates need to be packed
+  # into C files
+  GenerateBinaryInclude                                                        \
+      ${derived_dir}/kDartVmIsolateSnapshotBuffer                              \
+      ${derived_dir}/kDartVmIsolateSnapshotBuffer.c                            \
+
+  GenerateBinaryInclude                                                        \
+      ${derived_dir}/kDartIsolateSnapshotBuffer                                \
+      ${derived_dir}/kDartIsolateSnapshotBuffer.c                              \
+
+
+  # Remove intermediate build artifacts
+  RunCommand rm -f ${derived_dir}/kDartVmIsolateSnapshotBuffer
+  RunCommand rm -f ${derived_dir}/kDartIsolateSnapshotBuffer
+
+  echo "Precompilation snapshot successfully created for ${project_path} ..."
+
+  return $?
+}
+
+SnapshotProject $1
diff --git a/sky/build/sdk_xcode_harness/.gitignore b/sky/build/sdk_xcode_harness/.gitignore
new file mode 100644
index 0000000..59ed186
--- /dev/null
+++ b/sky/build/sdk_xcode_harness/.gitignore
@@ -0,0 +1,26 @@
+.idea/
+.vagrant/
+.sconsign.dblite
+.svn/
+
+.DS_Store
+*.swp
+*.lock
+profile
+
+DerivedData/
+build/
+
+*.pbxuser
+*.mode1v3
+*.mode2v3
+*.perspectivev3
+
+!default.pbxuser
+!default.mode1v3
+!default.mode2v3
+!default.perspectivev3
+
+xcuserdata
+
+*.moved-aside
diff --git a/sky/build/sdk_xcode_harness/FlutterApplication.xcodeproj/project.pbxproj b/sky/build/sdk_xcode_harness/FlutterApplication.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..359db21
--- /dev/null
+++ b/sky/build/sdk_xcode_harness/FlutterApplication.xcodeproj/project.pbxproj
@@ -0,0 +1,582 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		9E07CF8A1BE7F4D200BCD8DE /* FlutterApplication.h in Headers */ = {isa = PBXBuildFile; fileRef = 9E07CF891BE7F4D200BCD8DE /* FlutterApplication.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		9E07CFA91BE8280A00BCD8DE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9E07CFA81BE8280A00BCD8DE /* Assets.xcassets */; };
+		9E07CFAC1BE8280A00BCD8DE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9E07CFAA1BE8280A00BCD8DE /* LaunchScreen.storyboard */; };
+		9E07CFB51BE82D2600BCD8DE /* FlutterApplication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E07CF861BE7F4D200BCD8DE /* FlutterApplication.framework */; };
+		9E07CFB61BE82D2600BCD8DE /* FlutterApplication.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9E07CF861BE7F4D200BCD8DE /* FlutterApplication.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		9E07CFBA1BE82DFF00BCD8DE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E07CF9D1BE8280A00BCD8DE /* main.m */; };
+		9E07CFE81BEAA35200BCD8DE /* InstructionsSnapshot.S in Sources */ = {isa = PBXBuildFile; fileRef = 9E07CFE51BEAA35200BCD8DE /* InstructionsSnapshot.S */; };
+		9E07CFE91BEAA35200BCD8DE /* kDartIsolateSnapshotBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9E07CFE61BEAA35200BCD8DE /* kDartIsolateSnapshotBuffer.c */; };
+		9E07CFEA1BEAA35200BCD8DE /* kDartVmIsolateSnapshotBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9E07CFE71BEAA35200BCD8DE /* kDartVmIsolateSnapshotBuffer.c */; };
+		9E07CFEE1BEAA45400BCD8DE /* FlutterRunner in Copy Flutter Runner Executable And Resources */ = {isa = PBXBuildFile; fileRef = 9E07CFDE1BEAA08B00BCD8DE /* FlutterRunner */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
+		9E07CFEF1BEAA45400BCD8DE /* icudtl.dat in Copy Flutter Runner Executable And Resources */ = {isa = PBXBuildFile; fileRef = 9E07CFDF1BEAA08B00BCD8DE /* icudtl.dat */; };
+		9E07CFF41BEAB58200BCD8DE /* app.flx in Resources */ = {isa = PBXBuildFile; fileRef = 9E07CFF31BEAB58200BCD8DE /* app.flx */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		9E07CFB71BE82D2600BCD8DE /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 9E07CF7D1BE7F4D200BCD8DE /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 9E07CF851BE7F4D200BCD8DE;
+			remoteInfo = FlutterApplication;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		9E07CFB91BE82D2600BCD8DE /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+				9E07CFB61BE82D2600BCD8DE /* FlutterApplication.framework in Embed Frameworks */,
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		9E07CFBD1BE82E5900BCD8DE /* Copy Flutter Runner Executable And Resources */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 7;
+			files = (
+				9E07CFEE1BEAA45400BCD8DE /* FlutterRunner in Copy Flutter Runner Executable And Resources */,
+				9E07CFEF1BEAA45400BCD8DE /* icudtl.dat in Copy Flutter Runner Executable And Resources */,
+			);
+			name = "Copy Flutter Runner Executable And Resources";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		9E07CF861BE7F4D200BCD8DE /* FlutterApplication.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FlutterApplication.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		9E07CF891BE7F4D200BCD8DE /* FlutterApplication.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FlutterApplication.h; sourceTree = "<group>"; };
+		9E07CF8B1BE7F4D200BCD8DE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		9E07CF9A1BE8280A00BCD8DE /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		9E07CF9D1BE8280A00BCD8DE /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+		9E07CFA81BE8280A00BCD8DE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		9E07CFAB1BE8280A00BCD8DE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+		9E07CFAD1BE8280A00BCD8DE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		9E07CFDE1BEAA08B00BCD8DE /* FlutterRunner */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = FlutterRunner; path = RunnerResources/FlutterRunner; sourceTree = SOURCE_ROOT; };
+		9E07CFDF1BEAA08B00BCD8DE /* icudtl.dat */ = {isa = PBXFileReference; lastKnownFileType = file; name = icudtl.dat; path = RunnerResources/icudtl.dat; sourceTree = SOURCE_ROOT; };
+		9E07CFE11BEAA0F900BCD8DE /* EmbedderEntryPoints */ = {isa = PBXFileReference; lastKnownFileType = text; path = EmbedderEntryPoints; sourceTree = "<group>"; };
+		9E07CFE21BEAA0F900BCD8DE /* PackagerInvoke */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = PackagerInvoke; sourceTree = "<group>"; };
+		9E07CFE41BEAA0F900BCD8DE /* SnapshotterInvoke */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = SnapshotterInvoke; sourceTree = "<group>"; };
+		9E07CFE51BEAA35200BCD8DE /* InstructionsSnapshot.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = InstructionsSnapshot.S; path = Generated/InstructionsSnapshot.S; sourceTree = "<group>"; };
+		9E07CFE61BEAA35200BCD8DE /* kDartIsolateSnapshotBuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = kDartIsolateSnapshotBuffer.c; path = Generated/kDartIsolateSnapshotBuffer.c; sourceTree = "<group>"; };
+		9E07CFE71BEAA35200BCD8DE /* kDartVmIsolateSnapshotBuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = kDartVmIsolateSnapshotBuffer.c; path = Generated/kDartVmIsolateSnapshotBuffer.c; sourceTree = "<group>"; };
+		9E07CFEC1BEAA44D00BCD8DE /* FlutterRunner */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = FlutterRunner; sourceTree = "<group>"; };
+		9E07CFED1BEAA44D00BCD8DE /* icudtl.dat */ = {isa = PBXFileReference; lastKnownFileType = file; path = icudtl.dat; sourceTree = "<group>"; };
+		9E07CFF01BEAB1BA00BCD8DE /* Snapshotter */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = Snapshotter; sourceTree = "<group>"; };
+		9E07CFF31BEAB58200BCD8DE /* app.flx */ = {isa = PBXFileReference; lastKnownFileType = file; name = app.flx; path = Generated/app.flx; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		9E07CF821BE7F4D200BCD8DE /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		9E07CF971BE8280A00BCD8DE /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9E07CFB51BE82D2600BCD8DE /* FlutterApplication.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		9E07CF7C1BE7F4D200BCD8DE = {
+			isa = PBXGroup;
+			children = (
+				9E07CF881BE7F4D200BCD8DE /* FlutterApplication */,
+				9E07CF9B1BE8280A00BCD8DE /* Runner */,
+				9E07CFEB1BEAA44D00BCD8DE /* RunnerResources */,
+				9E07CFE01BEAA0F900BCD8DE /* Tools */,
+				9E07CF871BE7F4D200BCD8DE /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		9E07CF871BE7F4D200BCD8DE /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				9E07CF861BE7F4D200BCD8DE /* FlutterApplication.framework */,
+				9E07CF9A1BE8280A00BCD8DE /* Runner.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		9E07CF881BE7F4D200BCD8DE /* FlutterApplication */ = {
+			isa = PBXGroup;
+			children = (
+				9E07CFD61BE997D800BCD8DE /* Generated */,
+				9E07CF891BE7F4D200BCD8DE /* FlutterApplication.h */,
+				9E07CF8B1BE7F4D200BCD8DE /* Info.plist */,
+			);
+			path = FlutterApplication;
+			sourceTree = "<group>";
+		};
+		9E07CF9B1BE8280A00BCD8DE /* Runner */ = {
+			isa = PBXGroup;
+			children = (
+				9E07CFDD1BEAA07E00BCD8DE /* Resources */,
+				9E07CFA81BE8280A00BCD8DE /* Assets.xcassets */,
+				9E07CFAA1BE8280A00BCD8DE /* LaunchScreen.storyboard */,
+				9E07CFAD1BE8280A00BCD8DE /* Info.plist */,
+				9E07CF9C1BE8280A00BCD8DE /* Supporting Files */,
+			);
+			path = Runner;
+			sourceTree = "<group>";
+		};
+		9E07CF9C1BE8280A00BCD8DE /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				9E07CF9D1BE8280A00BCD8DE /* main.m */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+		9E07CFD61BE997D800BCD8DE /* Generated */ = {
+			isa = PBXGroup;
+			children = (
+				9E07CFF31BEAB58200BCD8DE /* app.flx */,
+				9E07CFE51BEAA35200BCD8DE /* InstructionsSnapshot.S */,
+				9E07CFE61BEAA35200BCD8DE /* kDartIsolateSnapshotBuffer.c */,
+				9E07CFE71BEAA35200BCD8DE /* kDartVmIsolateSnapshotBuffer.c */,
+			);
+			name = Generated;
+			sourceTree = "<group>";
+		};
+		9E07CFDD1BEAA07E00BCD8DE /* Resources */ = {
+			isa = PBXGroup;
+			children = (
+				9E07CFDE1BEAA08B00BCD8DE /* FlutterRunner */,
+				9E07CFDF1BEAA08B00BCD8DE /* icudtl.dat */,
+			);
+			name = Resources;
+			sourceTree = "<group>";
+		};
+		9E07CFE01BEAA0F900BCD8DE /* Tools */ = {
+			isa = PBXGroup;
+			children = (
+				9E07CFE11BEAA0F900BCD8DE /* EmbedderEntryPoints */,
+				9E07CFE21BEAA0F900BCD8DE /* PackagerInvoke */,
+				9E07CFF01BEAB1BA00BCD8DE /* Snapshotter */,
+				9E07CFE41BEAA0F900BCD8DE /* SnapshotterInvoke */,
+			);
+			path = Tools;
+			sourceTree = "<group>";
+		};
+		9E07CFEB1BEAA44D00BCD8DE /* RunnerResources */ = {
+			isa = PBXGroup;
+			children = (
+				9E07CFEC1BEAA44D00BCD8DE /* FlutterRunner */,
+				9E07CFED1BEAA44D00BCD8DE /* icudtl.dat */,
+			);
+			path = RunnerResources;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+		9E07CF831BE7F4D200BCD8DE /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9E07CF8A1BE7F4D200BCD8DE /* FlutterApplication.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+		9E07CF851BE7F4D200BCD8DE /* FlutterApplication */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 9E07CF8E1BE7F4D200BCD8DE /* Build configuration list for PBXNativeTarget "FlutterApplication" */;
+			buildPhases = (
+				9E07CFF21BEAB2E800BCD8DE /* Run FLX Packager */,
+				9E07CFCD1BE98FCD00BCD8DE /* Generate Precompiled Snapshot */,
+				9E07CF811BE7F4D200BCD8DE /* Sources */,
+				9E07CF821BE7F4D200BCD8DE /* Frameworks */,
+				9E07CF831BE7F4D200BCD8DE /* Headers */,
+				9E07CF841BE7F4D200BCD8DE /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = FlutterApplication;
+			productName = FlutterApplication;
+			productReference = 9E07CF861BE7F4D200BCD8DE /* FlutterApplication.framework */;
+			productType = "com.apple.product-type.framework";
+		};
+		9E07CF991BE8280A00BCD8DE /* Runner */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 9E07CFB01BE8280A00BCD8DE /* Build configuration list for PBXNativeTarget "Runner" */;
+			buildPhases = (
+				9E07CF961BE8280A00BCD8DE /* Sources */,
+				9E07CF971BE8280A00BCD8DE /* Frameworks */,
+				9E07CF981BE8280A00BCD8DE /* Resources */,
+				9E07CFB91BE82D2600BCD8DE /* Embed Frameworks */,
+				9E07CFBD1BE82E5900BCD8DE /* Copy Flutter Runner Executable And Resources */,
+				9E07CFC01BE82ECD00BCD8DE /* Remove Stub Runner */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				9E07CFB81BE82D2600BCD8DE /* PBXTargetDependency */,
+			);
+			name = Runner;
+			productName = Runner;
+			productReference = 9E07CF9A1BE8280A00BCD8DE /* Runner.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		9E07CF7D1BE7F4D200BCD8DE /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0710;
+				ORGANIZATIONNAME = Flutter;
+				TargetAttributes = {
+					9E07CF851BE7F4D200BCD8DE = {
+						CreatedOnToolsVersion = 7.1;
+					};
+					9E07CF991BE8280A00BCD8DE = {
+						CreatedOnToolsVersion = 7.1;
+					};
+				};
+			};
+			buildConfigurationList = 9E07CF801BE7F4D200BCD8DE /* Build configuration list for PBXProject "FlutterApplication" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 9E07CF7C1BE7F4D200BCD8DE;
+			productRefGroup = 9E07CF871BE7F4D200BCD8DE /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				9E07CF851BE7F4D200BCD8DE /* FlutterApplication */,
+				9E07CF991BE8280A00BCD8DE /* Runner */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		9E07CF841BE7F4D200BCD8DE /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9E07CFF41BEAB58200BCD8DE /* app.flx in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		9E07CF981BE8280A00BCD8DE /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9E07CFAC1BE8280A00BCD8DE /* LaunchScreen.storyboard in Resources */,
+				9E07CFA91BE8280A00BCD8DE /* Assets.xcassets in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		9E07CFC01BE82ECD00BCD8DE /* Remove Stub Runner */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Remove Stub Runner";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "rm ${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Runner";
+			showEnvVarsInLog = 0;
+		};
+		9E07CFCD1BE98FCD00BCD8DE /* Generate Precompiled Snapshot */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"$(SRCROOT)/Tools/SnapshotterInvoke",
+				"$(FLUTTER_APPLICATION_PATH)/lib/main.dart",
+				"$(FLUTTER_APPLICATION_PATH)/pubspec.lock",
+			);
+			name = "Generate Precompiled Snapshot";
+			outputPaths = (
+				"$(SRCROOT)/FlutterApplication/Generated/InstructionsSnapshot.S",
+				"$(SRCROOT)/FlutterApplication/Generated/kDartIsolateSnapshotBuffer.c",
+				"$(SRCROOT)/FlutterApplication/Generated/kDartVmIsolateSnapshotBuffer.c",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "${SOURCE_ROOT}/Tools/SnapshotterInvoke ${FLUTTER_APPLICATION_PATH}";
+			showEnvVarsInLog = 0;
+		};
+		9E07CFF21BEAB2E800BCD8DE /* Run FLX Packager */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"$(FLUTTER_APPLICATION_PATH)/flutter.yaml",
+				"$(SRCROOT)/Tools/PackagerInvoke",
+			);
+			name = "Run FLX Packager";
+			outputPaths = (
+				"${SRCROOT}/FlutterApplication/Generated/app.flx",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "${SOURCE_ROOT}/Tools/PackagerInvoke ${FLUTTER_APPLICATION_PATH}";
+			showEnvVarsInLog = 0;
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		9E07CF811BE7F4D200BCD8DE /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9E07CFEA1BEAA35200BCD8DE /* kDartVmIsolateSnapshotBuffer.c in Sources */,
+				9E07CFE91BEAA35200BCD8DE /* kDartIsolateSnapshotBuffer.c in Sources */,
+				9E07CFE81BEAA35200BCD8DE /* InstructionsSnapshot.S in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		9E07CF961BE8280A00BCD8DE /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9E07CFBA1BE82DFF00BCD8DE /* main.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		9E07CFB81BE82D2600BCD8DE /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 9E07CF851BE7F4D200BCD8DE /* FlutterApplication */;
+			targetProxy = 9E07CFB71BE82D2600BCD8DE /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+		9E07CFAA1BE8280A00BCD8DE /* LaunchScreen.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				9E07CFAB1BE8280A00BCD8DE /* Base */,
+			);
+			name = LaunchScreen.storyboard;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		9E07CF8C1BE7F4D200BCD8DE /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				CURRENT_PROJECT_VERSION = 1;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.1;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Debug;
+		};
+		9E07CF8D1BE7F4D200BCD8DE /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				CURRENT_PROJECT_VERSION = 1;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.1;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Release;
+		};
+		9E07CF8F1BE7F4D200BCD8DE /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				FLUTTER_APPLICATION_PATH = path/to/application;
+				INFOPLIST_FILE = FlutterApplication/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.flutter.aplication.FlutterApplication;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+			};
+			name = Debug;
+		};
+		9E07CF901BE7F4D200BCD8DE /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				FLUTTER_APPLICATION_PATH = path/to/application;
+				INFOPLIST_FILE = FlutterApplication/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.flutter.aplication.FlutterApplication;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+			};
+			name = Release;
+		};
+		9E07CFAE1BE8280A00BCD8DE /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				INFOPLIST_FILE = Runner/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.flutter.runner.Runner;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		9E07CFAF1BE8280A00BCD8DE /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				INFOPLIST_FILE = Runner/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.flutter.runner.Runner;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		9E07CF801BE7F4D200BCD8DE /* Build configuration list for PBXProject "FlutterApplication" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				9E07CF8C1BE7F4D200BCD8DE /* Debug */,
+				9E07CF8D1BE7F4D200BCD8DE /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		9E07CF8E1BE7F4D200BCD8DE /* Build configuration list for PBXNativeTarget "FlutterApplication" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				9E07CF8F1BE7F4D200BCD8DE /* Debug */,
+				9E07CF901BE7F4D200BCD8DE /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		9E07CFB01BE8280A00BCD8DE /* Build configuration list for PBXNativeTarget "Runner" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				9E07CFAE1BE8280A00BCD8DE /* Debug */,
+				9E07CFAF1BE8280A00BCD8DE /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 9E07CF7D1BE7F4D200BCD8DE /* Project object */;
+}
diff --git a/sky/build/sdk_xcode_harness/FlutterApplication.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/sky/build/sdk_xcode_harness/FlutterApplication.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..560d61a
--- /dev/null
+++ b/sky/build/sdk_xcode_harness/FlutterApplication.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:FlutterApplication.xcodeproj">
+   </FileRef>
+</Workspace>
diff --git a/sky/build/sdk_xcode_harness/FlutterApplication/.gitignore b/sky/build/sdk_xcode_harness/FlutterApplication/.gitignore
new file mode 100644
index 0000000..673b74e
--- /dev/null
+++ b/sky/build/sdk_xcode_harness/FlutterApplication/.gitignore
@@ -0,0 +1 @@
+Generated/
diff --git a/sky/build/sdk_xcode_harness/FlutterApplication/FlutterApplication.h b/sky/build/sdk_xcode_harness/FlutterApplication/FlutterApplication.h
new file mode 100644
index 0000000..f5953de
--- /dev/null
+++ b/sky/build/sdk_xcode_harness/FlutterApplication/FlutterApplication.h
@@ -0,0 +1,22 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if __cplusplus
+extern "C" {
+#endif
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stddef.h>
+
+extern double FlutterApplicationVersionNumber;
+extern const unsigned char FlutterApplicationVersionString[];
+
+extern const uint8_t* kInstructionsSnapshot;
+extern const uint8_t* kDartIsolateSnapshotBuffer;
+extern const uint8_t* kDartVmIsolateSnapshotBuffer;
+
+#if __cplusplus
+}
+#endif
diff --git a/sky/build/sdk_xcode_harness/FlutterApplication/Info.plist b/sky/build/sdk_xcode_harness/FlutterApplication/Info.plist
new file mode 100644
index 0000000..d3de8ee
--- /dev/null
+++ b/sky/build/sdk_xcode_harness/FlutterApplication/Info.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>FMWK</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+	<key>NSPrincipalClass</key>
+	<string></string>
+</dict>
+</plist>
diff --git a/sky/build/sdk_xcode_harness/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/sky/build/sdk_xcode_harness/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..36d2c80
--- /dev/null
+++ b/sky/build/sdk_xcode_harness/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "2x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/sky/build/sdk_xcode_harness/Runner/Base.lproj/LaunchScreen.storyboard b/sky/build/sdk_xcode_harness/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..497e9d7
--- /dev/null
+++ b/sky/build/sdk_xcode_harness/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9059" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9049"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="EHf-IW-A2E">
+            <objects>
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
+                        <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <animations/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="53" y="375"/>
+        </scene>
+    </scenes>
+</document>
diff --git a/sky/build/sdk_xcode_harness/Runner/Info.plist b/sky/build/sdk_xcode_harness/Runner/Info.plist
new file mode 100644
index 0000000..5b6b50f
--- /dev/null
+++ b/sky/build/sdk_xcode_harness/Runner/Info.plist
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>FlutterRunner</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+</dict>
+</plist>
diff --git a/sky/build/sdk_xcode_harness/Runner/main.m b/sky/build/sdk_xcode_harness/Runner/main.m
new file mode 100644
index 0000000..5ddbd34
--- /dev/null
+++ b/sky/build/sdk_xcode_harness/Runner/main.m
@@ -0,0 +1,10 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Foundation/Foundation.h>
+
+int main(int argc, char* argv[]) {
+  NSCAssert(NO, @"Placeholder for Xcode. Should never be run");
+  return EXIT_FAILURE;
+}
diff --git a/sky/build/sky_precompilation_sdk.gni b/sky/build/sky_precompilation_sdk.gni
new file mode 100644
index 0000000..806ae3b
--- /dev/null
+++ b/sky/build/sky_precompilation_sdk.gni
@@ -0,0 +1,86 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//sky/engine/bindings/bindings.gni")
+
+template("sky_precompilation_sdk") {
+  assert(is_ios, "The precompilation SDK is only supported for iOS targets")
+
+  assert(defined(invoker.sdk_name), "The SDK name must be defined")
+
+  sdk_name = invoker.sdk_name
+  sdk_dir = "$root_out_dir/$sdk_name"
+
+  snapshotter_copy_gen_target_name = target_name + "_copy_snapshotter"
+  copy(snapshotter_copy_gen_target_name) {
+    snapshotter_target = "//dart/runtime/bin:gen_snapshot($dart_host_toolchain)"
+    snapshotter_directory = get_label_info(snapshotter_target, "root_out_dir")
+    snapshotter_name = get_label_info(snapshotter_target, "name")
+
+    sources = [  "$snapshotter_directory/$snapshotter_name"  ]
+    outputs = [  "$sdk_dir/Tools/Snapshotter"  ]
+
+    deps = [  snapshotter_target  ]
+  }
+
+  copy("embedder_entry_points") {
+    sources = [ "//sky/engine/bindings/dart_vm_entry_points.txt" ]
+    outputs = [  "$sdk_dir/Tools/EmbedderEntryPoints"  ]
+  }
+
+  copy("precompilation_xcode_scripts") {
+    sources = [
+      "//sky/build/SnapshotterInvoke",
+      "//sky/build/PackagerInvoke",
+    ]
+    outputs = [  "$sdk_dir/Tools/{{source_file_part}}"  ]
+  }
+
+  copy("copy_sdk_xcode_harness") {
+    sources = [
+      "//sky/build/sdk_xcode_harness/FlutterApplication",
+      "//sky/build/sdk_xcode_harness/FlutterApplication.xcodeproj",
+      "//sky/build/sdk_xcode_harness/Runner",
+    ]
+    outputs = [  "$sdk_dir/{{source_file_part}}"  ]
+  }
+
+  executable_gen_target_name = target_name + "_runner"
+  executable(executable_gen_target_name) {
+    libs = [
+      "UIKit.framework",
+      "AVFoundation.framework",
+      "QuartzCore.framework",
+      "OpenGLES.framework",
+    ]
+    deps = [  "//sky/shell:ios_scaffolding"  ]
+  }
+
+  copy_runner_gen_target_name = target_name + "_copy_runner"
+  copy(copy_runner_gen_target_name) {
+    sources = [  "$root_out_dir/$executable_gen_target_name"  ]
+    outputs = [  "$sdk_dir/RunnerResources/FlutterRunner"  ]
+
+    deps = [  ":$executable_gen_target_name"  ]
+  }
+
+  copy_data_gen_target_name = target_name + "_copy_data"
+  copy(copy_data_gen_target_name) {
+    set_sources_assignment_filter([])
+    sources = [  "//third_party/icu/android/icudtl.dat"  ]
+    outputs = [  "$sdk_dir/RunnerResources/{{source_file_part}}"  ]
+    set_sources_assignment_filter(sources_assignment_filter)
+  }
+
+  group(target_name) {
+    deps = [
+      ":$snapshotter_copy_gen_target_name",
+      ":embedder_entry_points",
+      ":$copy_runner_gen_target_name",
+      ":$copy_data_gen_target_name",
+      ":precompilation_xcode_scripts",
+      ":copy_sdk_xcode_harness",
+    ]
+  }
+}
diff --git a/sky/engine/bindings/BUILD.gn b/sky/engine/bindings/BUILD.gn
index a0b7a25..e124beb 100644
--- a/sky/engine/bindings/BUILD.gn
+++ b/sky/engine/bindings/BUILD.gn
@@ -21,8 +21,6 @@
     "exception_state_placeholder.h",
     "exception_state.cc",
     "exception_state.h",
-    "mojo_natives.cc",
-    "mojo_natives.h",
     "nullable.h",
     "scheduled_action.cc",
     "scheduled_action.h",
@@ -35,6 +33,7 @@
     "//dart/runtime:libdart",
     "//mojo/public/c/system",
     "//mojo/public/cpp/system",
+    "//mojo/public/platform/dart:mojo_internal_impl",
     "//sky/engine/core:prerequisites",
     "//sky/engine/platform:platform",
     "//sky/engine/tonic",
@@ -116,11 +115,11 @@
   ]
   inputs = [
     "//dart/runtime/tools/create_snapshot_file.py",
-    "snapshot.cc.tmpl",
+    "snapshot.c.tmpl",
     "$target_gen_dir/vm_isolate_snapshot.bin",
     "$target_gen_dir/isolate_snapshot.bin",
   ]
-  output = "$target_gen_dir/snapshot.cc"
+  output = "$target_gen_dir/snapshot.c"
   outputs = [
     output,
   ]
@@ -132,7 +131,7 @@
     "--input_bin",
     rebase_path("$target_gen_dir/isolate_snapshot.bin"),
     "--input_cc",
-    rebase_path("snapshot.cc.tmpl"),
+    rebase_path("snapshot.c.tmpl"),
     "--output",
     rebase_path(output),
   ]
@@ -171,7 +170,7 @@
 
 source_set("snapshot_cc") {
   sources = [
-    "$target_gen_dir/snapshot.cc",
+    "$target_gen_dir/snapshot.c",
   ]
 
   deps = [
diff --git a/sky/engine/bindings/bindings.gni b/sky/engine/bindings/bindings.gni
index f6cd013..e936330 100644
--- a/sky/engine/bindings/bindings.gni
+++ b/sky/engine/bindings/bindings.gni
@@ -49,26 +49,26 @@
   "scripts/v8_utilities.py",
 ]
 
+dart_host_toolchain = host_toolchain
+if (target_os == "ios" && !use_ios_simulator) {
+  # During precompilation, a 64 bit Dart VM cannot generate code for a 32 bit
+  # architecture (and vice-versa). The snapshotter that is running on the host
+  # needs to know about the target architecture and built accordingly.
+  if (target_cpu == "arm") {
+    dart_host_toolchain = "//build/toolchain/mac:clang_i386"
+  } else if (target_cpu == "arm64") {
+    dart_host_toolchain = "//build/toolchain/mac:clang_x64"
+  } else {
+    assert(false, "Unknown active architecture on iOS")
+  }
+}
+
 template("dart_precompile") {
   assert(defined(invoker.dart_package_root),
          "The dart package root must be defined")
   assert(defined(invoker.dart_script),
          "The dart script must be specified")
 
-  dart_host_toolchain = host_toolchain
-  if (target_os == "ios" && !use_ios_simulator) {
-    # During precompilation, a 64 bit Dart VM cannot generate code for a 32 bit
-    # architecture (and vice-versa). The snapshotter that is running on the host
-    # needs to know about the target architecture and built accordingly.
-    if (target_cpu == "arm") {
-      dart_host_toolchain = "//build/toolchain/mac:clang_i386"
-    } else if (target_cpu == "arm64") {
-      dart_host_toolchain = "//build/toolchain/mac:clang_x64"
-    } else {
-      assert(false, "Unknown active architecture on iOS")
-    }
-  }
-
   vm_isolate_snapshot_name = target_name + "_vm_isolate.bin"
   vm_isolate_snapshot = "$target_gen_dir/$vm_isolate_snapshot_name"
   isolate_snapshot_name = target_name + "_isolate.bin"
@@ -137,14 +137,14 @@
     ]
   }
 
-  snapshot_cc = "$target_gen_dir/" + target_name + "_precompiled_snapshot.cc"
-  snapshot_cc_gen_target_name = target_name + "_snapshot_cc"
-  action(snapshot_cc_gen_target_name) {
+  snapshot_c = "$target_gen_dir/" + target_name + "_precompiled_snapshot.c"
+  snapshot_c_gen_target_name = target_name + "_snapshot_c"
+  action(snapshot_c_gen_target_name) {
     deps = [
       ":$instructions_gen_target_name",
     ]
 
-    template_file = "//sky/engine/bindings/snapshot.cc.tmpl"
+    template_file = "//sky/engine/bindings/snapshot.c.tmpl"
 
     inputs = [
       template_file,
@@ -153,7 +153,7 @@
     ]
 
     outputs = [
-      snapshot_cc,
+      snapshot_c,
     ]
 
     script = "//dart/runtime/tools/create_snapshot_file.py"
@@ -165,19 +165,19 @@
       "--input_cc",
       rebase_path(template_file, root_build_dir),
       "--output",
-      rebase_path(snapshot_cc),
+      rebase_path(snapshot_c),
     ]
   }
 
   source_set(target_name) {
     sources = [
       assembly_path,
-      snapshot_cc,
+      snapshot_c,
     ]
 
     deps = [
       ":$instructions_gen_target_name",
-      ":$snapshot_cc_gen_target_name",
+      ":$snapshot_c_gen_target_name",
     ]
   }
 }
diff --git a/sky/engine/bindings/dart_mojo_internal.cc b/sky/engine/bindings/dart_mojo_internal.cc
index 6be45ae..01dac0d 100644
--- a/sky/engine/bindings/dart_mojo_internal.cc
+++ b/sky/engine/bindings/dart_mojo_internal.cc
@@ -5,7 +5,7 @@
 #include "sky/engine/bindings/dart_mojo_internal.h"
 
 #include "dart/runtime/include/dart_api.h"
-#include "sky/engine/bindings/mojo_natives.h"
+#include "mojo/public/platform/dart/mojo_natives.h"
 #include "sky/engine/tonic/dart_converter.h"
 
 namespace blink {
@@ -13,8 +13,8 @@
 void DartMojoInternal::InitForIsolate() {
   DART_CHECK_VALID(Dart_SetNativeResolver(
       Dart_LookupLibrary(ToDart("dart:mojo.internal")),
-      MojoNativeLookup,
-      MojoNativeSymbol));
+      mojo::dart::MojoNativeLookup,
+      mojo::dart::MojoNativeSymbol));
 }
 
 }  // namespace blink
diff --git a/sky/engine/bindings/dart_ui.cc b/sky/engine/bindings/dart_ui.cc
index e113ef7..429589d 100644
--- a/sky/engine/bindings/dart_ui.cc
+++ b/sky/engine/bindings/dart_ui.cc
@@ -7,6 +7,7 @@
 #include "gen/sky/bindings/DartGlobal.h"
 #include "sky/engine/bindings/dart_runtime_hooks.h"
 #include "sky/engine/core/painting/painting.h"
+#include "sky/engine/core/tracing/tracing.h"
 #include "sky/engine/core/window/window.h"
 #include "sky/engine/tonic/dart_converter.h"
 #include "sky/engine/tonic/dart_error.h"
@@ -40,6 +41,7 @@
     DartRuntimeHooks::RegisterNatives(g_natives);
     Window::RegisterNatives(g_natives);
     Painting::RegisterNatives(g_natives);
+    Tracing::RegisterNatives(g_natives);
   }
 
   DART_CHECK_VALID(Dart_SetNativeResolver(
diff --git a/sky/engine/bindings/mojo_natives.cc b/sky/engine/bindings/mojo_natives.cc
deleted file mode 100644
index 4649cf9..0000000
--- a/sky/engine/bindings/mojo_natives.cc
+++ /dev/null
@@ -1,885 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "sky/engine/bindings/mojo_natives.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/memory/scoped_ptr.h"
-#include "dart/runtime/include/dart_api.h"
-#include "mojo/public/c/system/core.h"
-#include "mojo/public/cpp/system/core.h"
-#include "sky/engine/tonic/dart_converter.h"
-
-namespace blink {
-
-#define REGISTER_FUNCTION(name, count)                                         \
-  { "" #name, name, count },
-#define DECLARE_FUNCTION(name, count)                                          \
-  extern void name(Dart_NativeArguments args);
-
-#define MOJO_NATIVE_LIST(V)                \
-  V(MojoSharedBuffer_Create, 2)            \
-  V(MojoSharedBuffer_Duplicate, 2)         \
-  V(MojoSharedBuffer_Map, 5)               \
-  V(MojoSharedBuffer_Unmap, 1)             \
-  V(MojoDataPipe_Create, 3)                \
-  V(MojoDataPipe_WriteData, 4)             \
-  V(MojoDataPipe_BeginWriteData, 3)        \
-  V(MojoDataPipe_EndWriteData, 2)          \
-  V(MojoDataPipe_ReadData, 4)              \
-  V(MojoDataPipe_BeginReadData, 3)         \
-  V(MojoDataPipe_EndReadData, 2)           \
-  V(MojoMessagePipe_Create, 1)             \
-  V(MojoMessagePipe_Write, 5)              \
-  V(MojoMessagePipe_Read, 5)               \
-  V(MojoHandle_Close, 1)                   \
-  V(MojoHandle_Wait, 3)                    \
-  V(MojoHandle_Register, 2)                \
-  V(MojoHandle_WaitMany, 3)                \
-  V(MojoHandleWatcher_GrowStateArrays, 1)  \
-  V(MojoHandleWatcher_WaitMany, 2)         \
-  V(MojoHandleWatcher_SendControlData, 4)  \
-  V(MojoHandleWatcher_RecvControlData, 1)  \
-  V(MojoHandleWatcher_SetControlHandle, 1) \
-  V(MojoHandleWatcher_GetControlHandle, 0)
-
-MOJO_NATIVE_LIST(DECLARE_FUNCTION);
-
-static struct NativeEntries {
-  const char* name;
-  Dart_NativeFunction function;
-  int argument_count;
-} MojoEntries[] = {MOJO_NATIVE_LIST(REGISTER_FUNCTION)};
-
-Dart_NativeFunction MojoNativeLookup(Dart_Handle name,
-                                     int argument_count,
-                                     bool* auto_setup_scope) {
-  const char* function_name = nullptr;
-  Dart_Handle result = Dart_StringToCString(name, &function_name);
-  DART_CHECK_VALID(result);
-  DCHECK(function_name != nullptr);
-  DCHECK(auto_setup_scope != nullptr);
-  *auto_setup_scope = true;
-  size_t num_entries = arraysize(MojoEntries);
-  for (size_t i = 0; i < num_entries; ++i) {
-    const struct NativeEntries& entry = MojoEntries[i];
-    if (!strcmp(function_name, entry.name) &&
-        (entry.argument_count == argument_count)) {
-      return entry.function;
-    }
-  }
-  return nullptr;
-}
-
-const uint8_t* MojoNativeSymbol(Dart_NativeFunction nf) {
-  size_t num_entries = arraysize(MojoEntries);
-  for (size_t i = 0; i < num_entries; ++i) {
-    const struct NativeEntries& entry = MojoEntries[i];
-    if (entry.function == nf) {
-      return reinterpret_cast<const uint8_t*>(entry.name);
-    }
-  }
-  return nullptr;
-}
-
-static void SetNullReturn(Dart_NativeArguments arguments) {
-  Dart_SetReturnValue(arguments, Dart_Null());
-}
-
-static void SetInvalidArgumentReturn(Dart_NativeArguments arguments) {
-  Dart_SetIntegerReturnValue(
-      arguments, static_cast<int64_t>(MOJO_RESULT_INVALID_ARGUMENT));
-}
-
-static Dart_Handle SignalsStateToDart(const MojoHandleSignalsState& state) {
-  Dart_Handle list = Dart_NewList(2);
-  Dart_Handle arg0 = Dart_NewInteger(state.satisfied_signals);
-  Dart_Handle arg1 = Dart_NewInteger(state.satisfiable_signals);
-  Dart_ListSetAt(list, 0, arg0);
-  Dart_ListSetAt(list, 1, arg1);
-  return list;
-}
-
-#define CHECK_INTEGER_ARGUMENT(args, num, result, failure)                     \
-  {                                                                            \
-    Dart_Handle __status;                                                      \
-    __status = Dart_GetNativeIntegerArgument(args, num, result);               \
-    if (Dart_IsError(__status)) {                                              \
-      Set##failure##Return(arguments);                                         \
-      return;                                                                  \
-    }                                                                          \
-  }                                                                            \
-
-struct CloserCallbackPeer {
-  MojoHandle handle;
-};
-
-static void MojoHandleCloserCallback(void* isolate_data,
-                                     Dart_WeakPersistentHandle handle,
-                                     void* peer) {
-  CloserCallbackPeer* callback_peer =
-      reinterpret_cast<CloserCallbackPeer*>(peer);
-  if (callback_peer->handle != MOJO_HANDLE_INVALID) {
-    MojoClose(callback_peer->handle);
-  }
-  delete callback_peer;
-}
-
-// Setup a weak persistent handle for a MojoHandle that calls MojoClose on the
-// handle when the MojoHandle is GC'd or the VM is going down.
-void MojoHandle_Register(Dart_NativeArguments arguments) {
-  Dart_Handle mojo_handle_instance = Dart_GetNativeArgument(arguments, 0);
-  if (!Dart_IsInstance(mojo_handle_instance)) {
-    SetInvalidArgumentReturn(arguments);
-    return;
-  }
-
-  int64_t raw_handle = static_cast<int64_t>(MOJO_HANDLE_INVALID);
-  CHECK_INTEGER_ARGUMENT(arguments, 1, &raw_handle, InvalidArgument);
-  if (raw_handle == static_cast<int64_t>(MOJO_HANDLE_INVALID)) {
-    SetInvalidArgumentReturn(arguments);
-    return;
-  }
-
-  // Set up a finalizer.
-  CloserCallbackPeer* callback_peer = new CloserCallbackPeer();
-  callback_peer->handle = static_cast<MojoHandle>(raw_handle);
-  Dart_NewWeakPersistentHandle(mojo_handle_instance,
-                               reinterpret_cast<void*>(callback_peer),
-                               sizeof(CloserCallbackPeer),
-                               MojoHandleCloserCallback);
-  Dart_SetIntegerReturnValue(arguments, static_cast<int64_t>(MOJO_RESULT_OK));
-}
-
-void MojoHandle_Close(Dart_NativeArguments arguments) {
-  int64_t handle;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &handle, InvalidArgument);
-
-  MojoResult res = MojoClose(static_cast<MojoHandle>(handle));
-
-  Dart_SetIntegerReturnValue(arguments, static_cast<int64_t>(res));
-}
-
-void MojoHandle_Wait(Dart_NativeArguments arguments) {
-  int64_t handle = 0;
-  int64_t signals = 0;
-  int64_t deadline = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &handle, InvalidArgument);
-  CHECK_INTEGER_ARGUMENT(arguments, 1, &signals, InvalidArgument);
-  CHECK_INTEGER_ARGUMENT(arguments, 2, &deadline, InvalidArgument);
-
-  MojoHandleSignalsState state;
-  MojoResult r = mojo::Wait(mojo::Handle(static_cast<MojoHandle>(handle)),
-                            static_cast<MojoHandleSignals>(signals),
-                            static_cast<MojoDeadline>(deadline), &state);
-
-  // The return value is structured as a list of length 2:
-  // [0] MojoResult
-  // [1] MojoHandleSignalsState. (may be null)
-  Dart_Handle list = Dart_NewList(2);
-  Dart_ListSetAt(list, 0, Dart_NewInteger(r));
-  if (mojo::WaitManyResult(r).AreSignalsStatesValid()) {
-    Dart_ListSetAt(list, 1, SignalsStateToDart(state));
-  } else {
-    Dart_ListSetAt(list, 1, Dart_Null());
-  }
-  Dart_SetReturnValue(arguments, list);
-}
-
-void MojoHandle_WaitMany(Dart_NativeArguments arguments) {
-  int64_t deadline = 0;
-  Dart_Handle handles = Dart_GetNativeArgument(arguments, 0);
-  Dart_Handle signals = Dart_GetNativeArgument(arguments, 1);
-  CHECK_INTEGER_ARGUMENT(arguments, 2, &deadline, InvalidArgument);
-
-  if (!Dart_IsList(handles) || !Dart_IsList(signals)) {
-    SetInvalidArgumentReturn(arguments);
-    return;
-  }
-
-  intptr_t handles_len = 0;
-  intptr_t signals_len = 0;
-  Dart_ListLength(handles, &handles_len);
-  Dart_ListLength(signals, &signals_len);
-  if (handles_len != signals_len) {
-    SetInvalidArgumentReturn(arguments);
-    return;
-  }
-
-  std::vector<mojo::Handle> mojo_handles(handles_len);
-  std::vector<MojoHandleSignals> mojo_signals(handles_len);
-
-  for (int i = 0; i < handles_len; i++) {
-    Dart_Handle dart_handle = Dart_ListGetAt(handles, i);
-    Dart_Handle dart_signal = Dart_ListGetAt(signals, i);
-    if (!Dart_IsInteger(dart_handle) || !Dart_IsInteger(dart_signal)) {
-      SetInvalidArgumentReturn(arguments);
-      return;
-    }
-    int64_t mojo_handle = 0;
-    int64_t mojo_signal = 0;
-    Dart_IntegerToInt64(dart_handle, &mojo_handle);
-    Dart_IntegerToInt64(dart_signal, &mojo_signal);
-    mojo_handles[i] = mojo::Handle(mojo_handle);
-    mojo_signals[i] = static_cast<MojoHandleSignals>(mojo_signal);
-  }
-
-  std::vector<MojoHandleSignalsState> states(handles_len);
-  mojo::WaitManyResult wmr = mojo::WaitMany(
-      mojo_handles, mojo_signals, static_cast<MojoDeadline>(deadline), &states);
-
-  // The return value is structured as a list of length 3:
-  // [0] MojoResult
-  // [1] index of handle that caused a return (may be null)
-  // [2] list of MojoHandleSignalsState. (may be null)
-  Dart_Handle list = Dart_NewList(3);
-  Dart_ListSetAt(list, 0, Dart_NewInteger(wmr.result));
-  if (wmr.IsIndexValid())
-    Dart_ListSetAt(list, 1, Dart_NewInteger(wmr.index));
-  else
-    Dart_ListSetAt(list, 1, Dart_Null());
-  if (wmr.AreSignalsStatesValid()) {
-    Dart_Handle stateList = Dart_NewList(handles_len);
-    for (int i = 0; i < handles_len; i++) {
-      Dart_ListSetAt(stateList, i, SignalsStateToDart(states[i]));
-    }
-    Dart_ListSetAt(list, 2, stateList);
-  } else {
-    Dart_ListSetAt(list, 2, Dart_Null());
-  }
-  Dart_SetReturnValue(arguments, list);
-}
-
-void MojoSharedBuffer_Create(Dart_NativeArguments arguments) {
-  int64_t num_bytes = 0;
-  int64_t flags = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &num_bytes, Null);
-  CHECK_INTEGER_ARGUMENT(arguments, 1, &flags, Null);
-
-  MojoCreateSharedBufferOptions options;
-  options.struct_size = sizeof(MojoCreateSharedBufferOptions);
-  options.flags = static_cast<MojoCreateSharedBufferOptionsFlags>(flags);
-
-  MojoHandle out = MOJO_HANDLE_INVALID;;
-  MojoResult res = MojoCreateSharedBuffer(
-      &options, static_cast<int32_t>(num_bytes), &out);
-
-  Dart_Handle list = Dart_NewList(2);
-  Dart_ListSetAt(list, 0, Dart_NewInteger(res));
-  Dart_ListSetAt(list, 1, Dart_NewInteger(out));
-  Dart_SetReturnValue(arguments, list);
-}
-
-void MojoSharedBuffer_Duplicate(Dart_NativeArguments arguments) {
-  int64_t handle = 0;
-  int64_t flags = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &handle, Null);
-  CHECK_INTEGER_ARGUMENT(arguments, 1, &flags, Null);
-
-  MojoDuplicateBufferHandleOptions options;
-  options.struct_size = sizeof(MojoDuplicateBufferHandleOptions);
-  options.flags = static_cast<MojoDuplicateBufferHandleOptionsFlags>(flags);
-
-  MojoHandle out = MOJO_HANDLE_INVALID;;
-  MojoResult res = MojoDuplicateBufferHandle(
-      static_cast<MojoHandle>(handle), &options, &out);
-
-  Dart_Handle list = Dart_NewList(2);
-  Dart_ListSetAt(list, 0, Dart_NewInteger(res));
-  Dart_ListSetAt(list, 1, Dart_NewInteger(out));
-  Dart_SetReturnValue(arguments, list);
-}
-
-static void MojoBufferUnmapCallback(void* isolate_data,
-                                    Dart_WeakPersistentHandle handle,
-                                    void* peer) {
-  MojoUnmapBuffer(peer);
-}
-
-void MojoSharedBuffer_Map(Dart_NativeArguments arguments) {
-  int64_t handle = 0;
-  int64_t offset = 0;
-  int64_t num_bytes = 0;
-  int64_t flags = 0;
-  Dart_Handle mojo_buffer = Dart_GetNativeArgument(arguments, 0);
-  CHECK_INTEGER_ARGUMENT(arguments, 1, &handle, Null);
-  CHECK_INTEGER_ARGUMENT(arguments, 2, &offset, Null);
-  CHECK_INTEGER_ARGUMENT(arguments, 3, &num_bytes, Null);
-  CHECK_INTEGER_ARGUMENT(arguments, 4, &flags, Null);
-
-  void* out;
-  MojoResult res = MojoMapBuffer(static_cast<MojoHandle>(handle),
-                                 offset,
-                                 num_bytes,
-                                 &out,
-                                 static_cast<MojoMapBufferFlags>(flags));
-
-  Dart_Handle list = Dart_NewList(2);
-  Dart_Handle typed_data;
-  if (res == MOJO_RESULT_OK) {
-    typed_data = Dart_NewExternalTypedData(
-        Dart_TypedData_kByteData, out, num_bytes);
-    Dart_NewWeakPersistentHandle(
-        mojo_buffer, out, num_bytes, MojoBufferUnmapCallback);
-  } else {
-    typed_data = Dart_Null();
-  }
-  Dart_ListSetAt(list, 0, Dart_NewInteger(res));
-  Dart_ListSetAt(list, 1, typed_data);
-  Dart_SetReturnValue(arguments, list);
-}
-
-void MojoSharedBuffer_Unmap(Dart_NativeArguments arguments) {
-  Dart_Handle typed_data = Dart_GetNativeArgument(arguments, 0);
-  if (Dart_GetTypeOfExternalTypedData(typed_data) == Dart_TypedData_kInvalid) {
-    SetInvalidArgumentReturn(arguments);
-    return;
-  }
-
-  Dart_TypedData_Type typ;
-  void *data;
-  intptr_t len;
-  Dart_TypedDataAcquireData(typed_data, &typ, &data, &len);
-  MojoResult res = MojoUnmapBuffer(data);
-  Dart_TypedDataReleaseData(typed_data);
-
-  Dart_SetIntegerReturnValue(arguments, static_cast<int64_t>(res));
-}
-
-void MojoDataPipe_Create(Dart_NativeArguments arguments) {
-  int64_t element_bytes = 0;
-  int64_t capacity_bytes = 0;
-  int64_t flags = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &element_bytes, Null);
-  CHECK_INTEGER_ARGUMENT(arguments, 1, &capacity_bytes, Null);
-  CHECK_INTEGER_ARGUMENT(arguments, 2, &flags, Null);
-
-  MojoCreateDataPipeOptions options;
-  options.struct_size = sizeof(MojoCreateDataPipeOptions);
-  options.flags = static_cast<MojoCreateDataPipeOptionsFlags>(flags);
-  options.element_num_bytes = static_cast<uint32_t>(element_bytes);
-  options.capacity_num_bytes = static_cast<uint32_t>(capacity_bytes);
-
-  MojoHandle producer = MOJO_HANDLE_INVALID;
-  MojoHandle consumer = MOJO_HANDLE_INVALID;
-  MojoResult res = MojoCreateDataPipe(&options, &producer, &consumer);
-
-  Dart_Handle list = Dart_NewList(3);
-  Dart_ListSetAt(list, 0, Dart_NewInteger(res));
-  Dart_ListSetAt(list, 1, Dart_NewInteger(producer));
-  Dart_ListSetAt(list, 2, Dart_NewInteger(consumer));
-  Dart_SetReturnValue(arguments, list);
-}
-
-void MojoDataPipe_WriteData(Dart_NativeArguments arguments) {
-  int64_t handle = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &handle, Null);
-
-  Dart_Handle typed_data = Dart_GetNativeArgument(arguments, 1);
-  if (!Dart_IsTypedData(typed_data)) {
-    SetNullReturn(arguments);
-    return;
-  }
-
-  int64_t num_bytes = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 2, &num_bytes, Null);
-  if (num_bytes <= 0) {
-    SetNullReturn(arguments);
-    return;
-  }
-
-  int64_t flags = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 3, &flags, Null);
-
-  Dart_TypedData_Type type;
-  void* data;
-  intptr_t data_length;
-  Dart_TypedDataAcquireData(typed_data, &type, &data, &data_length);
-  uint32_t length = static_cast<uint32_t>(num_bytes);
-  MojoResult res = MojoWriteData(
-      static_cast<MojoHandle>(handle),
-      data,
-      &length,
-      static_cast<MojoWriteDataFlags>(flags));
-  Dart_TypedDataReleaseData(typed_data);
-
-  Dart_Handle list = Dart_NewList(2);
-  Dart_ListSetAt(list, 0, Dart_NewInteger(res));
-  Dart_ListSetAt(list, 1, Dart_NewInteger(length));
-  Dart_SetReturnValue(arguments, list);
-}
-
-void MojoDataPipe_BeginWriteData(Dart_NativeArguments arguments) {
-  int64_t handle = 0;
-  int64_t buffer_bytes = 0;
-  int64_t flags = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &handle, Null);
-  CHECK_INTEGER_ARGUMENT(arguments, 1, &buffer_bytes, Null);
-  CHECK_INTEGER_ARGUMENT(arguments, 2, &flags, Null);
-
-  void* buffer;
-  uint32_t size = static_cast<uint32_t>(buffer_bytes);
-  MojoResult res = MojoBeginWriteData(
-      static_cast<MojoHandle>(handle),
-      &buffer,
-      &size,
-      static_cast<MojoWriteDataFlags>(flags));
-
-  Dart_Handle list = Dart_NewList(2);
-  Dart_Handle typed_data;
-  if (res == MOJO_RESULT_OK) {
-    typed_data = Dart_NewExternalTypedData(
-        Dart_TypedData_kByteData, buffer, size);
-  } else {
-    typed_data = Dart_Null();
-  }
-  Dart_ListSetAt(list, 0, Dart_NewInteger(res));
-  Dart_ListSetAt(list, 1, typed_data);
-  Dart_SetReturnValue(arguments, list);
-}
-
-void MojoDataPipe_EndWriteData(Dart_NativeArguments arguments) {
-  int64_t handle = 0;
-  int64_t num_bytes_written = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &handle, InvalidArgument);
-  CHECK_INTEGER_ARGUMENT(arguments, 1, &num_bytes_written, InvalidArgument);
-
-  MojoResult res = MojoEndWriteData(
-      static_cast<MojoHandle>(handle),
-      static_cast<uint32_t>(num_bytes_written));
-
-  Dart_SetIntegerReturnValue(arguments, static_cast<int64_t>(res));
-}
-
-void MojoDataPipe_ReadData(Dart_NativeArguments arguments) {
-  int64_t handle = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &handle, Null);
-
-  Dart_Handle typed_data = Dart_GetNativeArgument(arguments, 1);
-  if (!Dart_IsTypedData(typed_data) && !Dart_IsNull(typed_data)) {
-    SetNullReturn(arguments);
-    return;
-  }
-  // When querying the amount of data available to read from the pipe,
-  // null is passed in for typed_data.
-
-  int64_t num_bytes = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 2, &num_bytes, Null);
-
-  int64_t flags = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 3, &flags, Null);
-
-  Dart_TypedData_Type typ;
-  void* data = nullptr;
-  intptr_t bdlen = 0;
-  if (!Dart_IsNull(typed_data)) {
-    Dart_TypedDataAcquireData(typed_data, &typ, &data, &bdlen);
-  }
-  uint32_t len = static_cast<uint32_t>(num_bytes);
-  MojoResult res = MojoReadData(
-      static_cast<MojoHandle>(handle),
-      data,
-      &len,
-      static_cast<MojoReadDataFlags>(flags));
-  if (!Dart_IsNull(typed_data)) {
-    Dart_TypedDataReleaseData(typed_data);
-  }
-
-  Dart_Handle list = Dart_NewList(2);
-  Dart_ListSetAt(list, 0, Dart_NewInteger(res));
-  Dart_ListSetAt(list, 1, Dart_NewInteger(len));
-  Dart_SetReturnValue(arguments, list);
-}
-
-void MojoDataPipe_BeginReadData(Dart_NativeArguments arguments) {
-  int64_t handle = 0;
-  int64_t buffer_bytes = 0;
-  int64_t flags = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &handle, Null);
-  CHECK_INTEGER_ARGUMENT(arguments, 1, &buffer_bytes, Null);
-  CHECK_INTEGER_ARGUMENT(arguments, 2, &flags, Null);
-
-  void* buffer;
-  uint32_t size = static_cast<uint32_t>(buffer_bytes);
-  MojoResult res = MojoBeginReadData(
-      static_cast<MojoHandle>(handle),
-      const_cast<const void**>(&buffer),
-      &size,
-      static_cast<MojoWriteDataFlags>(flags));
-
-  Dart_Handle list = Dart_NewList(2);
-  Dart_Handle typed_data;
-  if (res == MOJO_RESULT_OK) {
-    typed_data = Dart_NewExternalTypedData(
-        Dart_TypedData_kByteData, buffer, size);
-  } else {
-    typed_data = Dart_Null();
-  }
-  Dart_ListSetAt(list, 0, Dart_NewInteger(res));
-  Dart_ListSetAt(list, 1, typed_data);
-  Dart_SetReturnValue(arguments, list);
-}
-
-void MojoDataPipe_EndReadData(Dart_NativeArguments arguments) {
-  int64_t handle = 0;
-  int64_t num_bytes_read = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &handle, InvalidArgument);
-  CHECK_INTEGER_ARGUMENT(arguments, 1, &num_bytes_read, InvalidArgument);
-
-  MojoResult res = MojoEndReadData(
-      static_cast<MojoHandle>(handle),
-      static_cast<uint32_t>(num_bytes_read));
-
-  Dart_SetIntegerReturnValue(arguments, static_cast<int64_t>(res));
-}
-
-void MojoMessagePipe_Create(Dart_NativeArguments arguments) {
-  int64_t flags = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &flags, Null);
-
-  MojoCreateMessagePipeOptions options;
-  options.struct_size = sizeof(MojoCreateMessagePipeOptions);
-  options.flags = static_cast<MojoCreateMessagePipeOptionsFlags>(flags);
-
-  MojoHandle end1 = MOJO_HANDLE_INVALID;
-  MojoHandle end2 = MOJO_HANDLE_INVALID;
-  MojoResult res = MojoCreateMessagePipe(&options, &end1, &end2);
-
-  Dart_Handle list = Dart_NewList(3);
-  Dart_ListSetAt(list, 0, Dart_NewInteger(res));
-  Dart_ListSetAt(list, 1, Dart_NewInteger(end1));
-  Dart_ListSetAt(list, 2, Dart_NewInteger(end2));
-  Dart_SetReturnValue(arguments, list);
-}
-
-void MojoMessagePipe_Write(Dart_NativeArguments arguments) {
-  int64_t handle = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &handle, InvalidArgument);
-
-  Dart_Handle typed_data = Dart_GetNativeArgument(arguments, 1);
-  if (!Dart_IsTypedData(typed_data) && !Dart_IsNull(typed_data)) {
-    SetInvalidArgumentReturn(arguments);
-    return;
-  }
-
-  int64_t num_bytes = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 2, &num_bytes, InvalidArgument);
-  if ((Dart_IsNull(typed_data) && (num_bytes != 0)) ||
-      (!Dart_IsNull(typed_data) && (num_bytes <= 0))) {
-    SetInvalidArgumentReturn(arguments);
-    return;
-  }
-
-  Dart_Handle handles = Dart_GetNativeArgument(arguments, 3);
-  if (!Dart_IsList(handles) && !Dart_IsNull(handles)) {
-    SetInvalidArgumentReturn(arguments);
-    return;
-  }
-
-  int64_t flags = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 4, &flags, InvalidArgument);
-
-  // Grab the data if there is any.
-  Dart_TypedData_Type typ;
-  void* bytes = nullptr;
-  intptr_t bdlen = 0;
-  if (!Dart_IsNull(typed_data)) {
-    Dart_TypedDataAcquireData(typed_data, &typ, &bytes, &bdlen);
-  }
-
-  // Grab the handles if there are any.
-  scoped_ptr<MojoHandle[]> mojo_handles;
-  intptr_t handles_len = 0;
-  if (!Dart_IsNull(handles)) {
-    Dart_ListLength(handles, &handles_len);
-    if (handles_len > 0) {
-      mojo_handles.reset(new MojoHandle[handles_len]);
-    }
-    for (int i = 0; i < handles_len; i++) {
-      Dart_Handle dart_handle = Dart_ListGetAt(handles, i);
-      if (!Dart_IsInteger(dart_handle)) {
-        SetInvalidArgumentReturn(arguments);
-        return;
-      }
-      int64_t mojo_handle = 0;
-      Dart_IntegerToInt64(dart_handle, &mojo_handle);
-      mojo_handles[i] = static_cast<MojoHandle>(mojo_handle);
-    }
-  }
-
-  MojoResult res = MojoWriteMessage(
-      static_cast<MojoHandle>(handle),
-      const_cast<const void*>(bytes),
-      static_cast<uint32_t>(num_bytes),
-      mojo_handles.get(),
-      static_cast<uint32_t>(handles_len),
-      static_cast<MojoWriteMessageFlags>(flags));
-
-  // Release the data.
-  if (!Dart_IsNull(typed_data)) {
-    Dart_TypedDataReleaseData(typed_data);
-  }
-
-  Dart_SetIntegerReturnValue(arguments, static_cast<int64_t>(res));
-}
-
-void MojoMessagePipe_Read(Dart_NativeArguments arguments) {
-  int64_t handle = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &handle, Null);
-
-  Dart_Handle typed_data = Dart_GetNativeArgument(arguments, 1);
-  if (!Dart_IsTypedData(typed_data) && !Dart_IsNull(typed_data)) {
-    SetNullReturn(arguments);
-    return;
-  }
-  // When querying the amount of data available to read from the pipe,
-  // null is passed in for typed_data.
-
-  int64_t num_bytes = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 2, &num_bytes, Null);
-  if ((Dart_IsNull(typed_data) && (num_bytes != 0)) ||
-      (!Dart_IsNull(typed_data) && (num_bytes <= 0))) {
-    SetNullReturn(arguments);
-    return;
-  }
-
-  Dart_Handle handles = Dart_GetNativeArgument(arguments, 3);
-  if (!Dart_IsList(handles) && !Dart_IsNull(handles)) {
-    SetNullReturn(arguments);
-    return;
-  }
-
-  int64_t flags = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 4, &flags, Null);
-
-  // Grab the data if there is any.
-  Dart_TypedData_Type typ;
-  void* bytes = nullptr;
-  intptr_t byte_data_len = 0;
-  if (!Dart_IsNull(typed_data)) {
-    Dart_TypedDataAcquireData(typed_data, &typ, &bytes, &byte_data_len);
-  }
-  uint32_t blen = static_cast<uint32_t>(num_bytes);
-
-  // Grab the handles if there are any.
-  scoped_ptr<MojoHandle[]> mojo_handles;
-  intptr_t handles_len = 0;
-  if (!Dart_IsNull(handles)) {
-    Dart_ListLength(handles, &handles_len);
-    mojo_handles.reset(new MojoHandle[handles_len]);
-  }
-  uint32_t hlen = static_cast<uint32_t>(handles_len);
-
-  MojoResult res = MojoReadMessage(
-      static_cast<MojoHandle>(handle),
-      bytes,
-      &blen,
-      mojo_handles.get(),
-      &hlen,
-      static_cast<MojoReadMessageFlags>(flags));
-
-  // Release the data.
-  if (!Dart_IsNull(typed_data)) {
-    Dart_TypedDataReleaseData(typed_data);
-  }
-
-  if (!Dart_IsNull(handles)) {
-    for (int i = 0; i < handles_len; i++) {
-      Dart_ListSetAt(handles, i, Dart_NewInteger(mojo_handles[i]));
-    }
-  }
-
-  Dart_Handle list = Dart_NewList(3);
-  Dart_ListSetAt(list, 0, Dart_NewInteger(res));
-  Dart_ListSetAt(list, 1, Dart_NewInteger(blen));
-  Dart_ListSetAt(list, 2, Dart_NewInteger(hlen));
-  Dart_SetReturnValue(arguments, list);
-}
-
-struct MojoWaitManyState {
-  MojoWaitManyState() {}
-
-  std::vector<uint32_t> handles;
-  std::vector<uint32_t> signals;
-  std::vector<uint32_t> out_index;
-  std::vector<MojoHandleSignalsState> out_signals;
-
-  static MojoWaitManyState* GetInstance();
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MojoWaitManyState);
-};
-
-// This global is safe because it is only accessed by the single handle watcher
-// isolate. If multiple handle watcher isolates are ever needed, it will need
-// to be replicated.
-MojoWaitManyState* MojoWaitManyState::GetInstance() {
-  static MojoWaitManyState* state = new MojoWaitManyState;
-  return state;
-}
-
-void MojoHandleWatcher_GrowStateArrays(Dart_NativeArguments arguments) {
-  int64_t new_length;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &new_length, InvalidArgument);
-
-  MojoWaitManyState& handle_watcher_wait_state =
-      *MojoWaitManyState::GetInstance();
-
-  handle_watcher_wait_state.handles.resize(new_length);
-  handle_watcher_wait_state.signals.resize(new_length);
-  handle_watcher_wait_state.out_index.resize(1);
-  handle_watcher_wait_state.out_signals.resize(new_length);
-
-  Dart_Handle dart_handles = Dart_NewExternalTypedData(
-      Dart_TypedData_kUint32, handle_watcher_wait_state.handles.data(),
-      handle_watcher_wait_state.handles.size());
-  if (Dart_IsError(dart_handles)) {
-    Dart_PropagateError(dart_handles);
-  }
-  if (Dart_IsNull(dart_handles)) {
-    SetNullReturn(arguments);
-    return;
-  }
-
-  Dart_Handle dart_signals = Dart_NewExternalTypedData(
-      Dart_TypedData_kUint32, handle_watcher_wait_state.signals.data(),
-      handle_watcher_wait_state.signals.size());
-  if (Dart_IsError(dart_signals)) {
-    Dart_PropagateError(dart_signals);
-  }
-  if (Dart_IsNull(dart_signals)) {
-    SetNullReturn(arguments);
-    return;
-  }
-
-  Dart_Handle dart_out_index = Dart_NewExternalTypedData(
-      Dart_TypedData_kUint32, handle_watcher_wait_state.out_index.data(),
-      handle_watcher_wait_state.out_index.size());
-  if (Dart_IsError(dart_out_index)) {
-    Dart_PropagateError(dart_out_index);
-  }
-  if (Dart_IsNull(dart_out_index)) {
-    SetNullReturn(arguments);
-    return;
-  }
-
-  Dart_Handle dart_out_signals = Dart_NewExternalTypedData(
-      Dart_TypedData_kUint64, handle_watcher_wait_state.out_signals.data(),
-      handle_watcher_wait_state.out_signals.size());
-  if (Dart_IsError(dart_out_signals)) {
-    Dart_PropagateError(dart_out_signals);
-  }
-  if (Dart_IsNull(dart_out_signals)) {
-    SetNullReturn(arguments);
-    return;
-  }
-
-  Dart_Handle list = Dart_NewList(4);
-  Dart_ListSetAt(list, 0, dart_handles);
-  Dart_ListSetAt(list, 1, dart_signals);
-  Dart_ListSetAt(list, 2, dart_out_index);
-  Dart_ListSetAt(list, 3, dart_out_signals);
-  Dart_SetReturnValue(arguments, list);
-}
-
-void MojoHandleWatcher_WaitMany(Dart_NativeArguments arguments) {
-  int64_t handles_len = 0;
-  int64_t deadline = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &handles_len, InvalidArgument);
-  CHECK_INTEGER_ARGUMENT(arguments, 1, &deadline, InvalidArgument);
-
-  MojoWaitManyState& handle_watcher_wait_state =
-      *MojoWaitManyState::GetInstance();
-
-  uint32_t* handles = handle_watcher_wait_state.handles.data();
-  uint32_t* signals = handle_watcher_wait_state.signals.data();
-  uint32_t* out_index = handle_watcher_wait_state.out_index.data();
-  MojoHandleSignalsState* out_signals =
-      handle_watcher_wait_state.out_signals.data();
-
-  Dart_IsolateBlocked();
-  MojoResult mojo_result = MojoWaitMany(handles, signals, handles_len, deadline,
-                                        out_index, out_signals);
-  Dart_IsolateUnblocked();
-
-  Dart_SetIntegerReturnValue(arguments, static_cast<int64_t>(mojo_result));
-}
-
-struct ControlData {
-  int64_t handle;
-  Dart_Port port;
-  int64_t data;
-};
-
-void MojoHandleWatcher_SendControlData(Dart_NativeArguments arguments) {
-  int64_t control_handle = 0;
-  int64_t client_handle = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &control_handle, InvalidArgument);
-  CHECK_INTEGER_ARGUMENT(arguments, 1, &client_handle, InvalidArgument);
-
-  Dart_Handle send_port_handle = Dart_GetNativeArgument(arguments, 2);
-  Dart_Port send_port_id = ILLEGAL_PORT;
-  if (!Dart_IsNull(send_port_handle)) {
-    Dart_Handle result = Dart_SendPortGetId(send_port_handle, &send_port_id);
-    if (Dart_IsError(result)) {
-      SetInvalidArgumentReturn(arguments);
-      return;
-    }
-  }
-
-  int64_t data = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 3, &data, InvalidArgument);
-
-  ControlData cd;
-  cd.handle = client_handle;
-  cd.port = send_port_id;
-  cd.data = data;
-  const void* bytes = reinterpret_cast<const void*>(&cd);
-  MojoResult res = MojoWriteMessage(
-      control_handle, bytes, sizeof(cd), nullptr, 0, 0);
-  Dart_SetIntegerReturnValue(arguments, static_cast<int64_t>(res));
-}
-
-void MojoHandleWatcher_RecvControlData(Dart_NativeArguments arguments) {
-  int64_t control_handle = 0;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &control_handle, Null);
-
-  ControlData cd;
-  void* bytes = reinterpret_cast<void*>(&cd);
-  uint32_t num_bytes = sizeof(cd);
-  uint32_t num_handles = 0;
-  MojoResult res = MojoReadMessage(
-      control_handle, bytes, &num_bytes, nullptr, &num_handles, 0);
-  if (res != MOJO_RESULT_OK) {
-    SetNullReturn(arguments);
-    return;
-  }
-
-  Dart_Handle list = Dart_NewList(3);
-  Dart_ListSetAt(list, 0, Dart_NewInteger(cd.handle));
-  if (cd.port != ILLEGAL_PORT) {
-    Dart_ListSetAt(list, 1, Dart_NewSendPort(cd.port));
-  }
-  Dart_ListSetAt(list, 2, Dart_NewInteger(cd.data));
-  Dart_SetReturnValue(arguments, list);
-}
-
-static int64_t mojo_control_handle = MOJO_HANDLE_INVALID;
-void MojoHandleWatcher_SetControlHandle(Dart_NativeArguments arguments) {
-  int64_t control_handle;
-  CHECK_INTEGER_ARGUMENT(arguments, 0, &control_handle, InvalidArgument);
-  mojo_control_handle = control_handle;
-  Dart_SetIntegerReturnValue(arguments, static_cast<int64_t>(MOJO_RESULT_OK));
-}
-
-void MojoHandleWatcher_GetControlHandle(Dart_NativeArguments arguments) {
-  Dart_SetIntegerReturnValue(arguments, mojo_control_handle);
-}
-
-}  // namespace blink
diff --git a/sky/engine/bindings/mojo_natives.h b/sky/engine/bindings/mojo_natives.h
deleted file mode 100644
index e5b1405..0000000
--- a/sky/engine/bindings/mojo_natives.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SKY_ENGINE_BINDINGS_MOJO_NATIVES_H_
-#define SKY_ENGINE_BINDINGS_MOJO_NATIVES_H_
-
-#include "dart/runtime/include/dart_api.h"
-
-namespace blink {
-
-Dart_NativeFunction MojoNativeLookup(Dart_Handle name,
-                                     int argument_count,
-                                     bool* auto_setup_scope);
-
-const uint8_t* MojoNativeSymbol(Dart_NativeFunction nf);
-
-}  // namespace blink
-
-#endif  // SKY_ENGINE_BINDINGS_MOJO_NATIVES_H_
diff --git a/sky/engine/bindings/snapshot.cc.tmpl b/sky/engine/bindings/snapshot.c.tmpl
similarity index 66%
rename from sky/engine/bindings/snapshot.cc.tmpl
rename to sky/engine/bindings/snapshot.c.tmpl
index 1f20a68..f36ed9f 100644
--- a/sky/engine/bindings/snapshot.cc.tmpl
+++ b/sky/engine/bindings/snapshot.c.tmpl
@@ -6,22 +6,16 @@
 #include <stdint.h>
 #include <stddef.h>
 
-namespace blink {
-
 // The string on the next line will be filled in with the contents of the
 // generated snapshot binary file for the vm isolate.
 // This string forms the content of the dart vm isolate snapshot which
 // is loaded into the vm isolate.
-static const uint8_t vm_isolate_snapshot_buffer_[]
-    __attribute__((aligned(8))) = { % s};
-const uint8_t* kDartVmIsolateSnapshotBuffer = vm_isolate_snapshot_buffer_;
+const uint8_t kDartVmIsolateSnapshotBuffer[]
+    __attribute__((visibility("default"), aligned(8), used)) = { %s };
 
 // The string on the next line will be filled in with the contents of the
 // generated snapshot binary file for a regular dart isolate.
 // This string forms the content of a regular dart isolate snapshot which
 // is loaded into an isolate when it is created.
-static const uint8_t isolate_snapshot_buffer_[]
-    __attribute__((aligned(8))) = { % s};
-const uint8_t* kDartIsolateSnapshotBuffer = isolate_snapshot_buffer_;
-
-}  // namespace blink
+const uint8_t kDartIsolateSnapshotBuffer[]
+    __attribute__((visibility("default"), aligned(8), used)) = { %s };
diff --git a/sky/engine/core/core.gni b/sky/engine/core/core.gni
index bc409ee..47c76ca 100644
--- a/sky/engine/core/core.gni
+++ b/sky/engine/core/core.gni
@@ -244,8 +244,8 @@
   "text/Paragraph.h",
   "text/ParagraphBuilder.cpp",
   "text/ParagraphBuilder.h",
-  "view/Tracing.cpp",
-  "view/Tracing.h",
+  "tracing/tracing.cc",
+  "tracing/tracing.h",
   "window/window.cc",
   "window/window.h",
 ]
@@ -275,7 +275,6 @@
                                  "painting/Shader.idl",
                                  "text/Paragraph.idl",
                                  "text/ParagraphBuilder.idl",
-                                 "view/Tracing.idl",
                                ],
                                "abspath")
 
@@ -285,6 +284,7 @@
                                   "dart/lerp.dart",
                                   "dart/painting.dart",
                                   "dart/text.dart",
+                                  "dart/tracing.dart",
                                   "dart/window.dart",
                                   "painting/Color.dart",
                                   "painting/ColorFilter.dart",
diff --git a/sky/engine/core/dart/tracing.dart b/sky/engine/core/dart/tracing.dart
new file mode 100644
index 0000000..983c79c
--- /dev/null
+++ b/sky/engine/core/dart/tracing.dart
@@ -0,0 +1,12 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+part of dart_ui;
+
+class _Tracing {
+  void begin(String name) native "Tracing_begin";
+  void end(String name) native "Tracing_end";
+}
+
+final _Tracing tracing = new _Tracing();
diff --git a/sky/engine/core/dart/window.dart b/sky/engine/core/dart/window.dart
index b6e52d4..5d42f1a 100644
--- a/sky/engine/core/dart/window.dart
+++ b/sky/engine/core/dart/window.dart
@@ -38,4 +38,3 @@
 }
 
 final Window window = new Window._();
-final Tracing tracing = new Tracing();
diff --git a/sky/engine/core/painting/Paint.cpp b/sky/engine/core/painting/Paint.cpp
index 8468c27..f518e3f 100644
--- a/sky/engine/core/painting/Paint.cpp
+++ b/sky/engine/core/painting/Paint.cpp
@@ -51,6 +51,16 @@
   Dart_Handle value_handle = DOMDartState::Current()->value_handle();
   Dart_Handle data = Dart_GetField(dart_paint, value_handle);
 
+  if (Dart_IsInteger(data)) {
+    // This is a simple Paint object that just contains a color with
+    // anti-aliasing enabled. The data is the color, represented as an
+    // int in the same format as SkColor.
+    result.sk_paint.setColor(DartConverter<SkColor>::FromDart(data));
+    result.sk_paint.setAntiAlias(true);
+    result.is_null = false;
+    return result;
+  }
+
   DCHECK(Dart_IsList(data));
 
   intptr_t length;
diff --git a/sky/engine/core/painting/Paint.dart b/sky/engine/core/painting/Paint.dart
index ba91a1f..9c2ba52 100644
--- a/sky/engine/core/painting/Paint.dart
+++ b/sky/engine/core/painting/Paint.dart
@@ -29,7 +29,23 @@
   StrokeCap strokeCap;
 
   // Must match PaintFields enum in Paint.cpp.
-  List<dynamic> get _value {
+  dynamic get _value {
+    // The most common usage is a Paint with no options besides a color and
+    // anti-aliasing.  In this case, save time by just returning the color
+    // as an int.
+    if (strokeWidth == null &&
+        isAntiAlias &&
+        colorFilter == null &&
+        drawLooper == null &&
+        filterQuality == null &&
+        maskFilter == null &&
+        shader == null &&
+        style == null &&
+        transferMode == null &&
+        strokeCap == null) {
+      return color.value;
+    }
+
     return [
       strokeWidth,
       isAntiAlias,
diff --git a/sky/engine/core/script/dart_controller.cc b/sky/engine/core/script/dart_controller.cc
index 5048a3c..0835697 100644
--- a/sky/engine/core/script/dart_controller.cc
+++ b/sky/engine/core/script/dart_controller.cc
@@ -137,11 +137,11 @@
 }
 
 void DartController::CreateIsolateFor(std::unique_ptr<DOMDartState> state) {
-  CHECK(kDartIsolateSnapshotBuffer);
   char* error = nullptr;
   dom_dart_state_ = std::move(state);
   Dart_Isolate isolate = Dart_CreateIsolate(
-      dom_dart_state_->url().utf8().data(), "main", kDartIsolateSnapshotBuffer,
+      dom_dart_state_->url().utf8().data(), "main",
+      reinterpret_cast<uint8_t*>(DART_SYMBOL(kDartIsolateSnapshotBuffer)),
       nullptr, static_cast<DartState*>(dom_dart_state_.get()), &error);
   Dart_SetMessageNotifyCallback(MessageNotifyCallback);
   CHECK(isolate) << error;
diff --git a/sky/engine/core/script/dart_init.cc b/sky/engine/core/script/dart_init.cc
index e092fd4..8a961f6 100644
--- a/sky/engine/core/script/dart_init.cc
+++ b/sky/engine/core/script/dart_init.cc
@@ -48,17 +48,12 @@
   // during shutdown.
   Dart_Handle mojo_core_lib = Dart_LookupLibrary(ToDart("dart:mojo.internal"));
   CHECK(!LogIfError((mojo_core_lib)));
-  Dart_Handle handle_watcher_type = Dart_GetType(
-      mojo_core_lib,
-      Dart_NewStringFromCString("MojoHandleWatcher"),
-      0,
-      nullptr);
+  Dart_Handle handle_watcher_type =
+      Dart_GetType(mojo_core_lib,
+                   Dart_NewStringFromCString("MojoHandleWatcher"), 0, nullptr);
   CHECK(!LogIfError(handle_watcher_type));
   CHECK(!LogIfError(Dart_Invoke(
-      handle_watcher_type,
-      Dart_NewStringFromCString("_start"),
-      0,
-      nullptr)));
+      handle_watcher_type, Dart_NewStringFromCString("_start"), 0, nullptr)));
 
   // RunLoop until the handle watcher isolate is spun-up.
   CHECK(!LogIfError(Dart_RunLoop()));
@@ -80,23 +75,22 @@
     // default profile period to 100Hz. This number is suitable for older
     // Raspberry Pi devices but quite low for current smartphones.
     "--profile_period=1000",
-#if (WTF_OS_IOS || WTF_OS_MACOSX) && !defined(NDEBUG)
+#if (WTF_OS_IOS || WTF_OS_MACOSX)
     // On platforms where LLDB is the primary debugger, SIGPROF signals
-    // overwhelm LLDB. Since in debug builds, CPU profiling information is
-    // less useful anyway, vm profiling is disabled to enable a usable debugger.
+    // overwhelm LLDB.
     "--no-profile",
 #endif
 };
 
-static const char* kDartPrecompilationArgs[] {
-  "--precompilation",
+static const char* kDartPrecompilationArgs[]{
+    "--precompilation",
 };
 
 static const char* kDartCheckedModeArgs[] = {
-  "--enable_asserts",
-  "--enable_type_checks",
-  "--error_on_bad_type",
-  "--error_on_bad_override",
+    "--enable_asserts",
+    "--enable_type_checks",
+    "--error_on_bad_type",
+    "--error_on_bad_override",
 };
 
 void UnhandledExceptionCallback(Dart_Handle error) {
@@ -109,21 +103,7 @@
 
 bool IsServiceIsolateURL(const char* url_name) {
   return url_name != nullptr &&
-      String(url_name) == DART_VM_SERVICE_ISOLATE_NAME;
-}
-
-static const uint8_t* PrecompiledInstructionsSymbolIfPresent() {
-  dlerror();  // clear previous errors on thread
-  void* sym = nullptr;
-#ifdef RTLD_SELF
-  static const char kInstructionsSnapshotSymbolName[] = "kInstructionsSnapshot";
-  sym = dlsym(RTLD_SELF, kInstructionsSnapshotSymbolName);
-#endif
-  return (dlerror() != nullptr) ? nullptr : reinterpret_cast<uint8_t * >(sym);
-}
-
-static bool IsRunningPrecompiledCode() {
-  return PrecompiledInstructionsSymbolIfPresent() != nullptr;
+         String(url_name) == DART_VM_SERVICE_ISOLATE_NAME;
 }
 
 // TODO(rafaelw): Right now this only supports the creation of the handle
@@ -137,11 +117,11 @@
                                    void* callback_data,
                                    char** error) {
   if (IsServiceIsolateURL(script_uri)) {
-    CHECK(kDartIsolateSnapshotBuffer);
     DartState* dart_state = new DartState();
-    Dart_Isolate isolate =
-        Dart_CreateIsolate(script_uri, "main", kDartIsolateSnapshotBuffer,
-                           nullptr, nullptr, error);
+    Dart_Isolate isolate = Dart_CreateIsolate(
+        script_uri, "main", reinterpret_cast<const uint8_t*>(
+                                DART_SYMBOL(kDartIsolateSnapshotBuffer)),
+        nullptr, nullptr, error);
     CHECK(isolate) << error;
     dart_state->SetIsolate(isolate);
     CHECK(Dart_IsServiceIsolate(isolate));
@@ -158,9 +138,8 @@
       if (RuntimeEnabledFeatures::observatoryEnabled()) {
         std::string ip = "127.0.0.1";
         const intptr_t port = 8181;
-        const bool service_isolate_booted =
-            DartServiceIsolate::Startup(ip, port, DartLibraryTagHandler,
-                                        IsRunningPrecompiledCode(), error);
+        const bool service_isolate_booted = DartServiceIsolate::Startup(
+            ip, port, DartLibraryTagHandler, IsRunningPrecompiledCode(), error);
         CHECK(service_isolate_booted) << error;
       }
     }
@@ -169,12 +148,12 @@
   }
 
   // Create & start the handle watcher isolate
-  CHECK(kDartIsolateSnapshotBuffer);
   // TODO(abarth): Who deletes this DartState instance?
   DartState* dart_state = new DartState();
-  Dart_Isolate isolate =
-      Dart_CreateIsolate("sky:handle_watcher", "", kDartIsolateSnapshotBuffer,
-                         nullptr, dart_state, error);
+  Dart_Isolate isolate = Dart_CreateIsolate(
+      "sky:handle_watcher", "",
+      reinterpret_cast<uint8_t*>(DART_SYMBOL(kDartIsolateSnapshotBuffer)),
+      nullptr, dart_state, error);
   CHECK(isolate) << error;
   dart_state->SetIsolate(isolate);
 
@@ -196,7 +175,69 @@
   return isolate;
 }
 
-} // namespace
+}  // namespace
+
+#if DART_ALLOW_DYNAMIC_RESOLUTION
+
+const char* kDartVmIsolateSnapshotBufferName = "kDartVmIsolateSnapshotBuffer";
+const char* kDartIsolateSnapshotBufferName = "kDartIsolateSnapshotBuffer";
+const char* kInstructionsSnapshotName = "kInstructionsSnapshot";
+
+const char* kDartApplicationLibraryPath =
+    "FlutterApplication.framework/FlutterApplication";
+
+static void* DartLookupSymbolInLibrary(const char* symbol_name,
+                                       const char* library) {
+  if (symbol_name == nullptr) {
+    return nullptr;
+  }
+  dlerror();  // clear previous errors on thread
+  void* library_handle = dlopen(library, RTLD_NOW);
+  if (dlerror() != nullptr) {
+    return nullptr;
+  }
+  void* sym = dlsym(library_handle, symbol_name);
+  return dlerror() != nullptr ? nullptr : sym;
+}
+
+void* _DartSymbolLookup(const char* symbol_name) {
+  if (symbol_name == nullptr) {
+    return nullptr;
+  }
+
+  // First the application library is checked for the valid symbols. This
+  // library may not necessarily exist. If it does exist, it is loaded and the
+  // symbols resolved. Once the application library is loaded, there is
+  // currently no provision to unload the same.
+  void* symbol =
+      DartLookupSymbolInLibrary(symbol_name, kDartApplicationLibraryPath);
+  if (symbol != nullptr) {
+    return symbol;
+  }
+
+  // Check inside the default library
+  return DartLookupSymbolInLibrary(symbol_name, nullptr);
+}
+
+static const uint8_t* PrecompiledInstructionsSymbolIfPresent() {
+  return reinterpret_cast<uint8_t*>(DART_SYMBOL(kInstructionsSnapshot));
+}
+
+bool IsRunningPrecompiledCode() {
+  return PrecompiledInstructionsSymbolIfPresent() != nullptr;
+}
+
+#else  // DART_ALLOW_DYNAMIC_RESOLUTION
+
+static const uint8_t* PrecompiledInstructionsSymbolIfPresent() {
+  return nullptr;
+}
+
+bool IsRunningPrecompiledCode() {
+  return false;
+}
+
+#endif  // DART_ALLOW_DYNAMIC_RESOLUTION
 
 void InitDartVM() {
   dart::bin::BootstrapDartIo();
@@ -218,21 +259,21 @@
   CHECK(Dart_SetVMFlags(args.size(), args.data()));
   // This should be called before calling Dart_Initialize.
   DartDebugger::InitDebugger();
-  CHECK(Dart_Initialize(
-      kDartVmIsolateSnapshotBuffer,
-      PrecompiledInstructionsSymbolIfPresent(),
-      IsolateCreateCallback,
-      nullptr,  // Isolate interrupt callback.
-      UnhandledExceptionCallback, IsolateShutdownCallback,
-      // File IO callbacks.
-      nullptr, nullptr, nullptr, nullptr,
-      // Entroy source
-      nullptr,
-      // VM service assets archive
-      nullptr) == nullptr);
+  CHECK(
+      Dart_Initialize(
+          reinterpret_cast<uint8_t*>(DART_SYMBOL(kDartVmIsolateSnapshotBuffer)),
+          PrecompiledInstructionsSymbolIfPresent(), IsolateCreateCallback,
+          nullptr,  // Isolate interrupt callback.
+          UnhandledExceptionCallback, IsolateShutdownCallback,
+          // File IO callbacks.
+          nullptr, nullptr, nullptr, nullptr,
+          // Entroy source
+          nullptr,
+          // VM service assets archive
+          nullptr) == nullptr);
   // Wait for load port- ensures handle watcher and service isolates are
   // running.
   Dart_ServiceWaitForLoadPort();
 }
 
-} // namespace blink
+}  // namespace blink
diff --git a/sky/engine/core/script/dart_init.h b/sky/engine/core/script/dart_init.h
index 55203fb..8349eaa 100644
--- a/sky/engine/core/script/dart_init.h
+++ b/sky/engine/core/script/dart_init.h
@@ -6,18 +6,40 @@
 #define SKY_ENGINE_CORE_SCRIPT_DART_INIT_H_
 
 #include "dart/runtime/include/dart_api.h"
+#include "sky/engine/wtf/OperatingSystem.h"
 
 namespace blink {
 
-extern const uint8_t* kDartVmIsolateSnapshotBuffer;
-extern const uint8_t* kDartIsolateSnapshotBuffer;
+#define DART_ALLOW_DYNAMIC_RESOLUTION (WTF_OS_IOS || WTF_OS_MACOSX)
+
+#if DART_ALLOW_DYNAMIC_RESOLUTION
+
+extern const char* kDartVmIsolateSnapshotBufferName;
+extern const char* kDartIsolateSnapshotBufferName;
+extern const char* kInstructionsSnapshotName;
+
+void* _DartSymbolLookup(const char* symbol_name);
+
+#define DART_SYMBOL(symbol) _DartSymbolLookup(symbol##Name)
+
+#else  // DART_ALLOW_DYNAMIC_RESOLUTION
+
+extern "C" {
+extern void* kDartVmIsolateSnapshotBuffer;
+extern void* kDartIsolateSnapshotBuffer;
+}
+
+#define DART_SYMBOL(symbol) (&symbol)
+
+#endif  // DART_ALLOW_DYNAMIC_RESOLUTION
+
+bool IsRunningPrecompiledCode();
 
 void InitDartVM();
 Dart_Handle DartLibraryTagHandler(Dart_LibraryTag tag,
                                   Dart_Handle library,
                                   Dart_Handle url);
 void EnsureHandleWatcherStarted();
+}  // namespace blink
 
-}
-
-#endif // SKY_ENGINE_CORE_SCRIPT_DART_INIT_H_
+#endif  // SKY_ENGINE_CORE_SCRIPT_DART_INIT_H_
diff --git a/sky/engine/core/tracing/tracing.cc b/sky/engine/core/tracing/tracing.cc
new file mode 100644
index 0000000..0e2da07
--- /dev/null
+++ b/sky/engine/core/tracing/tracing.cc
@@ -0,0 +1,52 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sky/engine/core/tracing/tracing.h"
+
+#include "base/trace_event/trace_event.h"
+#include "sky/engine/tonic/dart_converter.h"
+#include "sky/engine/wtf/text/StringUTF8Adaptor.h"
+
+namespace blink {
+
+namespace {
+
+void BeginTracing(Dart_NativeArguments args) {
+  Dart_Handle exception = nullptr;
+  String name = DartConverter<String>::FromArguments(args, 1, exception);
+  if (exception) {
+    Dart_ThrowException(exception);
+    return;
+  }
+
+  StringUTF8Adaptor utf8(name);
+  // TRACE_EVENT_COPY_BEGIN0 needs a c-style null-terminated string.
+  CString cstring(utf8.data(), utf8.length());
+  TRACE_EVENT_COPY_BEGIN0("script", cstring.data());
+}
+
+void EndTracing(Dart_NativeArguments args) {
+  Dart_Handle exception = nullptr;
+  String name = DartConverter<String>::FromArguments(args, 1, exception);
+  if (exception) {
+    Dart_ThrowException(exception);
+    return;
+  }
+
+  StringUTF8Adaptor utf8(name);
+  // TRACE_EVENT_COPY_END0 needs a c-style null-terminated string.
+  CString cstring(utf8.data(), utf8.length());
+  TRACE_EVENT_COPY_END0("script", cstring.data());
+}
+
+}  // namespace
+
+void Tracing::RegisterNatives(DartLibraryNatives* natives) {
+  natives->Register({
+    { "Tracing_begin", BeginTracing, 2, true },
+    { "Tracing_end", EndTracing, 2, true },
+  });
+}
+
+}  // namespace blink
diff --git a/sky/engine/core/tracing/tracing.h b/sky/engine/core/tracing/tracing.h
new file mode 100644
index 0000000..b50db9f
--- /dev/null
+++ b/sky/engine/core/tracing/tracing.h
@@ -0,0 +1,19 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKY_ENGINE_CORE_TRACING_TRACING_H_
+#define SKY_ENGINE_CORE_TRACING_TRACING_H_
+
+#include "sky/engine/tonic/dart_library_natives.h"
+
+namespace blink {
+
+class Tracing {
+ public:
+   static void RegisterNatives(DartLibraryNatives* natives);
+};
+
+}  // namespace blink
+
+#endif  // SKY_ENGINE_CORE_TRACING_TRACING_H_
diff --git a/sky/engine/core/view/Tracing.cpp b/sky/engine/core/view/Tracing.cpp
deleted file mode 100644
index b648472..0000000
--- a/sky/engine/core/view/Tracing.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "sky/engine/core/view/Tracing.h"
-
-#include "base/trace_event/trace_event.h"
-#include "sky/engine/wtf/text/StringUTF8Adaptor.h"
-
-namespace blink {
-
-Tracing::Tracing()
-{
-}
-
-Tracing::~Tracing()
-{
-}
-
-void Tracing::begin(const String& name)
-{
-    StringUTF8Adaptor utf8(name);
-    // TRACE_EVENT_COPY_BEGIN0 needs a c-style null-terminated string.
-    CString cstring(utf8.data(), utf8.length());
-    TRACE_EVENT_COPY_BEGIN0("script", cstring.data());
-}
-
-void Tracing::end(const String& name)
-{
-    StringUTF8Adaptor utf8(name);
-    // TRACE_EVENT_COPY_END0 needs a c-style null-terminated string.
-    CString cstring(utf8.data(), utf8.length());
-    TRACE_EVENT_COPY_END0("script", cstring.data());
-}
-
-} // namespace blink
diff --git a/sky/engine/core/view/Tracing.h b/sky/engine/core/view/Tracing.h
deleted file mode 100644
index d7acc25..0000000
--- a/sky/engine/core/view/Tracing.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SKY_ENGINE_CORE_VIEW_TRACING_H_
-#define SKY_ENGINE_CORE_VIEW_TRACING_H_
-
-#include "sky/engine/tonic/dart_wrappable.h"
-#include "sky/engine/wtf/PassRefPtr.h"
-#include "sky/engine/wtf/RefCounted.h"
-#include "sky/engine/wtf/text/WTFString.h"
-
-namespace blink {
-
-class Tracing : public RefCounted<Tracing>, public DartWrappable {
-    DEFINE_WRAPPERTYPEINFO();
-public:
-    ~Tracing() override;
-    static PassRefPtr<Tracing> create() { return adoptRef(new Tracing); }
-
-    void begin(const String& name);
-    void end(const String& name);
-
-private:
-    Tracing();
-};
-
-} // namespace blink
-
-#endif  // SKY_ENGINE_CORE_VIEW_TRACING_H_
diff --git a/sky/engine/core/view/Tracing.idl b/sky/engine/core/view/Tracing.idl
deleted file mode 100644
index 1e2a417..0000000
--- a/sky/engine/core/view/Tracing.idl
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-[
-    Constructor()
-] interface Tracing {
-  void begin(DOMString name);
-  void end(DOMString name);
-};
diff --git a/sky/packages/flx/pubspec.yaml b/sky/packages/flx/pubspec.yaml
index 5368b17..56e9fc0 100644
--- a/sky/packages/flx/pubspec.yaml
+++ b/sky/packages/flx/pubspec.yaml
@@ -1,11 +1,11 @@
 name: flx
-version: 0.0.8
+version: 0.0.10
 author: Flutter Authors <flutter-dev@googlegroups.com>
 description: Library for dealing with Flutter bundle (.flx) files
 homepage: http://flutter.io
 dependencies:
   sky_services: 
-    '0.0.47'
+    '0.0.50'
   yaml: ^2.1.3
   asn1lib: ^0.4.1
   cipher: ^0.7.1
diff --git a/sky/packages/sky/lib/src/gestures/arena.dart b/sky/packages/sky/lib/src/gestures/arena.dart
index 184a314..50c7a5b 100644
--- a/sky/packages/sky/lib/src/gestures/arena.dart
+++ b/sky/packages/sky/lib/src/gestures/arena.dart
@@ -48,6 +48,12 @@
   bool isHeld = false;
   bool hasPendingSweep = false;
 
+  /// If a gesture attempts to win while the arena is still open, it becomes the
+  /// "eager winnner". We look for an eager winner when closing the arena to new
+  /// participants, and if there is one, we resolve the arena it its favour at
+  /// that time.
+  GestureArenaMember eagerWinner;
+
   void add(GestureArenaMember member) {
     assert(isOpen);
     members.add(member);
@@ -122,6 +128,8 @@
       state.members.first.acceptGesture(key);
     } else if (state.members.isEmpty) {
       _arenas.remove(key);
+    } else if (state.eagerWinner != null) {
+      _resolveInFavorOf(key, state, state.eagerWinner);
     }
   }
 
@@ -129,20 +137,33 @@
     _GestureArenaState state = _arenas[key];
     if (state == null)
       return;  // This arena has already resolved.
-    assert(!state.isOpen);
     assert(state.members.contains(member));
     if (disposition == GestureDisposition.rejected) {
       state.members.remove(member);
       member.rejectGesture(key);
-      _tryToResolveArena(key, state);
+      if (!state.isOpen)
+        _tryToResolveArena(key, state);
     } else {
       assert(disposition == GestureDisposition.accepted);
-      _arenas.remove(key);
-      for (GestureArenaMember rejectedMember in state.members) {
-        if (rejectedMember != member)
-          rejectedMember.rejectGesture(key);
+      if (state.isOpen) {
+        if (state.eagerWinner == null)
+          state.eagerWinner = member;
+      } else {
+        _resolveInFavorOf(key, state, member);
       }
-      member.acceptGesture(key);
     }
   }
-}
+
+  void _resolveInFavorOf(Object key, _GestureArenaState state, GestureArenaMember member) {
+    assert(state == _arenas[key]);
+    assert(state != null);
+    assert(state.eagerWinner == null || state.eagerWinner == member);
+    assert(!state.isOpen);
+    _arenas.remove(key);
+    for (GestureArenaMember rejectedMember in state.members) {
+      if (rejectedMember != member)
+        rejectedMember.rejectGesture(key);
+    }
+    member.acceptGesture(key);
+  }
+}
\ No newline at end of file
diff --git a/sky/packages/sky/lib/src/gestures/multitap.dart b/sky/packages/sky/lib/src/gestures/multitap.dart
index 841b968..ed9fdd1 100644
--- a/sky/packages/sky/lib/src/gestures/multitap.dart
+++ b/sky/packages/sky/lib/src/gestures/multitap.dart
@@ -10,7 +10,13 @@
 import 'events.dart';
 import 'pointer_router.dart';
 import 'recognizer.dart';
-import 'tap.dart' show GestureTapDownCallback, GestureTapDownCallback, GestureTapCallback, GestureTapCancelCallback;
+
+typedef void GestureDoubleTapCallback();
+
+typedef void GestureMultiTapDownCallback(Point globalPosition, int pointer);
+typedef void GestureMultiTapUpCallback(Point globalPosition, int pointer);
+typedef void GestureMultiTapCallback(int pointer);
+typedef void GestureMultiTapCancelCallback(int pointer);
 
 /// TapTracker helps track individual tap sequences as part of a
 /// larger gesture.
@@ -52,7 +58,12 @@
 
 class DoubleTapGestureRecognizer extends GestureRecognizer {
 
-  DoubleTapGestureRecognizer({ this.router, this.onDoubleTap });
+  DoubleTapGestureRecognizer({
+    PointerRouter router,
+    this.onDoubleTap
+  }) : _router = router {
+    assert(router != null);
+  }
 
   // Implementation notes:
   // The double tap recognizer can be in one of four states. There's no
@@ -74,8 +85,8 @@
   // - The long timer between taps expires
   // - The gesture arena decides we have been rejected wholesale
 
-  PointerRouter router;
-  GestureTapCallback onDoubleTap;
+  PointerRouter _router;
+  GestureDoubleTapCallback onDoubleTap;
 
   Timer _doubleTapTimer;
   _TapTracker _firstTap;
@@ -92,22 +103,26 @@
       entry: GestureArena.instance.add(event.pointer, this)
     );
     _trackers[event.pointer] = tracker;
-    tracker.startTrackingPointer(router, handleEvent);
+    tracker.startTrackingPointer(_router, handleEvent);
   }
 
   void handleEvent(PointerInputEvent event) {
     _TapTracker tracker = _trackers[event.pointer];
     assert(tracker != null);
-    if (event.type == 'pointerup') {
-      if (_firstTap == null)
-        _registerFirstTap(tracker);
-      else
-        _registerSecondTap(tracker);
-    } else if (event.type == 'pointermove' &&
-        !tracker.isWithinTolerance(event, kDoubleTapTouchSlop)) {
-      _reject(tracker);
-    } else if (event.type == 'pointercancel') {
-      _reject(tracker);
+    switch (event.type) {
+      case 'pointerup':
+        if (_firstTap == null)
+          _registerFirstTap(tracker);
+        else
+          _registerSecondTap(tracker);
+        break;
+      case 'pointermove':
+        if (!tracker.isWithinTolerance(event, kDoubleTapTouchSlop))
+          _reject(tracker);
+        break;
+      case 'pointercancel':
+        _reject(tracker);
+        break;
     }
   }
 
@@ -139,7 +154,7 @@
 
   void dispose() {
     _reset();
-    router = null;
+    _router = null;
   }
 
   void _reset() {
@@ -184,7 +199,7 @@
   }
 
   void _freezeTracker(_TapTracker tracker) {
-    tracker.stopTrackingPointer(router, handleEvent);
+    tracker.stopTrackingPointer(_router, handleEvent);
   }
 
   void _startDoubleTapTimer() {
@@ -213,21 +228,35 @@
 
   _TapGesture({
     MultiTapGestureRecognizer gestureRecognizer,
-    PointerInputEvent event
+    PointerInputEvent event,
+    Duration longTapDelay
   }) : gestureRecognizer = gestureRecognizer,
+       _lastPosition = event.position,
        super(event: event, entry: GestureArena.instance.add(event.pointer, gestureRecognizer)) {
     startTrackingPointer(gestureRecognizer.router, handleEvent);
+    if (longTapDelay > Duration.ZERO) {
+      _timer = new Timer(longTapDelay, () {
+        _timer = null;
+        gestureRecognizer._handleLongTap(event.pointer, _lastPosition);
+      });
+    }
   }
 
   final MultiTapGestureRecognizer gestureRecognizer;
 
   bool _wonArena = false;
+  Timer _timer;
+
+  Point _lastPosition;
   Point _finalPosition;
 
   void handleEvent(PointerInputEvent event) {
     assert(event.pointer == pointer);
-    if (event.type == 'pointermove' && !isWithinTolerance(event, kTouchSlop)) {
-      cancel();
+    if (event.type == 'pointermove') {
+      if (!isWithinTolerance(event, kTouchSlop))
+        cancel();
+      else
+        _lastPosition = event.position;
     } else if (event.type == 'pointercancel') {
       cancel();
     } else if (event.type == 'pointerup') {
@@ -237,6 +266,12 @@
     }
   }
 
+  void stopTrackingPointer(PointerRouter router, PointerRoute route) {
+    _timer?.cancel();
+    _timer = null;
+    super.stopTrackingPointer(router, route);
+  }
+
   void accept() {
     _wonArena = true;
     _check();
@@ -269,61 +304,77 @@
 /// taps, on up-1 and up-2.
 class MultiTapGestureRecognizer extends GestureRecognizer {
   MultiTapGestureRecognizer({
-    this.router,
+    PointerRouter router,
     this.onTapDown,
     this.onTapUp,
     this.onTap,
-    this.onTapCancel
-  });
+    this.onTapCancel,
+    this.longTapDelay: Duration.ZERO,
+    this.onLongTapDown
+  }) : _router = router {
+    assert(router != null);
+  }
 
-  PointerRouter router;
-  GestureTapDownCallback onTapDown;
-  GestureTapDownCallback onTapUp;
-  GestureTapCallback onTap;
-  GestureTapCancelCallback onTapCancel;
+  PointerRouter get router => _router;
+  PointerRouter _router;
+  GestureMultiTapDownCallback onTapDown;
+  GestureMultiTapUpCallback onTapUp;
+  GestureMultiTapCallback onTap;
+  GestureMultiTapCancelCallback onTapCancel;
+  Duration longTapDelay;
+  GestureMultiTapDownCallback onLongTapDown;
 
-  Map<int, _TapGesture> _gestureMap = new Map<int, _TapGesture>();
+  final Map<int, _TapGesture> _gestureMap = new Map<int, _TapGesture>();
 
   void addPointer(PointerInputEvent event) {
     assert(!_gestureMap.containsKey(event.pointer));
     _gestureMap[event.pointer] = new _TapGesture(
       gestureRecognizer: this,
-      event: event
+      event: event,
+      longTapDelay: longTapDelay
     );
     if (onTapDown != null)
-      onTapDown(event.position);
+      onTapDown(event.position, event.pointer);
   }
 
   void acceptGesture(int pointer) {
     assert(_gestureMap.containsKey(pointer));
     _gestureMap[pointer]?.accept();
+    assert(!_gestureMap.containsKey(pointer));
   }
 
   void rejectGesture(int pointer) {
     assert(_gestureMap.containsKey(pointer));
     _gestureMap[pointer]?.reject();
+    assert(!_gestureMap.containsKey(pointer));
   }
 
   void _resolveTap(int pointer, _TapResolution resolution, Point globalPosition) {
     _gestureMap.remove(pointer);
     if (resolution == _TapResolution.tap) {
       if (onTapUp != null)
-        onTapUp(globalPosition);
+        onTapUp(globalPosition, pointer);
       if (onTap != null)
-        onTap();
+        onTap(pointer);
     } else {
       if (onTapCancel != null)
-        onTapCancel();
+        onTapCancel(pointer);
     }
   }
 
+  void _handleLongTap(int pointer, Point lastPosition) {
+    assert(_gestureMap.containsKey(pointer));
+    if (onLongTapDown != null)
+      onLongTapDown(lastPosition, pointer);
+  }
+
   void dispose() {
     List<_TapGesture> localGestures = new List<_TapGesture>.from(_gestureMap.values);
     for (_TapGesture gesture in localGestures)
       gesture.cancel();
     // Rejection of each gesture should cause it to be removed from our map
     assert(_gestureMap.isEmpty);
-    router = null;
+    _router = null;
   }
 
 }
diff --git a/sky/packages/sky/lib/src/gestures/recognizer.dart b/sky/packages/sky/lib/src/gestures/recognizer.dart
index 984e065..5c5c5a8 100644
--- a/sky/packages/sky/lib/src/gestures/recognizer.dart
+++ b/sky/packages/sky/lib/src/gestures/recognizer.dart
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:ui' as ui;
+import 'dart:ui' show Point, Offset;
 
 import 'arena.dart';
 import 'constants.dart';
@@ -84,10 +84,6 @@
   defunct
 }
 
-ui.Point _getPoint(PointerInputEvent event) {
-  return new ui.Point(event.x, event.y);
-}
-
 abstract class PrimaryPointerGestureRecognizer extends OneSequenceGestureRecognizer {
   PrimaryPointerGestureRecognizer({ PointerRouter router, this.deadline })
     : super(router: router);
@@ -96,7 +92,7 @@
 
   GestureRecognizerState state = GestureRecognizerState.ready;
   int primaryPointer;
-  ui.Point initialPosition;
+  Point initialPosition;
   Timer _timer;
 
   void addPointer(PointerInputEvent event) {
@@ -104,7 +100,7 @@
     if (state == GestureRecognizerState.ready) {
       state = GestureRecognizerState.possible;
       primaryPointer = event.pointer;
-      initialPosition = _getPoint(event);
+      initialPosition = event.position;
       if (deadline != null)
         _timer = new Timer(deadline, didExceedDeadline);
     }
@@ -159,7 +155,7 @@
   }
 
   double _getDistance(PointerInputEvent event) {
-    ui.Offset offset = _getPoint(event) - initialPosition;
+    Offset offset = event.position - initialPosition;
     return offset.distance;
   }
 
diff --git a/sky/packages/sky/lib/src/material/bottom_sheet.dart b/sky/packages/sky/lib/src/material/bottom_sheet.dart
index cdda92b..7e81c30 100644
--- a/sky/packages/sky/lib/src/material/bottom_sheet.dart
+++ b/sky/packages/sky/lib/src/material/bottom_sheet.dart
@@ -16,15 +16,15 @@
 const double _kMinFlingVelocity = 700.0;
 const double _kFlingVelocityScale = 1.0 / 300.0;
 
-class _BottomSheet extends StatefulComponent {
-  _BottomSheet({ Key key, this.route }) : super(key: key);
+class _ModalBottomSheet extends StatefulComponent {
+  _ModalBottomSheet({ Key key, this.route }) : super(key: key);
 
   final _ModalBottomSheetRoute route;
 
-  _BottomSheetState createState() => new _BottomSheetState();
+  _ModalBottomSheetState createState() => new _ModalBottomSheetState();
 }
 
-class _BottomSheetLayout extends OneChildLayoutDelegate {
+class _ModalBottomSheetLayout extends OneChildLayoutDelegate {
   // The distance from the bottom of the parent to the top of the BottomSheet child.
   AnimatedValue<double> childTop = new AnimatedValue<double>(0.0);
 
@@ -43,9 +43,9 @@
   }
 }
 
-class _BottomSheetState extends State<_BottomSheet> {
+class _ModalBottomSheetState extends State<_ModalBottomSheet> {
 
-  final _BottomSheetLayout _layout = new _BottomSheetLayout();
+  final _ModalBottomSheetLayout _layout = new _ModalBottomSheetLayout();
   bool _dragEnabled = false;
 
   void _handleDragStart(Point position) {
@@ -111,7 +111,7 @@
   }
 
   Color get barrierColor => Colors.black54;
-  Widget buildModalWidget(BuildContext context) => new _BottomSheet(route: this);
+  Widget buildModalWidget(BuildContext context) => new _ModalBottomSheet(route: this);
 
   void didPop([dynamic result]) {
     completer.complete(result);
@@ -120,6 +120,7 @@
 }
 
 Future showModalBottomSheet({ BuildContext context, Widget child }) {
+  assert(child != null);
   final Completer completer = new Completer();
   Navigator.of(context).pushEphemeral(new _ModalBottomSheetRoute(
     completer: completer,
@@ -127,3 +128,36 @@
   ));
   return completer.future;
 }
+
+class _PersistentBottomSheet extends StatelessComponent {
+  _PersistentBottomSheet({
+    Key key,
+    this.child,
+    this.route
+  }) : super(key: key);
+
+  final TransitionRoute route;
+  final Widget child;
+
+  Widget build(BuildContext context) {
+    return new AlignTransition(
+      performance: route.performance,
+      alignment: new AnimatedValue<FractionalOffset>(const FractionalOffset(0.0, 0.0)),
+      heightFactor: new AnimatedValue<double>(0.0, end: 1.0),
+      child: child
+    );
+  }
+}
+
+class _PersistentBottomSheetRoute extends TransitionRoute {
+  bool get opaque => false;
+  Duration get transitionDuration => _kBottomSheetDuration;
+}
+
+void showBottomSheet({ BuildContext context, GlobalKey<PlaceholderState> placeholderKey, Widget child }) {
+  assert(child != null);
+  assert(placeholderKey != null);
+  _PersistentBottomSheetRoute route = new _PersistentBottomSheetRoute();
+  placeholderKey.currentState.child = new _PersistentBottomSheet(route: route, child: child);
+  Navigator.of(context).pushEphemeral(route);
+}
diff --git a/sky/packages/sky/lib/src/material/drawer.dart b/sky/packages/sky/lib/src/material/drawer.dart
index 2cb4347..29d1aa5 100644
--- a/sky/packages/sky/lib/src/material/drawer.dart
+++ b/sky/packages/sky/lib/src/material/drawer.dart
@@ -6,8 +6,7 @@
 import 'package:flutter/widgets.dart';
 
 import 'colors.dart';
-import 'shadows.dart';
-import 'theme.dart';
+import 'material.dart';
 
 // TODO(eseidel): Draw width should vary based on device size:
 // http://www.google.com/design/spec/layout/structure.html#structure-side-nav
@@ -24,11 +23,7 @@
 
 const double _kWidth = 304.0;
 const double _kMinFlingVelocity = 365.0;
-const double _kFlingVelocityScale = 1.0 / 300.0;
 const Duration _kBaseSettleDuration = const Duration(milliseconds: 246);
-const Duration _kThemeChangeDuration = const Duration(milliseconds: 200);
-const Point _kOpenPosition = Point.origin;
-const Point _kClosedPosition = const Point(-_kWidth, 0.0);
 
 class _Drawer extends StatelessComponent {
   _Drawer({ Key key, this.route }) : super(key: key);
@@ -38,101 +33,138 @@
   Widget build(BuildContext context) {
     return new Focus(
       key: new GlobalObjectKey(route),
-      autofocus: true,
-      child: new GestureDetector(
-        onHorizontalDragStart: (_) {
-          if (route.interactive)
-            route._takeControl();
-        },
-        onHorizontalDragUpdate: (double delta) {
-          if (route.interactive)
-            route._moveDrawer(delta);
-        },
-        onHorizontalDragEnd: (Offset velocity) {
-          if (route.interactive)
-            route._settle(velocity);
-        },
-        child: new Stack(<Widget>[
-          // mask
-          new GestureDetector(
-            onTap: () {
-              if (route.interactive)
-                route._close();
-            },
-            child: new ColorTransition(
-              performance: route.performance,
-              color: new AnimatedColorValue(Colors.transparent, end: Colors.black54),
-              child: new Container()
-            )
-          ),
-          new Positioned(
-            top: 0.0,
-            left: 0.0,
-            bottom: 0.0,
-            child: new SlideTransition(
-              performance: route.performance,
-              position: new AnimatedValue<Point>(_kClosedPosition, end: _kOpenPosition),
-              child: new AnimatedContainer(
-                curve: Curves.ease,
-                duration: _kThemeChangeDuration,
-                decoration: new BoxDecoration(
-                  backgroundColor: Theme.of(context).canvasColor,
-                  boxShadow: shadows[route.level]),
-                width: _kWidth,
-                child: route.child
-              )
-            )
-          )
-        ])
+      child: new ConstrainedBox(
+        constraints: const BoxConstraints.expand(width: _kWidth),
+        child: new Material(
+          level: route.level,
+          child: route.child
+        )
       )
     );
   }
-
 }
 
-class _DrawerRoute extends TransitionRoute {
+enum _DrawerState {
+  showing,
+  popped,
+  closed,
+}
+
+class _DrawerRoute extends OverlayRoute {
   _DrawerRoute({ this.child, this.level });
 
   final Widget child;
   final int level;
 
-  Duration get transitionDuration => _kBaseSettleDuration;
-  bool get opaque => false;
-
-  bool get interactive => _interactive;
-  bool _interactive = true;
-
-  Performance _performance;
-
-  Performance createPerformance() {
-    _performance = super.createPerformance();
-    return _performance;
-  }
-
   List<WidgetBuilder> get builders => <WidgetBuilder>[ _build ];
 
-  Widget _build(BuildContext context) => new _Drawer(route: this);
+  final GlobalKey<_DrawerControllerState> _drawerKey = new GlobalKey<_DrawerControllerState>();
+  _DrawerState _state = _DrawerState.showing;
 
-  void didPop([dynamic result]) {
-    assert(result == null); // because we don't do anything with it, so otherwise it'd be lost
-    super.didPop(result);
-    _interactive = false;
+  Widget _build(BuildContext context) {
+    return new _DrawerController(
+      key: _drawerKey,
+      settleDuration: _kBaseSettleDuration,
+      onClosed: () {
+        _DrawerState previousState = _state;
+        _state = _DrawerState.closed;
+        switch (previousState) {
+          case _DrawerState.showing:
+            Navigator.of(context).pop();
+            break;
+          case _DrawerState.popped:
+            super.didPop(null);
+            break;
+          case _DrawerState.closed:
+            assert(false);
+            break;
+        }
+      },
+      child: new _Drawer(route: this)
+    );
   }
 
-  void _takeControl() {
-    assert(_interactive);
+  void didPop(dynamic result) {
+    switch (_state) {
+      case _DrawerState.showing:
+        _drawerKey.currentState?._close();
+        _state = _DrawerState.popped;
+        break;
+      case _DrawerState.popped:
+        assert(false);
+        break;
+      case _DrawerState.closed:
+        super.didPop(null);
+        break;
+    }
+  }
+}
+
+class _DrawerController extends StatefulComponent {
+  _DrawerController({
+    Key key,
+    this.settleDuration,
+    this.onClosed,
+    this.child
+  }) : super(key: key);
+
+  final Duration settleDuration;
+  final Widget child;
+  final VoidCallback onClosed;
+
+  _DrawerControllerState createState() => new _DrawerControllerState();
+}
+
+class _DrawerControllerState extends State<_DrawerController> {
+  void initState() {
+    super.initState();
+    _performance = new Performance(duration: config.settleDuration)
+      ..addListener(_performanceChanged)
+      ..addStatusListener(_performanceStatusChanged)
+      ..play();
+  }
+
+  void dispose() {
+    _performance
+      ..removeListener(_performanceChanged)
+      ..removeStatusListener(_performanceStatusChanged)
+      ..stop();
+    super.dispose();
+  }
+
+  void _performanceChanged() {
+    setState(() {
+      // The performance's state is our build state, and it changed already.
+    });
+  }
+
+  void _performanceStatusChanged(PerformanceStatus status) {
+    if (status == PerformanceStatus.dismissed && config.onClosed != null)
+      config.onClosed();
+  }
+
+  Performance _performance;
+  double _width;
+
+  final AnimatedColorValue _color = new AnimatedColorValue(Colors.transparent, end: Colors.black54);
+
+  void _handleSizeChanged(Size newSize) {
+    setState(() {
+      _width = newSize.width;
+    });
+  }
+
+  void _handlePointerDown(_) {
     _performance.stop();
   }
 
-  void _moveDrawer(double delta) {
-    assert(_interactive);
-    _performance.progress += delta / _kWidth;
+  void _move(double delta) {
+    _performance.progress += delta / _width;
   }
 
   void _settle(Offset velocity) {
-    assert(_interactive);
     if (velocity.dx.abs() >= _kMinFlingVelocity) {
-      _performance.fling(velocity: velocity.dx * _kFlingVelocityScale);
+      _performance.fling(velocity: velocity.dx / _width);
     } else if (_performance.progress < 0.5) {
       _close();
     } else {
@@ -141,9 +173,43 @@
   }
 
   void _close() {
-    assert(_interactive);
     _performance.fling(velocity: -1.0);
   }
+
+  Widget build(BuildContext context) {
+    _performance.updateVariable(_color);
+    return new GestureDetector(
+      onHorizontalDragUpdate: _move,
+      onHorizontalDragEnd: _settle,
+      child: new Stack(<Widget>[
+        new GestureDetector(
+          onTap: _close,
+          child: new DecoratedBox(
+            decoration: new BoxDecoration(
+              backgroundColor: _color.value
+            ),
+            child: new Container()
+          )
+        ),
+        new Positioned(
+          top: 0.0,
+          left: 0.0,
+          bottom: 0.0,
+          child: new Listener(
+            onPointerDown: _handlePointerDown,
+            child: new Align(
+              alignment: const FractionalOffset(1.0, 0.5),
+              widthFactor: _performance.progress,
+              child: new SizeObserver(
+                onSizeChanged: _handleSizeChanged,
+                child: config.child
+              )
+            )
+          )
+        )
+      ])
+    );
+  }
 }
 
 void showDrawer({ BuildContext context, Widget child, int level: 3 }) {
diff --git a/sky/packages/sky/lib/src/material/dropdown.dart b/sky/packages/sky/lib/src/material/dropdown.dart
index 66a152d..b11b107 100644
--- a/sky/packages/sky/lib/src/material/dropdown.dart
+++ b/sky/packages/sky/lib/src/material/dropdown.dart
@@ -107,7 +107,7 @@
             builder: (BuildContext context) {
               RenderBox renderBox = context.findRenderObject();
               return new CustomPaint(
-                child: new ScrollableViewport(child: new Container(child: new Column(children))),
+                child: new Block(children),
                 onPaint: (ui.Canvas canvas, Size size) {
                   double top = renderBox.globalToLocal(new Point(0.0, menuTop.value)).y;
                   double bottom = renderBox.globalToLocal(new Point(0.0, menuBottom.value)).y;
diff --git a/sky/packages/sky/lib/src/material/icon_button.dart b/sky/packages/sky/lib/src/material/icon_button.dart
index b8d3c5d..087c284 100644
--- a/sky/packages/sky/lib/src/material/icon_button.dart
+++ b/sky/packages/sky/lib/src/material/icon_button.dart
@@ -22,6 +22,8 @@
   final VoidCallback onPressed;
 
   Widget build(BuildContext context) {
+    // TODO(abarth): We should use a radial reaction here so you can hit the
+    // 8.0 pixel padding as well as the icon.
     return new GestureDetector(
       onTap: onPressed,
       child: new Padding(
diff --git a/sky/packages/sky/lib/src/material/ink_well.dart b/sky/packages/sky/lib/src/material/ink_well.dart
index 21eb524..6051dc9 100644
--- a/sky/packages/sky/lib/src/material/ink_well.dart
+++ b/sky/packages/sky/lib/src/material/ink_well.dart
@@ -248,6 +248,8 @@
       onLongPress();
   }
 
+  bool hitTestSelf(Point position) => true;
+
   void paint(PaintingContext context, Offset offset) {
     if (!_splashes.isEmpty) {
       final PaintingCanvas canvas = context.canvas;
diff --git a/sky/packages/sky/lib/src/material/material_app.dart b/sky/packages/sky/lib/src/material/material_app.dart
index 01be129..1d35a92 100644
--- a/sky/packages/sky/lib/src/material/material_app.dart
+++ b/sky/packages/sky/lib/src/material/material_app.dart
@@ -31,14 +31,24 @@
 
 final AssetBundle _defaultBundle = _initDefaultBundle();
 
+class RouteArguments {
+  const RouteArguments({ this.context });
+  final BuildContext context;
+}
+typedef Widget RouteBuilder(RouteArguments args);
+typedef RouteBuilder RouteGenerator(String name);
+
 class MaterialApp extends StatefulComponent {
   MaterialApp({
     Key key,
     this.title,
     this.theme,
-    this.routes,
+    this.routes: const <String, RouteBuilder>{},
     this.onGenerateRoute
-  }) : super(key: key);
+  }) : super(key: key) {
+    assert(routes != null);
+    assert(routes.containsKey(Navigator.defaultRouteName) || onGenerateRoute != null);
+  }
 
   final String title;
   final ThemeData theme;
diff --git a/sky/packages/sky/lib/src/material/material_button.dart b/sky/packages/sky/lib/src/material/material_button.dart
index 65eb954..e304429 100644
--- a/sky/packages/sky/lib/src/material/material_button.dart
+++ b/sky/packages/sky/lib/src/material/material_button.dart
@@ -92,7 +92,7 @@
     Widget contents = new Container(
       padding: new EdgeDims.symmetric(horizontal: 8.0),
       child: new Center(
-        shrinkWrap: ShrinkWrap.width,
+        widthFactor: 1.0,
         child: config.child
       )
     );
diff --git a/sky/packages/sky/lib/src/material/popup_menu.dart b/sky/packages/sky/lib/src/material/popup_menu.dart
index 6c82313..5fc1cf9 100644
--- a/sky/packages/sky/lib/src/material/popup_menu.dart
+++ b/sky/packages/sky/lib/src/material/popup_menu.dart
@@ -76,14 +76,11 @@
               ),
               child: new IntrinsicWidth(
                 stepWidth: _kMenuWidthStep,
-                child: new ScrollableViewport(
-                  child: new Container(
-                    // TODO(abarth): Teach Block about padding.
-                    padding: const EdgeDims.symmetric(
-                      horizontal: _kMenuHorizontalPadding,
-                      vertical: _kMenuVerticalPadding
-                    ),
-                    child: new BlockBody(children)
+                child: new Block(
+                  children,
+                  padding: const EdgeDims.symmetric(
+                    horizontal: _kMenuHorizontalPadding,
+                    vertical: _kMenuVerticalPadding
                   )
                 )
               )
diff --git a/sky/packages/sky/lib/src/material/scaffold.dart b/sky/packages/sky/lib/src/material/scaffold.dart
index 487569b..3a88ce9 100644
--- a/sky/packages/sky/lib/src/material/scaffold.dart
+++ b/sky/packages/sky/lib/src/material/scaffold.dart
@@ -14,15 +14,10 @@
 
 const double _kFloatingActionButtonMargin = 16.0; // TODO(hmuller): should be device dependent
 
-const int _kBodyIndex = 0;
-const int _kToolBarIndex = 1;
-const int _kBottomSheetIndex = 2;
-const int _kSnackBarIndex = 3;
-const int _kFloatingActionButtonIndex = 4;
+enum _Child { body, toolBar, bottomSheet, snackBar, floatingActionButton }
 
 class _ScaffoldLayout extends MultiChildLayoutDelegate {
-  void performLayout(Size size, BoxConstraints constraints, int childCount) {
-    assert(childCount == 5);
+  void performLayout(Size size, BoxConstraints constraints) {
 
     // This part of the layout has the same effect as putting the toolbar and
     // body in a column and making the body flexible. What's different is that
@@ -30,12 +25,19 @@
     // so the toolbar's shadow is drawn on top of the body.
 
     final BoxConstraints toolBarConstraints = constraints.loosen().tightenWidth(size.width);
-    final Size toolBarSize = layoutChild(_kToolBarIndex, toolBarConstraints);
-    final double bodyHeight = size.height - toolBarSize.height;
-    final BoxConstraints bodyConstraints = toolBarConstraints.tightenHeight(bodyHeight);
-    layoutChild(_kBodyIndex, bodyConstraints);
-    positionChild(_kToolBarIndex, Point.origin);
-    positionChild(_kBodyIndex, new Point(0.0, toolBarSize.height));
+    Size toolBarSize = Size.zero;
+
+    if (isChild(_Child.toolBar)) {
+      toolBarSize = layoutChild(_Child.toolBar, toolBarConstraints);
+      positionChild(_Child.toolBar, Point.origin);
+    }
+
+    if (isChild(_Child.body)) {
+      final double bodyHeight = size.height - toolBarSize.height;
+      final BoxConstraints bodyConstraints = toolBarConstraints.tightenHeight(bodyHeight);
+      layoutChild(_Child.body, bodyConstraints);
+      positionChild(_Child.body, new Point(0.0, toolBarSize.height));
+    }
 
     // The BottomSheet and the SnackBar are anchored to the bottom of the parent,
     // they're as wide as the parent and are given their intrinsic height.
@@ -47,24 +49,39 @@
     // by _kFloatingActionButtonMargin.
 
     final BoxConstraints fullWidthConstraints = constraints.loosen().tightenWidth(size.width);
-    final Size bottomSheetSize = layoutChild(_kBottomSheetIndex, fullWidthConstraints);
-    final Size snackBarSize = layoutChild(_kSnackBarIndex, fullWidthConstraints);
-    final Size fabSize = layoutChild(_kFloatingActionButtonIndex, constraints.loosen());
-    positionChild(_kBottomSheetIndex, new Point(0.0, size.height - bottomSheetSize.height));
-    positionChild(_kSnackBarIndex, new Point(0.0, size.height - snackBarSize.height));
+    Size bottomSheetSize = Size.zero;
+    Size snackBarSize = Size.zero;
 
-    final double fabX = size.width - fabSize.width - _kFloatingActionButtonMargin;
-    double fabY = size.height - fabSize.height - _kFloatingActionButtonMargin;
-    if (snackBarSize.height > 0.0)
-      fabY = math.min(fabY, size.height - snackBarSize.height - fabSize.height - _kFloatingActionButtonMargin);
-    if (bottomSheetSize.height > 0.0)
-      fabY = math.min(fabY, size.height - bottomSheetSize.height - fabSize.height / 2.0);
-    positionChild(_kFloatingActionButtonIndex, new Point(fabX, fabY));
+    if (isChild(_Child.bottomSheet)) {
+      bottomSheetSize = layoutChild(_Child.bottomSheet, fullWidthConstraints);
+      positionChild(_Child.bottomSheet, new Point(0.0, size.height - bottomSheetSize.height));
+    }
+
+    if (isChild(_Child.snackBar)) {
+      snackBarSize = layoutChild(_Child.snackBar, fullWidthConstraints);
+      positionChild(_Child.snackBar, new Point(0.0, size.height - snackBarSize.height));
+    }
+
+    if (isChild(_Child.floatingActionButton)) {
+      final Size fabSize = layoutChild(_Child.floatingActionButton, constraints.loosen());
+      final double fabX = size.width - fabSize.width - _kFloatingActionButtonMargin;
+      double fabY = size.height - fabSize.height - _kFloatingActionButtonMargin;
+      if (snackBarSize.height > 0.0)
+        fabY = math.min(fabY, size.height - snackBarSize.height - fabSize.height - _kFloatingActionButtonMargin);
+      if (bottomSheetSize.height > 0.0)
+        fabY = math.min(fabY, size.height - bottomSheetSize.height - fabSize.height / 2.0);
+      positionChild(_Child.floatingActionButton, new Point(fabX, fabY));
+    }
   }
 }
 
 final _ScaffoldLayout _scaffoldLayout = new _ScaffoldLayout();
 
+void _addIfNonNull(List<LayoutId> children, Widget child, Object childId) {
+  if (child != null)
+    children.add(new LayoutId(child: child, id: childId));
+}
+
 class Scaffold extends StatelessComponent {
   Scaffold({
     Key key,
@@ -92,14 +109,14 @@
         child: snackBar
       );
     }
-    return new CustomMultiChildLayout(<Widget>[
-      materialBody ?? new Container(height: 0.0),
-      paddedToolBar ?? new Container(height: 0.0),
-      bottomSheet ?? new Container(height: 0.0),
-      constrainedSnackBar ?? new Container(height: 0.0),
-      floatingActionButton ?? new Container(height: 0.0)
-    ],
-      delegate: _scaffoldLayout
-    );
+
+    final List<LayoutId>children = new List<LayoutId>();
+    _addIfNonNull(children, materialBody, _Child.body);
+    _addIfNonNull(children, paddedToolBar, _Child.toolBar);
+    _addIfNonNull(children, bottomSheet, _Child.bottomSheet);
+    _addIfNonNull(children, constrainedSnackBar, _Child.snackBar);
+    _addIfNonNull(children, floatingActionButton, _Child.floatingActionButton);
+
+    return new CustomMultiChildLayout(children, delegate: _scaffoldLayout);
   }
 }
diff --git a/sky/packages/sky/lib/src/material/tabs.dart b/sky/packages/sky/lib/src/material/tabs.dart
index 40ae53c..efb3cd6 100644
--- a/sky/packages/sky/lib/src/material/tabs.dart
+++ b/sky/packages/sky/lib/src/material/tabs.dart
@@ -205,8 +205,8 @@
       reportLayoutChangedIfNeeded();
   }
 
-  void hitTestChildren(HitTestResult result, { Point position }) {
-    defaultHitTestChildren(result, position: position);
+  bool hitTestChildren(HitTestResult result, { Point position }) {
+    return defaultHitTestChildren(result, position: position);
   }
 
   void _paintIndicator(PaintingCanvas canvas, RenderBox selectedTab, Offset offset) {
@@ -344,7 +344,7 @@
     }
 
     Container centeredLabel = new Container(
-      child: new Center(child: labelContent, shrinkWrap: ShrinkWrap.both),
+      child: new Center(child: labelContent, widthFactor: 1.0, heightFactor: 1.0),
       constraints: new BoxConstraints(minWidth: _kMinTabWidth),
       padding: _kTabLabelPadding
     );
diff --git a/sky/packages/sky/lib/src/rendering/auto_layout.dart b/sky/packages/sky/lib/src/rendering/auto_layout.dart
index 4aca6f7..54f31ef 100644
--- a/sky/packages/sky/lib/src/rendering/auto_layout.dart
+++ b/sky/packages/sky/lib/src/rendering/auto_layout.dart
@@ -219,8 +219,8 @@
     // only indicates that the value has been flushed to the variable.
   }
 
-  void hitTestChildren(HitTestResult result, {Point position}) {
-    defaultHitTestChildren(result, position: position);
+  bool hitTestChildren(HitTestResult result, {Point position}) {
+    return defaultHitTestChildren(result, position: position);
   }
 
   void paint(PaintingContext context, Offset offset) {
diff --git a/sky/packages/sky/lib/src/rendering/binding.dart b/sky/packages/sky/lib/src/rendering/binding.dart
index 3d4f72a7..0ba8995 100644
--- a/sky/packages/sky/lib/src/rendering/binding.dart
+++ b/sky/packages/sky/lib/src/rendering/binding.dart
@@ -144,7 +144,7 @@
       _renderView = renderViewOverride;
     }
     assert(_renderView != null);
-    scheduler.addPersistentFrameCallback(beginFrame);
+    scheduler.addPersistentFrameCallback(_handlePersistentFrameCallback);
 
     assert(_instance == this);
   }
@@ -172,8 +172,12 @@
       listener(size);
   }
 
+  void _handlePersistentFrameCallback(Duration timeStamp) {
+    beginFrame();
+  }
+
   /// Pump the rendering pipeline to generate a frame for the given time stamp
-  void beginFrame(Duration timeStamp) {
+  void beginFrame() {
     RenderObject.flushLayout();
     _renderView.updateCompositingBits();
     RenderObject.flushPaint();
diff --git a/sky/packages/sky/lib/src/rendering/block.dart b/sky/packages/sky/lib/src/rendering/block.dart
index 463ea55..275a883 100644
--- a/sky/packages/sky/lib/src/rendering/block.dart
+++ b/sky/packages/sky/lib/src/rendering/block.dart
@@ -219,8 +219,8 @@
     defaultPaint(context, offset);
   }
 
-  void hitTestChildren(HitTestResult result, { Point position }) {
-    defaultHitTestChildren(result, position: position);
+  bool hitTestChildren(HitTestResult result, { Point position }) {
+    return defaultHitTestChildren(result, position: position);
   }
 
 }
@@ -423,11 +423,11 @@
       transform.translate(startOffset, 0.0);
   }
 
-  void hitTestChildren(HitTestResult result, { Point position }) {
+  bool hitTestChildren(HitTestResult result, { Point position }) {
     if (isVertical)
-      defaultHitTestChildren(result, position: position + new Offset(0.0, -startOffset));
+      return defaultHitTestChildren(result, position: position + new Offset(0.0, -startOffset));
     else
-      defaultHitTestChildren(result, position: position + new Offset(-startOffset, 0.0));
+      return defaultHitTestChildren(result, position: position + new Offset(-startOffset, 0.0));
   }
 
   void debugDescribeSettings(List<String> settings) {
diff --git a/sky/packages/sky/lib/src/rendering/box.dart b/sky/packages/sky/lib/src/rendering/box.dart
index 7e1a89e..f2e1df4 100644
--- a/sky/packages/sky/lib/src/rendering/box.dart
+++ b/sky/packages/sky/lib/src/rendering/box.dart
@@ -5,6 +5,7 @@
 import 'dart:math' as math;
 import 'dart:ui' as ui;
 
+import 'package:flutter/animation.dart';
 import 'package:flutter/gestures.dart';
 import 'package:flutter/painting.dart';
 import 'package:vector_math/vector_math_64.dart';
@@ -543,22 +544,28 @@
   /// coordinate space of the callee.  The callee is responsible for checking
   /// whether the given position is within its bounds.
   bool hitTest(HitTestResult result, { Point position }) {
+    assert(!needsLayout);
     if (position.x >= 0.0 && position.x < _size.width &&
         position.y >= 0.0 && position.y < _size.height) {
-      hitTestChildren(result, position: position);
-      result.add(new BoxHitTestEntry(this, position));
-      return true;
+      if (hitTestChildren(result, position: position) || hitTestSelf(position)) {
+        result.add(new BoxHitTestEntry(this, position));
+        return true;
+      }
     }
     return false;
   }
 
+  /// Override this function if this render object can be hit even if its
+  /// children were not hit
+  bool hitTestSelf(Point position) => false;
+
   /// Override this function to check whether any children are located at the
   /// given position
   ///
   /// Typically children should be hit tested in reverse paint order so that
   /// hit tests at locations where children overlap hit the child that is
   /// visually "on top" (i.e., paints later).
-  void hitTestChildren(HitTestResult result, { Point position }) { }
+  bool hitTestChildren(HitTestResult result, { Point position }) => false;
 
   /// Multiply the transform from the parent's coordinate system to this box's
   /// coordinate system into the given transform
@@ -787,5 +794,21 @@
     value = 37 * value + y.hashCode;
     return value;
   }
+  static FractionalOffset lerp(FractionalOffset a, FractionalOffset b, double t) {
+    if (a == null && b == null)
+      return null;
+    if (a == null)
+      return new FractionalOffset(b.x * t, b.y * t);
+    if (b == null)
+      return new FractionalOffset(b.x * (1.0 - t), b.y * (1.0 - t));
+    return new FractionalOffset(ui.lerpDouble(a.x, b.x, t), ui.lerpDouble(a.y, b.y, t));
+  }
   String toString() => '$runtimeType($x, $y)';
 }
+
+class AnimatedFractionalOffsetValue extends AnimatedValue<FractionalOffset> {
+  AnimatedFractionalOffsetValue(FractionalOffset begin, { FractionalOffset end, Curve curve, Curve reverseCurve })
+    : super(begin, end: end, curve: curve, reverseCurve: reverseCurve);
+
+  FractionalOffset lerp(double t) => FractionalOffset.lerp(begin, end, t);
+}
diff --git a/sky/packages/sky/lib/src/rendering/custom_layout.dart b/sky/packages/sky/lib/src/rendering/custom_layout.dart
index d08c90c..871c797 100644
--- a/sky/packages/sky/lib/src/rendering/custom_layout.dart
+++ b/sky/packages/sky/lib/src/rendering/custom_layout.dart
@@ -5,10 +5,12 @@
 import 'box.dart';
 import 'object.dart';
 
-class _MultiChildParentData extends ContainerBoxParentDataMixin<RenderBox> { }
+class MultiChildLayoutParentData extends ContainerBoxParentDataMixin<RenderBox> {
+  Object id;
+}
 
 abstract class MultiChildLayoutDelegate {
-  final List<RenderBox> _indexToChild = <RenderBox>[];
+  final Map<Object, RenderBox> _idToChild = new Map<Object, RenderBox>();
 
   /// Returns the size of this object given the incomming constraints.
   /// The size cannot reflect the instrinsic sizes of the children.
@@ -16,41 +18,47 @@
   /// can reflect that.
   Size getSize(BoxConstraints constraints) => constraints.biggest;
 
+  /// True if a non-null LayoutChild was provided for the specified id.
+  bool isChild(Object childId) => _idToChild[childId] != null;
+
   /// Ask the child to update its layout within the limits specified by
   /// the constraints parameter. The child's size is returned.
-  Size layoutChild(int childIndex, BoxConstraints constraints) {
-    final RenderBox child = _indexToChild[childIndex];
+  Size layoutChild(Object childId, BoxConstraints constraints) {
+    final RenderBox child = _idToChild[childId];
+    assert(child != null);
     child.layout(constraints, parentUsesSize: true);
     return child.size;
   }
 
   /// Specify the child's origin relative to this origin.
-  void positionChild(int childIndex, Point position) {
-    final RenderBox child = _indexToChild[childIndex];
-    final _MultiChildParentData childParentData = child.parentData;
+  void positionChild(Object childId, Point position) {
+    final RenderBox child = _idToChild[childId];
+    assert(child != null);
+    final MultiChildLayoutParentData childParentData = child.parentData;
     childParentData.position = position;
   }
 
   void _callPerformLayout(Size size, BoxConstraints constraints, RenderBox firstChild) {
     RenderBox child = firstChild;
     while (child != null) {
-      _indexToChild.add(child);
-      final _MultiChildParentData childParentData = child.parentData;
+      final MultiChildLayoutParentData childParentData = child.parentData;
+      assert(childParentData.id != null);
+      _idToChild[childParentData.id] = child;
       child = childParentData.nextSibling;
     }
-    performLayout(size, constraints, _indexToChild.length);
-    _indexToChild.clear();
+    performLayout(size, constraints);
+    _idToChild.clear();
   }
 
   /// Layout and position all children given this widget's size and the specified
   /// constraints. This method must apply layoutChild() to each child. It should
   /// specify the final position of each child with positionChild().
-  void performLayout(Size size, BoxConstraints constraints, int childCount);
+  void performLayout(Size size, BoxConstraints constraints);
 }
 
 class RenderCustomMultiChildLayoutBox extends RenderBox
-  with ContainerRenderObjectMixin<RenderBox, _MultiChildParentData>,
-       RenderBoxContainerDefaultsMixin<RenderBox, _MultiChildParentData> {
+  with ContainerRenderObjectMixin<RenderBox, MultiChildLayoutParentData>,
+       RenderBoxContainerDefaultsMixin<RenderBox, MultiChildLayoutParentData> {
   RenderCustomMultiChildLayoutBox({
     List<RenderBox> children,
     MultiChildLayoutDelegate delegate
@@ -60,8 +68,8 @@
   }
 
   void setupParentData(RenderBox child) {
-    if (child.parentData is! _MultiChildParentData)
-      child.parentData = new _MultiChildParentData();
+    if (child.parentData is! MultiChildLayoutParentData)
+      child.parentData = new MultiChildLayoutParentData();
   }
 
   MultiChildLayoutDelegate get delegate => _delegate;
@@ -108,7 +116,7 @@
     defaultPaint(context, offset);
   }
 
-  void hitTestChildren(HitTestResult result, { Point position }) {
-    defaultHitTestChildren(result, position: position);
+  bool hitTestChildren(HitTestResult result, { Point position }) {
+    return defaultHitTestChildren(result, position: position);
   }
 }
diff --git a/sky/packages/sky/lib/src/rendering/error.dart b/sky/packages/sky/lib/src/rendering/error.dart
index 01a72d0..4e4b8c2 100644
--- a/sky/packages/sky/lib/src/rendering/error.dart
+++ b/sky/packages/sky/lib/src/rendering/error.dart
@@ -30,6 +30,8 @@
 
   bool get sizedByParent => true;
 
+  bool hitTestSelf(Point position) => true;
+
   void performResize() {
     size = constraints.constrain(const Size(_kMaxWidth, _kMaxHeight));
   }
diff --git a/sky/packages/sky/lib/src/rendering/flex.dart b/sky/packages/sky/lib/src/rendering/flex.dart
index d304795..9de7591 100644
--- a/sky/packages/sky/lib/src/rendering/flex.dart
+++ b/sky/packages/sky/lib/src/rendering/flex.dart
@@ -542,8 +542,8 @@
     }
   }
 
-  void hitTestChildren(HitTestResult result, { Point position }) {
-    defaultHitTestChildren(result, position: position);
+  bool hitTestChildren(HitTestResult result, { Point position }) {
+    return defaultHitTestChildren(result, position: position);
   }
 
   void paint(PaintingContext context, Offset offset) {
diff --git a/sky/packages/sky/lib/src/rendering/grid.dart b/sky/packages/sky/lib/src/rendering/grid.dart
index 05c69cc..b22ff83 100644
--- a/sky/packages/sky/lib/src/rendering/grid.dart
+++ b/sky/packages/sky/lib/src/rendering/grid.dart
@@ -134,8 +134,8 @@
     }
   }
 
-  void hitTestChildren(HitTestResult result, { Point position }) {
-    defaultHitTestChildren(result, position: position);
+  bool hitTestChildren(HitTestResult result, { Point position }) {
+    return defaultHitTestChildren(result, position: position);
   }
 
   void paint(PaintingContext context, Offset offset) {
diff --git a/sky/packages/sky/lib/src/rendering/image.dart b/sky/packages/sky/lib/src/rendering/image.dart
index 84b6249..0de513c 100644
--- a/sky/packages/sky/lib/src/rendering/image.dart
+++ b/sky/packages/sky/lib/src/rendering/image.dart
@@ -179,6 +179,8 @@
     return _sizeForConstraints(constraints).height;
   }
 
+  bool hitTestSelf(Point position) => true;
+
   void performLayout() {
     size = _sizeForConstraints(constraints);
   }
diff --git a/sky/packages/sky/lib/src/rendering/overflow.dart b/sky/packages/sky/lib/src/rendering/overflow.dart
index e72a5db..d1cd29d 100644
--- a/sky/packages/sky/lib/src/rendering/overflow.dart
+++ b/sky/packages/sky/lib/src/rendering/overflow.dart
@@ -125,11 +125,8 @@
       child.layout(_getInnerConstraints(constraints));
   }
 
-  void hitTestChildren(HitTestResult result, { Point position }) {
-    if (child != null)
-      child.hitTest(result, position: position);
-    else
-      super.hitTestChildren(result, position: position);
+  bool hitTestChildren(HitTestResult result, { Point position }) {
+    return child?.hitTest(result, position: position) ?? false;
   }
 
   void paint(PaintingContext context, Offset offset) {
@@ -195,11 +192,8 @@
       child.layout(constraints);
   }
 
-  void hitTestChildren(HitTestResult result, { Point position }) {
-    if (child != null)
-      child.hitTest(result, position: position);
-    else
-      super.hitTestChildren(result, position: position);
+  bool hitTestChildren(HitTestResult result, { Point position }) {
+    return child?.hitTest(result, position: position) ?? false;
   }
 
   void paint(PaintingContext context, Offset offset) {
diff --git a/sky/packages/sky/lib/src/rendering/paragraph.dart b/sky/packages/sky/lib/src/rendering/paragraph.dart
index ab7b2eb..7277206 100644
--- a/sky/packages/sky/lib/src/rendering/paragraph.dart
+++ b/sky/packages/sky/lib/src/rendering/paragraph.dart
@@ -96,6 +96,8 @@
     return textPainter.computeDistanceToActualBaseline(baseline);
   }
 
+  bool hitTestSelf(Point position) => true;
+
   void performLayout() {
     layoutText(constraints);
     size = constraints.constrain(textPainter.size);
diff --git a/sky/packages/sky/lib/src/rendering/proxy_box.dart b/sky/packages/sky/lib/src/rendering/proxy_box.dart
index 2bf0993..27e3fbc 100644
--- a/sky/packages/sky/lib/src/rendering/proxy_box.dart
+++ b/sky/packages/sky/lib/src/rendering/proxy_box.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'dart:math' as math;
 import 'dart:ui' as ui;
 
 import 'package:flutter/painting.dart';
@@ -70,11 +71,8 @@
     }
   }
 
-  void hitTestChildren(HitTestResult result, { Point position }) {
-    if (child != null)
-      child.hitTest(result, position: position);
-    else
-      super.hitTestChildren(result, position: position);
+  bool hitTestChildren(HitTestResult result, { Point position }) {
+    return child?.hitTest(result, position: position) ?? false;
   }
 
   void paint(PaintingContext context, Offset offset) {
@@ -625,6 +623,15 @@
     return _cachedPath;
   }
 
+  bool hitTest(HitTestResult result, { Point position }) {
+    Point center = size.center(Point.origin);
+    Offset offset = new Offset((position.x - center.x) / size.width,
+                               (position.y - center.y) / size.height);
+    if (offset.distance > 0.5)
+      return false;
+    return super.hitTest(result, position: position);
+  }
+
   void paint(PaintingContext context, Offset offset) {
     if (child != null) {
       Rect rect = offset & size;
@@ -706,6 +713,19 @@
     super.detach();
   }
 
+  bool hitTestSelf(Point position) {
+    switch (_painter.decoration.shape) {
+      case Shape.rectangle:
+        // TODO(abarth): We should check the border radius.
+        return true;
+      case Shape.circle:
+        // Circles are inscribed into our smallest dimension.
+        Point center = size.center(Point.origin);
+        double distance = (position - center).distance;
+        return distance <= math.min(size.width, size.height) / 2.0;
+    }
+  }
+
   void paint(PaintingContext context, Offset offset) {
     assert(size.width != null);
     assert(size.height != null);
@@ -894,6 +914,7 @@
 
 /// Called when its time to paint into the given canvas
 typedef void CustomPaintCallback(PaintingCanvas canvas, Size size);
+typedef bool CustomHitTestCallback(Point position);
 
 /// Delegates its painting to [onPaint]
 ///
@@ -911,6 +932,7 @@
 
   RenderCustomPaint({
     CustomPaintCallback onPaint,
+    this.onHitTest,
     RenderBox child
   }) : super(child) {
     assert(onPaint != null);
@@ -931,11 +953,17 @@
     markNeedsPaint();
   }
 
+  CustomHitTestCallback onHitTest;
+
   void attach() {
     assert(_onPaint != null);
     super.attach();
   }
 
+  bool hitTestSelf(Point position) {
+    return onHitTest == null || onHitTest(position);
+  }
+
   void paint(PaintingContext context, Offset offset) {
     assert(_onPaint != null);
     context.canvas.translate(offset.dx, offset.dy);
@@ -949,6 +977,12 @@
 
 typedef void PointerEventListener(PointerInputEvent e);
 
+enum HitTestBehavior {
+  deferToChild,
+  opaque,
+  translucent,
+}
+
 /// Invokes the callbacks in response to pointer events.
 class RenderPointerListener extends RenderProxyBox {
   RenderPointerListener({
@@ -956,6 +990,7 @@
     this.onPointerMove,
     this.onPointerUp,
     this.onPointerCancel,
+    this.behavior: HitTestBehavior.deferToChild,
     RenderBox child
   }) : super(child);
 
@@ -963,6 +998,20 @@
   PointerEventListener onPointerMove;
   PointerEventListener onPointerUp;
   PointerEventListener onPointerCancel;
+  HitTestBehavior behavior;
+
+  bool hitTest(HitTestResult result, { Point position }) {
+    bool hitTarget = false;
+    if (position.x >= 0.0 && position.x < size.width &&
+        position.y >= 0.0 && position.y < size.height) {
+      hitTarget = hitTestChildren(result, position: position) || hitTestSelf(position);
+      if (hitTarget || behavior == HitTestBehavior.translucent)
+        result.add(new BoxHitTestEntry(this, position));
+    }
+    return hitTarget;
+  }
+
+  bool hitTestSelf(Point position) => behavior == HitTestBehavior.opaque;
 
   void handleEvent(InputEvent event, HitTestEntry entry) {
     if (onPointerDown != null && event.type == 'pointerdown')
@@ -989,6 +1038,8 @@
     if (listeners.isEmpty)
       listeners.add('<none>');
     settings.add('listeners: ${listeners.join(", ")}');
+    if (behavior != HitTestBehavior.deferToChild)
+      settings.add('behavior: $behavior');
   }
 }
 
diff --git a/sky/packages/sky/lib/src/rendering/shifted_box.dart b/sky/packages/sky/lib/src/rendering/shifted_box.dart
index 63901e0..a2b2886 100644
--- a/sky/packages/sky/lib/src/rendering/shifted_box.dart
+++ b/sky/packages/sky/lib/src/rendering/shifted_box.dart
@@ -60,12 +60,14 @@
     }
   }
 
-  void hitTestChildren(HitTestResult result, { Point position }) {
+  bool hitTestChildren(HitTestResult result, { Point position }) {
     if (child != null) {
       final BoxParentData childParentData = child.parentData;
-      child.hitTest(result, position: new Point(position.x - childParentData.position.x,
-                                                position.y - childParentData.position.y));
+      final Point childPosition = new Point(position.x - childParentData.position.x,
+                                            position.y - childParentData.position.y);
+      return child.hitTest(result, position: childPosition);
     }
+    return false;
   }
 
 }
@@ -141,13 +143,6 @@
   }
 }
 
-enum ShrinkWrap {
-  width,
-  height,
-  both,
-  none
-}
-
 class RenderPositionedBox extends RenderShiftedBox {
 
   // This box aligns a child box within itself. It's only useful for
@@ -159,49 +154,57 @@
   RenderPositionedBox({
     RenderBox child,
     FractionalOffset alignment: const FractionalOffset(0.5, 0.5),
-    ShrinkWrap shrinkWrap: ShrinkWrap.none
+    double widthFactor,
+    double heightFactor
   }) : _alignment = alignment,
-       _shrinkWrap = shrinkWrap,
+       _widthFactor = widthFactor,
+       _heightFactor = heightFactor,
        super(child) {
     assert(alignment != null);
-    assert(shrinkWrap != null);
   }
 
   FractionalOffset get alignment => _alignment;
   FractionalOffset _alignment;
   void set alignment (FractionalOffset newAlignment) {
-    assert(newAlignment == null || (newAlignment.x != null && newAlignment.y != null));
+    assert(newAlignment != null && newAlignment.x != null && newAlignment.y != null);
     if (_alignment == newAlignment)
       return;
     _alignment = newAlignment;
     markNeedsLayout();
   }
 
-  ShrinkWrap _shrinkWrap;
-  ShrinkWrap get shrinkWrap => _shrinkWrap;
-  void set shrinkWrap (ShrinkWrap value) {
-    assert(value != null);
-    if (_shrinkWrap == value)
+  double _widthFactor;
+  double get widthFactor => _widthFactor;
+  void set widthFactor (double value) {
+    if (_widthFactor == value)
       return;
-    _shrinkWrap = value;
+    _widthFactor = value;
     markNeedsLayout();
   }
 
-  // These are only valid during performLayout() and paint(), since they rely on constraints which is only set after layout() is called.
-  bool get _shinkWrapWidth => _shrinkWrap == ShrinkWrap.width || _shrinkWrap == ShrinkWrap.both || constraints.maxWidth == double.INFINITY;
-  bool get _shinkWrapHeight => _shrinkWrap == ShrinkWrap.height || _shrinkWrap == ShrinkWrap.both || constraints.maxHeight == double.INFINITY;
+  double _heightFactor;
+  double get heightFactor => _heightFactor;
+  void set heightFactor (double value) {
+    if (_heightFactor == value)
+      return;
+    _heightFactor = value;
+    markNeedsLayout();
+  }
 
   void performLayout() {
+    final bool shrinkWrapWidth = _widthFactor != null || constraints.maxWidth == double.INFINITY;
+    final bool shrinkWrapHeight = _heightFactor != null || constraints.maxHeight == double.INFINITY;
+
     if (child != null) {
       child.layout(constraints.loosen(), parentUsesSize: true);
-      size = constraints.constrain(new Size(_shinkWrapWidth ? child.size.width : double.INFINITY,
-                                            _shinkWrapHeight ? child.size.height : double.INFINITY));
-      Offset delta = size - child.size;
+      size = constraints.constrain(new Size(shrinkWrapWidth ? child.size.width * (_widthFactor ?? 1.0) : double.INFINITY,
+                                            shrinkWrapHeight ? child.size.height * (_heightFactor ?? 1.0) : double.INFINITY));
+      final Offset delta = size - child.size;
       final BoxParentData childParentData = child.parentData;
-      childParentData.position = (delta.scale(_alignment.x, _alignment.y)).toPoint();
+      childParentData.position = delta.scale(_alignment.x, _alignment.y).toPoint();
     } else {
-      size = constraints.constrain(new Size(_shinkWrapWidth ? 0.0 : double.INFINITY,
-                                            _shinkWrapHeight ? 0.0 : double.INFINITY));
+      size = constraints.constrain(new Size(shrinkWrapWidth ? 0.0 : double.INFINITY,
+                                            shrinkWrapHeight ? 0.0 : double.INFINITY));
     }
   }
 
diff --git a/sky/packages/sky/lib/src/rendering/stack.dart b/sky/packages/sky/lib/src/rendering/stack.dart
index da97748..40db7cc 100644
--- a/sky/packages/sky/lib/src/rendering/stack.dart
+++ b/sky/packages/sky/lib/src/rendering/stack.dart
@@ -357,8 +357,8 @@
     }
   }
 
-  void hitTestChildren(HitTestResult result, { Point position }) {
-    defaultHitTestChildren(result, position: position);
+  bool hitTestChildren(HitTestResult result, { Point position }) {
+    return defaultHitTestChildren(result, position: position);
   }
 
   void paintStack(PaintingContext context, Offset offset);
@@ -455,15 +455,15 @@
     return child;
   }
 
-  void hitTestChildren(HitTestResult result, { Point position }) {
+  bool hitTestChildren(HitTestResult result, { Point position }) {
     if (firstChild == null)
-      return;
+      return false;
     assert(position != null);
     RenderBox child = _childAtIndex();
     final StackParentData childParentData = child.parentData;
     Point transformed = new Point(position.x - childParentData.position.x,
                                   position.y - childParentData.position.y);
-    child.hitTest(result, position: transformed);
+    return child.hitTest(result, position: transformed);
   }
 
   void paintStack(PaintingContext context, Offset offset) {
diff --git a/sky/packages/sky/lib/src/rendering/toggleable.dart b/sky/packages/sky/lib/src/rendering/toggleable.dart
index 7e7e1ef..5c9d22d 100644
--- a/sky/packages/sky/lib/src/rendering/toggleable.dart
+++ b/sky/packages/sky/lib/src/rendering/toggleable.dart
@@ -73,4 +73,6 @@
     if (onChanged != null)
       onChanged(!_value);
   }
+
+  bool hitTestSelf(Point position) => true;
 }
diff --git a/sky/packages/sky/lib/src/rendering/viewport.dart b/sky/packages/sky/lib/src/rendering/viewport.dart
index 942dbcb..03f317e 100644
--- a/sky/packages/sky/lib/src/rendering/viewport.dart
+++ b/sky/packages/sky/lib/src/rendering/viewport.dart
@@ -160,11 +160,12 @@
     transform.translate(-scrollOffset.dx, -scrollOffset.dy);
   }
 
-  void hitTestChildren(HitTestResult result, { Point position }) {
+  bool hitTestChildren(HitTestResult result, { Point position }) {
     if (child != null) {
       assert(child.parentData is BoxParentData);
       Point transformed = position + _scrollOffsetRoundedToIntegerDevicePixels;
-      child.hitTest(result, position: transformed);
+      return child.hitTest(result, position: transformed);
     }
+    return false;
   }
 }
diff --git a/sky/packages/sky/lib/src/widgets/basic.dart b/sky/packages/sky/lib/src/widgets/basic.dart
index d36e357..a99e007 100644
--- a/sky/packages/sky/lib/src/widgets/basic.dart
+++ b/sky/packages/sky/lib/src/widgets/basic.dart
@@ -31,6 +31,7 @@
     FontWeight,
     FractionalOffset,
     Gradient,
+    HitTestBehavior,
     ImageFit,
     ImageRepeat,
     InputEvent,
@@ -47,7 +48,6 @@
     Rect,
     ScrollDirection,
     Shape,
-    ShrinkWrap,
     Size,
     StyledTextSpan,
     TextAlign,
@@ -138,7 +138,7 @@
 }
 
 class CustomPaint extends OneChildRenderObjectWidget {
-  CustomPaint({ Key key, this.onPaint, this.token, Widget child })
+  CustomPaint({ Key key, this.onPaint, this.onHitTest, this.token, Widget child })
     : super(key: key, child: child) {
     assert(onPaint != null);
   }
@@ -151,19 +151,23 @@
   /// has a more stable identity.
   final CustomPaintCallback onPaint;
 
+  final CustomHitTestCallback onHitTest;
+
   /// This widget repaints whenever you supply a new token.
   final Object token;
 
-  RenderCustomPaint createRenderObject() => new RenderCustomPaint(onPaint: onPaint);
+  RenderCustomPaint createRenderObject() => new RenderCustomPaint(onPaint: onPaint, onHitTest: onHitTest);
 
   void updateRenderObject(RenderCustomPaint renderObject, CustomPaint oldWidget) {
     if (oldWidget.token != token)
       renderObject.markNeedsPaint();
     renderObject.onPaint = onPaint;
+    renderObject.onHitTest = onHitTest;
   }
 
   void didUnmountRenderObject(RenderCustomPaint renderObject) {
     renderObject.onPaint = null;
+    renderObject.onHitTest = null;
   }
 }
 
@@ -233,26 +237,27 @@
   Align({
     Key key,
     this.alignment: const FractionalOffset(0.5, 0.5),
-    this.shrinkWrap: ShrinkWrap.none,
+    this.widthFactor,
+    this.heightFactor,
     Widget child
-  }) : super(key: key, child: child) {
-    assert(shrinkWrap != null);
-  }
+  }) : super(key: key, child: child);
 
   final FractionalOffset alignment;
-  final ShrinkWrap shrinkWrap;
+  final double widthFactor;
+  final double heightFactor;
 
-  RenderPositionedBox createRenderObject() => new RenderPositionedBox(alignment: alignment, shrinkWrap: shrinkWrap);
+  RenderPositionedBox createRenderObject() => new RenderPositionedBox(alignment: alignment, widthFactor: widthFactor, heightFactor: heightFactor);
 
   void updateRenderObject(RenderPositionedBox renderObject, Align oldWidget) {
     renderObject.alignment = alignment;
-    renderObject.shrinkWrap = shrinkWrap;
+    renderObject.widthFactor = widthFactor;
+    renderObject.heightFactor = heightFactor;
   }
 }
 
 class Center extends Align {
-  Center({ Key key, ShrinkWrap shrinkWrap: ShrinkWrap.none, Widget child })
-    : super(key: key, shrinkWrap: shrinkWrap, child: child);
+  Center({ Key key, widthFactor, heightFactor, Widget child })
+    : super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);
 }
 
 class CustomOneChildLayout extends OneChildRenderObjectWidget {
@@ -284,6 +289,39 @@
   }
 }
 
+class LayoutId extends ParentDataWidget {
+  LayoutId({
+    Key key,
+    Widget child,
+    this.id
+  }) : super(key: key, child: child);
+
+  final Object id;
+
+  void debugValidateAncestor(Widget ancestor) {
+    assert(() {
+      'LayoutId must placed inside a CustomMultiChildLayout';
+      return ancestor is CustomMultiChildLayout;
+    });
+  }
+
+  void applyParentData(RenderObject renderObject) {
+    assert(renderObject.parentData is MultiChildLayoutParentData);
+    final MultiChildLayoutParentData parentData = renderObject.parentData;
+    if (parentData.id != id) {
+      parentData.id = id;
+      AbstractNode targetParent = renderObject.parent;
+      if (targetParent is RenderObject)
+        targetParent.markNeedsLayout();
+    }
+  }
+
+  void debugFillDescription(List<String> description) {
+    super.debugFillDescription(description);
+    description.add('id: $id');
+  }
+}
+
 class CustomMultiChildLayout extends MultiChildRenderObjectWidget {
   CustomMultiChildLayout(List<Widget> children, {
     Key key,
@@ -549,6 +587,7 @@
   }) : super(key: key) {
     assert(margin == null || margin.isNonNegative);
     assert(padding == null || padding.isNonNegative);
+    assert(decoration == null || decoration.shape != Shape.circle || decoration.borderRadius == null); // can't have a border radius if you're a circle
   }
 
   final Widget child;
@@ -1192,19 +1231,24 @@
     this.onPointerDown,
     this.onPointerMove,
     this.onPointerUp,
-    this.onPointerCancel
-  }) : super(key: key, child: child);
+    this.onPointerCancel,
+    this.behavior: HitTestBehavior.deferToChild
+  }) : super(key: key, child: child) {
+    assert(behavior != null);
+  }
 
   final PointerEventListener onPointerDown;
   final PointerEventListener onPointerMove;
   final PointerEventListener onPointerUp;
   final PointerEventListener onPointerCancel;
+  final HitTestBehavior behavior;
 
   RenderPointerListener createRenderObject() => new RenderPointerListener(
     onPointerDown: onPointerDown,
     onPointerMove: onPointerMove,
     onPointerUp: onPointerUp,
-    onPointerCancel: onPointerCancel
+    onPointerCancel: onPointerCancel,
+    behavior: behavior
   );
 
   void updateRenderObject(RenderPointerListener renderObject, Listener oldWidget) {
@@ -1212,6 +1256,7 @@
     renderObject.onPointerMove = onPointerMove;
     renderObject.onPointerUp = onPointerUp;
     renderObject.onPointerCancel = onPointerCancel;
+    renderObject.behavior = behavior;
   }
 }
 
diff --git a/sky/packages/sky/lib/src/widgets/binding.dart b/sky/packages/sky/lib/src/widgets/binding.dart
index b6935e3..5f9eb53 100644
--- a/sky/packages/sky/lib/src/widgets/binding.dart
+++ b/sky/packages/sky/lib/src/widgets/binding.dart
@@ -22,9 +22,9 @@
 
   static WidgetFlutterBinding get instance => FlutterBinding.instance;
 
-  void beginFrame(Duration timeStamp) {
+  void beginFrame() {
     buildDirtyElements();
-    super.beginFrame(timeStamp);
+    super.beginFrame();
     Element.finalizeTree();
   }
 
@@ -74,6 +74,7 @@
       container: renderView,
       child: app
     ).attachToRenderTree(_renderViewElement);
+    beginFrame();
   }
 }
 
diff --git a/sky/packages/sky/lib/src/widgets/drag_target.dart b/sky/packages/sky/lib/src/widgets/drag_target.dart
index 99abd50..92a6be0 100644
--- a/sky/packages/sky/lib/src/widgets/drag_target.dart
+++ b/sky/packages/sky/lib/src/widgets/drag_target.dart
@@ -4,7 +4,9 @@
 
 import 'dart:collection';
 
+import 'package:flutter/gestures.dart';
 import 'package:flutter/rendering.dart';
+import 'package:flutter/services.dart';
 
 import 'basic.dart';
 import 'binding.dart';
@@ -15,6 +17,16 @@
 typedef bool DragTargetWillAccept<T>(T data);
 typedef void DragTargetAccept<T>(T data);
 typedef Widget DragTargetBuilder<T>(BuildContext context, List<T> candidateData, List<dynamic> rejectedData);
+typedef void DragStartCallback(Point position, int pointer);
+
+typedef DraggableBase<T> DraggableConstructor<T>({
+  Key key,
+  T data,
+  Widget child,
+  Widget feedback,
+  Offset feedbackOffset,
+  DragAnchor dragAnchor
+});
 
 enum DragAnchor {
   /// Display the feedback anchored at the position of the original child. If
@@ -35,8 +47,8 @@
   pointer,
 }
 
-class Draggable extends StatefulComponent {
-  Draggable({
+abstract class DraggableBase<T> extends StatefulComponent {
+  DraggableBase({
     Key key,
     this.data,
     this.child,
@@ -48,7 +60,7 @@
     assert(feedback != null);
   }
 
-  final dynamic data;
+  final T data;
   final Widget child;
   final Widget feedback;
 
@@ -58,69 +70,122 @@
   final Offset feedbackOffset;
   final DragAnchor dragAnchor;
 
-  _DraggableState createState() => new _DraggableState();
+  /// Should return a GestureRecognizer instance that is configured to call the starter
+  /// argument when the drag is to begin. The arena for the pointer must not yet have
+  /// resolved at the time that the callback is invoked, because the draggable itself
+  /// is going to attempt to win the pointer's arena in that case.
+  GestureRecognizer createRecognizer(PointerRouter router, DragStartCallback starter);
+
+  _DraggableState<T> createState() => new _DraggableState<T>();
 }
 
-class _DraggableState extends State<Draggable> {
-  _DragAvatar _avatar;
+class Draggable<T> extends DraggableBase<T> {
+  Draggable({
+    Key key,
+    T data,
+    Widget child,
+    Widget feedback,
+    Offset feedbackOffset: Offset.zero,
+    DragAnchor dragAnchor: DragAnchor.child
+  }) : super(
+    key: key,
+    data: data,
+    child: child,
+    feedback: feedback,
+    feedbackOffset: feedbackOffset,
+    dragAnchor: dragAnchor
+  );
 
-  void _startDrag(PointerInputEvent event) {
-    if (_avatar != null)
-      return; // TODO(ianh): once we switch to using gestures, just hand the gesture to the avatar so it can do everything itself. then we can have multiple drags at the same time.
-    final Point point = new Point(event.x, event.y);
+  GestureRecognizer createRecognizer(PointerRouter router, DragStartCallback starter) {
+    return new MultiTapGestureRecognizer(
+      router: router,
+      onTapDown: starter
+    );
+  }
+}
+
+class LongPressDraggable<T> extends DraggableBase<T> {
+  LongPressDraggable({
+    Key key,
+    T data,
+    Widget child,
+    Widget feedback,
+    Offset feedbackOffset: Offset.zero,
+    DragAnchor dragAnchor: DragAnchor.child
+  }) : super(
+    key: key,
+    data: data,
+    child: child,
+    feedback: feedback,
+    feedbackOffset: feedbackOffset,
+    dragAnchor: dragAnchor
+  );
+
+  GestureRecognizer createRecognizer(PointerRouter router, DragStartCallback starter) {
+    return new MultiTapGestureRecognizer(
+      router: router,
+      longTapDelay: kLongPressTimeout,
+      onLongTapDown: (Point position, int pointer) {
+        userFeedback.performHapticFeedback(HapticFeedbackType.VIRTUAL_KEY);
+        starter(position, pointer);
+      }
+    );
+  }
+}
+
+class _DraggableState<T> extends State<DraggableBase<T>> implements GestureArenaMember {
+
+  PointerRouter get router => FlutterBinding.instance.pointerRouter;
+
+  void initState() {
+    super.initState();
+    _recognizer = config.createRecognizer(router, _startDrag);
+  }
+
+  GestureRecognizer _recognizer;
+  Map<int, GestureArenaEntry> _activePointers = <int, GestureArenaEntry>{};
+
+  void _routePointer(PointerInputEvent event) {
+    _activePointers[event.pointer] = GestureArena.instance.add(event.pointer, this);
+    _recognizer.addPointer(event);
+  }
+
+  void acceptGesture(int pointer) {
+    _activePointers.remove(pointer);
+  }
+
+  void rejectGesture(int pointer) {
+    _activePointers.remove(pointer);
+  }
+
+  void _startDrag(Point position, int pointer) {
+    assert(_activePointers.containsKey(pointer));
+    _activePointers[pointer].resolve(GestureDisposition.accepted);
     Point dragStartPoint;
     switch (config.dragAnchor) {
       case DragAnchor.child:
         final RenderBox renderObject = context.findRenderObject();
-        dragStartPoint = renderObject.globalToLocal(point);
+        dragStartPoint = renderObject.globalToLocal(position);
         break;
       case DragAnchor.pointer:
         dragStartPoint = Point.origin;
-        break;
+      break;
     }
-    assert(dragStartPoint != null);
-    _avatar = new _DragAvatar(
+    new _DragAvatar<T>(
+      pointer: pointer,
+      router: router,
+      overlay: Navigator.of(context).overlay,
       data: config.data,
+      initialPosition: position,
       dragStartPoint: dragStartPoint,
       feedback: config.feedback,
-      feedbackOffset: config.feedbackOffset,
-      onDragFinished: () {
-        _avatar = null;
-      }
+      feedbackOffset: config.feedbackOffset
     );
-    _avatar.update(point);
-    _avatar.markNeedsBuild(context);
-  }
-
-  void _updateDrag(PointerInputEvent event) {
-    if (_avatar != null) {
-      _avatar.update(new Point(event.x, event.y));
-      _avatar.markNeedsBuild(context);
-    }
-  }
-
-  void _cancelDrag(PointerInputEvent event) {
-    if (_avatar != null) {
-      _avatar.finish(_DragEndKind.canceled);
-      assert(_avatar == null);
-    }
-  }
-
-  void _drop(PointerInputEvent event) {
-    if (_avatar != null) {
-      _avatar.update(new Point(event.x, event.y));
-      _avatar.finish(_DragEndKind.dropped);
-      assert(_avatar == null);
-    }
   }
 
   Widget build(BuildContext context) {
-    // TODO(abarth): We should be using a GestureDetector
     return new Listener(
-      onPointerDown: _startDrag,
-      onPointerMove: _updateDrag,
-      onPointerCancel: _cancelDrag,
-      onPointerUp: _drop,
+      onPointerDown: _routePointer,
       child: config.child
     );
   }
@@ -181,7 +246,8 @@
       metaData: this,
       child: config.builder(context,
                             new UnmodifiableListView<T>(_candidateData),
-                            new UnmodifiableListView<dynamic>(_rejectedData))
+                            new UnmodifiableListView<dynamic>(_rejectedData)
+      )
     );
   }
 }
@@ -189,30 +255,63 @@
 
 enum _DragEndKind { dropped, canceled }
 
-class _DragAvatar {
+// The lifetime of this object is a little dubious right now. Specifically, it
+// lives as long as the pointer is down. Arguably it should self-immolate if the
+// overlay goes away, or maybe even if the Draggable that created goes away.
+// This will probably need to be changed once we have more experience with using
+// this widget.
+class _DragAvatar<T> {
   _DragAvatar({
+    this.pointer,
+    this.router,
+    OverlayState overlay,
     this.data,
+    Point initialPosition,
     this.dragStartPoint: Point.origin,
     this.feedback,
-    this.feedbackOffset: Offset.zero,
-    this.onDragFinished
+    this.feedbackOffset: Offset.zero
   }) {
+    assert(pointer != null);
+    assert(router != null);
+    assert(overlay != null);
+    assert(dragStartPoint != null);
     assert(feedbackOffset != null);
+    router.addRoute(pointer, handleEvent);
+    _entry = new OverlayEntry(builder: _build);
+    overlay.insert(_entry);
+    update(initialPosition);
   }
 
-  final dynamic data;
+  final int pointer;
+  final PointerRouter router;
+  final T data;
   final Point dragStartPoint;
   final Widget feedback;
   final Offset feedbackOffset;
-  final VoidCallback onDragFinished;
 
   DragTargetState _activeTarget;
   bool _activeTargetWillAcceptDrop = false;
   Offset _lastOffset;
   OverlayEntry _entry;
 
+  void handleEvent(PointerInputEvent event) {
+    switch(event.type) {
+      case 'pointerup':
+        update(event.position);
+        finish(_DragEndKind.dropped);
+        break;
+      case 'pointercancel':
+        finish(_DragEndKind.canceled);
+        break;
+      case 'pointermove':
+        update(event.position);
+        break;
+    }
+  }
+
   void update(Point globalPosition) {
     _lastOffset = globalPosition - dragStartPoint;
+    _entry.markNeedsBuild();
     HitTestResult result = WidgetFlutterBinding.instance.hitTest(globalPosition + feedbackOffset);
     DragTargetState target = _getDragTarget(result.path);
     if (target == _activeTarget)
@@ -223,18 +322,10 @@
     _activeTargetWillAcceptDrop = _activeTarget != null && _activeTarget.didEnter(data);
   }
 
-  void markNeedsBuild(BuildContext context) {
-    if (_entry == null) {
-      _entry = new OverlayEntry(builder: _build);
-      Navigator.of(context).overlay.insert(_entry);
-    } else {
-      _entry.markNeedsBuild();
-    }
-  }
-
   DragTargetState _getDragTarget(List<HitTestEntry> path) {
-    // TODO(abarth): Why do we reverse the path here?
-    for (HitTestEntry entry in path.reversed) {
+    // Look for the RenderBox that corresponds to the hit target (the hit target
+    // widget builds a RenderMetadata box for us for this purpose).
+    for (HitTestEntry entry in path) {
       if (entry.target is RenderMetaData) {
         RenderMetaData renderMetaData = entry.target;
         if (renderMetaData.metaData is DragTargetState)
@@ -255,8 +346,7 @@
     _activeTargetWillAcceptDrop = false;
     _entry.remove();
     _entry = null;
-    if (onDragFinished != null)
-      onDragFinished();
+    router.removeRoute(pointer, handleEvent);
   }
 
   Widget _build(BuildContext context) {
diff --git a/sky/packages/sky/lib/src/widgets/gesture_detector.dart b/sky/packages/sky/lib/src/widgets/gesture_detector.dart
index 97d2f3d..e62764e 100644
--- a/sky/packages/sky/lib/src/widgets/gesture_detector.dart
+++ b/sky/packages/sky/lib/src/widgets/gesture_detector.dart
@@ -48,7 +48,8 @@
     this.onPanEnd,
     this.onScaleStart,
     this.onScaleUpdate,
-    this.onScaleEnd
+    this.onScaleEnd,
+    this.behavior
   }) : super(key: key);
 
   final Widget child;
@@ -77,11 +78,13 @@
   final GestureScaleUpdateCallback onScaleUpdate;
   final GestureScaleEndCallback onScaleEnd;
 
+  final HitTestBehavior behavior;
+
   _GestureDetectorState createState() => new _GestureDetectorState();
 }
 
 class _GestureDetectorState extends State<GestureDetector> {
-  final PointerRouter _router = FlutterBinding.instance.pointerRouter;
+  PointerRouter get _router => FlutterBinding.instance.pointerRouter;
 
   TapGestureRecognizer _tap;
   DoubleTapGestureRecognizer _doubleTap;
@@ -224,9 +227,14 @@
       _scale.addPointer(event);
   }
 
+  HitTestBehavior get _defaultBehavior {
+    return config.child == null ? HitTestBehavior.translucent : HitTestBehavior.deferToChild;
+  }
+
   Widget build(BuildContext context) {
     return new Listener(
       onPointerDown: _handlePointerDown,
+      behavior: config.behavior ?? _defaultBehavior,
       child: config.child
     );
   }
diff --git a/sky/packages/sky/lib/src/widgets/hero_controller.dart b/sky/packages/sky/lib/src/widgets/hero_controller.dart
index 14cce39..fb44db9 100644
--- a/sky/packages/sky/lib/src/widgets/hero_controller.dart
+++ b/sky/packages/sky/lib/src/widgets/hero_controller.dart
@@ -20,9 +20,23 @@
   }) : super(builder: builder, settings: settings);
 
   final HeroController heroController;
+  NavigatorState _navigator;
 
-  void didMakeCurrent() {
-    heroController?.didMakeCurrent(this);
+  void didPush(OverlayState overlay, OverlayEntry insertionPoint) {
+    super.didPush(overlay, insertionPoint);
+    // TODO(abarth): Pass the NavigatorState explicitly.
+    if (overlay != null) {
+      _navigator = Navigator.of(overlay.context);
+      heroController?.didPush(_navigator, this);
+    }
+  }
+
+  void didPop(dynamic result) {
+    super.didPop(result);
+    if (_navigator != null) {
+      heroController?.didPop(_navigator, this);
+      _navigator = null;
+    }
   }
 }
 
@@ -32,29 +46,47 @@
   }
 
   HeroParty _party;
+  PerformanceView _performance;
   HeroPageRoute _from;
   HeroPageRoute _to;
 
   final List<OverlayEntry> _overlayEntries = new List<OverlayEntry>();
 
-  void didMakeCurrent(PageRoute current) {
-    assert(current != null);
-    assert(current.performance != null);
-    if (_from == null) {
-      _from = current;
-      return;
+  void didPush(NavigatorState navigator, HeroPageRoute route) {
+    assert(route != null);
+    assert(route.performance != null);
+    Route from = navigator.currentRoute;
+    if (from is HeroPageRoute)
+      _from = from;
+    _to = route;
+    _performance = route.performance;
+    _checkForHeroQuest();
+  }
+
+  void didPop(NavigatorState navigator, HeroPageRoute route) {
+    assert(route != null);
+    assert(route.performance != null);
+    Route to = navigator.currentRoute;
+    if (to is HeroPageRoute) {
+      _to = to;
+      _from = route;
+      _performance = route.performance;
+      _checkForHeroQuest();
     }
-    _to = current;
-    if (_from != _to) {
-      current.offstage = current.performance.status != PerformanceStatus.completed;
+  }
+
+  void _checkForHeroQuest() {
+    if (_from != null && _to != null && _from != _to) {
+      _to.offstage = _to.performance.status != PerformanceStatus.completed;
       scheduler.requestPostFrameCallback(_updateQuest);
     }
   }
 
   void _handleQuestFinished() {
     _removeHeroesFromOverlay();
-    _from = _to;
+    _from = null;
     _to = null;
+    _performance = null;
   }
 
   Rect _getAnimationArea(BuildContext context) {
@@ -97,7 +129,7 @@
     Map<Object, HeroHandle> heroesTo = Hero.of(context, mostValuableKeys);
     _to.offstage = false;
 
-    PerformanceView performance = _to.performance;
+    PerformanceView performance = _performance;
     Curve curve = Curves.ease;
     if (performance.status == PerformanceStatus.reverse) {
       performance = new ReversePerformance(performance);
diff --git a/sky/packages/sky/lib/src/widgets/heroes.dart b/sky/packages/sky/lib/src/widgets/heroes.dart
index 0dee9ab..d0b826a 100644
--- a/sky/packages/sky/lib/src/widgets/heroes.dart
+++ b/sky/packages/sky/lib/src/widgets/heroes.dart
@@ -366,14 +366,17 @@
 
   PerformanceView _currentPerformance;
 
+  void _clearCurrentPerformance() {
+    _currentPerformance?.removeStatusListener(_handleUpdate);
+    _currentPerformance = null;
+  }
+
   Iterable<Widget> getWidgets(BuildContext context, PerformanceView performance) sync* {
     assert(performance != null || _heroes.length == 0);
     if (performance != _currentPerformance) {
-      if (_currentPerformance != null)
-        _currentPerformance.removeStatusListener(_handleUpdate);
+      _clearCurrentPerformance();
       _currentPerformance = performance;
-      if (_currentPerformance != null)
-        _currentPerformance.addStatusListener(_handleUpdate);
+      _currentPerformance?.addStatusListener(_handleUpdate);
     }
     for (_HeroQuestState hero in _heroes)
       yield hero.build(context, performance);
@@ -389,7 +392,7 @@
           source._resetChild();
       }
       _heroes.clear();
-      _currentPerformance = null;
+      _clearCurrentPerformance();
       if (onQuestFinished != null)
         onQuestFinished();
     }
diff --git a/sky/packages/sky/lib/src/widgets/modal_barrier.dart b/sky/packages/sky/lib/src/widgets/modal_barrier.dart
index 6b2cd17..d11ab19 100644
--- a/sky/packages/sky/lib/src/widgets/modal_barrier.dart
+++ b/sky/packages/sky/lib/src/widgets/modal_barrier.dart
@@ -17,29 +17,23 @@
 class ModalBarrier extends StatelessComponent {
   ModalBarrier({
     Key key,
-    this.color
+    this.color: _kTransparent
   }) : super(key: key);
 
   final Color color;
 
   Widget build(BuildContext context) {
-    Widget child;
-
-    if (color != null) {
-      child = new DecoratedBox(
-        decoration: new BoxDecoration(
-          backgroundColor: color
-        )
-      );
-    }
-
     return new Listener(
       onPointerDown: (_) {
         Navigator.of(context).pop();
       },
       child: new ConstrainedBox(
         constraints: const BoxConstraints.expand(),
-        child: child
+        child: new DecoratedBox(
+          decoration: new BoxDecoration(
+            backgroundColor: color
+          )
+        )
       )
     );
   }
diff --git a/sky/packages/sky/lib/src/widgets/navigator.dart b/sky/packages/sky/lib/src/widgets/navigator.dart
index 22a2cc3..8223aac 100644
--- a/sky/packages/sky/lib/src/widgets/navigator.dart
+++ b/sky/packages/sky/lib/src/widgets/navigator.dart
@@ -5,25 +5,15 @@
 import 'framework.dart';
 import 'overlay.dart';
 
-// ---------------- Begin scaffolding for Navigator1 to Navigator2 transition
-class RouteArguments {
-  const RouteArguments({ this.context });
-  final BuildContext context;
-}
-typedef Widget RouteBuilder(RouteArguments args);
-typedef RouteBuilder RouteGenerator(String name);
-// ---------------- End scaffolding for Navigator1 to Navigator2 transition
-
 abstract class Route {
   List<OverlayEntry> get overlayEntries;
 
   void didPush(OverlayState overlay, OverlayEntry insertionPoint);
-  void didMakeCurrent();
   void didPop(dynamic result);
 }
 
 class NamedRouteSettings {
-  const NamedRouteSettings({ this.name: '<anonymous>', this.mostValuableKeys });
+  const NamedRouteSettings({ this.name, this.mostValuableKeys });
 
   final String name;
   final Set<Key> mostValuableKeys;
@@ -92,6 +82,7 @@
   }
 
   void pushNamed(String name, { Set<Key> mostValuableKeys }) {
+    assert(name != null);
     NamedRouteSettings settings = new NamedRouteSettings(
       name: name,
       mostValuableKeys: mostValuableKeys
@@ -103,13 +94,11 @@
     _popAllEphemeralRoutes();
     route.didPush(overlay, _currentOverlay);
     _modal.add(route);
-    route.didMakeCurrent();
   }
 
   void pushEphemeral(Route route) {
     route.didPush(overlay, _currentOverlay);
     _ephemeral.add(route);
-    route.didMakeCurrent();
   }
 
   void _popAllEphemeralRoutes() {
@@ -122,7 +111,6 @@
 
   void pop([dynamic result]) {
     _removeCurrentRoute().didPop(result);
-    currentRoute.didMakeCurrent();
   }
 
   Widget build(BuildContext context) {
diff --git a/sky/packages/sky/lib/src/widgets/routes.dart b/sky/packages/sky/lib/src/widgets/routes.dart
index 0d4d9ed..98ec21a 100644
--- a/sky/packages/sky/lib/src/widgets/routes.dart
+++ b/sky/packages/sky/lib/src/widgets/routes.dart
@@ -17,7 +17,6 @@
   List<OverlayEntry> get overlayEntries => const <OverlayEntry>[];
 
   void didPush(OverlayState overlay, OverlayEntry insertionPoint) { }
-  void didMakeCurrent() { }
   void didPop(dynamic result) {
     if (onPop != null)
       onPop();
@@ -38,8 +37,6 @@
     }
   }
 
-  void didMakeCurrent() { }
-
   void didPop(dynamic result) {
     for (OverlayEntry entry in _overlayEntries)
       entry.remove();
diff --git a/sky/packages/sky/lib/src/widgets/scrollable.dart b/sky/packages/sky/lib/src/widgets/scrollable.dart
index f22443e..b5223e1 100644
--- a/sky/packages/sky/lib/src/widgets/scrollable.dart
+++ b/sky/packages/sky/lib/src/widgets/scrollable.dart
@@ -382,6 +382,7 @@
 class Block extends StatelessComponent {
   Block(this.children, {
     Key key,
+    this.padding,
     this.initialScrollOffset,
     this.scrollDirection: ScrollDirection.vertical,
     this.onScroll
@@ -390,6 +391,7 @@
   }
 
   final List<Widget> children;
+  final EdgeDims padding;
   final double initialScrollOffset;
   final ScrollDirection scrollDirection;
   final ScrollListener onScroll;
@@ -401,11 +403,14 @@
   }
 
   Widget build(BuildContext context) {
+    Widget contents = new BlockBody(children, direction: _direction);
+    if (padding != null)
+      contents = new Padding(padding: padding, child: contents);
     return new ScrollableViewport(
       initialScrollOffset: initialScrollOffset,
       scrollDirection: scrollDirection,
       onScroll: onScroll,
-      child: new BlockBody(children, direction: _direction)
+      child: contents
     );
   }
 }
diff --git a/sky/packages/sky/lib/src/widgets/transitions.dart b/sky/packages/sky/lib/src/widgets/transitions.dart
index 6278378..333f87d 100644
--- a/sky/packages/sky/lib/src/widgets/transitions.dart
+++ b/sky/packages/sky/lib/src/widgets/transitions.dart
@@ -177,6 +177,37 @@
   }
 }
 
+class AlignTransition extends TransitionWithChild {
+  AlignTransition({
+    Key key,
+    this.alignment,
+    this.widthFactor,
+    this.heightFactor,
+    PerformanceView performance,
+    Widget child
+  }) : super(key: key,
+             performance: performance,
+             child: child);
+
+  final AnimatedValue<FractionalOffset> alignment;
+  final AnimatedValue<double> widthFactor;
+  final AnimatedValue<double> heightFactor;
+
+  Widget buildWithChild(BuildContext context, Widget child) {
+    if (alignment != null)
+      performance.updateVariable(alignment);
+    if (widthFactor != null)
+      performance.updateVariable(widthFactor);
+    if (heightFactor != null)
+      performance.updateVariable(heightFactor);
+    return new Align(
+      alignment: alignment?.value,
+      widthFactor: widthFactor?.value,
+      heightFactor: heightFactor?.value,
+      child: child);
+  }
+}
+
 /// An animated variable containing a RelativeRectangle
 ///
 /// This class specializes the interpolation of AnimatedValue<RelativeRect> to
diff --git a/sky/packages/sky/pubspec.yaml b/sky/packages/sky/pubspec.yaml
index 98d0f7f..e334c21 100644
--- a/sky/packages/sky/pubspec.yaml
+++ b/sky/packages/sky/pubspec.yaml
@@ -1,5 +1,5 @@
 name: flutter
-version: 0.0.16
+version: 0.0.18
 author: Flutter Authors <flutter-dev@googlegroups.com>
 description: A framework for writing Flutter applications
 homepage: http://flutter.io
@@ -7,13 +7,12 @@
   cassowary: '>=0.1.7 <0.2.0'
   intl: '>=0.12.4+2 <0.13.0'
   material_design_icons: '>=0.0.3 <0.1.0'
-  mojo_sdk: 0.1.0
   newton: '>=0.1.4 <0.2.0'
   sky_engine: 
-    '0.0.47'
+    '0.0.49'
   sky_services: 
-    '0.0.47'
-  sky_tools: '>=0.0.35 <0.1.0'
+    '0.0.50'
+  sky_tools: '>=0.0.37 <0.1.0'
   vector_math: '>=1.4.3 <2.0.0'
 environment:
   sdk: '>=1.12.0 <2.0.0'
diff --git a/sky/packages/sky_engine/BUILD.gn b/sky/packages/sky_engine/BUILD.gn
index 70daa5d..3c7a8c9 100644
--- a/sky/packages/sky_engine/BUILD.gn
+++ b/sky/packages/sky_engine/BUILD.gn
@@ -26,9 +26,14 @@
     "//sky/engine/bindings",
   ]
 
+  service_isolate_dir = "//sky/engine/core/script/dart_service_isolate"
   sdk_ext_directory = "$root_gen_dir/sky/bindings"
   sdk_ext_files = [
     "//sky/engine/bindings/internals.dart",
+    "$service_isolate_dir/main.dart",
+    "$service_isolate_dir/loader.dart",
+    "$service_isolate_dir/resources.dart",
+    "$service_isolate_dir/server.dart",
   ]
   sdk_ext_mappings = [
     "dart:ui,dart_ui.dart",
diff --git a/sky/packages/sky_engine/pubspec.yaml b/sky/packages/sky_engine/pubspec.yaml
index eee0704..3544a63 100644
--- a/sky/packages/sky_engine/pubspec.yaml
+++ b/sky/packages/sky_engine/pubspec.yaml
@@ -1,5 +1,5 @@
 name: sky_engine
-version: 0.0.48
+version: 0.0.49
 author: Flutter Authors <flutter-dev@googlegroups.com>
 description: Dart SDK extensions for dart:ui
 homepage: http://flutter.io
diff --git a/sky/packages/sky_services/pubspec.yaml b/sky/packages/sky_services/pubspec.yaml
index a35526a..48bb43c 100644
--- a/sky/packages/sky_services/pubspec.yaml
+++ b/sky/packages/sky_services/pubspec.yaml
@@ -1,9 +1,9 @@
 name: sky_services
-version: 0.0.48
+version: 0.0.50
 author: Flutter Authors <flutter-dev@googlegroups.com>
 description: Mojom interfaces associated with Flutter
 homepage: http://flutter.io
 dependencies:
-  mojo_sdk: 0.1.0
+  mojo_sdk: 0.2.1
 environment:
   sdk: '>=1.8.0 <2.0.0'
diff --git a/sky/packages/updater/lib/main.dart b/sky/packages/updater/lib/main.dart
index a7247c00..c363bad 100644
--- a/sky/packages/updater/lib/main.dart
+++ b/sky/packages/updater/lib/main.dart
@@ -18,8 +18,8 @@
 import 'pipe_to_file.dart';
 import 'version.dart';
 
-const String kManifestFile = 'sky.yaml';
-const String kBundleFile = 'flutter.flx';
+const String kManifestFile = 'flutter.yaml';
+const String kBundleFile = 'app.flx';
 
 UpdateServiceProxy _initUpdateService() {
   UpdateServiceProxy updateService = new UpdateServiceProxy.unbound();
diff --git a/sky/packages/updater/pubspec.yaml b/sky/packages/updater/pubspec.yaml
index 6753c60..b8a3bd6 100644
--- a/sky/packages/updater/pubspec.yaml
+++ b/sky/packages/updater/pubspec.yaml
@@ -3,15 +3,10 @@
 description: The autoupdater for flutter
 homepage: http://flutter.io
 dependencies:
-  mojo: 0.3.0
-  flutter: 
-    '0.0.16'
-  sky_services: ^0.0.40
+  flutter: '0.0.18'
   yaml: ^2.1.3
   path: ^1.3.0
   flx: 0.0.1
-  flutter_sprites:
-    '0.0.13'
 dependency_overrides:
   flutter:
     path: ../sky
diff --git a/sky/shell/BUILD.gn b/sky/shell/BUILD.gn
index 6c31643..34bbaf8 100644
--- a/sky/shell/BUILD.gn
+++ b/sky/shell/BUILD.gn
@@ -189,6 +189,7 @@
 } else if (is_ios) {
   import("//build/config/ios/rules.gni")
   import("//build/config/ios/ios_sdk.gni")
+  import("//sky/build/sky_precompilation_sdk.gni")
 
   source_set("ios_scaffolding") {
     sources = [
@@ -220,9 +221,8 @@
            ]
   }
 
-  group("shell") {
-    # iOS only supports application bundles with a precompiled instruction
-    # buffer. There is not Shell target for the same.
+  sky_precompilation_sdk("shell") {
+    sdk_name = "SkySDK"
   }
 } else if (is_linux) {
   executable("shell") {
@@ -269,9 +269,6 @@
     app_name = "SkyShell"
     info_plist = "mac/Info.plist"
 
-    # entitlements_path = ""
-    # code_signing_identity = ""
-
     xibs = [ "mac/sky_mac.xib" ]
 
     resource_copy_mac("sky_resources") {
diff --git a/sky/shell/ios/sky_surface.mm b/sky/shell/ios/sky_surface.mm
index 67e8e25..a94bfd5 100644
--- a/sky/shell/ios/sky_surface.mm
+++ b/sky/shell/ios/sky_surface.mm
@@ -166,11 +166,25 @@
   self.platformView->SurfaceCreated(self.acceleratedWidget);
 }
 
+-(const char *) flxBundlePath {
+  // In case this runner is part of the precompilation SDK, the FLX bundle is
+  // present in the application bundle instead of the runner bundle. Attempt
+  // to resolve the path there first.
+  // TODO: Allow specification of the application bundle identifier
+  NSBundle* applicationBundle = [NSBundle
+      bundleWithIdentifier:@"io.flutter.aplication.FlutterApplication"];
+  NSString* path = [applicationBundle pathForResource:@"app" ofType:@"flx"];
+  if (path.length != 0) {
+    return path.UTF8String;
+  }
+  return
+      [[NSBundle mainBundle] pathForResource:@"app" ofType:@"flx"].UTF8String;
+}
+
 - (void)connectToEngineAndLoad {
   auto interface_request = mojo::GetProxy(&_sky_engine);
   self.platformView->ConnectToEngine(interface_request.Pass());
-  mojo::String bundle_path(
-      [[NSBundle mainBundle] pathForResource:@"app" ofType:@"flx"].UTF8String);
+  mojo::String bundle_path([self flxBundlePath]);
   _sky_engine->RunFromPrecompiledSnapshot(bundle_path);
 }
 
diff --git a/sky/specs/style-guide.md b/sky/specs/style-guide.md
index 6ec21b9..1ae4fa2 100644
--- a/sky/specs/style-guide.md
+++ b/sky/specs/style-guide.md
@@ -170,7 +170,9 @@
 If a flow control structure's statement is one line long, then don't
 use braces around it, unless it's part of an "if" chain and any of the
 other blocks have more than one line. (Keeping the code free of
-boilerplate or redundant punctuation keeps it concise and readable.)
+boilerplate or redundant punctuation keeps it concise and readable.
+The analyzer will catch "goto fail"-style errors with its dead-code
+detection.)
 
 > For example,
 > ```dart
diff --git a/sky/tools/release_packages.py b/sky/tools/release_packages.py
index 810dcc8..32a5eb8 100755
--- a/sky/tools/release_packages.py
+++ b/sky/tools/release_packages.py
@@ -18,9 +18,9 @@
 
     pub_path = os.path.join(engine_root, 'third_party/dart-sdk/dart-sdk/bin/pub')
 
-    if args.publish:
-        subprocess.check_call([pub_path, 'publish', '--force'], cwd=os.path.join(engine_root, 'sky/packages/sky'))
-        subprocess.check_call([pub_path, 'publish', '--force'], cwd=os.path.join(engine_root, 'skysprites'))
+    subprocess.check_call([pub_path, 'publish', '--force'], cwd=os.path.join(engine_root, 'sky/packages/sky'))
+    subprocess.check_call([pub_path, 'publish', '--force'], cwd=os.path.join(engine_root, 'sky/packages/flx'))
+    subprocess.check_call([pub_path, 'publish', '--force'], cwd=os.path.join(engine_root, 'skysprites'))
 
 
 if __name__ == '__main__':
diff --git a/sky/tools/roll/roll.py b/sky/tools/roll/roll.py
index 18f2f0a..5726e45 100755
--- a/sky/tools/roll/roll.py
+++ b/sky/tools/roll/roll.py
@@ -75,7 +75,6 @@
     'mojo/common',
     'mojo/converters',
     ('mojo/dart/embedder', ['embedder.gni']),
-    'mojo/dart/observatory',
     'mojo/data_pipe_utils',
     'mojo/edk',
     'mojo/environment',
diff --git a/sky/tools/sky_snapshot/vm.cc b/sky/tools/sky_snapshot/vm.cc
index 3f2febd..bd06bc4 100644
--- a/sky/tools/sky_snapshot/vm.cc
+++ b/sky/tools/sky_snapshot/vm.cc
@@ -8,9 +8,9 @@
 #include "sky/tools/sky_snapshot/loader.h"
 #include "sky/tools/sky_snapshot/logging.h"
 
-namespace blink {
-extern const uint8_t* kDartVmIsolateSnapshotBuffer;
-extern const uint8_t* kDartIsolateSnapshotBuffer;
+extern "C" {
+extern void* kDartVmIsolateSnapshotBuffer;
+extern void* kDartIsolateSnapshotBuffer;
 }
 
 static const char* kDartArgs[] = {
@@ -19,17 +19,19 @@
 
 void InitDartVM() {
   CHECK(Dart_SetVMFlags(arraysize(kDartArgs), kDartArgs));
-  CHECK(Dart_Initialize(blink::kDartVmIsolateSnapshotBuffer, nullptr, nullptr,
-                        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
-                        nullptr, nullptr, nullptr) == nullptr);
+  CHECK(
+      Dart_Initialize(reinterpret_cast<uint8_t*>(&kDartVmIsolateSnapshotBuffer),
+                      nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+                      nullptr, nullptr, nullptr, nullptr, nullptr) == nullptr);
 }
 
 Dart_Isolate CreateDartIsolate() {
-  CHECK(blink::kDartIsolateSnapshotBuffer);
+  CHECK(kDartIsolateSnapshotBuffer);
   char* error = nullptr;
-  Dart_Isolate isolate = Dart_CreateIsolate("dart:snapshot", "main",
-                                            blink::kDartIsolateSnapshotBuffer,
-                                            nullptr, nullptr, &error);
+  Dart_Isolate isolate = Dart_CreateIsolate(
+      "dart:snapshot", "main",
+      reinterpret_cast<uint8_t*>(&kDartIsolateSnapshotBuffer), nullptr, nullptr,
+      &error);
 
   CHECK(isolate) << error;
   CHECK(!LogIfError(Dart_SetLibraryTagHandler(HandleLibraryTag)));
diff --git a/sky/tools/skyanalyzer b/sky/tools/skyanalyzer
index fc2ae42..2bb3d32 100755
--- a/sky/tools/skyanalyzer
+++ b/sky/tools/skyanalyzer
@@ -36,14 +36,12 @@
   re.compile(r'^\[(warning|hint)\] .+ will need runtime check to cast to type .+'), # https://github.com/dart-lang/sdk/issues/24542
   re.compile(r'^\[error\] Type check failed: .*\(dynamic\) is not of type'), # allow unchecked casts from dynamic
 
-  # String mode hints. Those are going away anyway, but these in particular aren't useful.
-  re.compile(r'^\[hint\] .+ requires dynamic invoke'), # too many false positives, e.g. https://github.com/dart-lang/sdk/issues/24564
-  re.compile(r'^\[hint\] Runtime check on non-ground type .+ may throw StrongModeError'), # https://github.com/dart-lang/sdk/issues/24565
-  re.compile(r'^\[hint] \([_, ]+\) .+ has inferred type '), # ignore underscore-only arguments
-
   # Bogus hint - https://github.com/dart-lang/sdk/issues/24710
   re.compile(r'^\[hint\] \(toText == toPlainText\) \? toStyledText : toPlainText has inferred type \(String, String\) → Widget \(.+styled_text.dart,.+\)'),
-  re.compile(r'^\[hint\] .+ [?:] \([_, ]+\) .+ has inferred type .+'), #
+  re.compile(r'^\[hint\] .+ [?:] \([_, ]+\) .+ has inferred type .+'),
+
+  # analyzer doesn't support constructor tear-offs yet
+  re.compile(r'.+/examples/widgets/drag_and_drop.dart, line 8[03], col .+'),
 
   # Disable the lint checks that will be caught by code review
   re.compile(r'^\[lint\] Avoid defining a one-member abstract class when a simple function will do'),
@@ -72,10 +70,10 @@
         app_paths = []
         for root, dirs, files in os.walk(SKY_UNIT_TESTS):
             app_paths.extend(os.path.join(root, f)
-                             for f in files if f.endswith(".dart"))
+                             for f in files if f.endswith(".dart") and not '.#' in f)
         for root, dirs, files in os.walk(SKY_EXAMPLES):
             app_paths.extend(os.path.join(root, f)
-                             for f in files if f.endswith(".dart"))
+                             for f in files if f.endswith(".dart") and not '.#' in f)
             if '.pub' in dirs:
               dirs.remove('.pub')
 
diff --git a/sky/unit/test/gestures/arena_test.dart b/sky/unit/test/gestures/arena_test.dart
index 4f0d062..a5b59f7 100644
--- a/sky/unit/test/gestures/arena_test.dart
+++ b/sky/unit/test/gestures/arena_test.dart
@@ -3,239 +3,161 @@
 
 typedef void GestureArenaCallback(Object key);
 
+const int primaryKey = 4;
+
 class TestGestureArenaMember extends GestureArenaMember {
-  TestGestureArenaMember({ this.onAcceptGesture, this.onRejectGesture });
-
-  final GestureArenaCallback onAcceptGesture;
-  final GestureArenaCallback onRejectGesture;
-
+  bool acceptRan = false;
   void acceptGesture(Object key) {
-    onAcceptGesture(key);
+    expect(key, equals(primaryKey));
+    acceptRan = true;
+  }
+  bool rejectRan = false;
+  void rejectGesture(Object key) {
+    expect(key, equals(primaryKey));
+    rejectRan = true;
+  }
+}
+
+class GestureTester {
+  GestureArena arena = new GestureArena();
+  TestGestureArenaMember first = new TestGestureArenaMember();
+  TestGestureArenaMember second = new TestGestureArenaMember();
+
+  GestureArenaEntry firstEntry;
+  void addFirst() {
+    firstEntry = arena.add(primaryKey, first);
   }
 
-  void rejectGesture(Object key) {
-    onRejectGesture(key);
+  GestureArenaEntry secondEntry;
+  void addSecond() {
+    secondEntry = arena.add(primaryKey, second);
+  }
+
+  void expectNothing() {
+    expect(first.acceptRan, isFalse);
+    expect(first.rejectRan, isFalse);
+    expect(second.acceptRan, isFalse);
+    expect(second.rejectRan, isFalse);
+  }
+
+  void expectFirstWin() {
+    expect(first.acceptRan, isTrue);
+    expect(first.rejectRan, isFalse);
+    expect(second.acceptRan, isFalse);
+    expect(second.rejectRan, isTrue);
+  }
+
+  void expectSecondWin() {
+    expect(first.acceptRan, isFalse);
+    expect(first.rejectRan, isTrue);
+    expect(second.acceptRan, isTrue);
+    expect(second.rejectRan, isFalse);
   }
 }
 
 void main() {
   test('Should win by accepting', () {
-    GestureArena arena = new GestureArena();
-
-    int primaryKey = 4;
-    bool firstAcceptRan = false;
-    bool firstRejectRan = false;
-    bool secondAcceptRan = false;
-    bool secondRejectRan = false;
-
-    TestGestureArenaMember first = new TestGestureArenaMember(
-      onAcceptGesture: (int key) {
-        expect(key, equals(primaryKey));
-        firstAcceptRan = true;
-      },
-      onRejectGesture: (int key) {
-        expect(key, equals(primaryKey));
-        firstRejectRan = true;
-      }
-    );
-
-    TestGestureArenaMember second = new TestGestureArenaMember(
-      onAcceptGesture: (int key) {
-        expect(key, equals(primaryKey));
-        secondAcceptRan = true;
-      },
-      onRejectGesture: (int key) {
-        expect(key, equals(primaryKey));
-        secondRejectRan = true;
-      }
-    );
-
-    GestureArenaEntry firstEntry = arena.add(primaryKey, first);
-    arena.add(primaryKey, second);
-    arena.close(primaryKey);
-
-    expect(firstAcceptRan, isFalse);
-    expect(firstRejectRan, isFalse);
-    expect(secondAcceptRan, isFalse);
-    expect(secondRejectRan, isFalse);
-
-    firstEntry.resolve(GestureDisposition.accepted);
-
-    expect(firstAcceptRan, isTrue);
-    expect(firstRejectRan, isFalse);
-    expect(secondAcceptRan, isFalse);
-    expect(secondRejectRan, isTrue);
+    GestureTester tester = new GestureTester();
+    tester.addFirst();
+    tester.addSecond();
+    tester.arena.close(primaryKey);
+    tester.expectNothing();
+    tester.firstEntry.resolve(GestureDisposition.accepted);
+    tester.expectFirstWin();
   });
 
   test('Should win by sweep', () {
-    GestureArena arena = new GestureArena();
-
-    int primaryKey = 4;
-    bool firstAcceptRan = false;
-    bool firstRejectRan = false;
-    bool secondAcceptRan = false;
-    bool secondRejectRan = false;
-
-    TestGestureArenaMember first = new TestGestureArenaMember(
-      onAcceptGesture: (int key) {
-        expect(key, equals(primaryKey));
-        firstAcceptRan = true;
-      },
-      onRejectGesture: (int key) {
-        expect(key, equals(primaryKey));
-        firstRejectRan = true;
-      }
-    );
-
-    TestGestureArenaMember second = new TestGestureArenaMember(
-      onAcceptGesture: (int key) {
-        expect(key, equals(primaryKey));
-        secondAcceptRan = true;
-      },
-      onRejectGesture: (int key) {
-        expect(key, equals(primaryKey));
-        secondRejectRan = true;
-      }
-    );
-
-    arena.add(primaryKey, first);
-    arena.add(primaryKey, second);
-    arena.close(primaryKey);
-
-    expect(firstAcceptRan, isFalse);
-    expect(firstRejectRan, isFalse);
-    expect(secondAcceptRan, isFalse);
-    expect(secondRejectRan, isFalse);
-
-    arena.sweep(primaryKey);
-
-    expect(firstAcceptRan, isTrue);
-    expect(firstRejectRan, isFalse);
-    expect(secondAcceptRan, isFalse);
-    expect(secondRejectRan, isTrue);
+    GestureTester tester = new GestureTester();
+    tester.addFirst();
+    tester.addSecond();
+    tester.arena.close(primaryKey);
+    tester.expectNothing();
+    tester.arena.sweep(primaryKey);
+    tester.expectFirstWin();
   });
 
   test('Should win on release after hold sweep release', () {
-    GestureArena arena = new GestureArena();
-
-    int primaryKey = 4;
-    bool firstAcceptRan = false;
-    bool firstRejectRan = false;
-    bool secondAcceptRan = false;
-    bool secondRejectRan = false;
-
-    TestGestureArenaMember first = new TestGestureArenaMember(
-      onAcceptGesture: (int key) {
-        expect(key, equals(primaryKey));
-        firstAcceptRan = true;
-      },
-      onRejectGesture: (int key) {
-        expect(key, equals(primaryKey));
-        firstRejectRan = true;
-      }
-    );
-
-    TestGestureArenaMember second = new TestGestureArenaMember(
-      onAcceptGesture: (int key) {
-        expect(key, equals(primaryKey));
-        secondAcceptRan = true;
-      },
-      onRejectGesture: (int key) {
-        expect(key, equals(primaryKey));
-        secondRejectRan = true;
-      }
-    );
-
-    arena.add(primaryKey, first);
-    arena.add(primaryKey, second);
-    arena.close(primaryKey);
-
-    expect(firstAcceptRan, isFalse);
-    expect(firstRejectRan, isFalse);
-    expect(secondAcceptRan, isFalse);
-    expect(secondRejectRan, isFalse);
-
-    arena.hold(primaryKey);
-
-    expect(firstAcceptRan, isFalse);
-    expect(firstRejectRan, isFalse);
-    expect(secondAcceptRan, isFalse);
-    expect(secondRejectRan, isFalse);
-
-    arena.sweep(primaryKey);
-
-    expect(firstAcceptRan, isFalse);
-    expect(firstRejectRan, isFalse);
-    expect(secondAcceptRan, isFalse);
-    expect(secondRejectRan, isFalse);
-
-    arena.release(primaryKey);
-
-    expect(firstAcceptRan, isTrue);
-    expect(firstRejectRan, isFalse);
-    expect(secondAcceptRan, isFalse);
-    expect(secondRejectRan, isTrue);
+    GestureTester tester = new GestureTester();
+    tester.addFirst();
+    tester.addSecond();
+    tester.arena.close(primaryKey);
+    tester.expectNothing();
+    tester.arena.hold(primaryKey);
+    tester.expectNothing();
+    tester.arena.sweep(primaryKey);
+    tester.expectNothing();
+    tester.arena.release(primaryKey);
+    tester.expectFirstWin();
   });
 
   test('Should win on sweep after hold release sweep', () {
-    GestureArena arena = new GestureArena();
+    GestureTester tester = new GestureTester();
+    tester.addFirst();
+    tester.addSecond();
+    tester.arena.close(primaryKey);
+    tester.expectNothing();
+    tester.arena.hold(primaryKey);
+    tester.expectNothing();
+    tester.arena.release(primaryKey);
+    tester.expectNothing();
+    tester.arena.sweep(primaryKey);
+    tester.expectFirstWin();
+  });
 
-    int primaryKey = 4;
-    bool firstAcceptRan = false;
-    bool firstRejectRan = false;
-    bool secondAcceptRan = false;
-    bool secondRejectRan = false;
+  test('Only first winner should win', () {
+    GestureTester tester = new GestureTester();
+    tester.addFirst();
+    tester.addSecond();
+    tester.arena.close(primaryKey);
+    tester.expectNothing();
+    tester.firstEntry.resolve(GestureDisposition.accepted);
+    tester.secondEntry.resolve(GestureDisposition.accepted);
+    tester.expectFirstWin();
+  });
 
-    TestGestureArenaMember first = new TestGestureArenaMember(
-      onAcceptGesture: (int key) {
-        expect(key, equals(primaryKey));
-        firstAcceptRan = true;
-      },
-      onRejectGesture: (int key) {
-        expect(key, equals(primaryKey));
-        firstRejectRan = true;
-      }
-    );
+  test('Only first winner should win, regardless of order', () {
+    GestureTester tester = new GestureTester();
+    tester.addFirst();
+    tester.addSecond();
+    tester.arena.close(primaryKey);
+    tester.expectNothing();
+    tester.secondEntry.resolve(GestureDisposition.accepted);
+    tester.firstEntry.resolve(GestureDisposition.accepted);
+    tester.expectSecondWin();
+  });
 
-    TestGestureArenaMember second = new TestGestureArenaMember(
-      onAcceptGesture: (int key) {
-        expect(key, equals(primaryKey));
-        secondAcceptRan = true;
-      },
-      onRejectGesture: (int key) {
-        expect(key, equals(primaryKey));
-        secondRejectRan = true;
-      }
-    );
+  test('Win before close is delayed to close', () {
+    GestureTester tester = new GestureTester();
+    tester.addFirst();
+    tester.addSecond();
+    tester.expectNothing();
+    tester.firstEntry.resolve(GestureDisposition.accepted);
+    tester.expectNothing();
+    tester.arena.close(primaryKey);
+    tester.expectFirstWin();
+  });
 
-    arena.add(primaryKey, first);
-    arena.add(primaryKey, second);
-    arena.close(primaryKey);
+  test('Win before close is delayed to close, and only first winner should win', () {
+    GestureTester tester = new GestureTester();
+    tester.addFirst();
+    tester.addSecond();
+    tester.expectNothing();
+    tester.firstEntry.resolve(GestureDisposition.accepted);
+    tester.secondEntry.resolve(GestureDisposition.accepted);
+    tester.expectNothing();
+    tester.arena.close(primaryKey);
+    tester.expectFirstWin();
+  });
 
-    expect(firstAcceptRan, isFalse);
-    expect(firstRejectRan, isFalse);
-    expect(secondAcceptRan, isFalse);
-    expect(secondRejectRan, isFalse);
-
-    arena.hold(primaryKey);
-
-    expect(firstAcceptRan, isFalse);
-    expect(firstRejectRan, isFalse);
-    expect(secondAcceptRan, isFalse);
-    expect(secondRejectRan, isFalse);
-
-    arena.release(primaryKey);
-
-    expect(firstAcceptRan, isFalse);
-    expect(firstRejectRan, isFalse);
-    expect(secondAcceptRan, isFalse);
-    expect(secondRejectRan, isFalse);
-
-    arena.sweep(primaryKey);
-
-    expect(firstAcceptRan, isTrue);
-    expect(firstRejectRan, isFalse);
-    expect(secondAcceptRan, isFalse);
-    expect(secondRejectRan, isTrue);
+  test('Win before close is delayed to close, and only first winner should win, regardless of order', () {
+    GestureTester tester = new GestureTester();
+    tester.addFirst();
+    tester.addSecond();
+    tester.expectNothing();
+    tester.secondEntry.resolve(GestureDisposition.accepted);
+    tester.firstEntry.resolve(GestureDisposition.accepted);
+    tester.expectNothing();
+    tester.arena.close(primaryKey);
+    tester.expectSecondWin();
   });
 }
diff --git a/sky/unit/test/rendering/positioned_box_test.dart b/sky/unit/test/rendering/positioned_box_test.dart
index aaa783e..3b71b91 100644
--- a/sky/unit/test/rendering/positioned_box_test.dart
+++ b/sky/unit/test/rendering/positioned_box_test.dart
@@ -21,22 +21,49 @@
       additionalConstraints: new BoxConstraints.tight(new Size(100.0, 100.0)),
       child: new RenderDecoratedBox(decoration: new BoxDecoration())
     );
-    RenderPositionedBox positioner = new RenderPositionedBox(child: sizer, shrinkWrap: ShrinkWrap.width);
+    RenderPositionedBox positioner = new RenderPositionedBox(child: sizer, widthFactor: 1.0);
     layout(positioner, constraints: new BoxConstraints.loose(new Size(200.0, 200.0)));
 
     expect(positioner.size.width, equals(100.0), reason: "positioner width");
     expect(positioner.size.height, equals(200.0), reason: "positioner height");
 
-    positioner.shrinkWrap = ShrinkWrap.height;
+    positioner.widthFactor = null;
+    positioner.heightFactor = 1.0;
     pumpFrame();
 
     expect(positioner.size.width, equals(200.0), reason: "positioner width");
     expect(positioner.size.height, equals(100.0), reason: "positioner height");
 
-    positioner.shrinkWrap = ShrinkWrap.both;
+    positioner.widthFactor = 1.0;
     pumpFrame();
 
     expect(positioner.size.width, equals(100.0), reason: "positioner width");
     expect(positioner.size.height, equals(100.0), reason: "positioner height");
   });
+
+  test('RenderPositionedBox width and height factors', () {
+    RenderConstrainedBox sizer = new RenderConstrainedBox(
+      additionalConstraints: new BoxConstraints.tight(new Size(100.0, 100.0)),
+      child: new RenderDecoratedBox(decoration: new BoxDecoration())
+    );
+    RenderPositionedBox positioner = new RenderPositionedBox(child: sizer, widthFactor: 1.0, heightFactor: 0.0);
+    layout(positioner, constraints: new BoxConstraints.loose(new Size(200.0, 200.0)));
+
+    expect(positioner.size.width, equals(100.0));
+    expect(positioner.size.height, equals(0.0));
+
+    positioner.widthFactor = 0.5;
+    positioner.heightFactor = 0.5;
+    pumpFrame();
+
+    expect(positioner.size.width, equals(50.0));
+    expect(positioner.size.height, equals(50.0));
+
+    positioner.widthFactor = null;
+    positioner.heightFactor = null;
+    pumpFrame();
+
+    expect(positioner.size.width, equals(200.0));
+    expect(positioner.size.height, equals(200.0));
+  });
 }
diff --git a/sky/unit/test/rendering/viewport_test.dart b/sky/unit/test/rendering/viewport_test.dart
index dae2c93..af1cf16 100644
--- a/sky/unit/test/rendering/viewport_test.dart
+++ b/sky/unit/test/rendering/viewport_test.dart
@@ -5,8 +5,14 @@
 
 void main() {
   test('Should be able to hit with negative scroll offset', () {
+    RenderBox green = new RenderDecoratedBox(
+      decoration: new BoxDecoration(
+        backgroundColor: const Color(0xFF00FF00)
+      ));
+
     RenderBox size = new RenderConstrainedBox(
-      additionalConstraints: new BoxConstraints.tight(const Size(100.0, 100.0)));
+      additionalConstraints: new BoxConstraints.tight(const Size(100.0, 100.0)),
+      child: green);
 
     RenderBox red = new RenderDecoratedBox(
       decoration: new BoxDecoration(
@@ -21,10 +27,10 @@
 
     result = new HitTestResult();
     renderView.hitTest(result, position: new Point(15.0, 0.0));
-    expect(result.path.first.target, equals(viewport));
+    expect(result.path.first.target.runtimeType, equals(TestRenderView));
 
     result = new HitTestResult();
     renderView.hitTest(result, position: new Point(15.0, 15.0));
-    expect(result.path.first.target, equals(size));
+    expect(result.path.first.target, equals(green));
   });
 }
diff --git a/sky/unit/test/widget/align_test.dart b/sky/unit/test/widget/align_test.dart
index a66c2db..6d3d05f 100644
--- a/sky/unit/test/widget/align_test.dart
+++ b/sky/unit/test/widget/align_test.dart
@@ -1,3 +1,4 @@
+import 'package:flutter/rendering.dart';
 import 'package:flutter/widgets.dart';
 import 'package:test/test.dart';
 
@@ -21,4 +22,26 @@
       );
     });
   });
+
+  test('Shrink wraps in finite space', () {
+    testWidgets((WidgetTester tester) {
+      GlobalKey alignKey = new GlobalKey();
+      tester.pumpWidget(
+        new ScrollableViewport(
+          child: new Align(
+            key: alignKey,
+            child: new Container(
+              width: 10.0,
+              height: 10.0
+            ),
+            alignment: const FractionalOffset(0.50, 0.50)
+          )
+        )
+      );
+
+      RenderBox box = alignKey.currentContext.findRenderObject();
+      expect(box.size.width, equals(800.0));
+      expect(box.size.height, equals(10.0));
+    });
+  });
 }
diff --git a/sky/unit/test/widget/custom_multi_child_layout_test.dart b/sky/unit/test/widget/custom_multi_child_layout_test.dart
index 28bc745..3a4cb19 100644
--- a/sky/unit/test/widget/custom_multi_child_layout_test.dart
+++ b/sky/unit/test/widget/custom_multi_child_layout_test.dart
@@ -14,16 +14,16 @@
 
   Size performLayoutSize;
   BoxConstraints performLayoutConstraints;
-  int performLayoutChildCount;
   Size performLayoutSize0;
   Size performLayoutSize1;
+  bool performLayoutIsChild;
 
-  void performLayout(Size size, BoxConstraints constraints, int childCount) {
+  void performLayout(Size size, BoxConstraints constraints) {
     performLayoutSize = size;
     performLayoutConstraints = constraints;
-    performLayoutChildCount = childCount;
     performLayoutSize0 = layoutChild(0, constraints);
     performLayoutSize1 = layoutChild(1, constraints);
+    performLayoutIsChild = isChild('fred');
   }
 }
 
@@ -33,8 +33,8 @@
       TestMultiChildLayoutDelegate delegate = new TestMultiChildLayoutDelegate();
       tester.pumpWidget(new Center(
         child: new CustomMultiChildLayout([
-          new Container(width: 150.0, height: 100.0),
-          new Container(width: 100.0, height: 200.0)
+          new LayoutId(id: 0, child: new Container(width: 150.0, height: 100.0)),
+          new LayoutId(id: 1, child: new Container(width: 100.0, height: 200.0))
         ],
           delegate: delegate
         )
@@ -51,11 +51,11 @@
       expect(delegate.performLayoutConstraints.maxWidth, 800.0);
       expect(delegate.performLayoutConstraints.minHeight, 0.0);
       expect(delegate.performLayoutConstraints.maxHeight, 600.0);
-      expect(delegate.performLayoutChildCount, 2);
       expect(delegate.performLayoutSize0.width, 150.0);
       expect(delegate.performLayoutSize0.height, 100.0);
       expect(delegate.performLayoutSize1.width, 100.0);
       expect(delegate.performLayoutSize1.height, 200.0);
+      expect(delegate.performLayoutIsChild, false);
     });
   });
 }
diff --git a/sky/unit/test/widget/gesture_detector_test.dart b/sky/unit/test/widget/gesture_detector_test.dart
index 56f9bde..030af3c 100644
--- a/sky/unit/test/widget/gesture_detector_test.dart
+++ b/sky/unit/test/widget/gesture_detector_test.dart
@@ -23,7 +23,11 @@
         onVerticalDragEnd: (Offset velocity) {
           didEndDrag = true;
         },
-        child: new Container()
+        child: new Container(
+          decoration: const BoxDecoration(
+            backgroundColor: const Color(0xFF00FF00)
+          )
+        )
       );
 
       tester.pumpWidget(widget);
@@ -70,7 +74,11 @@
         onVerticalDragEnd: (Offset velocity) { gestureCount += 1; },
         onHorizontalDragUpdate: (_) { fail("gesture should not match"); },
         onHorizontalDragEnd: (Offset velocity) { fail("gesture should not match"); },
-        child: new Container()
+        child: new Container(
+          decoration: const BoxDecoration(
+            backgroundColor: const Color(0xFF00FF00)
+          )
+        )
       );
       tester.pumpWidget(widget);
 
@@ -106,7 +114,11 @@
           onPanEnd: (_) {
             didEndPan = true;
           },
-          child: new Container()
+          child: new Container(
+            decoration: const BoxDecoration(
+              backgroundColor: const Color(0xFF00FF00)
+            )
+          )
         )
       );
 
@@ -122,4 +134,69 @@
       expect(didEndPan, isTrue);
     });
   });
+
+  test('Translucent', () {
+    testWidgets((WidgetTester tester) {
+      bool didReceivePointerDown;
+      bool didTap;
+
+      void pumpWidgetTree(HitTestBehavior behavior) {
+        tester.pumpWidget(
+          new Stack([
+            new Listener(
+              onPointerDown: (_) {
+                didReceivePointerDown = true;
+              },
+              child: new Container(
+                width: 100.0,
+                height: 100.0,
+                decoration: const BoxDecoration(
+                  backgroundColor: const Color(0xFF00FF00)
+                )
+              )
+            ),
+            new Container(
+              width: 100.0,
+              height: 100.0,
+              child: new GestureDetector(
+                onTap: () {
+                  didTap = true;
+                },
+                behavior: behavior
+              )
+            )
+          ])
+        );
+      }
+
+      didReceivePointerDown = false;
+      didTap = false;
+      pumpWidgetTree(null);
+      tester.tapAt(new Point(10.0, 10.0));
+      expect(didReceivePointerDown, isTrue);
+      expect(didTap, isTrue);
+
+      didReceivePointerDown = false;
+      didTap = false;
+      pumpWidgetTree(HitTestBehavior.deferToChild);
+      tester.tapAt(new Point(10.0, 10.0));
+      expect(didReceivePointerDown, isTrue);
+      expect(didTap, isFalse);
+
+      didReceivePointerDown = false;
+      didTap = false;
+      pumpWidgetTree(HitTestBehavior.opaque);
+      tester.tapAt(new Point(10.0, 10.0));
+      expect(didReceivePointerDown, isFalse);
+      expect(didTap, isTrue);
+
+      didReceivePointerDown = false;
+      didTap = false;
+      pumpWidgetTree(HitTestBehavior.translucent);
+      tester.tapAt(new Point(10.0, 10.0));
+      expect(didReceivePointerDown, isTrue);
+      expect(didTap, isTrue);
+
+    });
+  });
 }
diff --git a/sky/unit/test/widget/snack_bar_test.dart b/sky/unit/test/widget/snack_bar_test.dart
index 77d9ebc..f79ef54 100644
--- a/sky/unit/test/widget/snack_bar_test.dart
+++ b/sky/unit/test/widget/snack_bar_test.dart
@@ -21,9 +21,14 @@
                   content: new Text(helloSnackBar)
                 );
               },
-              child: new Center(
-                key: tapTarget,
-                child: new Placeholder(key: placeholderKey)
+              child: new Container(
+                decoration: const BoxDecoration(
+                  backgroundColor: const Color(0xFF00FF00)
+                ),
+                child: new Center(
+                  key: tapTarget,
+                  child: new Placeholder(key: placeholderKey)
+                )
               )
             );
           }
diff --git a/sky/unit/test/widget/transform_test.dart b/sky/unit/test/widget/transform_test.dart
index 03c0eef..b1e9afd 100644
--- a/sky/unit/test/widget/transform_test.dart
+++ b/sky/unit/test/widget/transform_test.dart
@@ -33,7 +33,11 @@
                   onTap: () {
                     didReceiveTap = true;
                   },
-                  child: new Container()
+                  child: new Container(
+                    decoration: new BoxDecoration(
+                      backgroundColor: new Color(0xFF00FFFF)
+                    )
+                  )
                 )
               )
             )
@@ -78,7 +82,11 @@
                   onTap: () {
                     didReceiveTap = true;
                   },
-                  child: new Container()
+                  child: new Container(
+                    decoration: new BoxDecoration(
+                      backgroundColor: new Color(0xFF00FFFF)
+                    )
+                  )
                 )
               )
             )
@@ -124,7 +132,11 @@
                   onTap: () {
                     didReceiveTap = true;
                   },
-                  child: new Container()
+                  child: new Container(
+                    decoration: new BoxDecoration(
+                      backgroundColor: new Color(0xFF00FFFF)
+                    )
+                  )
                 )
               )
             )
diff --git a/skysprites/pubspec.yaml b/skysprites/pubspec.yaml
index 45af436..137113c 100644
--- a/skysprites/pubspec.yaml
+++ b/skysprites/pubspec.yaml
@@ -1,9 +1,9 @@
 name: flutter_sprites
 description: A sprite toolkit built on top of Flutter
-version: 0.0.13
+version: 0.0.15
 author: Flutter Authors <flutter-dev@googlegroups.com>
 homepage: http://flutter.io
 dependencies:
   flutter: 
-    '0.0.16'
+    '0.0.18'
   box2d: ">=0.2.0 <0.3.0"