`E2ESession.respond` hard-coded `version = if (pq == null) 1 else 2`, so
a session this device answered could never be v3 — while `initiate`
twelve lines above correctly returns 3 whenever the post-quantum ratchet
is on. A conversation iOS started therefore ran as v3 on one side and v2
on the other, and every message this device sent into it was refused by
the peer as a downgrade (§13.4) and dropped on arrival. iOS to Android
worked; Android to iOS did not, for as long as the pairing existed.
Kotlin talking to Kotlin never noticed, because both ends drive the
ratchet from their own PQ state and ignore the label. Only a peer that
enforces the version sees it. `HybridSessionTest` asserted the wrong
value — `assertEquals(2, bobSide.version)`, two lines below requiring
the initiator to be 3 — so the suite passed while encoding the defect.
Corrected, with the reason recorded next to it.
The version now follows the initiator rather than assuming 3 whenever PQ
is present: a v2 peer sends a PQ block too, and answering that in v3
breaks the pairing in the other direction.
Receive-side failures are no longer swallowed. `catch (_: Exception) {}`
threw away the reason for every undecryptable envelope, which left this
half of every delivery investigation unreadable — the relay says
delivered, the app shows nothing, and the one place that knew why
discarded it. The envelope is still skipped; it now names the id, the
device and the exception. Sends report the protocol shape they emit, and
receives report which of the three chains opened them.
Also here:
- proguard-rules.pro, which did not exist while build.gradle.kts
referenced it. R8 accepts a missing entry in silence, so release
builds ran on the platform defaults and answering a call aborted the
process inside WebRTC's JNI_OnLoad — it resolves Java classes by
name, and R8 had renamed them. The build now fails if the file is
missing rather than shipping without it.
- Server refusals read as sentences. `RelayException.Http` had no case
in `describe()`, so choosing a taken handle showed "relay returned
409:" and a JSON fragment, which says nothing about picking another
name. Transport failures showed a Java networking string naming an
address the person never typed.
- Switching between signing in and creating an account clears the last
error. It belonged to the other mode and stayed on screen, so taking
the advice it gave looked like it had failed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
51 lines
2.5 KiB
Prolog
51 lines
2.5 KiB
Prolog
# R8 rules for the release build.
|
|
#
|
|
# 🔴 **This file did not exist, and `build.gradle.kts` referenced it anyway.**
|
|
# R8 accepts a missing `proguardFiles` entry in silence, so the release build
|
|
# ran with the platform defaults alone and nobody was told. What that cost:
|
|
# answering a call on a release build killed the process instantly —
|
|
#
|
|
# Fatal signal 6 (SIGABRT), thread DefaultDispatch
|
|
# JNI_OnLoad <- libjingle_peerconnection_so.so
|
|
# System.loadLibrary
|
|
# org.webrtc.PeerConnectionFactory
|
|
#
|
|
# — because WebRTC's native `JNI_OnLoad` resolves its Java classes **by name**,
|
|
# and R8 had renamed them. The lookup fails and the library aborts the process
|
|
# rather than throwing, so there is no Kotlin stack trace and nothing in the
|
|
# call log: the app is gone before it can write one. Reported as "calling from
|
|
# iOS to Android crashes Android when I hit answer" and, from the other side,
|
|
# "iOS shows 2 calls, Android shows 0". One cause, two symptoms.
|
|
#
|
|
# ⚠️ **Debug builds cannot catch this and never will.** `connectedDebugAndroidTest`
|
|
# runs the unminified variant, where every one of these classes still has its
|
|
# real name. Every call test this project has ever run was blind to it. A
|
|
# release-build call test is the only thing that would have.
|
|
|
|
# --- WebRTC ----------------------------------------------------------------
|
|
# Everything, members included: the native side looks up classes, methods AND
|
|
# fields by their source names. Narrowing this to class names alone is the
|
|
# tempting half-fix and it still aborts, in a different frame.
|
|
-keep class org.webrtc.** { *; }
|
|
-dontwarn org.webrtc.**
|
|
|
|
# Any class holding native methods must keep those names, or the JNI binding
|
|
# that pairs them by signature finds nothing.
|
|
-keepclasseswithmembernames class * {
|
|
native <methods>;
|
|
}
|
|
|
|
# WebRTC marks its JNI entry points with this annotation. Keeping the
|
|
# annotation itself is what lets the rule above stay meaningful after R8's
|
|
# attribute stripping.
|
|
-keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod
|
|
|
|
# --- Conscrypt -------------------------------------------------------------
|
|
# ⚠️ The engine selects Conscrypt explicitly by provider name (see
|
|
# android/PARITY.md). Renaming its classes turns that selection into a silent
|
|
# fallback to the JVM's own provider, which is a different implementation with
|
|
# different behaviour — the exact class of divergence the parity tests exist
|
|
# to catch, arriving only in release.
|
|
-keep class org.conscrypt.** { *; }
|
|
-dontwarn org.conscrypt.**
|