diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 65f08b4f..aa195176 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -177,6 +177,18 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/java/eu/faircode/netguard/Accessibility.java b/app/src/main/java/eu/faircode/netguard/Accessibility.java
new file mode 100644
index 00000000..b3ecf51f
--- /dev/null
+++ b/app/src/main/java/eu/faircode/netguard/Accessibility.java
@@ -0,0 +1,80 @@
+package eu.faircode.netguard;
+
+/*
+ This file is part of NetGuard.
+
+ NetGuard is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NetGuard is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with NetGuard. If not, see .
+
+ Copyright 2015-2016 by Marcel Bokhorst (M66B)
+*/
+
+import android.accessibilityservice.AccessibilityService;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Accessibility extends AccessibilityService {
+ private String lastActivePackageName = null;
+ private Map mapComponentNameActivity = new HashMap<>();
+
+ public static final String ACTION_FOREGROUND_ACTIVITY_CHANGED = "eu.faircode.netguard.ACTION_FOREGROUND_ACTIVITY_CHANGED";
+ public static final String EXTRA_PACKAGE_NAME = "Package";
+
+ private static final String TAG = "NetGuard.Accessibility";
+
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+ ComponentName componentName = new ComponentName(
+ event.getPackageName().toString(),
+ event.getClassName().toString()
+ );
+
+ synchronized (this) {
+ boolean activity = false;
+ if (mapComponentNameActivity.containsKey(componentName))
+ activity = mapComponentNameActivity.get(componentName);
+ else {
+ try {
+ getPackageManager().getActivityInfo(componentName, 0);
+ activity = true;
+ } catch (PackageManager.NameNotFoundException ignored) {
+ }
+ mapComponentNameActivity.put(componentName, activity);
+ }
+
+ Log.i(TAG, componentName + "=" + activity);
+
+ if (activity && (lastActivePackageName == null || !lastActivePackageName.equals((event.getPackageName().toString())))) {
+ lastActivePackageName = event.getPackageName().toString();
+ Log.i(TAG, "Foreground activity=" + lastActivePackageName);
+
+ Intent intent = new Intent(ACTION_FOREGROUND_ACTIVITY_CHANGED);
+ intent.putExtra(EXTRA_PACKAGE_NAME, lastActivePackageName);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onInterrupt() {
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java b/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java
index dd1b2467..a8cae8e1 100644
--- a/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java
+++ b/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java
@@ -112,12 +112,14 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
private boolean registeredIdleState = false;
private boolean registeredConnectivityChanged = false;
private boolean registeredPackageAdded = false;
+ private boolean registeredActivityChanged = false;
private State state = State.none;
private boolean user_foreground = true;
private boolean last_connected = false;
private boolean last_metered = true;
private boolean last_interactive = false;
+ private String last_activity = null;
private boolean powersaving = false;
private boolean phone_state = false;
private Object subscriptionsChangedListener = null;
@@ -1387,6 +1389,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
" generation=" + generation +
" roaming=" + roaming + "/" + org_roaming +
" interactive=" + last_interactive +
+ " activity=" + last_activity +
" tethering=" + tethering +
" filter=" + filter);
@@ -1396,6 +1399,10 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
boolean screen = (metered ? rule.screen_other : rule.screen_wifi);
if ((!blocked || (screen && last_interactive)) && (!metered || !(rule.roaming && roaming)))
listAllowed.add(rule);
+ else if (last_activity != null && last_activity.equals(rule.info.packageName)) {
+ listAllowed.add(rule);
+ Log.i(TAG, "Allowed foreground activity=" + rule.info.packageName);
+ }
}
Log.i(TAG, "Allowed " + listAllowed.size() + " of " + listRule.size());
@@ -1668,6 +1675,16 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
}
};
+ private BroadcastReceiver activityChangedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "Received " + intent);
+ Util.logExtras(intent);
+ last_activity = intent.getStringExtra(Accessibility.EXTRA_PACKAGE_NAME);
+ reload("activity changed", ServiceSinkhole.this);
+ }
+ };
+
private PhoneStateListener phoneStateListener = new PhoneStateListener() {
private String last_generation = null;
private int last_international = -1;
@@ -1789,6 +1806,11 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
registerReceiver(packageAddedReceiver, ifPackage);
registeredPackageAdded = true;
+ // Listen for foreground activity changes
+ IntentFilter ifActivity = new IntentFilter(Accessibility.ACTION_FOREGROUND_ACTIVITY_CHANGED);
+ LocalBroadcastManager.getInstance(this).registerReceiver(activityChangedReceiver, ifActivity);
+ registeredActivityChanged = true;
+
// Setup house holding
Intent alarmIntent = new Intent(this, ServiceSinkhole.class);
alarmIntent.setAction(ACTION_HOUSE_HOLDING);
@@ -1937,6 +1959,10 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
unregisterReceiver(packageAddedReceiver);
registeredPackageAdded = false;
}
+ if (registeredActivityChanged) {
+ LocalBroadcastManager.getInstance(this).unregisterReceiver(activityChangedReceiver);
+ registeredActivityChanged = false;
+ }
if (phone_state) {
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
diff --git a/app/src/main/res/xml/accessibility.xml b/app/src/main/res/xml/accessibility.xml
new file mode 100644
index 00000000..33f6deed
--- /dev/null
+++ b/app/src/main/res/xml/accessibility.xml
@@ -0,0 +1,6 @@
+
+