mojo/discovery: Add Dart generation rule and pubspec

This makes our Dart code into a package.
Until it is published, you can do a dependency_override to get it.

For example, the Croupier project would do this:

name: croupier
dependencies:
  discovery: ">=0.0.0 <0.1.0"
  syncbase: ">=0.0.0 <0.1.0"
  flutter: ">=0.0.3 <0.1.0"
  sky_tools: any
  test: any

dependency_overrides:
  discovery:
    path: ../../mojo/discovery

Note: The .mojom.go file was updated since Mojo has recently changed its
code generator.

Change-Id: I6d311a754a6cbe121d016b2b473cca23d8565142
diff --git a/.gitignore b/.gitignore
index d1b26e4..0530729 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,10 @@
 /.jiri
 #TODO(nlacasse): Get rid of .v23 below once v23->jiri transition is complete.
 /.v23
-gen
+/gen
+/lib/gen/dart-pkg
+/lib/gen/mojom
+/packages
+/.packages
+/.pub
+/pubspec.lock
diff --git a/.jiriignore b/.jiriignore
index 0cb2b99..ed981e9 100644
--- a/.jiriignore
+++ b/.jiriignore
@@ -1 +1,2 @@
 go/src/mojom/vanadium/discovery/discovery.mojom.go
+lib/gen/dart-gen/mojom/lib/discovery/discovery.mojom.dart
diff --git a/Makefile b/Makefile
index c5e7152..c2acbc4 100644
--- a/Makefile
+++ b/Makefile
@@ -22,13 +22,23 @@
 endef
 all: $(DISCOVERY_BUILD_DIR)/discovery.mojo
 
+# Installs dart dependencies.
+.PHONY: packages
+packages:
+	pub upgrade
 
 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
+	$(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 | mojo-env-check
+$(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
 	$(call MOGO_BUILD,vanadium/discovery,$@)
 
 $(DISCOVERY_BUILD_DIR)/advertiser.mojo: $(V23_GO_FILES) $(MOJO_SHARED_LIB) go/src/mojom/vanadium/discovery/discovery.mojom.go | mojo-env-check
diff --git a/go/src/mojom/vanadium/discovery/discovery.mojom.go b/go/src/mojom/vanadium/discovery/discovery.mojom.go
index 0c0393f..6d35293 100644
--- a/go/src/mojom/vanadium/discovery/discovery.mojom.go
+++ b/go/src/mojom/vanadium/discovery/discovery.mojom.go
@@ -14,9 +14,77 @@
 	"fmt"
 	"mojo/public/go/bindings"
 	"mojo/public/go/system"
+	"mojo/public/interfaces/bindings/mojom_types"
+	"mojo/public/interfaces/bindings/service_describer"
 	"sort"
 )
 
+// These IDs are the Mojom Identifiers / Type Keys.
+// Mojom libraries importing this one will use these identifiers when building
+// TypeReference objects.
+var ID_discovery_Service__ string = "discovery_Service__"
+var ID_discovery_Error__ string = "discovery_Error__"
+var ID_discovery_Advertiser__ string = "discovery_Advertiser__"
+var ID_discovery_Scanner__ string = "discovery_Scanner__"
+var ID_discovery_ScanHandler__ string = "discovery_ScanHandler__"
+
+var discoveryDesc__ = make(map[string]mojom_types.UserDefinedType)
+
+func init() {
+	discoveryDesc__["discovery_Service__"] = &mojom_types.UserDefinedTypeStructType{
+		Value: discovery_Service__(),
+	}
+
+	discoveryDesc__["discovery_Error__"] = &mojom_types.UserDefinedTypeStructType{
+		Value: discovery_Error__(),
+	}
+
+	discoveryDesc__["discovery_Advertiser__"] = &mojom_types.UserDefinedTypeInterfaceType{
+		Value: discovery_Advertiser__(),
+	}
+	discoveryDesc__["discovery_Advertiser_Advertise_Params__"] = &mojom_types.UserDefinedTypeStructType{
+		Value: discovery_Advertiser_Advertise_Params__(),
+	}
+
+	discoveryDesc__["discovery_Advertiser_Advertise_ResponseParams__"] = &mojom_types.UserDefinedTypeStructType{
+		Value: discovery_Advertiser_Advertise_ResponseParams__(),
+	}
+
+	discoveryDesc__["discovery_Advertiser_Stop_Params__"] = &mojom_types.UserDefinedTypeStructType{
+		Value: discovery_Advertiser_Stop_Params__(),
+	}
+
+	discoveryDesc__["discovery_Scanner__"] = &mojom_types.UserDefinedTypeInterfaceType{
+		Value: discovery_Scanner__(),
+	}
+	discoveryDesc__["discovery_Scanner_Scan_Params__"] = &mojom_types.UserDefinedTypeStructType{
+		Value: discovery_Scanner_Scan_Params__(),
+	}
+
+	discoveryDesc__["discovery_ScanHandler__"] = &mojom_types.UserDefinedTypeInterfaceType{
+		Value: discovery_ScanHandler__(),
+	}
+	discoveryDesc__["discovery_ScanHandler_Found_Params__"] = &mojom_types.UserDefinedTypeStructType{
+		Value: discovery_ScanHandler_Found_Params__(),
+	}
+
+	discoveryDesc__["discovery_ScanHandler_Lost_Params__"] = &mojom_types.UserDefinedTypeStructType{
+		Value: discovery_ScanHandler_Lost_Params__(),
+	}
+
+	discoveryDesc__["discovery_Scanner_Scan_ResponseParams__"] = &mojom_types.UserDefinedTypeStructType{
+		Value: discovery_Scanner_Scan_ResponseParams__(),
+	}
+
+	discoveryDesc__["discovery_Scanner_Stop_Params__"] = &mojom_types.UserDefinedTypeStructType{
+		Value: discovery_Scanner_Stop_Params__(),
+	}
+
+}
+func GetAllMojomTypeDefinitions() map[string]mojom_types.UserDefinedType {
+	return discoveryDesc__
+}
+
 type Advertiser interface {
 	Advertise(inS Service, inVisibility []string) (outHandle uint32, outErr *Error, err error)
 	Stop(inH uint32) (err error)
@@ -48,6 +116,10 @@
 	return advertiser_Name
 }
 
+func (f *Advertiser_ServiceFactory) ServiceDescription() service_describer.ServiceDescription {
+	return &Advertiser_ServiceDescription{}
+}
+
 func (f *Advertiser_ServiceFactory) Create(messagePipe system.MessagePipeHandle) {
 	request := Advertiser_Request{bindings.NewMessagePipeHandleOwner(messagePipe)}
 	f.Delegate.Create(request)
@@ -188,6 +260,36 @@
 	return nil
 }
 
+// String names and labels used by the MojomStruct types.
+var (
+	structName_AdvertiserAdvertiseParams                   = "AdvertiserAdvertiseParams"
+	structFieldName_AdvertiserAdvertiseParams_InS          = "InS"
+	structFieldName_AdvertiserAdvertiseParams_InVisibility = "InVisibility"
+)
+
+func discovery_Advertiser_Advertise_Params__() mojom_types.MojomStruct {
+	return mojom_types.MojomStruct{
+		DeclData: &mojom_types.DeclarationData{
+			ShortName: &structName_AdvertiserAdvertiseParams,
+		}, Fields: []mojom_types.StructField{mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_AdvertiserAdvertiseParams_InS,
+			},
+			Type: &mojom_types.TypeTypeReference{
+				Value: mojom_types.TypeReference{Identifier: &ID_discovery_Service__,
+					TypeKey: &ID_discovery_Service__},
+			},
+		}, mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_AdvertiserAdvertiseParams_InVisibility,
+			},
+			Type: &mojom_types.TypeArrayType{
+				Value: mojom_types.ArrayType{ElementType: &mojom_types.TypeStringType{mojom_types.StringType{false}}},
+			},
+		}},
+	}
+}
+
 type advertiser_Advertise_ResponseParams struct {
 	outHandle uint32
 	outErr    *Error
@@ -264,6 +366,34 @@
 	return nil
 }
 
