Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 7 additions & 22 deletions pkg/bluetooth/advert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}

Expand All @@ -127,35 +127,20 @@ func reverseBytes(b []byte) []byte {
return out
}

// A real Omnipod 5 names itself "AP <controller id> <lot><tid>", 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)
}
}
2 changes: 1 addition & 1 deletion pkg/bluetooth/gatt_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion pkg/bluetooth/impossible.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
22 changes: 10 additions & 12 deletions pkg/bluetooth/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 <controller id> <lot><tid>", 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"
9 changes: 6 additions & 3 deletions pkg/pod/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Loading