mojo/discovery: update according to changes in v23 discovery

  Update mojo/discovery according to recent changes in v23 discovery.

Change-Id: Ida26b851a2db107ce4e3fed45c70423e85c7fabc
diff --git a/Makefile b/Makefile
index c2acbc4..940f314 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,8 @@
 include ../shared/mojo.mk
-V23_GO_FILES := $(shell find $(JIRI_ROOT) -name "*.go")
 
+ifndef MOJO_DIR
+        $(error MOJO_DIR is not set)
+endif
 
 ifdef ANDROID
 	DISCOVERY_BUILD_DIR := $(PWD)/gen/mojo/android
@@ -18,43 +20,59 @@
 	CGO_CXXFLAGS="-I$(MOJO_DIR)/src $(CGO_CXXFLAGS)" \
 	CGO_LDFLAGS="-L$(dir $(MOJO_SHARED_LIB)) -lsystem_thunk $(CGO_LDFLAGS)" \
 	$(GOROOT)/bin/go test -v $1
-
 endef
-all: $(DISCOVERY_BUILD_DIR)/discovery.mojo
+
+V23_GO_FILES := $(shell find $(JIRI_ROOT) -name "*.go")
+
+all: build
 
 # Installs dart dependencies.
 .PHONY: packages
 packages:
 	pub upgrade
 
+.PHONY: build
+build: packages $(DISCOVERY_BUILD_DIR)/discovery.mojo gen-mojom
+
+.PHONY: test
+test: discovery-test
+
+.PHONY: gen-mojom
+gen-mojom: go/src/mojom/vanadium/discovery/discovery.mojom.go lib/gen/dart-gen/mojom/lib/mojo/discovery.mojom.dart
+
 go/src/mojom/vanadium/discovery/discovery.mojom.go: mojom/vanadium/discovery.mojom | mojo-env-check
 	$(call MOJOM_GEN,$<,.,.,go)
 	gofmt -w $@
 
-go/src/mojom/vanadium/discovery/discovery.mojom.dart: mojom/vanadium/discovery.mojom packages | mojo-env-check
+lib/gen/dart-gen/mojom/lib/mojo/discovery.mojom.dart: mojom/vanadium/discovery.mojom | mojo-env-check
 	$(call MOJOM_GEN,$<,.,lib/gen,dart)
 	# TODO(nlacasse): mojom_bindings_generator creates bad symlinks on dart
 	# files, so we delete them.  Stop doing this once the generator is fixed.
 	# See https://github.com/domokit/mojo/issues/386
 	rm -f lib/gen/mojom/$(notdir $@)
 
-$(DISCOVERY_BUILD_DIR)/discovery.mojo: $(V23_GO_FILES) $(MOJO_SHARED_LIB) go/src/mojom/vanadium/discovery/discovery.mojom.go go/src/mojom/vanadium/discovery/discovery.mojom.dart | mojo-env-check
+$(DISCOVERY_BUILD_DIR)/discovery.mojo: $(V23_GO_FILES) $(MOJO_SHARED_LIB) | mojo-env-check
 	$(call MOGO_BUILD,vanadium/discovery,$@)
 
+discovery-test: $(V23_GO_FILES) $(MOJO_SHARED_LIB) go/src/mojom/vanadium/discovery/discovery.mojom.go | mojo-env-check
+	$(call CGO_TEST,vanadium/discovery/internal)
+
+clean:
+	rm -rf gen
+	rm -rf lib/gen/dart-pkg
+	rm -rf lib/gen/mojom
+
+# Examples.
+run-advertiser: $(DISCOVERY_BUILD_DIR)/advertiser.mojo $(DISCOVERY_BUILD_DIR)/discovery.mojo
+	$(MOJO_DIR)/src/mojo/devtools/common/mojo_run --config-file $(PWD)/mojoconfig $(MOJO_SHELL_FLAGS) $(MOJO_ANDROID_FLAGS) https://mojo.v.io/advertiser.mojo \
+	--args-for="https://mojo.v.io/discovery.mojo"
+
+run-scanner: $(DISCOVERY_BUILD_DIR)/scanner.mojo $(DISCOVERY_BUILD_DIR)/discovery.mojo
+	$(MOJO_DIR)/src/mojo/devtools/common/mojo_run --config-file $(PWD)/mojoconfig $(MOJO_SHELL_FLAGS) $(MOJO_ANDROID_FLAGS) https://mojo.v.io/scanner.mojo \
+	--args-for="https://mojo.v.io/discovery.mojo"
+
 $(DISCOVERY_BUILD_DIR)/advertiser.mojo: $(V23_GO_FILES) $(MOJO_SHARED_LIB) go/src/mojom/vanadium/discovery/discovery.mojom.go | mojo-env-check
 	$(call MOGO_BUILD,examples/advertiser,$@)
 
 $(DISCOVERY_BUILD_DIR)/scanner.mojo: $(V23_GO_FILES) $(MOJO_SHARED_LIB) go/src/mojom/vanadium/discovery/discovery.mojom.go | mojo-env-check
 	$(call MOGO_BUILD,examples/scanner,$@)
