diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java
index 947d86076286765d6c4d6dc4ad2cb6216d517e07..eba5d57eec64dfdc9645227d19e8809ecc793702 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java
@@ -180,8 +180,11 @@ public class AppModule {
 	}
 
 	@Provides
+	@Singleton
 	ScreenFilterMonitor provideScreenFilterMonitor(
+			LifecycleManager lifecycleManager,
 			ScreenFilterMonitorImpl screenFilterMonitor) {
+		lifecycleManager.registerService(screenFilterMonitor);
 		return screenFilterMonitor;
 	}
 
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/ScreenFilterMonitorImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/ScreenFilterMonitorImpl.java
index d9c5ac65eaee341ce3597704d26fdaf4f7dba433..fa9122c451fad394af99141c7b3716951b5897cd 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/ScreenFilterMonitorImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/ScreenFilterMonitorImpl.java
@@ -2,6 +2,10 @@ package org.briarproject.briar.android;
 
 import android.annotation.SuppressLint;
 import android.app.Application;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -9,7 +13,10 @@ import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.Signature;
 import android.support.annotation.UiThread;
 
+import org.briarproject.bramble.api.lifecycle.Service;
+import org.briarproject.bramble.api.lifecycle.ServiceException;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.bramble.api.system.AndroidExecutor;
 import org.briarproject.bramble.util.StringUtils;
 import org.briarproject.briar.api.android.ScreenFilterMonitor;
 
@@ -24,11 +31,17 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Logger;
 
+import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_CHANGED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.Intent.ACTION_PACKAGE_REPLACED;
 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
 import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
@@ -38,7 +51,7 @@ import static android.os.Build.VERSION.SDK_INT;
 import static java.util.logging.Level.WARNING;
 
 @NotNullByDefault
-class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
+class ScreenFilterMonitorImpl implements ScreenFilterMonitor, Service {
 
 	private static final Logger LOG =
 			Logger.getLogger(ScreenFilterMonitorImpl.class.getName());
@@ -65,17 +78,32 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
 	private static final String PREF_KEY_ALLOWED = "allowedOverlayApps";
 
 	private final PackageManager pm;
+	private final Application app;
+	private final AndroidExecutor androidExecutor;
 	private final SharedPreferences prefs;
+	private final AtomicBoolean used = new AtomicBoolean(false);
+
+	// UiThread
+	@Nullable
+	private BroadcastReceiver receiver = null;
+
+	// UiThread
+	@Nullable
+	private Collection<AppDetails> cachedApps = null;
 
 	@Inject
-	ScreenFilterMonitorImpl(Application app, SharedPreferences prefs) {
+	ScreenFilterMonitorImpl(Application app, AndroidExecutor androidExecutor,
+			SharedPreferences prefs) {
 		pm = app.getPackageManager();
+		this.app = app;
+		this.androidExecutor = androidExecutor;
 		this.prefs = prefs;
 	}
 
 	@Override
 	@UiThread
 	public Collection<AppDetails> getApps() {
+		if (cachedApps != null) return cachedApps;
 		Set<String> allowed = prefs.getStringSet(PREF_KEY_ALLOWED,
 				Collections.emptySet());
 		List<AppDetails> apps = new ArrayList<>();
@@ -89,11 +117,15 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
 			}
 		}
 		Collections.sort(apps, (a, b) -> a.name.compareTo(b.name));
+		apps = Collections.unmodifiableList(apps);
+		cachedApps = apps;
 		return apps;
 	}
 
 	@Override
+	@UiThread
 	public void allowApps(Collection<String> packageNames) {
+		cachedApps = null;
 		Set<String> allowed = prefs.getStringSet(PREF_KEY_ALLOWED,
 				Collections.emptySet());
 		Set<String> merged = new HashSet<>(allowed);
@@ -121,12 +153,11 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
 		if (SDK_INT >= 16 && SDK_INT < 23) {
 			// Check whether the permission has been requested and granted
 			int[] flags = packageInfo.requestedPermissionsFlags;
-			if (flags == null || flags.length != requestedPermissions.length)
-				throw new AssertionError();
 			for (int i = 0; i < requestedPermissions.length; i++) {
-				if (requestedPermissions[i].equals(SYSTEM_ALERT_WINDOW)
-						&& (flags[i] & REQUESTED_PERMISSION_GRANTED) != 0) {
-					return true;
+				if (requestedPermissions[i].equals(SYSTEM_ALERT_WINDOW)) {
+					// 'flags' may be null on Robolectric
+					return flags == null ||
+							(flags[i] & REQUESTED_PERMISSION_GRANTED) != 0;
 				}
 			}
 		} else {
@@ -163,4 +194,36 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
 			return false;
 		}
 	}
+
+	@Override
+	public void startService() throws ServiceException {
+		if (used.getAndSet(true)) throw new IllegalStateException();
+		androidExecutor.runOnUiThread(() -> {
+			IntentFilter filter = new IntentFilter();
+			filter.addAction(ACTION_PACKAGE_ADDED);
+			filter.addAction(ACTION_PACKAGE_CHANGED);
+			filter.addAction(ACTION_PACKAGE_REMOVED);
+			filter.addAction(ACTION_PACKAGE_REPLACED);
+			filter.addDataScheme("package");
+			receiver = new PackageBroadcastReceiver();
+			app.registerReceiver(receiver, filter);
+			cachedApps = null;
+		});
+	}
+
+	@Override
+	public void stopService() throws ServiceException {
+		androidExecutor.runOnUiThread(() -> {
+			if (receiver != null) app.unregisterReceiver(receiver);
+		});
+	}
+
+	private class PackageBroadcastReceiver extends BroadcastReceiver {
+
+		@Override
+		@UiThread
+		public void onReceive(Context context, Intent intent) {
+			cachedApps = null;
+		}
+	}
 }
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java
index 59e0cca707012430a8b47a74361b933268b387b3..dd684b7d38f8fc025cde263b8eacc4885c112615 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java
@@ -49,7 +49,10 @@ public abstract class BaseActivity extends AppCompatActivity
 	private final List<ActivityLifecycleController> lifecycleControllers =
 			new ArrayList<>();
 	private boolean destroyed = false;
