Merge branch 'master' into ble
diff --git a/.gitignore b/.gitignore
index b7e2fde..afc89b2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,6 @@
-/.v23
\ No newline at end of file
+/.v23
+gen/
+out/
+go/.idea/
+.idea/
+*.iml
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2652b3f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,34 @@
+PWD := $(shell pwd)
+DISCOVERY_BUILD_DIR=$(PWD)/out
+
+include ../shared/mojo.mk
+
+# NOTE(nlacasse): Running Go Mojo services requires passing the
+# --enable-multiprocess flag to mojo_shell.  This is because the Go runtime is
+# very large, and can interfere with C++ memory if they are in the same
+# process.
+MOJO_SHELL_FLAGS += \
+	--config-alias DISCOVERY_BUILD_DIR=$(DISCOVERY_BUILD_DIR)
+
+echo-go-path:
+	echo $(GOPATH)
+
+run-advertiser-service: $(DISCOVERY_BUILD_DIR)/driver.mojo $(DISCOVERY_BUILD_DIR)/ble.mojo
+	sudo $(MOJO_DIR)/src/mojo/devtools/common/mojo_run --config-file $(PWD)/mojoconfig $(MOJO_SHELL_FLAGS) --args-for="https://mojo.v.io/ble.mojo test" https://mojo.v.io/driver.mojo
+
+run-scanner-service: $(DISCOVERY_BUILD_DIR)/scanner.mojo $(DISCOVERY_BUILD_DIR)/ble.mojo
+	sudo $(MOJO_DIR)/src/mojo/devtools/common/mojo_run --config-file $(PWD)/mojoconfig $(MOJO_SHELL_FLAGS) --args-for="https://mojo.v.io/ble.mojo test" https://mojo.v.io/scanner.mojo
+
+all_mojom: go/src/mojom/v.io/x/ref/services/discovery/ble/mojo/ble.mojom.go go/src/mojom/v.io/x/ref/services/discovery/mojo/discovery.mojom.go
+go/src/mojom/v.io/x/ref/services/discovery/ble/mojo/ble.mojom.go: mojom/v.io/x/ref/services/discovery/ble/ble.mojom
+	$(call MOJOM_GEN,$<,.,go)
+go/src/mojom/v.io/x/ref/services/discovery/mojo/discovery.mojom.go: mojom/v.io/x/ref/services/discovery/discovery.mojom
+	$(call MOJOM_GEN,$<,.,go)
+$(DISCOVERY_BUILD_DIR)/ble.mojo:  go/src/mojom/v.io/x/ref/services/discovery/ble/ble/ble.mojom.go go/src/v.io/x/ref/services/discovery/ble/neighborhood.go go/src/v.io/x/ref/services/discovery/ble/service.go $(MOJO_SHARED_LIB)
+	$(call MOGO_BUILD,v.io/x/ref/services/discovery/ble,$@)
+
+$(DISCOVERY_BUILD_DIR)/driver.mojo:  go/src/mojom/v.io/x/ref/services/discovery/ble/ble/ble.mojom.go go/src/v.io/x/ref/services/discovery/driver/driver.go $(MOJO_SHARED_LIB)
+	$(call MOGO_BUILD,v.io/x/ref/services/discovery/driver,$@)
+$(DISCOVERY_BUILD_DIR)/scanner.mojo:  go/src/mojom/v.io/x/ref/services/discovery/ble/ble/ble.mojom.go go/src/v.io/x/ref/services/discovery/scanner/scanner.go $(MOJO_SHARED_LIB)
+	$(call MOGO_BUILD,v.io/x/ref/services/discovery/scanner,$@)
+
diff --git a/go/src/mojom/v.io/x/ref/services/discovery/ble/ble/ble.mojom.go b/go/src/mojom/v.io/x/ref/services/discovery/ble/ble/ble.mojom.go
new file mode 100644
index 0000000..4276952
--- /dev/null
+++ b/go/src/mojom/v.io/x/ref/services/discovery/ble/ble/ble.mojom.go
@@ -0,0 +1,1393 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is autogenerated by:
+//     mojo/public/tools/bindings/mojom_bindings_generator.py
+// For:
+//     mojom/v.io/x/ref/services/discovery/ble/ble.mojom
+//
+
+package ble
+
+import (
+	"fmt"
+	"mojo/public/go/bindings"
+	"mojo/public/go/system"
+	"sort"
+)
+
+type V23Ble interface {
+	Advertise(inAdv Advertisement) (outHandler PluginStopper_Pointer, err error)
+	Scan(inId *[]uint8, inAttributes map[string]string, inHandler PluginScanHandler_Pointer) (outHandler PluginStopper_Pointer, err error)
+}
+
+var v23BLE_Name = "ble::V23BLE"
+
+type V23Ble_Request bindings.InterfaceRequest
+
+func (r *V23Ble_Request) Name() string {
+	return v23BLE_Name
+}
+
+type V23Ble_Pointer bindings.InterfacePointer
+
+func (p *V23Ble_Pointer) Name() string {
+	return v23BLE_Name
+}
+
+type V23Ble_ServiceFactory struct{
+	Delegate V23Ble_Factory
+}
+
+type V23Ble_Factory interface {
+	Create(request V23Ble_Request)
+}
+
+func (f *V23Ble_ServiceFactory) Name() string {
+	return v23BLE_Name
+}
+
+func (f *V23Ble_ServiceFactory) Create(messagePipe system.MessagePipeHandle) {
+	request := V23Ble_Request{bindings.NewMessagePipeHandleOwner(messagePipe)}
+	f.Delegate.Create(request)
+}
+
+// CreateMessagePipeForV23Ble creates a message pipe for use with the
+// V23Ble interface with a V23Ble_Request on one end and a V23Ble_Pointer on the other.
+func CreateMessagePipeForV23Ble() (V23Ble_Request, V23Ble_Pointer) {
+        r, p := bindings.CreateMessagePipeForMojoInterface()
+        return V23Ble_Request(r), V23Ble_Pointer(p)
+}
+
+const v23BLE_Advertise_Name uint32 = 0
+const v23BLE_Scan_Name uint32 = 1
+
+type V23Ble_Proxy struct {
+	router *bindings.Router
+	ids bindings.Counter
+}
+
+func NewV23BleProxy(p V23Ble_Pointer, waiter bindings.AsyncWaiter) *V23Ble_Proxy {
+	return &V23Ble_Proxy{
+		bindings.NewRouter(p.PassMessagePipe(), waiter),
+		bindings.NewCounter(),
+	}
+}
+
+func (p *V23Ble_Proxy) Close_Proxy() {
+	p.router.Close()
+}
+
+type v23Ble_Advertise_Params struct {
+	inAdv Advertisement
+}
+
+func (s *v23Ble_Advertise_Params) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(8, 0)
+	if err := encoder.WritePointer(); err != nil {
+		return err
+	}
+	if err := s.inAdv.Encode(encoder); err != nil {
+		return err
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var v23Ble_Advertise_Params_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{16, 0},
+}
+
+func (s *v23Ble_Advertise_Params) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(v23Ble_Advertise_Params_Versions), func(i int) bool {
+		return v23Ble_Advertise_Params_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(v23Ble_Advertise_Params_Versions) {
+		if v23Ble_Advertise_Params_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := v23Ble_Advertise_Params_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		pointer0, err := decoder.ReadPointer()
+		if err != nil {
+			return err
+		}
+		if pointer0 == 0 {
+			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+		} else {
+			if err := s.inAdv.Decode(decoder); err != nil {
+				return err
+			}
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+type v23Ble_Advertise_ResponseParams struct {
+	outHandler PluginStopper_Pointer
+}
+
+func (s *v23Ble_Advertise_ResponseParams) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(8, 0)
+	if err := encoder.WriteInterface(s.outHandler.PassMessagePipe()); err != nil {
+		return err
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var v23Ble_Advertise_ResponseParams_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{16, 0},
+}
+
+func (s *v23Ble_Advertise_ResponseParams) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(v23Ble_Advertise_ResponseParams_Versions), func(i int) bool {
+		return v23Ble_Advertise_ResponseParams_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(v23Ble_Advertise_ResponseParams_Versions) {
+		if v23Ble_Advertise_ResponseParams_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := v23Ble_Advertise_ResponseParams_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		handle0, err := decoder.ReadInterface()
+		if err != nil {
+			return err
+		}
+		if handle0.IsValid() {
+			handleOwner := bindings.NewMessagePipeHandleOwner(handle0)
+			s.outHandler = PluginStopper_Pointer{handleOwner}
+		} else {
+			return &bindings.ValidationError{bindings.UnexpectedInvalidHandle, "unexpected invalid handle"}
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (p *V23Ble_Proxy) Advertise(inAdv Advertisement) (outHandler PluginStopper_Pointer, err error) {
+	payload := &v23Ble_Advertise_Params{
+		inAdv,
+	}
+	header := bindings.MessageHeader{
+		Type: v23BLE_Advertise_Name,
+		Flags: bindings.MessageExpectsResponseFlag,
+		RequestId: p.ids.Count(),
+	}
+	var message *bindings.Message
+	if message, err = bindings.EncodeMessage(header, payload); err != nil {
+		err = fmt.Errorf("can't encode request: %v", err.Error())
+		p.Close_Proxy()
+		return
+	}
+	readResult := <-p.router.AcceptWithResponse(message)
+	if err = readResult.Error; err != nil {
+		p.Close_Proxy()
+		return
+	}
+	if readResult.Message.Header.Flags != bindings.MessageIsResponseFlag {
+		err = &bindings.ValidationError{bindings.MessageHeaderInvalidFlags,
+			fmt.Sprintf("invalid message header flag: %v", readResult.Message.Header.Flags),
+		}
+		return
+	}
+	if got, want := readResult.Message.Header.Type, v23BLE_Advertise_Name; got != want {
+		err = &bindings.ValidationError{bindings.MessageHeaderUnknownMethod,
+			fmt.Sprintf("invalid method in response: expected %v, got %v", want, got),
+		}
+		return
+	}
+	var response v23Ble_Advertise_ResponseParams
+	if err = readResult.Message.DecodePayload(&response); err != nil {
+		p.Close_Proxy()
+		return
+	}
+	outHandler = response.outHandler
+	return
+}
+
+type v23Ble_Scan_Params struct {
+	inId *[]uint8
+	inAttributes map[string]string
+	inHandler PluginScanHandler_Pointer
+}
+
+func (s *v23Ble_Scan_Params) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(24, 0)
+	if s.inId == nil {
+		encoder.WriteNullPointer()
+	} else {
+		if err := encoder.WritePointer(); err != nil {
+			return err
+		}
+		encoder.StartArray(uint32(len((*s.inId))), 8)
+		for _, elem0 := range (*s.inId) {
+			if err := encoder.WriteUint8(elem0); err != nil {
+				return err
+			}
+		}
+		if err := encoder.Finish(); err != nil {
+			return err
+		}
+	}
+	if err := encoder.WritePointer(); err != nil {
+		return err
+	}
+	encoder.StartMap()
+	{
+		var keys0 []string
+		var values0 []string
+		for key0, value0 := range s.inAttributes {
+			keys0 = append(keys0, key0)
+			values0 = append(values0, value0)
+		}
+		if err := encoder.WritePointer(); err != nil {
+			return err
+		}
+		encoder.StartArray(uint32(len(keys0)), 64)
+		for _, elem1 := range keys0 {
+			if err := encoder.WritePointer(); err != nil {
+				return err
+			}
+			if err := encoder.WriteString(elem1); err != nil {
+				return err
+			}
+		}
+		if err := encoder.Finish(); err != nil {
+			return err
+		}
+		if err := encoder.WritePointer(); err != nil {
+			return err
+		}
+		encoder.StartArray(uint32(len(values0)), 64)
+		for _, elem1 := range values0 {
+			if err := encoder.WritePointer(); err != nil {
+				return err
+			}
+			if err := encoder.WriteString(elem1); err != nil {
+				return err
+			}
+		}
+		if err := encoder.Finish(); err != nil {
+			return err
+		}
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	if err := encoder.WriteInterface(s.inHandler.PassMessagePipe()); err != nil {
+		return err
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var v23Ble_Scan_Params_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{32, 0},
+}
+
+func (s *v23Ble_Scan_Params) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(v23Ble_Scan_Params_Versions), func(i int) bool {
+		return v23Ble_Scan_Params_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(v23Ble_Scan_Params_Versions) {
+		if v23Ble_Scan_Params_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := v23Ble_Scan_Params_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		pointer0, err := decoder.ReadPointer()
+		if err != nil {
+			return err
+		}
+		if pointer0 == 0 {
+			s.inId = nil
+		} else {
+			s.inId = new([]uint8)
+			len0, err := decoder.StartArray(8)
+			if err != nil {
+				return err
+			}
+			(*s.inId) = make([]uint8, len0)
+			for i0 := uint32(0); i0 < len0; i0++ {
+				value1, err := decoder.ReadUint8()
+				if err != nil {
+					return err
+				}
+				(*s.inId)[i0] = value1
+			}
+			if err := decoder.Finish(); err != nil {
+				return err
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		pointer0, err := decoder.ReadPointer()
+		if err != nil {
+			return err
+		}
+		if pointer0 == 0 {
+			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+		} else {
+			if err := decoder.StartMap(); err != nil {
+				return err
+			}
+			var keys0 []string
+			{
+				pointer1, err := decoder.ReadPointer()
+				if err != nil {
+					return err
+				}
+				if pointer1 == 0 {
+					return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+				} else {
+					len1, err := decoder.StartArray(64)
+					if err != nil {
+						return err
+					}
+					keys0 = make([]string, len1)
+					for i1 := uint32(0); i1 < len1; i1++ {
+						pointer2, err := decoder.ReadPointer()
+						if err != nil {
+							return err
+						}
+						if pointer2 == 0 {
+							return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+						} else {
+							value2, err := decoder.ReadString()
+							if err != nil {
+								return err
+							}
+							keys0[i1] = value2
+						}
+					}
+					if err := decoder.Finish(); err != nil {
+						return err
+					}
+				}
+			}
+			var values0 []string
+			{
+				pointer1, err := decoder.ReadPointer()
+				if err != nil {
+					return err
+				}
+				if pointer1 == 0 {
+					return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+				} else {
+					len1, err := decoder.StartArray(64)
+					if err != nil {
+						return err
+					}
+					values0 = make([]string, len1)
+					for i1 := uint32(0); i1 < len1; i1++ {
+						pointer2, err := decoder.ReadPointer()
+						if err != nil {
+							return err
+						}
+						if pointer2 == 0 {
+							return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+						} else {
+							value2, err := decoder.ReadString()
+							if err != nil {
+								return err
+							}
+							values0[i1] = value2
+						}
+					}
+					if err := decoder.Finish(); err != nil {
+						return err
+					}
+				}
+			}
+			if len(keys0) != len(values0) {
+				return &bindings.ValidationError{bindings.DifferentSizedArraysInMap,
+					fmt.Sprintf("Number of keys %d is different from number of values %d", len(keys0), len(values0)),
+				}
+			}
+			if err := decoder.Finish(); err != nil {
+				return err
+			}
+			len0 := len(keys0)
+			map0 := make(map[string]string)
+			for i0 := 0; i0 < len0; i0++ {
+				map0[keys0[i0]] = values0[i0]
+			}
+			s.inAttributes = map0
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		handle0, err := decoder.ReadInterface()
+		if err != nil {
+			return err
+		}
+		if handle0.IsValid() {
+			handleOwner := bindings.NewMessagePipeHandleOwner(handle0)
+			s.inHandler = PluginScanHandler_Pointer{handleOwner}
+		} else {
+			return &bindings.ValidationError{bindings.UnexpectedInvalidHandle, "unexpected invalid handle"}
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+type v23Ble_Scan_ResponseParams struct {
+	outHandler PluginStopper_Pointer
+}
+
+func (s *v23Ble_Scan_ResponseParams) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(8, 0)
+	if err := encoder.WriteInterface(s.outHandler.PassMessagePipe()); err != nil {
+		return err
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var v23Ble_Scan_ResponseParams_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{16, 0},
+}
+
+func (s *v23Ble_Scan_ResponseParams) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(v23Ble_Scan_ResponseParams_Versions), func(i int) bool {
+		return v23Ble_Scan_ResponseParams_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(v23Ble_Scan_ResponseParams_Versions) {
+		if v23Ble_Scan_ResponseParams_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := v23Ble_Scan_ResponseParams_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		handle0, err := decoder.ReadInterface()
+		if err != nil {
+			return err
+		}
+		if handle0.IsValid() {
+			handleOwner := bindings.NewMessagePipeHandleOwner(handle0)
+			s.outHandler = PluginStopper_Pointer{handleOwner}
+		} else {
+			return &bindings.ValidationError{bindings.UnexpectedInvalidHandle, "unexpected invalid handle"}
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (p *V23Ble_Proxy) Scan(inId *[]uint8, inAttributes map[string]string, inHandler PluginScanHandler_Pointer) (outHandler PluginStopper_Pointer, err error) {
+	payload := &v23Ble_Scan_Params{
+		inId,
+		inAttributes,
+		inHandler,
+	}
+	header := bindings.MessageHeader{
+		Type: v23BLE_Scan_Name,
+		Flags: bindings.MessageExpectsResponseFlag,
+		RequestId: p.ids.Count(),
+	}
+	var message *bindings.Message
+	if message, err = bindings.EncodeMessage(header, payload); err != nil {
+		err = fmt.Errorf("can't encode request: %v", err.Error())
+		p.Close_Proxy()
+		return
+	}
+	readResult := <-p.router.AcceptWithResponse(message)
+	if err = readResult.Error; err != nil {
+		p.Close_Proxy()
+		return
+	}
+	if readResult.Message.Header.Flags != bindings.MessageIsResponseFlag {
+		err = &bindings.ValidationError{bindings.MessageHeaderInvalidFlags,
+			fmt.Sprintf("invalid message header flag: %v", readResult.Message.Header.Flags),
+		}
+		return
+	}
+	if got, want := readResult.Message.Header.Type, v23BLE_Scan_Name; got != want {
+		err = &bindings.ValidationError{bindings.MessageHeaderUnknownMethod,
+			fmt.Sprintf("invalid method in response: expected %v, got %v", want, got),
+		}
+		return
+	}
+	var response v23Ble_Scan_ResponseParams
+	if err = readResult.Message.DecodePayload(&response); err != nil {
+		p.Close_Proxy()
+		return
+	}
+	outHandler = response.outHandler
+	return
+}
+
+type v23BLE_Stub struct {
+	connector *bindings.Connector
+	impl V23Ble
+}
+
+func NewV23BleStub(r V23Ble_Request, impl V23Ble, waiter bindings.AsyncWaiter) *bindings.Stub {
+	connector := bindings.NewConnector(r.PassMessagePipe(), waiter)
+	return bindings.NewStub(connector, &v23BLE_Stub{connector, impl})
+}
+
+func (s *v23BLE_Stub) Accept(message *bindings.Message) (err error) {
+	switch message.Header.Type {
+	case v23BLE_Advertise_Name:
+		if message.Header.Flags != bindings.MessageExpectsResponseFlag {
+			return &bindings.ValidationError{bindings.MessageHeaderInvalidFlags,
+				fmt.Sprintf("invalid message header flag: %v", message.Header.Flags),
+			}
+		}
+		var request v23Ble_Advertise_Params
+		if err := message.DecodePayload(&request); err != nil {
+			return err
+		}
+		var response v23Ble_Advertise_ResponseParams
+		response.outHandler, err = s.impl.Advertise(request.inAdv)
+		if err != nil {
+			return
+		}
+		header := bindings.MessageHeader{
+			Type: v23BLE_Advertise_Name,
+			Flags: bindings.MessageIsResponseFlag,
+			RequestId: message.Header.RequestId,
+		}
+		message, err = bindings.EncodeMessage(header, &response)
+		if err != nil {
+			return err
+		}
+		return s.connector.WriteMessage(message)
+	case v23BLE_Scan_Name:
+		if message.Header.Flags != bindings.MessageExpectsResponseFlag {
+			return &bindings.ValidationError{bindings.MessageHeaderInvalidFlags,
+				fmt.Sprintf("invalid message header flag: %v", message.Header.Flags),
+			}
+		}
+		var request v23Ble_Scan_Params
+		if err := message.DecodePayload(&request); err != nil {
+			return err
+		}
+		var response v23Ble_Scan_ResponseParams
+		response.outHandler, err = s.impl.Scan(request.inId, request.inAttributes, request.inHandler)
+		if err != nil {
+			return
+		}
+		header := bindings.MessageHeader{
+			Type: v23BLE_Scan_Name,
+			Flags: bindings.MessageIsResponseFlag,
+			RequestId: message.Header.RequestId,
+		}
+		message, err = bindings.EncodeMessage(header, &response)
+		if err != nil {
+			return err
+		}
+		return s.connector.WriteMessage(message)
+	default:
+		return &bindings.ValidationError{
+			bindings.MessageHeaderUnknownMethod,
+			fmt.Sprintf("unknown method %v", message.Header.Type),
+		}
+	}
+	return
+}
+
+type PluginScanHandler interface {
+	Found(inS Advertisement) (err error)
+	Lost(inS Advertisement) (err error)
+}
+
+var pluginScanHandler_Name = "ble::PluginScanHandler"
+
+type PluginScanHandler_Request bindings.InterfaceRequest
+
+func (r *PluginScanHandler_Request) Name() string {
+	return pluginScanHandler_Name
+}
+
+type PluginScanHandler_Pointer bindings.InterfacePointer
+
+func (p *PluginScanHandler_Pointer) Name() string {
+	return pluginScanHandler_Name
+}
+
+type PluginScanHandler_ServiceFactory struct{
+	Delegate PluginScanHandler_Factory
+}
+
+type PluginScanHandler_Factory interface {
+	Create(request PluginScanHandler_Request)
+}
+
+func (f *PluginScanHandler_ServiceFactory) Name() string {
+	return pluginScanHandler_Name
+}
+
+func (f *PluginScanHandler_ServiceFactory) Create(messagePipe system.MessagePipeHandle) {
+	request := PluginScanHandler_Request{bindings.NewMessagePipeHandleOwner(messagePipe)}
+	f.Delegate.Create(request)
+}
+
+// CreateMessagePipeForPluginScanHandler creates a message pipe for use with the
+// PluginScanHandler interface with a PluginScanHandler_Request on one end and a PluginScanHandler_Pointer on the other.
+func CreateMessagePipeForPluginScanHandler() (PluginScanHandler_Request, PluginScanHandler_Pointer) {
+        r, p := bindings.CreateMessagePipeForMojoInterface()
+        return PluginScanHandler_Request(r), PluginScanHandler_Pointer(p)
+}
+
+const pluginScanHandler_Found_Name uint32 = 0
+const pluginScanHandler_Lost_Name uint32 = 1
+
+type PluginScanHandler_Proxy struct {
+	router *bindings.Router
+	ids bindings.Counter
+}
+
+func NewPluginScanHandlerProxy(p PluginScanHandler_Pointer, waiter bindings.AsyncWaiter) *PluginScanHandler_Proxy {
+	return &PluginScanHandler_Proxy{
+		bindings.NewRouter(p.PassMessagePipe(), waiter),
+		bindings.NewCounter(),
+	}
+}
+
+func (p *PluginScanHandler_Proxy) Close_Proxy() {
+	p.router.Close()
+}
+
+type pluginScanHandler_Found_Params struct {
+	inS Advertisement
+}
+
+func (s *pluginScanHandler_Found_Params) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(8, 0)
+	if err := encoder.WritePointer(); err != nil {
+		return err
+	}
+	if err := s.inS.Encode(encoder); err != nil {
+		return err
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var pluginScanHandler_Found_Params_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{16, 0},
+}
+
+func (s *pluginScanHandler_Found_Params) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(pluginScanHandler_Found_Params_Versions), func(i int) bool {
+		return pluginScanHandler_Found_Params_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(pluginScanHandler_Found_Params_Versions) {
+		if pluginScanHandler_Found_Params_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := pluginScanHandler_Found_Params_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		pointer0, err := decoder.ReadPointer()
+		if err != nil {
+			return err
+		}
+		if pointer0 == 0 {
+			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+		} else {
+			if err := s.inS.Decode(decoder); err != nil {
+				return err
+			}
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (p *PluginScanHandler_Proxy) Found(inS Advertisement) (err error) {
+	payload := &pluginScanHandler_Found_Params{
+		inS,
+	}
+	header := bindings.MessageHeader{
+		Type: pluginScanHandler_Found_Name,
+		Flags: bindings.MessageNoFlag,
+	}
+	var message *bindings.Message
+	if message, err = bindings.EncodeMessage(header, payload); err != nil {
+		err = fmt.Errorf("can't encode request: %v", err.Error())
+		p.Close_Proxy()
+		return
+	}
+	if err = p.router.Accept(message); err != nil {
+		p.Close_Proxy()
+		return
+	}
+	return
+}
+
+type pluginScanHandler_Lost_Params struct {
+	inS Advertisement
+}
+
+func (s *pluginScanHandler_Lost_Params) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(8, 0)
+	if err := encoder.WritePointer(); err != nil {
+		return err
+	}
+	if err := s.inS.Encode(encoder); err != nil {
+		return err
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var pluginScanHandler_Lost_Params_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{16, 0},
+}
+
+func (s *pluginScanHandler_Lost_Params) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(pluginScanHandler_Lost_Params_Versions), func(i int) bool {
+		return pluginScanHandler_Lost_Params_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(pluginScanHandler_Lost_Params_Versions) {
+		if pluginScanHandler_Lost_Params_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := pluginScanHandler_Lost_Params_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		pointer0, err := decoder.ReadPointer()
+		if err != nil {
+			return err
+		}
+		if pointer0 == 0 {
+			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+		} else {
+			if err := s.inS.Decode(decoder); err != nil {
+				return err
+			}
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (p *PluginScanHandler_Proxy) Lost(inS Advertisement) (err error) {
+	payload := &pluginScanHandler_Lost_Params{
+		inS,
+	}
+	header := bindings.MessageHeader{
+		Type: pluginScanHandler_Lost_Name,
+		Flags: bindings.MessageNoFlag,
+	}
+	var message *bindings.Message
+	if message, err = bindings.EncodeMessage(header, payload); err != nil {
+		err = fmt.Errorf("can't encode request: %v", err.Error())
+		p.Close_Proxy()
+		return
+	}
+	if err = p.router.Accept(message); err != nil {
+		p.Close_Proxy()
+		return
+	}
+	return
+}
+
+type pluginScanHandler_Stub struct {
+	connector *bindings.Connector
+	impl PluginScanHandler
+}
+
+func NewPluginScanHandlerStub(r PluginScanHandler_Request, impl PluginScanHandler, waiter bindings.AsyncWaiter) *bindings.Stub {
+	connector := bindings.NewConnector(r.PassMessagePipe(), waiter)
+	return bindings.NewStub(connector, &pluginScanHandler_Stub{connector, impl})
+}
+
+func (s *pluginScanHandler_Stub) Accept(message *bindings.Message) (err error) {
+	switch message.Header.Type {
+	case pluginScanHandler_Found_Name:
+		if message.Header.Flags != bindings.MessageNoFlag {
+			return &bindings.ValidationError{bindings.MessageHeaderInvalidFlags,
+				fmt.Sprintf("invalid message header flag: %v", message.Header.Flags),
+			}
+		}
+		var request pluginScanHandler_Found_Params
+		if err := message.DecodePayload(&request); err != nil {
+			return err
+		}
+		err = s.impl.Found(request.inS)
+		if err != nil {
+			return
+		}
+	case pluginScanHandler_Lost_Name:
+		if message.Header.Flags != bindings.MessageNoFlag {
+			return &bindings.ValidationError{bindings.MessageHeaderInvalidFlags,
+				fmt.Sprintf("invalid message header flag: %v", message.Header.Flags),
+			}
+		}
+		var request pluginScanHandler_Lost_Params
+		if err := message.DecodePayload(&request); err != nil {
+			return err
+		}
+		err = s.impl.Lost(request.inS)
+		if err != nil {
+			return
+		}
+	default:
+		return &bindings.ValidationError{
+			bindings.MessageHeaderUnknownMethod,
+			fmt.Sprintf("unknown method %v", message.Header.Type),
+		}
+	}
+	return
+}
+
+type PluginStopper interface {
+	Stop() (err error)
+}
+
+var pluginStopper_Name = "ble::PluginStopper"
+
+type PluginStopper_Request bindings.InterfaceRequest
+
+func (r *PluginStopper_Request) Name() string {
+	return pluginStopper_Name
+}
+
+type PluginStopper_Pointer bindings.InterfacePointer
+
+func (p *PluginStopper_Pointer) Name() string {
+	return pluginStopper_Name
+}
+
+type PluginStopper_ServiceFactory struct{
+	Delegate PluginStopper_Factory
+}
+
+type PluginStopper_Factory interface {
+	Create(request PluginStopper_Request)
+}
+
+func (f *PluginStopper_ServiceFactory) Name() string {
+	return pluginStopper_Name
+}
+
+func (f *PluginStopper_ServiceFactory) Create(messagePipe system.MessagePipeHandle) {
+	request := PluginStopper_Request{bindings.NewMessagePipeHandleOwner(messagePipe)}
+	f.Delegate.Create(request)
+}
+
+// CreateMessagePipeForPluginStopper creates a message pipe for use with the
+// PluginStopper interface with a PluginStopper_Request on one end and a PluginStopper_Pointer on the other.
+func CreateMessagePipeForPluginStopper() (PluginStopper_Request, PluginStopper_Pointer) {
+        r, p := bindings.CreateMessagePipeForMojoInterface()
+        return PluginStopper_Request(r), PluginStopper_Pointer(p)
+}
+
+const pluginStopper_Stop_Name uint32 = 0
+
+type PluginStopper_Proxy struct {
+	router *bindings.Router
+	ids bindings.Counter
+}
+
+func NewPluginStopperProxy(p PluginStopper_Pointer, waiter bindings.AsyncWaiter) *PluginStopper_Proxy {
+	return &PluginStopper_Proxy{
+		bindings.NewRouter(p.PassMessagePipe(), waiter),
+		bindings.NewCounter(),
+	}
+}
+
+func (p *PluginStopper_Proxy) Close_Proxy() {
+	p.router.Close()
+}
+
+type pluginStopper_Stop_Params struct {
+}
+
+func (s *pluginStopper_Stop_Params) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(0, 0)
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var pluginStopper_Stop_Params_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{8, 0},
+}
+
+func (s *pluginStopper_Stop_Params) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(pluginStopper_Stop_Params_Versions), func(i int) bool {
+		return pluginStopper_Stop_Params_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(pluginStopper_Stop_Params_Versions) {
+		if pluginStopper_Stop_Params_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := pluginStopper_Stop_Params_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (p *PluginStopper_Proxy) Stop() (err error) {
+	payload := &pluginStopper_Stop_Params{
+	}
+	header := bindings.MessageHeader{
+		Type: pluginStopper_Stop_Name,
+		Flags: bindings.MessageNoFlag,
+	}
+	var message *bindings.Message
+	if message, err = bindings.EncodeMessage(header, payload); err != nil {
+		err = fmt.Errorf("can't encode request: %v", err.Error())
+		p.Close_Proxy()
+		return
+	}
+	if err = p.router.Accept(message); err != nil {
+		p.Close_Proxy()
+		return
+	}
+	return
+}
+
+type pluginStopper_Stub struct {
+	connector *bindings.Connector
+	impl PluginStopper
+}
+
+func NewPluginStopperStub(r PluginStopper_Request, impl PluginStopper, waiter bindings.AsyncWaiter) *bindings.Stub {
+	connector := bindings.NewConnector(r.PassMessagePipe(), waiter)
+	return bindings.NewStub(connector, &pluginStopper_Stub{connector, impl})
+}
+
+func (s *pluginStopper_Stub) Accept(message *bindings.Message) (err error) {
+	switch message.Header.Type {
+	case pluginStopper_Stop_Name:
+		if message.Header.Flags != bindings.MessageNoFlag {
+			return &bindings.ValidationError{bindings.MessageHeaderInvalidFlags,
+				fmt.Sprintf("invalid message header flag: %v", message.Header.Flags),
+			}
+		}
+		var request pluginStopper_Stop_Params
+		if err := message.DecodePayload(&request); err != nil {
+			return err
+		}
+		err = s.impl.Stop()
+		if err != nil {
+			return
+		}
+	default:
+		return &bindings.ValidationError{
+			bindings.MessageHeaderUnknownMethod,
+			fmt.Sprintf("unknown method %v", message.Header.Type),
+		}
+	}
+	return
+}
+
+type Advertisement struct {
+	ServiceId []uint8
+	Service Service
+}
+
+func (s *Advertisement) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(16, 0)
+	if err := encoder.WritePointer(); err != nil {
+		return err
+	}
+	encoder.StartArray(uint32(len(s.ServiceId)), 8)
+	for _, elem0 := range s.ServiceId {
+		if err := encoder.WriteUint8(elem0); err != nil {
+			return err
+		}
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	if err := encoder.WritePointer(); err != nil {
+		return err
+	}
+	if err := s.Service.Encode(encoder); err != nil {
+		return err
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var advertisement_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{24, 0},
+}
+
+func (s *Advertisement) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(advertisement_Versions), func(i int) bool {
+		return advertisement_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(advertisement_Versions) {
+		if advertisement_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := advertisement_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		pointer0, err := decoder.ReadPointer()
+		if err != nil {
+			return err
+		}
+		if pointer0 == 0 {
+			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+		} else {
+			len0, err := decoder.StartArray(8)
+			if err != nil {
+				return err
+			}
+			s.ServiceId = make([]uint8, len0)
+			for i0 := uint32(0); i0 < len0; i0++ {
+				value1, err := decoder.ReadUint8()
+				if err != nil {
+					return err
+				}
+				s.ServiceId[i0] = value1
+			}
+			if err := decoder.Finish(); err != nil {
+				return err
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		pointer0, err := decoder.ReadPointer()
+		if err != nil {
+			return err
+		}
+		if pointer0 == 0 {
+			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+		} else {
+			if err := s.Service.Decode(decoder); err != nil {
+				return err
+			}
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+type Service struct {
+	InstanceId []uint8
+	Attributes map[string]string
+}
+
+func (s *Service) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(16, 0)
+	if err := encoder.WritePointer(); err != nil {
+		return err
+	}
+	encoder.StartArray(uint32(len(s.InstanceId)), 8)
+	for _, elem0 := range s.InstanceId {
+		if err := encoder.WriteUint8(elem0); err != nil {
+			return err
+		}
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	if err := encoder.WritePointer(); err != nil {
+		return err
+	}
+	encoder.StartMap()
+	{
+		var keys0 []string
+		var values0 []string
+		for key0, value0 := range s.Attributes {
+			keys0 = append(keys0, key0)
+			values0 = append(values0, value0)
+		}
+		if err := encoder.WritePointer(); err != nil {
+			return err
+		}
+		encoder.StartArray(uint32(len(keys0)), 64)
+		for _, elem1 := range keys0 {
+			if err := encoder.WritePointer(); err != nil {
+				return err
+			}
+			if err := encoder.WriteString(elem1); err != nil {
+				return err
+			}
+		}
+		if err := encoder.Finish(); err != nil {
+			return err
+		}
+		if err := encoder.WritePointer(); err != nil {
+			return err
+		}
+		encoder.StartArray(uint32(len(values0)), 64)
+		for _, elem1 := range values0 {
+			if err := encoder.WritePointer(); err != nil {
+				return err
+			}
+			if err := encoder.WriteString(elem1); err != nil {
+				return err
+			}
+		}
+		if err := encoder.Finish(); err != nil {
+			return err
+		}
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var service_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{24, 0},
+}
+
+func (s *Service) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(service_Versions), func(i int) bool {
+		return service_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(service_Versions) {
+		if service_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := service_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		pointer0, err := decoder.ReadPointer()
+		if err != nil {
+			return err
+		}
+		if pointer0 == 0 {
+			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+		} else {
+			len0, err := decoder.StartArray(8)
+			if err != nil {
+				return err
+			}
+			s.InstanceId = make([]uint8, len0)
+			for i0 := uint32(0); i0 < len0; i0++ {
+				value1, err := decoder.ReadUint8()
+				if err != nil {
+					return err
+				}
+				s.InstanceId[i0] = value1
+			}
+			if err := decoder.Finish(); err != nil {
+				return err
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		pointer0, err := decoder.ReadPointer()
+		if err != nil {
+			return err
+		}
+		if pointer0 == 0 {
+			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+		} else {
+			if err := decoder.StartMap(); err != nil {
+				return err
+			}
+			var keys0 []string
+			{
+				pointer1, err := decoder.ReadPointer()
+				if err != nil {
+					return err
+				}
+				if pointer1 == 0 {
+					return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+				} else {
+					len1, err := decoder.StartArray(64)
+					if err != nil {
+						return err
+					}
+					keys0 = make([]string, len1)
+					for i1 := uint32(0); i1 < len1; i1++ {
+						pointer2, err := decoder.ReadPointer()
+						if err != nil {
+							return err
+						}
+						if pointer2 == 0 {
+							return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+						} else {
+							value2, err := decoder.ReadString()
+							if err != nil {
+								return err
+							}
+							keys0[i1] = value2
+						}
+					}
+					if err := decoder.Finish(); err != nil {
+						return err
+					}
+				}
+			}
+			var values0 []string
+			{
+				pointer1, err := decoder.ReadPointer()
+				if err != nil {
+					return err
+				}
+				if pointer1 == 0 {
+					return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+				} else {
+					len1, err := decoder.StartArray(64)
+					if err != nil {
+						return err
+					}
+					values0 = make([]string, len1)
+					for i1 := uint32(0); i1 < len1; i1++ {
+						pointer2, err := decoder.ReadPointer()
+						if err != nil {
+							return err
+						}
+						if pointer2 == 0 {
+							return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+						} else {
+							value2, err := decoder.ReadString()
+							if err != nil {
+								return err
+							}
+							values0[i1] = value2
+						}
+					}
+					if err := decoder.Finish(); err != nil {
+						return err
+					}
+				}
+			}
+			if len(keys0) != len(values0) {
+				return &bindings.ValidationError{bindings.DifferentSizedArraysInMap,
+					fmt.Sprintf("Number of keys %d is different from number of values %d", len(keys0), len(values0)),
+				}
+			}
+			if err := decoder.Finish(); err != nil {
+				return err
+			}
+			len0 := len(keys0)
+			map0 := make(map[string]string)
+			for i0 := 0; i0 < len0; i0++ {
+				map0[keys0[i0]] = values0[i0]
+			}
+			s.Attributes = map0
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
diff --git a/go/src/mojom/v.io/x/ref/services/discovery/discovery/discovery.mojom.go b/go/src/mojom/v.io/x/ref/services/discovery/discovery/discovery.mojom.go
new file mode 100644
index 0000000..9ad87c7
--- /dev/null
+++ b/go/src/mojom/v.io/x/ref/services/discovery/discovery/discovery.mojom.go
@@ -0,0 +1,1183 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is autogenerated by:
+//     mojo/public/tools/bindings/mojom_bindings_generator.py
+// For:
+//     mojom/v.io/x/ref/services/discovery/discovery.mojom
+//
+
+package discovery
+
+import (
+	"fmt"
+	"mojo/public/go/bindings"
+	"mojo/public/go/system"
+	"sort"
+)
+
+type Discovery interface {
+	Advertise(inService Service) (outHandle DiscoveryStopper_Pointer, err error)
+	Scan(inQuery string, inHandler DiscoveryHandler_Pointer) (outHandle DiscoveryStopper_Pointer, err error)
+}
+
+var discovery_Name = "discovery::Discovery"
+
+type Discovery_Request bindings.InterfaceRequest
+
+func (r *Discovery_Request) Name() string {
+	return discovery_Name
+}
+
+type Discovery_Pointer bindings.InterfacePointer
+
+func (p *Discovery_Pointer) Name() string {
+	return discovery_Name
+}
+
+type Discovery_ServiceFactory struct{
+	Delegate Discovery_Factory
+}
+
+type Discovery_Factory interface {
+	Create(request Discovery_Request)
+}
+
+func (f *Discovery_ServiceFactory) Name() string {
+	return discovery_Name
+}
+
+func (f *Discovery_ServiceFactory) Create(messagePipe system.MessagePipeHandle) {
+	request := Discovery_Request{bindings.NewMessagePipeHandleOwner(messagePipe)}
+	f.Delegate.Create(request)
+}
+
+// CreateMessagePipeForDiscovery creates a message pipe for use with the
+// Discovery interface with a Discovery_Request on one end and a Discovery_Pointer on the other.
+func CreateMessagePipeForDiscovery() (Discovery_Request, Discovery_Pointer) {
+        r, p := bindings.CreateMessagePipeForMojoInterface()
+        return Discovery_Request(r), Discovery_Pointer(p)
+}
+
+const discovery_Advertise_Name uint32 = 0
+const discovery_Scan_Name uint32 = 1
+
+type Discovery_Proxy struct {
+	router *bindings.Router
+	ids bindings.Counter
+}
+
+func NewDiscoveryProxy(p Discovery_Pointer, waiter bindings.AsyncWaiter) *Discovery_Proxy {
+	return &Discovery_Proxy{
+		bindings.NewRouter(p.PassMessagePipe(), waiter),
+		bindings.NewCounter(),
+	}
+}
+
+func (p *Discovery_Proxy) Close_Proxy() {
+	p.router.Close()
+}
+
+type discovery_Advertise_Params struct {
+	inService Service
+}
+
+func (s *discovery_Advertise_Params) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(8, 0)
+	if err := encoder.WritePointer(); err != nil {
+		return err
+	}
+	if err := s.inService.Encode(encoder); err != nil {
+		return err
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var discovery_Advertise_Params_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{16, 0},
+}
+
+func (s *discovery_Advertise_Params) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(discovery_Advertise_Params_Versions), func(i int) bool {
+		return discovery_Advertise_Params_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(discovery_Advertise_Params_Versions) {
+		if discovery_Advertise_Params_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := discovery_Advertise_Params_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		pointer0, err := decoder.ReadPointer()
+		if err != nil {
+			return err
+		}
+		if pointer0 == 0 {
+			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+		} else {
+			if err := s.inService.Decode(decoder); err != nil {
+				return err
+			}
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+type discovery_Advertise_ResponseParams struct {
+	outHandle DiscoveryStopper_Pointer
+}
+
+func (s *discovery_Advertise_ResponseParams) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(8, 0)
+	if err := encoder.WriteInterface(s.outHandle.PassMessagePipe()); err != nil {
+		return err
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var discovery_Advertise_ResponseParams_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{16, 0},
+}
+
+func (s *discovery_Advertise_ResponseParams) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(discovery_Advertise_ResponseParams_Versions), func(i int) bool {
+		return discovery_Advertise_ResponseParams_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(discovery_Advertise_ResponseParams_Versions) {
+		if discovery_Advertise_ResponseParams_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := discovery_Advertise_ResponseParams_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		handle0, err := decoder.ReadInterface()
+		if err != nil {
+			return err
+		}
+		if handle0.IsValid() {
+			handleOwner := bindings.NewMessagePipeHandleOwner(handle0)
+			s.outHandle = DiscoveryStopper_Pointer{handleOwner}
+		} else {
+			return &bindings.ValidationError{bindings.UnexpectedInvalidHandle, "unexpected invalid handle"}
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (p *Discovery_Proxy) Advertise(inService Service) (outHandle DiscoveryStopper_Pointer, err error) {
+	payload := &discovery_Advertise_Params{
+		inService,
+	}
+	header := bindings.MessageHeader{
+		Type: discovery_Advertise_Name,
+		Flags: bindings.MessageExpectsResponseFlag,
+		RequestId: p.ids.Count(),
+	}
+	var message *bindings.Message
+	if message, err = bindings.EncodeMessage(header, payload); err != nil {
+		err = fmt.Errorf("can't encode request: %v", err.Error())
+		p.Close_Proxy()
+		return
+	}
+	readResult := <-p.router.AcceptWithResponse(message)
+	if err = readResult.Error; err != nil {
+		p.Close_Proxy()
+		return
+	}
+	if readResult.Message.Header.Flags != bindings.MessageIsResponseFlag {
+		err = &bindings.ValidationError{bindings.MessageHeaderInvalidFlags,
+			fmt.Sprintf("invalid message header flag: %v", readResult.Message.Header.Flags),
+		}
+		return
+	}
+	if got, want := readResult.Message.Header.Type, discovery_Advertise_Name; got != want {
+		err = &bindings.ValidationError{bindings.MessageHeaderUnknownMethod,
+			fmt.Sprintf("invalid method in response: expected %v, got %v", want, got),
+		}
+		return
+	}
+	var response discovery_Advertise_ResponseParams
+	if err = readResult.Message.DecodePayload(&response); err != nil {
+		p.Close_Proxy()
+		return
+	}
+	outHandle = response.outHandle
+	return
+}
+
+type discovery_Scan_Params struct {
+	inQuery string
+	inHandler DiscoveryHandler_Pointer
+}
+
+func (s *discovery_Scan_Params) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(16, 0)
+	if err := encoder.WritePointer(); err != nil {
+		return err
+	}
+	if err := encoder.WriteString(s.inQuery); err != nil {
+		return err
+	}
+	if err := encoder.WriteInterface(s.inHandler.PassMessagePipe()); err != nil {
+		return err
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var discovery_Scan_Params_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{24, 0},
+}
+
+func (s *discovery_Scan_Params) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(discovery_Scan_Params_Versions), func(i int) bool {
+		return discovery_Scan_Params_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(discovery_Scan_Params_Versions) {
+		if discovery_Scan_Params_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := discovery_Scan_Params_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		pointer0, err := decoder.ReadPointer()
+		if err != nil {
+			return err
+		}
+		if pointer0 == 0 {
+			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+		} else {
+			value0, err := decoder.ReadString()
+			if err != nil {
+				return err
+			}
+			s.inQuery = value0
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		handle0, err := decoder.ReadInterface()
+		if err != nil {
+			return err
+		}
+		if handle0.IsValid() {
+			handleOwner := bindings.NewMessagePipeHandleOwner(handle0)
+			s.inHandler = DiscoveryHandler_Pointer{handleOwner}
+		} else {
+			return &bindings.ValidationError{bindings.UnexpectedInvalidHandle, "unexpected invalid handle"}
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+type discovery_Scan_ResponseParams struct {
+	outHandle DiscoveryStopper_Pointer
+}
+
+func (s *discovery_Scan_ResponseParams) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(8, 0)
+	if err := encoder.WriteInterface(s.outHandle.PassMessagePipe()); err != nil {
+		return err
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var discovery_Scan_ResponseParams_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{16, 0},
+}
+
+func (s *discovery_Scan_ResponseParams) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(discovery_Scan_ResponseParams_Versions), func(i int) bool {
+		return discovery_Scan_ResponseParams_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(discovery_Scan_ResponseParams_Versions) {
+		if discovery_Scan_ResponseParams_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := discovery_Scan_ResponseParams_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		handle0, err := decoder.ReadInterface()
+		if err != nil {
+			return err
+		}
+		if handle0.IsValid() {
+			handleOwner := bindings.NewMessagePipeHandleOwner(handle0)
+			s.outHandle = DiscoveryStopper_Pointer{handleOwner}
+		} else {
+			return &bindings.ValidationError{bindings.UnexpectedInvalidHandle, "unexpected invalid handle"}
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (p *Discovery_Proxy) Scan(inQuery string, inHandler DiscoveryHandler_Pointer) (outHandle DiscoveryStopper_Pointer, err error) {
+	payload := &discovery_Scan_Params{
+		inQuery,
+		inHandler,
+	}
+	header := bindings.MessageHeader{
+		Type: discovery_Scan_Name,
+		Flags: bindings.MessageExpectsResponseFlag,
+		RequestId: p.ids.Count(),
+	}
+	var message *bindings.Message
+	if message, err = bindings.EncodeMessage(header, payload); err != nil {
+		err = fmt.Errorf("can't encode request: %v", err.Error())
+		p.Close_Proxy()
+		return
+	}
+	readResult := <-p.router.AcceptWithResponse(message)
+	if err = readResult.Error; err != nil {
+		p.Close_Proxy()
+		return
+	}
+	if readResult.Message.Header.Flags != bindings.MessageIsResponseFlag {
+		err = &bindings.ValidationError{bindings.MessageHeaderInvalidFlags,
+			fmt.Sprintf("invalid message header flag: %v", readResult.Message.Header.Flags),
+		}
+		return
+	}
+	if got, want := readResult.Message.Header.Type, discovery_Scan_Name; got != want {
+		err = &bindings.ValidationError{bindings.MessageHeaderUnknownMethod,
+			fmt.Sprintf("invalid method in response: expected %v, got %v", want, got),
+		}
+		return
+	}
+	var response discovery_Scan_ResponseParams
+	if err = readResult.Message.DecodePayload(&response); err != nil {
+		p.Close_Proxy()
+		return
+	}
+	outHandle = response.outHandle
+	return
+}
+
+type discovery_Stub struct {
+	connector *bindings.Connector
+	impl Discovery
+}
+
+func NewDiscoveryStub(r Discovery_Request, impl Discovery, waiter bindings.AsyncWaiter) *bindings.Stub {
+	connector := bindings.NewConnector(r.PassMessagePipe(), waiter)
+	return bindings.NewStub(connector, &discovery_Stub{connector, impl})
+}
+
+func (s *discovery_Stub) Accept(message *bindings.Message) (err error) {
+	switch message.Header.Type {
+	case discovery_Advertise_Name:
+		if message.Header.Flags != bindings.MessageExpectsResponseFlag {
+			return &bindings.ValidationError{bindings.MessageHeaderInvalidFlags,
+				fmt.Sprintf("invalid message header flag: %v", message.Header.Flags),
+			}
+		}
+		var request discovery_Advertise_Params
+		if err := message.DecodePayload(&request); err != nil {
+			return err
+		}
+		var response discovery_Advertise_ResponseParams
+		response.outHandle, err = s.impl.Advertise(request.inService)
+		if err != nil {
+			return
+		}
+		header := bindings.MessageHeader{
+			Type: discovery_Advertise_Name,
+			Flags: bindings.MessageIsResponseFlag,
+			RequestId: message.Header.RequestId,
+		}
+		message, err = bindings.EncodeMessage(header, &response)
+		if err != nil {
+			return err
+		}
+		return s.connector.WriteMessage(message)
+	case discovery_Scan_Name:
+		if message.Header.Flags != bindings.MessageExpectsResponseFlag {
+			return &bindings.ValidationError{bindings.MessageHeaderInvalidFlags,
+				fmt.Sprintf("invalid message header flag: %v", message.Header.Flags),
+			}
+		}
+		var request discovery_Scan_Params
+		if err := message.DecodePayload(&request); err != nil {
+			return err
+		}
+		var response discovery_Scan_ResponseParams
+		response.outHandle, err = s.impl.Scan(request.inQuery, request.inHandler)
+		if err != nil {
+			return
+		}
+		header := bindings.MessageHeader{
+			Type: discovery_Scan_Name,
+			Flags: bindings.MessageIsResponseFlag,
+			RequestId: message.Header.RequestId,
+		}
+		message, err = bindings.EncodeMessage(header, &response)
+		if err != nil {
+			return err
+		}
+		return s.connector.WriteMessage(message)
+	default:
+		return &bindings.ValidationError{
+			bindings.MessageHeaderUnknownMethod,
+			fmt.Sprintf("unknown method %v", message.Header.Type),
+		}
+	}
+	return
+}
+
+type DiscoveryStopper interface {
+	Stop() (err error)
+}
+
+var discoveryStopper_Name = "discovery::DiscoveryStopper"
+
+type DiscoveryStopper_Request bindings.InterfaceRequest
+
+func (r *DiscoveryStopper_Request) Name() string {
+	return discoveryStopper_Name
+}
+
+type DiscoveryStopper_Pointer bindings.InterfacePointer
+
+func (p *DiscoveryStopper_Pointer) Name() string {
+	return discoveryStopper_Name
+}
+
+type DiscoveryStopper_ServiceFactory struct{
+	Delegate DiscoveryStopper_Factory
+}
+
+type DiscoveryStopper_Factory interface {
+	Create(request DiscoveryStopper_Request)
+}
+
+func (f *DiscoveryStopper_ServiceFactory) Name() string {
+	return discoveryStopper_Name
+}
+
+func (f *DiscoveryStopper_ServiceFactory) Create(messagePipe system.MessagePipeHandle) {
+	request := DiscoveryStopper_Request{bindings.NewMessagePipeHandleOwner(messagePipe)}
+	f.Delegate.Create(request)
+}
+
+// CreateMessagePipeForDiscoveryStopper creates a message pipe for use with the
+// DiscoveryStopper interface with a DiscoveryStopper_Request on one end and a DiscoveryStopper_Pointer on the other.
+func CreateMessagePipeForDiscoveryStopper() (DiscoveryStopper_Request, DiscoveryStopper_Pointer) {
+        r, p := bindings.CreateMessagePipeForMojoInterface()
+        return DiscoveryStopper_Request(r), DiscoveryStopper_Pointer(p)
+}
+
+const discoveryStopper_Stop_Name uint32 = 0
+
+type DiscoveryStopper_Proxy struct {
+	router *bindings.Router
+	ids bindings.Counter
+}
+
+func NewDiscoveryStopperProxy(p DiscoveryStopper_Pointer, waiter bindings.AsyncWaiter) *DiscoveryStopper_Proxy {
+	return &DiscoveryStopper_Proxy{
+		bindings.NewRouter(p.PassMessagePipe(), waiter),
+		bindings.NewCounter(),
+	}
+}
+
+func (p *DiscoveryStopper_Proxy) Close_Proxy() {
+	p.router.Close()
+}
+
+type discoveryStopper_Stop_Params struct {
+}
+
+func (s *discoveryStopper_Stop_Params) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(0, 0)
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var discoveryStopper_Stop_Params_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{8, 0},
+}
+
+func (s *discoveryStopper_Stop_Params) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(discoveryStopper_Stop_Params_Versions), func(i int) bool {
+		return discoveryStopper_Stop_Params_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(discoveryStopper_Stop_Params_Versions) {
+		if discoveryStopper_Stop_Params_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := discoveryStopper_Stop_Params_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (p *DiscoveryStopper_Proxy) Stop() (err error) {
+	payload := &discoveryStopper_Stop_Params{
+	}
+	header := bindings.MessageHeader{
+		Type: discoveryStopper_Stop_Name,
+		Flags: bindings.MessageNoFlag,
+	}
+	var message *bindings.Message
+	if message, err = bindings.EncodeMessage(header, payload); err != nil {
+		err = fmt.Errorf("can't encode request: %v", err.Error())
+		p.Close_Proxy()
+		return
+	}
+	if err = p.router.Accept(message); err != nil {
+		p.Close_Proxy()
+		return
+	}
+	return
+}
+
+type discoveryStopper_Stub struct {
+	connector *bindings.Connector
+	impl DiscoveryStopper
+}
+
+func NewDiscoveryStopperStub(r DiscoveryStopper_Request, impl DiscoveryStopper, waiter bindings.AsyncWaiter) *bindings.Stub {
+	connector := bindings.NewConnector(r.PassMessagePipe(), waiter)
+	return bindings.NewStub(connector, &discoveryStopper_Stub{connector, impl})
+}
+
+func (s *discoveryStopper_Stub) Accept(message *bindings.Message) (err error) {
+	switch message.Header.Type {
+	case discoveryStopper_Stop_Name:
+		if message.Header.Flags != bindings.MessageNoFlag {
+			return &bindings.ValidationError{bindings.MessageHeaderInvalidFlags,
+				fmt.Sprintf("invalid message header flag: %v", message.Header.Flags),
+			}
+		}
+		var request discoveryStopper_Stop_Params
+		if err := message.DecodePayload(&request); err != nil {
+			return err
+		}
+		err = s.impl.Stop()
+		if err != nil {
+			return
+		}
+	default:
+		return &bindings.ValidationError{
+			bindings.MessageHeaderUnknownMethod,
+			fmt.Sprintf("unknown method %v", message.Header.Type),
+		}
+	}
+	return
+}
+
+type DiscoveryHandler interface {
+	Found(inS Service) (err error)
+	Lost(inId []uint8) (err error)
+}
+
+var discoveryHandler_Name = "discovery::DiscoveryHandler"
+
+type DiscoveryHandler_Request bindings.InterfaceRequest
+
+func (r *DiscoveryHandler_Request) Name() string {
+	return discoveryHandler_Name
+}
+
+type DiscoveryHandler_Pointer bindings.InterfacePointer
+
+func (p *DiscoveryHandler_Pointer) Name() string {
+	return discoveryHandler_Name
+}
+
+type DiscoveryHandler_ServiceFactory struct{
+	Delegate DiscoveryHandler_Factory
+}
+
+type DiscoveryHandler_Factory interface {
+	Create(request DiscoveryHandler_Request)
+}
+
+func (f *DiscoveryHandler_ServiceFactory) Name() string {
+	return discoveryHandler_Name
+}
+
+func (f *DiscoveryHandler_ServiceFactory) Create(messagePipe system.MessagePipeHandle) {
+	request := DiscoveryHandler_Request{bindings.NewMessagePipeHandleOwner(messagePipe)}
+	f.Delegate.Create(request)
+}
+
+// CreateMessagePipeForDiscoveryHandler creates a message pipe for use with the
+// DiscoveryHandler interface with a DiscoveryHandler_Request on one end and a DiscoveryHandler_Pointer on the other.
+func CreateMessagePipeForDiscoveryHandler() (DiscoveryHandler_Request, DiscoveryHandler_Pointer) {
+        r, p := bindings.CreateMessagePipeForMojoInterface()
+        return DiscoveryHandler_Request(r), DiscoveryHandler_Pointer(p)
+}
+
+const discoveryHandler_Found_Name uint32 = 0
+const discoveryHandler_Lost_Name uint32 = 1
+
+type DiscoveryHandler_Proxy struct {
+	router *bindings.Router
+	ids bindings.Counter
+}
+
+func NewDiscoveryHandlerProxy(p DiscoveryHandler_Pointer, waiter bindings.AsyncWaiter) *DiscoveryHandler_Proxy {
+	return &DiscoveryHandler_Proxy{
+		bindings.NewRouter(p.PassMessagePipe(), waiter),
+		bindings.NewCounter(),
+	}
+}
+
+func (p *DiscoveryHandler_Proxy) Close_Proxy() {
+	p.router.Close()
+}
+
+type discoveryHandler_Found_Params struct {
+	inS Service
+}
+
+func (s *discoveryHandler_Found_Params) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(8, 0)
+	if err := encoder.WritePointer(); err != nil {
+		return err
+	}
+	if err := s.inS.Encode(encoder); err != nil {
+		return err
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var discoveryHandler_Found_Params_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{16, 0},
+}
+
+func (s *discoveryHandler_Found_Params) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(discoveryHandler_Found_Params_Versions), func(i int) bool {
+		return discoveryHandler_Found_Params_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(discoveryHandler_Found_Params_Versions) {
+		if discoveryHandler_Found_Params_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := discoveryHandler_Found_Params_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		pointer0, err := decoder.ReadPointer()
+		if err != nil {
+			return err
+		}
+		if pointer0 == 0 {
+			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+		} else {
+			if err := s.inS.Decode(decoder); err != nil {
+				return err
+			}
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (p *DiscoveryHandler_Proxy) Found(inS Service) (err error) {
+	payload := &discoveryHandler_Found_Params{
+		inS,
+	}
+	header := bindings.MessageHeader{
+		Type: discoveryHandler_Found_Name,
+		Flags: bindings.MessageNoFlag,
+	}
+	var message *bindings.Message
+	if message, err = bindings.EncodeMessage(header, payload); err != nil {
+		err = fmt.Errorf("can't encode request: %v", err.Error())
+		p.Close_Proxy()
+		return
+	}
+	if err = p.router.Accept(message); err != nil {
+		p.Close_Proxy()
+		return
+	}
+	return
+}
+
+type discoveryHandler_Lost_Params struct {
+	inId []uint8
+}
+
+func (s *discoveryHandler_Lost_Params) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(8, 0)
+	if err := encoder.WritePointer(); err != nil {
+		return err
+	}
+	encoder.StartArray(uint32(len(s.inId)), 8)
+	for _, elem0 := range s.inId {
+		if err := encoder.WriteUint8(elem0); err != nil {
+			return err
+		}
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var discoveryHandler_Lost_Params_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{16, 0},
+}
+
+func (s *discoveryHandler_Lost_Params) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(discoveryHandler_Lost_Params_Versions), func(i int) bool {
+		return discoveryHandler_Lost_Params_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(discoveryHandler_Lost_Params_Versions) {
+		if discoveryHandler_Lost_Params_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := discoveryHandler_Lost_Params_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		pointer0, err := decoder.ReadPointer()
+		if err != nil {
+			return err
+		}
+		if pointer0 == 0 {
+			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+		} else {
+			len0, err := decoder.StartArray(8)
+			if err != nil {
+				return err
+			}
+			s.inId = make([]uint8, len0)
+			for i0 := uint32(0); i0 < len0; i0++ {
+				value1, err := decoder.ReadUint8()
+				if err != nil {
+					return err
+				}
+				s.inId[i0] = value1
+			}
+			if err := decoder.Finish(); err != nil {
+				return err
+			}
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (p *DiscoveryHandler_Proxy) Lost(inId []uint8) (err error) {
+	payload := &discoveryHandler_Lost_Params{
+		inId,
+	}
+	header := bindings.MessageHeader{
+		Type: discoveryHandler_Lost_Name,
+		Flags: bindings.MessageNoFlag,
+	}
+	var message *bindings.Message
+	if message, err = bindings.EncodeMessage(header, payload); err != nil {
+		err = fmt.Errorf("can't encode request: %v", err.Error())
+		p.Close_Proxy()
+		return
+	}
+	if err = p.router.Accept(message); err != nil {
+		p.Close_Proxy()
+		return
+	}
+	return
+}
+
+type discoveryHandler_Stub struct {
+	connector *bindings.Connector
+	impl DiscoveryHandler
+}
+
+func NewDiscoveryHandlerStub(r DiscoveryHandler_Request, impl DiscoveryHandler, waiter bindings.AsyncWaiter) *bindings.Stub {
+	connector := bindings.NewConnector(r.PassMessagePipe(), waiter)
+	return bindings.NewStub(connector, &discoveryHandler_Stub{connector, impl})
+}
+
+func (s *discoveryHandler_Stub) Accept(message *bindings.Message) (err error) {
+	switch message.Header.Type {
+	case discoveryHandler_Found_Name:
+		if message.Header.Flags != bindings.MessageNoFlag {
+			return &bindings.ValidationError{bindings.MessageHeaderInvalidFlags,
+				fmt.Sprintf("invalid message header flag: %v", message.Header.Flags),
+			}
+		}
+		var request discoveryHandler_Found_Params
+		if err := message.DecodePayload(&request); err != nil {
+			return err
+		}
+		err = s.impl.Found(request.inS)
+		if err != nil {
+			return
+		}
+	case discoveryHandler_Lost_Name:
+		if message.Header.Flags != bindings.MessageNoFlag {
+			return &bindings.ValidationError{bindings.MessageHeaderInvalidFlags,
+				fmt.Sprintf("invalid message header flag: %v", message.Header.Flags),
+			}
+		}
+		var request discoveryHandler_Lost_Params
+		if err := message.DecodePayload(&request); err != nil {
+			return err
+		}
+		err = s.impl.Lost(request.inId)
+		if err != nil {
+			return
+		}
+	default:
+		return &bindings.ValidationError{
+			bindings.MessageHeaderUnknownMethod,
+			fmt.Sprintf("unknown method %v", message.Header.Type),
+		}
+	}
+	return
+}
+
+type Service struct {
+	InterfaceNames string
+	Attrs map[string]string
+	Addrs []string
+}
+
+func (s *Service) Encode(encoder *bindings.Encoder) error {
+	encoder.StartStruct(24, 0)
+	if err := encoder.WritePointer(); err != nil {
+		return err
+	}
+	if err := encoder.WriteString(s.InterfaceNames); err != nil {
+		return err
+	}
+	if err := encoder.WritePointer(); err != nil {
+		return err
+	}
+	encoder.StartMap()
+	{
+		var keys0 []string
+		var values0 []string
+		for key0, value0 := range s.Attrs {
+			keys0 = append(keys0, key0)
+			values0 = append(values0, value0)
+		}
+		if err := encoder.WritePointer(); err != nil {
+			return err
+		}
+		encoder.StartArray(uint32(len(keys0)), 64)
+		for _, elem1 := range keys0 {
+			if err := encoder.WritePointer(); err != nil {
+				return err
+			}
+			if err := encoder.WriteString(elem1); err != nil {
+				return err
+			}
+		}
+		if err := encoder.Finish(); err != nil {
+			return err
+		}
+		if err := encoder.WritePointer(); err != nil {
+			return err
+		}
+		encoder.StartArray(uint32(len(values0)), 64)
+		for _, elem1 := range values0 {
+			if err := encoder.WritePointer(); err != nil {
+				return err
+			}
+			if err := encoder.WriteString(elem1); err != nil {
+				return err
+			}
+		}
+		if err := encoder.Finish(); err != nil {
+			return err
+		}
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	if err := encoder.WritePointer(); err != nil {
+		return err
+	}
+	encoder.StartArray(uint32(len(s.Addrs)), 64)
+	for _, elem0 := range s.Addrs {
+		if err := encoder.WritePointer(); err != nil {
+			return err
+		}
+		if err := encoder.WriteString(elem0); err != nil {
+			return err
+		}
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	if err := encoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
+var service_Versions []bindings.DataHeader = []bindings.DataHeader{
+	bindings.DataHeader{32, 0},
+}
+
+func (s *Service) Decode(decoder *bindings.Decoder) error {
+	header, err := decoder.StartStruct()
+	if err != nil {
+		return err
+	}
+	index := sort.Search(len(service_Versions), func(i int) bool {
+		return service_Versions[i].ElementsOrVersion >= header.ElementsOrVersion
+	})
+	if index < len(service_Versions) {
+		if service_Versions[index].ElementsOrVersion > header.ElementsOrVersion {
+			index--
+		}
+		expectedSize := service_Versions[index].Size
+		if expectedSize != header.Size {
+			return &bindings.ValidationError{bindings.UnexpectedStructHeader,
+				fmt.Sprintf("invalid struct header size: should be %d, but was %d", expectedSize, header.Size),
+			}
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		pointer0, err := decoder.ReadPointer()
+		if err != nil {
+			return err
+		}
+		if pointer0 == 0 {
+			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+		} else {
+			value0, err := decoder.ReadString()
+			if err != nil {
+				return err
+			}
+			s.InterfaceNames = value0
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		pointer0, err := decoder.ReadPointer()
+		if err != nil {
+			return err
+		}
+		if pointer0 == 0 {
+			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+		} else {
+			if err := decoder.StartMap(); err != nil {
+				return err
+			}
+			var keys0 []string
+			{
+				pointer1, err := decoder.ReadPointer()
+				if err != nil {
+					return err
+				}
+				if pointer1 == 0 {
+					return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+				} else {
+					len1, err := decoder.StartArray(64)
+					if err != nil {
+						return err
+					}
+					keys0 = make([]string, len1)
+					for i1 := uint32(0); i1 < len1; i1++ {
+						pointer2, err := decoder.ReadPointer()
+						if err != nil {
+							return err
+						}
+						if pointer2 == 0 {
+							return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+						} else {
+							value2, err := decoder.ReadString()
+							if err != nil {
+								return err
+							}
+							keys0[i1] = value2
+						}
+					}
+					if err := decoder.Finish(); err != nil {
+						return err
+					}
+				}
+			}
+			var values0 []string
+			{
+				pointer1, err := decoder.ReadPointer()
+				if err != nil {
+					return err
+				}
+				if pointer1 == 0 {
+					return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+				} else {
+					len1, err := decoder.StartArray(64)
+					if err != nil {
+						return err
+					}
+					values0 = make([]string, len1)
+					for i1 := uint32(0); i1 < len1; i1++ {
+						pointer2, err := decoder.ReadPointer()
+						if err != nil {
+							return err
+						}
+						if pointer2 == 0 {
+							return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+						} else {
+							value2, err := decoder.ReadString()
+							if err != nil {
+								return err
+							}
+							values0[i1] = value2
+						}
+					}
+					if err := decoder.Finish(); err != nil {
+						return err
+					}
+				}
+			}
+			if len(keys0) != len(values0) {
+				return &bindings.ValidationError{bindings.DifferentSizedArraysInMap,
+					fmt.Sprintf("Number of keys %d is different from number of values %d", len(keys0), len(values0)),
+				}
+			}
+			if err := decoder.Finish(); err != nil {
+				return err
+			}
+			len0 := len(keys0)
+			map0 := make(map[string]string)
+			for i0 := 0; i0 < len0; i0++ {
+				map0[keys0[i0]] = values0[i0]
+			}
+			s.Attrs = map0
+		}
+	}
+	if header.ElementsOrVersion >= 0 {
+		pointer0, err := decoder.ReadPointer()
+		if err != nil {
+			return err
+		}
+		if pointer0 == 0 {
+			return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+		} else {
+			len0, err := decoder.StartArray(64)
+			if err != nil {
+				return err
+			}
+			s.Addrs = make([]string, len0)
+			for i0 := uint32(0); i0 < len0; i0++ {
+				pointer1, err := decoder.ReadPointer()
+				if err != nil {
+					return err
+				}
+				if pointer1 == 0 {
+					return &bindings.ValidationError{bindings.UnexpectedNullPointer, "unexpected null pointer"}
+				} else {
+					value1, err := decoder.ReadString()
+					if err != nil {
+						return err
+					}
+					s.Addrs[i0] = value1
+				}
+			}
+			if err := decoder.Finish(); err != nil {
+				return err
+			}
+		}
+	}
+	if err := decoder.Finish(); err != nil {
+		return err
+	}
+	return nil
+}
+
diff --git a/go/src/v.io/x/ref/services/discovery/ble/neighborhood.go b/go/src/v.io/x/ref/services/discovery/ble/neighborhood.go
new file mode 100644
index 0000000..301316a
--- /dev/null
+++ b/go/src/v.io/x/ref/services/discovery/ble/neighborhood.go
@@ -0,0 +1,525 @@
+// 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.
+
+package main
+
+import (
+	"bytes"
+	"encoding/binary"
+	"encoding/hex"
+	"hash/fnv"
+	"log"
+	"sync"
+	"time"
+
+	"mojom/v.io/x/ref/services/discovery/ble/ble"
+
+	"github.com/paypal/gatt"
+	"github.com/paypal/gatt/linux/cmd"
+	"reflect"
+)
+
+func newService(uuid string, serviceId []byte, attributes map[string]string) *gatt.Service {
+	s := gatt.NewService(gatt.MustParseUUID(uuid))
+	for u, v := range attributes {
+		s.AddCharacteristic(gatt.MustParseUUID(u)).SetValue([]byte(v))
+	}
+	s.AddCharacteristic(uniqueServiceId).SetValue(serviceId)
+	return s
+}
+
+var (
+	attrGAPUUID = gatt.UUID16(0x1800)
+
+	attrDeviceNameUUID        = gatt.UUID16(0x2A00)
+	attrAppearanceUUID        = gatt.UUID16(0x2A01)
+	attrPeripheralPrivacyUUID = gatt.UUID16(0x2A02)
+	attrReconnectionAddrUUID  = gatt.UUID16(0x2A03)
+	attrPeferredParamsUUID    = gatt.UUID16(0x2A04)
+
+	attrGATTUUID           = gatt.UUID16(0x1801)
+	attrServiceChangedUUID = gatt.UUID16(0x2A05)
+)
+
+const (
+	manufacturerId = uint16(1001)
+)
+
+var uniqueServiceId gatt.UUID
+
+func init() {
+	uniqueServiceId = gatt.MustParseUUID("f6445c7f-73fd-4b8d-98d0-c4e02b087844")
+
+}
+
+// https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml
+var gapCharAppearanceGenericComputer = []byte{0x00, 0x80}
+
+func newGapService(name string) *gatt.Service {
+	s := gatt.NewService(attrGAPUUID)
+	s.AddCharacteristic(attrDeviceNameUUID).SetValue([]byte(name))
+	s.AddCharacteristic(attrAppearanceUUID).SetValue(gapCharAppearanceGenericComputer)
+	s.AddCharacteristic(attrPeripheralPrivacyUUID).SetValue([]byte{0x00})
+	s.AddCharacteristic(attrReconnectionAddrUUID).SetValue([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
+	s.AddCharacteristic(attrPeferredParamsUUID).SetValue([]byte{0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xd0, 0x07})
+	return s
+}
+
+func newGattService() *gatt.Service {
+	s := gatt.NewService(attrGATTUUID)
+	s.AddCharacteristic(attrServiceChangedUUID).HandleNotifyFunc(
+		func(r gatt.Request, n gatt.Notifier) {})
+	return s
+}
+
+var gattOptions = []gatt.Option{
+	gatt.LnxMaxConnections(1),
+	gatt.LnxDeviceID(-1, true),
+	gatt.LnxSetAdvertisingParameters(&cmd.LESetAdvertisingParameters{
+		// Set an advertising rate of 150ms.  This value is multipled by
+		// 0.625ms to get the actual rate.
+		AdvertisingIntervalMin: 0x00f4,
+		AdvertisingIntervalMax: 0x00f4,
+		AdvertisingChannelMap:  0x7,
+	}),
+}
+
+type scanner struct {
+	mu         sync.Mutex
+	uuid       string
+	attributes map[string]string
+	ch         chan *update
+	done       bool
+}
+
+func (s *scanner) handleChange(id string, oldService *ble.Service, newService *ble.Service) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	if s.done {
+		return
+	}
+	matches := s.matches(id, newService)
+	oldMatches := s.matches(id, oldService)
+	uuid, err := hex.DecodeString(id)
+	if err != nil {
+		log.Fatal("Failed to decode uuid:",id,",",err)
+	}
+	if oldMatches {
+		s.ch <- &update{
+			found: false,
+			adv: ble.Advertisement{
+				ServiceId: uuid,
+				Service: *oldService,
+			},
+		}
+	}
+
+	if matches {
+		s.ch <- &update{
+			found: true,
+			adv: ble.Advertisement{
+				ServiceId: uuid,
+				Service: *newService,
+			},
+		}
+	}
+}
+
+func (s *scanner) stop() {
+	s.mu.Lock()
+	s.done = true
+	s.mu.Unlock()
+}
+
+func attributeMatch(filter map[string]string, attr map[string]string) bool {
+	for k, v := range filter {
+		if attr[k] != v {
+			return false
+		}
+	}
+	return true
+}
+
+func (s *scanner) matches(id string, service *ble.Service) bool {
+	if service == nil {
+		return false
+	}
+	return (s.uuid == "" || id == s.uuid) && attributeMatch(s.attributes, service.Attributes)
+}
+
+type update struct {
+	found   bool
+	adv ble.Advertisement
+}
+
+type bleCacheEntry struct {
+	id       string
+	name     string
+	services map[string]*ble.Service
+	hash     string
+	lastSeen time.Time
+}
+
+type bleNeighborHood struct {
+	mu sync.Mutex
+
+	neighborsHashCache map[string]*bleCacheEntry
+	knownNeighbors		map[string]*bleCacheEntry
+	services           map[string]*gatt.Service
+	// Scanners out standing calls to Scan that need be serviced.  Each time a
+	// new device appears or disappears, the scanner is notified of the event.
+	scanners map[int64]*scanner
+	// If both sides try to connect to each other at the same time, then only
+	// one will succeed and the other hangs forever.  This means that the side
+	// that hangs won't ever start scanning or advertising again.  To avoid this
+	// we timeout any connections that don't finish in under 4 seconds.  This
+	// channel is closed when a connection has been made successfully, to notify
+	// the cancel goroutine that it doesn't need to do anything.
+	timeoutMap map[string]chan struct{}
+	// The hash that we use to avoid multiple connections are stored in the
+	// advertising data, so we need to store somewhere in the bleNeighorhood
+	// until we are ready to save the new device data.  This map is
+	// the keeper of the data.
+	pendingHashMap map[string]string
+	name           string
+	device         gatt.Device
+	isStopped      bool
+	nextScanId     int64
+}
+
+func newBleNeighborhood(name string) (*bleNeighborHood, error) {
+	b := &bleNeighborHood{
+		neighborsHashCache: make(map[string]*bleCacheEntry),
+		knownNeighbors: make(map[string]*bleCacheEntry),
+		name:               name,
+		services:           make(map[string]*gatt.Service),
+		scanners:           make(map[int64]*scanner),
+		timeoutMap:         make(map[string]chan struct{}),
+		pendingHashMap:     make(map[string]string),
+	}
+	if err := b.startBLEService(); err != nil {
+		return nil, err
+	}
+	return b, nil
+}
+
+func (b *bleNeighborHood) addService(id string, service ble.Service) {
+	b.mu.Lock()
+	b.services[id] = newService(id, service.InstanceId, service.Attributes)
+	v := make([]*gatt.Service, 0, len(b.services))
+	for _, s := range b.services {
+		v = append(v, s)
+	}
+	b.mu.Unlock()
+	b.device.SetServices(v)
+}
+
+func (b *bleNeighborHood) removeService(id string) {
+	b.mu.Lock()
+	delete(b.services, id)
+	v := make([]*gatt.Service, 0, len(b.services))
+	for _, s := range b.services {
+		v = append(v, s)
+	}
+	b.mu.Unlock()
+	b.device.SetServices(v)
+}
+
+func (b *bleNeighborHood) addScanner(uuid *[]byte, attr map[string]string, ch chan *update) int64 {
+	s := &scanner{
+		attributes: attr,
+		ch:         ch,
+	}
+	if uuid != nil {
+		s.uuid = hex.EncodeToString(*uuid)
+	}
+	b.mu.Lock()
+	id := b.nextScanId
+	b.nextScanId++
+	b.scanners[id] = s
+	b.mu.Unlock()
+	return id
+}
+
+func (b *bleNeighborHood) removeScanner(id int64) {
+	b.mu.Lock()
+	scanner, found := b.scanners[id]
+	if found {
+		scanner.stop()
+	}
+	delete(b.scanners, id)
+	b.mu.Unlock()
+}
+
+func (b *bleNeighborHood) Stop() error {
+	b.mu.Lock()
+	b.isStopped = true
+	b.mu.Unlock()
+	b.device.StopAdvertising()
+	b.device.StopScanning()
+	return nil
+}
+
+func (b *bleNeighborHood) advertiseAndScan() {
+	b.mu.Lock()
+	isStopped := b.isStopped
+	b.mu.Unlock()
+	if isStopped {
+		log.Println("Quitting")
+		return
+	}
+	log.Println("starting advertisement and scanning")
+	b.device.Advertise(b.computeAdvertisement())
+	b.device.Scan([]gatt.UUID{}, false)
+}
+
+// seenHash returns
+func (b *bleNeighborHood) seenHash(id string, h string) bool {
+	log.Println("Checking for existence of", h)
+	b.mu.Lock()
+	defer b.mu.Unlock()
+	entry, ok := b.neighborsHashCache[h]
+	if !ok {
+		b.pendingHashMap[id] = h
+		return false
+	}
+
+	if entry.id != id {
+		// This can happen either because two different devices chose the same
+		// endpoint and name, or that one device changed its mac address.  It
+		// seems more likely that the latter happened
+		// TODO(bjornick): Deal with the first case.
+		entry.id = id
+	}
+	entry.lastSeen = time.Now()
+	log.Println("Skipping connect because hashes match")
+	return true
+}
+
+// shouldConnect returns true if a connection should be made to p to get an update on the
+// state of the services on that device.
+func (b *bleNeighborHood) shouldConnect(p gatt.Peripheral, a *gatt.Advertisement) bool {
+	md := a.ManufacturerData
+	// The manufuacture data for other vanadium devices have the format:
+	// 0xe9 0x03 <length> <hash>
+	if len(md) < 2 {
+		return false
+	}
+	if md[0] != uint8(0xe9) || md[1] != uint8(0x03) {
+		return false
+	}
+	hash := md[3:]
+	return !b.seenHash(p.ID(), hex.EncodeToString(hash))
+}
+
+func (b *bleNeighborHood) getAllServices(p gatt.Peripheral) {
+	log.Println("Connected to device")
+
+	b.mu.Lock()
+	h := b.pendingHashMap[p.ID()]
+	delete(b.pendingHashMap, p.ID())
+	b.mu.Unlock()
+	defer func() {
+		b.mu.Lock()
+		ch := b.timeoutMap[p.ID()]
+		delete(b.timeoutMap, p.ID())
+		b.mu.Unlock()
+		if ch != nil {
+			log.Println("Closing channel")
+			close(ch)
+		}
+		p.Device().CancelConnection(p)
+		b.advertiseAndScan()
+	}()
+	/*
+		if err := p.SetMTU(500); err != nil {
+			log.Errorf("Failed to set MTU, err: %s", err)
+			return
+		}
+	*/
+
+	log.Println("Scanning for services")
+	ss, err := p.DiscoverServices(nil)
+
+	if err != nil {
+		log.Printf("Failed to discover services, err: %s\n", err)
+		return
+	}
+
+	services := map[string]*ble.Service{}
+	var name string
+	for _, s := range ss {
+		if s.UUID().Equal(attrGAPUUID) {
+			continue
+		}
+
+		cs, err := p.DiscoverCharacteristics(nil, s)
+		if err != nil {
+			log.Printf("Failed to discover characteristics: %s\n", err)
+			continue
+		}
+
+		charMap := make(map[string]string)
+		uniqueId := []byte{}
+		for _, c := range cs {
+			if s.UUID().Equal(attrGATTUUID) {
+				if !c.UUID().Equal(attrDeviceNameUUID) {
+					continue
+				}
+				v, err := p.ReadLongCharacteristic(c)
+				if err != nil {
+					log.Printf("Failed to read the name: %v\n", err)
+					continue
+
+				}
+				name = string(v)
+				continue
+			}
+			key := c.UUID().String()
+			v, err := p.ReadLongCharacteristic(c)
+			if err != nil {
+				log.Printf("Failed to read the characteristc (%s): %v\n", key, err)
+				continue
+
+			}
+
+			if c.UUID().Equal(uniqueServiceId) {
+				uniqueId = v
+				continue
+			}
+			charMap[key] = string(v)
+		}
+		services[s.UUID().String()] = &ble.Service{
+			Attributes: charMap,
+			InstanceId:  uniqueId,
+		}
+	}
+	b.saveDevice(h, p.ID(), name, services)
+}
+
+func (b *bleNeighborHood) startBLEService() error {
+	d, err := gatt.NewDevice(gattOptions...)
+	if err != nil {
+		return err
+	}
+	onPeriphDiscovered := func(p gatt.Peripheral, a *gatt.Advertisement, rssi int) {
+		log.Printf("Found a device (%s)!\n", p.Name())
+		if b.shouldConnect(p, a) {
+			log.Println("trying to connect to ", p.Name())
+			// We stop the scanning and advertising so we can connect to the new device.
+			// If one device is changing too frequently we might never find all the devices,
+			// since we restart the scan every time we finish connecting, but hopefully
+			// that is rare.
+			p.Device().StopScanning()
+			p.Device().StopAdvertising()
+			p.Device().Connect(p)
+			b.mu.Lock()
+			cancel := make(chan struct{}, 1)
+			b.timeoutMap[p.ID()] = cancel
+			b.mu.Unlock()
+			go func() {
+				select {
+				case <-time.After(4 * time.Second):
+					p.Device().CancelConnection(p)
+					b.advertiseAndScan()
+				case <-cancel:
+				}
+			}()
+		}
+	}
+
+	onPeriphConnected := func(p gatt.Peripheral, err error) {
+		if err != nil {
+			log.Println("Failed to connect:", err)
+			return
+		}
+		b.getAllServices(p)
+	}
+
+	onStateChanged := func(d gatt.Device, s gatt.State) {
+		log.Printf("State: %s\n", s)
+		switch s {
+		case gatt.StatePoweredOn:
+			d.AddService(newGapService(b.name))
+			d.AddService(newGattService())
+
+			b.advertiseAndScan()
+		default:
+			d.StopScanning()
+
+		}
+	}
+
+	d.Handle(
+		gatt.CentralConnected(func(c gatt.Central) { log.Printf("Connect: %v\n", c.ID()) }),
+		gatt.CentralDisconnected(func(c gatt.Central) { log.Printf("Disconnected: %v\n", c.ID()) }),
+		gatt.PeripheralDiscovered(onPeriphDiscovered),
+		gatt.PeripheralConnected(onPeriphConnected),
+	)
+
+	d.Init(onStateChanged)
+	b.device = d
+	return nil
+}
+
+func (b *bleNeighborHood) saveDevice(hash string, id string, name string, services map[string]*ble.Service) {
+	b.mu.Lock()
+	defer b.mu.Unlock()
+	_, found := b.neighborsHashCache[hash]
+	if found {
+		log.Printf("Skipping a new save for the same hash (%s) for %s\n",
+			hash, name)
+		return
+	}
+	oldServices := map[string]*ble.Service{}
+	if oldEntry, ok := b.knownNeighbors[id]; ok {
+		oldServices = oldEntry.services
+	}
+
+	newEntry := &bleCacheEntry{
+		id: id,
+		hash:     hash,
+		name:     name,
+		services: services,
+		lastSeen: time.Now(),
+	}
+	b.neighborsHashCache[hash] = newEntry
+	b.knownNeighbors[id] = newEntry
+	log.Println("Looking through", len(b.scanners), "scanners *****")
+	for _, s := range b.scanners {
+		for id, oldService := range oldServices {
+			newValue := services[id]
+			if !reflect.DeepEqual(oldService, newValue) {
+				s.handleChange(id, oldService, newValue)
+			}
+		}
+
+		for id, newService := range newEntry.services {
+			if _, ok := oldServices[id]; !ok {
+				s.handleChange(id, nil, newService)
+			}
+		}
+	}
+
+}
+
+func (b *bleNeighborHood) computeAdvertisement() *gatt.AdvPacket {
+	// The hash is:
+	// Hash(Hash(name),Hash(b.endpoints))
+	hasher := fnv.New64()
+	nameHasher := fnv.New64()
+	nameHasher.Write([]byte(b.name))
+	binary.Write(hasher, binary.BigEndian, nameHasher.Sum64())
+	for k, _ := range b.services {
+		innerHash := fnv.New64()
+		innerHash.Write([]byte(k))
+		binary.Write(hasher, binary.BigEndian, innerHash.Sum64())
+	}
+	var buf bytes.Buffer
+	binary.Write(&buf, binary.BigEndian, hasher.Sum64())
+	adv := &gatt.AdvPacket{}
+	adv.AppendManufacturerData(manufacturerId, buf.Bytes())
+	adv.AppendName(b.name)
+	return adv
+}
diff --git a/go/src/v.io/x/ref/services/discovery/ble/service.go b/go/src/v.io/x/ref/services/discovery/ble/service.go
new file mode 100644
index 0000000..bf0a91a
--- /dev/null
+++ b/go/src/v.io/x/ref/services/discovery/ble/service.go
@@ -0,0 +1,145 @@
+package main
+
+import (
+	"encoding/hex"
+	"log"
+	"sync"
+
+	"mojo/public/go/application"
+	"mojo/public/go/bindings"
+	"mojo/public/go/system"
+
+	"mojom/v.io/x/ref/services/discovery/ble/ble"
+)
+
+//#include "mojo/public/c/system/types.h"
+import "C"
+
+type bleImpl struct {
+	n *bleNeighborHood
+	d *delegate
+}
+
+func (b *bleImpl) Advertise(adv ble.Advertisement) (ble.PluginStopper_Pointer, error) {
+	uuid := hex.EncodeToString(adv.ServiceId)
+	b.n.addService(uuid, adv.Service)
+	return b.createHandleFor(&stopAdvertingImpl{uuid: uuid, n: b.n}), nil
+}
+
+func (b *bleImpl) Scan(uuid *[]uint8, attr map[string]string, handler ble.PluginScanHandler_Pointer) (ble.PluginStopper_Pointer, error) {
+	ch := make(chan *update, 10)
+	proxy := ble.NewPluginScanHandlerProxy(handler, bindings.GetAsyncWaiter())
+	go func() {
+		for v := range ch {
+			if v.found {
+				proxy.Found(v.adv)
+			} else {
+				proxy.Lost(v.adv)
+			}
+		}
+	}()
+	id := b.n.addScanner(uuid, attr, ch)
+	return b.createHandleFor(&stopScanImpl{id: id, n: b.n}), nil
+}
+
+func (b *bleImpl) createHandleFor(h ble.PluginStopper) ble.PluginStopper_Pointer {
+	req, ptr := ble.CreateMessagePipeForPluginStopper()
+	stub := ble.NewPluginStopperStub(req, h, bindings.GetAsyncWaiter())
+	b.d.addStub(stub)
+	go func() {
+		for {
+			if err := stub.ServeRequest(); err != nil {
+				connErr, ok := err.(*bindings.ConnectionError)
+				if !ok || !connErr.Closed() {
+					log.Println(err)
+				}
+				break
+			}
+		}
+	}()
+
+	return ptr
+}
+
+type stopAdvertingImpl struct {
+	uuid string
+	n    *bleNeighborHood
+}
+
+func (s *stopAdvertingImpl) Stop() error {
+	s.n.removeService(s.uuid)
+	return nil
+}
+
+type stopScanImpl struct {
+	id int64
+	n  *bleNeighborHood
+}
+
+func (s *stopScanImpl) Stop() error {
+	s.n.removeScanner(s.id)
+	return nil
+}
+
+type delegate struct {
+	mu    sync.Mutex
+	n     *bleNeighborHood
+	stubs []*bindings.Stub
+}
+
+func (d *delegate) Initialize(ctx application.Context) {
+	args := ctx.Args()
+	if len(args) < 2 {
+		log.Println("Device name needs to be passed in")
+		ctx.Close()
+	}
+	name := ctx.Args()[1]
+	n, err := newBleNeighborhood(name)
+	if err != nil {
+		log.Println("Failed to start neighborhood", err)
+		ctx.Close()
+	}
+	d.n = n
+}
+
+func (d *delegate) Create(req ble.V23Ble_Request) {
+	stub := ble.NewV23BleStub(req, &bleImpl{n: d.n, d: d}, bindings.GetAsyncWaiter())
+	d.addStub(stub)
+	go func() {
+		for {
+			if err := stub.ServeRequest(); err != nil {
+				connErr, ok := err.(*bindings.ConnectionError)
+				if !ok || !connErr.Closed() {
+					log.Println(err)
+				}
+				break
+			}
+		}
+	}()
+}
+
+func (d *delegate) addStub(s *bindings.Stub) {
+	d.mu.Lock()
+	d.stubs = append(d.stubs, s)
+	d.mu.Unlock()
+}
+func (d *delegate) AcceptConnection(conn *application.Connection) {
+	conn.ProvideServices(&ble.V23Ble_ServiceFactory{d})
+}
+
+func (d *delegate) Quit() {
+	for _, stub := range d.stubs {
+		stub.Close()
+	}
+	if d.n != nil {
+		d.n.Stop()
+	}
+}
+
+//export MojoMain
+func MojoMain(handle C.MojoHandle) C.MojoResult {
+	application.Run(&delegate{}, system.MojoHandle(handle))
+	return C.MOJO_RESULT_OK
+}
+
+func main() {}
diff --git a/go/src/v.io/x/ref/services/discovery/driver/driver.go b/go/src/v.io/x/ref/services/discovery/driver/driver.go
new file mode 100644
index 0000000..038345b
--- /dev/null
+++ b/go/src/v.io/x/ref/services/discovery/driver/driver.go
@@ -0,0 +1,60 @@
+package main
+
+import (
+	"crypto/sha256"
+	"encoding/hex"
+	"log"
+	"mojo/public/go/application"
+	"mojo/public/go/bindings"
+	"mojo/public/go/system"
+	"mojom/v.io/x/ref/services/discovery/ble/ble"
+)
+
+//#include "mojo/public/c/system/types.h"
+import "C"
+
+type discoveryDelegate struct {
+	bleHandlerProxy *ble.PluginStopper_Proxy
+}
+
+func (d *discoveryDelegate) Initialize(ctx application.Context) {
+	hash := sha256.Sum256([]byte("foobar"))
+	uuid := hash[:16]
+	hash = sha256.Sum256([]byte("key1"))
+	key1 := hex.EncodeToString(hash[:16])
+	bleRequest, blePointer := ble.CreateMessagePipeForV23Ble()
+	ctx.ConnectToApplication("https://mojo.v.io/ble.mojo").ConnectToService(&bleRequest)
+	bleProxy := ble.NewV23BleProxy(blePointer, bindings.GetAsyncWaiter())
+	adv := ble.Advertisement{
+		ServiceId: uuid,
+		Service: ble.Service{
+			Attributes: map[string]string{key1: "value1"},
+			InstanceId: []byte("random-uuid"),
+		},
+	}
+	handlerPtr, err := bleProxy.Advertise(adv)
+	if err != nil {
+		log.Println(err)
+		ctx.Close()
+		return
+	}
+	d.bleHandlerProxy = ble.NewPluginStopperProxy(handlerPtr, bindings.GetAsyncWaiter())
+}
+
+func (d *discoveryDelegate) AcceptConnection(conn *application.Connection) {
+	conn.Close()
+}
+
+func (d *discoveryDelegate) Quit() {
+	if d.bleHandlerProxy != nil {
+		d.bleHandlerProxy.Stop()
+	}
+}
+
+//export MojoMain
+func MojoMain(handle C.MojoHandle) C.MojoResult {
+	application.Run(&discoveryDelegate{}, system.MojoHandle(handle))
+	return C.MOJO_RESULT_OK
+}
+
+func main() {}
diff --git a/go/src/v.io/x/ref/services/discovery/scanner/scanner.go b/go/src/v.io/x/ref/services/discovery/scanner/scanner.go
new file mode 100644
index 0000000..65745d7
--- /dev/null
+++ b/go/src/v.io/x/ref/services/discovery/scanner/scanner.go
@@ -0,0 +1,84 @@
+package main
+
+import (
+	"crypto/sha256"
+	"log"
+	"mojo/public/go/application"
+	"mojo/public/go/bindings"
+	"mojo/public/go/system"
+	"mojom/v.io/x/ref/services/discovery/ble/ble"
+)
+
+//#include "mojo/public/c/system/types.h"
+import "C"
+
+type responseHandler struct{}
+
+func (r *responseHandler) Found(a ble.Advertisement) error {
+	log.Println("Found:", a)
+	return nil
+}
+
+func (r *responseHandler) Lost(a ble.Advertisement) error {
+	log.Println("Removed:", a)
+	return nil
+}
+
+type scannerDelegate struct {
+	bleHandlerProxy *ble.PluginStopper_Proxy
+	stubs           []*bindings.Stub
+}
+
+func (s *scannerDelegate) Initialize(ctx application.Context) {
+	hash := sha256.Sum256([]byte("foobar"))
+	uuid := hash[:16]
+	hash = sha256.Sum256([]byte("key1"))
+	// key1 := hex.EncodeToString(hash[:16])
+	bleRequest, blePointer := ble.CreateMessagePipeForV23Ble()
+	ctx.ConnectToApplication("https://mojo.v.io/ble.mojo").ConnectToService(&bleRequest)
+	bleProxy := ble.NewV23BleProxy(blePointer, bindings.GetAsyncWaiter())
+	cbRequest, cbPointer := ble.CreateMessagePipeForPluginScanHandler()
+	stub := ble.NewPluginScanHandlerStub(cbRequest, &responseHandler{}, bindings.GetAsyncWaiter())
+	s.stubs = append(s.stubs, stub)
+	go func() {
+		for {
+			if err := stub.ServeRequest(); err != nil {
+				connErr, ok := err.(*bindings.ConnectionError)
+				if !ok || !connErr.Closed() {
+					log.Println(err)
+				}
+				break
+			}
+		}
+	}()
+	log.Println("Calling scanner")
+	handlerPtr, err := bleProxy.Scan(&uuid, map[string]string{}, cbPointer)
+	if err != nil {
+		log.Println(err)
+		ctx.Close()
+		return
+	}
+	s.bleHandlerProxy = ble.NewPluginStopperProxy(handlerPtr, bindings.GetAsyncWaiter())
+}
+
+func (*scannerDelegate) AcceptConnection(conn *application.Connection) {
+	conn.Close()
+}
+
+func (s *scannerDelegate) Quit() {
+	if s.bleHandlerProxy != nil {
+		s.bleHandlerProxy.Stop()
+	}
+
+	for _, stub := range s.stubs {
+		stub.Close()
+	}
+}
+
+//export MojoMain
+func MojoMain(handle C.MojoHandle) C.MojoResult {
+	application.Run(&scannerDelegate{}, system.MojoHandle(handle))
+	return C.MOJO_RESULT_OK
+}
+
+func main() {}
diff --git a/mojoconfig b/mojoconfig
new file mode 100644
index 0000000..47f802a
--- /dev/null
+++ b/mojoconfig
@@ -0,0 +1,15 @@
+# Derived from $MOJO_DIR/mojoconfig.
+
+{
+  'dev_servers': [
+    {
+      'host': 'https://mojo.v.io/',
+      'mappings': [
+        ('', [
+          # For ble.mojo.
+          '@{DISCOVERY_BUILD_DIR}',
+        ]),
+      ],
+    }
+  ],
+}
diff --git a/mojom/v.io/x/ref/services/discovery/ble/ble.mojom b/mojom/v.io/x/ref/services/discovery/ble/ble.mojom
new file mode 100644
index 0000000..62985f7
--- /dev/null
+++ b/mojom/v.io/x/ref/services/discovery/ble/ble.mojom
@@ -0,0 +1,37 @@
+module ble;
+
+interface V23BLE {
+    // Exports the given service id with the attributes specified. The
+    // keys should be string forms of 128-bit uuids.  The values can be anything.
+    // Returns a handle to the service that can be used to stop the Advertisement.
+    Advertise(Advertisement adv) => (PluginStopper Handler);
+
+    // Scans for all services which match the given attributes.  The key in
+    // the attribute map should be 128-bit uuids in human readable format.
+    // All services that have an exact match for the given attributes are
+    // returned.  Attributes not specified in attributes will not be used
+    // in the match.  Calls handler with each new service found.  Returns
+    // a handle to the Scan operation so it can be canceled when no longer
+    // relevant
+    Scan(array<uint8>? id, map<string, string> attributes, PluginScanHandler handler) => (PluginStopper Handler);
+};
+
+struct Advertisement {
+    array<uint8> ServiceId;
+    Service Service;
+};
+
+struct Service {
+  array<uint8> InstanceId;
+  map<string, string> Attributes;
+};
+
+interface PluginScanHandler {
+    Found(Advertisement s);
+
+    Lost(Advertisement s);
+};
+
+interface PluginStopper {
+    Stop();
+};
diff --git a/mojom/v.io/x/ref/services/discovery/discovery.mojom b/mojom/v.io/x/ref/services/discovery/discovery.mojom
new file mode 100644
index 0000000..17b5146
--- /dev/null
+++ b/mojom/v.io/x/ref/services/discovery/discovery.mojom
@@ -0,0 +1,29 @@
+module discovery;
+
+interface Discovery {
+	// TODO(bjornick): Figure out how to do security in mojo
+	Advertise(Service service) => (DiscoveryStopper Handle);
+
+	Scan(string query, DiscoveryHandler handler) => (DiscoveryStopper Handle);
+};
+
+interface DiscoveryStopper {
+	// Stops the Advertisement/Scanning that is associated with this service
+	Stop();
+};
+
+interface DiscoveryHandler {
+	Found(Service s);
+
+	Lost(array<uint8> id);
+};
+
+struct Service {
+	// The interface name this service implements.
+	// E.g. 'v.io/v23/Display.T
+	string InterfaceNames;
+
+	map<string, string> Attrs;
+
+	array<string> Addrs;
+};