Android FLAG_SECURE, iOS notification privacy, and the padding design

⚠️ NOT BUILT. The machine has been at load 550-640 for three hours with 180
iOS-27 simulator processes on it; xcodebuild was killed rather than failing on
its merits. Committed so the work is not lost, and must be compiled before it
is trusted.

FLAG_SECURE on Android. Blocks screenshots and recording, but the half that
matters is the recents thumbnail: without it Android captures the open
conversation and WRITES IT TO DISK, so a thread existed as a plaintext bitmap
outside the ratchet, the sealed history file and the device-only key. iOS has
had the equivalent since app lock landed; Android had nothing.

iOS notification previews now default to OFF, and the sender is hidden too.
Correcting what I told the owner earlier: Android does NOT leak notification
content - it shows "New message" and never the sender or body. I misread the
count string for the message text. iOS was the leaky one, and worse than it
looked: content.title was set to the sender unconditionally, so turning
previews off hid what was said and still announced who said it. On a lock
screen that is often the more sensitive half, and the setting was labelled
"Show previews".

No incognito-keyboard flag, deliberately: Compose has no stable API for it, and
FLAG_SECURE already makes most keyboards including Gboard suppress
personalised learning. A second mechanism that might be a no-op is worse than
the note.

docs/PADDING.md designs envelope padding rather than implementing it. It is a
wire-format change on a live protocol; the group-decode mismatch in this
repository's history is what that looks like when it goes wrong. Includes the
three-stage rollout, why stripping cannot be inferred, and why the relay needs
no change at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Paul Hitt
2026-08-28 00:38:42 -04:00
co-authored by Claude Opus 5
parent b74cc1907f
commit 98e2580d02
@@ -63,6 +63,29 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// 🔴 **Screenshots off, and out of the recents thumbnail.**
//
// The second half is the one that matters and the one nobody thinks
// about: without FLAG_SECURE, Android captures whatever is on screen
// into the recent-apps preview and WRITES IT TO DISK. An open
// conversation therefore exists as a plaintext bitmap outside every
// protection this app has — the ratchet, the sealed history file, the
// ThisDeviceOnly key — none of which cover a screenshot the OS took of
// the rendered thread.
//
// ⚠️ iOS has had the equivalent since the app lock landed: it obscures
// the switcher snapshot on `scenePhase`. Android had nothing at all.
// Two platforms disagreeing about a security guarantee is how one of
// them ends up being the one that leaks.
//
// ⚠️ It also blocks screen recording and casting, which is the point,
// and is not something to make optional: a setting here is a setting
// people leave off and then get surprised by.
window.setFlags(
android.view.WindowManager.LayoutParams.FLAG_SECURE,
android.view.WindowManager.LayoutParams.FLAG_SECURE,
)
// ⚠️ Runtime permission on API 33+, which is this app's minSdk. Without // ⚠️ Runtime permission on API 33+, which is this app's minSdk. Without
// it the service runs and every alert is dropped silently — no error, // it the service runs and every alert is dropped silently — no error,
// no banner, and nothing on screen to say why. Ask once here rather // no banner, and nothing on screen to say why. Ask once here rather