mirror of https://github.com/M66B/NetGuard.git
254 lines
10 KiB
Java
254 lines
10 KiB
Java
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 <http://www.gnu.org/licenses/>.
|
|
|
|
Copyright 2015-2016 by Marcel Bokhorst (M66B)
|
|
*/
|
|
|
|
import android.annotation.TargetApi;
|
|
import android.app.job.JobInfo;
|
|
import android.app.job.JobParameters;
|
|
import android.app.job.JobScheduler;
|
|
import android.app.job.JobService;
|
|
import android.content.ComponentName;
|
|
import android.content.Context;
|
|
import android.content.SharedPreferences;
|
|
import android.content.pm.PackageManager;
|
|
import android.content.res.Configuration;
|
|
import android.content.res.Resources;
|
|
import android.os.AsyncTask;
|
|
import android.os.Build;
|
|
import android.os.PersistableBundle;
|
|
import android.provider.Settings;
|
|
import android.util.Log;
|
|
|
|
import org.json.JSONObject;
|
|
|
|
import java.io.BufferedOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStreamReader;
|
|
import java.io.OutputStream;
|
|
import java.net.URL;
|
|
import java.util.Date;
|
|
import java.util.Locale;
|
|
|
|
import javax.net.ssl.HttpsURLConnection;
|
|
|
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
public class ServiceJob extends JobService {
|
|
private static int id = 0;
|
|
private static final String TAG = "NetGuard.Job";
|
|
|
|
private static final String cUrl = "https://crowd.netguard.me/";
|
|
private static final int cTimeOutMs = 15000;
|
|
|
|
@Override
|
|
public boolean onStartJob(JobParameters params) {
|
|
Log.i(TAG, "Start job=" + params.getJobId());
|
|
|
|
new AsyncTask<JobParameters, Object, Object>() {
|
|
|
|
@Override
|
|
protected JobParameters doInBackground(JobParameters... params) {
|
|
Log.i(TAG, "Executing job=" + params[0].getJobId());
|
|
|
|
HttpsURLConnection urlConnection = null;
|
|
try {
|
|
String android_id = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
|
|
JSONObject json = new JSONObject();
|
|
|
|
json.put("device", Util.sha256(android_id, ""));
|
|
json.put("product", Build.DEVICE);
|
|
json.put("sdk", Build.VERSION.SDK_INT);
|
|
json.put("country", Locale.getDefault().getCountry());
|
|
|
|
json.put("netguard", Util.getSelfVersionCode(ServiceJob.this));
|
|
try {
|
|
json.put("store", getPackageManager().getInstallerPackageName(getPackageName()));
|
|
} catch (Throwable ex) {
|
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
|
json.put("store", null);
|
|
}
|
|
|
|
for (String name : params[0].getExtras().keySet())
|
|
json.put(name, params[0].getExtras().get(name));
|
|
|
|
urlConnection = (HttpsURLConnection) new URL(cUrl).openConnection();
|
|
urlConnection.setConnectTimeout(cTimeOutMs);
|
|
urlConnection.setReadTimeout(cTimeOutMs);
|
|
urlConnection.setRequestProperty("Accept", "application/json");
|
|
urlConnection.setRequestProperty("Content-type", "application/json");
|
|
urlConnection.setRequestMethod("POST");
|
|
urlConnection.setDoInput(true);
|
|
urlConnection.setDoOutput(true);
|
|
|
|
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
|
|
out.write(json.toString().getBytes()); // UTF-8
|
|
out.flush();
|
|
|
|
int code = urlConnection.getResponseCode();
|
|
if (code != HttpsURLConnection.HTTP_OK)
|
|
throw new IOException("HTTP " + code);
|
|
|
|
InputStreamReader isr = new InputStreamReader(urlConnection.getInputStream());
|
|
Log.i(TAG, "Response=" + Util.readString(isr).toString());
|
|
|
|
jobFinished(params[0], false);
|
|
|
|
if ("rule".equals(params[0].getExtras().getString("type"))) {
|
|
SharedPreferences history = getSharedPreferences("history", Context.MODE_PRIVATE);
|
|
history.edit().putLong(params[0].getExtras().getString("package") + ":submitted", new Date().getTime()).apply();
|
|
}
|
|
|
|
} catch (Throwable ex) {
|
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
|
jobFinished(params[0], true);
|
|
|
|
} finally {
|
|
if (urlConnection != null)
|
|
urlConnection.disconnect();
|
|
|
|
try {
|
|
Thread.sleep(1000);
|
|
} catch (InterruptedException ignored) {
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}.execute(params);
|
|
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean onStopJob(JobParameters params) {
|
|
Log.i(TAG, "Stop job=" + params.getJobId());
|
|
return true;
|
|
}
|
|
|
|
public static void submit(Rule rule, Context context) {
|
|
PersistableBundle bundle = new PersistableBundle();
|
|
bundle.putString("type", "rule");
|
|
|
|
bundle.putInt("wifi_default", rule.wifi_default ? 1 : 0);
|
|
bundle.putInt("other_default", rule.other_default ? 1 : 0);
|
|
bundle.putInt("screen_wifi_default", rule.screen_wifi_default ? 1 : 0);
|
|
bundle.putInt("screen_other_default", rule.screen_other_default ? 1 : 0);
|
|
bundle.putInt("roaming_default", rule.roaming_default ? 1 : 0);
|
|
|
|
bundle.putInt("wifi_blocked", rule.wifi_blocked ? 1 : 0);
|
|
bundle.putInt("other_blocked", rule.other_blocked ? 1 : 0);
|
|
bundle.putInt("screen_wifi", rule.screen_wifi ? 1 : 0);
|
|
bundle.putInt("screen_other", rule.screen_other ? 1 : 0);
|
|
bundle.putInt("roaming", rule.roaming ? 1 : 0);
|
|
|
|
bundle.putInt("apply", rule.apply ? 1 : 0);
|
|
bundle.putInt("notify", rule.notify ? 1 : 0);
|
|
|
|
submit(rule, bundle, context);
|
|
}
|
|
|
|
public static void submit(Rule rule, int version, int protocol, String daddr, int dport, int blocked, Context context) {
|
|
PersistableBundle bundle = new PersistableBundle();
|
|
bundle.putString("type", "host");
|
|
|
|
bundle.putInt("version", version);
|
|
bundle.putInt("protocol", protocol);
|
|
bundle.putString("daddr", daddr);
|
|
bundle.putInt("dport", dport);
|
|
bundle.putInt("blocked", blocked);
|
|
|
|
submit(rule, bundle, context);
|
|
}
|
|
|
|
private static void submit(Rule rule, PersistableBundle bundle, Context context) {
|
|
PackageManager pm = context.getPackageManager();
|
|
JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
|
|
|
|
// Get english application label
|
|
String label = null;
|
|
try {
|
|
Configuration config = new Configuration();
|
|
config.setLocale(new Locale("en"));
|
|
Resources res = pm.getResourcesForApplication(rule.info.packageName);
|
|
res.updateConfiguration(config, res.getDisplayMetrics());
|
|
label = res.getString(rule.info.applicationInfo.labelRes);
|
|
} catch (Throwable ex) {
|
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
|
CharSequence cs = rule.info.applicationInfo.loadLabel(pm);
|
|
if (cs != null)
|
|
label = cs.toString();
|
|
}
|
|
|
|
// Add application data
|
|
bundle.putInt("uid", rule.info.applicationInfo.uid);
|
|
bundle.putString("package", rule.info.packageName);
|
|
bundle.putInt("version_code", rule.info.versionCode);
|
|
bundle.putString("version_name", rule.info.versionName);
|
|
bundle.putString("label", label);
|
|
bundle.putInt("system", rule.system ? 1 : 0);
|
|
try {
|
|
bundle.putString("installer", pm.getInstallerPackageName(rule.info.packageName));
|
|
} catch (Throwable ex) {
|
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
|
bundle.putString("installer", null);
|
|
}
|
|
|
|
// Cancel overlapping jobs
|
|
for (JobInfo pending : scheduler.getAllPendingJobs()) {
|
|
String type = pending.getExtras().getString("type");
|
|
if (type != null && type.equals(bundle.getString("type"))) {
|
|
if (type.equals("rule")) {
|
|
int uid = pending.getExtras().getInt("uid");
|
|
if (uid == bundle.getInt("uid")) {
|
|
Log.i(TAG, "Canceling id=" + pending.getId());
|
|
scheduler.cancel(pending.getId());
|
|
}
|
|
} else if (type.equals("host")) {
|
|
int uid = pending.getExtras().getInt("uid");
|
|
int version = pending.getExtras().getInt("version");
|
|
int protocol = pending.getExtras().getInt("protocol");
|
|
String daddr = pending.getExtras().getString("daddr");
|
|
int dport = pending.getExtras().getInt("dport");
|
|
if (uid == bundle.getInt("uid") &&
|
|
version == bundle.getInt("version") &&
|
|
protocol == bundle.getInt("protocol") &&
|
|
daddr != null && daddr.equals(bundle.getString("daddr")) &&
|
|
dport == bundle.getInt("dport")) {
|
|
Log.i(TAG, "Canceling id=" + pending.getId());
|
|
scheduler.cancel(pending.getId());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Schedule job
|
|
ComponentName serviceName = new ComponentName(context, ServiceJob.class);
|
|
JobInfo job = new JobInfo.Builder(++id, serviceName)
|
|
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
|
|
.setMinimumLatency(Util.isDebuggable(context) ? 10 * 1000 : 60 * 1000)
|
|
.setExtras(bundle)
|
|
.setPersisted(true)
|
|
.build();
|
|
if (scheduler.schedule(job) == JobScheduler.RESULT_SUCCESS)
|
|
Log.i(TAG, "Scheduled job=" + job.getId() + " success");
|
|
else
|
|
Log.e(TAG, "Scheduled job=" + job.getId() + " failed");
|
|
}
|
|
}
|