[dart-sdk] remove _link_entity.dart

This CL removes the LinkEntity class
in favor of using story scoped entities.

TEST=fuchsia_modular_package_integration_tests

Change-Id: If9d622f48dd6f88469fb9f11fe638b21e95bd310
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..dce6ba5 100644
--- a/examples/modular/slider_mod/lib/src/blocs/slider_bloc.dart
+++ b/examples/modular/slider_mod/lib/src/blocs/slider_bloc.dart
@@ -12,7 +12,7 @@
 /// The [SliderBloc] provides actions and streams associated with
 /// the onscreen slider.
 class SliderBloc implements BlocBase {
-  final Entity<Shape> shapeEntity;
+  final Entity shapeEntity;
   final Shape shape;
 
   final _valueController = StreamController<double>.broadcast();
@@ -34,6 +34,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..0d1680e 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
@@ -13,14 +13,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 Entity.createStoryScoped(
+      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..0634ae6 100644
--- a/examples/modular/slider_mod/lib/src/widgets/slider_scaffold.dart
+++ b/examples/modular/slider_mod/lib/src/widgets/slider_scaffold.dart
@@ -12,7 +12,7 @@
 import 'value_slider.dart';
 
 class SliderScaffold extends StatelessWidget {
-  final Entity<Shape> shapeEntity;
+  final Entity 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 4cbef16..c8e22ef 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",
@@ -28,8 +27,8 @@
     "src/agent/internal/_agent_context.dart",
     "src/agent/internal/_agent_impl.dart",
     "src/entity/entity.dart",
-    "src/entity/entity_codec.dart",
-    "src/entity/internal/_link_entity.dart",
+    "src/entity/entity_exceptions.dart",
+    "src/entity/internal/_entity_impl.dart",
     "src/internal/_component_context.dart",
     "src/lifecycle/internal/_lifecycle_impl.dart",
     "src/lifecycle/lifecycle.dart",
@@ -53,8 +52,8 @@
     "//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",
     "//topaz/public/dart/fuchsia",
     "//topaz/public/dart/fuchsia_logger",
@@ -105,6 +104,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",
@@ -113,6 +113,7 @@
 
   deps = [
     ":fuchsia_modular",
+    "//garnet/public/fidl/fuchsia.testing.runner",
     "//third_party/dart-pkg/pub/mockito",  # Remove after DX-470 is fixed
     "//third_party/dart-pkg/pub/test",
   ]
diff --git a/public/dart/fuchsia_modular/lib/entity.dart b/public/dart/fuchsia_modular/lib/entity.dart
index c88f58a..9c4782e 100644
--- a/public/dart/fuchsia_modular/lib/entity.dart
+++ b/public/dart/fuchsia_modular/lib/entity.dart
@@ -4,4 +4,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/entity_codec.dart b/public/dart/fuchsia_modular/lib/entity_codec.dart
deleted file mode 100644
index 63facf2..0000000
--- a/public/dart/fuchsia_modular/lib/entity_codec.dart
+++ /dev/null
@@ -1,6 +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.
-
-/// The base class for implementing typed codecs for structured entity values.
-library entity_codec;
diff --git a/public/dart/fuchsia_modular/lib/src/entity/entity.dart b/public/dart/fuchsia_modular/lib/src/entity/entity.dart
index 6c0f51d..89c6e6f 100644
--- a/public/dart/fuchsia_modular/lib/src/entity/entity.dart
+++ b/public/dart/fuchsia_modular/lib/src/entity/entity.dart
@@ -3,35 +3,72 @@
 // found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:typed_data';
 
+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:uuid/uuid.dart';
+import 'package:zircon/zircon.dart';
 
-import 'entity_codec.dart';
-import 'internal/_link_entity.dart';
+import '../internal/_component_context.dart';
+import '../module/internal/_module_context.dart';
+import 'entity_exceptions.dart';
+import 'internal/_entity_impl.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
-abstract class Entity<T> {
-  /// 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.
+abstract class Entity {
+  /// 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();
+  /// ```
   factory Entity({
-    @required EntityCodec<T> codec,
+    @required String entityReference,
+    @required String type,
   }) {
-    // This is temporary and go away when we remove link entities.
-    final linkName = Uuid().v4().toString();
-    return LinkEntity<T>(linkName: linkName, codec: codec);
+    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,
+    );
   }
 
-  /// Returns the data stored in the entity.
-  Future<T> getData();
+  /// The type of data that this object represents.
+  String get type;
 
-  /// Writes the object stored in value
-  Future<void> write(T object);
+  /// Returns the data stored in the entity.
+  Future<Uint8List> getData();
+
+  /// 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.
   ///
@@ -41,19 +78,40 @@
   /// The returned stream is a single subscription stream
   /// which, when closed, will close the underlying fidl
   /// connection.
-  Stream<T> watch();
-}
+  Stream<Uint8List> 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;
+  /// Writes the object stored in value
+  Future<void> write(Uint8List object);
 
-  /// Create a new [EntityTypeException].
-  EntityTypeException(this.type);
+  /// 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.
+  static Future<Entity> createStoryScoped({
+    @required String type,
+    @required Uint8List initialData,
+  }) async {
+    ArgumentError.checkNotNull(type, 'type');
+    ArgumentError.checkNotNull(initialData, 'initialData');
 
-  @override
-  String toString() =>
-      'EntityTypeError: type "$type" is not available for Entity';
+    if (type.isEmpty) {
+      throw ArgumentError.value(type, 'type cannot be an empty string');
+    }
+
+    final context = getModuleContext();
+
+    // need to create the proxy and write data immediately so other modules
+    // can extract values
+    final proxy = fidl_modular.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('Entity.createStoryScopedentity creation failed because'
+          ' the framework was unable to create the entity.');
+    }
+
+    return EntityImpl(type: type, proxyFactory: () => proxy);
+  }
 }
diff --git a/public/dart/fuchsia_modular/lib/src/entity/entity_codec.dart b/public/dart/fuchsia_modular/lib/src/entity/entity_codec.dart
deleted file mode 100644
index 0403ea4..0000000
--- a/public/dart/fuchsia_modular/lib/src/entity/entity_codec.dart
+++ /dev/null
@@ -1,121 +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:async';
-import 'dart:convert';
-import 'dart:typed_data';
-
-import 'package:meta/meta.dart';
-
-/// The [EntityCodec] is a typed [Codec] which translates
-/// entity data between the [Uint8List] wire format and a
-/// dart object.
-///
-/// [EntityCodec]s have a specific semantic type and encoding
-/// which allow them to operate with the entity system.
-abstract class EntityCodec<T> extends Codec<T, Uint8List> {
-  /// The semantic type of the entity which this codec supports.
-  /// This type will be used when requesting entities from the
-  /// framework.
-  final String type;
-
-  /// The encoding that this [EntityCodec] knows how to encode/decode data
-  final String encoding;
-
-  /// The default constructor.
-  const EntityCodec({
-    @required this.type,
-    @required this.encoding,
-  })  : assert(type != null),
-        assert(encoding != null);
-}
-
-/// A [Codec] used for handling the the automatic translation to and from an
-/// Entity's source data to the specified Dart type [T].
-class SimpleEntityCodec<T> extends EntityCodec<T> {
-  final _EntityEncoder<T> _encoder;
-  final _EntityDecoder<T> _decoder;
-
-  /// Create a new [EntityCodec].
-  SimpleEntityCodec({
-    @required String type,
-    @required String encoding,
-    @required _EncodeEntity<T> encode,
-    @required _DecodeEntity<T> decode,
-  })  : assert(type != null),
-        assert(encoding != null),
-        assert(encode != null),
-        assert(decode != null),
-        _encoder = _EntityEncoder<T>(encode),
-        _decoder = _EntityDecoder<T>(decode),
-        super(type: type, encoding: encoding);
-
-  @override
-  _EntityEncoder<T> get encoder => _encoder;
-
-  @override
-  _EntityDecoder<T> get decoder => _decoder;
-}
-
-typedef _EncodeEntity<T> = Uint8List Function(T value);
-
-class _EntityEncoder<T> extends Converter<T, Uint8List> {
-  final _EncodeEntity<T> encode;
-
-  const _EntityEncoder(this.encode);
-
-  @override
-  Uint8List convert(T source) {
-    return encode(source);
-  }
-}
-
-typedef _DecodeEntity<T> = T Function(Uint8List data);
-
-class _EntityDecoder<T> extends Converter<Uint8List, T> {
-  final _DecodeEntity<T> decode;
-
-  const _EntityDecoder(this.decode);
-
-  @override
-  T convert(Uint8List data) => decode(data);
-
-  @override
-  Stream<T> bind(Stream<Uint8List> source) {
-    _EntityDecoderSink<T> map(EventSink<T> out) =>
-        new _EntityDecoderSink<T>(out, this);
-
-    return new Stream<T>.eventTransformed(source, map);
-  }
-}
-
-/// Entity data [String]s in, [T] out.
-class _EntityDecoderSink<T> extends EventSink<Uint8List> {
-  /// The [EventSink] that values are decoded into. Errors generated by the
-  /// decoder are also added to [out].
-  final EventSink<T> out;
-
-  /// The decoder to used to convert the source ([Stream<String>]) events.
-  final _EntityDecoder<T> decoder;
-
-  /// Create an instance of [_EntityDecoderSink], usually via the
-  /// [Stream#eventTransformed] constructor.
-  _EntityDecoderSink(this.out, this.decoder);
-
-  @override
-  void add(Uint8List data) {
-    try {
-      T value = decoder.decode(data);
-      out.add(value);
-    } on Object catch (err, stackTrace) {
-      addError(err, stackTrace);
-    }
-  }
-
-  @override
-  void addError(Object e, [StackTrace s]) => out.addError(e, s);
-
-  @override
-  void close() => out.close();
-}
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..1e6ab75
--- /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 {
+  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
deleted file mode 100644
index dc59146..0000000
--- a/public/dart/fuchsia_modular/lib/src/entity/internal/_link_entity.dart
+++ /dev/null
@@ -1,180 +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:async';
-import 'dart:convert';
-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:meta/meta.dart';
-import 'package:zircon/zircon.dart';
-
-import '../../internal/_component_context.dart';
-import '../../module/internal/_module_context.dart';
-import '../entity.dart';
-import '../entity_codec.dart';
-
-/// An [Entity] implementation which supports
-/// data that lives inside links.
-///
-/// Note: This is a temporary solution that will go away when links are removed.
-class LinkEntity<T> implements Entity<T> {
-  /// The name of the link to pull data from
-  final String linkName;
-
-  /// The codec used to decode/encode data
-  final EntityCodec codec;
-
-  fidl.Link _link;
-
-  /// Constructor
-  LinkEntity({
-    @required this.linkName,
-    @required this.codec,
-  })  : assert(linkName != null),
-        assert(codec != null);
-
-  @override
-  Future<T> getData() async => _getSnapshot();
-
-  @override
-  Stream<T> watch() {
-    final link = _getLink();
-    final controller = StreamController<fuchsia_mem.Buffer>();
-    final watcher = _LinkWatcher(onNotify: (buffer) async {
-      controller.add(buffer);
-    });
-
-    link.watch(watcher.getInterfaceHandle());
-
-    // 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();
-      }
-    });
-
-    // Use _getSnapshot here instead of using the buffer directly because
-    // most uses of links are using the entityRef value on the link instead
-    // of raw json.
-    return controller.stream.asyncMap((_) => _getSnapshot());
-  }
-
-  @override
-  Future<void> write(T value) async {
-    final link = _getLink();
-
-    // Convert the object to raw bytes
-    final bytes = codec.encode(value);
-
-    // need to base64 encode so we can encode it as json.
-    final b64ByteString = base64.encode(bytes);
-    final jsonString = json.encode(b64ByteString);
-
-    // convert the json encoded string into bytes so we can put it in a vmo
-    final jsonData = Uint8List.fromList(utf8.encode(jsonString));
-    final vmo = SizedVmo.fromUint8List(jsonData);
-    final buffer = fuchsia_mem.Buffer(vmo: vmo, size: jsonData.length);
-
-    await link.set(null, buffer);
-  }
-
-  Future<T> _getEntityData<T>(String entityReference) async {
-    final resolver = fidl.EntityResolverProxy();
-    await getComponentContext().getEntityResolver(resolver.ctrl.request());
-
-    final entity = fidl.EntityProxy();
-    await resolver.resolveEntity(entityReference, entity.ctrl.request());
-
-    final types = await entity.getTypes();
-    if (!types.contains(codec.type)) {
-      throw EntityTypeException(codec.type);
-    }
-
-    final buffer = await entity.getData(codec.type);
-    final dataVmo = SizedVmo(buffer.vmo.handle, buffer.size);
-    final result = dataVmo.read(buffer.size);
-
-    if (result.status != 0) {
-      throw new Exception('Failed to read VMO');
-    }
-
-    dataVmo.close();
-
-    return codec.decode(result.bytesAsUint8List());
-  }
-
-  T _getJsonData<T>(fuchsia_mem.Buffer jsonBuffer) {
-    final vmo = SizedVmo(jsonBuffer.vmo.handle, jsonBuffer.size);
-    final result = vmo.read(jsonBuffer.size);
-    if (result.status != 0) {
-      throw Exception('Failed to read VMO');
-    }
-    vmo.close();
-
-    final resultBytes = result.bytesAsUint8List();
-    Uint8List bytesToDecode;
-
-    try {
-      // Try to decode the values in the format that this entity encoded them
-      final utf8String = utf8.decode(resultBytes);
-      final jsonDecoded = json.decode(utf8String);
-      bytesToDecode = base64.decode(jsonDecoded);
-    } on Exception catch (_) {
-      bytesToDecode = resultBytes;
-    }
-
-    return codec.decode(bytesToDecode);
-  }
-
-  fidl.Link _getLink() {
-    if (_link != null) {
-      return _link;
-    }
-
-    // Store the Link instead of the LinkProxy to avoid closing.
-    fidl.LinkProxy linkProxy = fidl.LinkProxy();
-    _link = linkProxy;
-    getModuleContext().getLink(linkName, linkProxy.ctrl.request());
-
-    return _link;
-  }
-
-  Future<T> _getSnapshot() async {
-    final link = _getLink();
-
-    final buffer = await link.get(null);
-    if (buffer == null) {
-      final entityReference = await link.getEntity();
-      if (entityReference == null) {
-        return null;
-      }
-      return _getEntityData(entityReference);
-    }
-    return _getJsonData(buffer);
-  }
-}
-
-class _LinkWatcher extends fidl.LinkWatcher {
-  final fidl.LinkWatcherBinding binding = fidl.LinkWatcherBinding();
-  final Future<void> Function(fuchsia_mem.Buffer) onNotify;
-
-  _LinkWatcher({@required this.onNotify}) : assert(onNotify != null);
-
-  InterfaceHandle<fidl.LinkWatcher> getInterfaceHandle() {
-    if (binding.isBound) {
-      throw Exception(
-          'Attempting to call _LinkWatcher.getInterfaceHandle on already bound binding');
-    }
-    return binding.wrap(this);
-  }
-
-  @override
-  Future<void> notify(fuchsia_mem.Buffer data) => onNotify(data);
-}
diff --git a/public/dart/fuchsia_modular/lib/src/module/intent.dart b/public/dart/fuchsia_modular/lib/src/module/intent.dart
index 38e3531..1c44e8a 100644
--- a/public/dart/fuchsia_modular/lib/src/module/intent.dart
+++ b/public/dart/fuchsia_modular/lib/src/module/intent.dart
@@ -6,8 +6,6 @@
 import 'package:meta/meta.dart';
 
 import '../entity/entity.dart';
