Neural Band
The Meta Neural Band is the wrist band that comes with Meta Ray-Ban Display. It reads hand gestures (EMG) and sends them to the Display. It has its own link to the phone, with the same AirShield and DataX as the glasses, a few differences in link setup, and one RPC service. This page covers the band's link, its ownership, its RPC service, and how it is shared with the Display.
| Meta Neural Band | |
|---|---|
| Product id | 1537 |
| Device type | 4 |
| Codename | ceres |
| Update device type | ota.ceres.user |
| L2CAP PSM | 129 |
The link
The band is a bonded LE peer, like the glasses. The phone reads the PSM over GATT (129) and opens an L2CAP channel. Then it runs the same AirShield handshake and svc36 trust as with glasses. See Link setup and AirShield. The differences:
| Glasses | Neural Band | |
|---|---|---|
| AirShield version, RequestEncryption field 7 | 17 | 17 (0x11) |
| Parameters | 31 | 27, without Multikey: one HKDF key is both the AES and the HMAC key |
| Key hint | One | One |
| Its EndLinkSetup | On 0x8001 | On a channel of its own, 0x9001, with no service tag |
| Its channel numbers | From 0x0002 | From 0x1001 |
The band also sends svc79 constellation auth trust. The phone answers it with 0xC001, as it does for glasses.
Ownership
Change of owner runs over svc36 as for glasses. See Meta login and ownership. The one difference is in pair_request: its additional_data must carry secondary_cert, the base64 of field 5 of the band's identity response. Glasses leave field 5 empty. Without secondary_cert, Meta's pair answers HTTP 500.
svc52822: wireless input
Service 52822 (wireless_input_service) is the band's RPC service. Every request is message type 788, wrapped in an RpcRequest. The field of the RpcRequest names the call:
| Field | Call |
|---|---|
| 2 | Battery |
| 3 | Device info |
| 4 | Stream control |
| 5 | Configuration, such as handedness (bit 512 and isLeftHanded) |
| 7 | Firmware update |
| 9 | Bulk transfer |
| 11 | Gesture acknowledgement |
| 12 | Bug report |
| 14 | Uniband |
The band also sends EMG, IMU and gesture events to the phone on this service.
A firmware update runs as follows: the phone gets the image from FetchSystemUpdates (device type ota.ceres.user) and its key-encryption key from FetchEncryptedKEKInfoByKEKId. It installs the key (type 6), sends the file over bulk transfer, then starts, verifies and commits the update.
Sharing the band with the Display
The Display is the band's central, and the band its peripheral. The phone holds its own band link only while the Display does not need it. The Display asks for the band on service 150 (conn_device_lease):
| NotifyLease field | Content |
|---|---|
peerKeyHint |
Which peer the lease is for |
leaseType |
1 RELAY, 2 LOW, 3 MED, 4 HIGH |
The phone matches the key hint against the band's key, the device_ec_public_key in additional_data of Meta's final ownership receipt for the band. It is not the key in the band's certificate. When it matches, the phone answers OK and pauses its own band link while the Display holds any lease other than RELAY.
Pairing the band to a Display
Service 41109 (bt_peer_connection) on the Display pairs a band to it. The sequence:
- The phone owns the band and has a link to it.
- The phone sends GET_BLE_PAIRING_INFO to both the band and the Display.
- The phone sends ENABLE_PAIRING_MODE to the band and waits for PAIRING_MODE_READY.
- The phone lets go of the band.
- The phone sends PAIR to the Display with the band's identity, its advertising address and its key hint. When the band and the Display already list each other, it sends CONNECT instead.
- The Display answers PAIRING_EVENT 0 when the band is paired.
The key hint is a 4-byte salt and the first 8 bytes of SHA-256(SHA-256(public key) || salt), where the public key is the 64-byte raw key.
The band's manifest
The band holds a constellation manifest too, with the same messages as the glasses. They go on svc102 (embconn_link_mgr_manifest) when the band's AirShield version is 0x11 or higher, and on svc10 before that. The band and the Display hand a newer manifest to each other when both trust its authority.
The band's streams
A StreamControlReq (RpcRequest field 4) turns the band's streams on and off. From protocol 2 it carries a map in field 36 {1 message type, 2 on}, with priority 35 (90, GENERIC_C50) and the use case 37 (GenericC50). Before protocol 2 it has one bool a stream, and a bool left out reads false, so one request must name every stream that stays on. The band then sends each stream's samples as svc52822 messages of that type. Names from Meta AI 297 (EnumC31314GFt), fields from the Display's EmgImu and StreamControl protos.
| Type | Stream | Bool before protocol 2 |
|---|---|---|
| 13, 60 to 200 | Gestures | 3 enableGestures |
| 15 | IMU_GYRO |
6 enableGyro |
| 16 | IMU_ACCEL |
5 enableAcc |
| 17 | IMU_MAG |
7 enableMag |
| 18 | IMU_QUAT |
8 enableQuat |
| 32 | TOUCH |
24 |
| 35, 38 | Steps and activity detection (health data collection) | |
| 46 | BAND_ORIENTATION_RESULT |
29 |
| 53, 54 | WRIST_TILT_EVENT, WRIST_DOWN_EVENT |
39, 40 |
| 55 | TRACKPAD |
45 |
| 56 | HR |
47 |
| 58 | GRIP_STRENGTH |
49 |
| 201 | THERMAL |
41 |
The IMU samples are RawImu*Sample {1 seq, 2 timestamp, 3 channels}. For the gyroscope, accelerometer and magnetometer, the channels are three little-endian int16. The Meta app scales the gyroscope by 0.0012217305 rad/s and the accelerometer by 0.0023928226 m/s² a step, and leaves the magnetometer unscaled. For the orientation, the channels are four little-endian floats, x, y, z and w. The unit of the timestamp is not checked.
Visor turns the IMU on for the VDK's band.motion while the band is shared. Not measured yet.
Gestures and the buzz
A svc7009 session on an app's link forwards the band's gestures while the band stays with the Display. See Device Access Toolkit.
The Display does not relay svc52822 from the phone: a buzz sent on the phone's link to the Display answers 0xC001. The Display's own haptics services are internal to it. To buzz the band, the phone needs its own band link. It takes the band with svc41109 DISCONNECT, buzzes it and gives it back with CONNECT. Meanwhile the Display has no band.
The relay: the Display reaches the band through the phone
The band takes one central. While the phone holds it, the Display can still reach it through the phone, as the Meta app does on a RELAY lease (Meta AI 297 RelayStreamManager, EndpointCoordinator, RelayedConnection):
- The phone opens a relay sub-stream on its link to the Display and one on its link to the band. See AirShield frames and ciphers.
- It sends each side
OPEN_TO_ADVERTISEwhen both peers announced AirShield 0x11 or later in their RequestEncryption (field 7), and plainOPENotherwise. - From then on it passes every frame and command of one sub-stream to the other. The Display and the band run their own link setup and session inside, end to end. The phone reads none of it.
- When one side sends
LOST,REJECTEDorNO_ROUTE, the phone sendsLOSTto the other and opens both again, up to five times.
The Meta app joins the band's endpoint to the Display's when it sets the pair up ("establishing direct connection between central and peripheral"). The Display has persist.vendor.meta.atc.enable_relay=1 and an ATC RelayManager that takes such sub-streams, matches the advertised peer with its manifest, and starts its link to the band on them. Its leases then say RELAY, and the phone keeps its own band link: it reads the band's gestures and buzzes it while the Display uses the band too.
Visor does this when the person turns on Share with the phone in the band settings. Not measured on the glasses yet.
The EMG relay receiver
The Display has a second, simpler path: /system_ext/bin/emgrelay_receiver. It starts once at boot (class main, oneshot) and exits unless MobileConfig hn_demos.enable_emg_receiver is true. hn_demos.emg_relay_receiver_always_on picks its mode:
- Off: it serves svc23466 (
EMG_RELAY_RECEIVER) on each link. When the phone opens a channel, it answers with an empty message (the connection ack). - On: it opens svc23466 on the phone itself, on request from the virtual-EMG service or
debug.emgrelay.stream_request=1, and sends an empty message (the handshake init).
Either way the phone then sends message type 1, an OpaqueMessage, or type 2, an EmgInferenceEvent, and the receiver injects them into the Display's virtual-EMG service (vemg_aidl, com.meta.wearable.emg.sim) as if the band had sent them:
| Message | Fields |
|---|---|
OpaqueMessage |
1 OpaqueData, 2 stream id (1 custom gestures, 2 trackpad, 3 gestures), 3 timestamp in microseconds |
OpaqueData |
1 encoding, 2 bytes |
GestureEvent, in the bytes of stream 3 |
1 seq, 2 timestamp, 3 finger, 4 action, 5 derived action, as the band sends it |
Stream 3 goes to the service's gesture call with finger, action and derived action. Other streams go in whole with their stream id and encoding. Both flags are demo flags, off by default, so Visor uses the relay above.
Reference code: Band.kt, GlassesSession.kt
Open questions
- Most svc52822 bodies beyond the call list.
- Whether the band's own firmware update flow works end to end from a phone other than Meta's.
- The full svc41109 message table and its error codes.
- When the Display asks for each lease type.
- Whether the band and the Display bring up their link through a phone that is not Meta's relay, and whether the band then sends gestures on both sessions.