Guides

From simulator to real glasses

The browser simulator validates your protocol, tool wiring, and audio bytes; real Ray-Ban Meta glasses are the final gate for audio character, Bluetooth timing, and hardware failure modes. This guide walks the full transition — getting a Meta production identity (App ID, Client Token, package + SHA-256 registration), storing credentials in your dashboard so generateConnectionModule bakes them into the build, resolving the Meta DAT SDK from GitHub Packages with a read:packages token, flipping the transport off the simulator, pairing glasses to an Android device, and running the real-hardware verification pass — plus exactly what the simulator already proved and what it can't.

You built your app against the browser simulator and the agent-driven test loop closes: you inject an utterance, the right tool fires, the event log shows the expected sequence. That gives you high confidence the protocol, tool wiring, dispatch, and event reporting are correct — because those bytes are identical to what real glasses send and receive. It does not tell you whether the app will sound clear on a noisy street, recover from a dropped Bluetooth link, or survive a 30-minute session on the glasses' battery. Real hardware is the only thing that proves those.

This page is the transition between the two. The simulator needs zero credentials and no Meta involvement — that's the point of it. Moving to real glasses is where your app first acquires a production identity and flips off the simulated transport. There are four moves, all of them one-time setup, and an emulator can't do any of it (no real Bluetooth radio, no Meta companion app), so you need a physical Android device.

The four moves

MoveWhat you doWhere
1 — IdentityCreate a Meta app, enable Wearables DAT, get an App ID + Client Token, register your package name + release SHA-256 signatureMeta for Developers
2 — StorePaste the App ID + Client Token into your project's Credentials section; generateConnectionModule bakes them into the buildExtentos dashboard
3 — ResolveGive Gradle a read:packages GitHub token so it can pull Meta's DAT SDK from GitHub PackagesYour shell or local.properties
4 — Flip & verifyBuild a release variant with no baked simulator URL, pair glasses to an Android device, install, and run the verification passYour machine + a paired Ray-Ban Meta

The rest of this page walks each one, then covers what the simulator already proved (so you know what not to re-test) and what only hardware can confirm.

What the simulator already proved — and what it didn't

Don't re-test on hardware what the simulator already guarantees, and don't assume the simulator covered what it can't. The split is sharp.

Byte-identical with real glasses (proven in sim — trust it):

AspectWhy it's identical
Audio input bytes (8 kHz mono i16 PCM, app → assistant)Same format the glasses' HFP microphone produces
Audio output bytes (tts_audio_chunk, assistant → speaker)Same path that drives the glasses' speaker
The WebSocket to your AI providerThe emulator/sim opens the same connection real hardware does — no proxy difference
Tool dispatch + the assistant.* event logSDK-side, not transport-side — behaves identically everywhere

Only real hardware can confirm (the simulator does not model these):

BucketWhat hardware adds
Audio characterThe glasses' beamforming HFP mic is narrower-band and noisier than a laptop mic. A model that picks tools reliably from clean sim audio may stumble on real capture, especially outdoors.
Hardware timingThe first utterance after the assistant opens is partially clipped while Bluetooth switches A2DP→HFP (~200–500 ms). Music playing over A2DP collides with the assistant and drops to mono.
Hardware failure modesBluetooth range loss, an incoming call pausing the session, ~30 min of continuous HFP draining the battery, sustained camera + assistant throttling the SoC.

If your app passes the agent loop in sim, the logic is correct. The verification pass below is about the second table, not the first.

Move 1 — Get your Meta production identity

Real-hardware traffic is gated by Meta. The simulator's placeholder credentials only ever route to the simulator; a real session needs your own Meta registration.

  1. Create an app at developers.facebook.com/apps and enable the Meta Wearables Device Access Toolkit product.
  2. Copy the App ID and Client Token from the DAT configuration.
  3. Register your app's identity: its package name and its release SHA-256 signing signature. Meta binds the DAT entitlement to that exact pair — your app will not connect to real glasses until it's registered. (On iOS the pair is bundle ID + Team ID instead.)

Because the entitlement is bound to your package and signature, this registration is per-app and can't be shared — Extentos can deliver credentials into your build, but it can't substitute its identity into your signed app.

