From 94ee36a38d3b06422c25613c31b8e01e1ef9c4af Mon Sep 17 00:00:00 2001 From: Matthew Wong Date: Sun, 27 Dec 2015 04:07:11 -0500 Subject: Use website title from chrome to match with passwords --- .../zeapo/pwdstore/autofill/AutofillService.java | 116 ++++++++++++++------- 1 file changed, 77 insertions(+), 39 deletions(-) (limited to 'app') diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.java b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.java index e3ed41e8..6a880ccc 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.java @@ -39,6 +39,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; +import java.util.Map; public class AutofillService extends AccessibilityService { private OpenPgpServiceConnection serviceConnection; @@ -51,6 +52,7 @@ public class AutofillService extends AccessibilityService { private static Intent resultData = null; // need the intent which contains results from user interaction private CharSequence packageName; private boolean ignoreActionFocus = false; + private String webViewTitle = null; public final class Constants { public static final String TAG = "Keychain"; @@ -75,12 +77,14 @@ public class AutofillService extends AccessibilityService { bindDecryptAndVerify(); } - // need to see if window has a WebView every time, so future events are sent? - AccessibilityNodeInfo source = event.getSource(); - if (source == null) { - return; + // look for webView and trigger accessibility events if window changes + // or if page changes in chrome + if ((event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED + || (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED + && event.getSource().getPackageName().equals("com.android.chrome"))) + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + webViewTitle = searchWebView(getRootInActiveWindow()); } - searchWebView(source); // nothing to do if not password field focus, android version, or field is keychain app if (!event.isPassword() @@ -88,7 +92,6 @@ public class AutofillService extends AccessibilityService { || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2 || event.getPackageName().equals("org.sufficientlysecure.keychain")) { dismissDialog(event); - source.recycle(); // is this necessary??? return; } @@ -96,7 +99,6 @@ public class AutofillService extends AccessibilityService { // the current dialog must belong to this window; ignore clicks on this password field // why handle clicks at all then? some cases e.g. Paypal there is no initial focus event if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) { - source.recycle(); return; } // if it was not a click, the field was refocused or another field was focused; recreate @@ -107,7 +109,6 @@ public class AutofillService extends AccessibilityService { // ignore the ACTION_FOCUS from decryptAndVerify otherwise dialog will appear after Fill if (ignoreActionFocus) { ignoreActionFocus = false; - source.recycle(); return; } @@ -123,48 +124,58 @@ public class AutofillService extends AccessibilityService { // we are now going to attempt to fill, save AccessibilityNodeInfo for later in decryptAndVerify // (there should be a proper way to do this, although this seems to work 90% of the time) - info = source; + info = event.getSource(); // save the dialog's corresponding window so we can use getWindows() in dismissDialog if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { window = info.getWindow(); } - // get the app name and find a corresponding password - PackageManager packageManager = getPackageManager(); - ApplicationInfo applicationInfo; - try { - applicationInfo = packageManager.getApplicationInfo(event.getPackageName().toString(), 0); - } catch (PackageManager.NameNotFoundException e) { - applicationInfo = null; + String appName; + if (webViewTitle == null) { + // get the app name and find a corresponding password + PackageManager packageManager = getPackageManager(); + ApplicationInfo applicationInfo; + try { + applicationInfo = packageManager.getApplicationInfo(event.getPackageName().toString(), 0); + } catch (PackageManager.NameNotFoundException e) { + applicationInfo = null; + } + appName = (applicationInfo != null ? packageManager.getApplicationLabel(applicationInfo) : "").toString(); + + setMatchingPasswords(appName, info.getPackageName().toString()); + + } else { + appName = webViewTitle; + + setMatchingPasswordsWeb(webViewTitle); } - final String appName = (applicationInfo != null ? packageManager.getApplicationLabel(applicationInfo) : "").toString(); - setMatchingPasswords(appName, info.getPackageName().toString()); - if (items.isEmpty()) { + if (items.isEmpty()) { // show anyway preference? return; } - showDialog(appName); + } - private boolean searchWebView(AccessibilityNodeInfo source) { + private String searchWebView(AccessibilityNodeInfo source) { for (int i = 0; i < source.getChildCount(); i++) { AccessibilityNodeInfo u = source.getChild(i); if (u == null) { continue; } // this is not likely to always work - if (u.getContentDescription() != null && u.getContentDescription().equals("Web View") - || u.getClassName() != null && u.getClassName().equals("android.webkit.WebView")) { - return true; + if (u.getClassName() != null && u.getClassName().equals("android.webkit.WebView")) { + if (u.getContentDescription() != null) + return u.getContentDescription().toString(); + return ""; } - if (searchWebView(u)) { - return true; + if (searchWebView(u) != null) { + return searchWebView(u); } u.recycle(); } - return false; + return null; } // dismiss the dialog if the window has changed @@ -202,20 +213,47 @@ public class AutofillService extends AccessibilityService { items.clear(); return; default: - if (!PasswordRepository.isInitialized()) { - PasswordRepository.initialize(this); - } - String preferred[] = preference.split("\n"); - items = new ArrayList<>(); - for (String prefer : preferred) { - String path = PasswordRepository.getWorkTree() + "/" + prefer + ".gpg"; - if (new File(path).exists()) { - items.add(new File(path)); - } - } + getPreferredPasswords(preference); } } + private void setMatchingPasswordsWeb(String webViewTitle) { + SharedPreferences prefs = getSharedPreferences("autofill_web", Context.MODE_PRIVATE); + Map prefsMap = prefs.getAll(); + for (String key : prefsMap.keySet()) { + if (webViewTitle.toLowerCase().contains(key.toLowerCase())) { + getPreferredPasswords(prefs.getString(key, "")); + return; + } + } + // possible user-defined match not found, try default setting + if (settings.getBoolean("autofill_default", true)) { + if (!PasswordRepository.isInitialized()) { + PasswordRepository.initialize(this); + } + items = searchPasswords(PasswordRepository.getRepositoryDirectory(this), webViewTitle); + } else { + items.clear(); + } + } + + // Put the newline separated list of passwords from the SharedPreferences + // file into the items list. + private void getPreferredPasswords(String preference) { + if (!PasswordRepository.isInitialized()) { + PasswordRepository.initialize(this); + } + String preferredPasswords[] = preference.split("\n"); + items = new ArrayList<>(); + for (String password : preferredPasswords) { + String path = PasswordRepository.getWorkTree() + "/" + password + ".gpg"; + if (new File(path).exists()) { + items.add(new File(path)); + } + } + } + + private ArrayList searchPasswords(File path, String appName) { ArrayList passList = PasswordRepository.getFilesList(path); @@ -226,7 +264,7 @@ public class AutofillService extends AccessibilityService { for (File file : passList) { if (file.isFile()) { - if (file.toString().toLowerCase().contains(appName.toLowerCase())) { + if (appName.toLowerCase().contains(file.getName().toLowerCase().replace(".gpg", ""))) { items.add(file); } } else { -- cgit v1.2.3