From dc8d32fbdd514704f88340d179ee5e5072840087 Mon Sep 17 00:00:00 2001 From: David Perez Date: Wed, 7 Oct 2026 15:17:53 +0000 Subject: [PATCH] Chore: Add Autofill logging for debug purposes (#7450) --- .../parser/AccessibilityParserImpl.kt | 4 +- .../accessibility/parser/util/LogUtil.kt | 27 ++++++++ .../autofill/parser/AutofillParserImpl.kt | 21 ++++-- .../data/autofill/parser/util/LogUtil.kt | 68 +++++++++++++++++++ .../accessibility/parser/util/LogUtilsTest.kt | 11 +++ .../data/autofill/parser/util/LogUtilTests.kt | 11 +++ 6 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/accessibility/parser/util/LogUtil.kt create mode 100644 app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/parser/util/LogUtil.kt create mode 100644 app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/accessibility/parser/util/LogUtilsTest.kt create mode 100644 app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/parser/util/LogUtilTests.kt diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/accessibility/parser/AccessibilityParserImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/accessibility/parser/AccessibilityParserImpl.kt index a2fac6be35..912fbe0fec 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/accessibility/parser/AccessibilityParserImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/accessibility/parser/AccessibilityParserImpl.kt @@ -5,6 +5,7 @@ import android.view.accessibility.AccessibilityNodeInfo import androidx.core.net.toUri import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityNodeInfoManager import com.x8bit.bitwarden.data.autofill.accessibility.model.FillableFields +import com.x8bit.bitwarden.data.autofill.accessibility.parser.util.logAccessibilityNodeInfo import com.x8bit.bitwarden.data.autofill.accessibility.util.getSupportedBrowserOrNull import com.x8bit.bitwarden.data.autofill.accessibility.util.isEditText import com.x8bit.bitwarden.data.autofill.accessibility.util.toUriOrNull @@ -13,7 +14,7 @@ import com.x8bit.bitwarden.data.platform.util.hasHttpProtocol /** * The default implementation for the [AccessibilityParser]. */ -class AccessibilityParserImpl( +internal class AccessibilityParserImpl( private val accessibilityNodeInfoManager: AccessibilityNodeInfoManager, ) : AccessibilityParser { override fun parseForFillableFields( @@ -36,6 +37,7 @@ class AccessibilityParserImpl( } override fun parseForUriOrPackageName(rootNode: AccessibilityNodeInfo): Uri? { + rootNode.logAccessibilityNodeInfo(depth = 0) val packageName = rootNode.packageName.toString() val browser = packageName .getSupportedBrowserOrNull() diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/accessibility/parser/util/LogUtil.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/accessibility/parser/util/LogUtil.kt new file mode 100644 index 0000000000..1670aac130 --- /dev/null +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/accessibility/parser/util/LogUtil.kt @@ -0,0 +1,27 @@ +package com.x8bit.bitwarden.data.autofill.accessibility.parser.util + +import android.view.accessibility.AccessibilityNodeInfo +import androidx.annotation.VisibleForTesting +import com.bitwarden.annotation.OmitFromCoverage +import timber.log.Timber + +/** + * Indicates whether Accessibility Autofill verbose logging is enabled or not. This should be + * `false` unless you are actively debugging. + */ +@VisibleForTesting +internal const val ENABLE_ACCESSIBILITY_LOGS: Boolean = false + +/** + * Logs data about a [AccessibilityNodeInfo] that is currently being parsed. + */ +@OmitFromCoverage +internal fun AccessibilityNodeInfo.logAccessibilityNodeInfo(depth: Int = 0) { + if (!ENABLE_ACCESSIBILITY_LOGS) return + Timber + .tag(tag = "BitwardenAccessibilityAutofill") + .v(message = "${" ".repeat(n = depth)} $this") + for (i in 0.. = listOf( * The default [AutofillParser] implementation for the app. This is a tool for parsing autofill data * from the OS into domain models. */ -class AutofillParserImpl( +internal class AutofillParserImpl( private val settingsRepository: SettingsRepository, private val fillAssistManager: FillAssistManager, private val featureFlagManager: FeatureFlagManager, @@ -316,14 +319,17 @@ private fun AssistStructure.traverse( (0 until windowNodeCount) .map { getWindowNodeAt(it) } .mapNotNull { windowNode -> + windowNode.logWindowNodeStart() windowNode .rootViewNode ?.traverse( parentWebsite = null, isIdentityAutofillEnabled = isIdentityAutofillEnabled, + depth = 1, ) ?.updateForMissingPasswordFields() ?.updateForMissingUsernameFields() + .also { windowNode.logWindowNodeEnd() } } /** @@ -436,6 +442,7 @@ private fun ViewNodeTraversalData.copyAndMapAutofillViews( private fun AssistStructure.ViewNode.traverse( parentWebsite: String?, isIdentityAutofillEnabled: Boolean, + depth: Int, ): ViewNodeTraversalData { // Set up mutable lists for collecting valid AutofillViews and ignorable view ids. val mutableAutofillViewList: MutableList = mutableListOf() @@ -455,10 +462,11 @@ private fun AssistStructure.ViewNode.traverse( // Try converting this `ViewNode` into an `AutofillView`. If a valid instance is returned, add // it to the list. Otherwise, ignore the `AutofillId` associated with this `ViewNode`. - toAutofillView( - parentWebsite = parentWebsite, - isIdentityAutofillEnabled = isIdentityAutofillEnabled, - ) + this + .toAutofillView( + parentWebsite = parentWebsite, + isIdentityAutofillEnabled = isIdentityAutofillEnabled, + ) ?.also { view -> if (view !is AutofillView.Unused) { claimedAutofillIds.add(view.data.autofillId) @@ -482,7 +490,7 @@ private fun AssistStructure.ViewNode.traverse( } } ?: autofillId?.run(mutableIgnoreAutofillIdList::add) - + this.logViewNode(depth = depth) // Recursively traverse all of this view node's children. for (i in 0 until childCount) { // Extract the traversal data from each child view node and add it to the lists. @@ -490,6 +498,7 @@ private fun AssistStructure.ViewNode.traverse( .traverse( parentWebsite = website, isIdentityAutofillEnabled = isIdentityAutofillEnabled, + depth = depth + 1, ) .let { viewNodeTraversalData -> // Ids already claimed by an ancestor's own view (e.g. a container-redirect diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/parser/util/LogUtil.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/parser/util/LogUtil.kt new file mode 100644 index 0000000000..5e3b25b54c --- /dev/null +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/parser/util/LogUtil.kt @@ -0,0 +1,68 @@ +package com.x8bit.bitwarden.data.autofill.parser.util + +import android.app.assist.AssistStructure +import androidx.annotation.VisibleForTesting +import com.bitwarden.annotation.OmitFromCoverage +import com.x8bit.bitwarden.data.autofill.util.website +import timber.log.Timber + +/** + * Indicates whether Autofill verbose logging is enabled or not. This should be `false` unless + * you are actively debugging. + */ +@VisibleForTesting +internal const val ENABLE_ASSIST_STRUCTURE_LOGS: Boolean = false + +/** + * Logs data about a [AssistStructure.WindowNode] that is about to be parsed. + */ +@OmitFromCoverage +internal fun AssistStructure.WindowNode.logWindowNodeStart() { + if (!ENABLE_ASSIST_STRUCTURE_LOGS) return + Timber + .tag(tag = "BitwardenAutofill") + .v( + message = buildString { + append("START :: windowNode: ") + append("${this@logWindowNodeStart.title} -- ") + append("${this@logWindowNodeStart.displayId}") + }, + ) +} + +/** + * Logs data about a [AssistStructure.WindowNode] that has just been parsed. + */ +@OmitFromCoverage +internal fun AssistStructure.WindowNode.logWindowNodeEnd() { + if (!ENABLE_ASSIST_STRUCTURE_LOGS) return + Timber + .tag(tag = "BitwardenAutofill") + .v( + message = buildString { + append("END :: windowNode: ") + append("${this@logWindowNodeEnd.title} -- ") + append("${this@logWindowNodeEnd.displayId}") + }, + ) +} + +/** + * Logs data about a [AssistStructure.ViewNode] that is currently being parsed. + */ +@OmitFromCoverage +internal fun AssistStructure.ViewNode.logViewNode(depth: Int) { + if (!ENABLE_ASSIST_STRUCTURE_LOGS) return + Timber + .tag(tag = "BitwardenAutofill") + .v( + message = buildString { + append("${" ".repeat(n = depth)} ViewNode: ") + append("${this@logViewNode.website} -- ") + append("${this@logViewNode.className} -- ") + append("${this@logViewNode.idEntry} -- ") + append("${this@logViewNode.idPackage} -- ") + append("${this@logViewNode.text} -- ") + }, + ) +} diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/accessibility/parser/util/LogUtilsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/accessibility/parser/util/LogUtilsTest.kt new file mode 100644 index 0000000000..29112f052e --- /dev/null +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/accessibility/parser/util/LogUtilsTest.kt @@ -0,0 +1,11 @@ +package com.x8bit.bitwarden.data.autofill.accessibility.parser.util + +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Test + +class LogUtilsTest { + @Test + fun `accessibility autofill logs must be disabled`() { + assertFalse(ENABLE_ACCESSIBILITY_LOGS) + } +} diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/parser/util/LogUtilTests.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/parser/util/LogUtilTests.kt new file mode 100644 index 0000000000..ae7fe95601 --- /dev/null +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/parser/util/LogUtilTests.kt @@ -0,0 +1,11 @@ +package com.x8bit.bitwarden.data.autofill.parser.util + +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Test + +class LogUtilTests { + @Test + fun `autofill logs must be disabled`() { + assertFalse(ENABLE_ASSIST_STRUCTURE_LOGS) + } +}