diff --git a/.gitignore b/.gitignore
index dfaad42ad6bdf181d2fcdb3d2368ed3d5a2538c4..5ff581a1e24da15230971df48659d552a6ff8a6c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -82,4 +82,8 @@ app/.externalNativeBuild
 releases
 currentReleases
 
-go/golang/go
\ No newline at end of file
+go/golang/go
+go/lib/bitmask-core-sources.jar
+go/lib/bitmask-core.aar
+go/lib/bitmask-web-core-sources.jar
+go/lib/bitmask-web-core.aar
diff --git a/app/build.gradle b/app/build.gradle
index 7134e54366e7b19928ad5989bfdbe1a0d7ce513e..0b3403ffa7fa9d8855d74667d8085979dfca9cbd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -27,8 +27,6 @@ android {
     //This is the default donation URL and should be set to the donation page of LEAP
     // and this should not be set/altered anywhere else.
     buildConfigField 'String', 'default_donation_url', '"https://leap.se/en/about-us/donate"'
-    //This is the donation URL and should be set to the relevant donation page.
-    buildConfigField 'String', 'donation_url', 'null'
     //The field to enable donations in the app.
     buildConfigField 'boolean', 'enable_donation', 'true'
     //The field to enable donation reminder popup in the app if enable_donation is set to 'false' this will be disabled.
@@ -37,7 +35,16 @@ android {
     buildConfigField 'int', 'donation_reminder_duration', '30'
     //skip the account creation / login screen if the provider offers anonymous vpn usage, use directly the anonymous cert instead
     buildConfigField 'boolean', 'priotize_anonymous_usage', 'false'
+
+    // static update url pointing to the latest stable release apk
+    buildConfigField "String", "update_apk_url", '"https://dl.bitmask.net/client/android/Bitmask-Android-latest.apk"'
+    // the the pgp signature file of the apk
+    buildConfigField "String", "signature_url", '"https://dl.bitmask.net/client/android/Bitmask-Android-latest.apk.sig"'
+    // the version file contains the versionCode of the latest release
+    buildConfigField "String", "version_file_url", '"https://dl.bitmask.net/client/android/versioncode.txt"'
+
     //ignore the following configs, only used in custom flavor
+    buildConfigField 'String', 'donation_url', 'null'
     buildConfigField "String", "customProviderUrl", '""'
     buildConfigField "String", "customProviderIp", '""'
     buildConfigField "String", "customProviderApiIp", '""'
@@ -108,6 +115,17 @@ android {
       //skip the account creation / login screen if the provider offers anonymous vpn usage, use directly the anonymous cert instead
       buildConfigField 'boolean', 'priotize_anonymous_usage', 'true'
 
+      //Build Config Fields for automatic apk update checks
+
+      // static update url pointing to the latest stable release apk
+      def apkURL = '"https://dl.bitmask.net/RiseupVPN/android/RiseupVPN-Android-latest.apk"'
+      buildConfigField "String", "update_apk_url", apkURL
+      // the the pgp signature file of the apk
+      def signatureURL = '"https://dl.bitmask.net/RiseupVPN/android/RiseupVPN-Android-latest.apk.sig"'
+      buildConfigField "String", "signature_url", signatureURL
+      // the version file should contain a single line with the versionCode of the latest release
+      buildConfigField "String", "version_file_url", '"https://dl.bitmask.net/client/android/versioncode.txt"' //'"https://dl.bitmask.net/RiseupVPN/android/versioncode.txt"'
+
       //Build Config Fields for default donation details
 
       //This is the donation URL and should be set to the relevant donation page.
@@ -130,6 +148,13 @@ android {
 
     }
 
+    fatweb {
+      dimension "abi"
+      ext {
+        abiVersionCode = 0
+        abiFilter = ""
+      }
+    }
 
     fat {
       dimension "abi"
@@ -232,6 +257,31 @@ android {
     androidTest {
       java.srcDirs += ['src/sharedTest/java']
     }
+
+    fatweb {
+      java.srcDirs += ['src/fatweb/java']
+    }
+
+    fat {
+      java.srcDirs += ['src/notFatweb/java']
+    }
+
+    x86 {
+      java.srcDirs += ['src/notFatweb/java']
+    }
+
+    x86_64 {
+      java.srcDirs += ['src/notFatweb/java']
+    }
+
+    armv7 {
+      java.srcDirs += ['src/notFatweb/java']
+    }
+
+    arm64 {
+      java.srcDirs += ['src/notFatweb/java']
+    }
+
   }
 
   /**
@@ -241,6 +291,8 @@ android {
    * --------------------
    * customProductionFatDebug -> branded development build, includes all ABIs
    * normalProductionFatDebug -> Bitmask development build, includes all ABIS
+   * customProductionFatwebDebug -> branded development build, includes all ABIs, for distribution through a download page
+   * normalProductionFatWebDebug ->  Bitmask development build, includes all ABIS, for distribution through a download page
    * customInsecureFatDebug -> branded development build, doesn't checks certificates (for test server setup w/o valid certificates), includes all ABIs
    * normalInsecureFatDebug -> Bitmask development build, doesn't checks certificates (for test server setup w/o valid certificates), includes all ABIs
    *
@@ -248,6 +300,7 @@ android {
    * -----------------
    * customProductionFatBeta -> branded build, includes all ABI's, Beta release
    * customProductionFatRelease -> branded build, includes all ABI's, stable release (-> F-Droid, GPlay if not splitApk is set to true)
+   * customProductionFatwebRelease -> branded build, includes all ABI's, stable release (-> F-Droid, GPlay if not splitApk is set to true), for distribution through a download page
    *
    * Bitmask Beta releases:
    * ----------------------
@@ -263,7 +316,8 @@ android {
    * normalProductionArmv7Release  ->  Bitmask build, only for ABI armeabi-v7a, for GPlay releases with split apks (2 of 4)
    * normalProductionX86Release    ->  Bitmask build, only for ABI x86, for GPlay releases with split apks (3 of 4)
    * normalProductionX86_64Release ->  Bitmask build, only for ABI x86 64 bit, for GPlay releases with split apks (4 of 4)
-   * normalProductionFatRelease    ->  Bitmask build, including all ABIS, for official F-Droid repo and stable link on download page
+   * normalProductionFatRelease    ->  Bitmask build, including all ABIS, for official F-Droid repo
+   * normalProductionFatWebRelease ->  Bitmask build, including all ABIS, for distribution through a download page
    */
 
   variantFilter { variant ->
@@ -276,7 +330,8 @@ android {
     if (((names.contains("insecure") && !names.contains("fat")) ||
             (names.contains("insecure") && buildTypeName.contains("beta")) ||
             (names.contains("insecure") && buildTypeName.contains("release")) ||
-            (buildTypeName.contains("debug") && !names.contains("fat")) ||
+            (buildTypeName.contains("debug") && !(names.contains("fatweb") || names.contains("fat"))) ||
+            (names.contains("fatweb") && buildTypeName.contains("beta")) ||
             (!supportsSplitApk && !names.contains("fat")))
     ) {
       // Gradle ignores any variants that satisfy the conditions above.
@@ -330,7 +385,13 @@ dependencies {
   implementation 'androidx.cardview:cardview:1.0.0'
   implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
   implementation 'de.hdodenhof:circleimageview:3.1.0'
-  implementation project(path: ':shapeshifter')
+  //implementation project(path: ':shapeshifter')
+  fatwebImplementation project(path: ':bitmask-web-core')
+  fatImplementation project(path: ':bitmask-core')
+  x86Implementation project(path: ':bitmask-core')
+  x86_64Implementation project(path: ':bitmask-core')
+  armv7Implementation project(path: ':bitmask-core')
+  arm64Implementation project(path: ':bitmask-core')
 }
 
 android.applicationVariants.all { variant ->
diff --git a/app/src/fatweb/AndroidManifest.xml b/app/src/fatweb/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..28246e5f4d794dd5eb54c447647c5d45c740d3c9
--- /dev/null
+++ b/app/src/fatweb/AndroidManifest.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="se.leap.bitmaskclient">
+
+    <!-- if you want to run test, this permissions are needed. Gradle will get rid of them once we implement it. --> 
+  <!--  <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> -->
+    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
+
+    <application>
+        <service
+            android:name=".appUpdate.DownloadService"
+            android:exported="false"
+            android:permission="android.permission.BIND_JOB_SERVICE">
+        </service>
+
+        <!-- other intent filters are added on runtime -->
+        <receiver android:name=".appUpdate.DownloadBroadcastReceiver"  android:exported="false">
+            <intent-filter>
+                <action android:name="android.intent.action.PACKAGE_INSTALL"/>
+                <data android:scheme="package"/>
+            </intent-filter>
+        </receiver>
+
+        <provider android:name="androidx.core.content.FileProvider"
+            android:authorities="${applicationId}.fileprovider"
+            android:exported="false"
+            android:grantUriPermissions="true">
+
+            <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_provider_paths" />
+
+        </provider>
+        <activity
+            android:name=".appUpdate.InstallActivity"
+            android:theme="@style/invisibleTheme" />
+    </application>
+
+
+
+
+
+</manifest>
diff --git a/app/src/fatweb/assets/public.pgp b/app/src/fatweb/assets/public.pgp
new file mode 100644
index 0000000000000000000000000000000000000000..6964df424f6246dc0b0404a078741613f676ec7b
--- /dev/null
+++ b/app/src/fatweb/assets/public.pgp
@@ -0,0 +1,458 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+=9gOD
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4acc2aa30d056da8a3dd114b0bada333d0aa78e
--- /dev/null
+++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java
@@ -0,0 +1,102 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.appUpdate;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Toast;
+
+import se.leap.bitmaskclient.Constants;
+import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.utils.PreferenceHelper;
+
+import static android.app.Activity.RESULT_CANCELED;
+import static se.leap.bitmaskclient.Constants.BROADCAST_DOWNLOAD_SERVICE_EVENT;
+import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.DOWNLOAD_PROGRESS;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.NO_NEW_VERISON;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.PROGRESS_VALUE;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_DOWNLOADED;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_DOWNLOAD_FAILED;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_FOUND;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_NOT_FOUND;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.VERIFICATION_ERROR;
+import static se.leap.bitmaskclient.appUpdate.DownloadServiceCommand.DOWNLOAD_UPDATE;
+
+public class DownloadBroadcastReceiver extends BroadcastReceiver {
+
+    public static final String ACTION_DOWNLOAD = "se.leap.bitmaskclient.appUpdate.ACTION_DOWNLOAD";
+    private static final String TAG = DownloadBroadcastReceiver.class.getSimpleName();
+
+    private DownloadNotificationManager notificationManager;
+
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+        if (action == null) {
+            return;
+        }
+
+        if (notificationManager == null) {
+            notificationManager = new DownloadNotificationManager(context.getApplicationContext());
+        }
+
+        int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, RESULT_CANCELED);
+        Bundle resultData = intent.getParcelableExtra(Constants.BROADCAST_RESULT_KEY);
+
+        switch (action) {
+            case BROADCAST_DOWNLOAD_SERVICE_EVENT:
+                switch (resultCode) {
+                    case UPDATE_FOUND:
+                        notificationManager.buildDownloadFoundNotification();
+                        break;
+                    case UPDATE_NOT_FOUND:
+                        if (resultData.getBoolean(NO_NEW_VERISON, false)) {
+                            PreferenceHelper.setLastAppUpdateCheck(context.getApplicationContext());
+                        }
+                        break;
+                    case UPDATE_DOWNLOADED:
+                        notificationManager.buildDownloadSuccessfulNotification();
+                        break;
+                    case UPDATE_DOWNLOAD_FAILED:
+                        if (resultData.getBoolean(VERIFICATION_ERROR, false)) {
+                            Toast.makeText(context.getApplicationContext(), context.getString(R.string.version_update_error_pgp_verification), Toast.LENGTH_LONG).show();
+                        } else {
+                            Toast.makeText(context.getApplicationContext(), context.getString(R.string.version_update_error), Toast.LENGTH_LONG).show();
+                        }
+                        notificationManager.cancelNotifications();
+                        break;
+                    case DOWNLOAD_PROGRESS:
+                        int progress = resultData.getInt(PROGRESS_VALUE, 0);
+                        notificationManager.buildDownloadUpdateProgress(progress);
+                        break;
+                }
+                break;
+
+            case ACTION_DOWNLOAD:
+                DownloadServiceCommand.execute(context.getApplicationContext(), DOWNLOAD_UPDATE);
+                break;
+
+            default:
+                break;
+        }
+
+    }
+}
diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java
new file mode 100644
index 0000000000000000000000000000000000000000..8723f5159681e0429a112c6df7626634c8b8b8ac
--- /dev/null
+++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java
@@ -0,0 +1,120 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.appUpdate;
+
+
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Scanner;
+
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+import okio.Buffer;
+import okio.BufferedSink;
+import okio.BufferedSource;
+import okio.Okio;
+
+
+/**
+ * This class encapsulates HTTP requests so that the results can be mocked
+ * and it's owning UpdateDownloadManager class logic can be unit tested properly
+ *
+ */
+public class DownloadConnector {
+
+    private static final String TAG = DownloadConnector.class.getSimpleName();
+    public final static String APP_TYPE = "application/vnd.android.package-archive";
+    public final static String TEXT_FILE_TYPE = "application/text";
+
+    public interface DownloadProgress {
+        void onUpdate(int progress);
+    }
+
+    static String requestTextFileFromServer(@NonNull String url, @NonNull OkHttpClient okHttpClient) {
+        try {
+            Request request = new Request.Builder()
+                    .url(url)
+                    .addHeader("Content-Type", TEXT_FILE_TYPE)
+                    .build();
+
+            Response response = okHttpClient.newCall(request).execute();
+            if (!response.isSuccessful()) {
+                return null;
+            }
+            InputStream inputStream = response.body().byteStream();
+            Scanner scanner = new Scanner(inputStream).useDelimiter("\\A");
+            if (scanner.hasNext()) {
+                return scanner.next();
+            }
+            return null;
+
+        } catch (Exception e) {
+            Log.d(TAG, "Text file download failed");
+        }
+
+        return null;
+    }
+
+    static File requestFileFromServer(@NonNull String url, @NonNull OkHttpClient okHttpClient, File destFile, DownloadProgress callback) {
+        BufferedSink sink;
+        BufferedSource source;
+        try {
+            Request.Builder requestBuilder = new Request.Builder()
+                    .url(url)
+                    .addHeader("Content-Type", APP_TYPE);
+            Request request = requestBuilder.build();
+
+            Response response = okHttpClient.newCall(request).execute();
+            ResponseBody body = response.body();
+            long contentLength = body.contentLength();
+            source = body.source();
+            sink = Okio.buffer(Okio.sink(destFile));
+            Buffer sinkBuffer = sink.buffer();
+            long totalBytesRead = 0;
+            int bufferSize = 8 * 1024;
+            long bytesRead;
+            int lastProgress = 0;
+            while ((bytesRead = source.read(sinkBuffer, bufferSize)) != -1) {
+                sink.emit();
+                totalBytesRead += bytesRead;
+                int progress = (int) ((totalBytesRead * 100) / contentLength);
+                // debouncing callbacks
+                if (lastProgress < progress) {
+                    lastProgress = progress;
+                    callback.onUpdate(progress);
+                }
+            }
+            sink.flush();
+
+            return destFile;
+
+        } catch (Exception e) {
+            Log.d(TAG, "File download failed");
+        }
+
+        return null;
+    }
+
+}
diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadNotificationManager.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadNotificationManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..aaf487aae10745f339ab424d5f8d70c7359b3182
--- /dev/null
+++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadNotificationManager.java
@@ -0,0 +1,145 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.appUpdate;
+
+import android.annotation.TargetApi;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.core.app.NotificationCompat;
+
+import se.leap.bitmaskclient.R;
+
+import static android.content.Intent.CATEGORY_DEFAULT;
+import static se.leap.bitmaskclient.appUpdate.DownloadBroadcastReceiver.ACTION_DOWNLOAD;
+
+public class DownloadNotificationManager {
+    private Context context;
+    private  final static int DOWNLOAD_NOTIFICATION_ID = 1;
+
+    public DownloadNotificationManager(@NonNull Context context) {
+        this.context = context;
+    }
+
+    public void buildDownloadFoundNotification() {
+        NotificationManager notificationManager = initNotificationManager();
+        if (notificationManager == null) {
+            return;
+        }
+        NotificationCompat.Builder notificationBuilder = initNotificationBuilderDefaults();
+        notificationBuilder
+                .setSmallIcon(R.drawable.ic_about_36)
+                .setWhen(System.currentTimeMillis())
+                .setTicker(context.getString(R.string.version_update_title, context.getString(R.string.app_name)))
+                .setContentTitle(context.getString(R.string.version_update_title, context.getString(R.string.app_name)))
+                .setContentText(context.getString(R.string.version_update_found))
+                .setContentIntent(getDownloadIntent());
+        notificationManager.notify(DOWNLOAD_NOTIFICATION_ID, notificationBuilder.build());
+    }
+
+    public void buildDownloadSuccessfulNotification() {
+        NotificationManager notificationManager = initNotificationManager();
+        if (notificationManager == null) {
+            return;
+        }
+        NotificationCompat.Builder notificationBuilder = initNotificationBuilderDefaults();
+        notificationBuilder
+                .setSmallIcon(android.R.drawable.stat_sys_download_done)
+                .setWhen(System.currentTimeMillis())
+                .setTicker(context.getString(R.string.version_update_title, context.getString(R.string.app_name)))
+                .setContentTitle(context.getString(R.string.version_update_download_title, context.getString(R.string.app_name)))
+                .setContentText(context.getString(R.string.version_update_download_description))
+                .setContentIntent(getInstallIntent());
+        notificationManager.notify(DOWNLOAD_NOTIFICATION_ID, notificationBuilder.build());
+    }
+
+    public void buildDownloadUpdateProgress(int progress) {
+        NotificationManager notificationManager = initNotificationManager();
+        if (notificationManager == null) {
+            return;
+        }
+
+        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this.context, DownloadService.NOTIFICATION_CHANNEL_NEWSTATUS_ID);
+        notificationBuilder
+                .setDefaults(Notification.DEFAULT_ALL)
+                .setAutoCancel(false)
+                .setOngoing(true)
+                .setSmallIcon(android.R.drawable.stat_sys_download)
+                .setContentTitle(context.getString(R.string.version_update_apk_description, context.getString(R.string.app_name)))
+                .setProgress(100, progress, false)
+                .setContentIntent(getDownloadIntent());
+        notificationManager.notify(DOWNLOAD_NOTIFICATION_ID, notificationBuilder.build());
+    }
+
+    private NotificationManager initNotificationManager() {
+        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+        if (notificationManager == null) {
+            return null;
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            createNotificationChannel(notificationManager);
+        }
+        return notificationManager;
+    }
+
+    @TargetApi(26)
+    private void createNotificationChannel(NotificationManager notificationManager) {
+        CharSequence name = "Bitmask Updates";
+        String description = "Informs about available updates";
+        NotificationChannel channel = new NotificationChannel(DownloadService.NOTIFICATION_CHANNEL_NEWSTATUS_ID,
+                name,
+                NotificationManager.IMPORTANCE_LOW);
+        channel.setSound(null, null);
+        channel.setDescription(description);
+        // Register the channel with the system; you can't change the importance
+        // or other notification behaviors after this
+        notificationManager.createNotificationChannel(channel);
+    }
+
+    private NotificationCompat.Builder initNotificationBuilderDefaults() {
+        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this.context, DownloadService.NOTIFICATION_CHANNEL_NEWSTATUS_ID);
+        notificationBuilder.
+                setDefaults(Notification.DEFAULT_ALL).
+                setAutoCancel(true);
+        return notificationBuilder;
+    }
+
+    private PendingIntent getDownloadIntent() {
+        Intent downloadIntent = new Intent(context, DownloadBroadcastReceiver.class);
+        downloadIntent.setAction(ACTION_DOWNLOAD);
+        return PendingIntent.getBroadcast(context, 0, downloadIntent, PendingIntent.FLAG_CANCEL_CURRENT);
+    }
+
+    private PendingIntent getInstallIntent() {
+        Intent installIntent = new Intent(context, InstallActivity.class);
+        return PendingIntent.getActivity(context, 0, installIntent, PendingIntent.FLAG_CANCEL_CURRENT);
+    }
+
+    public void cancelNotifications() {
+        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+        if (notificationManager == null) {
+            return;
+        }
+        notificationManager.cancel(DOWNLOAD_NOTIFICATION_ID);
+    }
+}
diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadService.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadService.java
new file mode 100644
index 0000000000000000000000000000000000000000..bc9adfc1d6211289e1dd042feddd64f5fcecd5cf
--- /dev/null
+++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadService.java
@@ -0,0 +1,82 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.appUpdate;
+
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.annotation.NonNull;
+import androidx.core.app.JobIntentService;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import se.leap.bitmaskclient.OkHttpClientGenerator;
+
+public class DownloadService extends JobIntentService implements UpdateDownloadManager.DownloadServiceCallback {
+
+    static final int JOB_ID = 161376;
+    static final String NOTIFICATION_CHANNEL_NEWSTATUS_ID = "bitmask_download_service_news";
+
+    final public static String TAG = DownloadService.class.getSimpleName(),
+            PROGRESS_VALUE = "progressValue",
+            NO_NEW_VERISON = "noNewVersion",
+            DOWNLOAD_FAILED = "downloadFailed",
+            NO_PUB_KEY = "noPubKey",
+            VERIFICATION_ERROR = "verificationError";
+
+    final public static int
+            UPDATE_DOWNLOADED = 1,
+            UPDATE_DOWNLOAD_FAILED = 2,
+            UPDATE_FOUND = 3,
+            UPDATE_NOT_FOUND = 4,
+            DOWNLOAD_PROGRESS = 6;
+
+
+    private UpdateDownloadManager updateDownloadManager;
+
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        updateDownloadManager = initDownloadManager();
+    }
+
+    @Override
+    protected void onHandleWork(@NonNull Intent intent) {
+        updateDownloadManager.handleIntent(intent);
+    }
+
+    /**
+     * Convenience method for enqueuing work in to this service.
+     */
+    static void enqueueWork(Context context, Intent work) {
+        try {
+            DownloadService.enqueueWork(context, DownloadService.class, JOB_ID, work);
+        } catch (IllegalStateException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private UpdateDownloadManager initDownloadManager() {
+        OkHttpClientGenerator clientGenerator = new OkHttpClientGenerator(null);
+        return new UpdateDownloadManager(this, clientGenerator, this);
+    }
+
+    @Override
+    public void broadcastEvent(Intent intent) {
+        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
+    }
+}
diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadServiceCommand.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadServiceCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4e809f2ddc4eb2078f35fa0e7ba924cb4a7f33d
--- /dev/null
+++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadServiceCommand.java
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.appUpdate;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.ResultReceiver;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import se.leap.bitmaskclient.ProviderAPI;
+
+public class DownloadServiceCommand {
+
+    public final static String
+            CHECK_VERSION_FILE = "checkVersionFile",
+            DOWNLOAD_UPDATE = "downloadUpdate";
+
+    private Context context;
+    private String action;
+    private ResultReceiver resultReceiver;
+
+    private DownloadServiceCommand(@NotNull Context context, @NotNull String action) {
+        this(context.getApplicationContext(), action, null);
+    }
+
+    private DownloadServiceCommand(@NotNull Context context, @NotNull String action, @Nullable ResultReceiver resultReceiver) {
+        super();
+        this.context = context;
+        this.action = action;
+        this.resultReceiver = resultReceiver;
+    }
+
+
+    private Intent setUpIntent() {
+        Intent command = new Intent(context, ProviderAPI.class);
+        command.setAction(action);
+        if (resultReceiver != null) {
+            command.putExtra(ProviderAPI.RECEIVER_KEY, resultReceiver);
+        }
+        return command;
+    }
+
+    private boolean isInitialized() {
+        return context != null;
+    }
+
+
+    private void execute() {
+        if (isInitialized()) {
+            Intent intent = setUpIntent();
+            DownloadService.enqueueWork(context, intent);
+        }
+    }
+
+    public static void execute(Context context, String action) {
+        DownloadServiceCommand command = new DownloadServiceCommand(context, action);
+        command.execute();
+    }
+
+    public static void execute(Context context, String action, ResultReceiver resultReceiver) {
+        DownloadServiceCommand command = new DownloadServiceCommand(context, action, resultReceiver);
+        command.execute();
+    }
+
+}
diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/FileProviderUtil.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/FileProviderUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..4966a863f015694eaf0e65ba53faed91266251bc
--- /dev/null
+++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/FileProviderUtil.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.appUpdate;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
+import androidx.annotation.NonNull;
+import androidx.core.content.FileProvider;
+
+import java.io.File;
+
+import se.leap.bitmaskclient.BuildConfig;
+
+public class FileProviderUtil {
+
+    private static final String AUTHORITY = BuildConfig.APPLICATION_ID +".fileprovider";
+
+    public static Uri getUriFor(@NonNull Context context, @NonNull File file) {
+        if (Build.VERSION.SDK_INT >= 24) return FileProvider.getUriForFile(context, AUTHORITY, file);
+        else                             return Uri.fromFile(file);
+    }
+}
diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..6629425c6859ee7575e4abd49d27ef49588ba2f8
--- /dev/null
+++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.appUpdate;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+
+import java.io.File;
+
+import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.utils.PreferenceHelper;
+
+import static se.leap.bitmaskclient.Constants.REQUEST_CODE_REQUEST_UPDATE;
+import static se.leap.bitmaskclient.appUpdate.DownloadConnector.APP_TYPE;
+import static se.leap.bitmaskclient.appUpdate.FileProviderUtil.getUriFor;
+
+public class InstallActivity extends Activity {
+
+    private static final String TAG = InstallActivity.class.getSimpleName();
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        requestPermissionAndInstall();
+    }
+
+    private void requestPermissionAndInstall() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !this.getPackageManager().canRequestPackageInstalls()) {
+            startActivityForResult(new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:"+getPackageName())),
+                    REQUEST_CODE_REQUEST_UPDATE);
+        } else {
+            installUpdate();
+        }
+    }
+
+    protected void installUpdate() {
+        PreferenceHelper.restartOnUpdate(this.getApplicationContext(), true);
+
+        Intent installIntent = new Intent(Intent.ACTION_VIEW);
+        File update = UpdateDownloadManager.getUpdateFile(this.getApplicationContext());
+        if (update.exists()) {
+            installIntent.setDataAndType(getUriFor(this.getApplicationContext(), update), APP_TYPE);
+            installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            this.startActivity(installIntent);
+            finish();
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        if (requestCode == REQUEST_CODE_REQUEST_UPDATE) {
+            if (resultCode == RESULT_OK) {
+                installUpdate();
+            } else {
+                Toast.makeText(this, getString(R.string.version_update_error_permissions), Toast.LENGTH_LONG).show();
+                finish();
+            }
+        }
+    }
+}
diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/UpdateDownloadManager.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/UpdateDownloadManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..b79c2a911ac03b9e133b12d39e0f90a1582f599f
--- /dev/null
+++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/UpdateDownloadManager.java
@@ -0,0 +1,211 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.appUpdate;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+import java.io.File;
+
+import okhttp3.OkHttpClient;
+import pgpverify.Logger;
+import pgpverify.PgpVerifier;
+import se.leap.bitmaskclient.BuildConfig;
+import se.leap.bitmaskclient.OkHttpClientGenerator;
+import se.leap.bitmaskclient.R;
+
+import static android.text.TextUtils.isEmpty;
+import static se.leap.bitmaskclient.Constants.BROADCAST_DOWNLOAD_SERVICE_EVENT;
+import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE;
+import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY;
+import static se.leap.bitmaskclient.ProviderAPI.RECEIVER_KEY;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.DOWNLOAD_FAILED;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.DOWNLOAD_PROGRESS;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.NO_NEW_VERISON;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.NO_PUB_KEY;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.PROGRESS_VALUE;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_DOWNLOADED;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_DOWNLOAD_FAILED;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_FOUND;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_NOT_FOUND;
+import static se.leap.bitmaskclient.appUpdate.DownloadService.VERIFICATION_ERROR;
+import static se.leap.bitmaskclient.appUpdate.DownloadServiceCommand.CHECK_VERSION_FILE;
+import static se.leap.bitmaskclient.appUpdate.DownloadServiceCommand.DOWNLOAD_UPDATE;
+import static se.leap.bitmaskclient.utils.FileHelper.readPublicKey;
+
+public class UpdateDownloadManager implements Logger, DownloadConnector.DownloadProgress {
+
+
+    private static final String TAG = UpdateDownloadManager.class.getSimpleName();
+
+    public interface DownloadServiceCallback {
+        void broadcastEvent(Intent intent);
+    }
+
+    private Context context;
+
+    private PgpVerifier pgpVerifier;
+    private DownloadServiceCallback serviceCallback;
+    OkHttpClientGenerator clientGenerator;
+
+
+    public UpdateDownloadManager(Context context, OkHttpClientGenerator clientGenerator, DownloadServiceCallback callback) {
+        this.context = context;
+        this.clientGenerator = clientGenerator;
+        pgpVerifier = new PgpVerifier();
+        pgpVerifier.setLogger(this);
+        serviceCallback = callback;
+    }
+
+    //pgpverify Logger interface
+    @Override
+    public void log(String s) {
+
+    }
+
+    @Override
+    public void onUpdate(int progress) {
+        Bundle resultData = new Bundle();
+        resultData.putInt(PROGRESS_VALUE, progress);
+        broadcastEvent(DOWNLOAD_PROGRESS, resultData);
+    }
+
+    public void handleIntent(Intent command) {
+        ResultReceiver receiver = null;
+        if (command.getParcelableExtra(RECEIVER_KEY) != null) {
+            receiver = command.getParcelableExtra(RECEIVER_KEY);
+        }
+        String action = command.getAction();
+
+        Bundle result = new Bundle();
+        switch (action) {
+            case CHECK_VERSION_FILE:
+                result = checkVersionFile(result);
+                if (result.getBoolean(BROADCAST_RESULT_KEY)) {
+                    sendToReceiverOrBroadcast(receiver, UPDATE_FOUND, result);
+                } else {
+                    sendToReceiverOrBroadcast(receiver, UPDATE_NOT_FOUND, result);
+                }
+                break;
+            case DOWNLOAD_UPDATE:
+                result = downloadUpdate(result);
+                if (result.getBoolean(BROADCAST_RESULT_KEY)) {
+                    sendToReceiverOrBroadcast(receiver, UPDATE_DOWNLOADED, result);
+                } else {
+                    sendToReceiverOrBroadcast(receiver, UPDATE_DOWNLOAD_FAILED, result);
+                }
+                break;
+        }
+    }
+
+    public static File getUpdateFile(Context context) {
+        return new File(context.getExternalFilesDir(null) + "/" + context.getString(R.string.app_name) + "_update.apk");
+    }
+
+    private Bundle downloadUpdate(Bundle task) {
+
+        String publicKey = readPublicKey(context);
+        if (isEmpty(publicKey)) {
+            task.putBoolean(BROADCAST_RESULT_KEY, false);
+            task.putBoolean(NO_PUB_KEY, true);
+            return task;
+        }
+
+        OkHttpClient client = clientGenerator.init();
+        String signature = DownloadConnector.requestTextFileFromServer(BuildConfig.signature_url, client);
+        if (signature == null) {
+            task.putBoolean(BROADCAST_RESULT_KEY, false);
+            task.putBoolean(DOWNLOAD_FAILED, true);
+            return task;
+        }
+
+        File destinationFile = getUpdateFile(context);
+        if (destinationFile.exists()) {
+            destinationFile.delete();
+        }
+
+        destinationFile = DownloadConnector.requestFileFromServer(BuildConfig.update_apk_url, client, destinationFile, this);
+
+        if (destinationFile == null) {
+            task.putBoolean(BROADCAST_RESULT_KEY, false);
+            task.putBoolean(DOWNLOAD_FAILED, true);
+            return task;
+        }
+
+        boolean successfulVerified = pgpVerifier.verify(signature, publicKey, destinationFile.getAbsolutePath());
+        if (!successfulVerified) {
+            destinationFile.delete();
+            task.putBoolean(BROADCAST_RESULT_KEY, false);
+            task.putBoolean(VERIFICATION_ERROR, true);
+            return task;
+        }
+
+        task.putBoolean(BROADCAST_RESULT_KEY, true);
+        return task;
+    }
+
+    private Bundle checkVersionFile(Bundle task) {
+        OkHttpClient client = clientGenerator.init();
+        String versionString = DownloadConnector.requestTextFileFromServer(BuildConfig.version_file_url, client);
+
+        if (versionString != null) {
+            versionString = versionString.replace("\n", "").trim();
+        }
+
+        int version = -1;
+        try {
+            version = Integer.parseInt(versionString);
+        } catch (NumberFormatException e) {
+            e.printStackTrace();
+            Log.e(TAG, "could not parse version code: " + versionString);
+        }
+
+        if (version == -1) {
+            task.putBoolean(BROADCAST_RESULT_KEY, false);
+            task.putBoolean(DOWNLOAD_FAILED, true);
+        } else if (BuildConfig.VERSION_CODE >= version) {
+            task.putBoolean(BROADCAST_RESULT_KEY, false);
+            task.putBoolean(NO_NEW_VERISON, true);
+        } else {
+            task.putBoolean(BROADCAST_RESULT_KEY, true);
+        }
+        return task;
+    }
+
+    private void sendToReceiverOrBroadcast(ResultReceiver receiver, int resultCode, Bundle resultData) {
+        if (resultData == null || resultData == Bundle.EMPTY) {
+            resultData = new Bundle();
+        }
+        if (receiver != null) {
+            receiver.send(resultCode, resultData);
+        } else {
+            broadcastEvent(resultCode, resultData);
+        }
+    }
+
+    private void broadcastEvent(int resultCode , Bundle resultData) {
+        Intent intentUpdate = new Intent(BROADCAST_DOWNLOAD_SERVICE_EVENT);
+        intentUpdate.addCategory(Intent.CATEGORY_DEFAULT);
+        intentUpdate.putExtra(BROADCAST_RESULT_CODE, resultCode);
+        intentUpdate.putExtra(BROADCAST_RESULT_KEY, resultData);
+        serviceCallback.broadcastEvent(intentUpdate);
+    }
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java b/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java
index bde5114b6b4187099afea494e586f8e93be1baaa..437998e0c0f753bd971f2e210728063d9e9678c2 100644
--- a/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java
+++ b/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java
@@ -1,16 +1,42 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
 package se.leap.bitmaskclient;
 
 import android.content.Context;
+import android.content.IntentFilter;
 import android.content.SharedPreferences;
-import androidx.multidex.MultiDexApplication;
+
 import androidx.appcompat.app.AppCompatDelegate;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import androidx.multidex.MultiDexApplication;
 
 import com.squareup.leakcanary.LeakCanary;
 import com.squareup.leakcanary.RefWatcher;
 
+import se.leap.bitmaskclient.appUpdate.DownloadBroadcastReceiver;
 import se.leap.bitmaskclient.tethering.TetheringStateManager;
 
+import static android.content.Intent.CATEGORY_DEFAULT;
+import static se.leap.bitmaskclient.Constants.BROADCAST_DOWNLOAD_SERVICE_EVENT;
 import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;
+import static se.leap.bitmaskclient.appUpdate.DownloadBroadcastReceiver.ACTION_DOWNLOAD;
+import static se.leap.bitmaskclient.appUpdate.DownloadServiceCommand.CHECK_VERSION_FILE;
+import static se.leap.bitmaskclient.appUpdate.DownloadServiceCommand.DOWNLOAD_UPDATE;
 import static se.leap.bitmaskclient.utils.PreferenceHelper.getSavedProviderFromSharedPreferences;
 
 /**
@@ -22,6 +48,7 @@ public class BitmaskApp extends MultiDexApplication {
     private final static String TAG = BitmaskApp.class.getSimpleName();
     private RefWatcher refWatcher;
     private ProviderObservable providerObservable;
+    private DownloadBroadcastReceiver downloadBroadcastReceiver;
 
 
     @Override
@@ -41,6 +68,15 @@ public class BitmaskApp extends MultiDexApplication {
         EipSetupObserver.init(this, preferences);
         AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
         TetheringStateManager.getInstance().init(this);
+        if (BuildConfig.FLAVOR.contains("Fatweb")) {
+            downloadBroadcastReceiver = new DownloadBroadcastReceiver();
+            IntentFilter intentFilter = new IntentFilter(BROADCAST_DOWNLOAD_SERVICE_EVENT);
+            intentFilter.addAction(ACTION_DOWNLOAD);
+            intentFilter.addAction(CHECK_VERSION_FILE);
+            intentFilter.addAction(DOWNLOAD_UPDATE);
+            intentFilter.addCategory(CATEGORY_DEFAULT);
+            LocalBroadcastManager.getInstance(this.getApplicationContext()).registerReceiver(downloadBroadcastReceiver, intentFilter);
+        }
     }
 
     /**
diff --git a/app/src/main/java/se/leap/bitmaskclient/ButterKnifeActivity.java b/app/src/main/java/se/leap/bitmaskclient/ButterKnifeActivity.java
index 0ef77e2b287c5eb2e58d91b35cdc589460fa9050..4f27f88a8d8ca38de1c735e3e70abdcec212baad 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ButterKnifeActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/ButterKnifeActivity.java
@@ -1,3 +1,19 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
 package se.leap.bitmaskclient;
 
 import androidx.appcompat.app.AppCompatActivity;
diff --git a/app/src/main/java/se/leap/bitmaskclient/Constants.java b/app/src/main/java/se/leap/bitmaskclient/Constants.java
index 6462b6634bba9bf908101860c6fa51a68bbb2fd2..1d364074c5e0d72d8c235954bd6bb1419df550a7 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Constants.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Constants.java
@@ -1,3 +1,19 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
 package se.leap.bitmaskclient;
 
 import android.text.TextUtils;
@@ -20,6 +36,8 @@ public interface Constants {
     String ALLOW_TETHERING_USB = "tethering_usb";
     String SHOW_EXPERIMENTAL = "show_experimental";
     String USE_IPv6_FIREWALL = "use_ipv6_firewall";
+    String RESTART_ON_UPDATE = "restart_on_update";
+    String LAST_UPDATE_CHECK = "last_update_check";
 
 
      //////////////////////////////////////////////
@@ -31,6 +49,7 @@ public interface Constants {
     int REQUEST_CODE_SWITCH_PROVIDER = 1;
     int REQUEST_CODE_LOG_IN = 2;
     int REQUEST_CODE_ADD_PROVIDER = 3;
+    int REQUEST_CODE_REQUEST_UPDATE = 4;
 
 
     //////////////////////////////////////////////
@@ -105,6 +124,7 @@ public interface Constants {
     String BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT = "BROADCAST.GATEWAY_SETUP_WATCHER_EVENT";
     String BROADCAST_RESULT_CODE = "BROADCAST.RESULT_CODE";
     String BROADCAST_RESULT_KEY = "BROADCAST.RESULT_KEY";
+    String BROADCAST_DOWNLOAD_SERVICE_EVENT = "BROADCAST.DOWNLOAD_SERVICE_EVENT";
 
 
     //////////////////////////////////////////////
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java b/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java
index 7504e0c04d276e820d03a7a43aba152d244baa01..e365c8574430e9dbbef5eebb725af8b9392d908e 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java
@@ -1,3 +1,20 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
 package se.leap.bitmaskclient;
 
 import android.content.BroadcastReceiver;
@@ -21,6 +38,7 @@ import de.blinkt.openvpn.VpnProfile;
 import de.blinkt.openvpn.core.ConnectionStatus;
 import de.blinkt.openvpn.core.LogItem;
 import de.blinkt.openvpn.core.VpnStatus;
+import se.leap.bitmaskclient.appUpdate.DownloadServiceCommand;
 import se.leap.bitmaskclient.eip.EIP;
 import se.leap.bitmaskclient.eip.EipCommand;
 import se.leap.bitmaskclient.eip.EipStatus;
@@ -48,6 +66,7 @@ import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE
 import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_GEOIP_JSON;
 import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE;
 import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_GEOIP_JSON;
+import static se.leap.bitmaskclient.appUpdate.DownloadServiceCommand.CHECK_VERSION_FILE;
 
 /**
  * Created by cyberta on 05.12.18.
@@ -58,6 +77,7 @@ class EipSetupObserver extends BroadcastReceiver implements VpnStatus.StateListe
 
     //The real timout is 4*2s + 1*4s + 1*8s + 1*16s + 1*32s + 1*64s = 132 s;
     private static final String TIMEOUT = "4";
+    private static final int UPDATE_CHECK_TIMEOUT = 1000*60*60*24*7;
     private Context context;
     private VpnProfile setupVpnProfile;
     private String observedProfileFromVpnStatus;
@@ -292,12 +312,20 @@ class EipSetupObserver extends BroadcastReceiver implements VpnStatus.StateListe
                 //setupNClostestGateway > 0: at least one failed gateway -> did the provider change it's gateways?
                 ProviderAPICommand.execute(context, ProviderAPI.DOWNLOAD_SERVICE_JSON, provider);
             }
+
+            if (shouldCheckAppUpdate()) {
+                DownloadServiceCommand.execute(context, CHECK_VERSION_FILE);
+            }
             finishGatewaySetup(false);
         } else if ("TCP_CONNECT".equals(state)) {
             changingGateway.set(false);
         }
     }
 
+    private boolean shouldCheckAppUpdate() {
+        return System.currentTimeMillis() - PreferenceHelper.getLastAppUpdateCheck(context) >= UPDATE_CHECK_TIMEOUT;
+    }
+
     private void selectNextGateway() {
         changingGateway.set(true);
         reconnectTry.set(0);
diff --git a/app/src/main/java/se/leap/bitmaskclient/FeatureVersionCode.java b/app/src/main/java/se/leap/bitmaskclient/FeatureVersionCode.java
index 3b67d96b7627baabc20e55e74c6188bea6c91fe5..519e4fc21c1e60c65ed33c373eae457eb59dfb3a 100644
--- a/app/src/main/java/se/leap/bitmaskclient/FeatureVersionCode.java
+++ b/app/src/main/java/se/leap/bitmaskclient/FeatureVersionCode.java
@@ -1,7 +1,6 @@
 package se.leap.bitmaskclient;
 
 public interface FeatureVersionCode {
-    int MULTIPLE_PROFILES = 132;
     int RENAMED_EIP_IN_PREFERENCES = 132;
     int GEOIP_SERVICE = 148;
 }
diff --git a/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java b/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java
index 7b9874e0d1cc3b84de2dde595de121c1912b3dda..576e76e054cc8f10cb460ddb886d8e6a817b66eb 100644
--- a/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java
@@ -62,7 +62,7 @@ public class OkHttpClientGenerator {
 
     Resources resources;
 
-    public OkHttpClientGenerator(SharedPreferences preferences, Resources resources) {
+    public OkHttpClientGenerator(/*SharedPreferences preferences,*/ Resources resources) {
         this.resources = resources;
     }
 
@@ -74,22 +74,21 @@ public class OkHttpClientGenerator {
         return initHttpClient(initError, caCert);
     }
 
+    public OkHttpClient init() {
+        try {
+            return createClient(null);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
     private OkHttpClient initHttpClient(JSONObject initError, String certificate) {
+        if (resources == null) {
+            return null;
+        }
         try {
-            TLSCompatSocketFactory sslCompatFactory;
-            ConnectionSpec spec = getConnectionSpec();
-            OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
-
-            if (!isEmpty(certificate)) {
-                sslCompatFactory = new TLSCompatSocketFactory(certificate);
-            } else {
-                sslCompatFactory = new TLSCompatSocketFactory();
-            }
-            sslCompatFactory.initSSLSocketFactory(clientBuilder);
-            clientBuilder.cookieJar(getCookieJar())
-                    .connectionSpecs(Collections.singletonList(spec));
-            clientBuilder.dns(new DnsResolver());
-            return clientBuilder.build();
+            return createClient(certificate);
         } catch (IllegalArgumentException e) {
             e.printStackTrace();
             // TODO ca cert is invalid - show better error ?!
@@ -110,10 +109,31 @@ public class OkHttpClientGenerator {
         } catch (IOException e) {
             e.printStackTrace();
             addErrorMessageToJson(initError, resources.getString(error_io_exception_user_message));
+        } catch (Exception e) {
+            e.printStackTrace();
+            // unexpected exception, should never happen
+            // only to shorten the method signature createClient(String certificate)
         }
         return null;
     }
 
+    private OkHttpClient createClient(String certificate) throws Exception {
+        TLSCompatSocketFactory sslCompatFactory;
+        ConnectionSpec spec = getConnectionSpec();
+        OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
+
+        if (!isEmpty(certificate)) {
+            sslCompatFactory = new TLSCompatSocketFactory(certificate);
+        } else {
+            sslCompatFactory = new TLSCompatSocketFactory();
+        }
+        sslCompatFactory.initSSLSocketFactory(clientBuilder);
+        clientBuilder.cookieJar(getCookieJar())
+                .connectionSpecs(Collections.singletonList(spec));
+        clientBuilder.dns(new DnsResolver());
+        return clientBuilder.build();
+    }
+
 
 
     @NonNull
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java
index 4058b824973856469b4b6458b36d3c7e0e263155..bec1613903a5aa756d7a252f8d1c6de66eaddf4f 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java
@@ -120,7 +120,7 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB
 
     private ProviderApiManager initApiManager() {
         SharedPreferences preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
-        OkHttpClientGenerator clientGenerator = new OkHttpClientGenerator(preferences, getResources());
+        OkHttpClientGenerator clientGenerator = new OkHttpClientGenerator(getResources());
         return new ProviderApiManager(preferences, getResources(), clientGenerator, this);
     }
 }
diff --git a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java
index 9937eeebca730c8196d00bbd03c14e8e43fae7cb..1a679b1cb6aeff12035dd52ddbab77327e8f3d02 100644
--- a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java
@@ -133,9 +133,6 @@ public class StartActivity extends Activity{
      * execute necessary upgrades for version change
      */
     private void executeUpgrade() {
-        if (hasNewFeature(FeatureVersionCode.MULTIPLE_PROFILES)) {
-            // TODO prepare usage of multiple profiles
-        }
         if (hasNewFeature(FeatureVersionCode.RENAMED_EIP_IN_PREFERENCES)) {
             String eipJson = preferences.getString(PROVIDER_KEY, null);
             if (eipJson != null) {
@@ -183,9 +180,14 @@ public class StartActivity extends Activity{
                 if (getIntent() != null && getIntent().getBooleanExtra(EIP_RESTART_ON_BOOT, false)) {
                     EipCommand.startVPN(this.getApplicationContext(), true);
                     finish();
-                    return;
+                } else if (PreferenceHelper.getRestartOnUpdate(this.getApplicationContext())) {
+                    PreferenceHelper.restartOnUpdate(this.getApplicationContext(), false);
+                    EipCommand.startVPN(this.getApplicationContext(), false);
+                    showMainActivity();
+                    finish();
+                } else {
+                    showMainActivity();
                 }
-                showMainActivity();
             }
         } else {
             configureLeapProvider();
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java
index 1ee3265447002570159ee54bcadb3f4246d958b9..25450f5664f53d549a72fb3dab4cc08ba5d2b0b6 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java
@@ -4,9 +4,9 @@ import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.os.ResultReceiver;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
-import androidx.core.content.ContextCompat;
 
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -38,7 +38,6 @@ public class EipCommand {
      * @param resultReceiver The resultreceiver to reply to
      */
     private static void execute(@NotNull Context context, @NotNull String action, @Nullable ResultReceiver resultReceiver, @Nullable Intent vpnIntent) {
-        // TODO validate "action"...how do we get the list of intent-filters for a class via Android API?
         if (vpnIntent == null) {
             vpnIntent = new Intent();
         }
diff --git a/app/src/main/java/se/leap/bitmaskclient/utils/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/ConfigHelper.java
index 2748c944ec3bc127433bfc9a573d7293e89dd1d6..5a142d9043e7fd919b8a1bfd3c4e1a512d56b594 100644
--- a/app/src/main/java/se/leap/bitmaskclient/utils/ConfigHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/utils/ConfigHelper.java
@@ -53,6 +53,7 @@ import static se.leap.bitmaskclient.Constants.DEFAULT_BITMASK;
 
 /**
  * Stores constants, and implements auxiliary methods used across all Bitmask Android classes.
+ * Wraps BuildConfigFields for to support easier unit testing
  *
  * @author parmegv
  * @author MeanderingCode
@@ -120,7 +121,7 @@ public class ConfigHelper {
         try {
             KeyFactory kf;
             if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
-               kf = KeyFactory.getInstance("RSA", "BC");
+                kf = KeyFactory.getInstance("RSA", "BC");
             } else {
                 kf = KeyFactory.getInstance("RSA");
             }
@@ -201,4 +202,29 @@ public class ConfigHelper {
         return (string1 == null && string2 == null) ||
                 (string1 != null && string1.equals(string2));
     }
+
+    public static String getApkFileName() {
+        try {
+            return BuildConfig.update_apk_url.substring(BuildConfig.update_apk_url.lastIndexOf("/"));
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public static String getVersionFileName() {
+        try {
+            return BuildConfig.version_file_url.substring(BuildConfig.version_file_url.lastIndexOf("/"));
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public static String getSignatureFileName() {
+        try {
+            return BuildConfig.signature_url.substring(BuildConfig.signature_url.lastIndexOf("/"));
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
 }
diff --git a/app/src/main/java/se/leap/bitmaskclient/utils/FileHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/FileHelper.java
index 1c3e1ebb3b7f37bc27ff575ab6c2760551349a3d..ebcc32ba7a523608946adf4fa6a7bfa5638522c7 100644
--- a/app/src/main/java/se/leap/bitmaskclient/utils/FileHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/utils/FileHelper.java
@@ -1,8 +1,13 @@
 package se.leap.bitmaskclient.utils;
 
+import android.content.Context;
+
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 
 /**
  * Created by cyberta on 18.03.18.
@@ -19,4 +24,23 @@ public class FileHelper {
         writer.close();
     }
 
+    public static String readPublicKey(Context context) {
+        {
+            InputStream inputStream;
+            try {
+                inputStream = context.getAssets().open("public.pgp");
+                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
+                StringBuilder sb = new StringBuilder();
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    sb.append(line).append("\n");
+                }
+                reader.close();
+                return sb.toString();
+            } catch (IOException errabi) {
+                return null;
+            }
+        }
+    }
+
 }
diff --git a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java
index cb2aeb26204eac7354f0011a9f9ed89db8791c36..5b62d0ffadb539cf7359194ebd2751092120fcfa 100644
--- a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java
@@ -22,11 +22,13 @@ import static se.leap.bitmaskclient.Constants.ALLOW_TETHERING_WIFI;
 import static se.leap.bitmaskclient.Constants.ALWAYS_ON_SHOW_DIALOG;
 import static se.leap.bitmaskclient.Constants.DEFAULT_SHARED_PREFS_BATTERY_SAVER;
 import static se.leap.bitmaskclient.Constants.EXCLUDED_APPS;
+import static se.leap.bitmaskclient.Constants.LAST_UPDATE_CHECK;
 import static se.leap.bitmaskclient.Constants.LAST_USED_PROFILE;
 import static se.leap.bitmaskclient.Constants.PROVIDER_CONFIGURED;
 import static se.leap.bitmaskclient.Constants.PROVIDER_EIP_DEFINITION;
 import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY;
 import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;
+import static se.leap.bitmaskclient.Constants.RESTART_ON_UPDATE;
 import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;
 import static se.leap.bitmaskclient.Constants.SHOW_EXPERIMENTAL;
 import static se.leap.bitmaskclient.Constants.USE_IPv6_FIREWALL;
@@ -121,6 +123,22 @@ public class PreferenceHelper {
                 apply();
     }
 
+    public static void setLastAppUpdateCheck(Context context) {
+        putLong(context, LAST_UPDATE_CHECK, System.currentTimeMillis());
+    }
+
+    public static long getLastAppUpdateCheck(Context context) {
+        return getLong(context, LAST_UPDATE_CHECK, 0);
+    }
+
+    public static void restartOnUpdate(Context context, boolean isEnabled) {
+        putBoolean(context, RESTART_ON_UPDATE, isEnabled);
+    }
+
+    public static boolean getRestartOnUpdate(Context context) {
+        return getBoolean(context, RESTART_ON_UPDATE, false);
+    }
+
     public static boolean getUsePluggableTransports(Context context) {
         return getBoolean(context, USE_PLUGGABLE_TRANSPORTS, false);
     }
@@ -214,6 +232,16 @@ public class PreferenceHelper {
         return preferences.getStringSet(EXCLUDED_APPS, new HashSet<>());
     }
 
+    public static long getLong(Context context, String key, long defValue) {
+        SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
+        return preferences.getLong(key, defValue);
+    }
+
+    public static void putLong(Context context, String key, long value) {
+        SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
+        preferences.edit().putLong(key, value).apply();
+    }
+
     public static String getString(Context context, String key, String defValue) {
         SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
         return preferences.getString(key, defValue);
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6d4c62e1032e55bc6f55770976b1628e4295f86d..d047da2192b18bb8ab3231d73487ea61644c90c5 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -142,4 +142,14 @@
   <string name="root_permission_error">%s cannot execute features like VPN Hotspot or IPv6 firewall without root permissions.</string>
   
   <string name="qs_enable_vpn">Start %s</string>
+  <string name="version_update_found">Tap here to start the download.</string>
+  <string name="version_update_title">A new %s version has been found.</string>
+  <string name="version_update_apk_description">Downloading a new %s version</string>
+  <string name="version_update_storage_access_required">Storage access is required to web update.</string>
+  <string name="version_update_storage_permission_denied">Storage permission request was denied.</string>
+  <string name="version_update_download_title">A new %s version has been downloaded.</string>
+  <string name="version_update_download_description">Tap here to install the update.</string>
+  <string name="version_update_error_pgp_verification">PGP verification error. Ignoring download.</string>
+  <string name="version_update_error">Update failed.</string>
+  <string name="version_update_error_permissions">No permissions to install app.</string>
 </resources>
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 7e98ccf467c3230e45e0dd03164492cc38101ae8..ab489fc63e1bff1c661290d3e3cffb6242914033 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -22,6 +22,8 @@
 
     <style name="invisibleTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
         <item name="android:windowAnimationStyle">@null</item>
+        <item name="android:windowDisablePreview">true</item>
+        <item name="android:windowFullscreen">true</item>
     </style>
 
 </resources>
diff --git a/app/src/main/res/xml/file_provider_paths.xml b/app/src/main/res/xml/file_provider_paths.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5b9dd9fb9637afa70404e5c584fb7920c537fa8b
--- /dev/null
+++ b/app/src/main/res/xml/file_provider_paths.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths>
+    <external-path
+        name="external"
+        path="." />
+    <external-files-path
+        name="external_files"
+        path="." />
+    <files-path
+        name="files"
+        path="." />
+</paths>
\ No newline at end of file
diff --git a/app/src/notFatweb/java/se/leap/bitmaskclient/appUpdate/DownloadBroadcastReceiver.java b/app/src/notFatweb/java/se/leap/bitmaskclient/appUpdate/DownloadBroadcastReceiver.java
new file mode 100644
index 0000000000000000000000000000000000000000..7adbb85df07ac23fad424deb7bea874801353317
--- /dev/null
+++ b/app/src/notFatweb/java/se/leap/bitmaskclient/appUpdate/DownloadBroadcastReceiver.java
@@ -0,0 +1,18 @@
+package se.leap.bitmaskclient.appUpdate;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * DownloadBroadcastReceiver is only implemented in Fatweb builds
+ *
+ */
+public class DownloadBroadcastReceiver extends BroadcastReceiver {
+
+    public static final String ACTION_DOWNLOAD = "se.leap.bitmaskclient.appUpdate.ACTION_DOWNLOAD";
+    @Override
+    public void onReceive(Context context, Intent intent) {
+
+    }
+}
diff --git a/app/src/notFatweb/java/se/leap/bitmaskclient/appUpdate/DownloadServiceCommand.java b/app/src/notFatweb/java/se/leap/bitmaskclient/appUpdate/DownloadServiceCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..157f04fe6cc4d54b0925799a6aa17c4a34a3915c
--- /dev/null
+++ b/app/src/notFatweb/java/se/leap/bitmaskclient/appUpdate/DownloadServiceCommand.java
@@ -0,0 +1,19 @@
+package se.leap.bitmaskclient.appUpdate;
+
+import android.content.Context;
+
+/**
+ * DownloadServiceCommand is only implemented in Fatweb builds
+ *
+ */
+public class DownloadServiceCommand {
+
+    public final static String
+            CHECK_VERSION_FILE = "checkVersionFile",
+            DOWNLOAD_UPDATE = "downloadUpdate";
+
+
+    public static void execute(Context context, String action) {
+        // DO NOTHING.
+    }
+}
diff --git a/shapeshifter/shapeshifter-sources.jar b/bitmask-core/bitmask-core-sources.jar
similarity index 100%
rename from shapeshifter/shapeshifter-sources.jar
rename to bitmask-core/bitmask-core-sources.jar
diff --git a/shapeshifter/shapeshifter.aar b/bitmask-core/bitmask-core.aar
similarity index 51%
rename from shapeshifter/shapeshifter.aar
rename to bitmask-core/bitmask-core.aar
index a99762396f722dd6e0ab230d63a381b9d24f2cab..37e25808d0ef12cf9647928105a8680f2b888a8c 100644
Binary files a/shapeshifter/shapeshifter.aar and b/bitmask-core/bitmask-core.aar differ
diff --git a/bitmask-core/build.gradle b/bitmask-core/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..51d38614d1dc6aef20279940da57f425dece0343
--- /dev/null
+++ b/bitmask-core/build.gradle
@@ -0,0 +1,2 @@
+configurations.maybeCreate("default")
+artifacts.add("default", file('bitmask-core.aar'))
\ No newline at end of file
diff --git a/bitmask-web-core/bitmask-web-core-sources.jar b/bitmask-web-core/bitmask-web-core-sources.jar
new file mode 100644
index 0000000000000000000000000000000000000000..6788dc007bca3732d3b2c814ce8e0b703c9fd6a3
Binary files /dev/null and b/bitmask-web-core/bitmask-web-core-sources.jar differ
diff --git a/bitmask-web-core/bitmask-web-core.aar b/bitmask-web-core/bitmask-web-core.aar
new file mode 100644
index 0000000000000000000000000000000000000000..6f580439cba3b91c95651282ae6ed85b1093ef1e
Binary files /dev/null and b/bitmask-web-core/bitmask-web-core.aar differ
diff --git a/bitmask-web-core/build.gradle b/bitmask-web-core/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..650f38d61822d9e8c4983c063b8caf9d89c5df1e
--- /dev/null
+++ b/bitmask-web-core/build.gradle
@@ -0,0 +1,37 @@
+/*apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion 28
+    buildToolsVersion "29.0.1"
+
+
+    defaultConfig {
+        minSdkVersion 16
+        targetSdkVersion 28
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        consumerProguardFiles 'consumer-rules.pro'
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+    implementation 'androidx.appcompat:appcompat:1.2.0'
+    testImplementation 'junit:junit:4.12'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+}
+*/
+configurations.maybeCreate("default")
+artifacts.add("default", file('bitmask-web-core.aar'))
\ No newline at end of file
diff --git a/build_deps.sh b/build_deps.sh
index 156f53008f190dde9c9182e24a94a3ddcde4369e..beb5e13e567350f9384117b1697d44eb94fcf3ca 100755
--- a/build_deps.sh
+++ b/build_deps.sh
@@ -19,7 +19,7 @@ if [[ $(ls -A ${DIR_OVPNASSETS}) && $(ls -A ${DIR_OVPNLIBS}) ]]
 then
     echo "Dirty build: skipped externalNativeBuild - reusing existing libs"
 else
-    echo "Clean build: starting externalNativeBuild and GO dependency builds"
+    echo "Clean build: starting externalNativeBuild"
     cd ./ics-openvpn || quit "Directory ics-opevpn not found"
     ./gradlew clean main:externalNativeBuildCleanSkeletonRelease main:externalNativeBuildSkeletonRelease --debug --stacktrace || quit "Build ics-openvpn native libraries failed"
     cd ..
@@ -32,6 +32,7 @@ else
     echo "Clean build: compiling Go libraries"
     cd ./go || quit "Directory go not found"
     ./install_go.sh || quit "install_go.sh failed"
-    ./android_build_shapeshifter.sh createLibrary || quit "android_build_shapeshifter_dispatcher.sh failed"
+    ./android_build_web_core.sh || quit "android_build_web_core.sh (shapeshifter + pgpverify) failed"
+    ./android_build_core.sh || quit "android build core (shapeshifter) failed"
     cd ..
 fi
diff --git a/go/android_build_shapeshifter_lib.sh b/go/android_build_core.sh
similarity index 71%
rename from go/android_build_shapeshifter_lib.sh
rename to go/android_build_core.sh
index bbe3c84ac9cc3b30a24a61650df5270990404149..1a6af5b7cb7747f3d13fcdb45ba700284bcff8e3 100755
--- a/go/android_build_shapeshifter_lib.sh
+++ b/go/android_build_core.sh
@@ -16,6 +16,7 @@ echo "getting gomobile..."
 ./golang/go/bin/go get golang.org/x/mobile/cmd/gomobile
 echo "initiating gomobile..."
 ./bin/gomobile init
-echo "cross compiling shapeshifter lib..."
-./bin/gomobile bind -target=android -o ./lib/shapeshifter.aar se.leap.bitmaskclient/shapeshifter/
-cp lib/shapeshifter* ../shapeshifter/.
\ No newline at end of file
+if [ ! -d ./lib ]; then mkdir ./lib; fi
+echo "cross compiling bitmask core lib (shapeshifter)..."
+./bin/gomobile bind -target=android -o ./lib/bitmask-core.aar se.leap.bitmaskclient/shapeshifter/
+cp lib/bitmask-core* ../bitmask-core/.
\ No newline at end of file
diff --git a/go/android_build_web_core.sh b/go/android_build_web_core.sh
new file mode 100755
index 0000000000000000000000000000000000000000..762d4dada49c9a23845897480cd79bbf4ff1a7c5
--- /dev/null
+++ b/go/android_build_web_core.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+export GOPATH=`pwd`
+export GO_LANG=`pwd`/golang/go/bin
+export GO_COMPILED=`pwd`/bin
+PATH="${GO_LANG}:${GO_COMPILED}:${PATH}"
+
+if [ -z $ANDROID_NDK_HOME ]; then
+        echo "Android NDK path not specified!"
+        echo "Please set \$ANDROID_NDK_HOME before starting this script!"
+        exit 1;
+fi
+
+./golang/go/bin/go env
+echo "getting gomobile..."
+./golang/go/bin/go get golang.org/x/mobile/cmd/gomobile
+echo "initiating gomobile..."
+./bin/gomobile init
+if [ ! -d ./lib ]; then mkdir ./lib; fi
+echo "cross compiling bitmask web apk core lib (shapeshifter, pgpverify)..."
+./bin/gomobile bind -target=android -o ./lib/bitmask-web-core.aar se.leap.bitmaskclient/shapeshifter/ se.leap.bitmaskclient/pgpverify
+cp lib/bitmask-web-core* ../bitmask-web-core/.
\ No newline at end of file
diff --git a/go/src/se.leap.bitmaskclient/pgpverify/pgpverify.go b/go/src/se.leap.bitmaskclient/pgpverify/pgpverify.go
new file mode 100644
index 0000000000000000000000000000000000000000..653ea695f0f03dc23cbca45b337254b55e63449d
--- /dev/null
+++ b/go/src/se.leap.bitmaskclient/pgpverify/pgpverify.go
@@ -0,0 +1,82 @@
+package pgpverify
+
+import (
+	"os"
+	"strings"
+
+	"golang.org/x/crypto/openpgp"
+)
+
+// PgpVerifier - exported struct used for file verification
+type PgpVerifier struct {
+	//Signature string
+	//Target    string
+	//PublicKey string
+	Logger Logger
+}
+
+// Logger - logging interface
+type Logger interface {
+	Log(msg string)
+}
+
+// Verify checks if a file was signed with the correct pgp key
+// using a PEM formatted signature and a corresponding public key
+func (pgpv *PgpVerifier) Verify(signature string, publicKey string, targetPath string) bool {
+	keyRingReader := strings.NewReader(publicKey)
+	signatureReader := strings.NewReader(signature)
+
+	verificationTarget, err := os.Open(targetPath)
+	if err != nil {
+		pgpv.Logger.Log("Open verification target: " + err.Error())
+		return false
+	}
+
+	keyring, err := openpgp.ReadArmoredKeyRing(keyRingReader)
+	if err != nil {
+		pgpv.Logger.Log("Read Armored Key Ring: " + err.Error())
+		return false
+	}
+	_, err = openpgp.CheckArmoredDetachedSignature(keyring, verificationTarget, signatureReader)
+	if err != nil {
+		pgpv.Logger.Log("Verification failed: " + err.Error())
+		return false
+	}
+	pgpv.Logger.Log("Successfully verified: entity.Identities")
+	return true
+}
+
+/*func main() {
+	keyRingReader, err := os.Open("public_leap.asc")
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+
+	signature, err := os.Open("RiseupVPN_release_1.0.5.apk.sig")
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+
+	verificationTarget, err := os.Open("RiseupVPN_release_1.0.5.apk")
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+
+	keyring, err := openpgp.ReadArmoredKeyRing(keyRingReader)
+	if err != nil {
+		fmt.Println("Read Armored Key Ring: " + err.Error())
+		return
+	}
+	entity, err := openpgp.CheckArmoredDetachedSignature(keyring, verificationTarget, signature)
+	if err != nil {
+		fmt.Println("Check Detached Signature: " + err.Error())
+		return
+	} else {
+		fmt.Println("successfully verified")
+	}
+
+	fmt.Println(entity.Identities)
+}*/
diff --git a/settings.gradle b/settings.gradle
index 4b8df4a4e86e74b1b6c1806829bbafe57301475b..08a7095e2bd81295c6d2b0ed71526b818a9969ed 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app', ':shapeshifter'
\ No newline at end of file
+include ':app', ':bitmask-core', ':bitmask-web-core'
\ No newline at end of file
diff --git a/shapeshifter/build.gradle b/shapeshifter/build.gradle
deleted file mode 100644
index 667e13c93a740990e9cfe2046672ed2ae8238560..0000000000000000000000000000000000000000
--- a/shapeshifter/build.gradle
+++ /dev/null
@@ -1,2 +0,0 @@
-configurations.maybeCreate("default")
-artifacts.add("default", file('shapeshifter.aar'))
\ No newline at end of file