LinkScannerService: don't repeatedly scan

- have the system only send 10 events a second, instead of 1000
- don't scan the same package+class name more than once a second
- put each event view scanner on its own thread keyed by package+class
- don't scan if there is already an existing thread for the package+class

this can still cause high cpu usage on views with lots of text
especially if the user continually interacts with it
but is overall much better

to recap:
- a given package+class can only be scanned at most once a second
- a given package+class can only have one scanner at a time
- text to scan will not be scanned if already scanned
- domains to scan will not be scanned if already scanned

Signed-off-by: Tavi <tavi@divested.dev>
This commit is contained in:
Tavi 2024-05-25 18:43:58 -04:00
parent 7dfa3a7267
commit 52b79bf526
No known key found for this signature in database
GPG key ID: E599F62ECBAEAF2E
5 changed files with 39 additions and 4 deletions

View file

@ -5,6 +5,9 @@
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
<SelectionState runConfigName="MainActivity">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>

View file

@ -6,8 +6,8 @@ android {
applicationId "us.spotco.malwarescanner"
minSdkVersion 16
targetSdkVersion 32
versionCode 311
versionName "3.11"
versionCode 312
versionName "3.12"
resConfigs 'en', 'af', 'cs', 'de', 'el', 'es', 'fi', 'fr', 'gl', 'it', 'pl', 'pt', 'pt-rBR', 'ru', 'tr', 'zh-rCN'
}
buildTypes {

View file

@ -10,6 +10,7 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import java.util.Random;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -18,6 +19,9 @@ public class LinkScannerService extends AccessibilityService {
private static final String hostnameRegex = "^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,6}$"; //Credit: http://www.mkyong.com/regular-expressions/domain-name-regular-expression-example/
private static final Pattern hostnamePattern = Pattern.compile(hostnameRegex);
private static final ConcurrentSkipListMap<String, Long> scannedPackages = new ConcurrentSkipListMap<>();
private static final ConcurrentSkipListMap<String, Thread> scannerThreads = new ConcurrentSkipListMap<>();
private static final ConcurrentSkipListSet<Integer> scannedText = new ConcurrentSkipListSet<>();
private static final ConcurrentSkipListSet<String> scannedDomains = new ConcurrentSkipListSet<>();
private NotificationManager notificationManager = null;
@ -35,7 +39,34 @@ public class LinkScannerService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (Database.isDomainDatabaseLoaded()) {
scanViews(event.getSource());
boolean shouldScanView = true;
String packageName = (String) event.getPackageName();
if (event.getSource() != null) {
packageName += event.getSource().getClassName();
}
if (scannedPackages.containsKey(packageName)) {
if ((System.currentTimeMillis() - scannedPackages.get(packageName)) < 1000) {
shouldScanView = false;
}
}
if (shouldScanView) {
if (scannerThreads.containsKey(packageName)) {
Thread oldThread = scannerThreads.get(packageName);
if (oldThread != null) {
if (oldThread.isAlive()) {
shouldScanView = false;
} else {
scannerThreads.remove(packageName);
}
}
}
}
if (shouldScanView) {
scannedPackages.put(packageName, System.currentTimeMillis());
Thread thread = new Thread(() -> scanViews(event.getSource()));
scannerThreads.put(packageName, thread);
thread.start();
}
}
}

View file

@ -4,5 +4,5 @@
android:accessibilityEventTypes="typeWindowContentChanged|typeViewTextChanged"
android:accessibilityFlags="flagDefault"
android:accessibilityFeedbackType="feedbackGeneric"
android:notificationTimeout="1"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true" />

View file

@ -0,0 +1 @@
* Many optimizations to the link scanner feature