Repository navigation
fix(macos): keep classic inquiry running and emit resolved names - #19
Conversation
IOBluetoothDeviceInquiry stops after inquiryLength seconds, so a single startScan only scanned for 3 seconds and then went silent until stopScan. Restart the inquiry while a scan is requested, report scan intent from isScanning, and surface start failures. Also re-emit devices from deviceInquiryDeviceNameUpdated so listeners filtering by name see devices whose name was not resolved on first discovery.
macOS throttles back-to-back IOBluetoothDeviceInquiry starts. Retry a failed restart instead of ending the scan, and use a generation counter so stopScan or a new startScan cancels pending restarts.
|
Added a follow-up commit: macOS throttles back-to-back inquiries, so a failed restart is now retried instead of ending the scan. A generation counter cancels pending restarts on |
macOS can report a found device with its address as the name and never deliver a name update, so name-filtered consumers never see it. Disable automatic name updating, issue remote name requests after each inquiry stops, re-emit the device when the name resolves, and delay the next inquiry until requests finish (capped at 8s).
|
Final follow-up: macOS could keep reporting a device as |
openConnection can return I/O Timeout on the first page after classic discovery. Use a 10s page timeout and retry up to three times before reporting failure.
|
One more macOS reliability fix: |
Move inquiry lifecycle, name resolution, and restart policy into MacInquiryScanner, and bond retries into MacPairer. UniversalBluetoothPlugin now only adapts the Pigeon channels and SDP/HID lifecycle.
- Clear pending name requests on stop and restart, and ignore their late callbacks, so no onDeviceDiscover fires after stopScan. - Report a device once per scan and again only when its name changes. - Ignore inquiry completions from a previous scan session, so a late aborted completion cannot clear the state of a new inquiry. - Bound remote name requests with an explicit page timeout so they finish before the inquiry restarts. - Retry pairing only on a timeout or a busy controller; a rejected pairing now fails immediately instead of prompting again. - Document the continuous macOS scan and its discovery semantics.
Problem
On macOS,
IOBluetoothDeviceInquiryonly runs forinquiryLengthseconds (3 s in this plugin).startScan()started a single round and never restarted it, whileisScanning()reported the instantaneous inquiry state. Consumers that wait for a specific device (for example a camera in pairing mode) could miss the only scan window and then wait forever, even though the radio was idle.A second race:
deviceInquiryDeviceFoundfires as soon as a device is found, before macOS resolves its name. Listeners that filter by name dropped that event, anddeviceInquiryDeviceNameUpdatedonly printed the resolved name without re-emitting the device.Changes
scanRequestedand treat it as the source of truth forisScanning(), including the restart delay.stopScan().onDeviceDiscoverfromdeviceInquiryDeviceNameUpdatedso name-filtered listeners receive the device once its name is available.inquiry.start()failures fromstartScaninstead of silently doing nothing.No Dart API or Pigeon changes.
Verification
flutter pub getflutter analyzeflutter testflutter test --platform chromecd example && flutter build macos --debug