fix(aprs): the service could not start at all on Android 10 and later
The previous commit changed the manifest's foregroundServiceType to location and left
startForeground passing FOREGROUND_SERVICE_TYPE_DATA_SYNC. AOSP requires the passed
type to be a subset of the declared one - location is 0x08, dataSync is 0x01 - and
throws IllegalArgumentException otherwise, a check that has been there since API 29.
That throw landed in the surrounding catch, which calls stopSelf().
So APRS started, died, and said nothing. No notification, no beacon, no Toast, no
last-report row, and the settings switch stayed on because the config had already been
saved. This is worse than the defect the rewrite was written to fix: reporting success
for packets that never left at least sometimes worked, whereas this never ran at all,
on essentially every device in use, with no visible symptom. Two auditors found it
independently by reading the constants against AOSP's own check.
Two more findings from the same review.
Receive-only was reported as a wrong passcode. Both a deliberate -1 and a mismatched
entry log in with -1, and the server answers "unverified" to each, so the operator who
chose receive-only - the one way to test a setup without putting anything on the network
- was told to go and fix the passcode they had set on purpose. The report now carries
whether receive-only was asked for, and says so instead.
The card could show "failed - sent". The detail string was the write's own verdict, and
a write that succeeds on a refused login is exactly the case where those two disagree.
A failure now reports what actually failed.
Also: the packet is built before connecting. The reporter used to open a session and log
in only to discover it had nothing to send, which for an operator with no station
position set meant a pointless login every five minutes.
Still outstanding, and the reason this is not enough on its own: nothing tests the
service, so neither this defect nor the missing line terminator in 7ac54f0a could have
been caught by the suite. Both were found by audit. A location-typed foreground service
on API 34+ may also require a granted location permission before startForeground, which
the settings card does not request - that needs checking on hardware.
This commit is contained in:
1 parent
e0900778f0
commit
0a67f74369
7 files changed
+62
-16
No files matched your search
@@ -43,7 +43,16 @@ data class AprsReport(
|
||||
* nothing reaching the network. A login whose response we simply could not parse leaves this
|
||||
* true, since the packets may be landing and the passcode is not at fault.
|
||||
*/
|
||||
val verified: Boolean = true
|
||||
val verified: Boolean = true,
|
||||
/**
|
||||
* True when the operator asked for a receive-only connection.
|
||||
*
|
||||
* Distinguished from a refused login because both log in with -1 and the server answers
|
||||
* "unverified" to each: without this, deliberately choosing receive-only - the one way to
|
||||
* test a setup without putting anything on the network - was reported as a wrong passcode
|
||||
* and sent the operator to fix something they had set on purpose.
|
||||
*/
|
||||
val receiveOnly: Boolean = false
|
||||
)
|
||||
|
||||
/** Report scheduler (periodic + manual trigger); connection management lives in the foreground service */
|
||||
@@ -94,19 +103,9 @@ class AprsReporter(
|
||||
if (!cfg.enabled || cfg.callsign.isBlank()) return
|
||||
onState(AprsState.Connecting)
|
||||
try {
|
||||
val c = client ?: AprsIsClient(
|
||||
host = cfg.server,
|
||||
port = cfg.port,
|
||||
callsign = cfg.callsign,
|
||||
ssid = cfg.ssid,
|
||||
// Never derives one: a blank or wrong entry logs in receive-only rather than
|
||||
// transmitting under a passcode the app invented for an unchecked licence.
|
||||
passcode = AprsPasscode.loginValue(cfg.callsign, cfg.passcode),
|
||||
version = "Look4Sat 4.5.4"
|
||||
).also { client = it }
|
||||
if (!c.isConnected) c.connect()
|
||||
onState(AprsState.Connected)
|
||||
|
||||
// The packet is built BEFORE connecting: there is no reason to open a session and log
|
||||
// in only to discover there is nothing to send, which happened every five minutes for
|
||||
// an operator whose QTH was unset.
|
||||
val pos = positionProvider()
|
||||
val beacon = AprsBeacon.build(
|
||||
callsign = cfg.callsign,
|
||||
@@ -127,6 +126,23 @@ class AprsReporter(
|
||||
return
|
||||
}
|
||||
val packetLine = (beacon as AprsBeacon.Result.Line).text
|
||||
|
||||
// Receive-only is a deliberate choice, not a mistake, and has to be carried through:
|
||||
// it logs in with -1 exactly as a wrong passcode does, and the server answers
|
||||
// "unverified" to both.
|
||||
val wantsReceiveOnly = !AprsPasscode.canTransmit(cfg.callsign, cfg.passcode)
|
||||
val c = client ?: AprsIsClient(
|
||||
host = cfg.server,
|
||||
port = cfg.port,
|
||||
callsign = cfg.callsign,
|
||||
ssid = cfg.ssid,
|
||||
// Never derives one: a blank or wrong entry logs in receive-only rather than
|
||||
// transmitting under a passcode the app invented for an unchecked licence.
|
||||
passcode = AprsPasscode.loginValue(cfg.callsign, cfg.passcode),
|
||||
version = "Look4Sat 4.5.4"
|
||||
).also { client = it }
|
||||
if (!c.isConnected) c.connect()
|
||||
onState(AprsState.Connected)
|
||||
val result = c.sendPacket(packetLine)
|
||||
val sent = result?.first == true
|
||||
val detail = result?.second ?: "no connection"
|
||||
@@ -137,7 +153,21 @@ class AprsReporter(
|
||||
// the operator to fix something that is not broken.
|
||||
val refused = c.isRefusedByServer
|
||||
val ok = sent && !refused
|
||||
onReport(AprsReport(System.currentTimeMillis(), packetLine, ok, detail, !refused))
|
||||
// "sent" is the write's own verdict and reads as nonsense next to a failure - the card
|
||||
// showed "failed - sent" for a refused login. When the refusal is what failed the
|
||||
// report, say that instead.
|
||||
val reported = when {
|
||||
ok -> detail
|
||||
refused && wantsReceiveOnly -> "receive-only, not forwarded"
|
||||
refused -> "login not verified"
|
||||
else -> detail
|
||||
}
|
||||
onReport(
|
||||
AprsReport(
|
||||
System.currentTimeMillis(), packetLine, ok, reported,
|
||||
verified = !refused, receiveOnly = wantsReceiveOnly
|
||||
)
|
||||
)
|
||||
if (ok) onState(AprsState.Connected) else onState(AprsState.Error)
|
||||
} catch (e: Exception) {
|
||||
runCatching { client?.disconnect() }
|
||||
|
||||
Reference in new issue
Block a user