| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
fix(app): verify pendant capture from arriving audio and recover a silent link (#20378) * fix(app): verify pendant capture from arriving audio and recover a silent link A pendant could stay Bluetooth-connected for hours while no audio reached the app: a restored link was treated as capturing, nothing detected zero ingress, and nothing forced a fresh connection. Capture health on iOS now starts unverified on a restored or replayed link and is established only by a confirmed audio subscription plus audio on the current connection. When evidence is absent or stops, native code runs one persisted-budget recovery: repair the subscription, then a real disconnect and reconnect. The Listening claim and timer follow that evidence; the card, transcript and controls stay. Subscription results are reported to Dart on both platforms, and Recording Requested keeps the telemetry denominator now that Recording Started waits for audio. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): run native capture recovery tests in CI and tighten review-flagged tests Register the capture health and recovery transaction harnesses in the checks manifest so CI runs them, and let the BLE policy test run on its own. Make four tests assert what their names claim, keep the subscription-failure event in the scorecard export, and word the changelog as an attempt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): bound Android CCCD waits and harden the recovery reconnect path Address the remaining review findings on the pendant capture link: - Bound the accepted CCCD write with a 15s native timeout mirroring iOS OmiCaptureHealth.Policy.operationTimeout, so a missing onDescriptorWrite fails the pending completion and releases the GATT queue instead of blocking Dart's retry behind it forever. The pending map entry is the ownership token: a late ack after timeout or cleanup settles nothing and cannot double-release a later command's slot. - unsubscribeCharacteristic gets the same sentinel, timeout, and try/catch treatment (same unbounded-wait defect class). - cleanupPeripheral now fails and removes pending CCCD completions and their timeout callbacks for the torn-down peripheral. - subscribeCharacteristic never throws synchronously (pre-check and queued body wrapped); the generated Pigeon handler cannot strand Dart without a reply. BleHostApiImpl adds the same guard. - Recovery resume goes through existing.connect() instead of the bare transport: transport failures surface as DeviceConnectionException like the cold-connect path, and the pendant time sync runs after the link returns. The service state callback is re-passed so subscribers keep receiving state changes. - Recheck native ingress ownership after the feature-gate await so a legacy episode cannot land invisibly and block later declarations. - _resolveIfActive on ingress-failed episodes releases the telemetry slot while retaining the native-owned ingress failure. - The verified ingress start survives a lease expiry (only withdrawn proof clears it) so the capture timer continues instead of restarting at zero when audio returns. * fix(app): isolate CCCD write ownership and retire timed-out GATTs Serialize descriptor settlement and queue callbacks on the main handler. Arm each write token before issuing its CCCD operation, and release only the current command owner. Retire a GATT with a missing ACK, fail its queued subscription replies, and hand reconnection to the existing managed service so late ENABLE or DISABLE callbacks cannot settle a newer write. Add JVM regressions for late ACKs, stale timers, ENABLE/DISABLE ownership, retired connections, queue-owner changes, rejected writes, and cleanup. * fix(app): bound reconnects after CCCD timeout retirement Distinguish missing CCCD acknowledgements from ordinary GATT disconnects. Persist a per-device budget for two reconnects with backoff, and reset it only after a current successful descriptor ACK. Stop companion and sticky restoration from bypassing exhaustion. Project the exhausted result through existing capture health without a competing Dart retry. Preserve capture intent, replay stored failures when capture binds, and keep healthy Android recording telemetry unchanged. Cover the policy, ACK ownership, and recovery presentation with production-seam tests. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: David Zhang <9387252+Git-on-my-level@users.noreply.github.com> | 14 天前 | |
fix(app): verify pendant capture from arriving audio and recover a silent link (#20378) * fix(app): verify pendant capture from arriving audio and recover a silent link A pendant could stay Bluetooth-connected for hours while no audio reached the app: a restored link was treated as capturing, nothing detected zero ingress, and nothing forced a fresh connection. Capture health on iOS now starts unverified on a restored or replayed link and is established only by a confirmed audio subscription plus audio on the current connection. When evidence is absent or stops, native code runs one persisted-budget recovery: repair the subscription, then a real disconnect and reconnect. The Listening claim and timer follow that evidence; the card, transcript and controls stay. Subscription results are reported to Dart on both platforms, and Recording Requested keeps the telemetry denominator now that Recording Started waits for audio. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): run native capture recovery tests in CI and tighten review-flagged tests Register the capture health and recovery transaction harnesses in the checks manifest so CI runs them, and let the BLE policy test run on its own. Make four tests assert what their names claim, keep the subscription-failure event in the scorecard export, and word the changelog as an attempt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): bound Android CCCD waits and harden the recovery reconnect path Address the remaining review findings on the pendant capture link: - Bound the accepted CCCD write with a 15s native timeout mirroring iOS OmiCaptureHealth.Policy.operationTimeout, so a missing onDescriptorWrite fails the pending completion and releases the GATT queue instead of blocking Dart's retry behind it forever. The pending map entry is the ownership token: a late ack after timeout or cleanup settles nothing and cannot double-release a later command's slot. - unsubscribeCharacteristic gets the same sentinel, timeout, and try/catch treatment (same unbounded-wait defect class). - cleanupPeripheral now fails and removes pending CCCD completions and their timeout callbacks for the torn-down peripheral. - subscribeCharacteristic never throws synchronously (pre-check and queued body wrapped); the generated Pigeon handler cannot strand Dart without a reply. BleHostApiImpl adds the same guard. - Recovery resume goes through existing.connect() instead of the bare transport: transport failures surface as DeviceConnectionException like the cold-connect path, and the pendant time sync runs after the link returns. The service state callback is re-passed so subscribers keep receiving state changes. - Recheck native ingress ownership after the feature-gate await so a legacy episode cannot land invisibly and block later declarations. - _resolveIfActive on ingress-failed episodes releases the telemetry slot while retaining the native-owned ingress failure. - The verified ingress start survives a lease expiry (only withdrawn proof clears it) so the capture timer continues instead of restarting at zero when audio returns. * fix(app): isolate CCCD write ownership and retire timed-out GATTs Serialize descriptor settlement and queue callbacks on the main handler. Arm each write token before issuing its CCCD operation, and release only the current command owner. Retire a GATT with a missing ACK, fail its queued subscription replies, and hand reconnection to the existing managed service so late ENABLE or DISABLE callbacks cannot settle a newer write. Add JVM regressions for late ACKs, stale timers, ENABLE/DISABLE ownership, retired connections, queue-owner changes, rejected writes, and cleanup. * fix(app): bound reconnects after CCCD timeout retirement Distinguish missing CCCD acknowledgements from ordinary GATT disconnects. Persist a per-device budget for two reconnects with backoff, and reset it only after a current successful descriptor ACK. Stop companion and sticky restoration from bypassing exhaustion. Project the exhausted result through existing capture health without a competing Dart retry. Preserve capture intent, replay stored failures when capture binds, and keep healthy Android recording telemetry unchanged. Cover the policy, ACK ownership, and recovery presentation with production-seam tests. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: David Zhang <9387252+Git-on-my-level@users.noreply.github.com> | 14 天前 | |
fix(app): verify pendant capture from arriving audio and recover a silent link (#20378) * fix(app): verify pendant capture from arriving audio and recover a silent link A pendant could stay Bluetooth-connected for hours while no audio reached the app: a restored link was treated as capturing, nothing detected zero ingress, and nothing forced a fresh connection. Capture health on iOS now starts unverified on a restored or replayed link and is established only by a confirmed audio subscription plus audio on the current connection. When evidence is absent or stops, native code runs one persisted-budget recovery: repair the subscription, then a real disconnect and reconnect. The Listening claim and timer follow that evidence; the card, transcript and controls stay. Subscription results are reported to Dart on both platforms, and Recording Requested keeps the telemetry denominator now that Recording Started waits for audio. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): run native capture recovery tests in CI and tighten review-flagged tests Register the capture health and recovery transaction harnesses in the checks manifest so CI runs them, and let the BLE policy test run on its own. Make four tests assert what their names claim, keep the subscription-failure event in the scorecard export, and word the changelog as an attempt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): bound Android CCCD waits and harden the recovery reconnect path Address the remaining review findings on the pendant capture link: - Bound the accepted CCCD write with a 15s native timeout mirroring iOS OmiCaptureHealth.Policy.operationTimeout, so a missing onDescriptorWrite fails the pending completion and releases the GATT queue instead of blocking Dart's retry behind it forever. The pending map entry is the ownership token: a late ack after timeout or cleanup settles nothing and cannot double-release a later command's slot. - unsubscribeCharacteristic gets the same sentinel, timeout, and try/catch treatment (same unbounded-wait defect class). - cleanupPeripheral now fails and removes pending CCCD completions and their timeout callbacks for the torn-down peripheral. - subscribeCharacteristic never throws synchronously (pre-check and queued body wrapped); the generated Pigeon handler cannot strand Dart without a reply. BleHostApiImpl adds the same guard. - Recovery resume goes through existing.connect() instead of the bare transport: transport failures surface as DeviceConnectionException like the cold-connect path, and the pendant time sync runs after the link returns. The service state callback is re-passed so subscribers keep receiving state changes. - Recheck native ingress ownership after the feature-gate await so a legacy episode cannot land invisibly and block later declarations. - _resolveIfActive on ingress-failed episodes releases the telemetry slot while retaining the native-owned ingress failure. - The verified ingress start survives a lease expiry (only withdrawn proof clears it) so the capture timer continues instead of restarting at zero when audio returns. * fix(app): isolate CCCD write ownership and retire timed-out GATTs Serialize descriptor settlement and queue callbacks on the main handler. Arm each write token before issuing its CCCD operation, and release only the current command owner. Retire a GATT with a missing ACK, fail its queued subscription replies, and hand reconnection to the existing managed service so late ENABLE or DISABLE callbacks cannot settle a newer write. Add JVM regressions for late ACKs, stale timers, ENABLE/DISABLE ownership, retired connections, queue-owner changes, rejected writes, and cleanup. * fix(app): bound reconnects after CCCD timeout retirement Distinguish missing CCCD acknowledgements from ordinary GATT disconnects. Persist a per-device budget for two reconnects with backoff, and reset it only after a current successful descriptor ACK. Stop companion and sticky restoration from bypassing exhaustion. Project the exhausted result through existing capture health without a competing Dart retry. Preserve capture intent, replay stored failures when capture binds, and keep healthy Android recording telemetry unchanged. Cover the policy, ACK ownership, and recovery presentation with production-seam tests. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: David Zhang <9387252+Git-on-my-level@users.noreply.github.com> | 14 天前 | |
fix(app): discover NotePin S from the advertisement name (#18878) * fix(app): discover NotePin S from the advertisement name iOS often leaves CBPeripheral.name empty until the first connection, so a NotePin S sitting nearby never reached the name check. Fixes #18705 Co-authored-by: Cursor <cursoragent@cursor.com> * test(app): cover NotePin discovery naming from the advertisement Extract the scan-time naming policy into OmiBleDiscoveryNaming so the advertised-name precedence and PLAUD manufacturer fallback are exercised by `ruby ios/test/omi_ble_discovery_naming_test.rb`, mirroring OmiBlePairingPolicy. * fix(app): fall back to the NotePin advertisement on Android Android showed the same never-paired gap the iOS fix closed: when the NotePin S advertises no local name, the empty name is filtered before classification. Mirror the PLAUD manufacturer-id payload match and cover it with a JVM test. * chore(app): add the unreleased changelog fragment for NotePin discovery --------- Co-authored-by: Cursor <cursoragent@cursor.com> | 20 天前 | |
fix(app): keep a resolved audio outage until its recovery ages out Disconnect history pruned resolved rows by outage start, so a recovery that began more than seven days earlier was deleted before Dart could import it. Co-authored-by: Cursor <cursoragent@cursor.com> | 1 天前 | |
fix(app): keep a resolved audio outage until its recovery ages out Disconnect history pruned resolved rows by outage start, so a recovery that began more than seven days earlier was deleted before Dart could import it. Co-authored-by: Cursor <cursoragent@cursor.com> | 1 天前 | |
fix(app): detect stale iOS bond after CV1 DFU and stop reconnect loops (#14158) After guarded CV1 firmware updates, iOS can retain a stale bond while the pendant advertises normally. CoreBluetooth then fails with pairing_lost (error 14) and the app previously kept auto-reconnecting without surfacing recovery. Classify pairing_lost reliably (including NSError/CBErrorDomain variants), block native auto-reconnect for stale bonds, gate Dart ensureConnection while recovery is required, and present the existing forget-device guidance dialog. Verification: - Added app/test/services/devices/stale_bond_recovery_test.dart - Added app/ios/test/omi_ble_pairing_policy_test.rb (ios-ble-pairing-policy manifest check) Fixes #11308 Failure-Class: none Co-authored-by: Cursor Agent <cursoragent@cursor.com> | 1 个月前 | |
fix(app): verify pendant capture from arriving audio and recover a silent link (#20378) * fix(app): verify pendant capture from arriving audio and recover a silent link A pendant could stay Bluetooth-connected for hours while no audio reached the app: a restored link was treated as capturing, nothing detected zero ingress, and nothing forced a fresh connection. Capture health on iOS now starts unverified on a restored or replayed link and is established only by a confirmed audio subscription plus audio on the current connection. When evidence is absent or stops, native code runs one persisted-budget recovery: repair the subscription, then a real disconnect and reconnect. The Listening claim and timer follow that evidence; the card, transcript and controls stay. Subscription results are reported to Dart on both platforms, and Recording Requested keeps the telemetry denominator now that Recording Started waits for audio. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): run native capture recovery tests in CI and tighten review-flagged tests Register the capture health and recovery transaction harnesses in the checks manifest so CI runs them, and let the BLE policy test run on its own. Make four tests assert what their names claim, keep the subscription-failure event in the scorecard export, and word the changelog as an attempt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): bound Android CCCD waits and harden the recovery reconnect path Address the remaining review findings on the pendant capture link: - Bound the accepted CCCD write with a 15s native timeout mirroring iOS OmiCaptureHealth.Policy.operationTimeout, so a missing onDescriptorWrite fails the pending completion and releases the GATT queue instead of blocking Dart's retry behind it forever. The pending map entry is the ownership token: a late ack after timeout or cleanup settles nothing and cannot double-release a later command's slot. - unsubscribeCharacteristic gets the same sentinel, timeout, and try/catch treatment (same unbounded-wait defect class). - cleanupPeripheral now fails and removes pending CCCD completions and their timeout callbacks for the torn-down peripheral. - subscribeCharacteristic never throws synchronously (pre-check and queued body wrapped); the generated Pigeon handler cannot strand Dart without a reply. BleHostApiImpl adds the same guard. - Recovery resume goes through existing.connect() instead of the bare transport: transport failures surface as DeviceConnectionException like the cold-connect path, and the pendant time sync runs after the link returns. The service state callback is re-passed so subscribers keep receiving state changes. - Recheck native ingress ownership after the feature-gate await so a legacy episode cannot land invisibly and block later declarations. - _resolveIfActive on ingress-failed episodes releases the telemetry slot while retaining the native-owned ingress failure. - The verified ingress start survives a lease expiry (only withdrawn proof clears it) so the capture timer continues instead of restarting at zero when audio returns. * fix(app): isolate CCCD write ownership and retire timed-out GATTs Serialize descriptor settlement and queue callbacks on the main handler. Arm each write token before issuing its CCCD operation, and release only the current command owner. Retire a GATT with a missing ACK, fail its queued subscription replies, and hand reconnection to the existing managed service so late ENABLE or DISABLE callbacks cannot settle a newer write. Add JVM regressions for late ACKs, stale timers, ENABLE/DISABLE ownership, retired connections, queue-owner changes, rejected writes, and cleanup. * fix(app): bound reconnects after CCCD timeout retirement Distinguish missing CCCD acknowledgements from ordinary GATT disconnects. Persist a per-device budget for two reconnects with backoff, and reset it only after a current successful descriptor ACK. Stop companion and sticky restoration from bypassing exhaustion. Project the exhausted result through existing capture health without a competing Dart retry. Preserve capture intent, replay stored failures when capture binds, and keep healthy Android recording telemetry unchanged. Cover the policy, ACK ownership, and recovery presentation with production-seam tests. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: David Zhang <9387252+Git-on-my-level@users.noreply.github.com> | 14 天前 | |
feat(app): pendant audio-loss integrity — unresolved outages + per-day provenance (#21151) * feat(app): credible audio-loss accounting — persist unresolved outages, per-day provenance * fix(app): fence durable audio evidence and reconcile daily outage exposure * fix(app): rekey OmiDeviceHealthStore pbxproj entries to unique UUIDs * test(app): gate depends_on_21152 delivery tests out of shared CI runs * fix(app): buffer BLE outage writes when SharedPreferences binding is absent * test(app): enable depends_on_21152 delivery tests after #21152 merge | 7 天前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 14 天前 | ||
| 14 天前 | ||
| 14 天前 | ||
| 20 天前 | ||
| 1 天前 | ||
| 1 天前 | ||
| 1 个月前 | ||
| 14 天前 | ||
| 7 天前 |