+// String names and labels used by the MojomStruct types.
+var (
+	structName_AdvertiserAdvertiseResponseParams                = "AdvertiserAdvertiseResponseParams"
+	structFieldName_AdvertiserAdvertiseResponseParams_OutHandle = "OutHandle"
+	structFieldName_AdvertiserAdvertiseResponseParams_OutErr    = "OutErr"
+)
+
+func discovery_Advertiser_Advertise_ResponseParams__() mojom_types.MojomStruct {
+	return mojom_types.MojomStruct{
+		DeclData: &mojom_types.DeclarationData{
+			ShortName: &structName_AdvertiserAdvertiseResponseParams,
+		}, Fields: []mojom_types.StructField{mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_AdvertiserAdvertiseResponseParams_OutHandle,
+			},
+			Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT32},
+		}, mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_AdvertiserAdvertiseResponseParams_OutErr,
+			},
+			Type: &mojom_types.TypeTypeReference{
+				Value: mojom_types.TypeReference{Nullable: true, Identifier: &ID_discovery_Error__,
+					TypeKey: &ID_discovery_Error__},
+			},
+		}},
+	}
+}
+
 func (p *Advertiser_Proxy) Advertise(inS Service, inVisibility []string) (outHandle uint32, outErr *Error, err error) {
 	payload := &advertiser_Advertise_Params{
 		inS,
@@ -358,6 +488,25 @@
 	return nil
 }
 
+// String names and labels used by the MojomStruct types.
+var (
+	structName_AdvertiserStopParams          = "AdvertiserStopParams"
+	structFieldName_AdvertiserStopParams_InH = "InH"
+)
+
+func discovery_Advertiser_Stop_Params__() mojom_types.MojomStruct {
+	return mojom_types.MojomStruct{
+		DeclData: &mojom_types.DeclarationData{
+			ShortName: &structName_AdvertiserStopParams,
+		}, Fields: []mojom_types.StructField{mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_AdvertiserStopParams_InH,
+			},
+			Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT32},
+		}},
+	}
+}
+
 func (p *Advertiser_Proxy) Stop(inH uint32) (err error) {
 	payload := &advertiser_Stop_Params{
 		inH,
@@ -389,6 +538,61 @@
 	return bindings.NewStub(connector, &advertiser_Stub{connector, impl})
 }
 
+var (
+	interfaceName_Advertiser                 = "Advertiser"
+	interfaceMethodName_Advertiser_Advertise = "Advertise"
+	interfaceMethodName_Advertiser_Stop      = "Stop"
+)
+
+func discovery_Advertiser__() mojom_types.MojomInterface {
+	responseParamsMap := make(map[string]*mojom_types.MojomStruct)
+	_ = responseParamsMap // To avoid the declared but unused compiler error
+	mstruct_Advertise := discovery_Advertiser_Advertise_ResponseParams__()
+	responseParamsMap[interfaceMethodName_Advertiser_Advertise] = &mstruct_Advertise
+	return mojom_types.MojomInterface{
+		DeclData: &mojom_types.DeclarationData{
+			ShortName: &interfaceName_Advertiser,
+		},
+		Methods: map[uint32]mojom_types.MojomMethod{advertiser_Advertise_Name: mojom_types.MojomMethod{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &interfaceMethodName_Advertiser_Advertise,
+			},
+			Parameters:     discovery_Advertiser_Advertise_Params__(),
+			ResponseParams: responseParamsMap[interfaceMethodName_Advertiser_Advertise],
+		}, advertiser_Stop_Name: mojom_types.MojomMethod{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &interfaceMethodName_Advertiser_Stop,
+			},
+			Parameters:     discovery_Advertiser_Stop_Params__(),
+			ResponseParams: responseParamsMap[interfaceMethodName_Advertiser_Stop],
+		}},
+	}
+}
+
+func (f *Advertiser_Request) ServiceDescription() service_describer.ServiceDescription {
+	return &Advertiser_ServiceDescription{}
+}
+
+type Advertiser_ServiceDescription struct{}
+
+func (sd *Advertiser_ServiceDescription) GetTopLevelInterface() (outMojomInterface mojom_types.MojomInterface, err error) {
+	return discovery_Advertiser__(), nil
+}
+
+func (sd *Advertiser_ServiceDescription) GetTypeDefinition(inTypeKey string) (outType mojom_types.UserDefinedType, err error) {
+	if udt, ok := GetAllMojomTypeDefinitions()[inTypeKey]; ok {
+		return udt, nil
+	}
+	return nil, fmt.Errorf("%s_ServiceDescription does not recognize %s", "Advertiser", inTypeKey)
+}
+
+func (sd *Advertiser_ServiceDescription) GetAllTypeDefinitions() (outDefinitions *map[string]mojom_types.UserDefinedType, err error) {
+	o := GetAllMojomTypeDefinitions()
+	return &o, nil
+}
+
+var _ service_describer.ServiceDescription = (*Advertiser_ServiceDescription)(nil)
+
 func (s *advertiser_Stub) Accept(message *bindings.Message) (err error) {
 	switch message.Header.Type {
 	case advertiser_Advertise_Name:
@@ -470,6 +674,10 @@
 	return scanner_Name
 }
 
+func (f *Scanner_ServiceFactory) ServiceDescription() service_describer.ServiceDescription {
+	return &Scanner_ServiceDescription{}
+}
+
 func (f *Scanner_ServiceFactory) Create(messagePipe system.MessagePipeHandle) {
 	request := Scanner_Request{bindings.NewMessagePipeHandleOwner(messagePipe)}
 	f.Delegate.Create(request)
@@ -579,6 +787,34 @@
 	return nil
 }
 
+// String names and labels used by the MojomStruct types.
+var (
+	structName_ScannerScanParams                    = "ScannerScanParams"
+	structFieldName_ScannerScanParams_InQuery       = "InQuery"
+	structFieldName_ScannerScanParams_InScanHandler = "InScanHandler"
+)
+
+func discovery_Scanner_Scan_Params__() mojom_types.MojomStruct {
+	return mojom_types.MojomStruct{
+		DeclData: &mojom_types.DeclarationData{
+			ShortName: &structName_ScannerScanParams,
+		}, Fields: []mojom_types.StructField{mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_ScannerScanParams_InQuery,
+			},
+			Type: &mojom_types.TypeStringType{mojom_types.StringType{false}},
+		}, mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_ScannerScanParams_InScanHandler,
+			},
+			Type: &mojom_types.TypeTypeReference{
+				Value: mojom_types.TypeReference{Identifier: &ID_discovery_ScanHandler__,
+					TypeKey: &ID_discovery_ScanHandler__},
+			},
+		}},
+	}
+}
+
 type scanner_Scan_ResponseParams struct {
 	outHandle uint32
 	outErr    *Error
@@ -655,6 +891,34 @@
 	return nil
 }
 
