Revert "Work around Chrome Autofill issue (#921)" (#933)

This commit is contained in:
Fabian Henneke 2020-07-14 11:30:29 +02:00 committed by GitHub
parent 2f657108b3
commit 681c557e9e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 9 additions and 195 deletions

View file

@ -18,7 +18,6 @@ All notable changes to this project will be documented in this file.
- Fix authentication failure with usernames that contain the `@` character
- Text input boxes were illegible on dark theme
- Top-level password names had inconsistent top margin making them look askew
- Autofill can now be made more reliable in Chrome by enabling an accessibility service that works around known Chrome limitations
- Password Store no longer ignores the selected OpenKeychain key
- Password export now happens in a separate process, preventing possible freezes

View file

@ -99,19 +99,6 @@
android:name="android.accessibilityservice"
android:resource="@xml/autofill_config" />
</service>
<service
android:name=".autofill.oreo.ChromeCompatFix"
android:enabled="@bool/enable_chrome_compat_fix"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/oreo_autofill_chrome_compat_fix" />
</service>
<service
android:name=".ClipboardService"
android:process=":clipboard_service_process" />

View file

@ -41,9 +41,7 @@ import com.github.ajalt.timberkt.w
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import com.zeapo.pwdstore.autofill.AutofillPreferenceActivity
import com.zeapo.pwdstore.autofill.AutofillService
import com.zeapo.pwdstore.autofill.oreo.BrowserAutofillSupportLevel
import com.zeapo.pwdstore.autofill.oreo.ChromeCompatFix
import com.zeapo.pwdstore.autofill.oreo.getInstalledBrowsersWithAutofillSupportLevel
import com.zeapo.pwdstore.crypto.BasePgpActivity
import com.zeapo.pwdstore.crypto.GetKeyIdsActivity
@ -72,7 +70,6 @@ class UserPreference : AppCompatActivity() {
class PrefsFragment : PreferenceFragmentCompat() {
private var autoFillEnablePreference: SwitchPreferenceCompat? = null
private var oreoAutofillChromeCompatFix: SwitchPreferenceCompat? = null
private var clearSavedPassPreference: Preference? = null
private lateinit var autofillDependencies: List<Preference>
private lateinit var oreoAutofillDependencies: List<Preference>
@ -118,7 +115,6 @@ class UserPreference : AppCompatActivity() {
// Autofill preferences
autoFillEnablePreference = findPreference(PreferenceKeys.AUTOFILL_ENABLE)
oreoAutofillChromeCompatFix = findPreference(PreferenceKeys.OREO_AUTOFILL_CHROME_COMPAT_FIX)
val oreoAutofillDirectoryStructurePreference = findPreference<ListPreference>(PreferenceKeys.OREO_AUTOFILL_DIRECTORY_STRUCTURE)
val oreoAutofillDefaultUsername = findPreference<EditTextPreference>(PreferenceKeys.OREO_AUTOFILL_DEFAULT_USERNAME)
val oreoAutofillCustomPublixSuffixes = findPreference<EditTextPreference>(PreferenceKeys.OREO_AUTOFILL_CUSTOM_PUBLIC_SUFFIXES)
@ -277,16 +273,6 @@ class UserPreference : AppCompatActivity() {
true
}
oreoAutofillChromeCompatFix?.onPreferenceClickListener = ClickListener {
if (oreoAutofillChromeCompatFix!!.isChecked) {
startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
true
} else {
// Service will disable itself on startup if the preference has the value false.
false
}
}
findPreference<Preference>(PreferenceKeys.EXPORT_PASSWORDS)?.apply {
isVisible = sharedPreferences.getBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false)
onPreferenceClickListener = Preference.OnPreferenceClickListener {
@ -409,20 +395,16 @@ class UserPreference : AppCompatActivity() {
}
private fun updateAutofillSettings() {
val isAccessibilityAutofillServiceEnabled = callingActivity.isAccessibilityAutofillServiceEnabled
val isAccessibilityServiceEnabled = callingActivity.isAccessibilityServiceEnabled
val isAutofillServiceEnabled = callingActivity.isAutofillServiceEnabled
autoFillEnablePreference?.isChecked =
isAccessibilityAutofillServiceEnabled || isAutofillServiceEnabled
isAccessibilityServiceEnabled || isAutofillServiceEnabled
autofillDependencies.forEach {
it.isVisible = isAccessibilityAutofillServiceEnabled
it.isVisible = isAccessibilityServiceEnabled
}
oreoAutofillDependencies.forEach {
it.isVisible = isAutofillServiceEnabled
}
oreoAutofillChromeCompatFix?.apply {
isChecked = callingActivity.isChromeCompatFixServiceEnabled
isVisible = callingActivity.isChromeCompatFixServiceSupported
}
}
private fun updateClearSavedPassphrasePrefs() {
@ -443,16 +425,13 @@ class UserPreference : AppCompatActivity() {
}
private fun onEnableAutofillClick() {
if (callingActivity.isAccessibilityAutofillServiceEnabled) {
if (callingActivity.isAccessibilityServiceEnabled) {
startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
} else if (callingActivity.isAutofillServiceEnabled) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
callingActivity.autofillManager!!.disableAutofillServices()
ChromeCompatFix.setStatusInPreferences(requireContext(), false)
updateAutofillSettings()
} else {
else
throw IllegalStateException("isAutofillServiceEnabled == true, but Build.VERSION.SDK_INT < Build.VERSION_CODES.O")
}
} else {
val enableOreoAutofill = callingActivity.isAutofillServiceSupported
MaterialAlertDialogBuilder(callingActivity).run {
@ -744,32 +723,14 @@ class UserPreference : AppCompatActivity() {
File("$filesDir/.ssh_key").writeText(lines.joinToString("\n"))
}
private val isAccessibilityAutofillServiceEnabled: Boolean
private val isAccessibilityServiceEnabled: Boolean
get() {
val am = getSystemService<AccessibilityManager>() ?: return false
val runningServices = am
.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC)
return runningServices
.mapNotNull { it?.resolveInfo?.serviceInfo }
.any { it.packageName == BuildConfig.APPLICATION_ID && it.name == AutofillService::class.java.name }
}
private val isChromeCompatFixServiceEnabled: Boolean
get() {
val am = getSystemService<AccessibilityManager>() ?: return false
val runningServices = am
.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC)
return runningServices
.mapNotNull { it?.resolveInfo?.serviceInfo }
.any { it.packageName == BuildConfig.APPLICATION_ID && it.name == ChromeCompatFix::class.java.name }
}
private val isChromeCompatFixServiceSupported: Boolean
get() {
// Autofill compat mode is only available starting with Android Pie and only makes sense
// when used with Autofill enabled.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return false
return isAutofillServiceEnabled
.map { it.id.substringBefore("/") }
.any { it == BuildConfig.APPLICATION_ID }
}
private val isAutofillServiceSupported: Boolean

View file

@ -1,94 +0,0 @@
/*
* Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
*/
package com.zeapo.pwdstore.autofill.oreo
import android.accessibilityservice.AccessibilityService
import android.content.Context
import android.content.SharedPreferences
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.view.accessibility.AccessibilityEvent
import androidx.annotation.RequiresApi
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import com.github.ajalt.timberkt.i
import com.github.ajalt.timberkt.v
import com.github.ajalt.timberkt.w
import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.autofillManager
@RequiresApi(Build.VERSION_CODES.P)
class ChromeCompatFix : AccessibilityService() {
companion object {
fun setStatusInPreferences(context: Context, enabled: Boolean) {
PreferenceManager.getDefaultSharedPreferences(context).edit {
putBoolean(PreferenceKeys.OREO_AUTOFILL_CHROME_COMPAT_FIX, enabled)
}
}
}
private val isEnabledInPreferences
get() = PreferenceManager.getDefaultSharedPreferences(this).getBoolean(PreferenceKeys.OREO_AUTOFILL_CHROME_COMPAT_FIX, true)
private val handler = Handler(Looper.getMainLooper())
private val forceRootNodePopulation = Runnable {
val rootPackageName = rootInActiveWindow?.packageName.toString()
v { "$rootPackageName: forced root node population" }
}
private val disableListener = SharedPreferences.OnSharedPreferenceChangeListener { prefs: SharedPreferences, key: String ->
if (key != PreferenceKeys.OREO_AUTOFILL_CHROME_COMPAT_FIX)
return@OnSharedPreferenceChangeListener
if (!isEnabledInPreferences) {
i { "Disabled in settings, shutting down..." }
disableSelf()
}
}
override fun onAccessibilityEvent(event: AccessibilityEvent) {
handler.removeCallbacks(forceRootNodePopulation)
when (event.eventType) {
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, AccessibilityEvent.TYPE_ANNOUNCEMENT -> {
// WINDOW_STATE_CHANGED: Triggered on long press in a text field, replacement for
// the missing Autofill action menu item.
// ANNOUNCEMENT: Triggered when a password field is selected.
//
// These events are triggered only by user actions and thus don't need to be handled
// with debounce. However, they only trigger Autofill popups on the *next* input
// field selected by the user.
forceRootNodePopulation.run()
v { "${event.packageName} (${AccessibilityEvent.eventTypeToString(event.eventType)}): forced root node population" }
}
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED -> {
// WINDOW_CONTENT_CHANGED: Triggered whenever the page contents change.
//
// This event is triggered many times during page load, which makes a debounce
// necessary to prevent huge performance regressions in Chrome. However, it is the
// only event that reliably runs before the user selects a text field.
handler.postDelayed(forceRootNodePopulation, 300)
v { "${event.packageName} (${AccessibilityEvent.eventTypeToString(event.eventType)}): debounced root node population" }
}
}
}
override fun onServiceConnected() {
super.onServiceConnected()
// Allow the service to be activated only if the Autofill service is already enabled.
if (autofillManager?.hasEnabledAutofillServices() != true) {
w { "Autofill service not enabled, shutting down..." }
disableSelf()
return
}
// Update preferences if the user manually activated the service.
setStatusInPreferences(this, true)
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(disableListener)
}
override fun onInterrupt() {}
}

View file

@ -40,7 +40,6 @@ object PreferenceKeys {
const val OPENPGP_KEY_IDS_SET = "openpgp_key_ids_set"
const val OPENPGP_KEY_ID_PREF = "openpgp_key_id_pref"
const val OPENPGP_PROVIDER_LIST = "openpgp_provider_list"
const val OREO_AUTOFILL_CHROME_COMPAT_FIX = "oreo_autofill_chrome_compat_fix"
const val OREO_AUTOFILL_CUSTOM_PUBLIC_SUFFIXES = "oreo_autofill_custom_public_suffixes"
const val OREO_AUTOFILL_DEFAULT_USERNAME = "oreo_autofill_default_username"
const val OREO_AUTOFILL_DIRECTORY_STRUCTURE = "oreo_autofill_directory_structure"

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
~ SPDX-License-Identifier: GPL-3.0-only
-->
<resources>
<bool name="enable_chrome_compat_fix">true</bool>
</resources>

View file

@ -7,5 +7,4 @@
<bool name="leak_canary_allow_in_non_debuggable_build">true</bool>
<bool name="enable_accessibility_autofill">true</bool>
<bool name="light_status_bar">true</bool>
<bool name="enable_chrome_compat_fix">false</bool>
</resources>

View file

@ -275,16 +275,6 @@
<string name="oreo_autofill_enable_dialog_description">Password Store can offer to fill login forms and even save credentials you enter in apps or on websites.</string>
<string name="oreo_autofill_enable_dialog_instructions">To enable this feature, tap OK to go to Autofill settings. There, select Password Store from the list and confirm the confirmation prompt with OK.</string>
<string name="oreo_autofill_enable_dialog_installed_browsers">Autofill support with installed browsers:</string>
<string name="oreo_autofill_chrome_compat_fix_summary">Make Autofill more reliable in Chrome</string>
<string name="oreo_autofill_chrome_compat_fix_description">This accessibility service makes
Autofill work more reliably in Chrome. It can only be activated if you are already using
Password Store as your Autofill service.\n\nThis service is only active while you are
using Chrome. It does not access any data or take any actions on your behalf, but forces
Chrome to properly forward user interactions to the Password Store Autofill
service.\n\nChrome\'s performance should not be noticeably affected. If you are experiencing
any problems with this service, please create an issue at
https://msfjarvis.dev/aps.
</string>
<!-- Autofill -->
<string name="autofill_description">Autofills password fields in apps. Only works for Android versions 4.3 and up. Does not rely on the clipboard for Android versions 5.0 and up.</string>
@ -399,7 +389,6 @@
<string name="otp_import_success">Successfully imported TOTP configuration</string>
<string name="otp_import_failure">Failed to import TOTP configuration</string>
<string name="oreo_autofill_chrome_compat_fix_preference_title">Improve reliability in Chrome</string>
<string name="oreo_autofill_chrome_compat_fix_preference_summary">Requires activating an accessibility service and may affect overall Chrome performance</string>
<string name="exporting_passwords">Exporting passwords…</string>
<string name="invalid_filename_text">File name must not contain \'/\', set directory above</string>
<string name="directory_hint">Directory</string>

View file

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
~ SPDX-License-Identifier: GPL-3.0-only
-->
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowContentChanged|typeAnnouncement|typeWindowStateChanged"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault"
android:canRetrieveWindowContent="true"
android:description="@string/oreo_autofill_chrome_compat_fix_description"
android:notificationTimeout="100"
android:packageNames="com.android.chrome,com.chrome.beta,com.chrome.dev,com.chrome.canary"
android:summary="@string/oreo_autofill_chrome_compat_fix_summary" />

View file

@ -10,11 +10,6 @@
app:defaultValue="true"
app:key="autofill_enable"
app:title="@string/pref_autofill_enable_title" />
<SwitchPreferenceCompat
app:defaultValue="true"
app:key="oreo_autofill_chrome_compat_fix"
app:summary="@string/oreo_autofill_chrome_compat_fix_preference_summary"
app:title="@string/oreo_autofill_chrome_compat_fix_preference_title" />
<ListPreference
app:defaultValue="file"
app:entries="@array/oreo_autofill_directory_structure_entries"