feat: detect Meta/Snap/Vuzix smart glasses in the COMMERCIAL source

Camera-bearing smart glasses via Bluetooth SIG company IDs (Meta 0x01AB, Meta
Platforms Technologies 0x058E, Luxottica 0x0D53, Snap 0x03C2, Vuzix 0x060C) —
the same manufacturer-id vector used for Echo/Nest and the one Nearby Glasses
relies on. RayNeo/XREAL/Rokid (no dedicated SIG id) matched by BLE-name hint.
New Family.GLASSES; COMMERCIAL toggle label updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015jgJnzMr2bdNuWcDX2xRUR
This commit is contained in:
KaraZajac
2026-07-15 18:28:54 -04:00
parent 610d6161e8
commit c730657605
2 changed files with 36 additions and 7 deletions
@@ -5,17 +5,19 @@ import java.util.UUID
/** /**
* Curated targets for "device with a microphone in your space" detection. * Curated targets for "device with a microphone in your space" detection.
* *
* Scope is intentionally narrow — only well-known smart-home OEMs whose devices * Scope is intentionally narrow — well-known smart-home OEMs whose devices stay
* stay in fixed locations and continuously listen. Apple manufacturer id 0x004C * in fixed locations and continuously listen, plus body-worn smart glasses
* is deliberately excluded because every iPhone, AirPod, and Apple Watch * (Meta Ray-Ban, Snap Spectacles) that record the space around them. Apple
* advertises it; a coffee shop full of phones must not light up the alarm. * manufacturer id 0x004C is deliberately excluded because every iPhone, AirPod,
* and Apple Watch advertises it; a coffee shop full of phones must not light up
* the alarm.
* *
* Detection vectors collected from public OUI registries (Wireshark/IEEE) * Detection vectors collected from public OUI registries (Wireshark/IEEE)
* and device-setup advertisement docs. * and device-setup advertisement docs.
*/ */
object MicTargets { object MicTargets {
enum class Family { ECHO, RING, GOOGLE, HIDDEN_CAM } enum class Family { ECHO, RING, GOOGLE, HIDDEN_CAM, GLASSES }
/** Bluetooth SIG company identifiers for "voice/smart-home" device families. */ /** Bluetooth SIG company identifiers for "voice/smart-home" device families. */
private val MFG_GOOGLE = 0x00E0 private val MFG_GOOGLE = 0x00E0
@@ -23,6 +25,25 @@ object MicTargets {
/** Yingxin / cheap-spy-cam mfg id seen in field reports. */ /** Yingxin / cheap-spy-cam mfg id seen in field reports. */
private val MFG_YINGXIN = 0x05A7 private val MFG_YINGXIN = 0x05A7
/**
* Bluetooth SIG company identifiers for camera-bearing smart glasses. The
* manufacturer-id field is the reliable vector — device names and service
* UUIDs are inconsistent across advertising frames (per the open-source
* Nearby Glasses project, which this mirrors). Caveat: these ids are
* manufacturer-wide, so Meta's also match Quest VR headsets (still a
* camera/mic device, so an acceptable secondary signal). Luxottica in BLE
* mfg data is essentially only their *smart* eyewear — plain sunglasses
* have no radio. Brands without a dedicated SIG company id (RayNeo — which
* rides under TCL's 0x0BC6, too broad to use — plus XREAL, Rokid) advertise
* under a chipset vendor's id, so those are caught by distinctive BLE-name
* hints below instead.
*/
private val MFG_META = 0x01AB // Meta Platforms, Inc. (ex-Facebook)
private val MFG_META_TECH = 0x058E // Meta Platforms Technologies (Reality Labs; also Quest)
private val MFG_LUXOTTICA = 0x0D53 // Luxottica — Ray-Ban / Oakley Meta frames
private val MFG_SNAP = 0x03C2 // Snap Inc. — Spectacles
private val MFG_VUZIX = 0x060C // Vuzix — enterprise / AR smart glasses
/** Echo/Alexa Voice Service GATT (FE03 — assigned to Amazon Lab126). */ /** Echo/Alexa Voice Service GATT (FE03 — assigned to Amazon Lab126). */
private val UUID_AVS = UUID.fromString("0000fe03-0000-1000-8000-00805f9b34fb") private val UUID_AVS = UUID.fromString("0000fe03-0000-1000-8000-00805f9b34fb")
@@ -63,7 +84,13 @@ object MicTargets {
"Nest" to Family.GOOGLE, "Nest" to Family.GOOGLE,
"GoogleHome" to Family.GOOGLE, "GoogleHome" to Family.GOOGLE,
"Chromecast" to Family.GOOGLE, "Chromecast" to Family.GOOGLE,
"Google-Home" to Family.GOOGLE "Google-Home" to Family.GOOGLE,
"Spectacles" to Family.GLASSES,
"Ray-Ban" to Family.GLASSES,
"RayNeo" to Family.GLASSES,
"Vuzix" to Family.GLASSES,
"XREAL" to Family.GLASSES,
"Rokid" to Family.GLASSES
) )
private val SSID_HINTS: List<Pair<String, Family>> = listOf( private val SSID_HINTS: List<Pair<String, Family>> = listOf(
@@ -115,6 +142,7 @@ object MicTargets {
MFG_AMAZON -> Family.ECHO MFG_AMAZON -> Family.ECHO
MFG_GOOGLE -> Family.GOOGLE MFG_GOOGLE -> Family.GOOGLE
MFG_YINGXIN -> Family.HIDDEN_CAM MFG_YINGXIN -> Family.HIDDEN_CAM
MFG_META, MFG_META_TECH, MFG_LUXOTTICA, MFG_SNAP, MFG_VUZIX -> Family.GLASSES
else -> null else -> null
} }
@@ -149,5 +177,6 @@ object MicTargets {
Family.RING -> "Ring" Family.RING -> "Ring"
Family.GOOGLE -> "Google Nest / Home" Family.GOOGLE -> "Google Nest / Home"
Family.HIDDEN_CAM -> "Possible hidden mic / cam" Family.HIDDEN_CAM -> "Possible hidden mic / cam"
Family.GLASSES -> "Smart glasses"
} }
} }
@@ -100,7 +100,7 @@ fun SettingsScreen(
SourceToggle("DEFLOCK • ALPR map (Overpass)", deflock) { settings.setDeflockEnabled(it) } SourceToggle("DEFLOCK • ALPR map (Overpass)", deflock) { settings.setDeflockEnabled(it) }
SourceToggle("CITIZEN • Real-time incident feed", citizen) { settings.setCitizenEnabled(it) } SourceToggle("CITIZEN • Real-time incident feed", citizen) { settings.setCitizenEnabled(it) }
SourceToggle("WAZE • Live police reports", waze) { settings.setWazeEnabled(it) } SourceToggle("WAZE • Live police reports", waze) { settings.setWazeEnabled(it) }
SourceToggle("COMMERCIAL • Nest, Ring, Echo", mic) { settings.setMicEnabled(it) } SourceToggle("COMMERCIAL • Nest, Ring, Echo, glasses", mic) { settings.setMicEnabled(it) }
Spacer(Modifier.height(8.dp)) Spacer(Modifier.height(8.dp))
if (isRunning) { if (isRunning) {
Button( Button(