+// String names and labels used by the MojomStruct types.
+var (
+	structName_ScannerScanResponseParams                = "ScannerScanResponseParams"
+	structFieldName_ScannerScanResponseParams_OutHandle = "OutHandle"
+	structFieldName_ScannerScanResponseParams_OutErr    = "OutErr"
+)
+
+func discovery_Scanner_Scan_ResponseParams__() mojom_types.MojomStruct {
+	return mojom_types.MojomStruct{
+		DeclData: &mojom_types.DeclarationData{
+			ShortName: &structName_ScannerScanResponseParams,
+		}, Fields: []mojom_types.StructField{mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_ScannerScanResponseParams_OutHandle,
+			},
+			Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT32},
+		}, mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_ScannerScanResponseParams_OutErr,
+			},
+			Type: &mojom_types.TypeTypeReference{
+				Value: mojom_types.TypeReference{Nullable: true, Identifier: &ID_discovery_Error__,
+					TypeKey: &ID_discovery_Error__},
+			},
+		}},
+	}
+}
+
 func (p *Scanner_Proxy) Scan(inQuery string, inScanHandler ScanHandler_Pointer) (outHandle uint32, outErr *Error, err error) {
 	payload := &scanner_Scan_Params{
 		inQuery,
@@ -749,6 +1013,25 @@
 	return nil
 }
 
+// String names and labels used by the MojomStruct types.
+var (
+	structName_ScannerStopParams          = "ScannerStopParams"
+	structFieldName_ScannerStopParams_InH = "InH"
+)
+
+func discovery_Scanner_Stop_Params__() mojom_types.MojomStruct {
+	return mojom_types.MojomStruct{
+		DeclData: &mojom_types.DeclarationData{
+			ShortName: &structName_ScannerStopParams,
+		}, Fields: []mojom_types.StructField{mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_ScannerStopParams_InH,
+			},
+			Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT32},
+		}},
+	}
+}
+
 func (p *Scanner_Proxy) Stop(inH uint32) (err error) {
 	payload := &scanner_Stop_Params{
 		inH,
@@ -780,6 +1063,61 @@
 	return bindings.NewStub(connector, &scanner_Stub{connector, impl})
 }
 
+var (
+	interfaceName_Scanner            = "Scanner"
+	interfaceMethodName_Scanner_Scan = "Scan"
+	interfaceMethodName_Scanner_Stop = "Stop"
+)
+
+func discovery_Scanner__() mojom_types.MojomInterface {
+	responseParamsMap := make(map[string]*mojom_types.MojomStruct)
+	_ = responseParamsMap // To avoid the declared but unused compiler error
+	mstruct_Scan := discovery_Scanner_Scan_ResponseParams__()
+	responseParamsMap[interfaceMethodName_Scanner_Scan] = &mstruct_Scan
+	return mojom_types.MojomInterface{
+		DeclData: &mojom_types.DeclarationData{
+			ShortName: &interfaceName_Scanner,
+		},
+		Methods: map[uint32]mojom_types.MojomMethod{scanner_Scan_Name: mojom_types.MojomMethod{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &interfaceMethodName_Scanner_Scan,
+			},
+			Parameters:     discovery_Scanner_Scan_Params__(),
+			ResponseParams: responseParamsMap[interfaceMethodName_Scanner_Scan],
+		}, scanner_Stop_Name: mojom_types.MojomMethod{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &interfaceMethodName_Scanner_Stop,
+			},
+			Parameters:     discovery_Scanner_Stop_Params__(),
+			ResponseParams: responseParamsMap[interfaceMethodName_Scanner_Stop],
+		}},
+	}
+}
+
+func (f *Scanner_Request) ServiceDescription() service_describer.ServiceDescription {
+	return &Scanner_ServiceDescription{}
+}
+
+type Scanner_ServiceDescription struct{}
+
+func (sd *Scanner_ServiceDescription) GetTopLevelInterface() (outMojomInterface mojom_types.MojomInterface, err error) {
+	return discovery_Scanner__(), nil
+}
+
+func (sd *Scanner_ServiceDescription) GetTypeDefinition(inTypeKey string) (outType mojom_types.UserDefinedType, err error) {
+	if udt, ok := GetAllMojomTypeDefinitions()[inTypeKey]; ok {
+		return udt, nil
+	}
+	return nil, fmt.Errorf("%s_ServiceDescription does not recognize %s", "Scanner", inTypeKey)
+}
+
+func (sd *Scanner_ServiceDescription) GetAllTypeDefinitions() (outDefinitions *map[string]mojom_types.UserDefinedType, err error) {
+	o := GetAllMojomTypeDefinitions()
+	return &o, nil
+}
+
+var _ service_describer.ServiceDescription = (*Scanner_ServiceDescription)(nil)
+
 func (s *scanner_Stub) Accept(message *bindings.Message) (err error) {
 	switch message.Header.Type {
 	case scanner_Scan_Name:
@@ -861,6 +1199,10 @@
 	return scanHandler_Name
 }
 
+func (f *ScanHandler_ServiceFactory) ServiceDescription() service_describer.ServiceDescription {
+	return &ScanHandler_ServiceDescription{}
+}
+
 func (f *ScanHandler_ServiceFactory) Create(messagePipe system.MessagePipeHandle) {
 	request := ScanHandler_Request{bindings.NewMessagePipeHandleOwner(messagePipe)}
 	f.Delegate.Create(request)
@@ -952,6 +1294,28 @@
 	return nil
 }
 
+// String names and labels used by the MojomStruct types.
+var (
+	structName_ScanHandlerFoundParams          = "ScanHandlerFoundParams"
+	structFieldName_ScanHandlerFoundParams_InS = "InS"
+)
+
+func discovery_ScanHandler_Found_Params__() mojom_types.MojomStruct {
+	return mojom_types.MojomStruct{
+		DeclData: &mojom_types.DeclarationData{
+			ShortName: &structName_ScanHandlerFoundParams,
+		}, Fields: []mojom_types.StructField{mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_ScanHandlerFoundParams_InS,
+			},
+			Type: &mojom_types.TypeTypeReference{
+				Value: mojom_types.TypeReference{Identifier: &ID_discovery_Service__,
+					TypeKey: &ID_discovery_Service__},
+			},
+		}},
+	}
+}
+
 func (p *ScanHandler_Proxy) Found(inS Service) (err error) {
 	payload := &scanHandler_Found_Params{
 		inS,
@@ -1051,6 +1415,27 @@
 	return nil
 }
 
+// String names and labels used by the MojomStruct types.
+var (
+	structName_ScanHandlerLostParams                   = "ScanHandlerLostParams"
+	structFieldName_ScanHandlerLostParams_InInstanceId = "InInstanceId"
+)
+
+func discovery_ScanHandler_Lost_Params__() mojom_types.MojomStruct {
+	return mojom_types.MojomStruct{
+		DeclData: &mojom_types.DeclarationData{
+			ShortName: &structName_ScanHandlerLostParams,
+		}, Fields: []mojom_types.StructField{mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_ScanHandlerLostParams_InInstanceId,
+			},
+			Type: &mojom_types.TypeArrayType{
+				Value: mojom_types.ArrayType{ElementType: &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT8}},
+			},
+		}},
+	}
+}
+
 func (p *ScanHandler_Proxy) Lost(inInstanceId []uint8) (err error) {
 	payload := &scanHandler_Lost_Params{
 		inInstanceId,
@@ -1082,6 +1467,59 @@
 	return bindings.NewStub(connector, &scanHandler_Stub{connector, impl})
 }
 
+var (
+	interfaceName_ScanHandler             = "ScanHandler"
+	interfaceMethodName_ScanHandler_Found = "Found"
+	interfaceMethodName_ScanHandler_Lost  = "Lost"
+)
+
+func discovery_ScanHandler__() mojom_types.MojomInterface {
+	responseParamsMap := make(map[string]*mojom_types.MojomStruct)
+	_ = responseParamsMap // To avoid the declared but unused compiler error
+	return mojom_types.MojomInterface{
+		DeclData: &mojom_types.DeclarationData{
+			ShortName: &interfaceName_ScanHandler,
+		},
+		Methods: map[uint32]mojom_types.MojomMethod{scanHandler_Found_Name: mojom_types.MojomMethod{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &interfaceMethodName_ScanHandler_Found,
+			},
+			Parameters:     discovery_ScanHandler_Found_Params__(),
+			ResponseParams: responseParamsMap[interfaceMethodName_ScanHandler_Found],
+		}, scanHandler_Lost_Name: mojom_types.MojomMethod{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &interfaceMethodName_ScanHandler_Lost,
+			},
+			Parameters:     discovery_ScanHandler_Lost_Params__(),
+			ResponseParams: responseParamsMap[interfaceMethodName_ScanHandler_Lost],
+		}},
+	}
+}
+
+func (f *ScanHandler_Request) ServiceDescription() service_describer.ServiceDescription {
+	return &ScanHandler_ServiceDescription{}
+}
+
+type ScanHandler_ServiceDescription struct{}
+
+func (sd *ScanHandler_ServiceDescription) GetTopLevelInterface() (outMojomInterface mojom_types.MojomInterface, err error) {
+	return discovery_ScanHandler__(), nil
+}
+
+func (sd *ScanHandler_ServiceDescription) GetTypeDefinition(inTypeKey string) (outType mojom_types.UserDefinedType, err error) {
+	if udt, ok := GetAllMojomTypeDefinitions()[inTypeKey]; ok {
+		return udt, nil
+	}
+	return nil, fmt.Errorf("%s_ServiceDescription does not recognize %s", "ScanHandler", inTypeKey)
+}
+
+func (sd *ScanHandler_ServiceDescription) GetAllTypeDefinitions() (outDefinitions *map[string]mojom_types.UserDefinedType, err error) {
+	o := GetAllMojomTypeDefinitions()
+	return &o, nil
+}
+
+var _ service_describer.ServiceDescription = (*ScanHandler_ServiceDescription)(nil)
+
 func (s *scanHandler_Stub) Accept(message *bindings.Message) (err error) {
 	switch message.Header.Type {
 	case scanHandler_Found_Name:
@@ -1433,6 +1871,57 @@
 	return nil
 }
 
+// String names and labels used by the MojomStruct types.
+var (
+	structName_Service                    = "Service"
+	structFieldName_Service_InstanceUuid  = "InstanceUuid"
+	structFieldName_Service_InstanceName  = "InstanceName"
+	structFieldName_Service_InterfaceName = "InterfaceName"
+	structFieldName_Service_Attrs         = "Attrs"
+	structFieldName_Service_Addrs         = "Addrs"
+)
+
+func discovery_Service__() mojom_types.MojomStruct {
+	return mojom_types.MojomStruct{
+		DeclData: &mojom_types.DeclarationData{
+			ShortName: &structName_Service,
+		}, Fields: []mojom_types.StructField{mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_Service_InstanceUuid,
+			},
+			Type: &mojom_types.TypeArrayType{
+				Value: mojom_types.ArrayType{ElementType: &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT8}},
+			},
+		}, mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_Service_InstanceName,
+			},
+			Type: &mojom_types.TypeStringType{mojom_types.StringType{false}},
+		}, mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_Service_InterfaceName,
+			},
+			Type: &mojom_types.TypeStringType{mojom_types.StringType{false}},
+		}, mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_Service_Attrs,
+			},
+			Type: &mojom_types.TypeMapType{
+				Value: mojom_types.MapType{KeyType: &mojom_types.TypeStringType{mojom_types.StringType{false}},
+					ValueType: &mojom_types.TypeStringType{mojom_types.StringType{false}},
+				},
+			},
+		}, mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_Service_Addrs,
+			},
+			Type: &mojom_types.TypeArrayType{
+				Value: mojom_types.ArrayType{ElementType: &mojom_types.TypeStringType{mojom_types.StringType{false}}},
+			},
+		}},
+	}
+}
+
 type Error struct {
 	Id     string
 	Action int32
@@ -1527,3 +2016,34 @@
 	}
 	return nil
 }
