Index: adblockplussbrowser/src/main/java/org/adblockplus/sbrowser/contentblocker/engine/DownloadJobService.java
===================================================================
new file mode 100644
--- /dev/null
+++ b/adblockplussbrowser/src/main/java/org/adblockplus/sbrowser/contentblocker/engine/DownloadJobService.java
@@ -0,0 +1,199 @@
+/*
+ * This file is part of Adblock Plus <https://adblockplus.org/>,
+ * Copyright (C) 2006-present eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus 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 Adblock Plus.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.adblockplus.sbrowser.contentblocker.engine;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.PersistableBundle;
+import android.util.Log;
+
+import javax.net.ssl.HttpsURLConnection;
+
+/**
+ * JobService that handles download jobs
+ */
+public class DownloadJobService extends JobService implements EngineManager.OnEngineCreatedCallback
+{
+  private static final String TAG = DownloadJobService.class.getSimpleName();
+  private DownloadJobAsyncTask downloadJobAsyncTask = null;
+  private Engine engine = null;
+
+  @Override
+  public void onCreate()
+  {
+    super.onCreate();
+    Log.i(TAG, "DownloadJobService created.");
+    EngineManager.getInstance().retrieveEngine(this, this);
+  }
+
+  @Override
+  public int onStartCommand(final Intent intent, final int flags, final int startId)
+  {
+    return START_NOT_STICKY;
+  }
+
+  @Override
+  public boolean onStartJob(final JobParameters params)
+  {
+    Log.i(TAG, "Job with id " + params.getJobId() + " started.");
+    this.downloadJobAsyncTask = new DownloadJobAsyncTask()
+    {
+      @Override
+      protected void onPostExecute(final DownloadJob job)
+      {
+        Log.i(DownloadJobService.TAG, "Job with id " + params.getJobId() + " finished.");
+        jobFinished(params, false);
+        if (engine != null && job != null)
+        {
+          engine.downloadFinished(job.id, job.responseCode, job.responseText, job.headers);
+        }
+      }
+    };
+    try
+    {
+      this.downloadJobAsyncTask.execute(createDownloadJobFromExtras(params.getExtras()));
+    }
+    catch (final MalformedURLException e)
+    {
+      Log.e(TAG, "Malformed URL, cannot create download.", e);
+      return false;
+    }
+    return true;
+  }
+
+  @Override
+  public boolean onStopJob(JobParameters params)
+  {
+    if (downloadJobAsyncTask != null)
+    {
+      downloadJobAsyncTask.cancel(true);
+    }
+    return true;
+  }
+
+  @Override
+  public void onDestroy()
+  {
+    EngineManager.getInstance().removeOnEngineCreatedCallback(this);
+    Log.i(TAG, "DownloadJobService destroyed.");
+    super.onDestroy();
+  }
+
+  private DownloadJob createDownloadJobFromExtras(final PersistableBundle extras) throws MalformedURLException
+  {
+    return new DownloadJob(
+        new URL(extras.getString(Notification.KEY_EXTRA_URL)),
+        extras.getString(Notification.KEY_EXTRA_ID),
+        null);
+  }
+
+  @Override
+  public void onEngineCreated(final Engine engine)
+  {
+    this.engine = engine;
+  }
+
+  private static class DownloadJobAsyncTask extends AsyncTask<DownloadJob, Void, DownloadJob>
+  {
+    public static final String TAG = DownloadJobAsyncTask.class.getSimpleName();
+
+    @Override
+    protected DownloadJob doInBackground(final DownloadJob... downloadJob)
+    {
+      final DownloadJob job = downloadJob[0];
+      try
+      {
+        return download(job);
+      }
+      catch (Exception e)
+      {
+        Log.e(TAG, "Error at download: ", e);
+        return null;
+      }
+    }
+
+    private DownloadJob download(final DownloadJob job) throws IOException
+    {
+      final HttpsURLConnection connection = (HttpsURLConnection) job.url.openConnection();
+      connection.setRequestMethod("GET");
+      for (final Map.Entry<String, String> e : job.headers.entrySet())
+      {
+        connection.addRequestProperty(e.getKey(), e.getValue());
+      }
+      connection.connect();
+      job.responseCode = connection.getResponseCode();
+      job.responseHeaders.clear();
+      job.responseText = null;
+
+      for (int i = 1; ; i++)
+      {
+        final String key = connection.getHeaderFieldKey(i);
+        if (key == null)
+        {
+          break;
+        }
+        final String value = connection.getHeaderField(i);
+        job.responseHeaders.put(key.toLowerCase(), value);
+      }
+
+      final StringBuilder sb = new StringBuilder();
+      try (final BufferedReader r = new BufferedReader(new InputStreamReader(
+          connection.getInputStream(), StandardCharsets.UTF_8)))
+      {
+        for (int ch = r.read(); ch != -1; ch = r.read())
+        {
+          sb.append((char) ch);
+        }
+        job.responseText = sb.toString();
+      }
+      return job;
+    }
+  }
+
+  private static class DownloadJob
+  {
+    private final URL url;
+    private final String id;
+    private final HashMap<String, String> headers = new HashMap<>();
+
+    private int responseCode;
+    private final HashMap<String, String> responseHeaders = new HashMap<>();
+    private String responseText;
+
+    DownloadJob(final URL url, final String id, final Map<String, String> headers)
+    {
+      this.url = url;
+      this.id = id;
+      if (headers != null)
+      {
+        this.headers.putAll(headers);
+      }
+    }
+  }
+}
