diff --git a/pkg/bluetooth/advert_test.go b/pkg/bluetooth/advert_test.go index d3a6d03..5e1ad21 100644 --- a/pkg/bluetooth/advert_test.go +++ b/pkg/bluetooth/advert_test.go @@ -105,9 +105,9 @@ func TestO5AdvertisementFitsWithoutTheName(t *testing.T) { if n := a.Len(); n > 31 { t.Fatalf("the advertisement is %d bytes, over the 31 a packet allows", n) } - if a.Len()+len(AdvertisedName)+2 < 31 { + if a.Len()+len(O5AdvertisedName)+2 < 31 { t.Errorf("the name would fit after all (%d + %d), so the scan-response path is untested", - a.Len(), len(AdvertisedName)) + a.Len(), len(O5AdvertisedName)) } } @@ -127,35 +127,20 @@ func reverseBytes(b []byte) []byte { return out } -// A real Omnipod 5 names itself "AP ", captured from -// a pod as "AP FFFFFFFE 0E977731000A3AB7" while it was still unpaired. The -// simulator mirrors that shape so a pod looks like one in a scan. func TestO5AdvertisedName(t *testing.T) { - o5 := ProfileFor(ModeO5) - - unpaired := o5.AdvertisedName(0) - if unpaired != "AP FFFFFFFE 0E146DB10006E451" { - t.Errorf("an unpaired pod is named %q", unpaired) - } - paired := o5.AdvertisedName(0x002A1C6C) - if paired != "AP 002A1C6C 0E146DB10006E451" { - t.Errorf("a paired pod is named %q", paired) + name := ProfileFor(ModeO5).AdvertisedName() + if name != "pod simulator" { + t.Errorf("a simulated Omnipod 5 is named %q", name) } // The name goes in the scan response, which is also 31 bytes: two of // header plus the name itself. - if n := 2 + len(paired); n > 31 { + if n := 2 + len(name); n > 31 { t.Errorf("the name needs %d bytes of the scan response, over 31", n) } - // The lot in the name has to decode as an Omnipod 5, as the real pod's - // 0E977731 does: bits 25-30 are the product, 7 for a U100 Omnipod 5. - if got := (0x0E146DB1 >> 25) & 0x3f; got != 7 { - t.Errorf("the advertised lot decodes as product %d, want 7", got) - } - // A DASH keeps the name this simulator has always used. - if got := ProfileFor(ModeDash).AdvertisedName(0); got != AdvertisedName { + if got := ProfileFor(ModeDash).AdvertisedName(); got != AdvertisedName { t.Errorf("a DASH is named %q, want %q", got, AdvertisedName) } } diff --git a/pkg/bluetooth/gatt_linux.go b/pkg/bluetooth/gatt_linux.go index 0f735a4..fa663ab 100644 --- a/pkg/bluetooth/gatt_linux.go +++ b/pkg/bluetooth/gatt_linux.go @@ -153,7 +153,7 @@ func (r *gattRadio) advertise() error { } uuids = append(uuids, u) } - return r.device.AdvertiseNameAndServices(r.profile.AdvertisedName(pdmId), uuids) + return r.device.AdvertiseNameAndServices(r.profile.AdvertisedName(), uuids) } // reportMTU records and logs the ATT MTU once the phone has negotiated one. diff --git a/pkg/bluetooth/impossible.go b/pkg/bluetooth/impossible.go index 0a142a6..3e104fe 100644 --- a/pkg/bluetooth/impossible.go +++ b/pkg/bluetooth/impossible.go @@ -288,7 +288,7 @@ func (r *simRadio) discovered() { return } uuids := r.profile.AdvertisedServiceUUIDs(podId, pdmId, state) - name := r.profile.AdvertisedName(pdmId) + name := r.profile.AdvertisedName() c.send(map[string]interface{}{"type": "didDiscover", "id": r.id, "name": name, "rssi": -60, "adv": map[string]interface{}{ "kCBAdvDataLocalName": name, diff --git a/pkg/bluetooth/profile.go b/pkg/bluetooth/profile.go index 80b7863..4a798c1 100644 --- a/pkg/bluetooth/profile.go +++ b/pkg/bluetooth/profile.go @@ -3,8 +3,6 @@ package bluetooth import ( "encoding/binary" "fmt" - - "github.com/avereha/pod/pkg/response" ) // Mode is the kind of pod being simulated. It decides the BLE profile, the @@ -142,15 +140,15 @@ func O5PdmIdFromHello(cmd []byte) (uint32, bool) { // its scan response when a 128-bit service UUID has filled the advertisement. // // A real Omnipod 5 names itself "AP ", captured from -// a pod as "AP FFFFFFFE 0E977731000A3AB7" before it was paired. A DASH keeps -// the name this simulator has always used. -func (p Profile) AdvertisedName(pdmId uint32) string { - if !p.Mode.IsO5() { - return AdvertisedName - } - id := O5NotPairedPdmId - if pdmId != 0 { - id = pdmId +// a pod as "AP FFFFFFFE 0E977731000A3AB7" before it was paired. Nothing reads +// the name, so a simulated one just says what it is. A DASH keeps the name +// this simulator has always used. +func (p Profile) AdvertisedName() string { + if p.Mode.IsO5() { + return O5AdvertisedName } - return fmt.Sprintf("AP %08X %08X%08X", id, response.LotOmnipod5, response.Tid) + return AdvertisedName } + +// O5AdvertisedName is the local name a simulated Omnipod 5 advertises. +const O5AdvertisedName = "pod simulator" diff --git a/pkg/pod/pod.go b/pkg/pod/pod.go index f9391b6..22add65 100644 --- a/pkg/pod/pod.go +++ b/pkg/pod/pod.go @@ -89,9 +89,12 @@ func (p *Pod) StartAcceptingCommands() { log.Infof("pkg pod; got first command: as string: %s", firstCmd) if pdmId, ok := bluetooth.O5PdmIdFromHello(firstCmd); ok { log.Infof("pkg pod; HELLO from controller %08x", pdmId) - if p.isO5() { - // An Omnipod 5 puts its controller's id in the one service - // UUID it advertises, so the phone can find its own pod. + if p.isO5() && p.state.LTK != nil { + // A paired Omnipod 5 puts its controller's id in the one service + // UUID it advertises, so the phone can find its own pod. An + // unpaired one keeps advertising as pairable until pairing + // completes, so a phone that abandons the exchange can find it + // again. p.mtx.Lock() p.state.PdmId = pdmId p.mtx.Unlock()