Use website title from chrome to match with passwords

This commit is contained in:
Matthew Wong 2015-12-27 04:07:11 -05:00
parent ab2b4d699b
commit 94ee36a38d

View file

@ -39,6 +39,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Map;
public class AutofillService extends AccessibilityService { public class AutofillService extends AccessibilityService {
private OpenPgpServiceConnection serviceConnection; 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 static Intent resultData = null; // need the intent which contains results from user interaction
private CharSequence packageName; private CharSequence packageName;
private boolean ignoreActionFocus = false; private boolean ignoreActionFocus = false;
private String webViewTitle = null;
public final class Constants { public final class Constants {
public static final String TAG = "Keychain"; public static final String TAG = "Keychain";
@ -75,12 +77,14 @@ public class AutofillService extends AccessibilityService {
bindDecryptAndVerify(); bindDecryptAndVerify();
} }
// need to see if window has a WebView every time, so future events are sent? // look for webView and trigger accessibility events if window changes
AccessibilityNodeInfo source = event.getSource(); // or if page changes in chrome
if (source == null) { if ((event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
return; || (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 // nothing to do if not password field focus, android version, or field is keychain app
if (!event.isPassword() if (!event.isPassword()
@ -88,7 +92,6 @@ public class AutofillService extends AccessibilityService {
|| Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2 || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2
|| event.getPackageName().equals("org.sufficientlysecure.keychain")) { || event.getPackageName().equals("org.sufficientlysecure.keychain")) {
dismissDialog(event); dismissDialog(event);
source.recycle(); // is this necessary???
return; return;
} }
@ -96,7 +99,6 @@ public class AutofillService extends AccessibilityService {
// the current dialog must belong to this window; ignore clicks on this password field // 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 // why handle clicks at all then? some cases e.g. Paypal there is no initial focus event
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) { if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) {
source.recycle();
return; return;
} }
// if it was not a click, the field was refocused or another field was focused; recreate // 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 // ignore the ACTION_FOCUS from decryptAndVerify otherwise dialog will appear after Fill
if (ignoreActionFocus) { if (ignoreActionFocus) {
ignoreActionFocus = false; ignoreActionFocus = false;
source.recycle();
return; return;
} }
@ -123,48 +124,58 @@ public class AutofillService extends AccessibilityService {
// we are now going to attempt to fill, save AccessibilityNodeInfo for later in decryptAndVerify // 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) // (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 // save the dialog's corresponding window so we can use getWindows() in dismissDialog
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window = info.getWindow(); window = info.getWindow();
} }
// get the app name and find a corresponding password String appName;
PackageManager packageManager = getPackageManager(); if (webViewTitle == null) {
ApplicationInfo applicationInfo; // get the app name and find a corresponding password
try { PackageManager packageManager = getPackageManager();
applicationInfo = packageManager.getApplicationInfo(event.getPackageName().toString(), 0); ApplicationInfo applicationInfo;
} catch (PackageManager.NameNotFoundException e) { try {
applicationInfo = null; applicationInfo = packageManager.getApplicationInfo(event.getPackageName().toString(), 0);
} } catch (PackageManager.NameNotFoundException e) {
final String appName = (applicationInfo != null ? packageManager.getApplicationLabel(applicationInfo) : "").toString(); applicationInfo = null;
}
appName = (applicationInfo != null ? packageManager.getApplicationLabel(applicationInfo) : "").toString();
setMatchingPasswords(appName, info.getPackageName().toString()); setMatchingPasswords(appName, info.getPackageName().toString());
if (items.isEmpty()) {
} else {
appName = webViewTitle;
setMatchingPasswordsWeb(webViewTitle);
}
if (items.isEmpty()) { // show anyway preference?
return; return;
} }
showDialog(appName); showDialog(appName);
} }
private boolean searchWebView(AccessibilityNodeInfo source) { private String searchWebView(AccessibilityNodeInfo source) {
for (int i = 0; i < source.getChildCount(); i++) { for (int i = 0; i < source.getChildCount(); i++) {
AccessibilityNodeInfo u = source.getChild(i); AccessibilityNodeInfo u = source.getChild(i);
if (u == null) { if (u == null) {
continue; continue;
} }
// this is not likely to always work // this is not likely to always work
if (u.getContentDescription() != null && u.getContentDescription().equals("Web View") if (u.getClassName() != null && u.getClassName().equals("android.webkit.WebView")) {
|| u.getClassName() != null && u.getClassName().equals("android.webkit.WebView")) { if (u.getContentDescription() != null)
return true; return u.getContentDescription().toString();
return "";
} }
if (searchWebView(u)) { if (searchWebView(u) != null) {
return true; return searchWebView(u);
} }
u.recycle(); u.recycle();
} }
return false; return null;
} }
// dismiss the dialog if the window has changed // dismiss the dialog if the window has changed
@ -202,20 +213,47 @@ public class AutofillService extends AccessibilityService {
items.clear(); items.clear();
return; return;
default: default:
if (!PasswordRepository.isInitialized()) { getPreferredPasswords(preference);
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));
}
}
} }
} }
private void setMatchingPasswordsWeb(String webViewTitle) {
SharedPreferences prefs = getSharedPreferences("autofill_web", Context.MODE_PRIVATE);
Map<String, ?> 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<File> searchPasswords(File path, String appName) { private ArrayList<File> searchPasswords(File path, String appName) {
ArrayList<File> passList ArrayList<File> passList
= PasswordRepository.getFilesList(path); = PasswordRepository.getFilesList(path);
@ -226,7 +264,7 @@ public class AutofillService extends AccessibilityService {
for (File file : passList) { for (File file : passList) {
if (file.isFile()) { if (file.isFile()) {
if (file.toString().toLowerCase().contains(appName.toLowerCase())) { if (appName.toLowerCase().contains(file.getName().toLowerCase().replace(".gpg", ""))) {
items.add(file); items.add(file);
} }
} else { } else {