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 @@ + +