-import '../entity/entity_codec.dart';
-import '../entity/internal/_link_entity.dart';
 import 'module_state_exception.dart';
 
 /// An [Intent] is a fundamental building block of module development.
@@ -66,16 +64,6 @@
         );
 
   /// Appends a [fidl.IntentParameter] to the intent's parameters containing
-  /// the [entity] as its data value and the [name] as its name value.
-  void addParameterFromEntity(String name, Entity entity) {
-    if (entity is LinkEntity) {
-      _addParameterFromLinkEntity(name, entity);
-    } else {
-      throw UnimplementedError('Only link entities are supported at this time');
-    }
-  }
-
-  /// Appends a [fidl.IntentParameter] to the intent's parameters containing
   /// the [reference] to an entity as its data value and the [name] as its
   /// name value.
   void addParameterFromEntityReference(String name, String reference) =>
@@ -85,35 +73,27 @@
   /// Returns the entity with the given [name]. An entity's name maps to
   /// the name of a  parameter in your component's manifset.
   ///
-  /// The codec is used to translate the bytes in the entity to the dart
-  /// object type.
-  ///
-  /// The [codec] will need to match the EntityDataType. The name will need
-  /// to match the semantic type and the encoding will need to match the
-  /// encoding such that it can actually encode and decode values
+  /// 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.
   @experimental
   Entity getEntity({
     @required String name,
-    @required EntityCodec codec,
-  }) =>
-      _entityFromIntentParameter(
-        parameter: _getParameter(name),
-        codec: codec,
-      );
+    @required String type,
+  }) {
+    ArgumentError.checkNotNull(name, 'name');
+    ArgumentError.checkNotNull(type, 'type');
+    return _entityFromIntentParameter(_getParameter(name), type);
+  }
 
   void _addParameter(String name, fidl.IntentParameterData parameterData) =>
       parameters.add(fidl.IntentParameter(name: name, data: parameterData));
 
-  void _addParameterFromLinkEntity(String name, LinkEntity entity) =>
-      _addParameter(
-          name, fidl.IntentParameterData.withLinkName(entity.linkName));
-
-  Entity _entityFromIntentParameter({
-    fidl.IntentParameter parameter,
-    EntityCodec codec,
-  }) {
-    if (parameter.data.tag == fidl.IntentParameterDataTag.linkName) {
-      return LinkEntity(linkName: parameter.data.linkName, codec: codec);
+  Entity _entityFromIntentParameter(
+      fidl.IntentParameter parameter, String type) {
+    if (parameter.data.tag == fidl.IntentParameterDataTag.entityReference) {
+      return Entity(
+          type: type, entityReference: parameter.data.entityReference);
     }
     throw UnimplementedError();
   }
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;
-}