eu.faircode.netguard.ServiceJob.java Source code

Java tutorial

Introduction

Here is the source code for eu.faircode.netguard.ServiceJob.java

Source

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");
    }
}