Move 2 — Store credentials in your dashboard

Open your project's Credentials section and paste in the App ID and Client Token. These are build-time vendor identity — they ship inside your binary and are extractable, so they're build identity, not a server secret. (That's a different role from your managed-gateway AI key, which stays server-side and never ships. The section separates the two.)

You don't hand-edit any files. The next time you run generateConnectionModule, it reads your stored credentials over an owner-authenticated request and bakes them in:

  • Android — writes app/src/main/res/values/extentos_meta_credentials.xml with the meta_application_id and meta_client_token string resources, referenced by two <meta-data> elements (com.meta.wearable.mwdat.APPLICATION_ID / …CLIENT_TOKEN) the MWDAT SDK reads at init. (Values go through @string refs because a bare numeric App ID is typed as an int by the resource compiler.)
  • iOS — fills the Info.plist MWDAT dict (MetaAppID, ClientToken, plus the iOS-only TeamID and URL scheme).

If you haven't stored them yet, the scaffold leaves REPLACE_ME placeholders you can fill by hand. Either way, the simulator and emulator dev need none of this — credentials matter only for real hardware and release builds.

Move 3 — Resolve the Meta DAT SDK (the GitHub token)

This is the one step that surprises people, so it's worth understanding why it exists. The Extentos library depends on Meta's DAT SDK (com.meta.wearable:mwdat-*) transitively, and Meta publishes that SDK only through GitHub Packages — not Maven Central, not Google's Maven repository. GitHub Packages requires authentication even for public reads, so Gradle needs a token to pull it. This is Meta's distribution choice; Extentos just passes it through faithfully.

Concretely you need two things in your project:

  1. The repository, in settings.gradle.kts (inside dependencyResolutionManagement.repositories). generateConnectionModule emits this block for you — it's the only repository you add by hand, because the Extentos library itself resolves from its normal Maven coordinates:

    maven {
      url = uri("https://maven.pkg.github.com/facebook/meta-wearables-dat-android")
      credentials {
        val localProps = java.util.Properties().apply {
          val f = File(rootDir, "local.properties")
          if (f.exists()) f.inputStream().use { load(it) }
        }
        username = localProps.getProperty("github_username") ?: System.getenv("GITHUB_USERNAME") ?: "token"
        password = System.getenv("GITHUB_TOKEN") ?: localProps.getProperty("github_token")
      }
    }
  2. A GitHub personal access token with the read:packages scope, provided either as a GITHUB_TOKEN environment variable or as github_token in your local.properties (which is in the default Android Studio .gitignore — keep it that way). The username can be the literal token.

Validate the whole chain before your first build — token, scope, repo block, and live reachability — with the bundled diagnostic, run from your project root:

npx -p @extentos/mcp-server extentos-mcp setup

It does a ground-truth fetch against the actual mwdat artifact using the same auth scheme Gradle uses, so a green result genuinely predicts a successful build. Running it first saves the 1–3 minute build cycle that otherwise fails late at :app:checkDebugAarMetadata with a bare 401 Unauthorized that never mentions Extentos or Meta. If you skipped it and you're staring at exactly that 401, this is the cause.

Move 4 — Flip the transport, pair, and run

Flip off the simulator

During development the library auto-selects the browser simulator when a session URL is baked in (BuildConfig.EXTENTOS_SESSION_URL on Android, extentos.session.plist on iOS). The resolver checks that baked URL before it checks for bonded glasses — so an APK built with a simulator URL stays in simulator mode even on a phone with glasses paired. For your hardware build:

  • Build a release variant that leaves EXTENTOS_SESSION_URL null (and doesn't set the env var), so the resolver falls through to real Meta DAT when glasses are bonded. A release build also omits the debug-only pairing path.
  • Or, for a quick spike, pass TransportChoice.RealMeta explicitly in ExtentosConfig.

validateIntegration will flag a release build that still has a simulator URL baked, and a missing GitHub Packages repo block — run it before you build.

Pair and install

  1. A physical Android 12+ device (the library's minSdk is 31). No SIM needed.
  2. Install the Meta AI companion app from the Play Store, sign in, and pair your Ray-Ban Meta. Take a photo from the glasses to confirm the pairing works outside your app.
  3. adb install your release-variant APK, grant the runtime permissions (microphone, camera, Bluetooth) on first launch, and let the DAT registration flow hand off to the Meta AI app for approval. Once it returns REGISTERED, the session opens.

Run the verification pass

Walk 5–10 real voice interactions covering every tool you registered, and watch getEventLog(filter: "voice"). You're confirming two things: that the assistant.* sequence matches what the simulator showed (session_started → user_spoke → tool_called → tool_result → assistant_spoke), and how the app behaves on the three things the simulator couldn't model:

  • Audio character — do your tools still fire reliably with real HFP audio, including outdoors? If a tool that was solid in sim gets flaky on hardware, the fix is almost always a clearer tool description, not a code change.
  • Hardware timing — does the first utterance feel right, or is it clipped while Bluetooth switches profiles? Playing an earcon or a short say("ready") on assistant.session_started covers the gap. Check music coexistence if your app needs it.
  • Failure modes — does the app recover from walking out of Bluetooth range, from an incoming call pausing the session, and does it stay usable within the ~30-minute battery ceiling of continuous use?

Record what you find in your app's release notes — these are customer-facing UX expectations, not library bugs. When the pass is clean, getProductionChecklist covers the remaining ship gates (permissions audit, foreground service, store listing).

Troubleshooting

SymptomCauseFix
Build fails at :app:checkDebugAarMetadata with 401No read:packages token, or the GitHub Packages repo block is missingRun extentos-mcp setup; see Move 3
App runs on the phone but behaves like the simulatorA simulator session URL is baked into the buildBuild a release variant with EXTENTOS_SESSION_URL null (Move 4)
Glasses never reach REGISTEREDMeta AI companion app missing, glasses not bonded, or DAT registration not approvedPair and take a photo in the Meta AI app first; see Glasses won't connect
Wearables init fails on a tabletSome tablets can't install the Meta AI companion app from PlayUse an Android 12+ phone, or verify the companion app installs on your tablet before relying on it

Frequently asked questions

Can I just keep using the simulator?

For building and iterating, yes — that's the primary surface, and the agent-driven loop closes there without humans or hardware. But before you put the app on a customer's head, at least one real-hardware pass is required: the simulator is byte-accurate for protocol and logic, and deliberately silent on audio character, Bluetooth timing, and failure modes.

Can I test on an Android emulator instead of a real phone?

No. Emulators have only a virtual Bluetooth stack, so the glasses can't bond, Bluetooth SCO audio can't start, and the DAT registration has no companion app to hand off to. Cloud device farms are real phones but they're in a datacenter — they can't reach the glasses on your desk. Real-hardware testing needs a physical device with the glasses paired to it.

Do I need to create a GitHub personal access token?

You need a token with the read:packages scope so Gradle can pull Meta's DAT SDK from GitHub Packages — see Move 3. It can be a brand-new token used only for this; it never goes into your app binary or your git history. If you've already built the library locally, you likely have one set already (extentos-mcp setup will tell you).

Why does Meta's SDK need a token at all?

Because Meta distributes the Device Access Toolkit exclusively through GitHub Packages, and GitHub Packages requires authentication even for public packages. There's no unauthenticated mirror today. The token authenticates to GitHub, not to Meta or Extentos.

My build worked all through development and suddenly 401s — what changed?

Nothing about your code. The most common trigger is building on a fresh machine or CI runner where the read:packages token isn't configured, or a settings.gradle.kts where the GitHub Packages repo block was dropped. Run extentos-mcp setup to pinpoint which. (Note the SDK is a transitive dependency, so this can bite even simulator builds, not only hardware builds.)

Is the transition different on iOS?

The shape is the same — get a Meta identity, store credentials, flip the transport, pair and verify — but the concrete wiring uses Swift Package Manager and the Info.plist MWDAT dict instead of Gradle and resource files. See the iOS quickstart. The assistant-runtime walkthrough on this page is written against the Android library, which is the GA path today.