[dart-sdk] Add Module.createEntity api

TEST=
- fx set x64 --packages topaz/packages/buildbot --packages
vendor/google/packages/buildbot
- fx run-host-tess

Change-Id: I1d8380d323e36a8bccfee628ed7552226ab854fd
diff --git a/examples/modular/models/BUILD.gn b/examples/modular/models/BUILD.gn
index c5941ae..18bcbcb 100644
--- a/examples/modular/models/BUILD.gn
+++ b/examples/modular/models/BUILD.gn
@@ -12,7 +12,6 @@
   sources = [
     "shape.dart",
     "src/shape.dart",
-    "src/shape_codec.dart",
   ]
 
   deps = [
diff --git a/examples/modular/models/lib/shape.dart b/examples/modular/models/lib/shape.dart
index 7cd7d32..99e76c5 100644
--- a/examples/modular/models/lib/shape.dart
+++ b/examples/modular/models/lib/shape.dart
@@ -3,4 +3,3 @@
 // found in the LICENSE file.
 
 export 'src/shape.dart';
-export 'src/shape_codec.dart';
diff --git a/examples/modular/models/lib/src/shape.dart b/examples/modular/models/lib/src/shape.dart
index e23b5b2..c7b13a6 100644
--- a/examples/modular/models/lib/src/shape.dart
+++ b/examples/modular/models/lib/src/shape.dart
@@ -2,8 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'dart:typed_data';
+
 /// A Simple model object representing a shape
 class Shape {
+  static const String entityType = 'com.fuchsia.shapes_mod.shape';
+
   /// The size of the shape
   double size = 0.0;
 
@@ -15,4 +19,16 @@
 
   /// The default constructor
   Shape(this.size);
+
+  Shape.fromBytes(Uint8List bytes) {
+    size = ByteData.view(bytes.buffer).getFloat64(0);
+  }
+
+  Uint8List toBytes() {
+    final list = Uint8List(8);
+
+    ByteData.view(list.buffer).setFloat64(0, size);
+
+    return list;
+  }
 }
diff --git a/examples/modular/models/lib/src/shape_codec.dart b/examples/modular/models/lib/src/shape_codec.dart
deleted file mode 100644
index dd486d2..0000000
--- a/examples/modular/models/lib/src/shape_codec.dart
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2018 The Fuchsia 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 'dart:typed_data';
-
-import 'package:fuchsia_modular/entity.dart';
-
-import 'shape.dart';
-
-/// An [EntityCodec] which encodes and decodes a shape object
-class ShapeCodec extends SimpleEntityCodec<Shape> {
-  ShapeCodec()
-      : super(
-            type: 'com.fuchsia.shapes_mod.shape',
-            encoding: '*',
-            encode: _encode,
-            decode: _decode);
-
-  static Uint8List _encode(Shape shape) {
-    final list = Uint8List(8);
-
-    ByteData.view(list.buffer).setFloat64(0, shape.size);
-
-    return list;
-  }
-
-  static Shape _decode(Uint8List list) {
-    final size = ByteData.view(list.buffer).getFloat64(0);
-    return Shape(size);
-  }
-}
diff --git a/examples/modular/shapes_mod/lib/src/intent_handlers/root_intent_handler.dart b/examples/modular/shapes_mod/lib/src/intent_handlers/root_intent_handler.dart
index 1207504..4a082ce 100644
--- a/examples/modular/shapes_mod/lib/src/intent_handlers/root_intent_handler.dart
+++ b/examples/modular/shapes_mod/lib/src/intent_handlers/root_intent_handler.dart
@@ -15,9 +15,9 @@
   @override
   void handleIntent(Intent intent) {
     final stream = intent
-        .getEntity(name: 'shape', codec: ShapeCodec())
+        .getEntity(name: 'shape', type: Shape.entityType)
         .watch()
-        .cast<Shape>();
+        .map((b) => Shape.fromBytes(b));
 
     if (intent.action == _circleAction) {
       CircleRenderer().render(stream);
diff --git a/examples/modular/shapes_mod/meta/shapes_mod.cmx b/examples/modular/shapes_mod/meta/shapes_mod.cmx
index d297d04..be82af8 100644
--- a/examples/modular/shapes_mod/meta/shapes_mod.cmx
+++ b/examples/modular/shapes_mod/meta/shapes_mod.cmx
@@ -9,6 +9,7 @@
             "fuchsia.logger.LogSink",
             "fuchsia.modular.Clipboard",
             "fuchsia.modular.ContextWriter",
+            "fuchsia.modular.ComponentContext",
             "fuchsia.modular.ModuleContext",
             "fuchsia.netstack.Netstack",
             "fuchsia.sys.Environment",
diff --git a/examples/modular/slider_mod/lib/src/blocs/app_bloc.dart b/examples/modular/slider_mod/lib/src/blocs/app_bloc.dart
index c23045e..2411a38 100644
--- a/examples/modular/slider_mod/lib/src/blocs/app_bloc.dart
+++ b/examples/modular/slider_mod/lib/src/blocs/app_bloc.dart
@@ -13,18 +13,22 @@
 
   AppBloc(this.shapeEntity);
 
-  void launchSquare() => Module().addModuleToStory(
-      name: 'shape_module',
-      intent: _makeIntent('com.fuchsia.shapes_mod.show_square'));
+  void launchSquare() =>
+      _launchModule('shape_module', 'com.fuchsia.shapes_mod.show_square');
 
-  void launchCircle() => Module().addModuleToStory(
-      name: 'shape_module',
-      intent: _makeIntent('com.fuchsia.shapes_mod.show_circle'));
+  void launchCircle() =>
+      _launchModule('shape_module', 'com.fuchsia.shapes_mod.show_circle');
 
-  Intent _makeIntent(String action) => Intent(
+  void _launchModule(String name, String action) {
+    _makeIntent(action).then(
+        (intent) => Module().addModuleToStory(name: name, intent: intent));
+  }
+
+  Future<Intent> _makeIntent(String action) async => Intent(
         action: action,
-        handler: 'shapes_mod',
-      )..addParameterFromEntity('shape', shapeEntity);
+        handler: 'fuchsia-pkg://fuchsia.com/shapes_mod#meta/shapes_mod.cmx',
+      )..addParameterFromEntityReference(
+          'shape', await shapeEntity.getEntityReference());
 
   @override
   void dispose() {}
diff --git a/examples/modular/slider_mod/lib/src/blocs/slider_bloc.dart b/examples/modular/slider_mod/lib/src/blocs/slider_bloc.dart
index ab6e744..ee3e9a8 100644
--- a/examples/modular/slider_mod/lib/src/blocs/slider_bloc.dart
+++ b/examples/modular/slider_mod/lib/src/blocs/slider_bloc.dart
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:typed_data';
 
 import 'package:example_modular_models/shape.dart';
 import 'package:fuchsia_modular/entity.dart';
@@ -12,7 +13,7 @@
 /// The [SliderBloc] provides actions and streams associated with
 /// the onscreen slider.
 class SliderBloc implements BlocBase {
-  final Entity<Shape> shapeEntity;
+  final Entity<Uint8List> shapeEntity;
   final Shape shape;
 
   final _valueController = StreamController<double>.broadcast();
@@ -34,6 +35,6 @@
     _valueController.add(newValue);
     shape.size = newValue;
 
-    shapeEntity.write(shape);
+    shapeEntity.write(shape.toBytes());
   }
 }
diff --git a/examples/modular/slider_mod/lib/src/handlers/root_intent_handler.dart b/examples/modular/slider_mod/lib/src/handlers/root_intent_handler.dart
index 4b5b08f..2346a7a 100644
--- a/examples/modular/slider_mod/lib/src/handlers/root_intent_handler.dart
+++ b/examples/modular/slider_mod/lib/src/handlers/root_intent_handler.dart
@@ -4,7 +4,6 @@
 
 import 'package:example_modular_models/shape.dart';
 import 'package:flutter/material.dart';
-import 'package:fuchsia_modular/entity.dart';
 import 'package:fuchsia_modular/module.dart';
 
 import '../blocs/app_bloc.dart';
@@ -13,14 +12,16 @@
 
 class RootIntentHandler extends IntentHandler {
   @override
-  void handleIntent(Intent intent) {
+  void handleIntent(Intent intent) async {
     // if (intent.action != '') {
     //   throw Exception('Unknown intent action');
     // }
 
     final shape = Shape(5.0);
-
-    final shapeEntity = Entity(codec: ShapeCodec())..write(shape);
+    final shapeEntity = await Module().createEntity(
+      type: Shape.entityType,
+      initialData: shape.toBytes(),
+    );
 
     runApp(
       MaterialApp(
diff --git a/examples/modular/slider_mod/lib/src/widgets/slider_scaffold.dart b/examples/modular/slider_mod/lib/src/widgets/slider_scaffold.dart
index c860b8c..22637e8 100644
--- a/examples/modular/slider_mod/lib/src/widgets/slider_scaffold.dart
+++ b/examples/modular/slider_mod/lib/src/widgets/slider_scaffold.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'dart:typed_data';
+
 import 'package:example_modular_models/shape.dart';
 import 'package:flutter/material.dart';
 import 'package:fuchsia_modular/entity.dart';
@@ -12,7 +14,7 @@
 import 'value_slider.dart';
 
 class SliderScaffold extends StatelessWidget {
-  final Entity<Shape> shapeEntity;
+  final Entity<Uint8List> shapeEntity;
   final Shape shape;
 
   const SliderScaffold(this.shapeEntity, this.shape);
diff --git a/examples/modular/slider_mod/meta/slider_mod.cmx b/examples/modular/slider_mod/meta/slider_mod.cmx
index bdc92e8..5318f02 100644
--- a/examples/modular/slider_mod/meta/slider_mod.cmx
+++ b/examples/modular/slider_mod/meta/slider_mod.cmx
@@ -21,7 +21,8 @@
     "facets": {
         "fuchsia.module": {
             "@version": 2,
-            "suggestion_headline": "Show a slider"
+            "suggestion_headline": "Show a slider",
+            "intent_filters": []
         }
     }
 }
diff --git a/public/dart/fuchsia_modular/BUILD.gn b/public/dart/fuchsia_modular/BUILD.gn
index 42c7ac0..1f2a8f7 100644
--- a/public/dart/fuchsia_modular/BUILD.gn
+++ b/public/dart/fuchsia_modular/BUILD.gn
@@ -17,7 +17,6 @@
     "agent.dart",
     "codecs.dart",
     "entity.dart",
-    "entity_codec.dart",
     "lifecycle.dart",
     "logger.dart",
     "module.dart",
@@ -29,6 +28,8 @@
     "src/agent/internal/_agent_impl.dart",
     "src/entity/entity.dart",
     "src/entity/entity_codec.dart",
+    "src/entity/entity_exceptions.dart",
+    "src/entity/internal/_entity_impl.dart",
     "src/entity/internal/_link_entity.dart",
     "src/internal/_component_context.dart",
     "src/lifecycle/internal/_lifecycle_impl.dart",
@@ -53,6 +54,7 @@
     "//garnet/public/fidl/fuchsia.sys",
     "//garnet/public/fidl/fuchsia.ui.viewsv1token",
     "//peridot/public/fidl/fuchsia.modular",
+    "//third_party/dart-pkg/pub/async",
     "//third_party/dart-pkg/pub/meta",
     "//third_party/dart-pkg/pub/uuid",
     "//topaz/public/dart/fidl",
@@ -105,6 +107,7 @@
   ]
 
   sources = [
+    "entity/internal/entity_impl_test.dart",
     "internal/component_context_integ_test.dart",
     "lifecycle/internal/lifecycle_impl_test.dart",
     "module/internal/module_impl_integ_test.dart",
diff --git a/public/dart/fuchsia_modular/lib/entity.dart b/public/dart/fuchsia_modular/lib/entity.dart
index c88f58a..5d138c5 100644
--- a/public/dart/fuchsia_modular/lib/entity.dart
+++ b/public/dart/fuchsia_modular/lib/entity.dart
@@ -5,3 +5,4 @@
 /// The classes required for working with enities
 export 'src/entity/entity.dart';
 export 'src/entity/entity_codec.dart';
+export 'src/entity/entity_exceptions.dart';
diff --git a/public/dart/fuchsia_modular/lib/src/entity/entity.dart b/public/dart/fuchsia_modular/lib/src/entity/entity.dart
index 6c0f51d..e7b05ac 100644
--- a/public/dart/fuchsia_modular/lib/src/entity/entity.dart
+++ b/public/dart/fuchsia_modular/lib/src/entity/entity.dart
@@ -3,15 +3,20 @@
 // found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:typed_data';
 
+import 'package:fidl_fuchsia_modular/fidl_async.dart' as fidl_modular;
 import 'package:meta/meta.dart';
 import 'package:uuid/uuid.dart';
 
+import '../internal/_component_context.dart';
 import 'entity_codec.dart';
+import 'entity_exceptions.dart';
+import 'internal/_entity_impl.dart';
 import 'internal/_link_entity.dart';
 
 /// An [Entity] provides a mechanism for communicating
-/// data between compoonents.
+/// data between components.
 ///
 /// Note: this is a preliminary API that is likely to change.
 @experimental
@@ -19,19 +24,74 @@
   /// Creates an entity that will live for the scope of this story.
   /// The entity that is created will be backed by the framework and
   /// can be treated as if it was received from any other entity provider.
+  ///
+  /// Note: EntityCodec will be removed after soft transition
   factory Entity({
-    @required EntityCodec<T> codec,
+    EntityCodec<T> codec,
+    String type,
+    String entityReference,
   }) {
-    // This is temporary and go away when we remove link entities.
-    final linkName = Uuid().v4().toString();
-    return LinkEntity<T>(linkName: linkName, codec: codec);
+    if (codec != null) {
+      // This is temporary and go away when we remove link entities.
+      final linkName = Uuid().v4().toString();
+      return LinkEntity<T>(linkName: linkName, codec: codec);
+    } else if (T is Uint8List) {
+      return Entity._resolved(entityReference: entityReference, type: type);
+    }
+    return null;
   }
 
+  /// Creates an Entity instance.
+  ///
+  /// This method will lazily connect to the entity proxy for data transmission.
+  /// This object should be treated like it has a valid connection until
+  /// otherwise informed via a failed future.
+  ///
+  /// ```
+  ///   final entity = Entity(entityReference: 'foo', type: 'com.foo.bar');
+  ///   // fetch the data assuming that the entity resolved correctly. If it
+  ///   // did not the call to getData() will fail.
+  ///   final data = await entity.getData();
+  /// ```
+  // note: hidden and static until soft transition completes at which point
+  // will become `factory Entity(...)`
+  // ignore: unused_element
+  static Entity _resolved({
+    @required String entityReference,
+    @required String type,
+  }) {
+    ArgumentError.checkNotNull(entityReference, 'entityReference');
+    ArgumentError.checkNotNull(type, 'type');
+
+    return EntityImpl(
+      proxyFactory: () async {
+        final resolver = fidl_modular.EntityResolverProxy();
+        await getComponentContext().getEntityResolver(resolver.ctrl.request());
+
+        final proxy = fidl_modular.EntityProxy();
+        await resolver.resolveEntity(entityReference, proxy.ctrl.request());
+
+        final types = await proxy.getTypes();
+        if (!types.contains(type)) {
+          throw EntityTypeException(type);
+        }
+
+        return proxy;
+      },
+      type: type,
+    );
+  }
+
+  /// The type of data that this object represents.
+  String get type;
+
   /// Returns the data stored in the entity.
   Future<T> getData();
 
-  /// Writes the object stored in value
-  Future<void> write(T object);
+  /// Returns the reference for this entity. Entity references will never change
+  /// for a given entity so this value can be cached and used to access the
+  /// entity from a different process or at a later time.
+  Future<String> getEntityReference();
 
   /// Watches the entity for updates.
   ///
@@ -42,18 +102,7 @@
   /// which, when closed, will close the underlying fidl
   /// connection.
   Stream<T> watch();
-}
 
-/// An exception which is thrown when an Entity does not
-/// support a given type.
-class EntityTypeException implements Exception {
-  /// The unsuported type.
-  final String type;
-
-  /// Create a new [EntityTypeException].
-  EntityTypeException(this.type);
-
-  @override
-  String toString() =>
-      'EntityTypeError: type "$type" is not available for Entity';
+  /// Writes the object stored in value
+  Future<void> write(T object);
 }
diff --git a/public/dart/fuchsia_modular/lib/src/entity/entity_exceptions.dart b/public/dart/fuchsia_modular/lib/src/entity/entity_exceptions.dart
new file mode 100644
index 0000000..7d49b12
--- /dev/null
+++ b/public/dart/fuchsia_modular/lib/src/entity/entity_exceptions.dart
@@ -0,0 +1,33 @@
+// Copyright 2018 The Fuchsia 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 'package:fidl_fuchsia_modular/fidl_async.dart' as fidl_modular;
+
+/// An exception which is thrown when an Entity does not
+/// support a given type.
+class EntityTypeException implements Exception {
+  /// The unsuported type.
+  final String type;
+
+  /// Create a new [EntityTypeException].
+  EntityTypeException(this.type);
+
+  @override
+  String toString() =>
+      'EntityTypeError: type "$type" is not available for Entity';
+}
+
+/// An exception which is thrown when writes to an Entity
+/// fail with the give status.
+class EntityWriteException implements Exception {
+  /// The status code for this failure.
+  final fidl_modular.EntityWriteStatus status;
+
+  /// Create a new [EntityWriteException].
+  EntityWriteException(this.status);
+
+  @override
+  String toString() =>
+      'EntityWriteException: entity write failed with status $status';
+}
diff --git a/public/dart/fuchsia_modular/lib/src/entity/internal/_entity_impl.dart b/public/dart/fuchsia_modular/lib/src/entity/internal/_entity_impl.dart
new file mode 100644
index 0000000..8daf70f
--- /dev/null
+++ b/public/dart/fuchsia_modular/lib/src/entity/internal/_entity_impl.dart
@@ -0,0 +1,113 @@
+// Copyright 2018 The Fuchsia 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 'dart:async';
+import 'dart:typed_data';
+
+import 'package:async/async.dart';
+import 'package:fidl/fidl.dart';
+import 'package:fidl_fuchsia_mem/fidl_async.dart' as fuchsia_mem;
+import 'package:fidl_fuchsia_modular/fidl_async.dart' as fidl_modular;
+import 'package:meta/meta.dart';
+import 'package:zircon/zircon.dart';
+
+import '../entity.dart';
+import '../entity_exceptions.dart';
+
+typedef EntityProxyFactory = FutureOr<fidl_modular.Entity> Function();
+
+/// A concrete implementation of the [Entity] class.
+class EntityImpl implements Entity<Uint8List> {
+  final _proxyMemo = AsyncMemoizer<fidl_modular.Entity>();
+
+  @override
+  final String type;
+
+  final _entityReferenceMemo = AsyncMemoizer<String>();
+
+  /// A function which returns a bound proxy on demand. This method will only be
+  /// called once.
+  final EntityProxyFactory proxyFactory;
+
+  /// Constructs an instance of [EntityImpl].
+  EntityImpl({
+    @required this.proxyFactory,
+    @required this.type,
+  })  : assert(proxyFactory != null),
+        assert(type != null && type.isNotEmpty);
+
+  @override
+  Future<Uint8List> getData() =>
+      _getProxy().then((p) => p.getData(type)).then(_unwrapBuffer);
+
+  @override
+  Future<String> getEntityReference() => _entityReferenceMemo
+      .runOnce(() => _getProxy().then((p) => p.getReference()));
+
+  @override
+  Stream<Uint8List> watch() {
+    final controller = StreamController<fuchsia_mem.Buffer>();
+    final watcher = _EntityWatcher(controller.add);
+
+    // if the user closed the stream we close the binding to the watcher
+    controller.onCancel = watcher.binding.close;
+
+    // if the connection closes then we close the stream
+    watcher.binding.whenClosed.then((_) {
+      if (!controller.isClosed) {
+        controller.close();
+      }
+    });
+
+    _getProxy().then((proxy) {
+      proxy.watch(type, watcher.getInterfaceHandle());
+    });
+
+    return controller.stream.asyncMap(_unwrapBuffer);
+  }
+
+  @override
+  Future<void> write(Uint8List value) async {
+    ArgumentError.checkNotNull(value, 'value');
+
+    final vmo = SizedVmo.fromUint8List(value);
+    final buffer = fuchsia_mem.Buffer(vmo: vmo, size: value.length);
+
+    final proxy = await _getProxy();
+    final result = await proxy.writeData(type, buffer);
+    if (result != fidl_modular.EntityWriteStatus.ok) {
+      throw EntityWriteException(result);
+    }
+  }
+
+  Future<fidl_modular.Entity> _getProxy() => _proxyMemo.runOnce(proxyFactory);
+
+  Uint8List _unwrapBuffer(fuchsia_mem.Buffer buffer) {
+    final dataVmo = SizedVmo(buffer.vmo.handle, buffer.size);
+    final result = dataVmo.read(buffer.size);
+
+    if (result.status != 0) {
+      throw Exception(
+          'VMO read faile with status [${result.status}] when trying to read entity data with type [$type]');
+    }
+
+    dataVmo.close();
+    return result.bytesAsUint8List();
+  }
+}
+
+class _EntityWatcher implements fidl_modular.EntityWatcher {
+  final fidl_modular.EntityWatcherBinding binding =
+      fidl_modular.EntityWatcherBinding();
+  final void Function(fuchsia_mem.Buffer) onUpdatedFunc;
+
+  _EntityWatcher(this.onUpdatedFunc) : assert(onUpdatedFunc != null);
+
+  InterfaceHandle<fidl_modular.EntityWatcher> getInterfaceHandle() {
+    return binding.wrap(this);
+  }
+
+  @override
+  Future<void> onUpdated(fuchsia_mem.Buffer data) async => onUpdatedFunc(data);
+}
diff --git a/public/dart/fuchsia_modular/lib/src/entity/internal/_link_entity.dart b/public/dart/fuchsia_modular/lib/src/entity/internal/_link_entity.dart
index dc59146..f580a64 100644
--- a/public/dart/fuchsia_modular/lib/src/entity/internal/_link_entity.dart
+++ b/public/dart/fuchsia_modular/lib/src/entity/internal/_link_entity.dart
@@ -16,6 +16,7 @@
 import '../../module/internal/_module_context.dart';
 import '../entity.dart';
 import '../entity_codec.dart';
+import '../entity_exceptions.dart';
 
 /// An [Entity] implementation which supports
 /// data that lives inside links.
@@ -38,9 +39,15 @@
         assert(codec != null);
 
   @override
+  String get type => codec.type;
+
+  @override
   Future<T> getData() async => _getSnapshot();
 
   @override
+  Future<String> getEntityReference() => null;
+
+  @override
   Stream<T> watch() {
     final link = _getLink();
     final controller = StreamController<fuchsia_mem.Buffer>();
diff --git a/public/dart/fuchsia_modular/lib/src/module/intent.dart b/public/dart/fuchsia_modular/lib/src/module/intent.dart
index 38e3531..d139360 100644
--- a/public/dart/fuchsia_modular/lib/src/module/intent.dart
+++ b/public/dart/fuchsia_modular/lib/src/module/intent.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'dart:typed_data';
+
 import 'package:fidl_fuchsia_modular/fidl_async.dart' as fidl;
 import 'package:meta/meta.dart';
 
@@ -94,12 +96,35 @@
   @experimental
   Entity getEntity({
     @required String name,
-    @required EntityCodec codec,
-  }) =>
-      _entityFromIntentParameter(
+    EntityCodec codec,
+    String type,
+  }) {
+    if (codec != null) {
+      return _entityFromIntentParameter(
         parameter: _getParameter(name),
         codec: codec,
       );
+    } else {
+      return _getEntity(name: name, type: type);
+    }
+  }
+
+  /// Returns the entity with the given [name]. An entity's name maps to
+  /// the name of a  parameter in your component's manifset.
+  ///
+  /// The type is used to ensure the entity provider provides us with the
+  /// type of entity we are expecting. If the type does not match the entity
+  /// will fail to resolve.
+  // Note: this method will be renamed to getEntity when the soft transition completes.
+  Entity _getEntity({
+    @required String name,
+    @required String type,
+  }) {
+    ArgumentError.checkNotNull(name, 'name');
+    ArgumentError.checkNotNull(type, 'type');
+    return _entityFromIntentParameter(
+        parameter: _getParameter(name), type: type);
+  }
 
   void _addParameter(String name, fidl.IntentParameterData parameterData) =>
       parameters.add(fidl.IntentParameter(name: name, data: parameterData));
@@ -110,10 +135,15 @@
 
   Entity _entityFromIntentParameter({
     fidl.IntentParameter parameter,
+    String type,
     EntityCodec codec,
   }) {
     if (parameter.data.tag == fidl.IntentParameterDataTag.linkName) {
       return LinkEntity(linkName: parameter.data.linkName, codec: codec);
+    } else if (parameter.data.tag ==
+        fidl.IntentParameterDataTag.entityReference) {
+      return Entity<Uint8List>(
+          type: type, entityReference: parameter.data.entityReference);
     }
     throw UnimplementedError();
   }
diff --git a/public/dart/fuchsia_modular/lib/src/module/internal/_module_impl.dart b/public/dart/fuchsia_modular/lib/src/module/internal/_module_impl.dart
index 8af6697..8cd0bbe 100644
--- a/public/dart/fuchsia_modular/lib/src/module/internal/_module_impl.dart
+++ b/public/dart/fuchsia_modular/lib/src/module/internal/_module_impl.dart
@@ -3,13 +3,18 @@
 // found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:typed_data';
 
 import 'package:fidl/fidl.dart';
+import 'package:fidl_fuchsia_mem/fidl_async.dart' as fuchsia_mem;
 import 'package:fidl_fuchsia_modular/fidl_async.dart' as fidl;
 import 'package:fidl_fuchsia_ui_viewsv1token/fidl_async.dart' as views_fidl;
 import 'package:fuchsia_modular/lifecycle.dart';
 import 'package:meta/meta.dart';
+import 'package:zircon/zircon.dart';
 
+import '../../entity/entity.dart';
+import '../../entity/internal/_entity_impl.dart';
 import '../embedded_module.dart';
 import '../intent.dart';
 import '../intent_handler.dart';
@@ -80,6 +85,36 @@
   }
 
   @override
+  Future<Entity<Uint8List>> createEntity({
+    @required String type,
+    @required Uint8List initialData,
+  }) async {
+    ArgumentError.checkNotNull(type, 'type');
+    ArgumentError.checkNotNull(initialData, 'initialData');
+
+    if (type.isEmpty) {
+      throw ArgumentError.value(type, 'type cannot be an empty string');
+    }
+
+    final context = _getContext();
+
+    // need to create the proxy and write data immediately so other modules
+    // can extract values
+    final proxy = fidl.EntityProxy();
+    final vmo = SizedVmo.fromUint8List(initialData);
+    final buffer = fuchsia_mem.Buffer(vmo: vmo, size: initialData.length);
+    final ref = await context.createEntity(type, buffer, proxy.ctrl.request());
+
+    // use the ref value to determine if creation was successful
+    if (ref == null || ref.isEmpty) {
+      throw Exception('Module.createEntity creation failed because'
+          ' the framework was unable to create the entity.');
+    }
+
+    return EntityImpl(type: type, proxyFactory: () => proxy);
+  }
+
+  @override
   Future<EmbeddedModule> embedModule({
     @required String name,
     @required fidl.Intent intent,
diff --git a/public/dart/fuchsia_modular/lib/src/module/module.dart b/public/dart/fuchsia_modular/lib/src/module/module.dart
index ef06e02..d265e52 100644
--- a/public/dart/fuchsia_modular/lib/src/module/module.dart
+++ b/public/dart/fuchsia_modular/lib/src/module/module.dart
@@ -3,11 +3,13 @@
 // found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:typed_data';
 
 import 'package:fidl_fuchsia_modular/fidl_async.dart' as fidl;
 import 'package:fuchsia_services/services.dart';
 import 'package:meta/meta.dart';
 
+import '../entity/entity.dart';
 import 'embedded_module.dart';
 import 'intent_handler.dart';
 import 'internal/_intent_handler_impl.dart';
@@ -83,6 +85,14 @@
     fidl.SurfaceRelation surfaceRelation,
   });
 
+  /// Creates an entity that will live for the lifetime of the story.
+  /// The entity that is created will be backed by the framework and
+  /// can be treated as if it was received from any other entity provider.
+  Future<Entity<Uint8List>> createEntity({
+    @required String type,
+    @required Uint8List initialData,
+  });
+
   /// This method functions similarly to [addModuleToStory()], but instead
   /// of relying on the story shell for display it is up to the caller to
   /// display the view from the new module.
@@ -121,5 +131,4 @@
   /// module and remove it from the story. If there are no more running modules
   /// in the story the story will be stopped.
   void removeSelfFromStory();
-
 }
diff --git a/public/dart/fuchsia_modular/meta/fuchsia_modular_package_integration_tests.cmx b/public/dart/fuchsia_modular/meta/fuchsia_modular_package_integration_tests.cmx
index ea24f9b..71676e7 100644
--- a/public/dart/fuchsia_modular/meta/fuchsia_modular_package_integration_tests.cmx
+++ b/public/dart/fuchsia_modular/meta/fuchsia_modular_package_integration_tests.cmx
@@ -8,7 +8,9 @@
             "fuchsia.fonts.Provider",
             "fuchsia.logger.LogSink",
             "fuchsia.modular.Clipboard",
+            "fuchsia.modular.ComponentContext",
             "fuchsia.modular.ContextWriter",
+            "fuchsia.modular.Lifecycle",
             "fuchsia.modular.ModuleContext",
             "fuchsia.netstack.Netstack",
             "fuchsia.sys.Environment",
diff --git a/public/dart/fuchsia_modular/test/entity/internal/entity_impl_test.dart b/public/dart/fuchsia_modular/test/entity/internal/entity_impl_test.dart
new file mode 100644
index 0000000..99bec26
--- /dev/null
+++ b/public/dart/fuchsia_modular/test/entity/internal/entity_impl_test.dart
@@ -0,0 +1,152 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: implementation_imports
+
+import 'dart:typed_data';
+
+import 'package:fidl/fidl.dart';
+import 'package:fidl_fuchsia_mem/fidl_async.dart' as fuchsia_mem;
+import 'package:fidl_fuchsia_modular/fidl_async.dart' as fuchsia_modular;
+import 'package:fidl_fuchsia_modular/fidl_test.dart' as fuchsia_modular_test;
+import 'package:fuchsia_modular/src/entity/entity_exceptions.dart';
+import 'package:fuchsia_modular/src/entity/internal/_entity_impl.dart';
+import 'package:test/test.dart';
+import 'package:zircon/zircon.dart';
+
+void main() {
+  MockProxy proxy;
+  EntityImpl entity;
+
+  group('enity interactions', () {
+    setUp(() {
+      proxy = MockProxy();
+      entity = EntityImpl(proxyFactory: () => proxy, type: 'foo');
+    });
+
+    tearDown(() {
+      proxy = null;
+      entity = null;
+    });
+    test('getData returns correct data', () async {
+      final data = Uint8List.fromList([1, 2, 3]);
+      await entity.write(data);
+      expect(await entity.getData(), data);
+    });
+
+    test('write returns success for valid write', () async {
+      final data = Uint8List.fromList([1, 2, 3]);
+      await entity.write(data);
+      // test will fail if write fails
+    });
+
+    test('write fails for error in write', () async {
+      proxy.writeStatus = fuchsia_modular.EntityWriteStatus.error;
+      final data = Uint8List.fromList([1, 2, 3]);
+      expect(entity.write(data),
+          throwsA(const TypeMatcher<EntityWriteException>()));
+    });
+
+    test('write fails for readonly', () async {
+      proxy.writeStatus = fuchsia_modular.EntityWriteStatus.readOnly;
+      final data = Uint8List.fromList([1, 2, 3]);
+      expect(entity.write(data),
+          throwsA(const TypeMatcher<EntityWriteException>()));
+    });
+
+    test('getEntityReference returns the correct ref', () async {
+      proxy.entityReference = 'my-entity';
+      expect(await entity.getEntityReference(), 'my-entity');
+    });
+
+    test('watch closes stream when binding is closed', () {
+      proxy.forceCloseWatchers = true;
+      final stream = entity.watch();
+      expect(stream, emitsInOrder([emitsDone]));
+    });
+
+    test('watch udpates stream', () async {
+      Uint8List _bufferWithValue(int v) {
+        final data = ByteData(1)..setInt8(0, v);
+        return data.buffer.asUint8List();
+      }
+
+      int _getValue(Uint8List buffer) {
+        return ByteData.view(buffer.buffer).getInt8(0);
+      }
+
+      final stream = entity.watch();
+
+      expect(stream.map(_getValue), emitsInOrder([1, 2, 3]));
+
+      await entity.write(_bufferWithValue(1));
+      await entity.write(_bufferWithValue(2));
+      await entity.write(_bufferWithValue(3));
+    });
+  });
+}
+
+class MockProxy extends fuchsia_modular_test.Entity$TestBase {
+  Uint8List _data;
+  String entityReference;
+  List<fuchsia_modular.EntityWatcherProxy> watchers = [];
+
+  /// if set to true the watchers will be closed after they are added.
+  bool forceCloseWatchers = false;
+
+  /// Can be set to control how write results are handled.
+  fuchsia_modular.EntityWriteStatus writeStatus =
+      fuchsia_modular.EntityWriteStatus.ok;
+
+  @override
+  Future<fuchsia_mem.Buffer> getData(String _) async {
+    final vmo = SizedVmo.fromUint8List(_data);
+    final buffer = fuchsia_mem.Buffer(vmo: vmo, size: _data.length);
+    return buffer;
+  }
+
+  @override
+  Future<String> getReference() async => entityReference;
+
+  @override
+  Future<void> watch(String type,
+      InterfaceHandle<fuchsia_modular.EntityWatcher> watcher) async {
+    final proxy = fuchsia_modular.EntityWatcherProxy();
+    proxy.ctrl.bind(watcher);
+    watchers.add(proxy);
+
+    if (forceCloseWatchers) {
+      //ignore: unawaited_futures
+      Future(proxy.ctrl.close);
+    }
+  }
+
+  @override
+  Future<fuchsia_modular.EntityWriteStatus> writeData(
+      String type, fuchsia_mem.Buffer buffer) async {
+    if (writeStatus != fuchsia_modular.EntityWriteStatus.ok) {
+      return writeStatus;
+    }
+
+    final dataVmo = SizedVmo(buffer.vmo.handle, buffer.size);
+    final result = dataVmo.read(buffer.size);
+    dataVmo.close();
+    final data = result.bytesAsUint8List();
+    _setData(data, notify: true);
+    return fuchsia_modular.EntityWriteStatus.ok;
+  }
+
+  void _setData(Uint8List data, {bool notify = false}) {
+    _data = data;
+    if (notify) {
+      final vmo = SizedVmo.fromUint8List(data);
+      final buffer = fuchsia_mem.Buffer(vmo: vmo, size: data.length);
+      for (final watcher in watchers) {
+        if (watcher.ctrl.isBound) {
+          watcher.onUpdated(buffer);
+        }
+      }
+    }
+  }
+}
diff --git a/public/dart/fuchsia_modular/test/module/intent_test.dart b/public/dart/fuchsia_modular/test/module/intent_test.dart
index 9ac71e7..1450b8c 100644
--- a/public/dart/fuchsia_modular/test/module/intent_test.dart
+++ b/public/dart/fuchsia_modular/test/module/intent_test.dart
@@ -2,12 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'dart:convert';
-import 'dart:typed_data';
-
-import 'package:fidl_fuchsia_modular/fidl_async.dart' as fidl;
 import 'package:fuchsia_modular/src/module/intent.dart'; // ignore: implementation_imports
-import 'package:fuchsia_modular/src/entity/entity_codec.dart'; // ignore: implementation_imports
+
 import 'package:test/test.dart';
 
 import '../matchers.dart';
@@ -41,28 +37,8 @@
 
     test('getEntity throws for missing name', () {
       expect(() {
-        intent.getEntity(name: 'not-a-name', codec: stub);
+        intent.getEntity(name: 'not-a-name', type: 'foo');
       }, throwsModuleStateException);
     });
-
-    test('getEntity returns valid entity for link entity', () {
-      //NOTE: this test can be deleted when _link_entity goes away.
-      intent.parameters.add(fidl.IntentParameter(
-          name: 'name',
-          data: fidl.IntentParameterData.withLinkName('foo-link')));
-      final entity = intent.getEntity(name: 'name', codec: stub);
-      expect(entity, isNotNull);
-    });
   });
 }
-
-const StubEntityCodec stub = StubEntityCodec();
-
-class StubEntityCodec extends EntityCodec {
-  const StubEntityCodec() : super(type: '', encoding: '');
-  @override
-  Converter<Uint8List, dynamic> get decoder => null;
-
-  @override
-  Converter<dynamic, Uint8List> get encoder => null;
-}