2016-08-27 01:56:09 +03:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using Android.AccessibilityServices;
|
|
|
|
|
using Android.App;
|
|
|
|
|
using Android.Content;
|
|
|
|
|
using Android.OS;
|
|
|
|
|
using Android.Views.Accessibility;
|
2017-05-29 18:38:03 +03:00
|
|
|
|
using Bit.App.Abstractions;
|
|
|
|
|
using XLabs.Ioc;
|
2017-11-21 21:26:06 +03:00
|
|
|
|
using Bit.App.Resources;
|
2016-08-27 01:56:09 +03:00
|
|
|
|
|
|
|
|
|
namespace Bit.Android
|
|
|
|
|
{
|
2017-09-19 22:55:15 +03:00
|
|
|
|
[Service(Permission = global::Android.Manifest.Permission.BindAccessibilityService, Label = "bitwarden")]
|
2017-01-24 07:32:52 +03:00
|
|
|
|
[IntentFilter(new string[] { "android.accessibilityservice.AccessibilityService" })]
|
|
|
|
|
[MetaData("android.accessibilityservice", Resource = "@xml/accessibilityservice")]
|
2016-08-27 01:56:09 +03:00
|
|
|
|
public class AutofillService : AccessibilityService
|
|
|
|
|
{
|
2017-11-21 21:26:06 +03:00
|
|
|
|
private NotificationChannel _notificationChannel;
|
|
|
|
|
|
2018-02-10 17:35:14 +03:00
|
|
|
|
private const string BitwardenTag = "bw_access";
|
2017-01-24 07:32:52 +03:00
|
|
|
|
private const int AutoFillNotificationId = 34573;
|
2018-02-02 01:01:37 +03:00
|
|
|
|
private const string SystemUiPackage = "com.android.systemui";
|
2017-01-31 03:26:39 +03:00
|
|
|
|
private const string BitwardenPackage = "com.x8bit.bitwarden";
|
2017-02-01 04:45:51 +03:00
|
|
|
|
private const string BitwardenWebsite = "bitwarden.com";
|
2016-12-23 06:37:16 +03:00
|
|
|
|
|
2018-02-01 23:21:22 +03:00
|
|
|
|
private static Dictionary<string, Browser> SupportedBrowsers => new List<Browser>
|
2017-02-03 03:39:00 +03:00
|
|
|
|
{
|
2017-02-17 05:14:02 +03:00
|
|
|
|
new Browser("com.android.chrome", "url_bar"),
|
|
|
|
|
new Browser("com.chrome.beta", "url_bar"),
|
|
|
|
|
new Browser("com.android.browser", "url"),
|
|
|
|
|
new Browser("com.brave.browser", "url_bar"),
|
|
|
|
|
new Browser("com.opera.browser", "url_field"),
|
|
|
|
|
new Browser("com.opera.browser.beta", "url_field"),
|
|
|
|
|
new Browser("com.opera.mini.native", "url_field"),
|
|
|
|
|
new Browser("com.chrome.dev", "url_bar"),
|
|
|
|
|
new Browser("com.chrome.canary", "url_bar"),
|
|
|
|
|
new Browser("com.google.android.apps.chrome", "url_bar"),
|
|
|
|
|
new Browser("com.google.android.apps.chrome_dev", "url_bar"),
|
2017-05-29 15:35:36 +03:00
|
|
|
|
new Browser("org.codeaurora.swe.browser", "url_bar"),
|
2017-02-17 05:14:02 +03:00
|
|
|
|
new Browser("org.iron.srware", "url_bar"),
|
2017-07-23 06:31:38 +03:00
|
|
|
|
new Browser("com.sec.android.app.sbrowser", "location_bar_edit_text"),
|
2017-03-10 04:22:06 +03:00
|
|
|
|
new Browser("com.sec.android.app.sbrowser.beta", "location_bar_edit_text"),
|
2017-02-17 05:14:02 +03:00
|
|
|
|
new Browser("com.yandex.browser", "bro_omnibar_address_title_text",
|
2018-01-25 17:07:19 +03:00
|
|
|
|
(s) => s.Split(new char[]{' ', '<27>'}).FirstOrDefault()), // 0 = Regular Space, 1 = No-break space (00A0)
|
2017-02-17 05:14:02 +03:00
|
|
|
|
new Browser("org.mozilla.firefox", "url_bar_title"),
|
|
|
|
|
new Browser("org.mozilla.firefox_beta", "url_bar_title"),
|
2017-07-31 18:30:07 +03:00
|
|
|
|
new Browser("org.mozilla.focus", "display_url"),
|
2018-01-09 19:31:00 +03:00
|
|
|
|
new Browser("org.mozilla.klar", "display_url"),
|
2017-02-17 05:14:02 +03:00
|
|
|
|
new Browser("com.ghostery.android.ghostery", "search_field"),
|
|
|
|
|
new Browser("org.adblockplus.browser", "url_bar_title"),
|
|
|
|
|
new Browser("com.htc.sense.browser", "title"),
|
|
|
|
|
new Browser("com.amazon.cloud9", "url"),
|
|
|
|
|
new Browser("mobi.mgeek.TunnyBrowser", "title"),
|
|
|
|
|
new Browser("com.nubelacorp.javelin", "enterUrl"),
|
|
|
|
|
new Browser("com.jerky.browser2", "enterUrl"),
|
|
|
|
|
new Browser("com.mx.browser", "address_editor_with_progress"),
|
|
|
|
|
new Browser("com.mx.browser.tablet", "address_editor_with_progress"),
|
2017-02-17 07:09:40 +03:00
|
|
|
|
new Browser("com.linkbubble.playstore", "url_text"),
|
2017-07-31 18:30:07 +03:00
|
|
|
|
new Browser("com.ksmobile.cb", "address_bar_edit_text"),
|
|
|
|
|
new Browser("acr.browser.lightning", "search"),
|
2017-10-13 05:53:41 +03:00
|
|
|
|
new Browser("acr.browser.barebones", "search"),
|
2018-01-26 16:56:43 +03:00
|
|
|
|
new Browser("com.microsoft.emmx", "url_bar"),
|
|
|
|
|
new Browser("com.duckduckgo.mobile.android", "omnibarTextInput")
|
2018-02-01 23:21:22 +03:00
|
|
|
|
}.ToDictionary(n => n.PackageName);
|
2017-02-01 08:38:35 +03:00
|
|
|
|
|
2018-02-02 05:20:28 +03:00
|
|
|
|
// Known packages to skip
|
2018-02-02 00:56:54 +03:00
|
|
|
|
private static HashSet<string> FilteredPackageNames => new HashSet<string>
|
|
|
|
|
{
|
2018-02-02 01:17:18 +03:00
|
|
|
|
SystemUiPackage,
|
2018-02-02 00:56:54 +03:00
|
|
|
|
"com.google.android.googlequicksearchbox",
|
|
|
|
|
"com.google.android.apps.nexuslauncher",
|
|
|
|
|
"com.google.android.launcher",
|
2018-02-02 01:17:18 +03:00
|
|
|
|
"com.computer.desktop.ui.launcher",
|
|
|
|
|
"com.launcher.notelauncher",
|
|
|
|
|
"com.anddoes.launcher",
|
|
|
|
|
"com.actionlauncher.playstore",
|
2018-02-02 01:23:41 +03:00
|
|
|
|
"ch.deletescape.lawnchair.plah",
|
|
|
|
|
"com.microsoft.launcher",
|
|
|
|
|
"com.teslacoilsw.launcher",
|
2018-02-02 01:26:41 +03:00
|
|
|
|
"com.teslacoilsw.launcher.prime",
|
2018-02-02 01:23:41 +03:00
|
|
|
|
"is.shortcut",
|
|
|
|
|
"me.craftsapp.nlauncher",
|
|
|
|
|
"com.ss.squarehome2"
|
2018-02-02 00:56:54 +03:00
|
|
|
|
};
|
|
|
|
|
|
2017-05-29 18:38:03 +03:00
|
|
|
|
private readonly IAppSettingsService _appSettings;
|
2017-05-30 15:18:56 +03:00
|
|
|
|
private long _lastNotificationTime = 0;
|
|
|
|
|
private string _lastNotificationUri = null;
|
2018-02-02 05:20:28 +03:00
|
|
|
|
private HashSet<string> _launcherPackageNames = null;
|
|
|
|
|
private DateTime? _lastLauncherSetBuilt = null;
|
|
|
|
|
private TimeSpan _rebuildLauncherSpan = TimeSpan.FromHours(1);
|
2017-05-29 18:38:03 +03:00
|
|
|
|
|
|
|
|
|
public AutofillService()
|
|
|
|
|
{
|
2018-02-01 23:21:22 +03:00
|
|
|
|
_appSettings = Resolver.Resolve<IAppSettingsService>();
|
2017-05-29 18:38:03 +03:00
|
|
|
|
}
|
|
|
|
|
|
2016-08-27 01:56:09 +03:00
|
|
|
|
public override void OnAccessibilityEvent(AccessibilityEvent e)
|
|
|
|
|
{
|
2017-08-15 19:28:48 +03:00
|
|
|
|
var powerManager = (PowerManager)GetSystemService(PowerService);
|
2017-09-08 16:16:21 +03:00
|
|
|
|
if(Build.VERSION.SdkInt > BuildVersionCodes.KitkatWatch && !powerManager.IsInteractive)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if(Build.VERSION.SdkInt < BuildVersionCodes.Lollipop && !powerManager.IsScreenOn)
|
2017-08-15 19:28:48 +03:00
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-02-01 23:54:39 +03:00
|
|
|
|
|
2017-02-26 00:10:18 +03:00
|
|
|
|
try
|
2017-01-24 07:32:52 +03:00
|
|
|
|
{
|
2018-02-02 05:20:28 +03:00
|
|
|
|
if(SkipPackage(e?.PackageName))
|
2018-02-02 00:12:30 +03:00
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-02-01 23:42:07 +03:00
|
|
|
|
|
2018-02-02 00:12:30 +03:00
|
|
|
|
var root = RootInActiveWindow;
|
|
|
|
|
if(root == null || root.PackageName != e.PackageName)
|
2017-02-26 00:10:18 +03:00
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-02-17 07:09:40 +03:00
|
|
|
|
|
2018-02-01 22:31:53 +03:00
|
|
|
|
//var testNodes = GetWindowNodes(root, e, n => n.ViewIdResourceName != null && n.Text != null, false);
|
|
|
|
|
//var testNodesData = testNodes.Select(n => new { id = n.ViewIdResourceName, text = n.Text });
|
|
|
|
|
//testNodes.Dispose();
|
2017-02-09 02:19:59 +03:00
|
|
|
|
|
2017-02-26 00:10:18 +03:00
|
|
|
|
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
|
2017-05-29 18:38:03 +03:00
|
|
|
|
var cancelNotification = true;
|
|
|
|
|
|
2017-02-26 00:10:18 +03:00
|
|
|
|
switch(e.EventType)
|
|
|
|
|
{
|
2017-05-29 18:38:03 +03:00
|
|
|
|
case EventTypes.ViewFocused:
|
2018-02-09 01:07:01 +03:00
|
|
|
|
if(e.Source == null || !e.Source.Password || !_appSettings.AutofillPasswordField)
|
2017-05-29 18:38:03 +03:00
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-02-03 07:36:40 +03:00
|
|
|
|
|
2017-02-26 00:10:18 +03:00
|
|
|
|
if(e.PackageName == BitwardenPackage)
|
2016-12-23 06:37:16 +03:00
|
|
|
|
{
|
2017-05-29 18:38:03 +03:00
|
|
|
|
CancelNotification(notificationManager);
|
2017-02-26 00:10:18 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
2017-02-20 01:29:00 +03:00
|
|
|
|
|
2017-05-29 18:38:03 +03:00
|
|
|
|
if(ScanAndAutofill(root, e, notificationManager, cancelNotification))
|
2017-05-26 06:16:48 +03:00
|
|
|
|
{
|
2017-05-29 18:38:03 +03:00
|
|
|
|
CancelNotification(notificationManager);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case EventTypes.WindowContentChanged:
|
|
|
|
|
case EventTypes.WindowStateChanged:
|
|
|
|
|
if(_appSettings.AutofillPasswordField && e.Source.Password)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-02-03 17:47:16 +03:00
|
|
|
|
else if(_appSettings.AutofillPasswordField && AutofillActivity.LastCredentials == null)
|
2017-05-29 18:38:03 +03:00
|
|
|
|
{
|
2017-05-30 15:18:56 +03:00
|
|
|
|
if(string.IsNullOrWhiteSpace(_lastNotificationUri))
|
|
|
|
|
{
|
|
|
|
|
CancelNotification(notificationManager);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var uri = GetUri(root);
|
|
|
|
|
if(uri != _lastNotificationUri)
|
|
|
|
|
{
|
|
|
|
|
CancelNotification(notificationManager);
|
|
|
|
|
}
|
2017-05-30 15:35:57 +03:00
|
|
|
|
else if(uri.StartsWith(App.Constants.AndroidAppProtocol))
|
|
|
|
|
{
|
2017-05-31 15:29:58 +03:00
|
|
|
|
CancelNotification(notificationManager, 30000);
|
2017-05-30 15:35:57 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
2017-05-26 06:16:48 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-29 18:38:03 +03:00
|
|
|
|
if(e.PackageName == BitwardenPackage)
|
|
|
|
|
{
|
|
|
|
|
CancelNotification(notificationManager);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-05-26 06:16:48 +03:00
|
|
|
|
|
2017-05-29 18:38:03 +03:00
|
|
|
|
if(_appSettings.AutofillPersistNotification)
|
2017-02-26 00:10:18 +03:00
|
|
|
|
{
|
|
|
|
|
var uri = GetUri(root);
|
|
|
|
|
if(uri != null && !uri.Contains(BitwardenWebsite))
|
2017-02-17 05:14:02 +03:00
|
|
|
|
{
|
2017-05-29 18:38:03 +03:00
|
|
|
|
var needToFill = NeedToAutofill(AutofillActivity.LastCredentials, uri);
|
|
|
|
|
if(needToFill)
|
2017-02-26 00:10:18 +03:00
|
|
|
|
{
|
2017-05-29 18:38:03 +03:00
|
|
|
|
var passwordNodes = GetWindowNodes(root, e, n => n.Password, false);
|
|
|
|
|
needToFill = passwordNodes.Any();
|
|
|
|
|
if(needToFill)
|
|
|
|
|
{
|
|
|
|
|
var allEditTexts = GetWindowNodes(root, e, n => EditText(n), false);
|
|
|
|
|
var usernameEditText = allEditTexts.TakeWhile(n => !n.Password).LastOrDefault();
|
|
|
|
|
FillCredentials(usernameEditText, passwordNodes);
|
|
|
|
|
|
|
|
|
|
allEditTexts.Dispose();
|
|
|
|
|
usernameEditText.Dispose();
|
|
|
|
|
}
|
2017-05-30 15:18:56 +03:00
|
|
|
|
passwordNodes.Dispose();
|
2017-02-26 00:10:18 +03:00
|
|
|
|
}
|
2017-05-29 18:38:03 +03:00
|
|
|
|
|
|
|
|
|
if(!needToFill)
|
2017-02-26 00:10:18 +03:00
|
|
|
|
{
|
|
|
|
|
NotifyToAutofill(uri, notificationManager);
|
|
|
|
|
cancelNotification = false;
|
|
|
|
|
}
|
2017-02-17 05:14:02 +03:00
|
|
|
|
}
|
2017-02-26 00:10:18 +03:00
|
|
|
|
|
|
|
|
|
AutofillActivity.LastCredentials = null;
|
2016-12-23 06:37:16 +03:00
|
|
|
|
}
|
2017-05-29 18:38:03 +03:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
cancelNotification = ScanAndAutofill(root, e, notificationManager, cancelNotification);
|
|
|
|
|
}
|
2017-01-24 07:32:52 +03:00
|
|
|
|
|
2017-02-26 00:10:18 +03:00
|
|
|
|
if(cancelNotification)
|
|
|
|
|
{
|
2017-05-29 18:38:03 +03:00
|
|
|
|
CancelNotification(notificationManager);
|
2017-02-26 00:10:18 +03:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-02-21 02:22:24 +03:00
|
|
|
|
|
2017-02-26 00:10:18 +03:00
|
|
|
|
notificationManager?.Dispose();
|
|
|
|
|
root.Dispose();
|
|
|
|
|
e.Dispose();
|
2016-08-27 01:56:09 +03:00
|
|
|
|
}
|
2017-05-20 18:29:20 +03:00
|
|
|
|
// Suppress exceptions so that service doesn't crash
|
2018-02-09 22:02:17 +03:00
|
|
|
|
catch { }
|
2016-08-27 01:56:09 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void OnInterrupt()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
2016-12-23 06:37:16 +03:00
|
|
|
|
|
2017-05-29 18:38:03 +03:00
|
|
|
|
public bool ScanAndAutofill(AccessibilityNodeInfo root, AccessibilityEvent e,
|
|
|
|
|
NotificationManager notificationManager, bool cancelNotification)
|
|
|
|
|
{
|
|
|
|
|
var passwordNodes = GetWindowNodes(root, e, n => n.Password, false);
|
|
|
|
|
if(passwordNodes.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
var uri = GetUri(root);
|
|
|
|
|
if(uri != null && !uri.Contains(BitwardenWebsite))
|
|
|
|
|
{
|
|
|
|
|
if(NeedToAutofill(AutofillActivity.LastCredentials, uri))
|
|
|
|
|
{
|
|
|
|
|
var allEditTexts = GetWindowNodes(root, e, n => EditText(n), false);
|
|
|
|
|
var usernameEditText = allEditTexts.TakeWhile(n => !n.Password).LastOrDefault();
|
|
|
|
|
FillCredentials(usernameEditText, passwordNodes);
|
|
|
|
|
|
|
|
|
|
allEditTexts.Dispose();
|
|
|
|
|
usernameEditText.Dispose();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NotifyToAutofill(uri, notificationManager);
|
|
|
|
|
cancelNotification = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-08-10 04:40:59 +03:00
|
|
|
|
|
|
|
|
|
AutofillActivity.LastCredentials = null;
|
|
|
|
|
}
|
|
|
|
|
else if(AutofillActivity.LastCredentials != null)
|
|
|
|
|
{
|
|
|
|
|
System.Threading.Tasks.Task.Run(async () =>
|
|
|
|
|
{
|
|
|
|
|
await System.Threading.Tasks.Task.Delay(1000);
|
|
|
|
|
AutofillActivity.LastCredentials = null;
|
|
|
|
|
});
|
2017-05-29 18:38:03 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
passwordNodes.Dispose();
|
|
|
|
|
return cancelNotification;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-30 15:35:57 +03:00
|
|
|
|
public void CancelNotification(NotificationManager notificationManager, long limit = 250)
|
2017-05-29 18:38:03 +03:00
|
|
|
|
{
|
2017-05-30 15:35:57 +03:00
|
|
|
|
if(Java.Lang.JavaSystem.CurrentTimeMillis() - _lastNotificationTime < limit)
|
2017-05-29 18:38:03 +03:00
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-30 15:18:56 +03:00
|
|
|
|
_lastNotificationUri = null;
|
2017-05-29 18:38:03 +03:00
|
|
|
|
notificationManager?.Cancel(AutoFillNotificationId);
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-01 04:45:51 +03:00
|
|
|
|
private string GetUri(AccessibilityNodeInfo root)
|
|
|
|
|
{
|
|
|
|
|
var uri = string.Concat(App.Constants.AndroidAppProtocol, root.PackageName);
|
2017-02-17 05:14:02 +03:00
|
|
|
|
if(SupportedBrowsers.ContainsKey(root.PackageName))
|
2017-02-01 04:45:51 +03:00
|
|
|
|
{
|
2017-02-17 05:14:02 +03:00
|
|
|
|
var addressNode = root.FindAccessibilityNodeInfosByViewId(
|
|
|
|
|
$"{root.PackageName}:id/{SupportedBrowsers[root.PackageName].UriViewId}").FirstOrDefault();
|
|
|
|
|
if(addressNode != null)
|
2017-02-03 03:39:00 +03:00
|
|
|
|
{
|
2017-02-17 05:14:02 +03:00
|
|
|
|
uri = ExtractUri(uri, addressNode, SupportedBrowsers[root.PackageName]);
|
2017-02-21 02:22:24 +03:00
|
|
|
|
addressNode.Dispose();
|
2017-02-03 03:39:00 +03:00
|
|
|
|
}
|
2017-02-01 04:45:51 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return uri;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-17 05:14:02 +03:00
|
|
|
|
private string ExtractUri(string uri, AccessibilityNodeInfo addressNode, Browser browser)
|
2016-12-23 06:37:16 +03:00
|
|
|
|
{
|
2017-02-15 03:47:00 +03:00
|
|
|
|
if(addressNode?.Text != null)
|
2016-12-23 06:37:16 +03:00
|
|
|
|
{
|
2017-02-18 05:18:59 +03:00
|
|
|
|
uri = browser.GetUriFunction(addressNode.Text).Trim();
|
2017-02-17 05:14:02 +03:00
|
|
|
|
if(uri != null && uri.Contains("."))
|
2017-02-01 06:53:32 +03:00
|
|
|
|
{
|
2017-02-18 05:18:59 +03:00
|
|
|
|
if(!uri.Contains("://") && !uri.Contains(" "))
|
2017-02-01 06:53:32 +03:00
|
|
|
|
{
|
2017-02-17 05:14:02 +03:00
|
|
|
|
uri = string.Concat("http://", uri);
|
|
|
|
|
}
|
|
|
|
|
else if(Build.VERSION.SdkInt <= BuildVersionCodes.KitkatWatch)
|
|
|
|
|
{
|
|
|
|
|
var parts = uri.Split(new string[] { ". " }, StringSplitOptions.None);
|
|
|
|
|
if(parts.Length > 1)
|
2017-02-01 06:53:32 +03:00
|
|
|
|
{
|
2017-02-17 05:14:02 +03:00
|
|
|
|
var urlPart = parts.FirstOrDefault(p => p.StartsWith("http"));
|
|
|
|
|
if(urlPart != null)
|
|
|
|
|
{
|
|
|
|
|
uri = urlPart.Trim();
|
|
|
|
|
}
|
2017-02-01 06:53:32 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-23 06:37:16 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-24 07:32:52 +03:00
|
|
|
|
return uri;
|
2016-12-23 06:37:16 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-31 03:26:39 +03:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Check to make sure it is ok to autofill still on the current screen
|
|
|
|
|
/// </summary>
|
|
|
|
|
private bool NeedToAutofill(AutofillCredentials creds, string currentUriString)
|
2016-12-23 06:37:16 +03:00
|
|
|
|
{
|
2017-01-31 03:26:39 +03:00
|
|
|
|
if(creds == null)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-09 08:20:29 +03:00
|
|
|
|
Uri lastUri, currentUri;
|
|
|
|
|
if(Uri.TryCreate(creds.LastUri, UriKind.Absolute, out lastUri) &&
|
2017-01-31 03:26:39 +03:00
|
|
|
|
Uri.TryCreate(currentUriString, UriKind.Absolute, out currentUri) &&
|
2017-02-09 08:20:29 +03:00
|
|
|
|
lastUri.Host == currentUri.Host)
|
2017-01-24 07:32:52 +03:00
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
2016-12-23 06:37:16 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-24 07:32:52 +03:00
|
|
|
|
private static bool EditText(AccessibilityNodeInfo n)
|
2016-12-23 06:37:16 +03:00
|
|
|
|
{
|
2017-02-23 03:00:50 +03:00
|
|
|
|
return n?.ClassName?.Contains("EditText") ?? false;
|
2016-12-23 06:37:16 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-21 02:22:24 +03:00
|
|
|
|
private void NotifyToAutofill(string uri, NotificationManager notificationManager)
|
2016-12-23 06:37:16 +03:00
|
|
|
|
{
|
2017-02-23 03:00:50 +03:00
|
|
|
|
if(notificationManager == null || string.IsNullOrWhiteSpace(uri))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-29 18:38:03 +03:00
|
|
|
|
var now = Java.Lang.JavaSystem.CurrentTimeMillis();
|
2017-01-24 07:32:52 +03:00
|
|
|
|
var intent = new Intent(this, typeof(AutofillActivity));
|
|
|
|
|
intent.PutExtra("uri", uri);
|
|
|
|
|
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
|
|
|
|
|
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.UpdateCurrent);
|
2016-12-23 06:37:16 +03:00
|
|
|
|
|
2017-09-08 16:16:21 +03:00
|
|
|
|
var notificationContent = Build.VERSION.SdkInt > BuildVersionCodes.KitkatWatch ?
|
2017-11-22 01:52:23 +03:00
|
|
|
|
AppResources.BitwardenAutofillServiceNotificationContent :
|
|
|
|
|
AppResources.BitwardenAutofillServiceNotificationContentOld;
|
2017-09-08 16:16:21 +03:00
|
|
|
|
|
2016-12-23 06:37:16 +03:00
|
|
|
|
var builder = new Notification.Builder(this);
|
2017-01-29 07:58:26 +03:00
|
|
|
|
builder.SetSmallIcon(Resource.Drawable.notification_sm)
|
2017-11-22 01:52:23 +03:00
|
|
|
|
.SetContentTitle(AppResources.BitwardenAutofillService)
|
2017-09-08 16:16:21 +03:00
|
|
|
|
.SetContentText(notificationContent)
|
|
|
|
|
.SetTicker(notificationContent)
|
2017-05-29 18:38:03 +03:00
|
|
|
|
.SetWhen(now)
|
2017-01-24 07:32:52 +03:00
|
|
|
|
.SetContentIntent(pendingIntent);
|
2017-01-29 07:58:26 +03:00
|
|
|
|
|
2017-02-01 04:45:51 +03:00
|
|
|
|
if(Build.VERSION.SdkInt > BuildVersionCodes.KitkatWatch)
|
|
|
|
|
{
|
|
|
|
|
builder.SetVisibility(NotificationVisibility.Secret)
|
|
|
|
|
.SetColor(global::Android.Support.V4.Content.ContextCompat.GetColor(ApplicationContext,
|
|
|
|
|
Resource.Color.primary));
|
|
|
|
|
}
|
2017-11-21 21:26:06 +03:00
|
|
|
|
|
|
|
|
|
if(Build.VERSION.SdkInt >= BuildVersionCodes.O)
|
|
|
|
|
{
|
|
|
|
|
if(_notificationChannel == null)
|
|
|
|
|
{
|
|
|
|
|
_notificationChannel = new NotificationChannel("bitwarden_autofill_service",
|
2017-11-22 01:52:23 +03:00
|
|
|
|
AppResources.AutofillService, NotificationImportance.Low);
|
2017-11-21 21:26:06 +03:00
|
|
|
|
notificationManager.CreateNotificationChannel(_notificationChannel);
|
|
|
|
|
}
|
|
|
|
|
builder.SetChannelId(_notificationChannel.Id);
|
|
|
|
|
}
|
2017-02-01 04:45:51 +03:00
|
|
|
|
|
2017-05-30 04:52:12 +03:00
|
|
|
|
if(/*Build.VERSION.SdkInt <= BuildVersionCodes.N && */_appSettings.AutofillPersistNotification)
|
2017-05-29 15:35:36 +03:00
|
|
|
|
{
|
2017-05-30 16:24:29 +03:00
|
|
|
|
builder.SetPriority(-2);
|
2017-05-29 15:35:36 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-30 15:18:56 +03:00
|
|
|
|
_lastNotificationTime = now;
|
|
|
|
|
_lastNotificationUri = uri;
|
2017-01-24 07:32:52 +03:00
|
|
|
|
notificationManager.Notify(AutoFillNotificationId, builder.Build());
|
2017-02-21 02:22:24 +03:00
|
|
|
|
|
|
|
|
|
builder.Dispose();
|
2016-12-23 06:37:16 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-24 07:32:52 +03:00
|
|
|
|
private void FillCredentials(AccessibilityNodeInfo usernameNode, IEnumerable<AccessibilityNodeInfo> passwordNodes)
|
2016-12-23 06:37:16 +03:00
|
|
|
|
{
|
2017-02-23 03:00:50 +03:00
|
|
|
|
FillEditText(usernameNode, AutofillActivity.LastCredentials?.Username);
|
2017-01-31 03:26:39 +03:00
|
|
|
|
foreach(var n in passwordNodes)
|
2017-01-24 07:32:52 +03:00
|
|
|
|
{
|
2017-02-23 03:00:50 +03:00
|
|
|
|
FillEditText(n, AutofillActivity.LastCredentials?.Password);
|
2017-01-24 07:32:52 +03:00
|
|
|
|
}
|
2016-12-23 06:37:16 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-24 07:32:52 +03:00
|
|
|
|
private static void FillEditText(AccessibilityNodeInfo editTextNode, string value)
|
2016-12-23 06:37:16 +03:00
|
|
|
|
{
|
2017-01-31 03:26:39 +03:00
|
|
|
|
if(editTextNode == null || value == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-24 07:32:52 +03:00
|
|
|
|
var bundle = new Bundle();
|
|
|
|
|
bundle.PutString(AccessibilityNodeInfo.ActionArgumentSetTextCharsequence, value);
|
|
|
|
|
editTextNode.PerformAction(global::Android.Views.Accessibility.Action.SetText, bundle);
|
2016-12-23 06:37:16 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-21 02:22:24 +03:00
|
|
|
|
private NodeList GetWindowNodes(AccessibilityNodeInfo n, AccessibilityEvent e,
|
2018-02-09 23:00:54 +03:00
|
|
|
|
Func<AccessibilityNodeInfo, bool> condition, bool disposeIfUnused, NodeList nodes = null,
|
2018-02-10 17:35:14 +03:00
|
|
|
|
int recursionDepth = 0)
|
2016-12-23 06:37:16 +03:00
|
|
|
|
{
|
2017-02-18 18:50:27 +03:00
|
|
|
|
if(nodes == null)
|
|
|
|
|
{
|
2017-02-21 02:22:24 +03:00
|
|
|
|
nodes = new NodeList();
|
2017-02-18 18:50:27 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-10 17:35:14 +03:00
|
|
|
|
var dispose = disposeIfUnused;
|
|
|
|
|
if(n != null && recursionDepth < 50)
|
2016-12-23 06:37:16 +03:00
|
|
|
|
{
|
2017-02-17 06:22:19 +03:00
|
|
|
|
if(n.WindowId == e.WindowId && !(n.ViewIdResourceName?.StartsWith(SystemUiPackage) ?? false) && condition(n))
|
2016-12-23 06:37:16 +03:00
|
|
|
|
{
|
2017-02-21 02:22:24 +03:00
|
|
|
|
dispose = false;
|
2017-02-18 18:50:27 +03:00
|
|
|
|
nodes.Add(n);
|
2016-12-23 06:37:16 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-18 23:48:24 +03:00
|
|
|
|
for(var i = 0; i < n.ChildCount; i++)
|
2016-12-23 06:37:16 +03:00
|
|
|
|
{
|
2018-02-09 23:00:54 +03:00
|
|
|
|
var childNode = n.GetChild(i);
|
2018-02-10 17:35:14 +03:00
|
|
|
|
if(i > 100)
|
|
|
|
|
{
|
|
|
|
|
global::Android.Util.Log.Info(BitwardenTag, "Too many child iterations.");
|
|
|
|
|
}
|
|
|
|
|
else if(childNode.GetHashCode() == n.GetHashCode())
|
|
|
|
|
{
|
|
|
|
|
global::Android.Util.Log.Info(BitwardenTag,
|
|
|
|
|
"Child node is the same as parent for some reason.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GetWindowNodes(childNode, e, condition, true, nodes, recursionDepth++);
|
|
|
|
|
}
|
2017-02-21 02:22:24 +03:00
|
|
|
|
}
|
2018-02-10 17:35:14 +03:00
|
|
|
|
}
|
2017-02-21 02:22:24 +03:00
|
|
|
|
|
2018-02-10 17:35:14 +03:00
|
|
|
|
if(dispose)
|
|
|
|
|
{
|
|
|
|
|
n?.Dispose();
|
2016-12-23 06:37:16 +03:00
|
|
|
|
}
|
2017-02-18 18:50:27 +03:00
|
|
|
|
|
|
|
|
|
return nodes;
|
2016-12-23 06:37:16 +03:00
|
|
|
|
}
|
2017-02-17 05:14:02 +03:00
|
|
|
|
|
2018-02-02 05:20:28 +03:00
|
|
|
|
private bool SkipPackage(string eventPackageName)
|
|
|
|
|
{
|
|
|
|
|
if(string.IsNullOrWhiteSpace(eventPackageName) || FilteredPackageNames.Contains(eventPackageName)
|
|
|
|
|
|| eventPackageName.Contains("launcher"))
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(_launcherPackageNames == null || _lastLauncherSetBuilt == null ||
|
|
|
|
|
(DateTime.Now - _lastLauncherSetBuilt.Value) > _rebuildLauncherSpan)
|
|
|
|
|
{
|
|
|
|
|
// refresh launcher list every now and then
|
|
|
|
|
_lastLauncherSetBuilt = DateTime.Now;
|
|
|
|
|
var intent = new Intent(Intent.ActionMain);
|
|
|
|
|
intent.AddCategory(Intent.CategoryHome);
|
|
|
|
|
var resolveInfo = PackageManager.QueryIntentActivities(intent, 0);
|
|
|
|
|
_launcherPackageNames = resolveInfo.Select(ri => ri.ActivityInfo.PackageName).ToHashSet();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return _launcherPackageNames.Contains(eventPackageName);
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-17 05:14:02 +03:00
|
|
|
|
public class Browser
|
|
|
|
|
{
|
|
|
|
|
public Browser(string packageName, string uriViewId)
|
|
|
|
|
{
|
|
|
|
|
PackageName = packageName;
|
|
|
|
|
UriViewId = uriViewId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Browser(string packageName, string uriViewId, Func<string, string> getUriFunction)
|
|
|
|
|
: this(packageName, uriViewId)
|
|
|
|
|
{
|
|
|
|
|
GetUriFunction = getUriFunction;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string PackageName { get; set; }
|
|
|
|
|
public string UriViewId { get; set; }
|
|
|
|
|
public Func<string, string> GetUriFunction { get; set; } = (s) => s;
|
|
|
|
|
}
|
2017-02-21 02:22:24 +03:00
|
|
|
|
|
|
|
|
|
public class NodeList : List<AccessibilityNodeInfo>, IDisposable
|
|
|
|
|
{
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
foreach(var item in this)
|
|
|
|
|
{
|
|
|
|
|
item.Dispose();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-08-27 01:56:09 +03:00
|
|
|
|
}
|
2017-01-28 07:32:48 +03:00
|
|
|
|
}
|