The head tracker human interface device (HID) protocol, available for devices running Android 13 and higher, allows for a head-tracking device to be connected to an Android device through USB or Bluetooth and be exposed to the Android framework and apps through the sensors framework. This protocol is used for controlling an audio virtualizer effect (3D audio). This page uses the terms device and host in their Bluetooth sense, where device means the head-tracking device and host means the Android host.
Device manufacturers must configure their Android devices to enable support for the head tracker HID protocol. For more detailed information about configuration, see the Dynamic Sensors README.
This page assumes familiarity with the following resources:
Top-level structure
The Android framework identifies the head tracker device as a HID device.
For a complete example of a valid HID descriptor, see Appendix 1: Example of a HID Descriptor.
At the top level, the head tracker device is an app collection with the
Sensors
page (0x20
) and the Other: Custom
usage (0xE1
). Inside this
collection are several data fields (inputs) and properties (features).
Properties and data fields
This section describes the properties and data fields in an application collection of a head tracker device.
Property: Sensor Description (0x0308
)
The Sensor Description (0x0308
) property is a read-only ASCII (8-bit) string
property that must contain the following values:
Head tracker version 1.0:
#AndroidHeadTracker#1.0
Head tracker version 2.0 (available in Android 15 or higher), which includes support for LE audio:
#AndroidHeadTracker#2.0#x
The x
is an integer (1
, 2
, 3
) indicating the support transport:
- 1: ACL
- 2: ISO
- 3: ACL + ISO
No null-terminator is expected, meaning that the total size of this property is 23 8-bit characters for version 1.0.
This property serves as a discriminator to avoid collisions with other custom sensors.
Property: Persistent Unique ID (0x0302
)
The Persistent Unique ID (0x0302
) property is a read-only array of 16
elements, 8 bit each (total 128 bit). No null terminator is expected. This
property is optional.
This property allows head-tracking devices that are integrated in audio devices to reference the audio device that they're attached to. The following schemes are supported.
Standalone head tracker
If the Persistent Unique ID (0x0302
) property doesn't exist or is set to all
zeros, it means that the head tracker device isn't permanently attached to an
audio device and can be used separately, for example, by letting the user
manually associate the head tracker device with a separate audio device.
Reference using Bluetooth MAC address
Octet | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
Value | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | B | T | Bluetooth MAC |
In this scheme, the first 8 octets must be 0
, octets 8 and 9 must contain
the ASCII values B
and T
respectively, and the following 6 octets are
interpreted as a Bluetooth MAC address, assuming the head tracker device
applies to any audio device having this MAC address. This address must be the
identity address, even if the device uses a random MAC address to establish
connections. Dual-mode devices connecting over Bluetooth classic
(v1.0 HID format) and Bluetooth LE (v2.0 HID format) must expose two HID
descriptors with the same identity address. Dual-mode devices with separated
left and right devices must expose Bluetooth LE HID using the primary dual
mode device instead of the LE-only secondary device.
Reference using UUID
Whenever the most significant bit (MSB) of octet 8 is set (≥0x80
), the field
is interpreted as a UUID, as specified in
RFC-4122. The
corresponding audio device provides the same UUID, which is registered on
the Android framework, through an unspecified mechanism that is specific to the
type of transport used.
Property: Reporting State (0x0316
)
The Reporting State (0x0316
) property is a read/write property that has the
standard semantics as defined in the HID specification. The host uses this
property to indicate to the device which events to report. Only the values No
Events (0x0840
) and All Events (0x0841
) are used.
The initial value for this field must be No Events and it must never be modified by the device, only by the host.
Property: Power State (0x0319
)
The Power State (0x0319
) property is a read/write property that has the
standard semantics as defined in the HID specification. The host uses this
property to indicate to the device which power state it must be in. Only the
values Full Power (0x0851
) and Power Off (0x0855
) are used.
The initial value for this field is determined by the device and must never be modified by the device, only by the host.
Property: Report Interval (0x030E
)
The Report Interval (0x030E
) property is a read/write property that has the
standard semantics as defined in the HID specification. The host uses this
property to indicate to the device how often to report its data readings.
Units are seconds. The valid range for this value is determined by the device
and described using the Physical Min/Max mechanism. At least 50 Hz
reporting rate must be supported, and the recommended maximum reporting rate is
100 Hz. Therefore, the minimum report interval must be less than or equal
to 20 ms, and is recommended to be greater than or equal to 10 ms.
Property: Vendor-reserved LE Transport (0xF410
)
The Vendor-reserved LE Transport (0xF410
) property is a read/write property
that has the standard semantics as defined in the HID specification. The host
uses this property to indicate the selected transport (ACL or ISO). Only the
values ACL (0xF800
) and ISO (0xF801
) are used, and both must be included
in the logical collection.
This property is configured prior to the power or reporting states.
Data field: Custom Value 1 (0x0544
)
The Custom Value 1 (0x0544
) field is an input field used for reporting the
actual head-tracking information. It's a 3-element array, interpreted according
to the normal HID rules for physical values as specified in section 6.2.2.7 of
the HID specification. The valid range for each element is [-π, π] rad. Units
are always radians.
The elements are interpreted as: [rx, ry, rz]
, such that [rx, ry, rz]
is a
rotation vector,
representing the transform from the reference frame to the head frame.
Magnitude must be in the [0..π] range.
The reference frame is arbitrary, but it's generally fixed and must be right handed. A small amount of drift is acceptable. The head axes are:
- X from left ear to right
- Y from the back of the head to the nose (back to front)
- Z from the neck to the top of the head
Data field: Custom Value 2 (0x0545
)
The Custom Value 2 (0x0545
) field is an input field used for reporting the
actual head-tracking information. It's a 3-element fixed-point array,
interpreted according to the normal HID rules for physical values.
Units are always radians/second.
The elements are interpreted as: [vx, vy, vz]
, such that [vx, vy, vz]
is a
rotation vector,
representing the angular velocity of the head frame (relative to itself).
Data field: Custom Value 3 (0x0546
)
The Custom Value 3 (0x0546
) field is an input field used for tracking
discontinuities in the reference frame. It's a scalar integer 8 bit in
size. It must be incremented (with wraparound) by the device every time the
frame of reference is changed, for example, if an orientation filter algorithm
used for determining the orientation had its state reset. This value is
interpreted according to the normal HID rules for physical values. However,
the physical value and units don’t matter. The only information relevant to the
host is a changed value. To avoid numeric issues related to loss of precision
while converting from logical to physical units, it's recommended to set the
values for physical min, physical max, and unit exponent to zero for this field.
Report structure
The grouping of properties into reports (by assignment of report IDs) is flexible. For efficiency, we recommend separating the read-only properties from the read/write properties.
For the data fields, the Custom Value 1, 2, and 3 fields must be in the same report and be in only one report for a given device (app collection).
Send input reports
The device must periodically and asynchronously (through HID INPUT messages) send input reports when all these conditions are met:
- The Power State property is set to Full Power.
- The Reporting State property is set to All Events.
- The Reporting Interval property is non-zero.
The Reporting Interval property determines how often to send the reports. When any of the conditions above isn't met, the device must not send any reports.
Forward and backward compatibility
The head tracker HID protocol uses a versioning scheme that allows for updates, while allowing interoperability between a host and a device that use different versions of the protocol. Versions for the protocol are identified by two numbers, major and minor, which have distinct semantics as described in the following sections.
The versions supported by a device can be determined by examining
its Sensor Description (0x0308
) property.
Minor version compatibility
Changes to the minor version are backward compatible with earlier minor versions that are based on the same major version. In updates to the minor version, the host ignores additional data fields and properties. For example, a device using protocol version 1.6 is compatible with a host that supports protocol version 1.x, including version 1.5.
Major version compatibility
Non-backward-compatible changes are allowed for changes to major versions. To support multiple major versions for interoperability with old and new hosts, devices can specify multiple app collections in their report descriptors. For example:
const unsigned char ReportDescriptor[] = {
HID_USAGE_PAGE_SENSOR,
HID_USAGE_SENSOR_TYPE_OTHER_CUSTOM,
HID_COLLECTION(HID_APPLICATION),
// Feature report 2 (read-only).
HID_REPORT_ID(2),
// Magic value: "#AndroidHeadTracker#1.5"
HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
HID_LOGICAL_MIN_8(0),
HID_LOGICAL_MAX_8(0xFF),
HID_REPORT_SIZE(8),
HID_REPORT_COUNT(23),
HID_FEATURE(HID_CONST_VAR_ABS),
...
HID_END_COLLECTION,
HID_COLLECTION(HID_APPLICATION),
// Feature report 12 (read-only).
HID_REPORT_ID(12),
// Magic value: "#AndroidHeadTracker#2.4"
HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
HID_LOGICAL_MIN_8(0),
HID_LOGICAL_MAX_8(0xFF),
HID_REPORT_SIZE(8),
HID_REPORT_COUNT(23),
HID_FEATURE(HID_CONST_VAR_ABS),
...
HID_END_COLLECTION,
};
In this case, the host can enumerate all the different app collections advertised by the device, examining their Sensor Description property to determine the protocol versions that they each implement, then picking the latest protocol version that the host supports. When chosen, the host works with the single protocol that was chosen for the lifetime of the device connection.
Appendix: Example of a HID descriptor
The following example illustrates a typical valid HID descriptor. It uses the commonly used C macros, provided in HID Sensor Usages (section 4.1).
const unsigned char ReportDescriptor[] = {
HID_USAGE_PAGE_SENSOR,
HID_USAGE_SENSOR_TYPE_OTHER_CUSTOM,
HID_COLLECTION(HID_APPLICATION),
// Feature report 2 (read-only).
HID_REPORT_ID(2),
// Magic value: "#AndroidHeadTracker#1.0"
HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
HID_LOGICAL_MIN_8(0),
HID_LOGICAL_MAX_8(0xFF),
HID_REPORT_SIZE(8),
HID_REPORT_COUNT(23),
HID_FEATURE(HID_CONST_VAR_ABS),
// UUID.
HID_USAGE_SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID,
HID_LOGICAL_MIN_8(0),
HID_LOGICAL_MAX_8(0xFF),
HID_REPORT_SIZE(8),
HID_REPORT_COUNT(16),
HID_FEATURE(HID_CONST_VAR_ABS),
// Feature report 1 (read/write).
HID_REPORT_ID(1),
// 1-bit on/off reporting state.
HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE,
HID_LOGICAL_MIN_8(0),
HID_LOGICAL_MAX_8(1),
HID_REPORT_SIZE(1),
HID_REPORT_COUNT(1),
HID_COLLECTION(HID_LOGICAL),
HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_NO_EVENTS,
HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_ALL_EVENTS,
HID_FEATURE(HID_DATA_ARR_ABS),
HID_END_COLLECTION,
// 1-bit on/off power state.
HID_USAGE_SENSOR_PROPERTY_POWER_STATE,
HID_LOGICAL_MIN_8(0),
HID_LOGICAL_MAX_8(1),
HID_REPORT_SIZE(1),
HID_REPORT_COUNT(1),
HID_COLLECTION(HID_LOGICAL),
HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D4_POWER_OFF,
HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D0_FULL_POWER,
HID_FEATURE(HID_DATA_ARR_ABS),
HID_END_COLLECTION,
// 6-bit reporting interval, with values [0x00..0x3F] corresponding to [10ms..100ms].
HID_USAGE_SENSOR_PROPERTY_REPORT_INTERVAL,
HID_LOGICAL_MIN_8(0x00),
HID_LOGICAL_MAX_8(0x3F),
HID_PHYSICAL_MIN_8(10),
HID_PHYSICAL_MAX_8(100),
HID_REPORT_SIZE(6),
HID_REPORT_COUNT(1),
HID_USAGE_SENSOR_UNITS_SECOND,
HID_UNIT_EXPONENT(0xD), // 10^-3
HID_FEATURE(HID_DATA_VAR_ABS),
// Input report 1
// Orientation as rotation vector (scaled to [-pi..pi] rad).
HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_1,
HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
HID_PHYSICAL_MIN_32(0x60, 0x4F, 0x46, 0xED), // -314159265
HID_PHYSICAL_MAX_32(0xA1, 0xB0, 0xB9, 0x12), // 314159265
HID_UNIT_EXPONENT(0x08), // 10^-8
HID_REPORT_SIZE(16),
HID_REPORT_COUNT(3),
HID_INPUT(HID_DATA_VAR_ABS),
// Angular velocity as rotation vector (scaled to [-32..32] rad/sec).
HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_2,
HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
HID_PHYSICAL_MIN_8(0xE0),
HID_PHYSICAL_MAX_8(0x20),
HID_UNIT_EXPONENT(0x00), // 10^0
HID_REPORT_SIZE(16),
HID_REPORT_COUNT(3),
HID_INPUT(HID_DATA_VAR_ABS),
// Reference frame reset counter.
HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_3,
HID_LOGICAL_MIN_16(0x00, 0x00), // LOGICAL_MINIMUM (0)
HID_LOGICAL_MAX_16(0xFF, 0x00), // LOGICAL_MAXIMUM (255)
HID_PHYSICAL_MIN_8(0x00),
HID_PHYSICAL_MAX_8(0x00),
HID_UNIT_EXPONENT(0x00), // 10^0
HID_REPORT_SIZE(8),
HID_REPORT_COUNT(1),
HID_INPUT(HID_DATA_VAR_ABS),
HID_END_COLLECTION,
};
Appendix 2: Example of a v2.0 HID descriptor
The following example illustrates a v2.0 HID descriptor for a device supporting only the Bluetooth LE ACL transport.
const unsigned char ReportDescriptor[] = {
HID_USAGE_PAGE_SENSOR,
HID_USAGE_SENSOR_TYPE_OTHER_CUSTOM,
HID_COLLECTION(HID_APPLICATION),
// Feature report 2 (read-only).
HID_REPORT_ID(2),
// Magic value: "#AndroidHeadTracker#2.0#1"
HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
HID_LOGICAL_MIN_8(0),
HID_LOGICAL_MAX_8(0xFF),
HID_REPORT_SIZE(8),
HID_REPORT_COUNT(25),
HID_FEATURE(HID_CONST_VAR_ABS),
// UUID.
HID_USAGE_SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID,
HID_LOGICAL_MIN_8(0),
HID_LOGICAL_MAX_8(0xFF),
HID_REPORT_SIZE(8),
HID_REPORT_COUNT(16),
HID_FEATURE(HID_CONST_VAR_ABS),
// Feature report 1 (read/write).
HID_REPORT_ID(1),
// 1-bit on/off reporting state.
HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE,
HID_LOGICAL_MIN_8(0),
HID_LOGICAL_MAX_8(1),
HID_REPORT_SIZE(1),
HID_REPORT_COUNT(1),
HID_COLLECTION(HID_LOGICAL),
HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_NO_EVENTS,
HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_ALL_EVENTS,
HID_FEATURE(HID_DATA_ARR_ABS),
HID_END_COLLECTION,
// 1-bit on/off power state.
HID_USAGE_SENSOR_PROPERTY_POWER_STATE,
HID_LOGICAL_MIN_8(0),
HID_LOGICAL_MAX_8(1),
HID_REPORT_SIZE(1),
HID_REPORT_COUNT(1),
HID_COLLECTION(HID_LOGICAL),
HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D4_POWER_OFF,
HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D0_FULL_POWER,
HID_FEATURE(HID_DATA_ARR_ABS),
HID_END_COLLECTION,
// 6-bit reporting interval, with values [0x00..0x3F] corresponding to [10ms..100ms].
HID_USAGE_SENSOR_PROPERTY_REPORT_INTERVAL,
HID_LOGICAL_MIN_8(0x00),
HID_LOGICAL_MAX_8(0x3F),
HID_PHYSICAL_MIN_8(10),
HID_PHYSICAL_MAX_8(100),
HID_REPORT_SIZE(6),
HID_REPORT_COUNT(1),
HID_USAGE_SENSOR_UNITS_SECOND,
HID_UNIT_EXPONENT(0xD), // 10^-3
HID_FEATURE(HID_DATA_VAR_ABS),
// 1-bit transport selection
HID_USAGE_SENSOR_PROPERTY_VENDOR_LE_TRANSPORT,
HID_LOGICAL_MIN_8(0),
HID_LOGICAL_MAX_8(1),
HID_REPORT_SIZE(1),
HID_REPORT_COUNT(1),
HID_COLLECTION(HID_LOGICAL),
HID_USAGE_SENSOR_PROPERTY_VENDOR_LE_TRANSPORT_ACL,
HID_USAGE_SENSOR_PROPERTY_VENDOR_LE_TRANSPORT_ISO,
HID_FEATURE(HID_DATA_ARR_ABS),
HID_END_COLLECTION,
// Input report 1
// Orientation as rotation vector (scaled to [-pi..pi] rad).
HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_1,
HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
HID_PHYSICAL_MIN_32(0x60, 0x4F, 0x46, 0xED), // -314159265
HID_PHYSICAL_MAX_32(0xA1, 0xB0, 0xB9, 0x12), // 314159265
HID_UNIT_EXPONENT(0x08), // 10^-8
HID_REPORT_SIZE(16),
HID_REPORT_COUNT(3),
HID_INPUT(HID_DATA_VAR_ABS),
// Angular velocity as rotation vector (scaled to [-32..32] rad/sec).
HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_2,
HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
HID_PHYSICAL_MIN_8(0xE0),
HID_PHYSICAL_MAX_8(0x20),
HID_UNIT_EXPONENT(0x00), // 10^0
HID_REPORT_SIZE(16),
HID_REPORT_COUNT(3),
HID_INPUT(HID_DATA_VAR_ABS),
// Reference frame reset counter.
HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_3,
HID_LOGICAL_MIN_16(0x00, 0x00), // LOGICAL_MINIMUM (0)
HID_LOGICAL_MAX_16(0xFF, 0x00), // LOGICAL_MAXIMUM (255)
HID_PHYSICAL_MIN_8(0x00),
HID_PHYSICAL_MAX_8(0x00),
HID_UNIT_EXPONENT(0x00), // 10^0
HID_REPORT_SIZE(8),
HID_REPORT_COUNT(1),
HID_INPUT(HID_DATA_VAR_ABS),
HID_END_COLLECTION,
};