Generate SSD client logging from typed Jerboa

ober

41b913f1c5e1eef675c0726f9d858722ec014074

diff --git a/.build.yml b/.build.yml
index 141842b..7ee07e1 100644
--- a/.build.yml
+++ b/.build.yml
@@ -5,7 +5,7 @@ packages:
   - make=4.4.1-r4
 sources:
   # Build dependency: full immutable commit, mirrored in dependencies.lock.json.
-  - "https://git.sr.ht/~lisp/jerboa#7765fea05df47999d154b15211e779affcdc237a"
+  - "https://git.sr.ht/~lisp/jerboa#9fc12d13fd151e299d3694922aa9fac9a272ecaa"
   # The second source is the build subject selected by the SourceHut submitter.
   - https://git.sr.ht/~lisp/jerboa-android
 tasks:
@@ -14,6 +14,6 @@ tasks:
       test "$(apk info -v chez-scheme)" = chez-scheme-10.3.0-r2
       test "$(apk info -v git)" = git-2.54.0-r0
       test "$(apk info -v make)" = make-4.4.1-r4
-      test "$(git -C ../jerboa rev-parse HEAD)" = 7765fea05df47999d154b15211e779affcdc237a
-      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = 223a197c469afc20dcc7697ed892213f4baa0723
+      test "$(git -C ../jerboa rev-parse HEAD)" = 9fc12d13fd151e299d3694922aa9fac9a272ecaa
+      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = b41f749819c50653fe7a7c0254a5efa80fd44d80
       JERBOA="chez --libdirs .:../jerboa/lib --script" make test
diff --git a/dependencies.lock.json b/dependencies.lock.json
index 174c543..066bdfe 100644
--- a/dependencies.lock.json
+++ b/dependencies.lock.json
@@ -11,8 +11,8 @@
   "generator_runtime": {
     "name": "jerboa",
     "repository": "https://git.sr.ht/~lisp/jerboa",
-    "commit": "7765fea05df47999d154b15211e779affcdc237a",
-    "tree": "223a197c469afc20dcc7697ed892213f4baa0723"
+    "commit": "9fc12d13fd151e299d3694922aa9fac9a272ecaa",
+    "tree": "b41f749819c50653fe7a7c0254a5efa80fd44d80"
   },
   "assurance_tools": {
     "osv_scanner": {
diff --git a/scripts/verify-supply-chain.sh b/scripts/verify-supply-chain.sh
index 383ec6f..2dfd225 100755
--- a/scripts/verify-supply-chain.sh
+++ b/scripts/verify-supply-chain.sh
@@ -3,8 +3,8 @@ set -eu
 
 repo=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd -P)
 lock="$repo/dependencies.lock.json"
-jerboa_commit=7765fea05df47999d154b15211e779affcdc237a # gitsafe:ignore
-jerboa_tree=223a197c469afc20dcc7697ed892213f4baa0723 # gitsafe:ignore
+jerboa_commit=9fc12d13fd151e299d3694922aa9fac9a272ecaa # gitsafe:ignore
+jerboa_tree=b41f749819c50653fe7a7c0254a5efa80fd44d80 # gitsafe:ignore
 gradle_sha=20f1b1176237254a6fc204d8434196fa11a4cfb387567519c61556e8710aed78
 jdk_macos_sha=8fa1eff40bb637a33613b2ccb8b12c70dc3661cc22cf8e784943715769a05336
 jdk_linux_sha=d8afc263758141a66e0e3aafc321e783f7016696f4eaea067d340a269037d331
diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 1764c6d..a16b2ce 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -4697,6 +4697,8 @@
       (kotlin-imports (java io File)
                       (java io InputStream)
                       (java io OutputStream)
+                      (java lang Runnable)
+                      (java lang Thread)
                       (android app AlertDialog)
                       (android app AlertDialog Builder)
                       (android content Context)
@@ -4820,6 +4822,7 @@
                 mainActivitySetPositiveRequestDownloadAccess
                 mainActivitySetNegativeCancelAppend
                 mainActivitySetPositiveLoadPage
+                runBackground mainActivityLogClientEventAsync
                 mainActivityInstallApproveAreaDialogButton
                 mainActivityInstallAppendDialogButton
                 mainActivityInstallEditDialogButtons
@@ -4898,6 +4901,9 @@
         (type FloatArray)
         (type Bitmap)
         (type Char)
+        (type Exception)
+        (type Runnable)
+        (type Thread)
         (type AlertDialog)
         (type Builder)
         (type AdapterView)
@@ -5034,6 +5040,10 @@
                   (button : Button)
                   (listener : (-> View Unit))) : Unit
           (kotlin-member-call setOnClickListener))
