An auditor ran the plan's own release gate against live APRS-IS servers. It failed at
the login step, on every server tried:
sent: user N0CALL pass -1 vers Look4Sat-4.5.4
got: # Invalid login: software name and version are not separated by a space
Reproduced on euro.aprs2.net and noam.aprs2.net, aprsc 2.1.21. `vers` takes TWO tokens,
a software name and a version. An earlier commit read the rule "softwarename must not
contain a space" as "the field must be one token" and hyphenated the space between them
- and the unit test asserted that as correct, so the mistake was frozen in place.
Worse than the malformed line was what happened next. `# Invalid login:` is a comment
but not a logresp, so parse skipped it as keepalive chatter; the login then timed out
into Unknown, which is deliberately treated as "may be working"; so `ok = sent &&
!refused` was true and the operator was shown "APRS: report sent OK" for a login the
server had refused. That is v4.6.0's defining defect - every send reported successful
regardless of outcome - still live on the exact path every operator takes. The rebuild
narrowed it rather than closing it.
Both halves are fixed: the name and version stay separate tokens with whitespace
collapsed within each, and a refusal comment is classified as a refusal before the
logresp test. A socket test now replays the server's actual bytes.
Three smaller things from the same review:
The foreground service type goes back to dataSync. The previous commit chose location
to escape dataSync's six-hour cap, but a location-typed service is refused outright
unless a location runtime permission has already been granted, and the settings card
requests only notifications - so it would have failed silently for anyone who declined
location access. The cap that prompted the switch applies only when targetSdk is 35 or
higher, which this project does not declare. A test now reads the manifest and the
service source and fails if they disagree, which is the only way this class of defect
is visible from a JVM test.
The version string in the login was 4.5.4 while the app was 4.6.0. Now split into name
and version and corrected, though it is still hardcoded - core:data has no BuildConfig,
so passing it in properly is a separate change.
The passcode hint said "empty = auto-computed from callsign" in all five locales. The
app stopped doing that two commits ago; it now connects receive-only, and the hint says
so. It was the first thing an operator read next to the field, promising the behaviour
that was deliberately removed.
Not fixed, and known: the notification body is rebuilt from the previous cycle's state
so it can show a stale verdict, a deliberate receive-only choice is still styled as an
error, and no last-success timestamp exists - so an operator still cannot establish
whether their station has ever reached the network.
The position line was one string template, and it broke four rules at once.
No path. The specification says a client-originated packet carries TCPIP* in the
path, "nothing more or less", and there was none - `CALL>APRS:=...` went out bare.
No position meant 0,0. When the station QTH was unset and no GPS fix was available,
`lat ?: 0.0` put the operator at 0 degrees north, 0 degrees east - a point in the
Gulf of Guinea - on the global network, under their own callsign. There is no honest
default for "nowhere", so AprsBeacon refuses instead and the reporter says why. A
genuine 0,0 fix is still legal and still sent; the refusal is about absence.
No comment sanitising. A line break typed into the status field ended the packet and
started a second one from the remaining text, which an operator could trigger by
pressing return. Measured: the old builder emitted two lines from one call, the second
impersonating whatever callsign the text contained. Only printable ASCII survives now.
No length cap. A 600-character status produced a 636-byte line against a 512-byte
limit including CRLF. The comment is trimmed to whatever room is left after the
header and the coordinates, bounded also by the format's own 43-character limit.
Symbol handling was whatever character the operator typed first, including one that
breaks the fixed-width parse. It now accepts only what the specification allows -
the two table selectors and overlay characters - and falls back to the primary table.
aprs.fi names symbol misconfiguration as the most common reason a station never
appears on the map, so this is not cosmetic.
Separately, beaconing stopped whenever the screen locked. The interval was a coroutine
delay inside the reporter, and Doze suspends network access and ignores wake locks even
for a foreground service: the timer fired on schedule and then could not reach the
network, while the notification went on claiming the service was running. The service
now books each beacon with setExactAndAllowWhileIdle, which is the only scheduling that
survives Doze, and reschedules after each tick so a changed interval applies at once.
If the operator has revoked exact alarms it falls back to an inexact one, which beacons
late rather than not at all.
The foreground service type changes from dataSync to location. dataSync is capped at
six hours in any 24-hour window on recent Android and then stopped by the system, which
would silently end a beacon meant to run all day; the service reads the station position
and falls back to GPS, so location describes what it actually does.
The interval floor becomes five minutes rather than one. This station is fixed or
walking, and APRS-IS etiquette is to beacon no more often than the position changes.
The packet builder moves to core:domain as pure logic, so all of this is testable
without a socket - including that a comma-decimal locale cannot corrupt the coordinates,
which nothing covered before.
Root cause: user reported crash on enabling APRS with a callsign set
(empty callsign worked because the service stops early and never
reaches startForeground). Android 14+ requires the service to declare
foregroundServiceType when startForeground passes a type; the service
had none -> process died on toggle.
- Manifest: add android:foregroundServiceType="dataSync"
- AprsCard: use startForegroundService() for start/report actions
(Android 8+ standard for foreground services)
- AprsForegroundService: try-catch around startForeground, fallback
stopSelf instead of killing the process
- AprsReporter: passcode "-1" (APRSdroid "no auth" convention) also
auto-computes from callsign
- Merged upstream 418a05e3 (zh wording fix, no conflicts)
Verified: compileDebugKotlin all modules BUILD SUCCESSFUL,
check_strings 9 files OK.
- prefs_aprs_summary uses %2$d but AprsCard passed port.toString()
(String) -> String.format threw IllegalFormatConversionException
on every Settings screen open (crash). Pass Int now.
- Manifest service name was ".AprsForegroundService" (resolves under
applicationId) but the class lives in com.rtbishop.look4sat.app ->
ClassNotFoundException when toggling. Use full class name.
Verified: :feature:settings:compileDebugKotlin + :app:compileDebugKotlin
BUILD SUCCESSFUL.
The official app signs com.rtbishop.look4sat with its own certificate. A fork sharing that applicationId cannot be installed over the official build (signature mismatch) and users saw overwrite/install failures.
- Add applicationId version catalog entry; namespace stays com.rtbishop.look4sat so source imports are untouched, applicationId becomes com.rtbishop.look4sat.bg7nta.
- Update PROPERTY_SATELLITE_DATA_OPTIMIZED meta-data to the fork id.
- Document the fork-applicationId requirement in the version catalog.