Device state and build info
This page covers what the glasses push to the phone about their state and build. That is service 28 (device state, starting apps and capture intents), service 100 (build info and serial), service 22 (the clock and device info) and the capture signals on service 2000. Serial numbers, Bluetooth addresses and device uuids appear as placeholders: <serial>, AA:BB:CC:DD:EE:FF and <device-uuid>.
How the state arrives
The phone sends the svc100 announce and then the svc28 subscribe, in that order. Neither needs identity auth. The glasses answer with a snapshot and then push one message per change. A push updates only the fields it carries. Fields it omits keep their last value.
The sequence below shows the subscribe and the pushes that follow it. The order of the service 100 answer and the service 28 pushes is unknown.
sequenceDiagram
participant Phone
participant Glasses
Phone->>Glasses: svc100 type 1 PeerAppInfo, empty, new channel
Phone->>Glasses: svc28 type 1000 subscribe, field 1 = 1, field 2 = interaction key
Glasses-->>Phone: svc28 type 1001 acknowledgement, about 30 ms later
Glasses-->>Phone: svc28 type 2005 full snapshot
Glasses-->>Phone: svc100 type 3 DeviceInfo
Glasses-->>Phone: svc28 type 2007 battery, on each change
Glasses-->>Phone: svc28 type 2015 mount, on each change
Phone->>Glasses: svc28 type 1000 again, on the same channel
Glasses-->>Phone: svc28 type 2005 fresh snapshot
A refresh sends the subscribe again on the same svc28 channel. The glasses answer with a new snapshot, and no extra push stream starts. A second svc28 channel would add a full set of pushes.
svc28 message types
Service 28 is smartglasses_appmanager. The table lists the message types the glasses use on it, with the field that carries each one.
| Type | Direction | Kind | Notes |
|---|---|---|---|
| 1000 | Phone to glasses | subscribe | CompanionDeviceInit, field 1 is a packed varint 1, field 2 is the interaction key |
| 1001 | Glasses to phone | acknowledgement | Two-byte protobuf |
| 2000 | Phone to glasses | start app | See the start-app section. This is a message type, not service 2000 |
| 2001 | Glasses to phone | start app response | See the start-app section |
| 2002 | Phone to glasses | stop app | Same body as start app |
| 2003 | Glasses to phone | stop app response | Same status codes |
| 2300 | Phone to glasses | SNIntent | See the capture intents section |
| 2005 | Glasses to phone | device_state |
Full snapshot, once after the subscribe |
| 2006 | Glasses to phone | device_state_update |
General update, same shape as 2005 |
| 2007 | Glasses to phone | battery |
field 1 |
| 2008 | Glasses to phone | case_battery |
field 2 |
| 2009 | Glasses to phone | camera |
field 3 |
| 2010 | Glasses to phone | capture_button |
field 4 |
| 2011 | Glasses to phone | captouch |
field 5 |
| 2012 | Glasses to phone | hinge |
field 6 |
| 2013 | Glasses to phone | hardware |
field 7 |
| 2014 | Glasses to phone | led |
field 8 |
| 2015 | Glasses to phone | mount |
field 9 |
| 2016 | Glasses to phone | power |
field 10 |
| 2017 | Glasses to phone | storage |
field 11, the storage level |
| 2018 | Glasses to phone | tampering |
field 12 |
| 2019 | Glasses to phone | thermal |
field 13 |
| 2020 | Glasses to phone | verified_session |
field 14 |
| 2021 | Glasses to phone | peak_power |
field 15 |
| 2022 | Glasses to phone | audio_active |
field 16 |
| 2023 | Glasses to phone | streaming_active |
field 17 |
| 2024 | Glasses to phone | hot_car |
field 18 |
| 2025 | Glasses to phone | bluetooth |
field 19 |
| 2026 | Glasses to phone | app_interrupt |
field 20 |
| 2027 | Glasses to phone | storage_details |
field 21, the free and total space |
| 2028 | Glasses to phone | device_lock |
field 26 |
| 2029 | Glasses to phone | registered_apps |
field 22 |
| 2032 | Glasses to phone | volume |
field 25 |
| 2033 | Glasses to phone | apps |
field 27 |
| 2034 | Glasses to phone | call |
field 28 |
| 2035 | Glasses to phone | action_button |
field 29 |
| 2037 | Glasses to phone | assistant_attention |
field 31 |
| 2039 | Glasses to phone | peak_power_throttling |
No field is known. Its content is unknown |
| 2041 | Glasses to phone | water_detect |
field 35 |
| 2042 | Glasses to phone | camera_sensing |
field 36 |
| 2043 | Glasses to phone | odc |
field 37 |
| 2044 | Glasses to phone | verified_session_info |
No field is known. Its content is unknown |
| 2030, 2031, 2036, 2040 | Glasses to phone | EMG band | The glasses do not send these types |
The mapping from kind to field is inferred from the names.
The fields present in a message decide what it updates. The type number does not choose the fields.
On Meta Ray-Ban Display the snapshot and the updates come wrapped: the body is HNDeviceState {1: DeviceState}, so it starts 0a <length>. Read as a plain DeviceState, the wrapped body gives an empty battery, which reads as 0 percent. A phone tells the two apart by the model from svc100, so it holds svc28 messages until svc100 has answered.
Device state fields
The device state keeps every field as optional. A field that a message does not carry stays empty, so a partial push does not erase a reading. The table lists each field number, its name, its content and its unit.
| Field | Name | Content | Unit or value |
|---|---|---|---|
| 1 | battery |
sub-fields: 1 health enum, 2 charge enum, 3 level, 4 temperature, 5 voltage | level in percent. Temperature in milli-degrees C and voltage in millivolts. Divide each by 1000 once |
| 2 | case_battery |
sub-fields listed in the case section | see the case section |
| 3 | camera |
enum CAMERA_STATE |
values 0 to 10, such as CAPTURE_READY (5) and CAPTURE_VIDEO_STARTED (2) |
| 4 | capture_button |
enum CAPTURE_BUTTON |
values 0 to 8, such as SHORT_PRESS_DOWN (1) and DOUBLE_PRESS (8) |
| 5 | captouch |
enum CAPTOUCH |
values 0 to 12, such as SINGLE_TAP (1) and SWIPE_FORWARD (5) |
| 6 | hinge |
enum HINGE |
UNKNOWN (0), OPEN (1), CLOSED (2) |
| 7 | hardware |
1 dsp_ok, 2 imu_ok, 3 mcu_ok, 4 microphone_ok, 5 speaker_ok, 6 captouch_ok, all booleans | none |
| 8 | led |
1 led_ok, 2 privacy_led_ok, 3 led_active, 4 privacy_led_active, all booleans | none |
| 9 | mount |
enum MOUNT_STATE |
NONE (0), DON (1, worn), DOFF (2, removed) |
| 10 | power |
1 deep_sleep_pending, 2 in_deep_sleep, both booleans | none |
| 11 | storage |
enum STORAGE_LEVEL |
UNDEFINED (0), STABLE (1), LOW (2), ZERO (3) |
| 12 | tampering |
1 is_tampered, 2 lux, 3 is_indoor | lux, as given |
| 13 | thermal |
1 health enum, 2 temperature, 3 throttling boolean, 4 throttle reason | temperature unit unknown |
| 14 | verified_session |
boolean | none |
| 15 | peak_power |
repeated: 1 app id, 2 state enum | state enum PEAK_POWER |
| 16 | audio_active |
boolean | none |
| 17 | streaming_active |
boolean | none |
| 18 | hot_car |
boolean | none |
| 19 | bluetooth |
1 ancs, 2 map, 3 pbap, 4 btc | ancs, map and pbap use the enum ANCS_STATE. btc uses BTC_STATE |
| 20 | app_interrupt |
varint | app id |
| 21 | storage_details |
1 free, 2 total | megabytes. Field 1 is the space still free, not the space used |
| 22 | registered_apps |
repeated, 1 app id | app ids |
| 25 | volume |
1 level, 2 stream enum, 3 percent | percent for field 3. Level has no known unit |
| 26 | device_lock |
enum LOCK_STATE |
NO_LOCK (0), LOCKED (1), UNLOCKED (2) |
| 27 | apps |
repeated: 1 app id, 2 state enum APP_STATE |
UNKNOWN, ACTIVE, INACTIVE, PAUSED |
| 28 | call |
1 state enum, 2 calling state enum, 3 platform enum, 4 start time | start time in milliseconds |
| 29 | action_button |
enum ACTION_BUTTON |
UNKNOWN (0), SHORT_PRESS_DOWN (1), SHORT_PRESS_UP (2), LONG_PRESS_UP (3) |
| 31 | assistant_attention |
1 attention enum | see the attention section |
| 35 | water_detect |
enum WATER_DETECT |
UNKNOWN, DETECTED, NOT_DETECTED |
| 36 | camera_sensing |
enum CAMERA_SENSING |
values 0 to 6 |
| 37 | odc |
enum ODC_STATE |
values 0 to 5 |
| 39 | overhead |
boolean | none |
| 10000 | interaction_key |
string | the subscribe's uuid, echoed on every push |
The unit of field 12 is inferred from its name.
Case battery
The glasses know the case only through the charging pins. Their last reading stays until the glasses lose it. After enough time out of the case the reading is empty: level -1, an empty serial, firmware 0.0.0 and the present flag 0. Sent as a varint, -1 is 0xFFFFFFFF, five bytes.
| Field | Name | Content |
|---|---|---|
| 1 | health |
enum BATTERY_HEALTH |
| 2 | charging |
boolean, is charging |
| 3 | level |
signed 32-bit. -1 when the case is unknown |
| 4 | serial |
string. Empty means none |
| 5 | firmware |
string, the case firmware. 0.0.0 means none |
| 6 | incompatible_charger |
boolean |
| 7 | scale |
100 for a percentage, 4 for bars. Absent means 4 |
| 8 | present |
boolean, the case is present |
| 9 | platform |
enum CASE_PLATFORM, for example 253 is COCOS |
| 10 | liquid_detected |
boolean |
The reading follows these steps:
- Read level as a signed 32-bit value. An unsigned read of -1 gives 4294967295 percent.
- Use scale 4 when field 7 is absent or zero.
- The case is known when 0 is at most the level, and the level is at most the scale.
- A percentage applies only when the case is known and the scale is 100.
Meta Ray-Ban Display sends no case in its snapshot. The case comes only as type 2008 pushes while the glasses sit in it, with field 3 the level, field 7 scale 100 and field 2 set while the case is on USB power. A case on its charger goes on charging after the glasses leave, so a level taken while charging goes out of date.
Assistant attention
Field 31 holds one enum: UNKNOWN (0), IDLE (1), WAKE (2), LISTENING (3), PROCESSING (4), RESPONDING (5) and LONG_RUNNING_SESSION_ACTIVATED (6). Value 6 is unconfirmed. It is unknown whether the glasses send it. The order of the attention values is unknown, so this page has no state diagram for them.
Start-app request
The phone starts an app on the glasses before a live stream, and for live captions on Meta Ray-Ban Display. It sends service 28 type 2000 StartApp: field 1 the app id as a varint, field 2 an optional policy, and field 3 repeated, one SNIntentData each. Type 2002 StopApp has the same body. The glasses answer on type 2001 or 2003 with field 1 as a varint status code.
| Code | Meaning |
|---|---|
| 0 | unknown |
| 1 | success |
| 2 | unknown message |
| 3 | unhandled message |
| 4 | payload corrupted |
| 5 | unsupported app |
| 6 | not enough battery |
| 7 | thermal throttling |
| 8 | not enough storage |
| 9 | start failed |
| 10 | stop failed |
| 11 | not running |
| 12 | already started |
| 13 | not enough priority |
| 14 | charger not connected |
| 15 | denied by peak power |
| 16 | power down |
A start answered 12 means the app runs, the same as 1.
SNIntentData is one key and one value: field 1 the key, then the value in the field for its type. Field 2 is a bool, 3 an integer, 4 a double and 5 a string.
Capture intents
The phone can work the camera as the capture button does. It sends service 28 type 2300 SNIntent: field 1 the action, field 2 repeated SNIntentData. The glasses answer with the camera state on svc28 field 3.
| Action | Data | Effect |
|---|---|---|
action.robusta.capture |
com.meta.smartglass.intent.action.LAUNCH_TYPE = CAPTURE_EXTRA_LAUNCH_TYPE_PHOTO, android.intent.extra.REFERRER_NAME = SHORT_BUTTON, IS_MODAL = true |
Takes a photo. Works only with the MobileConfig flag wearables_robusta.demo_enabled on. With it off, the glasses do not answer |
com.meta.smartglass.app.intent.action.AGENTIC_START_RECORDING |
capture_type = video or hyperlapse |
Starts a recording. Any other capture type falls back to video |
com.meta.smartglass.app.intent.action.CLOSE_APP_CAPTURE |
none | Stops the recording |
The flag applies at once, with no restart. See MobileConfig.
Before the phone starts an app on a Wi-Fi session, it subscribes to device state. See Live stream.
svc100: build info
Service 100 is peer_build_info. The phone sends type 1, PeerAppInfo, with an empty payload. The glasses answer on type 3 with DeviceInfo. The glasses send DeviceInfo only in answer to the empty announce, not on their own.
| Field | Content |
|---|---|
| 1 | build fingerprint string |
| 2 | serial number, <serial> |
| 4 | device uuid, <device-uuid>, 36 characters in the 8-4-4-4-12 shape |
| 5 | not identified |
| 6 | Bluetooth address, AA:BB:CC:DD:EE:FF form |
| 7 | hardware stage, seen as PVT |
| 8 | model codename, seen as greathammerhead |
| 9 | not identified |
The build fingerprint has this shape. Firmware explains each part.
Meta/<product>/<device>:<release>/<build id>/<firmware version>:user/release-keys
A sample reads:
Meta/hammerhead/greathammerhead:14/UKQ1.250303.001/70523680060800100:user/release-keys
The number before :user is the firmware version. The same number names the newer firmware table in Service catalogue.
Field 4 is needed for settings. The settings namespace is <device-uuid>@individual_device_settings. The phone cannot start a settings read until service 100 has answered. See Settings and RLDrive.
The strings use ISO-8859-1. Every byte maps to a character, so decoding cannot fail.
svc22: clock and device info
Service 22 is smartglasses_device_info. Its messages are protobuf. The phone sets the glasses' wall clock here, and nothing else sets it. The glasses stamp every capture with that clock, so a pair whose clock falls behind files new captures weeks in the past.
The phone sends type 1 DeviceInfoRequest on every connect:
| Field | Content |
|---|---|
| 1 | Timestamp: field 1 seconds, field 2 nanoseconds |
| 2 | the phone's time zone id, for example Europe/Rome |
| 3 | a request uuid, 16 bytes |
The glasses answer type 2 DeviceInfoResponse. Field 5 is the status, and absent means success. Other fields carry device info: field 3 the SKU code, fields 14 and 16 the installed language packs, field 21 the bundle serial. Not every field of the response is mapped.
svc2000: capture signals
Service 2000 is media_capture_state. The glasses open a channel for it at link setup, within about 40 ms of the end of link setup. They send a ConcurrentSignal on message type 1. Field 1 is the signal name. Field 6 is the signal type.
The first message carries the name media_capture_state and the type ready. Only this signal is confirmed on hardware. Later signals come in start and stop pairs. These names are unconfirmed on the wire:
VIDEO_STARTEDandVIDEO_STOPPEDHYPERLAPSE_STARTEDandHYPERLAPSE_STOPPEDSLOWMOTION_STARTEDandSLOWMOTION_STOPPEDDYNAMIC_PHOTO_STARTEDandDYNAMIC_PHOTO_STOPPED
A _STOPPED signal marks the end of a recording. A plain photo makes svc28 report CAPTURE_COMPLETED.
stateDiagram-v2
[*] --> Ready : ready signal, confirmed on hardware
Ready --> Recording : any *_STARTED signal
Recording --> Ready : the matching *_STOPPED signal
The diagram shows the start and stop pairs. The order between recording kinds is unknown, so the diagram has one recording state.
Reference code: Status.kt, DeviceInfo.kt, GlassesConnection.kt, LiveStream.kt
Open questions
- Whether the field-to-kind mapping for svc28 is exact is unknown. The mapping above is inferred from the names.
- No svc28 type for field 39 (
overhead) is known. - The content of types 2039 and 2044 is unknown. No field is known for them.
- The thermal temperature unit is unknown.
- The lux unit in field 12 is inferred from its name.
- Fields 5 and 9 of the svc100 answer are not identified.
- Whether value 6 of the attention enum (
LONG_RUNNING_SESSION_ACTIVATED) arrives from the glasses is unknown. - The start-app status list may not be complete.
- Whether the pushes come back on the subscribe channel is unconfirmed.
- The content of the 1001 acknowledgement beyond its size is unknown.
- The full field list of the svc22 response is unknown.
- Only the
readysignal on service 2000 is confirmed. The other signal names are unconfirmed.