-
-discovery_test: $(V23_GO_FILES) $(MOJO_SHARED_LIB) go/src/mojom/vanadium/discovery/discovery.mojom.go | mojo-env-check
-	echo $(GOPATH)
-	$(call CGO_TEST,vanadium/discovery/internal)
-
-run-advertiser: $(DISCOVERY_BUILD_DIR)/advertiser.mojo $(DISCOVERY_BUILD_DIR)/discovery.mojo
-	sudo $(MOJO_DIR)/src/mojo/devtools/common/mojo_run --config-file $(PWD)/mojoconfig $(MOJO_SHELL_FLAGS) $(MOJO_ANDROID_FLAGS) https://mojo.v.io/advertiser.mojo \
-	--args-for="https://mojo.v.io/discovery.mojo host1 ble mdns"
-
-run-scanner: $(DISCOVERY_BUILD_DIR)/scanner.mojo $(DISCOVERY_BUILD_DIR)/discovery.mojo
-	sudo $(MOJO_DIR)/src/mojo/devtools/common/mojo_run --config-file $(PWD)/mojoconfig $(MOJO_SHELL_FLAGS) $(MOJO_ANDROID_FLAGS) https://mojo.v.io/scanner.mojo \
-	--args-for="https://mojo.v.io/discovery.mojo host2 ble mdns"
diff --git a/go/src/mojom/vanadium/discovery/discovery.mojom.go b/go/src/mojom/vanadium/discovery/discovery.mojom.go
index 6d35293..f39fb62 100644
--- a/go/src/mojom/vanadium/discovery/discovery.mojom.go
+++ b/go/src/mojom/vanadium/discovery/discovery.mojom.go
@@ -86,7 +86,7 @@
 }
 
 type Advertiser interface {
-	Advertise(inS Service, inVisibility []string) (outHandle uint32, outErr *Error, err error)
+	Advertise(inService Service, inVisibility []string) (outHandle uint32, outErr *Error, err error)
 	Stop(inH uint32) (err error)
 }
 
@@ -152,7 +152,7 @@
 }
 
 type advertiser_Advertise_Params struct {
-	inS          Service
+	inService    Service
 	inVisibility []string
 }
 
@@ -161,7 +161,7 @@
 	if err := encoder.WritePointer(); err != nil {
 		return err
 	}
