From d2e6b974b8d5fef09479a8e08853d22642b9dd19 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 25 Oct 2026 22:37:35 +0000 Subject: [PATCH] Reload rules when apps on external storage become available Apps on an SD card or adopted storage are announced only once the volume mounts, which on boot is after the service has built its rules. They stayed without network access until the app list was refreshed by hand. Listen for ACTION_EXTERNAL_APPLICATIONS_AVAILABLE/UNAVAILABLE in the service and the main activity, reload the rules, and resolve any saved settings that were still pending for the newly visible packages. Fixes #975 Claude-Session: https://claude.ai/code/session_014q8SwK4eSqqhJWWVKN6VJW --- .../eu/faircode/netguard/ActivityMain.java | 7 ++++ .../eu/faircode/netguard/ServiceSinkhole.java | 40 +++++++++++++++++++ 2 files changed, 47 insertions(+) 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 {