2015-10-24 18:01:55 +00:00
|
|
|
package eu.faircode.netguard;
|
|
|
|
|
2015-11-03 17:57:29 +00:00
|
|
|
/*
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Copyright 2015 by Marcel Bokhorst (M66B)
|
|
|
|
*/
|
|
|
|
|
2015-11-23 10:43:25 +00:00
|
|
|
import android.annotation.TargetApi;
|
2015-11-04 19:47:07 +00:00
|
|
|
import android.app.Notification;
|
2015-11-18 18:33:48 +00:00
|
|
|
import android.app.NotificationManager;
|
2015-10-24 18:01:55 +00:00
|
|
|
import android.app.PendingIntent;
|
|
|
|
import android.content.BroadcastReceiver;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.IntentFilter;
|
2015-10-25 09:29:49 +00:00
|
|
|
import android.content.SharedPreferences;
|
2015-10-25 15:16:20 +00:00
|
|
|
import android.content.pm.PackageManager;
|
2015-10-24 18:01:55 +00:00
|
|
|
import android.net.ConnectivityManager;
|
|
|
|
import android.net.VpnService;
|
2015-11-23 10:43:25 +00:00
|
|
|
import android.os.Build;
|
2015-11-08 07:36:05 +00:00
|
|
|
import android.os.Handler;
|
|
|
|
import android.os.HandlerThread;
|
|
|
|
import android.os.Looper;
|
|
|
|
import android.os.Message;
|
2015-10-24 18:01:55 +00:00
|
|
|
import android.os.ParcelFileDescriptor;
|
2015-11-09 13:48:20 +00:00
|
|
|
import android.os.PowerManager;
|
2015-10-24 19:50:29 +00:00
|
|
|
import android.preference.PreferenceManager;
|
2015-10-30 09:51:44 +00:00
|
|
|
import android.support.v4.app.NotificationCompat;
|
2015-11-04 20:15:57 +00:00
|
|
|
import android.support.v4.app.NotificationManagerCompat;
|
2015-11-04 20:13:17 +00:00
|
|
|
import android.support.v4.content.ContextCompat;
|
2015-11-25 19:05:17 +00:00
|
|
|
import android.support.v4.content.LocalBroadcastManager;
|
2015-10-24 18:01:55 +00:00
|
|
|
import android.util.Log;
|
|
|
|
|
2015-10-29 22:29:01 +00:00
|
|
|
import java.io.FileInputStream;
|
|
|
|
import java.io.FileOutputStream;
|
2015-10-24 18:01:55 +00:00
|
|
|
import java.io.IOException;
|
2015-10-29 22:29:01 +00:00
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
import java.nio.ByteOrder;
|
2015-10-24 18:01:55 +00:00
|
|
|
|
2015-10-29 06:47:12 +00:00
|
|
|
public class SinkholeService extends VpnService {
|
2015-10-25 15:16:20 +00:00
|
|
|
private static final String TAG = "NetGuard.Service";
|
2015-10-24 18:01:55 +00:00
|
|
|
|
2015-11-25 20:09:00 +00:00
|
|
|
private boolean last_connected;
|
2015-11-25 19:05:17 +00:00
|
|
|
private boolean last_metered;
|
2015-11-01 13:16:57 +00:00
|
|
|
private boolean last_roaming;
|
2015-10-25 15:16:20 +00:00
|
|
|
private ParcelFileDescriptor vpn = null;
|
2015-10-29 22:29:01 +00:00
|
|
|
private boolean debug = false;
|
2015-11-14 11:17:57 +00:00
|
|
|
private Thread debugThread = null;
|
2015-10-29 22:29:01 +00:00
|
|
|
|
2015-11-08 07:36:05 +00:00
|
|
|
private volatile Looper mServiceLooper;
|
|
|
|
private volatile ServiceHandler mServiceHandler;
|
|
|
|
|
2015-11-08 07:21:34 +00:00
|
|
|
private static final int NOTIFY_FOREGROUND = 1;
|
|
|
|
private static final int NOTIFY_DISABLED = 2;
|
2015-11-04 19:47:07 +00:00
|
|
|
|
2015-10-26 16:23:41 +00:00
|
|
|
private static final String EXTRA_COMMAND = "Command";
|
2015-10-24 18:01:55 +00:00
|
|
|
|
2015-10-26 16:23:41 +00:00
|
|
|
private enum Command {start, reload, stop}
|
2015-10-24 18:01:55 +00:00
|
|
|
|
2015-11-09 13:48:20 +00:00
|
|
|
private static volatile PowerManager.WakeLock wlInstance = null;
|
|
|
|
|
|
|
|
synchronized private static PowerManager.WakeLock getLock(Context context) {
|
|
|
|
if (wlInstance == null) {
|
|
|
|
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
2015-11-14 11:17:57 +00:00
|
|
|
wlInstance = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, context.getString(R.string.app_name) + " wakelock");
|
2015-11-09 13:48:20 +00:00
|
|
|
wlInstance.setReferenceCounted(true);
|
|
|
|
}
|
|
|
|
return wlInstance;
|
|
|
|
}
|
|
|
|
|
2015-11-08 07:36:05 +00:00
|
|
|
private final class ServiceHandler extends Handler {
|
|
|
|
public ServiceHandler(Looper looper) {
|
|
|
|
super(looper);
|
|
|
|
}
|
2015-10-25 09:29:49 +00:00
|
|
|
|
2015-11-08 07:36:05 +00:00
|
|
|
@Override
|
|
|
|
public void handleMessage(Message msg) {
|
2015-11-09 13:48:20 +00:00
|
|
|
try {
|
|
|
|
handleIntent((Intent) msg.obj);
|
2015-11-23 07:34:47 +00:00
|
|
|
} catch (Throwable ex) {
|
|
|
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
|
|
|
Util.sendCrashReport(ex, SinkholeService.this);
|
2015-11-09 13:48:20 +00:00
|
|
|
} finally {
|
|
|
|
try {
|
|
|
|
PowerManager.WakeLock wl = getLock(SinkholeService.this);
|
2015-11-27 19:31:39 +00:00
|
|
|
if (wl.isHeld())
|
|
|
|
wl.release();
|
|
|
|
else
|
|
|
|
Log.w(TAG, "Wakelock under-locked");
|
|
|
|
Log.i(TAG, "Messages=" + hasMessages(0) + " wakelock=" + wlInstance.isHeld());
|
2015-11-09 13:48:20 +00:00
|
|
|
} catch (Exception ex) {
|
|
|
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
2015-11-20 09:10:22 +00:00
|
|
|
Util.sendCrashReport(ex, SinkholeService.this);
|
2015-11-09 13:48:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleIntent(Intent intent) {
|
2015-11-23 09:58:05 +00:00
|
|
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SinkholeService.this);
|
|
|
|
|
2015-11-24 09:42:45 +00:00
|
|
|
Command cmd = (Command) intent.getSerializableExtra(EXTRA_COMMAND);
|
2015-11-08 11:30:39 +00:00
|
|
|
Log.i(TAG, "Executing intent=" + intent + " command=" + cmd + " vpn=" + (vpn != null));
|
2015-11-23 09:58:05 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
switch (cmd) {
|
|
|
|
case start:
|
|
|
|
if (vpn == null) {
|
|
|
|
startForeground(NOTIFY_FOREGROUND, getForegroundNotification(0, 0));
|
|
|
|
vpn = startVPN();
|
|
|
|
if (vpn == null)
|
|
|
|
throw new IllegalStateException("VPN start failed");
|
|
|
|
startDebug(vpn);
|
|
|
|
removeDisabledNotification();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case reload:
|
|
|
|
// Seamless handover
|
|
|
|
ParcelFileDescriptor prev = vpn;
|
2015-11-23 07:34:47 +00:00
|
|
|
vpn = startVPN();
|
2015-11-23 09:58:05 +00:00
|
|
|
if (prev != null && vpn == null) {
|
|
|
|
Log.w(TAG, "Handover failed");
|
|
|
|
stopDebug();
|
|
|
|
stopVPN(prev);
|
|
|
|
prev = null;
|
|
|
|
vpn = startVPN();
|
|
|
|
if (vpn == null)
|
|
|
|
throw new IllegalStateException("Handover failed");
|
|
|
|
}
|
2015-11-08 07:36:05 +00:00
|
|
|
stopDebug();
|
2015-11-23 09:58:05 +00:00
|
|
|
startDebug(vpn);
|
|
|
|
if (prev != null)
|
|
|
|
stopVPN(prev);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case stop:
|
2015-11-26 07:34:33 +00:00
|
|
|
if (vpn != null) {
|
2015-11-23 09:58:05 +00:00
|
|
|
stopDebug();
|
|
|
|
stopVPN(vpn);
|
|
|
|
vpn = null;
|
|
|
|
stopForeground(true);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2015-11-26 07:34:33 +00:00
|
|
|
|
2015-11-27 12:48:48 +00:00
|
|
|
// Update main view
|
2015-11-26 07:34:33 +00:00
|
|
|
Intent ruleset = new Intent(ActivityMain.ACTION_RULES_CHANGED);
|
|
|
|
ruleset.putExtra("connected", last_connected);
|
|
|
|
ruleset.putExtra("metered", last_metered);
|
|
|
|
LocalBroadcastManager.getInstance(SinkholeService.this).sendBroadcast(ruleset);
|
|
|
|
|
2015-11-27 12:48:48 +00:00
|
|
|
// Update widgets
|
|
|
|
Widget.updateWidgets(SinkholeService.this);
|
|
|
|
|
2015-11-23 09:58:05 +00:00
|
|
|
} catch (Throwable ex) {
|
|
|
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
|
|
|
|
|
|
|
// Disable firewall
|
|
|
|
prefs.edit().putBoolean("enabled", false).apply();
|
|
|
|
Widget.updateWidgets(SinkholeService.this);
|
|
|
|
|
|
|
|
// Report exception
|
|
|
|
Util.sendCrashReport(ex, SinkholeService.this);
|
2015-11-01 06:50:49 +00:00
|
|
|
}
|
2015-11-08 07:36:05 +00:00
|
|
|
}
|
2015-10-24 18:01:55 +00:00
|
|
|
}
|
|
|
|
|
2015-10-30 07:57:36 +00:00
|
|
|
private ParcelFileDescriptor startVPN() {
|
2015-11-15 09:43:06 +00:00
|
|
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
2015-10-25 15:16:20 +00:00
|
|
|
|
2015-11-04 12:04:27 +00:00
|
|
|
// Check state
|
2015-11-13 10:05:10 +00:00
|
|
|
boolean wifi = Util.isWifiActive(this);
|
2015-11-13 07:07:15 +00:00
|
|
|
boolean metered = Util.isMeteredNetwork(this);
|
2015-10-29 07:48:28 +00:00
|
|
|
boolean interactive = Util.isInteractive(this);
|
2015-11-15 09:43:06 +00:00
|
|
|
boolean useMetered = prefs.getBoolean("use_metered", false);
|
2015-11-25 19:05:17 +00:00
|
|
|
|
2015-11-25 20:09:00 +00:00
|
|
|
// Update connected state
|
|
|
|
last_connected = Util.isConnected(SinkholeService.this);
|
|
|
|
|
|
|
|
// Update metered state
|
|
|
|
if (wifi && !useMetered)
|
|
|
|
metered = false;
|
2015-11-27 07:27:29 +00:00
|
|
|
if (!last_connected)
|
|
|
|
metered = true;
|
2015-11-25 20:09:00 +00:00
|
|
|
last_metered = metered;
|
|
|
|
|
2015-11-25 19:05:17 +00:00
|
|
|
// Update roaming state
|
|
|
|
if (last_roaming != Util.isRoaming(SinkholeService.this)) {
|
|
|
|
last_roaming = !last_roaming;
|
|
|
|
Log.i(TAG, "New state roaming=" + last_roaming);
|
|
|
|
}
|
|
|
|
|
2015-11-25 20:09:00 +00:00
|
|
|
Log.i(TAG, "Starting connected=" + last_connected +
|
|
|
|
" wifi=" + wifi +
|
|
|
|
" metered=" + metered +
|
2015-11-08 16:06:12 +00:00
|
|
|
" roaming=" + last_roaming +
|
2015-11-08 16:01:23 +00:00
|
|
|
" interactive=" + interactive);
|
2015-10-29 07:48:28 +00:00
|
|
|
|
2015-10-25 15:16:20 +00:00
|
|
|
// Build VPN service
|
|
|
|
final Builder builder = new Builder();
|
2015-11-14 11:17:57 +00:00
|
|
|
builder.setSession(getString(R.string.app_name) + " session");
|
2015-11-08 16:06:12 +00:00
|
|
|
// TODO: make tunnel parameters configurable
|
2015-10-25 15:16:20 +00:00
|
|
|
builder.addAddress("10.1.10.1", 32);
|
2015-10-26 13:08:13 +00:00
|
|
|
builder.addAddress("fd00:1:fd00:1:fd00:1:fd00:1", 64);
|
2015-10-25 15:16:20 +00:00
|
|
|
builder.addRoute("0.0.0.0", 0);
|
2015-10-26 13:08:13 +00:00
|
|
|
builder.addRoute("0:0:0:0:0:0:0:0", 0);
|
2015-10-25 15:16:20 +00:00
|
|
|
|
|
|
|
// Add list of allowed applications
|
2015-11-18 18:33:48 +00:00
|
|
|
int nAllowed = 0;
|
|
|
|
int nBlocked = 0;
|
2015-11-01 13:16:57 +00:00
|
|
|
for (Rule rule : Rule.getRules(true, TAG, this)) {
|
2015-11-04 12:04:27 +00:00
|
|
|
boolean blocked = (metered ? rule.other_blocked : rule.wifi_blocked);
|
2015-11-21 08:48:33 +00:00
|
|
|
boolean screen = (metered ? rule.screen_other : rule.screen_wifi);
|
|
|
|
if ((!blocked || (screen && interactive)) && (!metered || !(rule.roaming && last_roaming))) {
|
2015-11-18 18:33:48 +00:00
|
|
|
nAllowed++;
|
2015-11-08 08:40:17 +00:00
|
|
|
if (debug)
|
|
|
|
Log.i(TAG, "Allowing " + rule.info.packageName);
|
2015-10-25 15:16:20 +00:00
|
|
|
try {
|
|
|
|
builder.addDisallowedApplication(rule.info.packageName);
|
2015-10-25 18:02:33 +00:00
|
|
|
} catch (PackageManager.NameNotFoundException ex) {
|
|
|
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
2015-11-20 09:10:22 +00:00
|
|
|
Util.sendCrashReport(ex, this);
|
2015-10-25 15:16:20 +00:00
|
|
|
}
|
2015-11-18 18:33:48 +00:00
|
|
|
} else
|
|
|
|
nBlocked++;
|
2015-11-01 13:16:57 +00:00
|
|
|
}
|
2015-11-18 18:33:48 +00:00
|
|
|
Log.i(TAG, "Allowed=" + nAllowed + " blocked=" + nBlocked);
|
|
|
|
|
|
|
|
// Update notification
|
|
|
|
Notification notification = getForegroundNotification(nAllowed, nBlocked);
|
|
|
|
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
|
|
|
nm.notify(NOTIFY_FOREGROUND, notification);
|
2015-10-25 15:16:20 +00:00
|
|
|
|
2015-10-25 15:28:41 +00:00
|
|
|
// Build configure intent
|
2015-10-25 15:16:20 +00:00
|
|
|
Intent configure = new Intent(this, ActivityMain.class);
|
|
|
|
PendingIntent pi = PendingIntent.getActivity(this, 0, configure, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
|
|
builder.setConfigureIntent(pi);
|
|
|
|
|
2015-10-29 22:29:01 +00:00
|
|
|
if (debug)
|
|
|
|
builder.setBlocking(true);
|
|
|
|
|
2015-10-25 15:16:20 +00:00
|
|
|
// Start VPN service
|
2015-11-23 09:58:05 +00:00
|
|
|
return builder.establish();
|
2015-10-25 15:16:20 +00:00
|
|
|
}
|
|
|
|
|
2015-10-30 07:57:36 +00:00
|
|
|
private void stopVPN(ParcelFileDescriptor pfd) {
|
2015-10-25 15:16:20 +00:00
|
|
|
Log.i(TAG, "Stopping");
|
|
|
|
try {
|
2015-10-26 13:32:14 +00:00
|
|
|
pfd.close();
|
2015-10-25 18:02:33 +00:00
|
|
|
} catch (IOException ex) {
|
|
|
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
2015-11-20 09:10:22 +00:00
|
|
|
Util.sendCrashReport(ex, this);
|
2015-10-25 15:16:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-30 07:57:36 +00:00
|
|
|
private void startDebug(final ParcelFileDescriptor pfd) {
|
2015-11-13 10:35:42 +00:00
|
|
|
if (pfd == null || !debug)
|
2015-10-29 22:29:01 +00:00
|
|
|
return;
|
|
|
|
|
2015-11-14 11:17:57 +00:00
|
|
|
debugThread = new Thread(new Runnable() {
|
2015-10-29 22:29:01 +00:00
|
|
|
@Override
|
|
|
|
public void run() {
|
2015-11-13 11:18:19 +00:00
|
|
|
FileInputStream in = null;
|
|
|
|
FileOutputStream out = null;
|
2015-10-29 22:29:01 +00:00
|
|
|
try {
|
2015-11-13 11:18:19 +00:00
|
|
|
in = new FileInputStream(pfd.getFileDescriptor());
|
|
|
|
out = new FileOutputStream(pfd.getFileDescriptor());
|
2015-10-29 22:29:01 +00:00
|
|
|
|
|
|
|
ByteBuffer buffer = ByteBuffer.allocate(32767);
|
|
|
|
buffer.order(ByteOrder.BIG_ENDIAN);
|
|
|
|
|
|
|
|
Log.i(TAG, "Start receiving");
|
|
|
|
while (!Thread.currentThread().isInterrupted() &&
|
|
|
|
pfd.getFileDescriptor() != null &&
|
|
|
|
pfd.getFileDescriptor().valid())
|
|
|
|
try {
|
|
|
|
buffer.clear();
|
|
|
|
int length = in.read(buffer.array());
|
|
|
|
if (length > 0) {
|
|
|
|
buffer.limit(length);
|
|
|
|
Packet pkt = new Packet(buffer);
|
|
|
|
|
|
|
|
if (pkt.IPv4.protocol == Packet.IPv4Header.TCP && pkt.TCP.SYN) {
|
|
|
|
int uid = pkt.getUid4();
|
|
|
|
if (uid < 0)
|
|
|
|
Log.w(TAG, "uid not found");
|
|
|
|
|
|
|
|
String[] pkg = getPackageManager().getPackagesForUid(uid);
|
|
|
|
if (pkg == null)
|
|
|
|
pkg = new String[]{uid == 0 ? "root" : "unknown"};
|
|
|
|
|
|
|
|
Log.i(TAG, "Connect " + pkt.IPv4.destinationAddress + ":" + pkt.TCP.destinationPort + " uid=" + uid + " pkg=" + pkg[0]);
|
|
|
|
|
|
|
|
// Send RST
|
|
|
|
pkt.swapAddresses();
|
|
|
|
pkt.TCP.clearFlags();
|
|
|
|
pkt.TCP.RST = true;
|
|
|
|
long ack = pkt.TCP.acknowledgementNumber;
|
|
|
|
pkt.TCP.acknowledgementNumber = (pkt.TCP.sequenceNumber + 1) & 0xFFFFFFFFL;
|
|
|
|
pkt.TCP.sequenceNumber = (ack + 1) & 0xFFFFFFFFL;
|
|
|
|
pkt.send(out);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Throwable ex) {
|
|
|
|
Log.e(TAG, ex.toString());
|
2015-11-20 09:10:22 +00:00
|
|
|
Util.sendCrashReport(ex, SinkholeService.this);
|
2015-10-29 22:29:01 +00:00
|
|
|
}
|
|
|
|
Log.i(TAG, "End receiving");
|
|
|
|
} catch (Throwable ex) {
|
|
|
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
2015-11-20 09:10:22 +00:00
|
|
|
Util.sendCrashReport(ex, SinkholeService.this);
|
2015-11-13 11:18:19 +00:00
|
|
|
} finally {
|
|
|
|
try {
|
|
|
|
if (in != null)
|
|
|
|
in.close();
|
|
|
|
} catch (IOException ignored) {
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
if (out != null)
|
|
|
|
out.close();
|
|
|
|
} catch (IOException ignored) {
|
|
|
|
}
|
2015-10-29 22:29:01 +00:00
|
|
|
}
|
|
|
|
}
|
2015-11-14 11:17:57 +00:00
|
|
|
}, getString(R.string.app_name) + " debug");
|
|
|
|
debugThread.start();
|
2015-10-29 22:29:01 +00:00
|
|
|
}
|
|
|
|
|
2015-10-30 07:57:36 +00:00
|
|
|
private void stopDebug() {
|
2015-11-14 11:17:57 +00:00
|
|
|
if (debugThread != null)
|
|
|
|
debugThread.interrupt();
|
2015-10-29 22:29:01 +00:00
|
|
|
}
|
|
|
|
|
2015-11-02 05:29:09 +00:00
|
|
|
private BroadcastReceiver interactiveStateReceiver = new BroadcastReceiver() {
|
2015-10-29 07:48:28 +00:00
|
|
|
@Override
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
|
Log.i(TAG, "Received " + intent);
|
2015-11-20 10:34:23 +00:00
|
|
|
Util.logExtras(intent);
|
2015-10-29 07:48:28 +00:00
|
|
|
reload(null, SinkholeService.this);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-11-23 10:43:25 +00:00
|
|
|
private BroadcastReceiver idleStateReceiver = new BroadcastReceiver() {
|
|
|
|
@Override
|
|
|
|
@TargetApi(Build.VERSION_CODES.M)
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
|
Log.i(TAG, "Received " + intent);
|
|
|
|
Util.logExtras(intent);
|
|
|
|
|
|
|
|
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
|
|
|
Log.i(TAG, "device idle=" + pm.isDeviceIdleMode());
|
|
|
|
|
2015-11-24 09:42:45 +00:00
|
|
|
// Reload rules when coming from idle mode
|
2015-11-23 10:43:25 +00:00
|
|
|
if (!pm.isDeviceIdleMode())
|
|
|
|
reload(null, SinkholeService.this);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-10-24 18:01:55 +00:00
|
|
|
private BroadcastReceiver connectivityChangedReceiver = new BroadcastReceiver() {
|
|
|
|
@Override
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
2015-11-25 19:05:17 +00:00
|
|
|
// Filter VPN connectivity changes
|
2015-11-08 08:40:17 +00:00
|
|
|
int networkType = intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, ConnectivityManager.TYPE_DUMMY);
|
|
|
|
if (!debug && networkType == ConnectivityManager.TYPE_VPN)
|
|
|
|
return;
|
|
|
|
|
2015-11-25 19:05:17 +00:00
|
|
|
// Reload rules
|
2015-10-25 09:29:49 +00:00
|
|
|
Log.i(TAG, "Received " + intent);
|
2015-11-20 10:34:23 +00:00
|
|
|
Util.logExtras(intent);
|
2015-11-25 19:05:17 +00:00
|
|
|
reload(null, SinkholeService.this);
|
2015-10-25 22:31:00 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-11-02 05:29:09 +00:00
|
|
|
private BroadcastReceiver packageAddedReceiver = new BroadcastReceiver() {
|
2015-10-25 22:31:00 +00:00
|
|
|
@Override
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
|
Log.i(TAG, "Received " + intent);
|
2015-11-20 10:34:23 +00:00
|
|
|
Util.logExtras(intent);
|
2015-10-29 06:47:12 +00:00
|
|
|
reload(null, SinkholeService.this);
|
2015-10-24 18:01:55 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCreate() {
|
|
|
|
super.onCreate();
|
2015-10-24 19:50:29 +00:00
|
|
|
Log.i(TAG, "Create");
|
2015-10-25 15:28:41 +00:00
|
|
|
|
2015-11-14 11:17:57 +00:00
|
|
|
HandlerThread thread = new HandlerThread(getString(R.string.app_name) + " handler");
|
2015-11-08 07:36:05 +00:00
|
|
|
thread.start();
|
|
|
|
|
|
|
|
mServiceLooper = thread.getLooper();
|
|
|
|
mServiceHandler = new ServiceHandler(mServiceLooper);
|
|
|
|
|
2015-11-25 20:09:00 +00:00
|
|
|
last_connected = Util.isConnected(this);
|
2015-11-25 19:05:17 +00:00
|
|
|
last_metered = Util.isMeteredNetwork(this);
|
|
|
|
last_roaming = Util.isRoaming(this);
|
2015-11-08 16:06:12 +00:00
|
|
|
|
2015-11-02 05:29:09 +00:00
|
|
|
// Listen for interactive state changes
|
|
|
|
IntentFilter ifInteractive = new IntentFilter();
|
|
|
|
ifInteractive.addAction(Intent.ACTION_SCREEN_ON);
|
|
|
|
ifInteractive.addAction(Intent.ACTION_SCREEN_OFF);
|
|
|
|
registerReceiver(interactiveStateReceiver, ifInteractive);
|
2015-10-29 07:48:28 +00:00
|
|
|
|
2015-11-23 10:43:25 +00:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
|
|
// Listen for idle mode state changes
|
|
|
|
IntentFilter ifIdle = new IntentFilter();
|
|
|
|
ifIdle.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
|
|
|
|
registerReceiver(idleStateReceiver, ifIdle);
|
|
|
|
}
|
|
|
|
|
2015-10-29 07:48:28 +00:00
|
|
|
// Listen for connectivity updates
|
|
|
|
IntentFilter ifConnectivity = new IntentFilter();
|
|
|
|
ifConnectivity.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
|
|
|
registerReceiver(connectivityChangedReceiver, ifConnectivity);
|
|
|
|
|
2015-11-02 05:29:09 +00:00
|
|
|
// Listen for added applications
|
|
|
|
IntentFilter ifPackage = new IntentFilter();
|
|
|
|
ifPackage.addAction(Intent.ACTION_PACKAGE_ADDED);
|
|
|
|
ifPackage.addDataScheme("package");
|
|
|
|
registerReceiver(packageAddedReceiver, ifPackage);
|
2015-10-24 18:01:55 +00:00
|
|
|
}
|
|
|
|
|
2015-11-08 07:36:05 +00:00
|
|
|
@Override
|
|
|
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
2015-11-28 15:15:55 +00:00
|
|
|
// Keep awake
|
|
|
|
getLock(this).acquire();
|
2015-11-09 13:48:20 +00:00
|
|
|
|
2015-11-24 09:42:45 +00:00
|
|
|
// Handle service restart
|
|
|
|
if (intent == null) {
|
2015-11-24 09:54:43 +00:00
|
|
|
Log.i(TAG, "Restart");
|
|
|
|
|
2015-11-24 09:42:45 +00:00
|
|
|
// Get enabled
|
|
|
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
|
|
|
boolean enabled = prefs.getBoolean("enabled", false);
|
|
|
|
|
|
|
|
// Recreate intent
|
|
|
|
intent = new Intent(this, SinkholeService.class);
|
|
|
|
intent.putExtra(EXTRA_COMMAND, enabled ? Command.start : Command.stop);
|
|
|
|
}
|
|
|
|
|
|
|
|
Command cmd = (Command) intent.getSerializableExtra(EXTRA_COMMAND);
|
2015-11-08 07:36:05 +00:00
|
|
|
Log.i(TAG, "Start intent=" + intent + " command=" + cmd + " vpn=" + (vpn != null));
|
|
|
|
|
|
|
|
// Queue command
|
|
|
|
Message msg = mServiceHandler.obtainMessage();
|
|
|
|
msg.arg1 = startId;
|
|
|
|
msg.obj = intent;
|
2015-11-27 19:31:39 +00:00
|
|
|
msg.what = 0;
|
2015-11-08 07:36:05 +00:00
|
|
|
mServiceHandler.sendMessage(msg);
|
|
|
|
|
2015-11-24 09:42:45 +00:00
|
|
|
return START_STICKY;
|
2015-11-08 07:36:05 +00:00
|
|
|
}
|
|
|
|
|
2015-11-24 09:44:02 +00:00
|
|
|
@Override
|
|
|
|
public void onRevoke() {
|
|
|
|
Log.i(TAG, "Revoke");
|
|
|
|
|
|
|
|
// Disable firewall (will result in stop command)
|
|
|
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
|
|
|
prefs.edit().putBoolean("enabled", false).apply();
|
|
|
|
|
|
|
|
// Feedback
|
|
|
|
showDisabledNotification();
|
|
|
|
Widget.updateWidgets(this);
|
|
|
|
|
|
|
|
super.onRevoke();
|
|
|
|
}
|
|
|
|
|
2015-10-24 18:01:55 +00:00
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
|
|
|
Log.i(TAG, "Destroy");
|
2015-10-25 15:28:41 +00:00
|
|
|
|
2015-11-08 08:40:17 +00:00
|
|
|
mServiceLooper.quit();
|
2015-10-25 15:28:41 +00:00
|
|
|
|
2015-10-29 07:48:28 +00:00
|
|
|
unregisterReceiver(interactiveStateReceiver);
|
2015-11-23 10:43:25 +00:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
|
|
|
unregisterReceiver(idleStateReceiver);
|
2015-11-02 05:29:09 +00:00
|
|
|
unregisterReceiver(connectivityChangedReceiver);
|
|
|
|
unregisterReceiver(packageAddedReceiver);
|
2015-10-25 15:28:41 +00:00
|
|
|
|
2015-11-08 08:40:17 +00:00
|
|
|
if (vpn != null) {
|
|
|
|
stopDebug();
|
|
|
|
stopVPN(vpn);
|
|
|
|
vpn = null;
|
|
|
|
}
|
2015-11-08 07:36:05 +00:00
|
|
|
|
2015-10-24 18:01:55 +00:00
|
|
|
super.onDestroy();
|
|
|
|
}
|
|
|
|
|
2015-11-18 18:33:48 +00:00
|
|
|
private Notification getForegroundNotification(int allowed, int blocked) {
|
2015-11-08 07:21:34 +00:00
|
|
|
Intent main = new Intent(this, ActivityMain.class);
|
2015-11-27 07:23:18 +00:00
|
|
|
PendingIntent pi = PendingIntent.getActivity(this, 0, main, PendingIntent.FLAG_UPDATE_CURRENT);
|
2015-11-08 07:21:34 +00:00
|
|
|
|
2015-11-18 18:33:48 +00:00
|
|
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
|
2015-11-08 07:21:34 +00:00
|
|
|
.setSmallIcon(R.drawable.ic_security_white_24dp)
|
|
|
|
.setContentTitle(getString(R.string.app_name))
|
|
|
|
.setContentText(getString(R.string.msg_started))
|
|
|
|
.setContentIntent(pi)
|
|
|
|
.setCategory(Notification.CATEGORY_STATUS)
|
|
|
|
.setVisibility(Notification.VISIBILITY_SECRET)
|
2015-11-10 14:42:27 +00:00
|
|
|
.setPriority(Notification.PRIORITY_MIN)
|
2015-11-08 08:40:17 +00:00
|
|
|
.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
|
2015-11-08 07:21:34 +00:00
|
|
|
.setAutoCancel(true);
|
|
|
|
|
2015-11-18 18:33:48 +00:00
|
|
|
if (allowed > 0 || blocked > 0) {
|
|
|
|
NotificationCompat.BigTextStyle notification = new NotificationCompat.BigTextStyle(builder);
|
|
|
|
notification.bigText(getString(R.string.msg_started));
|
|
|
|
notification.setSummaryText(getString(R.string.msg_packages, allowed, blocked));
|
|
|
|
return notification.build();
|
|
|
|
} else
|
|
|
|
return builder.build();
|
2015-11-08 07:21:34 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 06:44:48 +00:00
|
|
|
private void showDisabledNotification() {
|
2015-11-08 07:21:34 +00:00
|
|
|
Intent main = new Intent(this, ActivityMain.class);
|
2015-11-27 07:23:18 +00:00
|
|
|
PendingIntent pi = PendingIntent.getActivity(this, 0, main, PendingIntent.FLAG_UPDATE_CURRENT);
|
2015-10-30 11:49:24 +00:00
|
|
|
|
|
|
|
NotificationCompat.Builder notification = new NotificationCompat.Builder(this)
|
2015-11-28 07:27:20 +00:00
|
|
|
.setSmallIcon(R.drawable.ic_error_white_24dp)
|
2015-10-30 11:49:24 +00:00
|
|
|
.setContentTitle(getString(R.string.app_name))
|
|
|
|
.setContentText(getString(R.string.msg_revoked))
|
2015-11-08 07:21:34 +00:00
|
|
|
.setContentIntent(pi)
|
2015-11-04 20:13:17 +00:00
|
|
|
.setCategory(Notification.CATEGORY_STATUS)
|
|
|
|
.setVisibility(Notification.VISIBILITY_SECRET)
|
|
|
|
.setColor(ContextCompat.getColor(this, R.color.colorAccent))
|
2015-10-30 11:49:24 +00:00
|
|
|
.setAutoCancel(true);
|
|
|
|
|
2015-11-04 20:15:57 +00:00
|
|
|
NotificationManagerCompat.from(this).notify(NOTIFY_DISABLED, notification.build());
|
2015-11-01 06:44:48 +00:00
|
|
|
}
|
2015-10-30 11:49:24 +00:00
|
|
|
|
2015-11-01 06:44:48 +00:00
|
|
|
private void removeDisabledNotification() {
|
2015-11-04 20:15:57 +00:00
|
|
|
NotificationManagerCompat.from(this).cancel(NOTIFY_DISABLED);
|
2015-10-24 18:01:55 +00:00
|
|
|
}
|
2015-10-25 22:04:10 +00:00
|
|
|
|
2015-10-26 16:23:41 +00:00
|
|
|
public static void start(Context context) {
|
2015-10-29 06:47:12 +00:00
|
|
|
Intent intent = new Intent(context, SinkholeService.class);
|
2015-10-26 16:23:41 +00:00
|
|
|
intent.putExtra(EXTRA_COMMAND, Command.start);
|
|
|
|
context.startService(intent);
|
|
|
|
}
|
|
|
|
|
2015-10-26 16:32:03 +00:00
|
|
|
public static void reload(String network, Context context) {
|
2015-11-04 22:45:47 +00:00
|
|
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
2015-11-15 09:43:06 +00:00
|
|
|
if (prefs.getBoolean("enabled", false)) {
|
2015-11-17 09:44:08 +00:00
|
|
|
boolean wifi = Util.isWifiActive(context);
|
2015-11-15 09:43:06 +00:00
|
|
|
boolean metered = Util.isMeteredNetwork(context);
|
2015-11-17 09:44:08 +00:00
|
|
|
if (wifi && !prefs.getBoolean("use_metered", false))
|
2015-11-15 09:43:06 +00:00
|
|
|
metered = false;
|
|
|
|
if (network == null || ("wifi".equals(network) ? !metered : metered)) {
|
2015-11-04 22:45:47 +00:00
|
|
|
Intent intent = new Intent(context, SinkholeService.class);
|
|
|
|
intent.putExtra(EXTRA_COMMAND, Command.reload);
|
|
|
|
context.startService(intent);
|
|
|
|
}
|
2015-11-15 09:43:06 +00:00
|
|
|
}
|
2015-10-25 22:04:10 +00:00
|
|
|
}
|
2015-10-26 16:23:41 +00:00
|
|
|
|
|
|
|
public static void stop(Context context) {
|
2015-10-29 06:47:12 +00:00
|
|
|
Intent intent = new Intent(context, SinkholeService.class);
|
2015-10-26 16:23:41 +00:00
|
|
|
intent.putExtra(EXTRA_COMMAND, Command.stop);
|
|
|
|
context.startService(intent);
|
|
|
|
}
|
2015-10-24 18:01:55 +00:00
|
|
|
}
|