+
+// String names and labels used by the MojomStruct types.
+var (
+	structName_Error             = "Error"
+	structFieldName_Error_Id     = "Id"
+	structFieldName_Error_Action = "Action"
+	structFieldName_Error_Msg    = "Msg"
+)
+
+func discovery_Error__() mojom_types.MojomStruct {
+	return mojom_types.MojomStruct{
+		DeclData: &mojom_types.DeclarationData{
+			ShortName: &structName_Error,
+		}, Fields: []mojom_types.StructField{mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_Error_Id,
+			},
+			Type: &mojom_types.TypeStringType{mojom_types.StringType{false}},
+		}, mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_Error_Action,
+			},
+			Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32},
+		}, mojom_types.StructField{
+			DeclData: &mojom_types.DeclarationData{
+				ShortName: &structFieldName_Error_Msg,
+			},
+			Type: &mojom_types.TypeStringType{mojom_types.StringType{false}},
+		}},
+	}
+}
diff --git a/go/src/vanadium/discovery/discovery.go b/go/src/vanadium/discovery/discovery.go
index 62d27d8..a9cd6b8 100644
--- a/go/src/vanadium/discovery/discovery.go
+++ b/go/src/vanadium/discovery/discovery.go
@@ -43,6 +43,10 @@
 	// 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)