-	if err := s.inS.Encode(encoder); err != nil {
+	if err := s.inService.Encode(encoder); err != nil {
 		return err
 	}
 	if err := encoder.WritePointer(); err != nil {
@@ -216,7 +216,7 @@
 		if pointer0 == 0 {
 			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
 		} else {
-			if err := s.inS.Decode(decoder); err != nil {
+			if err := s.inService.Decode(decoder); err != nil {
 				return err
 			}
 		}
@@ -263,7 +263,7 @@
 // String names and labels used by the MojomStruct types.
 var (
 	structName_AdvertiserAdvertiseParams                   = "AdvertiserAdvertiseParams"
-	structFieldName_AdvertiserAdvertiseParams_InS          = "InS"
+	structFieldName_AdvertiserAdvertiseParams_InService    = "InService"
 	structFieldName_AdvertiserAdvertiseParams_InVisibility = "InVisibility"
 )
 
@@ -273,7 +273,7 @@
 			ShortName: &structName_AdvertiserAdvertiseParams,
 		}, Fields: []mojom_types.StructField{mojom_types.StructField{
 			DeclData: &mojom_types.DeclarationData{
-				ShortName: &structFieldName_AdvertiserAdvertiseParams_InS,
+				ShortName: &structFieldName_AdvertiserAdvertiseParams_InService,
 			},
 			Type: &mojom_types.TypeTypeReference{
 				Value: mojom_types.TypeReference{Identifier: &ID_discovery_Service__,
@@ -394,9 +394,9 @@
 	}
 }
 
-func (p *Advertiser_Proxy) Advertise(inS Service, inVisibility []string) (outHandle uint32, outErr *Error, err error) {
+func (p *Advertiser_Proxy) Advertise(inService Service, inVisibility []string) (outHandle uint32, outErr *Error, err error) {
 	payload := &advertiser_Advertise_Params{
-		inS,
+		inService,
 		inVisibility,
 	}
 	header := bindings.MessageHeader{
@@ -606,7 +606,7 @@
 			return err
 		}
 		var response advertiser_Advertise_ResponseParams
-		response.outHandle, response.outErr, err = s.impl.Advertise(request.inS, request.inVisibility)
+		response.outHandle, response.outErr, err = s.impl.Advertise(request.inService, request.inVisibility)
 		if err != nil {
 			return
 		}
@@ -1169,7 +1169,7 @@
 }
 
 type ScanHandler interface {
-	Found(inS Service) (err error)
+	Found(inService Service) (err error)
 	Lost(inInstanceId []uint8) (err error)
 }
 
@@ -1235,7 +1235,7 @@
 }
 
 type scanHandler_Found_Params struct {
-	inS Service
+	inService Service
 }
 
 func (s *scanHandler_Found_Params) Encode(encoder *bindings.Encoder) error {
@@ -1243,7 +1243,7 @@
 	if err := encoder.WritePointer(); err != nil {
 		return err
 	}
-	if err := s.inS.Encode(encoder); err != nil {
+	if err := s.inService.Encode(encoder); err != nil {
 		return err
 	}
 	if err := encoder.Finish(); err != nil {
@@ -1283,7 +1283,7 @@
 		if pointer0 == 0 {
 			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
 		} else {
-			if err := s.inS.Decode(decoder); err != nil {
+			if err := s.inService.Decode(decoder); err != nil {
 				return err
 			}
 		}
@@ -1296,8 +1296,8 @@
 
 // String names and labels used by the MojomStruct types.
 var (
-	structName_ScanHandlerFoundParams          = "ScanHandlerFoundParams"
-	structFieldName_ScanHandlerFoundParams_InS = "InS"
+	structName_ScanHandlerFoundParams                = "ScanHandlerFoundParams"
+	structFieldName_ScanHandlerFoundParams_InService = "InService"
 )
 
 func discovery_ScanHandler_Found_Params__() mojom_types.MojomStruct {
@@ -1306,7 +1306,7 @@
 			ShortName: &structName_ScanHandlerFoundParams,
 		}, Fields: []mojom_types.StructField{mojom_types.StructField{
 			DeclData: &mojom_types.DeclarationData{
-				ShortName: &structFieldName_ScanHandlerFoundParams_InS,
+				ShortName: &structFieldName_ScanHandlerFoundParams_InService,
 			},
 			Type: &mojom_types.TypeTypeReference{
 				Value: mojom_types.TypeReference{Identifier: &ID_discovery_Service__,
@@ -1316,9 +1316,9 @@
 	}
 }
 
-func (p *ScanHandler_Proxy) Found(inS Service) (err error) {
+func (p *ScanHandler_Proxy) Found(inService Service) (err error) {
 	payload := &scanHandler_Found_Params{
-		inS,
+		inService,
 	}
 	header := bindings.MessageHeader{
 		Type:  scanHandler_Found_Name,
@@ -1532,7 +1532,7 @@
 		if err := message.DecodePayload(&request); err != nil {
 			return err
 		}
-		err = s.impl.Found(request.inS)
+		err = s.impl.Found(request.inService)
 		if err != nil {
 			return
 		}
diff --git a/go/src/vanadium/discovery/discovery.go b/go/src/vanadium/discovery/discovery.go
index 1a829ac..86fce64 100644
--- a/go/src/vanadium/discovery/discovery.go
+++ b/go/src/vanadium/discovery/discovery.go
@@ -5,19 +5,19 @@
 package main
 
 import (
-	"v.io/v23"
-	discovery_factory "v.io/x/ref/lib/discovery/factory"
-
-	_ "v.io/x/ref/runtime/factories/generic"
-
-	mojom "mojom/vanadium/discovery"
-	"vanadium/discovery/internal"
+	"sync"
 
 	"mojo/public/go/application"
 	"mojo/public/go/bindings"
 	"mojo/public/go/system"
-	"sync"
+	mojom "mojom/vanadium/discovery"
+
+	"v.io/v23"
 	"v.io/v23/context"
+
+	_ "v.io/x/ref/runtime/factories/generic"
+
+	"vanadium/discovery/internal"
 )
 
 //#include "mojo/public/c/system/types.h"
@@ -32,8 +32,8 @@
 	stubs map[*bindings.Stub]struct{}
 
 	ctx      *context.T
-	impl     *internal.DiscoveryService
 	shutdown v23.Shutdown
+	impl     *internal.DiscoveryService
 }
 
 func (d *discoveryDelegate) Initialize(c application.Context) {
@@ -41,19 +41,8 @@
 	// will be bad.  For now, this is ok because this is the only
 	// vanadium service that will be used in the demos and each go library
 	// will be in its own process.
-	ctx, shutdown := v23.Init()
-
-	if len(c.Args()) <= 2 {
-		ctx.Fatalf("Not enough arguments passed to discovery.mojo. Given: %v. Pass a name to advertise, followed by 1+ discovery protocols.", c)
-	}
-	// TODO(bjornick): Change this to use the factory to determine which protocols to use.
-	inst, err := discovery_factory.New(c.Args()[2:]...)
-	if err != nil {
-		ctx.Fatalf("failed to initalize discovery: %v", err)
-	}
-
-	d.impl = internal.NewDiscoveryService(ctx, inst)
-	d.shutdown = shutdown
+	d.ctx, d.shutdown = v23.Init()
+	d.impl = internal.NewDiscoveryService(d.ctx)
 }
 
 func (d *discoveryDelegate) addAndServeStub(stub *bindings.Stub) {
diff --git a/go/src/vanadium/discovery/internal/discovery.go b/go/src/vanadium/discovery/internal/discovery.go
index c577c59..3ae5da2 100644
--- a/go/src/vanadium/discovery/internal/discovery.go
+++ b/go/src/vanadium/discovery/internal/discovery.go
@@ -10,6 +10,7 @@
 	"mojo/public/go/bindings"
 	mojom "mojom/vanadium/discovery"
 
+	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/discovery"
 	"v.io/v23/security"
@@ -21,8 +22,8 @@
 // DiscoveryService implements the mojom interface mojom/vanadium/discovery.DiscoveryService.  It
 // is basically a thin wrapper around the Vanadium Discovery API.
 type DiscoveryService struct {
-	ctx *context.T
-	s   discovery.T
+	ctx       *context.T
+	discovery discovery.T
 
 	// mu protects pending* and next*
 	mu sync.Mutex
@@ -47,10 +48,10 @@
 
 // NewDiscoveryService returns a new DiscoveryService bound to the context and the Vanadium
 // Discovery implementation passed in.
-func NewDiscoveryService(ctx *context.T, vDiscovery discovery.T) *DiscoveryService {
+func NewDiscoveryService(ctx *context.T) *DiscoveryService {
 	return &DiscoveryService{
 		ctx:         ctx,
-		s:           vDiscovery,
+		discovery:   v23.GetDiscovery(ctx),
 		nextAdv:     1,
 		activeAdvs:  map[id]func(){},
 		activeScans: map[id]func(){},
@@ -74,7 +75,7 @@
 	for i, pattern := range patterns {
 		perms[i] = security.BlessingPattern(pattern)
 	}
-	done, err := d.s.Advertise(ctx, vService, perms)
+	done, err := d.discovery.Advertise(ctx, vService, perms)
 	if err != nil {
 		cancel()
 		return 0, v2mError(err), nil
@@ -115,7 +116,7 @@
 // Returns the handle to this Scan.
 func (d *DiscoveryService) Scan(query string, scanHandler mojom.ScanHandler_Pointer) (uint32, *mojom.Error, error) {
 	ctx, cancel := context.WithCancel(d.ctx)
-	scanCh, err := d.s.Scan(ctx, query)
+	scanCh, err := d.discovery.Scan(ctx, query)
 	if err != nil {
 		cancel()
 		return 0, v2mError(err), nil
@@ -169,6 +170,6 @@
 	for _, cancel := range d.activeAdvs {
 		cancel()
 	}
-	d.s.Close()
+	d.discovery.Close()
 	d.mu.Unlock()
 }
diff --git a/go/src/vanadium/discovery/internal/discovery_test.go b/go/src/vanadium/discovery/internal/discovery_test.go
index b9ff152..6aa62b4 100644
--- a/go/src/vanadium/discovery/internal/discovery_test.go
+++ b/go/src/vanadium/discovery/internal/discovery_test.go
@@ -18,15 +18,12 @@
 	"v.io/v23/security"
 
 	idiscovery "v.io/x/ref/lib/discovery"
+	dfactory "v.io/x/ref/lib/discovery/factory"
 	_ "v.io/x/ref/runtime/factories/generic"
 	vtest "v.io/x/ref/test"
 )
 
-type mockAdv struct {
-	s discovery.Service
-}
-
-type discoveryMock struct {
+type mockDiscovery struct {
 	mu       sync.Mutex
 	trigger  *idiscovery.Trigger
 	id       int64
@@ -35,7 +32,7 @@
 	deleteCh chan struct{}
 }
 
-func (d *discoveryMock) Advertise(ctx *context.T, s discovery.Service, perms []security.BlessingPattern) (<-chan struct{}, error) {
+func (d *mockDiscovery) Advertise(ctx *context.T, s discovery.Service, perms []security.BlessingPattern) (<-chan struct{}, error) {
 	d.mu.Lock()
 	currId := d.id
 	d.services[currId] = s
@@ -54,11 +51,11 @@
 	return done, nil
 }
 
-func (*discoveryMock) Scan(ctx *context.T, query string) (<-chan discovery.Update, error) {
+func (*mockDiscovery) Scan(ctx *context.T, query string) (<-chan discovery.Update, error) {
 	return nil, nil
 }
 
-func (*discoveryMock) Close() {}
+func (*mockDiscovery) Close() {}
 
 func compare(want discovery.Service, got mojom.Service) error {
 	mwant := v2mService(want)
@@ -69,14 +66,17 @@
 }
 
 func TestAdvertising(t *testing.T) {
-	ctx, shutdown := vtest.V23Init()
-	defer shutdown()
-	mock := &discoveryMock{
+	mock := &mockDiscovery{
 		trigger:  idiscovery.NewTrigger(),
 		services: map[int64]discovery.Service{},
 		deleteCh: make(chan struct{}),
 	}
-	s := NewDiscoveryService(ctx, mock)
+	dfactory.InjectDiscovery(mock)
+
+	ctx, shutdown := vtest.V23Init()
+	defer shutdown()
+
+	s := NewDiscoveryService(ctx)
 	testService := mojom.Service{
 		InterfaceName: "v.io/v23/discovery.T",
 		Attrs: map[string]string{
diff --git a/lib/gen/dart-gen/mojom/lib/discovery/discovery.mojom.dart b/lib/gen/dart-gen/mojom/lib/discovery/discovery.mojom.dart
index 89d46bb..60a3e41 100644
--- a/lib/gen/dart-gen/mojom/lib/discovery/discovery.mojom.dart
+++ b/lib/gen/dart-gen/mojom/lib/discovery/discovery.mojom.dart
@@ -269,7 +269,7 @@
   static const List<bindings.StructDataHeader> kVersions = const [
     const bindings.StructDataHeader(24, 0)
   ];
-  Service s = null;
+  Service service = null;
   List<String> visibility = null;
 
   AdvertiserAdvertiseParams() : super(kVersions.last.size);
@@ -308,7 +308,7 @@
     if (mainDataHeader.version >= 0) {
       
       var decoder1 = decoder0.decodePointer(8, false);
-      result.s = Service.decode(decoder1);
+      result.service = Service.decode(decoder1);
     }
     if (mainDataHeader.version >= 0) {
       
@@ -328,7 +328,7 @@
   void encode(bindings.Encoder encoder) {
     var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
     
-    encoder0.encodeStruct(s, 8, false);
+    encoder0.encodeStruct(service, 8, false);
     
     if (visibility == null) {
       encoder0.encodeNullPointer(16, false);
@@ -343,13 +343,13 @@
 
   String toString() {
     return "AdvertiserAdvertiseParams("
-           "s: $s" ", "
+           "service: $service" ", "
            "visibility: $visibility" ")";
   }
 
   Map toJson() {
     Map map = new Map();
-    map["s"] = s;
+    map["service"] = service;
     map["visibility"] = visibility;
     return map;
   }
@@ -712,7 +712,7 @@
   static const List<bindings.StructDataHeader> kVersions = const [
     const bindings.StructDataHeader(16, 0)
   ];
-  Service s = null;
+  Service service = null;
 
   ScanHandlerFoundParams() : super(kVersions.last.size);
 
@@ -750,7 +750,7 @@
     if (mainDataHeader.version >= 0) {
       
       var decoder1 = decoder0.decodePointer(8, false);
-      result.s = Service.decode(decoder1);
+      result.service = Service.decode(decoder1);
     }
     return result;
   }
@@ -758,17 +758,17 @@
   void encode(bindings.Encoder encoder) {
     var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
     
-    encoder0.encodeStruct(s, 8, false);
+    encoder0.encodeStruct(service, 8, false);
   }
 
   String toString() {
     return "ScanHandlerFoundParams("
-           "s: $s" ")";
+           "service: $service" ")";
   }
 
   Map toJson() {
     Map map = new Map();
-    map["s"] = s;
+    map["service"] = service;
     return map;
   }
 }
@@ -845,7 +845,7 @@
       'discovery::Advertiser';
 
 abstract class Advertiser {
-  dynamic advertise(Service s,List<String> visibility,[Function responseFactory = null]);
+  dynamic advertise(Service service,List<String> visibility,[Function responseFactory = null]);
   void stop(int h);
 
 }
@@ -874,18 +874,25 @@
         var r = AdvertiserAdvertiseResponseParams.deserialize(
             message.payload);
         if (!message.header.hasRequestId) {
-          throw 'Expected a message with a valid request Id.';
+          proxyError("Expected a message with a valid request Id.");
+          return;
         }
         Completer c = completerMap[message.header.requestId];
         if (c == null) {
-          throw 'Message had unknown request Id: ${message.header.requestId}';
+          proxyError(
+              "Message had unknown request Id: ${message.header.requestId}");
+          return;
         }
         completerMap.remove(message.header.requestId);
-        assert(!c.isCompleted);
+        if (c.isCompleted) {
+          proxyError("Response completer already completed");
+          return;
+        }
         c.complete(r);
         break;
       default:
-        throw new bindings.MojoCodecError("Unexpected message name");
+        proxyError("Unexpected message type: ${message.header.type}");
+        close(immediate: true);
         break;
     }
   }
@@ -901,10 +908,9 @@
   AdvertiserProxyImpl _proxyImpl;
 
   _AdvertiserProxyCalls(this._proxyImpl);
-    dynamic advertise(Service s,List<String> visibility,[Function responseFactory = null]) {
-      assert(_proxyImpl.isBound);
+    dynamic advertise(Service service,List<String> visibility,[Function responseFactory = null]) {
       var params = new AdvertiserAdvertiseParams();
-      params.s = s;
+      params.service = service;
       params.visibility = visibility;
       return _proxyImpl.sendMessageWithRequestId(
           params,
@@ -913,7 +919,10 @@
           bindings.MessageHeader.kMessageExpectsResponse);
     }
     void stop(int h) {
-      assert(_proxyImpl.isBound);
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
       var params = new AdvertiserStopParams();
       params.h = h;
       _proxyImpl.sendMessage(params, kAdvertiser_stop_name);
@@ -962,6 +971,10 @@
 
   Future close({bool immediate: false}) => impl.close(immediate: immediate);
 
+  Future responseOrError(Future f) => impl.responseOrError(f);
+
+  Future get errorFuture => impl.errorFuture;
+
   int get version => impl.version;
 
   Future<int> queryVersion() => impl.queryVersion();
@@ -1015,7 +1028,7 @@
       case kAdvertiser_advertise_name:
         var params = AdvertiserAdvertiseParams.deserialize(
             message.payload);
-        var response = _impl.advertise(params.s,params.visibility,_AdvertiserAdvertiseResponseParamsFactory);
+        var response = _impl.advertise(params.service,params.visibility,_AdvertiserAdvertiseResponseParamsFactory);
         if (response is Future) {
           return response.then((response) {
             if (response != null) {
@@ -1096,18 +1109,25 @@
         var r = ScannerScanResponseParams.deserialize(
             message.payload);
         if (!message.header.hasRequestId) {
-          throw 'Expected a message with a valid request Id.';
+          proxyError("Expected a message with a valid request Id.");
+          return;
         }
         Completer c = completerMap[message.header.requestId];
         if (c == null) {
-          throw 'Message had unknown request Id: ${message.header.requestId}';
+          proxyError(
+              "Message had unknown request Id: ${message.header.requestId}");
+          return;
         }
         completerMap.remove(message.header.requestId);
-        assert(!c.isCompleted);
+        if (c.isCompleted) {
+          proxyError("Response completer already completed");
+          return;
+        }
         c.complete(r);
         break;
       default:
-        throw new bindings.MojoCodecError("Unexpected message name");
+        proxyError("Unexpected message type: ${message.header.type}");
+        close(immediate: true);
         break;
     }
   }
@@ -1124,7 +1144,6 @@
 
   _ScannerProxyCalls(this._proxyImpl);
     dynamic scan(String query,Object scanHandler,[Function responseFactory = null]) {
-      assert(_proxyImpl.isBound);
       var params = new ScannerScanParams();
       params.query = query;
       params.scanHandler = scanHandler;
@@ -1135,7 +1154,10 @@
           bindings.MessageHeader.kMessageExpectsResponse);
     }
     void stop(int h) {
-      assert(_proxyImpl.isBound);
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
       var params = new ScannerStopParams();
       params.h = h;
       _proxyImpl.sendMessage(params, kScanner_stop_name);
@@ -1184,6 +1206,10 @@
 
   Future close({bool immediate: false}) => impl.close(immediate: immediate);
 
+  Future responseOrError(Future f) => impl.responseOrError(f);
+
+  Future get errorFuture => impl.errorFuture;
+
   int get version => impl.version;
 
   Future<int> queryVersion() => impl.queryVersion();
@@ -1289,7 +1315,7 @@
       'discovery::ScanHandler';
 
 abstract class ScanHandler {
-  void found(Service s);
+  void found(Service service);
   void lost(List<int> instanceId);
 
 }
@@ -1315,7 +1341,8 @@
   void handleResponse(bindings.ServiceMessage message) {
     switch (message.header.type) {
       default:
-        throw new bindings.MojoCodecError("Unexpected message name");
+        proxyError("Unexpected message type: ${message.header.type}");
+        close(immediate: true);
         break;
     }
   }
@@ -1331,15 +1358,21 @@
   ScanHandlerProxyImpl _proxyImpl;
 
   _ScanHandlerProxyCalls(this._proxyImpl);
-    void found(Service s) {
-      assert(_proxyImpl.isBound);
+    void found(Service service) {
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
       var params = new ScanHandlerFoundParams();
-      params.s = s;
+      params.service = service;
       _proxyImpl.sendMessage(params, kScanHandler_found_name);
     }
   
     void lost(List<int> instanceId) {
-      assert(_proxyImpl.isBound);
+      if (!_proxyImpl.isBound) {
+        _proxyImpl.proxyError("The Proxy is closed.");
+        return;
+      }
       var params = new ScanHandlerLostParams();
       params.instanceId = instanceId;
       _proxyImpl.sendMessage(params, kScanHandler_lost_name);
@@ -1388,6 +1421,10 @@
 
   Future close({bool immediate: false}) => impl.close(immediate: immediate);
 
+  Future responseOrError(Future f) => impl.responseOrError(f);
+
+  Future get errorFuture => impl.errorFuture;
+
   int get version => impl.version;
 
   Future<int> queryVersion() => impl.queryVersion();
@@ -1435,7 +1472,7 @@
       case kScanHandler_found_name:
         var params = ScanHandlerFoundParams.deserialize(
             message.payload);
-        _impl.found(params.s);
+        _impl.found(params.service);
         break;
       case kScanHandler_lost_name:
         var params = ScanHandlerLostParams.deserialize(
diff --git a/mojom/vanadium/discovery.mojom b/mojom/vanadium/discovery.mojom
index b5c960c..f61ccdb 100644
--- a/mojom/vanadium/discovery.mojom
+++ b/mojom/vanadium/discovery.mojom
@@ -6,57 +6,70 @@
 
 // Copied from v.io/v23/discovery/types.vdl
 struct Service {
-    // The 128 bit (16 byte) universal unique identifier of a service instance.
-	// If this is not specified, a random UUID will be used.
-	array<uint8> InstanceUuid;
-	// Optional name of the service instance.
-	string InstanceName;
-	// The interface that the service implements.
-	// E.g., 'v.io/v23/services/vtrace.Store'.
-	string InterfaceName;
-	// The service attributes.
-	// E.g., {'resolution': '1024x768'}.
-	map<string, string> Attrs;
-	// The addresses that the service is served on.
-	// E.g., '/host:port/a/b/c'.
-	array<string> Addrs;
+  // The 128 bit (16 byte) universal unique identifier of a service instance.
+  // If this is not specified, a random UUID will be used.
+  array<uint8> InstanceUuid;
+  // Optional name of the service instance.
+  string InstanceName;
+  // The interface that the service implements.
+  // E.g., 'v.io/v23/services/vtrace.Store'.
+  string InterfaceName;
+  // The service attributes.
+  // E.g., {'resolution': '1024x768'}.
+  map<string, string> Attrs;
+  // The addresses that the service is served on.
+  // E.g., '/host:port/a/b/c'.
+  array<string> Addrs;
 };
 
 struct Error {
-   string id;
-   int32 action;
-   string msg;
+  string id;
+  int32 action;
+  string msg;
 };
 
 // Advertiser provides methods to do Vanadium Advertising.
 interface Advertiser {
-    // Advertise advertises the service to be discovered by "Scanner" implementations.
-	// visibility is used to limit the principals that can see the advertisement. An
-	// empty set means that there are no restrictions on visibility (i.e, equivalent
-	// to []security.BlessingPattern{security.AllPrincipals}). Advertising will continue
-	// until the context is canceled or exceeds its deadline.
-	Advertise(Service s, array<string> visibility) => (uint32 Handle, Error? Err);
+  // Advertise advertises the service to be discovered by "Scanner" implementations.
+  // visibility is used to limit the principals that can see the advertisement. An
+  // empty set means that there are no restrictions on visibility (i.e, equivalent
+  // to []security.BlessingPattern{security.AllPrincipals}).
+  //
+  // It is an error to have simultaneously active advertisements for two identical
+  // instances (service.InstanceUuid).
+  Advertise(Service service, array<string> visibility) => (uint32 Handle, Error? Err);
 
-	// Stop Stops the advertisement associated with the given handle.
-	Stop(uint32 h);
+  // Stop Stops the advertisement associated with the given handle.
+  Stop(uint32 h);
 };
 
 // Scanner provides methods to scan for Vanadium advertisements.
 interface Scanner {
-	// Scan scans for services matching the query passed and calls ScanHandler with updates.
-	// Returns a handle to the active scanner that can be used to stop the scanning.
-	Scan(string query, ScanHandler scanHandler) => (uint32 Handle, Error? Err);
+  // Scan scans for services matching the query passed and calls ScanHandler with updates.
+  //
+  // The query is a WHERE expression of syncQL query against scanned services, where
+  // keys are InstanceUuids and values are Service.
+  //
+  // Examples
+  //
+  //    v.InstanceName = "v.io/i"
+  //    v.InstanceName = "v.io/i" AND v.Attrs["a"] = "v"
+  //    v.Attrs["a"] = "v1" OR v.Attrs["a"] = "v2"
+  //
+  // SyncQL tutorial at:
+  //    https://github.com/vanadium/docs/blob/master/tutorials/syncql-tutorial.md
+  Scan(string query, ScanHandler scanHandler) => (uint32 Handle, Error? Err);
 
-	// StopScan stops the scanner associated weith the given handle.
-	Stop(uint32 h);
+  // StopScan stops the scanner associated weith the given handle.
+  Stop(uint32 h);
 };
 
-// ScanHandler is used to pass updates about Services that are found/lost during the
-// scan.
+// ScanHandler is used to pass updates about Services that are found/lost during
+// the scan.
 interface ScanHandler {
-	// Found will be called when a Service is found.
-	Found(Service s);
+  // Found will be called when a Service is found.
+  Found(Service service);
 
-	// Lost will be called when a service is lost.
-	Lost(array<uint8> instanceId);
-};
\ No newline at end of file
+  // Lost will be called when a service is lost.
+  Lost(array<uint8> instanceId);
+};