PM-12397: Clear accessibility action when on launcher or Bitwarden App (#3947)

This commit is contained in:
David Perez 2024-09-20 09:44:30 -05:00 committed by GitHub
parent 5667a1cfd0
commit d0c2bb5b7e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 101 additions and 20 deletions

View file

@ -10,6 +10,7 @@ import com.x8bit.bitwarden.data.autofill.accessibility.manager.LauncherPackageNa
import com.x8bit.bitwarden.data.autofill.accessibility.model.AccessibilityAction
import com.x8bit.bitwarden.data.autofill.accessibility.parser.AccessibilityParser
import com.x8bit.bitwarden.data.autofill.accessibility.util.fillTextField
import com.x8bit.bitwarden.data.autofill.accessibility.util.isSystemPackage
import com.x8bit.bitwarden.data.autofill.accessibility.util.shouldSkipPackage
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
import com.x8bit.bitwarden.data.autofill.util.createAutofillSelectionIntent
@ -28,10 +29,16 @@ class BitwardenAccessibilityProcessorImpl(
val rootNode = rootAccessibilityNodeInfo ?: return
// Ignore the event when the phone is inactive
if (!powerManager.isInteractive) return
// We skip if the package is not supported
if (rootNode.shouldSkipPackage) return
// We skip any package that is a launcher
if (launcherPackageNameManager.launcherPackages.any { it == rootNode.packageName }) return
// We skip if the system package
if (rootNode.isSystemPackage) return
// We skip any package that is a launcher or unsupported
if (rootNode.shouldSkipPackage ||
launcherPackageNameManager.launcherPackages.any { it == rootNode.packageName }
) {
// Clear the action since this event needs to be ignored completely
accessibilityAutofillManager.accessibilityAction = null
return
}
// Only process the event if the tile was clicked
val accessibilityAction = accessibilityAutofillManager.accessibilityAction ?: return

View file

@ -33,15 +33,19 @@ private val PACKAGE_NAME_BLOCK_LIST: List<String> = listOf(
val AccessibilityNodeInfo.shouldSkipPackage: Boolean
get() {
val packageName = this.packageName.takeUnless { it.isNullOrBlank() } ?: return true
if (packageName == PACKAGE_NAME_SYSTEM_UI) return true
if (packageName.startsWith(prefix = PACKAGE_NAME_BITWARDEN_PREFIX)) return true
if (packageName.contains(other = PACKAGE_NAME_LAUNCHER_PARTIAL, ignoreCase = true)) {
return true
}
if (PACKAGE_NAME_BLOCK_LIST.contains(packageName)) return true
return false
return PACKAGE_NAME_BLOCK_LIST.contains(packageName)
}
/**
* Returns true if the event is from the system UI package.
*/
val AccessibilityNodeInfo.isSystemPackage: Boolean
get() = this.packageName == PACKAGE_NAME_SYSTEM_UI
/**
* Fills the [AccessibilityNodeInfo] text field with the [value] provided.
*/

View file

@ -14,6 +14,7 @@ import com.x8bit.bitwarden.data.autofill.accessibility.model.AccessibilityAction
import com.x8bit.bitwarden.data.autofill.accessibility.model.FillableFields
import com.x8bit.bitwarden.data.autofill.accessibility.parser.AccessibilityParser
import com.x8bit.bitwarden.data.autofill.accessibility.util.fillTextField
import com.x8bit.bitwarden.data.autofill.accessibility.util.isSystemPackage
import com.x8bit.bitwarden.data.autofill.accessibility.util.shouldSkipPackage
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
import com.x8bit.bitwarden.data.autofill.util.createAutofillSelectionIntent
@ -49,7 +50,10 @@ class BitwardenAccessibilityProcessorTest {
@BeforeEach
fun setup() {
mockkStatic(AccessibilityNodeInfo::shouldSkipPackage)
mockkStatic(
AccessibilityNodeInfo::isSystemPackage,
AccessibilityNodeInfo::shouldSkipPackage,
)
mockkStatic(::createAutofillSelectionIntent)
mockkStatic(Toast::class)
every {
@ -61,7 +65,10 @@ class BitwardenAccessibilityProcessorTest {
@AfterEach
fun tearDown() {
unmockkStatic(AccessibilityNodeInfo::shouldSkipPackage)
unmockkStatic(
AccessibilityNodeInfo::isSystemPackage,
AccessibilityNodeInfo::shouldSkipPackage,
)
unmockkStatic(::createAutofillSelectionIntent)
unmockkStatic(Toast::class)
}
@ -92,9 +99,9 @@ class BitwardenAccessibilityProcessorTest {
}
@Test
fun `processAccessibilityEvent with skippable package should return`() {
fun `processAccessibilityEvent with system package should return`() {
val rootNode = mockk<AccessibilityNodeInfo> {
every { shouldSkipPackage } returns true
every { isSystemPackage } returns true
}
every { powerManager.isInteractive } returns true
@ -104,7 +111,28 @@ class BitwardenAccessibilityProcessorTest {
verify(exactly = 1) {
powerManager.isInteractive
rootNode.isSystemPackage
}
}
@Test
fun `processAccessibilityEvent with skippable package should return`() {
val rootNode = mockk<AccessibilityNodeInfo> {
every { isSystemPackage } returns false
every { shouldSkipPackage } returns true
}
every { powerManager.isInteractive } returns true
every { accessibilityAutofillManager.accessibilityAction = null } just runs
bitwardenAccessibilityProcessor.processAccessibilityEvent(
rootAccessibilityNodeInfo = rootNode,
)
verify(exactly = 1) {
powerManager.isInteractive
rootNode.isSystemPackage
rootNode.shouldSkipPackage
accessibilityAutofillManager.accessibilityAction = null
}
}
@ -112,11 +140,13 @@ class BitwardenAccessibilityProcessorTest {
fun `processAccessibilityEvent with launcher package should return`() {
val testPackageName = "com.google.android.launcher"
val rootNode = mockk<AccessibilityNodeInfo> {
every { isSystemPackage } returns false
every { shouldSkipPackage } returns false
every { packageName } returns testPackageName
}
every { launcherPackageNameManager.launcherPackages } returns listOf(testPackageName)
every { powerManager.isInteractive } returns true
every { accessibilityAutofillManager.accessibilityAction = null } just runs
bitwardenAccessibilityProcessor.processAccessibilityEvent(
rootAccessibilityNodeInfo = rootNode,
@ -124,8 +154,10 @@ class BitwardenAccessibilityProcessorTest {
verify(exactly = 1) {
powerManager.isInteractive
rootNode.isSystemPackage
rootNode.shouldSkipPackage
launcherPackageNameManager.launcherPackages
accessibilityAutofillManager.accessibilityAction = null
}
}
@ -133,6 +165,7 @@ class BitwardenAccessibilityProcessorTest {
fun `processAccessibilityEvent without accessibility action should return`() {
val testPackageName = "com.android.chrome"
val rootNode = mockk<AccessibilityNodeInfo> {
every { isSystemPackage } returns false
every { shouldSkipPackage } returns false
every { packageName } returns testPackageName
}
@ -147,6 +180,7 @@ class BitwardenAccessibilityProcessorTest {
verify(exactly = 1) {
powerManager.isInteractive
rootNode.shouldSkipPackage
rootNode.isSystemPackage
launcherPackageNameManager.launcherPackages
accessibilityAutofillManager.accessibilityAction
}
@ -156,6 +190,7 @@ class BitwardenAccessibilityProcessorTest {
fun `processAccessibilityEvent with AttemptParseUri and a invalid uri should show a toast`() {
val testPackageName = "com.android.chrome"
val rootNode = mockk<AccessibilityNodeInfo> {
every { isSystemPackage } returns false
every { shouldSkipPackage } returns false
every { packageName } returns testPackageName
}
@ -173,6 +208,7 @@ class BitwardenAccessibilityProcessorTest {
verify(exactly = 1) {
powerManager.isInteractive
rootNode.isSystemPackage
rootNode.shouldSkipPackage
launcherPackageNameManager.launcherPackages
accessibilityAutofillManager.accessibilityAction
@ -189,6 +225,7 @@ class BitwardenAccessibilityProcessorTest {
fun `processAccessibilityEvent with AttemptParseUri and a valid uri should start the main activity`() {
val testPackageName = "com.android.chrome"
val rootNode = mockk<AccessibilityNodeInfo> {
every { isSystemPackage } returns false
every { shouldSkipPackage } returns false
every { packageName } returns testPackageName
}
@ -214,6 +251,7 @@ class BitwardenAccessibilityProcessorTest {
verify(exactly = 1) {
powerManager.isInteractive
rootNode.isSystemPackage
rootNode.shouldSkipPackage
launcherPackageNameManager.launcherPackages
accessibilityAutofillManager.accessibilityAction
@ -238,6 +276,7 @@ class BitwardenAccessibilityProcessorTest {
val uri = mockk<Uri>()
val attemptFill = AccessibilityAction.AttemptFill(cipherView = cipherView, uri = uri)
val rootNode = mockk<AccessibilityNodeInfo> {
every { isSystemPackage } returns false
every { shouldSkipPackage } returns false
every { packageName } returns testPackageName
}
@ -252,6 +291,7 @@ class BitwardenAccessibilityProcessorTest {
verify(exactly = 1) {
powerManager.isInteractive
rootNode.isSystemPackage
rootNode.shouldSkipPackage
launcherPackageNameManager.launcherPackages
accessibilityAutofillManager.accessibilityAction
@ -285,6 +325,7 @@ class BitwardenAccessibilityProcessorTest {
val uri = mockk<Uri>()
val attemptFill = AccessibilityAction.AttemptFill(cipherView = cipherView, uri = uri)
val rootNode = mockk<AccessibilityNodeInfo> {
every { isSystemPackage } returns false
every { shouldSkipPackage } returns false
every { packageName } returns testPackageName
}
@ -302,6 +343,7 @@ class BitwardenAccessibilityProcessorTest {
verify(exactly = 1) {
powerManager.isInteractive
rootNode.isSystemPackage
rootNode.shouldSkipPackage
launcherPackageNameManager.launcherPackages
accessibilityAutofillManager.accessibilityAction

View file

@ -105,6 +105,43 @@ class AccessibilityNodeInfoExtensionsTest {
assertTrue(accessibilityNodeInfo.isEditText)
}
@Test
fun `isSystemPackage when packageName is null should return false`() {
val accessibilityNodeInfo = mockk<AccessibilityNodeInfo> {
every { packageName } returns null
}
assertFalse(accessibilityNodeInfo.isSystemPackage)
}
@Test
fun `isSystemPackage when packageName is blank should return false`() {
val accessibilityNodeInfo = mockk<AccessibilityNodeInfo> {
every { packageName } returns ""
}
assertFalse(accessibilityNodeInfo.isSystemPackage)
}
@Suppress("MaxLineLength")
@Test
fun `isSystemPackage when packageName is populated with non system UI package should return false`() {
val accessibilityNodeInfo = mockk<AccessibilityNodeInfo> {
every { packageName } returns "com.x8bit.bitwarden.beta"
}
assertFalse(accessibilityNodeInfo.isSystemPackage)
}
@Test
fun `isSystemPackage when packageName is system UI package should return true`() {
val accessibilityNodeInfo = mockk<AccessibilityNodeInfo> {
every { packageName } returns "com.android.systemui"
}
assertTrue(accessibilityNodeInfo.isSystemPackage)
}
@Test
fun `shouldSkipPackage when packageName is null should return true`() {
val accessibilityNodeInfo = mockk<AccessibilityNodeInfo> {
@ -123,15 +160,6 @@ class AccessibilityNodeInfoExtensionsTest {
assertTrue(accessibilityNodeInfo.shouldSkipPackage)
}
@Test
fun `shouldSkipPackage when packageName is system UI package should return true`() {
val accessibilityNodeInfo = mockk<AccessibilityNodeInfo> {
every { packageName } returns "com.android.systemui"
}
assertTrue(accessibilityNodeInfo.shouldSkipPackage)
}
@Suppress("MaxLineLength")
@Test
fun `shouldSkipPackage when packageName is prefixed with bitwarden package should return true`() {