925678181f45

fix: Autofill no longer crashes on third-party apps with malformed hint arrays (v2.14.11)

Root cause: AssistStructureParser.classify dereferenced entries of the autofillHints array that other apps supply. Facebook Messenger on the Fold 7 presents an array containing a null entry, NPE-ing on every fill request (AssistStructureParser.kt:176) and killing the autofill service process — One UI then shows the repeated "app keeps stopping / clear cache" dialog. The app code had not changed; Messenger's malformed hints are the new trigger (same latent signature also appeared 08-03). - Shared nonNullHints() filter applied at both hint read sites (classify + isDigitBoxCandidate) so null entries are dropped instead of crashing - Regression pin: AssistStructureNullHintsTest (3 cases) - VERSION 2.14.10 -> 2.14.11 Gates: testDebugUnitTest 987/0/0/0 (was 984, +3), lintDebug 0 errors / 85 warnings unchanged.

time
2026-08-07 20:15 UTC · 2 months ago
machine
koh2
parent
211e2d3269c5
3 files VERSION+1 −1app/src/main/java/com/peachpasswords/autofill/AssistStructureParser.kt+9 −2app/src/test/java/com/peachpasswords/autofill/AssistStructureNullHintsTest.kt+30 −0
VERSION+1 −1
@@ -1 +1 @@1-2.14.101+2.14.11

view file

app/src/main/java/com/peachpasswords/autofill/AssistStructureParser.kt+9 −2
@@ -157,7 +157,7 @@157157             if (attrs["maxlength"] != "1") return false158158             return (attrs["type"]?.lowercase() ?: "") in setOf("", "text", "number", "tel")159159         }160-        if (node.autofillHints?.any { it.equals("smsOTPCode", true) || it.equals("oneTimeCode", true) } == true) {160+        if (nonNullHints(node.autofillHints).any { it.equals("smsOTPCode", true) || it.equals("oneTimeCode", true) }) {161161             return true162162         }163163         val type = node.inputType@@ -171,8 +171,15 @@171171 172172     private enum class FieldKind { USERNAME, EMAIL, PASSWORD, OTP, CARD_NUMBER, CARD_EXP_MONTH, CARD_EXP_YEAR, CARD_EXP_DATE, CARD_CVC, CARD_NAME, NONE }173173 174+    /**175+     * Third-party apps supply the autofillHints array and some ship null176+     * entries (Facebook Messenger, 2026-08-07 Fold 7 crash loop). Hints are177+     * untrusted input: drop nulls so classification never dereferences one.178+     */179+    internal fun nonNullHints(hints: Array<String?>?): List<String> = hints?.filterNotNull().orEmpty()180+174181     private fun classify(node: AssistStructure.ViewNode): FieldKind {175-        node.autofillHints?.forEach { hint ->182+        nonNullHints(node.autofillHints).forEach { hint ->176183             when (hint.lowercase()) {177184                 View.AUTOFILL_HINT_PASSWORD, "password" -> return FieldKind.PASSWORD178185                 View.AUTOFILL_HINT_USERNAME, "username" -> return FieldKind.USERNAME

view file

app/src/test/java/com/peachpasswords/autofill/AssistStructureNullHintsTest.kt+30 −0
@@ -0,0 +1,30 @@1+package com.peachpasswords.autofill2+3+import org.junit.Assert.assertEquals4+import org.junit.Assert.assertTrue5+import org.junit.Test6+7+/**8+ * Regression pin for the 2026-08-07 Fold 7 crash loop: Facebook Messenger9+ * presented an autofillHints array containing a null entry and classify()10+ * dereferenced it, crashing the autofill service on every fill request11+ * (One UI then surfaces the repeated "app keeps stopping" dialog). Hint12+ * arrays are third-party input and must never crash the parser.13+ */14+class AssistStructureNullHintsTest {15+16+    @Test fun null_hint_entries_are_dropped() {17+        assertEquals(18+            listOf("password", "username"),19+            AssistStructureParser.nonNullHints(arrayOf(null, "password", null, "username")),20+        )21+    }22+23+    @Test fun all_null_array_is_empty() {24+        assertTrue(AssistStructureParser.nonNullHints(arrayOf<String?>(null, null)).isEmpty())25+    }26+27+    @Test fun absent_hints_is_empty() {28+        assertTrue(AssistStructureParser.nonNullHints(null).isEmpty())29+    }30+}

view file