Chore: Add Autofill logging for debug purposes (#7450)

This commit is contained in:
David Perez authored and GitHub committed 2026-10-07 15:17:53 +00:00
1 parent 1f03415c0a
commit dc8d32fbdd
6 files changed
+135 -7

No files matched your search

@@ -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()
@@ -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..<this.childCount) {
this.getChild(i)?.logAccessibilityNodeInfo(depth = depth + 1)
}
}
@@ -11,6 +11,9 @@ import com.x8bit.bitwarden.data.autofill.model.AutofillPartition
import com.x8bit.bitwarden.data.autofill.model.AutofillRequest
import com.x8bit.bitwarden.data.autofill.model.AutofillView
import com.x8bit.bitwarden.data.autofill.model.ViewNodeTraversalData
import com.x8bit.bitwarden.data.autofill.parser.util.logViewNode
import com.x8bit.bitwarden.data.autofill.parser.util.logWindowNodeEnd
import com.x8bit.bitwarden.data.autofill.parser.util.logWindowNodeStart
import com.x8bit.bitwarden.data.autofill.util.buildFillAssistViews
import com.x8bit.bitwarden.data.autofill.util.buildPackageNameOrNull
import com.x8bit.bitwarden.data.autofill.util.buildUriOrNull
@@ -86,7 +89,7 @@ private val IDENTITY_FILL_ASSIST_CATEGORIES: List<String> = 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<AutofillView> = 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
@@ -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} -- ")
},
)
}
@@ -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)
}
}
@@ -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)
}
}