fix: audit batch 1 — drop diagnostics, browser foreground heartbeat, Tor host key

- Remove the shipped scroll/zoom diagnostics from NappletBrowserService (per-touch
  MotionEvent log and per-page zoom log).
- NappletBrowserActivity now renews its foreground lease on a 30s heartbeat like
  NappletHostActivity, so the broker's 90s watchdog can't reap it (tearing down
  Tor/relays) while the browser is genuinely foreground.
- Persist the per-host Tor choice against the host actually displayed (webView.url),
  not the start URL, so an in-page navigation doesn't save the choice to the wrong site.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgMpRcWj6y82LxLiwcuzmN
This commit is contained in:
Claude
2026-06-23 23:51:18 +00:00
parent 7d5f68d115
commit 89a32e078f
2 changed files with 23 additions and 16 deletions
@@ -189,6 +189,17 @@ class NappletBrowserActivity : ComponentActivity() {
webView.loadUrl(startUrl)
}
// Renews the broker's foreground lease while resumed; without it the broker's watchdog would reap the
// lease (tearing down Tor/relays) while the browser is still genuinely foreground.
private val heartbeatHandler = Handler(Looper.getMainLooper())
private val heartbeat =
object : Runnable {
override fun run() {
setBrokerForeground(true)
heartbeatHandler.postDelayed(this, FOREGROUND_HEARTBEAT_MS)
}
}
override fun onResume() {
super.onResume()
if (this::webView.isInitialized) {
@@ -196,7 +207,8 @@ class NappletBrowserActivity : ComponentActivity() {
webView.resumeTimers()
}
resumed = true
setBrokerForeground(true)
heartbeatHandler.removeCallbacks(heartbeat)
heartbeat.run()
}
override fun onPause() {
@@ -205,6 +217,7 @@ class NappletBrowserActivity : ComponentActivity() {
webView.pauseTimers()
}
resumed = false
heartbeatHandler.removeCallbacks(heartbeat)
setBrokerForeground(false)
super.onPause()
}
@@ -400,7 +413,10 @@ class NappletBrowserActivity : ComponentActivity() {
useTor = newUseTor
applyWebViewProxy(if (useTor) proxyPort else -1)
if (this::webView.isInitialized) webView.reload()
val host = runCatching { Uri.parse(startUrl).host }.getOrNull()?.takeIf { it.isNotBlank() } ?: return
// Key the persisted choice on the host actually displayed (which may differ from startUrl after
// in-page navigation), so the preference sticks to the right site.
val liveUrl = if (this::webView.isInitialized) webView.url ?: startUrl else startUrl
val host = runCatching { Uri.parse(liveUrl).host }.getOrNull()?.takeIf { it.isNotBlank() } ?: return
val msg =
Message.obtain(null, NappletIpc.MSG_SET_WEB_TOR).apply {
data =
@@ -521,6 +537,10 @@ class NappletBrowserActivity : ComponentActivity() {
companion object {
private const val TAG = "NappletBrowserActivity"
/** How often a resumed browser renews its foreground lease (well under the broker's 90s TTL). */
private const val FOREGROUND_HEARTBEAT_MS = 30_000L
private const val EXTRA_URL = "url"
private const val EXTRA_PROXY_PORT = "proxyPort"
private const val EXTRA_USE_TOR = "useTor"
@@ -161,12 +161,6 @@ class NappletBrowserService : Service() {
configureWebView(wv)
// Theme the pre-load background so a blank/loading page shows Amethyst's background, not white.
wv.setBackgroundColor(bgColor)
// DIAGNOSTIC (scrolling): log whether touch input crosses the SurfaceControlViewHost boundary to
// the remote WebView at all. Returns false so it never consumes — the WebView still scrolls.
wv.setOnTouchListener { _, event ->
Log.w(TAG, "DIAG browser WebView touch action=${event.actionMasked} x=${event.x} y=${event.y}")
false
}
wv.dropSystemBarInsets()
applyWebViewProxy(if (useTor) proxyPort else -1)
val shim = readContractAsset(NappletWebContract.SHIM_JS_PATH).decodeToString()
@@ -242,14 +236,7 @@ class NappletBrowserService : Service() {
override fun onPageFinished(
view: WebView,
url: String,
) {
// DIAGNOSTIC (zoom): a responsive page rendering too large points to a density/viewport
// mismatch from streaming the WebView through SurfaceControlViewHost. scale≈4 confirms 400%.
val dm = view.resources.displayMetrics
@Suppress("DEPRECATION")
Log.w(TAG, "DIAG zoom: scale=${view.scale} density=${dm.density} dmWidthPx=${dm.widthPixels} webViewWidthPx=${view.width}")
pushUrl(view)
}
) = pushUrl(view)
}
private fun pushUrl(view: WebView) {