diff --git a/lib/discovery.dart b/lib/discovery.dart
new file mode 100644
index 0000000..ad37fb7
--- /dev/null
+++ b/lib/discovery.dart
@@ -0,0 +1,8 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+library discovery;
+
+// Export everything from discovery.mojom.
+export 'gen/dart-gen/mojom/lib/discovery/discovery.mojom.dart';
\ No newline at end of file
diff --git a/lib/gen/dart-gen/mojom/lib/discovery/discovery.mojom.dart b/lib/gen/dart-gen/mojom/lib/discovery/discovery.mojom.dart
new file mode 100644
index 0000000..89d46bb
--- /dev/null
+++ b/lib/gen/dart-gen/mojom/lib/discovery/discovery.mojom.dart
@@ -0,0 +1,1466 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library discovery_mojom;
+
+import 'dart:async';
+
+import 'package:mojo/bindings.dart' as bindings;
+import 'package:mojo/core.dart' as core;
+
+
+
+class Service extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(48, 0)
+  ];
+  List<int> instanceUuid = null;
+  String instanceName = null;
+  String interfaceName = null;
+  Map<String, String> attrs = null;
+  List<String> addrs = null;
+
+  Service() : super(kVersions.last.size);
+
+  static Service deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    decoder.excessHandles.forEach((h) => h.close());
+    return result;
+  }
+
+  static Service decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    Service result = new Service();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.instanceUuid = decoder0.decodeUint8Array(8, bindings.kNothingNullable, bindings.kUnspecifiedArrayLength);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.instanceName = decoder0.decodeString(16, false);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.interfaceName = decoder0.decodeString(24, false);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(32, false);
+      {
+        decoder1.decodeDataHeaderForMap();
+        List<String> keys0;
+        List<String> values0;
+        {
+          
+          var decoder2 = decoder1.decodePointer(bindings.ArrayDataHeader.kHeaderSize, false);
+          {
+            var si2 = decoder2.decodeDataHeaderForPointerArray(bindings.kUnspecifiedArrayLength);
+            keys0 = new List<String>(si2.numElements);
+            for (int i2 = 0; i2 < si2.numElements; ++i2) {
+              
+              keys0[i2] = decoder2.decodeString(bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i2, false);
+            }
+          }
+        }
+        {
+          
+          var decoder2 = decoder1.decodePointer(bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize, false);
+          {
+            var si2 = decoder2.decodeDataHeaderForPointerArray(keys0.length);
+            values0 = new List<String>(si2.numElements);
+            for (int i2 = 0; i2 < si2.numElements; ++i2) {
+              
+              values0[i2] = decoder2.decodeString(bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i2, false);
+            }
+          }
+        }
+        result.attrs = new Map<String, String>.fromIterables(
+            keys0, values0);
+      }
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(40, false);
+      {
+        var si1 = decoder1.decodeDataHeaderForPointerArray(bindings.kUnspecifiedArrayLength);
+        result.addrs = new List<String>(si1.numElements);
+        for (int i1 = 0; i1 < si1.numElements; ++i1) {
+          
+          result.addrs[i1] = decoder1.decodeString(bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
+        }
+      }
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint8Array(instanceUuid, 8, bindings.kNothingNullable, bindings.kUnspecifiedArrayLength);
+    
+    encoder0.encodeString(instanceName, 16, false);
+    
+    encoder0.encodeString(interfaceName, 24, false);
+    
+    if (attrs == null) {
+      encoder0.encodeNullPointer(32, false);
+    } else {
+      var encoder1 = encoder0.encoderForMap(32);
+      int size0 = attrs.length;
+      var keys0 = attrs.keys.toList();
+      var values0 = attrs.values.toList();
+      
+      {
+        var encoder2 = encoder1.encodePointerArray(keys0.length, bindings.ArrayDataHeader.kHeaderSize, bindings.kUnspecifiedArrayLength);
+        for (int i1 = 0; i1 < keys0.length; ++i1) {
+          
+          encoder2.encodeString(keys0[i1], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
+        }
+      }
+      
+      {
+        var encoder2 = encoder1.encodePointerArray(values0.length, bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize, bindings.kUnspecifiedArrayLength);
+        for (int i1 = 0; i1 < values0.length; ++i1) {
+          
+          encoder2.encodeString(values0[i1], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
+        }
+      }
+    }
+    
+    if (addrs == null) {
+      encoder0.encodeNullPointer(40, false);
+    } else {
+      var encoder1 = encoder0.encodePointerArray(addrs.length, 40, bindings.kUnspecifiedArrayLength);
+      for (int i0 = 0; i0 < addrs.length; ++i0) {
+        
+        encoder1.encodeString(addrs[i0], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i0, false);
+      }
+    }
+  }
+
+  String toString() {
+    return "Service("
+           "instanceUuid: $instanceUuid" ", "
+           "instanceName: $instanceName" ", "
+           "interfaceName: $interfaceName" ", "
+           "attrs: $attrs" ", "
+           "addrs: $addrs" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["instanceUuid"] = instanceUuid;
+    map["instanceName"] = instanceName;
+    map["interfaceName"] = interfaceName;
+    map["attrs"] = attrs;
+    map["addrs"] = addrs;
+    return map;
+  }
+}
+
+
+class Error extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(32, 0)
+  ];
+  String id = null;
+  int action = 0;
+  String msg = null;
+
+  Error() : super(kVersions.last.size);
+
+  static Error deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    decoder.excessHandles.forEach((h) => h.close());
+    return result;
+  }
+
+  static Error decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    Error result = new Error();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.id = decoder0.decodeString(8, false);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.action = decoder0.decodeInt32(16);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.msg = decoder0.decodeString(24, false);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeString(id, 8, false);
+    
+    encoder0.encodeInt32(action, 16);
+    
+    encoder0.encodeString(msg, 24, false);
+  }
+
+  String toString() {
+    return "Error("
+           "id: $id" ", "
+           "action: $action" ", "
+           "msg: $msg" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["id"] = id;
+    map["action"] = action;
+    map["msg"] = msg;
+    return map;
+  }
+}
+
+
+class AdvertiserAdvertiseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  Service s = null;
+  List<String> visibility = null;
+
+  AdvertiserAdvertiseParams() : super(kVersions.last.size);
+
+  static AdvertiserAdvertiseParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    decoder.excessHandles.forEach((h) => h.close());
+    return result;
+  }
+
+  static AdvertiserAdvertiseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    AdvertiserAdvertiseParams result = new AdvertiserAdvertiseParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(8, false);
+      result.s = Service.decode(decoder1);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(16, false);
+      {
+        var si1 = decoder1.decodeDataHeaderForPointerArray(bindings.kUnspecifiedArrayLength);
+        result.visibility = new List<String>(si1.numElements);
+        for (int i1 = 0; i1 < si1.numElements; ++i1) {
+          
+          result.visibility[i1] = decoder1.decodeString(bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
+        }
+      }
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeStruct(s, 8, false);
+    
+    if (visibility == null) {
+      encoder0.encodeNullPointer(16, false);
+    } else {
+      var encoder1 = encoder0.encodePointerArray(visibility.length, 16, bindings.kUnspecifiedArrayLength);
+      for (int i0 = 0; i0 < visibility.length; ++i0) {
+        
+        encoder1.encodeString(visibility[i0], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i0, false);
+      }
+    }
+  }
+
+  String toString() {
+    return "AdvertiserAdvertiseParams("
+           "s: $s" ", "
+           "visibility: $visibility" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["s"] = s;
+    map["visibility"] = visibility;
+    return map;
+  }
+}
+
+
+class AdvertiserAdvertiseResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  int handle = 0;
+  Error err = null;
+
+  AdvertiserAdvertiseResponseParams() : super(kVersions.last.size);
+
+  static AdvertiserAdvertiseResponseParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    decoder.excessHandles.forEach((h) => h.close());
+    return result;
+  }
+
+  static AdvertiserAdvertiseResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    AdvertiserAdvertiseResponseParams result = new AdvertiserAdvertiseResponseParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.handle = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(16, true);
+      result.err = Error.decode(decoder1);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(handle, 8);
+    
+    encoder0.encodeStruct(err, 16, true);
+  }
+
+  String toString() {
+    return "AdvertiserAdvertiseResponseParams("
+           "handle: $handle" ", "
+           "err: $err" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["handle"] = handle;
+    map["err"] = err;
+    return map;
+  }
+}
+
+
+class AdvertiserStopParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int h = 0;
+
+  AdvertiserStopParams() : super(kVersions.last.size);
+
+  static AdvertiserStopParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    decoder.excessHandles.forEach((h) => h.close());
+    return result;
+  }
+
+  static AdvertiserStopParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    AdvertiserStopParams result = new AdvertiserStopParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.h = decoder0.decodeUint32(8);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(h, 8);
+  }
+
+  String toString() {
+    return "AdvertiserStopParams("
+           "h: $h" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["h"] = h;
+    return map;
+  }
+}
+
+
+class ScannerScanParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  String query = null;
+  Object scanHandler = null;
+
+  ScannerScanParams() : super(kVersions.last.size);
+
+  static ScannerScanParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    decoder.excessHandles.forEach((h) => h.close());
+    return result;
+  }
+
+  static ScannerScanParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ScannerScanParams result = new ScannerScanParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.query = decoder0.decodeString(8, false);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.scanHandler = decoder0.decodeServiceInterface(16, false, ScanHandlerProxy.newFromEndpoint);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeString(query, 8, false);
+    
+    encoder0.encodeInterface(scanHandler, 16, false);
+  }
+
+  String toString() {
+    return "ScannerScanParams("
+           "query: $query" ", "
+           "scanHandler: $scanHandler" ")";
+  }
+
+  Map toJson() {
+    throw new bindings.MojoCodecError(
+        'Object containing handles cannot be encoded to JSON.');
+  }
+}
+
+
+class ScannerScanResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(24, 0)
+  ];
+  int handle = 0;
+  Error err = null;
+
+  ScannerScanResponseParams() : super(kVersions.last.size);
+
+  static ScannerScanResponseParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    decoder.excessHandles.forEach((h) => h.close());
+    return result;
+  }
+
+  static ScannerScanResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ScannerScanResponseParams result = new ScannerScanResponseParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.handle = decoder0.decodeUint32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(16, true);
+      result.err = Error.decode(decoder1);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(handle, 8);
+    
+    encoder0.encodeStruct(err, 16, true);
+  }
+
+  String toString() {
+    return "ScannerScanResponseParams("
+           "handle: $handle" ", "
+           "err: $err" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["handle"] = handle;
+    map["err"] = err;
+    return map;
+  }
+}
+
+
+class ScannerStopParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  int h = 0;
+
+  ScannerStopParams() : super(kVersions.last.size);
+
+  static ScannerStopParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    decoder.excessHandles.forEach((h) => h.close());
+    return result;
+  }
+
+  static ScannerStopParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ScannerStopParams result = new ScannerStopParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.h = decoder0.decodeUint32(8);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint32(h, 8);
+  }
+
+  String toString() {
+    return "ScannerStopParams("
+           "h: $h" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["h"] = h;
+    return map;
+  }
+}
+
+
+class ScanHandlerFoundParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  Service s = null;
+
+  ScanHandlerFoundParams() : super(kVersions.last.size);
+
+  static ScanHandlerFoundParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    decoder.excessHandles.forEach((h) => h.close());
+    return result;
+  }
+
+  static ScanHandlerFoundParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ScanHandlerFoundParams result = new ScanHandlerFoundParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      var decoder1 = decoder0.decodePointer(8, false);
+      result.s = Service.decode(decoder1);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeStruct(s, 8, false);
+  }
+
+  String toString() {
+    return "ScanHandlerFoundParams("
+           "s: $s" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["s"] = s;
+    return map;
+  }
+}
+
+
+class ScanHandlerLostParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  List<int> instanceId = null;
+
+  ScanHandlerLostParams() : super(kVersions.last.size);
+
+  static ScanHandlerLostParams deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    decoder.excessHandles.forEach((h) => h.close());
+    return result;
+  }
+
+  static ScanHandlerLostParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    ScanHandlerLostParams result = new ScanHandlerLostParams();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.instanceId = decoder0.decodeUint8Array(8, bindings.kNothingNullable, bindings.kUnspecifiedArrayLength);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeUint8Array(instanceId, 8, bindings.kNothingNullable, bindings.kUnspecifiedArrayLength);
+  }
+
+  String toString() {
+    return "ScanHandlerLostParams("
+           "instanceId: $instanceId" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["instanceId"] = instanceId;
+    return map;
+  }
+}
+
+const int kAdvertiser_advertise_name = 0;
+const int kAdvertiser_stop_name = 1;
+
+const String AdvertiserName =
+      'discovery::Advertiser';
+
+abstract class Advertiser {
+  dynamic advertise(Service s,List<String> visibility,[Function responseFactory = null]);
+  void stop(int h);
+
+}
+
+
+class AdvertiserProxyImpl extends bindings.Proxy {
+  AdvertiserProxyImpl.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint);
+
+  AdvertiserProxyImpl.fromHandle(core.MojoHandle handle) :
+      super.fromHandle(handle);
+
+  AdvertiserProxyImpl.unbound() : super.unbound();
+
+  static AdvertiserProxyImpl newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For AdvertiserProxyImpl"));
+    return new AdvertiserProxyImpl.fromEndpoint(endpoint);
+  }
+
+  String get name => AdvertiserName;
+
+  void handleResponse(bindings.ServiceMessage message) {
+    switch (message.header.type) {
+      case kAdvertiser_advertise_name:
+        var r = AdvertiserAdvertiseResponseParams.deserialize(
+            message.payload);
+        if (!message.header.hasRequestId) {
+          throw 'Expected a message with a valid request Id.';
+        }
+        Completer c = completerMap[message.header.requestId];
+        if (c == null) {
+          throw 'Message had unknown request Id: ${message.header.requestId}';
+        }
+        completerMap.remove(message.header.requestId);
+        assert(!c.isCompleted);
+        c.complete(r);
+        break;
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "AdvertiserProxyImpl($superString)";
+  }
+}
+
+
+class _AdvertiserProxyCalls implements Advertiser {
+  AdvertiserProxyImpl _proxyImpl;
+
+  _AdvertiserProxyCalls(this._proxyImpl);
+    dynamic advertise(Service s,List<String> visibility,[Function responseFactory = null]) {
+      assert(_proxyImpl.isBound);
+      var params = new AdvertiserAdvertiseParams();
+      params.s = s;
+      params.visibility = visibility;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          kAdvertiser_advertise_name,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    void stop(int h) {
+      assert(_proxyImpl.isBound);
+      var params = new AdvertiserStopParams();
+      params.h = h;
+      _proxyImpl.sendMessage(params, kAdvertiser_stop_name);
+    }
+  
+}
+
+
+class AdvertiserProxy implements bindings.ProxyBase {
+  final bindings.Proxy impl;
+  Advertiser ptr;
+  final String name = AdvertiserName;
+
+  AdvertiserProxy(AdvertiserProxyImpl proxyImpl) :
+      impl = proxyImpl,
+      ptr = new _AdvertiserProxyCalls(proxyImpl);
+
+  AdvertiserProxy.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) :
+      impl = new AdvertiserProxyImpl.fromEndpoint(endpoint) {
+    ptr = new _AdvertiserProxyCalls(impl);
+  }
+
+  AdvertiserProxy.fromHandle(core.MojoHandle handle) :
+      impl = new AdvertiserProxyImpl.fromHandle(handle) {
+    ptr = new _AdvertiserProxyCalls(impl);
+  }
+
+  AdvertiserProxy.unbound() :
+      impl = new AdvertiserProxyImpl.unbound() {
+    ptr = new _AdvertiserProxyCalls(impl);
+  }
+
+  factory AdvertiserProxy.connectToService(
+      bindings.ServiceConnector s, String url) {
+    AdvertiserProxy p = new AdvertiserProxy.unbound();
+    s.connectToService(url, p);
+    return p;
+  }
+
+  static AdvertiserProxy newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For AdvertiserProxy"));
+    return new AdvertiserProxy.fromEndpoint(endpoint);
+  }
+
+  Future close({bool immediate: false}) => impl.close(immediate: immediate);
+
+  int get version => impl.version;
+
+  Future<int> queryVersion() => impl.queryVersion();
+
+  void requireVersion(int requiredVersion) {
+    impl.requireVersion(requiredVersion);
+  }
+
+  String toString() {
+    return "AdvertiserProxy($impl)";
+  }
+}
+
+
+class AdvertiserStub extends bindings.Stub {
+  Advertiser _impl = null;
+
+  AdvertiserStub.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint, [this._impl])
+      : super.fromEndpoint(endpoint);
+
+  AdvertiserStub.fromHandle(core.MojoHandle handle, [this._impl])
+      : super.fromHandle(handle);
+
+  AdvertiserStub.unbound() : super.unbound();
+
+  static AdvertiserStub newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For AdvertiserStub"));
+    return new AdvertiserStub.fromEndpoint(endpoint);
+  }
+
+  static const String name = AdvertiserName;
+
+
+  AdvertiserAdvertiseResponseParams _AdvertiserAdvertiseResponseParamsFactory(int handle, Error err) {
+    var result = new AdvertiserAdvertiseResponseParams();
+    result.handle = handle;
+    result.err = err;
+    return result;
+  }
+
+  dynamic handleMessage(bindings.ServiceMessage message) {
+    if (bindings.ControlMessageHandler.isControlMessage(message)) {
+      return bindings.ControlMessageHandler.handleMessage(this,
+                                                          0,
+                                                          message);
+    }
+    assert(_impl != null);
+    switch (message.header.type) {
+      case kAdvertiser_advertise_name:
+        var params = AdvertiserAdvertiseParams.deserialize(
+            message.payload);
+        var response = _impl.advertise(params.s,params.visibility,_AdvertiserAdvertiseResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  kAdvertiser_advertise_name,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              kAdvertiser_advertise_name,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case kAdvertiser_stop_name:
+        var params = AdvertiserStopParams.deserialize(
+            message.payload);
+        _impl.stop(params.h);
+        break;
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+    return null;
+  }
+
+  Advertiser get impl => _impl;
+  set impl(Advertiser d) {
+    assert(_impl == null);
+    _impl = d;
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "AdvertiserStub($superString)";
+  }
+
+  int get version => 0;
+}
+
+const int kScanner_scan_name = 0;
+const int kScanner_stop_name = 1;
+
+const String ScannerName =
+      'discovery::Scanner';
+
+abstract class Scanner {
+  dynamic scan(String query,Object scanHandler,[Function responseFactory = null]);
+  void stop(int h);
+
+}
+
+
+class ScannerProxyImpl extends bindings.Proxy {
+  ScannerProxyImpl.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint);
+
+  ScannerProxyImpl.fromHandle(core.MojoHandle handle) :
+      super.fromHandle(handle);
+
+  ScannerProxyImpl.unbound() : super.unbound();
+
+  static ScannerProxyImpl newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For ScannerProxyImpl"));
+    return new ScannerProxyImpl.fromEndpoint(endpoint);
+  }
+
+  String get name => ScannerName;
+
+  void handleResponse(bindings.ServiceMessage message) {
+    switch (message.header.type) {
+      case kScanner_scan_name:
+        var r = ScannerScanResponseParams.deserialize(
+            message.payload);
+        if (!message.header.hasRequestId) {
+          throw 'Expected a message with a valid request Id.';
+        }
+        Completer c = completerMap[message.header.requestId];
+        if (c == null) {
+          throw 'Message had unknown request Id: ${message.header.requestId}';
+        }
+        completerMap.remove(message.header.requestId);
+        assert(!c.isCompleted);
+        c.complete(r);
+        break;
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "ScannerProxyImpl($superString)";
+  }
+}
+
+
+class _ScannerProxyCalls implements Scanner {
+  ScannerProxyImpl _proxyImpl;
+
+  _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;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          kScanner_scan_name,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+    void stop(int h) {
+      assert(_proxyImpl.isBound);
+      var params = new ScannerStopParams();
+      params.h = h;
+      _proxyImpl.sendMessage(params, kScanner_stop_name);
+    }
+  
+}
+
+
+class ScannerProxy implements bindings.ProxyBase {
+  final bindings.Proxy impl;
+  Scanner ptr;
+  final String name = ScannerName;
+
+  ScannerProxy(ScannerProxyImpl proxyImpl) :
+      impl = proxyImpl,
+      ptr = new _ScannerProxyCalls(proxyImpl);
+
+  ScannerProxy.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) :
+      impl = new ScannerProxyImpl.fromEndpoint(endpoint) {
+    ptr = new _ScannerProxyCalls(impl);
+  }
+
+  ScannerProxy.fromHandle(core.MojoHandle handle) :
+      impl = new ScannerProxyImpl.fromHandle(handle) {
+    ptr = new _ScannerProxyCalls(impl);
+  }
+
+  ScannerProxy.unbound() :
+      impl = new ScannerProxyImpl.unbound() {
+    ptr = new _ScannerProxyCalls(impl);
+  }
+
+  factory ScannerProxy.connectToService(
+      bindings.ServiceConnector s, String url) {
+    ScannerProxy p = new ScannerProxy.unbound();
+    s.connectToService(url, p);
+    return p;
+  }
+
+  static ScannerProxy newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For ScannerProxy"));
+    return new ScannerProxy.fromEndpoint(endpoint);
+  }
+
+  Future close({bool immediate: false}) => impl.close(immediate: immediate);
+
+  int get version => impl.version;
+
+  Future<int> queryVersion() => impl.queryVersion();
+
+  void requireVersion(int requiredVersion) {
+    impl.requireVersion(requiredVersion);
+  }
+
+  String toString() {
+    return "ScannerProxy($impl)";
+  }
+}
+
+
+class ScannerStub extends bindings.Stub {
+  Scanner _impl = null;
+
+  ScannerStub.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint, [this._impl])
+      : super.fromEndpoint(endpoint);
+
+  ScannerStub.fromHandle(core.MojoHandle handle, [this._impl])
+      : super.fromHandle(handle);
+
+  ScannerStub.unbound() : super.unbound();
+
+  static ScannerStub newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For ScannerStub"));
+    return new ScannerStub.fromEndpoint(endpoint);
+  }
+
+  static const String name = ScannerName;
+
+
+  ScannerScanResponseParams _ScannerScanResponseParamsFactory(int handle, Error err) {
+    var result = new ScannerScanResponseParams();
+    result.handle = handle;
+    result.err = err;
+    return result;
+  }
+
+  dynamic handleMessage(bindings.ServiceMessage message) {
+    if (bindings.ControlMessageHandler.isControlMessage(message)) {
+      return bindings.ControlMessageHandler.handleMessage(this,
+                                                          0,
+                                                          message);
+    }
+    assert(_impl != null);
+    switch (message.header.type) {
+      case kScanner_scan_name:
+        var params = ScannerScanParams.deserialize(
+            message.payload);
+        var response = _impl.scan(params.query,params.scanHandler,_ScannerScanResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  kScanner_scan_name,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              kScanner_scan_name,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      case kScanner_stop_name:
+        var params = ScannerStopParams.deserialize(
+            message.payload);
+        _impl.stop(params.h);
+        break;
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+    return null;
+  }
+
+  Scanner get impl => _impl;
+  set impl(Scanner d) {
+    assert(_impl == null);
+    _impl = d;
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "ScannerStub($superString)";
+  }
+
+  int get version => 0;
+}
+
+const int kScanHandler_found_name = 0;
+const int kScanHandler_lost_name = 1;
+
+const String ScanHandlerName =
+      'discovery::ScanHandler';
+
+abstract class ScanHandler {
+  void found(Service s);
+  void lost(List<int> instanceId);
+
+}
+
+
+class ScanHandlerProxyImpl extends bindings.Proxy {
+  ScanHandlerProxyImpl.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint);
+
+  ScanHandlerProxyImpl.fromHandle(core.MojoHandle handle) :
+      super.fromHandle(handle);
+
+  ScanHandlerProxyImpl.unbound() : super.unbound();
+
+  static ScanHandlerProxyImpl newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For ScanHandlerProxyImpl"));
+    return new ScanHandlerProxyImpl.fromEndpoint(endpoint);
+  }
+
+  String get name => ScanHandlerName;
+
+  void handleResponse(bindings.ServiceMessage message) {
+    switch (message.header.type) {
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "ScanHandlerProxyImpl($superString)";
+  }
+}
+
+
+class _ScanHandlerProxyCalls implements ScanHandler {
+  ScanHandlerProxyImpl _proxyImpl;
+
+  _ScanHandlerProxyCalls(this._proxyImpl);
+    void found(Service s) {
+      assert(_proxyImpl.isBound);
+      var params = new ScanHandlerFoundParams();
+      params.s = s;
+      _proxyImpl.sendMessage(params, kScanHandler_found_name);
+    }
+  
+    void lost(List<int> instanceId) {
+      assert(_proxyImpl.isBound);
+      var params = new ScanHandlerLostParams();
+      params.instanceId = instanceId;
+      _proxyImpl.sendMessage(params, kScanHandler_lost_name);
+    }
+  
+}
+
+
+class ScanHandlerProxy implements bindings.ProxyBase {
+  final bindings.Proxy impl;
+  ScanHandler ptr;
+  final String name = ScanHandlerName;
+
+  ScanHandlerProxy(ScanHandlerProxyImpl proxyImpl) :
+      impl = proxyImpl,
+      ptr = new _ScanHandlerProxyCalls(proxyImpl);
+
+  ScanHandlerProxy.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) :
+      impl = new ScanHandlerProxyImpl.fromEndpoint(endpoint) {
+    ptr = new _ScanHandlerProxyCalls(impl);
+  }
+
+  ScanHandlerProxy.fromHandle(core.MojoHandle handle) :
+      impl = new ScanHandlerProxyImpl.fromHandle(handle) {
+    ptr = new _ScanHandlerProxyCalls(impl);
+  }
+
+  ScanHandlerProxy.unbound() :
+      impl = new ScanHandlerProxyImpl.unbound() {
+    ptr = new _ScanHandlerProxyCalls(impl);
+  }
+
+  factory ScanHandlerProxy.connectToService(
+      bindings.ServiceConnector s, String url) {
+    ScanHandlerProxy p = new ScanHandlerProxy.unbound();
+    s.connectToService(url, p);
+    return p;
+  }
+
+  static ScanHandlerProxy newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For ScanHandlerProxy"));
+    return new ScanHandlerProxy.fromEndpoint(endpoint);
+  }
+
+  Future close({bool immediate: false}) => impl.close(immediate: immediate);
+
+  int get version => impl.version;
+
+  Future<int> queryVersion() => impl.queryVersion();
+
+  void requireVersion(int requiredVersion) {
+    impl.requireVersion(requiredVersion);
+  }
+
+  String toString() {
+    return "ScanHandlerProxy($impl)";
+  }
+}
+
+
+class ScanHandlerStub extends bindings.Stub {
+  ScanHandler _impl = null;
+
+  ScanHandlerStub.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint, [this._impl])
+      : super.fromEndpoint(endpoint);
+
+  ScanHandlerStub.fromHandle(core.MojoHandle handle, [this._impl])
+      : super.fromHandle(handle);
+
+  ScanHandlerStub.unbound() : super.unbound();
+
+  static ScanHandlerStub newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For ScanHandlerStub"));
+    return new ScanHandlerStub.fromEndpoint(endpoint);
+  }
+
+  static const String name = ScanHandlerName;
+
+
+
+  dynamic handleMessage(bindings.ServiceMessage message) {
+    if (bindings.ControlMessageHandler.isControlMessage(message)) {
+      return bindings.ControlMessageHandler.handleMessage(this,
+                                                          0,
+                                                          message);
+    }
+    assert(_impl != null);
+    switch (message.header.type) {
+      case kScanHandler_found_name:
+        var params = ScanHandlerFoundParams.deserialize(
+            message.payload);
+        _impl.found(params.s);
+        break;
+      case kScanHandler_lost_name:
+        var params = ScanHandlerLostParams.deserialize(
+            message.payload);
+        _impl.lost(params.instanceId);
+        break;
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+    return null;
+  }
+
+  ScanHandler get impl => _impl;
+  set impl(ScanHandler d) {
+    assert(_impl == null);
+    _impl = d;
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "ScanHandlerStub($superString)";
+  }
+
+  int get version => 0;
+}
+
+
diff --git a/mojom/vanadium/discovery.mojom b/mojom/vanadium/discovery.mojom
index 884d129..b5c960c 100644
--- a/mojom/vanadium/discovery.mojom
+++ b/mojom/vanadium/discovery.mojom
@@ -43,7 +43,7 @@
 
 // Scanner provides methods to scan for Vanadium advertisements.
 interface Scanner {
-	// Scan scans for services matching the query passed nad calls ScanHandler with updates.
+	// 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);
 
diff --git a/pubspec.yaml b/pubspec.yaml
new file mode 100644
index 0000000..7ff04d8
--- /dev/null
+++ b/pubspec.yaml
@@ -0,0 +1,12 @@
+author: Vanadium Authors
+description: Discovery is a discovery system for developers that makes it easy to advertise apps and scan for them. It works over MDNS and BLE.
+homepage: https://github.com/vanadium/mojo.discovery
+name: discovery
+version: 0.0.1
+dependencies:
+  mojo: '>=0.3.0 <0.4.0'
+dev_dependencies:
+  dart_style: any
+  test: any
+environment:
+  sdk: '>=1.12.0 <2.0.0'
\ No newline at end of file