+
 	private ScreenFilterDialogFragment dialogFrag;
+	private Toolbar toolbar = null;
+	private boolean searchedForToolbar = false;
 
 	public abstract void injectActivity(ActivityComponent component);
 
@@ -108,6 +111,12 @@ public abstract class BaseActivity extends AppCompatActivity
 		}
 	}
 
+	@Override
+	protected void onResume() {
+		super.onResume();
+		protectToolbar();
+	}
+
 	@Override
 	protected void onPause() {
 		super.onPause();
@@ -137,13 +146,18 @@ public abstract class BaseActivity extends AppCompatActivity
 		// If the dialog is already visible, filter the tap
 		if (dialogFrag != null && dialogFrag.isVisible()) return false;
 		Collection<AppDetails> apps = screenFilterMonitor.getApps();
-		// If all overlay apps are allowed or system apps, allow the tap
+		// If there are no overlay apps that haven't been allowed, allow the tap
 		if (apps.isEmpty()) return true;
+		// Create dialog
 		dialogFrag = ScreenFilterDialogFragment.newInstance(apps);
 		dialogFrag.setCancelable(false);
 		// Show dialog unless onSaveInstanceState() has been called, see #1112
 		FragmentManager fm = getSupportFragmentManager();
-		if (!fm.isStateSaved()) dialogFrag.show(fm, dialogFrag.getTag());
+		if (!fm.isStateSaved()) {
+			// When dialog is dismissed, update protection of toolbar
+			dialogFrag.setDismissListener(this::protectToolbar);
+			dialogFrag.show(fm, dialogFrag.getTag());
+		}
 		// Filter the tap
 		return false;
 	}
@@ -199,16 +213,21 @@ public abstract class BaseActivity extends AppCompatActivity
 	 * is outside the wrapper.
 	 */
 	private void protectToolbar() {
-		View decorView = getWindow().getDecorView();
-		if (decorView instanceof ViewGroup) {
-			Toolbar toolbar = findToolbar((ViewGroup) decorView);
-			if (toolbar != null) {
-				boolean filter = !screenFilterMonitor.getApps().isEmpty();
-				toolbar.setFilterTouchesWhenObscured(filter);
-			}
+		findToolbar();
+		if (toolbar != null) {
+			boolean filter = !screenFilterMonitor.getApps().isEmpty();
+			toolbar.setFilterTouchesWhenObscured(filter);
 		}
 	}
 
+	private void findToolbar() {
+		if (searchedForToolbar) return;
+		View decorView = getWindow().getDecorView();
+		if (decorView instanceof ViewGroup)
+			toolbar = findToolbar((ViewGroup) decorView);
+		searchedForToolbar = true;
+	}
+
 	@Nullable
 	private Toolbar findToolbar(ViewGroup vg) {
 		for (int i = 0, len = vg.getChildCount(); i < len; i++) {
@@ -230,19 +249,16 @@ public abstract class BaseActivity extends AppCompatActivity
 	@Override
 	public void setContentView(View v) {
 		super.setContentView(makeTapSafeWrapper(v));
-		protectToolbar();
 	}
 
 	@Override
 	public void setContentView(View v, LayoutParams layoutParams) {
 		super.setContentView(makeTapSafeWrapper(v), layoutParams);
-		protectToolbar();
 	}
 
 	@Override
 	public void addContentView(View v, LayoutParams layoutParams) {
 		super.addContentView(makeTapSafeWrapper(v), layoutParams);
-		protectToolbar();
 	}
 
 	@Override
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java
index 037438b50d5462de0fb14dcb42edc5c49485f4eb..0539497c7101f5af898ac8afe44e414700dd692e 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java
@@ -3,6 +3,7 @@ package org.briarproject.briar.android.fragment;
 import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.Dialog;
+import android.content.DialogInterface;
 import android.os.Bundle;
 import android.support.v4.app.DialogFragment;
 import android.support.v7.app.AlertDialog;
@@ -32,6 +33,8 @@ public class ScreenFilterDialogFragment extends DialogFragment {
 	@Inject
 	ScreenFilterMonitor screenFilterMonitor;
 
+	DismissListener dismissListener = null;
+
 	public static ScreenFilterDialogFragment newInstance(
 			Collection<AppDetails> apps) {
 		ScreenFilterDialogFragment frag = new ScreenFilterDialogFragment();
@@ -46,6 +49,10 @@ public class ScreenFilterDialogFragment extends DialogFragment {
 		return frag;
 	}
 
+	public void setDismissListener(DismissListener dismissListener) {
+		this.dismissListener = dismissListener;
+	}
+
 	@Override
 	public void onActivityCreated(@Nullable Bundle savedInstanceState) {
 		super.onActivityCreated(savedInstanceState);
@@ -83,4 +90,14 @@ public class ScreenFilterDialogFragment extends DialogFragment {
 		});
 		return builder.create();
 	}
+
+	@Override
+	public void onDismiss(DialogInterface dialog) {
+		super.onDismiss(dialog);
+		if (dismissListener != null) dismissListener.onDialogDismissed();
+	}
+
+	public interface DismissListener {
+		void onDialogDismissed();
+	}
 }