Skip to content

[Feature] Refactor and cleanup the connection and discovery behavior on both apps as the connection is now stable #139

Description

@jaroslav-sazma

First off — thanks for AirSync.

While reading through the source (main @ 3f3b494) I (AI) went looking for how the app behaves when the Mac is off or out of range for a long stretch, and I wanted to check a few observations with you before assuming I've misread something.

What I think I'm seeing

1. The retry interval settles at 10 s and stays there

In WebSocketUtil.tryStartAutoReconnect():

var backoffMs = 2000L
while (autoReconnectActive.get() && !isConnected()) {
    ...connect(...)
    delay(backoffMs)
    backoffMs = (backoffMs * 1.5).toLong().coerceAtMost(10_000L)
}

So the progression is 2 s → 3 → 4.5 → 6.75 → 10 → 10 → 10 … and the loop exits only on success, on manual disconnect, or when auto-reconnect is switched off. If the Mac stays off all day, that's roughly 8,600 attempts. I couldn't find a second-tier escalation anywhere (I grepped for WorkManager, AlarmManager and minute-scale constants) — is a longer ceiling something you'd already considered? I noticed autoReconnectAttempts and autoReconnectStartTime are assigned but never read, which made me wonder whether something along those lines was already planned.

2. The Wi-Fi lock is held across the whole loop

acquireWifiLock() is called once before the retry loop starts and WIFI_MODE_FULL_LOW_LATENCY is held until cancelAutoReconnect() runs. Would it make sense to acquire it around individual attempts instead, so the idle gaps aren't covered?

3. isLocalNetwork() checks the target address rather than reachability

if (ipAddress.startsWith("192.168.") || ipAddress.startsWith("10.") || ...

A stored Mac address always passes this check, so as far as I can tell the loop still calls connect() when the phone has no Wi-Fi association at all. Since the OkHttp client isn't bound to a specific Network, I think that means the attempt goes out over the default route — i.e. over cellular, where it can't succeed. I haven't verified this on a device, so I may well be wrong about what actually happens on the wire. Would a "do we currently have a network whose subnet matches a known address" pre-check be worth adding before the attempt?

The bit I found most interesting

AirSyncService.registerNetworkCallback() already consults BLE state:

if (isScanning && !BleGattServer.isAnyAuthenticated()) {
    DiscoveryOrchestrator.burstBroadcast(applicationContext)
    WebSocketUtil.requestAutoReconnect(applicationContext)
} else if (isScanning) {
    DiscoveryOrchestrator.burstBroadcast(applicationContext)
}

That uses BLE as a suppressor once a BLE link exists. What occurred to me is the inverse: BLE absence is also a reasonably strong hint that the Mac isn't nearby, and it's far cheaper to keep an advertisement running than to retry a TCP connection every 10 s.

I don't think "no BLE ⇒ stop entirely" would be right — BLE range is much shorter than Wi-Fi, a Mac two rooms away is reachable over the LAN but not over BLE, and Bluetooth may simply be off. But something graduated might fit:

  • BLE peer visible → connect promptly, as today
  • no BLE peer → keep retrying, but with a much longer ceiling (30 s → 2 min → 5 min?), and let discovery/wake-up handle the fast path

Since the Mac already sends a unicast wake-up via QuickConnectManager and bursts presence packets on launch/wake/network change, a slow Wi-Fi retry seems like it would still be enough of a safety net.

Why I'm asking

I'm only at my laptop for part of the day, so I got curious about what the Android side costs while there's simply no connection to be had. I'd also rather not have to flip auto-reconnect on and off by hand depending on where I happen to be.

To be clear, I haven't measured any of this — I was reading through the code out of interest rather than chasing an actual battery problem. If the cost of looking for a peer is small enough to go unnoticed in practice, please feel free to ignore all of this; I was mostly just curious.

Related: #131 (global on/off toggle) covers some of the same ground from the UX side, though this is more about what happens when the app is left in its default state.

Thanks again for the project.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions