Full API reference: https://wearables.developer.meta.com/llms.txt?full=true Developer docs: https://wearables.developer.meta.com/docs/develop/
The SDK is organized into three modules:
- mwdat-core: Device discovery, registration, permissions, device selectors
- mwdat-camera: StreamSession, VideoFrame, photo capture
- mwdat-mockdevice: MockDeviceKit for testing without hardware
- Use
suspendfunctions for async operations — no callbacks - Use
StateFlow/Flowfor observing state changes - Use
DatResult<T, E>for error handling — not exceptions - Prefer immutable collections
- Use
sealed interfacefor state hierarchies
The SDK uses DatResult<T, E> for type-safe error handling:
val result = Wearables.someOperation()
result.fold(
onSuccess = { value -> /* handle success */ },
onFailure = { error -> /* handle error */ }
)
// Or partial handling:
result.onSuccess { value -> /* handle success */ }
result.onFailure { error -> /* handle error */ }Do not use getOrThrow() — always handle both paths.
| Suffix | Purpose | Example |
|---|---|---|
*Manager |
Long-lived resource management | RegistrationManager |
*Session |
Short-lived flow component | StreamSession |
*Result |
DatResult type aliases | RegistrationResult |
*Error |
Error sealed interfaces | WearablesError |
Methods: get*, set*, check*, request*, observe*
import com.meta.wearable.dat.core.Wearables // Entry point
import com.meta.wearable.dat.camera.StreamSession // Camera streaming
import com.meta.wearable.dat.camera.types.* // VideoFrame, PhotoData, etc.For testing:
import com.meta.wearable.dat.mockdevice.MockDeviceKit // MockDeviceKitWearables— SDK entry point. CallWearables.initialize(context)at startupStreamSession— Camera streaming sessionVideoFrame— Individual video frame with bitmap dataAutoDeviceSelector— Auto-selects the best available deviceSpecificDeviceSelector— Selects a specific device by identifierStreamConfiguration— Configure video quality, frame rateMockDeviceKit— Factory for creating simulated devices in tests
Guide for setting up the Meta Wearables Device Access Toolkit in an Android app.
- Android Studio, minSdk 26+
- Meta AI companion app installed on test device
- Ray-Ban Meta glasses or Meta Ray-Ban Display glasses (or use MockDeviceKit for development)
- Developer Mode enabled in Meta AI app (Settings > Your glasses > Developer Mode)
- GitHub personal access token with
read:packagesscope
In settings.gradle.kts:
val localProperties =
Properties().apply {
val localPropertiesPath = rootDir.toPath() / "local.properties"
if (localPropertiesPath.exists()) {
load(localPropertiesPath.inputStream())
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/facebook/meta-wearables-dat-android")
credentials {
username = ""
password = System.getenv("GITHUB_TOKEN") ?: localProperties.getProperty("github_token")
}
}
}
}In libs.versions.toml:
[versions]
mwdat = "0.5.0"
[libraries]
mwdat-core = { group = "com.meta.wearable", name = "mwdat-core", version.ref = "mwdat" }
mwdat-camera = { group = "com.meta.wearable", name = "mwdat-camera", version.ref = "mwdat" }
mwdat-mockdevice = { group = "com.meta.wearable", name = "mwdat-mockdevice", version.ref = "mwdat" }In app/build.gradle.kts:
dependencies {
implementation(libs.mwdat.core)
implementation(libs.mwdat.camera)
implementation(libs.mwdat.mockdevice)
}<manifest ...>
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.INTERNET" />
<application ...>
<!-- Use 0 in Developer Mode; production apps get ID from Wearables Developer Center -->
<meta-data
android:name="com.meta.wearable.mwdat.APPLICATION_ID"
android:value="0" />
<activity android:name=".MainActivity" ...>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myexampleapp" />
</intent-filter>
</activity>
</application>
</manifest>Replace myexampleapp with your app's URL scheme.
import com.meta.wearable.dat.core.Wearables
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Wearables.initialize(this)
}
}Calling SDK APIs before initialization yields WearablesError.NOT_INITIALIZED.
fun startRegistration(context: Context) {
Wearables.startRegistration(context)
}Observe registration state:
lifecycleScope.launch {
Wearables.registrationState.collect { state ->
// Update UI based on registration state
}
}import com.meta.wearable.dat.camera.StreamSession
import com.meta.wearable.dat.camera.types.StreamConfiguration
import com.meta.wearable.dat.camera.types.VideoQuality
import com.meta.wearable.dat.core.selectors.AutoDeviceSelector
val session = Wearables.startStreamSession(
context = context,
deviceSelector = AutoDeviceSelector(),
streamConfiguration = StreamConfiguration(
videoQuality = VideoQuality.MEDIUM,
frameRate = 24,
),
)
lifecycleScope.launch {
session.videoStream.collect { frame ->
// Display frame
}
}
lifecycleScope.launch {
session.state.collect { state ->
// Update UI based on stream state
}
}- Camera Streaming — Resolution, frame rate, photo capture
- MockDevice Testing — Test without hardware
- Session Lifecycle — Handle pause/resume/stop
- Permissions — Camera permission flows
- Full documentation
Guide for testing DAT SDK integrations without physical Meta glasses.
MockDeviceKit simulates Meta glasses behavior for development and testing. It provides:
MockDeviceKit— Entry point for creating simulated devicesMockRaybanMeta— Simulated Ray-Ban Meta glassesMockCameraKit— Simulated camera with configurable video feed and photo capture
Add mwdat-mockdevice to your Gradle dependencies:
dependencies {
implementation(libs.mwdat.mockdevice)
}import com.meta.wearable.dat.mockdevice.MockDeviceKit
import com.meta.wearable.dat.mockdevice.api.MockDeviceKitConfig
val mockDeviceKit = MockDeviceKit.getInstance(context)
// Attach fake registration and connectivity (auto-initializes Wearables if needed).
// By default, Wearables.registrationState transitions to Registered.
mockDeviceKit.enable()
// Or start in unregistered state to test registration flows:
// mockDeviceKit.enable(MockDeviceKitConfig(initiallyRegistered = false))
val device = mockDeviceKit.pairRaybanMeta()You can check mockDeviceKit.isEnabled to query whether the mock environment is active.
// Simulate glasses lifecycle
device.powerOn()
device.unfold()
device.don() // Simulate wearing the glasses
// Later...
device.doff() // Simulate removing
device.fold()
device.powerOff()val camera = device.getCameraKit()
camera.setCameraFeed(videoUri)val camera = device.getCameraKit()
camera.setCapturedImage(imageUri)Note: Android doesn't transcode video automatically. Mock video files must be in h.265 format. Use FFmpeg to convert:
ffmpeg -hwaccel videotoolbox -i input.mp4 -c:v hevc_videotoolbox -c:a aac_at -tag:v hvc1 -vf "scale=540:960" output.movCreate a reusable test base class:
import android.content.Context
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.platform.app.InstrumentationRegistry
import com.meta.wearable.dat.mockdevice.MockDeviceKit
import com.meta.wearable.dat.mockdevice.api.MockDeviceKitInterface
import org.junit.After
import org.junit.Before
import org.junit.Rule
open class MockDeviceKitTestCase<T : Any>(
private val activityClass: Class<T>
) {
@get:Rule
val scenarioRule = ActivityScenarioRule(activityClass)
protected lateinit var mockDeviceKit: MockDeviceKitInterface
protected lateinit var targetContext: Context
@Before
open fun setUp() {
val instrumentation = InstrumentationRegistry.getInstrumentation()
targetContext = instrumentation.targetContext
mockDeviceKit = MockDeviceKit.getInstance(targetContext)
grantRuntimePermissions()
}
@After
open fun tearDown() {
mockDeviceKit.disable()
}
private fun grantRuntimePermissions() {
val packageName = targetContext.packageName
val shell = InstrumentationRegistry.getInstrumentation().uiAutomation
shell.executeShellCommand("pm grant $packageName android.permission.BLUETOOTH_CONNECT")
shell.executeShellCommand("pm grant $packageName android.permission.CAMERA")
}
}The CameraAccess sample app includes a Debug menu for MockDeviceKit:
- Tap the Debug icon to open the MockDeviceKit menu
- Tap Pair RayBan Meta to create a simulated device
- Use PowerOn, Unfold, Don to simulate glasses states
- Select video/image files for mock camera feeds
- Start streaming to see simulated frames
| Type | Formats |
|---|---|
| Video | h.264 (AVC), h.265 (HEVC) |
| Image | JPEG, PNG |
Guide for implementing camera streaming and photo capture with the DAT SDK.
- StreamSession: Main interface for camera streaming
- VideoFrame: Individual video frames from the stream
- StreamConfiguration: Configure resolution, frame rate
- PhotoData: Still image captured from glasses
import com.meta.wearable.dat.camera.types.StreamConfiguration
import com.meta.wearable.dat.camera.types.VideoQuality
import com.meta.wearable.dat.core.selectors.AutoDeviceSelector
val session = Wearables.startStreamSession(
context = context,
deviceSelector = AutoDeviceSelector(),
streamConfiguration = StreamConfiguration(
videoQuality = VideoQuality.MEDIUM, // 504x896
frameRate = 24,
),
)| Quality | Size |
|---|---|
VideoQuality.HIGH |
720 x 1280 |
VideoQuality.MEDIUM |
504 x 896 |
VideoQuality.LOW |
360 x 640 |
Valid values: 2, 7, 15, 24, 30 FPS.
Lower resolution and frame rate yield higher visual quality due to less Bluetooth compression.
StreamSessionState transitions: STARTING -> STARTED -> STREAMING -> STOPPING -> STOPPED -> CLOSED
lifecycleScope.launch {
session.state.collect { state ->
when (state) {
StreamSessionState.STREAMING -> {
// Stream is active, frames flowing
}
StreamSessionState.STOPPED -> {
// Stream ended, release resources
}
StreamSessionState.CLOSED -> {
// Session fully closed
}
else -> { /* handle other states */ }
}
}
}lifecycleScope.launch {
session.videoStream.collect { frame ->
// Display frame bitmap
updatePreview(frame)
}
}session.capturePhoto()
.onSuccess { photoData ->
// Handle captured photo data
val imageBytes = photoData.data
}
.onFailure { error ->
// Handle capture error
}Resolution and frame rate are constrained by Bluetooth Classic bandwidth. The SDK automatically reduces quality when bandwidth is limited:
- First lowers resolution (e.g., HIGH -> MEDIUM)
- Then reduces frame rate (e.g., 30 -> 24), never below 15 FPS
Request lower settings for higher visual quality per frame.
// Auto-select best available device
val auto = AutoDeviceSelector()
// Select specific device
val specific = SpecificDeviceSelector(deviceIdentifier = deviceId)Guide for managing device session states in DAT SDK integrations.
The DAT SDK runs work inside sessions. Meta glasses expose two experience types:
- Device sessions — sustained access to device sensors and outputs
- Transactions — short, system-owned interactions (notifications, "Hey Meta")
Your app observes session state changes — the device decides when to transition.
| State | Meaning | App action |
|---|---|---|
STOPPED |
Session inactive, not reconnecting | Free resources, wait for user action |
RUNNING |
Session active, streaming data | Perform live work |
PAUSED |
Temporarily suspended | Hold work, may resume |
lifecycleScope.launch {
Wearables.getDeviceSessionState(deviceId).collect { state ->
when (state) {
SessionState.RUNNING -> onRunning()
SessionState.PAUSED -> onPaused()
SessionState.STOPPED -> onStopped()
}
}
}STARTING -> STARTED -> STREAMING -> STOPPING -> STOPPED -> CLOSED
lifecycleScope.launch {
session.state.collect { state ->
// React to state changes
}
}The device changes session state when:
- User performs a system gesture that opens another experience
- Another app starts a device session
- User removes or folds the glasses (Bluetooth disconnects)
- User removes the app from Meta AI companion app
- Connectivity between companion app and glasses drops
When a session is paused:
- The device keeps the connection alive
- Streams stop delivering data
- The device may resume by returning to
RUNNING
Your app should not attempt to restart while paused — wait for RUNNING or STOPPED.
lifecycleScope.launch {
Wearables.devices.collect { devices ->
// Update list of available glasses
}
}Key behaviors:
- Closing hinges disconnects Bluetooth -> forces
STOPPED - Opening hinges restores Bluetooth but does not restart sessions
- Start a new session after the device becomes available again
- Handle all session states (
RUNNING,PAUSED,STOPPED) - Monitor device availability before starting work
- Release resources only after
STOPPED - Don't infer transition causes — rely only on observable state
- Don't restart during
PAUSED— wait for system to resume or stop
Guide for app registration and camera permission flows in the DAT SDK.
The DAT SDK separates two concepts:
- Registration — Your app registers with Meta AI to become a permitted integration
- Device permissions — After registration, request specific device permissions (e.g., camera)
All permission grants occur through the Meta AI companion app.
Wearables.startRegistration(context)This opens the Meta AI app where the user approves your app.
lifecycleScope.launch {
Wearables.registrationState.collect { state ->
when (state) {
is RegistrationState.Registered -> {
// App is registered, can request permissions
}
is RegistrationState.Unregistered -> {
// App is not registered
}
}
}
}Wearables.startUnregistration(context)val status = Wearables.checkPermissionStatus(Permission.CAMERA)
if (status == PermissionStatus.Granted) {
// Start streaming
}Use the SDK's RequestPermissionContract with the Activity Result API:
private var permissionContinuation: CancellableContinuation<PermissionStatus>? = null
private val permissionMutex = Mutex()
private val permissionsResultLauncher =
registerForActivityResult(Wearables.RequestPermissionContract()) { result ->
permissionContinuation?.resume(result)
permissionContinuation = null
}
suspend fun requestWearablesPermission(permission: Permission): PermissionStatus {
return permissionMutex.withLock {
suspendCancellableCoroutine { continuation ->
permissionContinuation = continuation
continuation.invokeOnCancellation { permissionContinuation = null }
permissionsResultLauncher.launch(permission)
}
}
}Users can choose:
- Allow once — temporary, single-session grant
- Allow always — persistent grant
Users can link multiple glasses to Meta AI. The SDK handles this transparently:
- Permission granted on any linked device means your app has access
- You don't need to track which device has permissions
- If all devices disconnect, permissions become unavailable
| Mode | Registration behavior |
|---|---|
| Developer Mode | Registration always allowed (use APPLICATION_ID = 0) |
| Production | Users must be in proper release channel |
For production, get your APPLICATION_ID from the Wearables Developer Center.
- Registration requires an internet connection
- Meta AI companion app must be installed
- For Developer Mode: enable in Meta AI > Settings > Your glasses > Developer Mode
Guide for diagnosing common issues with DAT SDK integrations.
Device not connecting?
|
+-- Is Developer Mode enabled? -> Enable in Meta AI app settings
|
+-- Is device registered? -> Check registrationState
|
+-- Is device in range? -> Bluetooth on, glasses powered on
|
+-- Did you call initialize()? -> Must call Wearables.initialize(context) first
|
+-- Stream not receiving frames? -> Check device connection state
Developer Mode must be enabled for 3P apps to access device features.
- Open Meta AI app on phone
- Go to Settings -> (Your connected glasses)
- Find "Developer Mode" toggle
- Toggle ON
- Device may restart
- Registration completes but device never connects
- StreamSession stuck without streaming
- Permission requests fail or never appear
- Developer Mode toggles off after firmware updates — re-enable it
- Developer Mode is per-device — enable for each glasses pair
- Some features need additional permissions beyond Developer Mode
STARTING -> STARTED -> STREAMING -> STOPPING -> STOPPED -> CLOSED
- Check that
Wearables.initialize(context)was called - Verify device is connected and in range
- Ensure camera permission was granted
- Check that the device selector matches an available device
- Device disconnected (out of range, battery died)
- Channel closed by device
- Error in frame processing
Ensure compatible versions of SDK, Meta AI app, and glasses firmware:
| SDK | Meta AI App | Ray-Ban Meta | Meta Ray-Ban Display |
|---|---|---|---|
| 0.5.0 | Check version dependencies | Check docs | Check docs |
| 0.4.0 | V254 | V20 | V21 |
| 0.3.0 | V249 | V20 | — |
| Issue | Workaround |
|---|---|
| No internet -> registration fails | Internet required for registration |
| Streams started with glasses doffed pause when donned | Unpause by tapping side of glasses |
DeviceSession unreliable with camera stream |
Avoid using DeviceSession |
import android.util.Log
private const val TAG = "DATWearables"
// In your streaming code:
Log.d(TAG, "Stream state changed to: $state")
Log.e(TAG, "Stream error", exception)-
Wearables.initialize(context)called before any API use - Developer Mode enabled in Meta AI app
- Meta AI app updated to compatible version
- Glasses firmware updated to compatible version
- Internet connection available for registration
- Bluetooth permissions granted (
BLUETOOTH_CONNECT) - Correct URL scheme in AndroidManifest.xml intent filter
-
APPLICATION_IDmeta-data set in manifest
Guide for building a complete DAT SDK app with camera streaming and photo capture.
This guide walks through building an Android app that connects to Meta glasses, streams video, and captures photos. Use it as a reference alongside the CameraAccess sample.
- Create a new Android Studio project (Compose Activity)
- Add the Maven repository in
settings.gradle.kts - Add
mwdat-core,mwdat-camera,mwdat-mockdevicedependencies - Configure
AndroidManifest.xml(see Getting Started)
A typical DAT app has these components:
app/src/main/java/com/example/myapp/
├── MyApplication.kt # Application class, SDK init
├── MainActivity.kt # Registration, permission handling
├── stream/
│ └── StreamViewModel.kt # Streaming, photo capture
└── ui/
├── RegistrationScreen.kt # Registration UI
└── StreamScreen.kt # Video preview, capture
import com.meta.wearable.dat.core.Wearables
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Wearables.initialize(this)
}
}import com.meta.wearable.dat.camera.types.StreamConfiguration
import com.meta.wearable.dat.camera.types.StreamSessionState
import com.meta.wearable.dat.camera.types.VideoQuality
import com.meta.wearable.dat.core.Wearables
import com.meta.wearable.dat.core.selectors.AutoDeviceSelector
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
class StreamViewModel : ViewModel() {
private val _streamState = MutableStateFlow<StreamSessionState?>(null)
val streamState = _streamState.asStateFlow()
private var session: StreamSession? = null
fun startStream(context: Context) {
val streamSession = Wearables.startStreamSession(
context = context,
deviceSelector = AutoDeviceSelector(),
streamConfiguration = StreamConfiguration(
videoQuality = VideoQuality.MEDIUM,
frameRate = 24,
),
)
session = streamSession
viewModelScope.launch {
streamSession.state.collect { state ->
_streamState.value = state
}
}
viewModelScope.launch {
streamSession.videoStream.collect { frame ->
// Update UI with frame
}
}
}
fun stopStream() {
session?.stop()
session = null
}
fun capturePhoto() {
session?.capturePhoto()
?.onSuccess { photoData ->
// Handle photo
}
?.onFailure { error ->
// Handle error
}
}
}class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
Wearables.registrationState.collect { state ->
// Update registration UI
}
}
lifecycleScope.launch {
Wearables.devices.collect { devices ->
// Update device list
}
}
}
fun register() {
Wearables.startRegistration(this)
}
fun unregister() {
Wearables.startUnregistration(this)
}
}import com.meta.wearable.dat.mockdevice.MockDeviceKit
import com.meta.wearable.dat.mockdevice.api.MockDeviceKitConfig
fun setupMockDevice(context: Context) {
val mockDeviceKit = MockDeviceKit.getInstance(context)
// Attach fake implementations (auto-initializes Wearables if needed).
// Starts Registered by default. Pass MockDeviceKitConfig(initiallyRegistered = false)
// to start in unregistered state for testing registration flows.
mockDeviceKit.enable()
val device = mockDeviceKit.pairRaybanMeta()
device.powerOn()
device.unfold()
device.don()
// Set up mock camera feed
val camera = device.getCameraKit()
camera.setCameraFeed(videoUri)
}
fun tearDownMockDevice(context: Context) {
val mockDeviceKit = MockDeviceKit.getInstance(context)
// Unpairs all mock devices, clears pairedDevices, restores real stack
mockDeviceKit.disable()
}Your DAT app should only depend on:
mwdat-core— always requiredmwdat-camera— for camera streamingmwdat-mockdevice— for testing