+        (extern (makeThread (runnable : Runnable)) : Thread
+          (kotlin-call Thread))
+        (extern (threadStartRaw (thread : Thread)) : Unit
+          (kotlin-member-call start))
         (extern (boxTypeRawIsWeaponFromActivity
                   (activity : MainActivity)
                   (raw : String)) : Bool
@@ -5369,6 +5379,16 @@
                   (savedStatus : String)
                   (afterSaved : (-> Unit))) : Unit
           (kotlin-member-call saveSessionAsync))
+        (extern (mainActivityClientLogPayloadRaw
+                  (activity : MainActivity)
+                  (event : String)
+                  (fields : JSONObject)) : JSONObject
+          (kotlin-member-call clientLogPayload))
+        (extern (mainActivitySendClientLogRaw
+                  (activity : MainActivity)
+                  (payload : JSONObject)
+                  (readTimeoutMillis : Int32)) : Unit
+          (kotlin-member-call sendClientLog))
         (extern (mainActivityReviewViewSet
                   (activity : MainActivity)
                   (view : SsdReviewView)) : Unit
@@ -6591,6 +6611,12 @@
             button
             (lambda ((unused : View))
               (invoke action))))
+        (def (runBackground (action : (-> Unit))) : Unit
+          (threadStartRaw
+            (makeThread
+              (object Runnable
+                (override (run) : Unit
+                  (invoke action))))))
         (def (alertDialogBuilderSetPositiveAction
                (builder : Builder)
                (text : String)
@@ -6705,6 +6731,28 @@
                         (- (nullablePageNumberOrOne page) (int32 1)))
                       (mainActivityRenderCurrentPageRaw activity))
                     (begin))))))
+        (def (mainActivityLogClientEventAsync
+               (activity : MainActivity)
+               (event : String)
+               (fields : JSONObject)
+               (readTimeoutMillis : Int32)) : Unit
+          (let ((payload
+                  (try
+                    (nullable-some
+                      (mainActivityClientLogPayloadRaw activity event fields))
+                    (catch (error : Exception)
+                      (nullable-none JSONObject)))))
+            (if (nullable-null? payload)
+                (begin)
+                (runBackground
+                  (lambda ()
+                    (try
+                      (mainActivitySendClientLogRaw
+                        activity
+                        (nullable-get payload)
+                        readTimeoutMillis)
+                      (catch (error : Exception)
+                        (begin))))))))
         (def (mainActivityInstallApproveAreaDialogButton
                (activity : MainActivity)
                (dialog : AlertDialog)
@@ -9476,10 +9524,7 @@
        "    }"
        ""
        "    private fun logClientEvent(event: String, fields: JSONObject = emptyJsonObject()) {"
-       "        val payload = try { clientLogPayload(event, fields) } catch (_: Exception) { return }"
-       "        thread {"
-       "            try { sendClientLog(payload, CLIENT_LOG_READ_TIMEOUT_MS) } catch (_: Exception) { }"
-       "        }"
+       "        mainActivityLogClientEventAsync(this, event, fields, CLIENT_LOG_READ_TIMEOUT_MS)"
        "    }"
        ""
        "    private fun logDetectResult("
@@ -9503,7 +9548,7 @@
        "        )"
        "    }"
        ""
-       "    private fun sendClientLog(payload: JSONObject, readTimeoutMillis: Int) {"
+       "    internal fun sendClientLog(payload: JSONObject, readTimeoutMillis: Int) {"
        "        val bytes = textUtf8Bytes(payload.toString())"
        "        val connection = (URL(CLIENT_LOG_ENDPOINT).openConnection() as HttpsURLConnection).apply {"
        "            requestMethod = \"POST\""
@@ -9523,7 +9568,7 @@
        "        }"
        "    }"
        ""
-       "    private fun clientLogPayload(event: String, fields: JSONObject): JSONObject ="
+       "    internal fun clientLogPayload(event: String, fields: JSONObject): JSONObject ="
        "        clientLogPayloadJson("
        "            event,"
        "            SystemClock.uptimeMillis(),"