diff --git a/app/src/main/java/eu/faircode/netguard/ActivityMain.java b/app/src/main/java/eu/faircode/netguard/ActivityMain.java index bbd32977d..314d00dd5 100644 --- a/app/src/main/java/eu/faircode/netguard/ActivityMain.java +++ b/app/src/main/java/eu/faircode/netguard/ActivityMain.java @@ -578,6 +578,13 @@ public void onRefresh() { intentFilter.addDataScheme("package"); ContextCompat.registerReceiver(this, packageChangedReceiver, intentFilter, ContextCompat.RECEIVER_NOT_EXPORTED); + // Apps on external or adopted storage being mounted or unmounted; these + // broadcasts carry no data URI, so they need a filter without a scheme + IntentFilter ifExternal = new IntentFilter(); + ifExternal.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + ifExternal.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + ContextCompat.registerReceiver(this, packageChangedReceiver, ifExternal, ContextCompat.RECEIVER_NOT_EXPORTED); + // Note first use if (!initialized) prefs.edit().putBoolean("initialized", true).apply(); diff --git a/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java b/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java index 5869aaaad..a4476b4fd 100644 --- a/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java +++ b/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java @@ -3654,6 +3654,39 @@ private void handlePackageChanged(Context context, Intent intent) { } reload("package deleted", context, false); + + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) { + // Apps on external or adopted storage become visible only once the + // volume mounts, which on boot is after this service has built its + // rules. Without a reload they stay unknown to the VPN, and so + // without network access, until the app list is refreshed. + Rule.clearCache(context); + + // Settings saved for these apps could not be resolved to a UID + // while the volume was missing, so resolve them now. + String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + if (packages != null) { + InternetBlocklist internetBlocklist = InternetBlocklist.getInstance(context); + TrackerBlocklist b = TrackerBlocklist.getInstance(context); + boolean internetChanged = false; + boolean trackerChanged = false; + for (String packageName : packages) { + internetChanged |= internetBlocklist.resolvePendingPackage(context, packageName); + trackerChanged |= b.resolvePendingPackage(context, packageName); + } + if (trackerChanged) + b.saveSettings(context); + if (internetChanged) + internetBlocklist.saveSettings(context); + } + + reload("external applications available", context, false); + + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) { + // The volume went away, but the apps are still installed: keep + // their settings and only rebuild the rules. + Rule.clearCache(context); + reload("external applications unavailable", context, false); } } @@ -3825,6 +3858,13 @@ public void onCreate() { ifPackage.addAction(Intent.ACTION_PACKAGE_REMOVED); ifPackage.addDataScheme("package"); ContextCompat.registerReceiver(this, packageChangedReceiver, ifPackage, ContextCompat.RECEIVER_NOT_EXPORTED); + + // Apps on external or adopted storage being mounted or unmounted. These + // broadcasts carry no data URI, so they need a filter without a scheme. + IntentFilter ifExternal = new IntentFilter(); + ifExternal.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + ifExternal.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + ContextCompat.registerReceiver(this, packageChangedReceiver, ifExternal, ContextCompat.RECEIVER_NOT_EXPORTED); registeredPackageChanged = true; try {