Skip to content

Commit 29dc05d

Browse files
kasnderclaude
andauthored
Cache package names and Fix stats update loop continuing every 10s when screen is off (#549)
* Fix stats update loop continuing every 10s when screen is off The recent battery optimization (9f15d42) introduced a race condition: when screen turns off, stopStats() removes pending MSG_STATS_UPDATE messages and sets stats=false. However, if an already-delayed update fires after stopStats(), updateStats() would reschedule itself every 10 seconds indefinitely since it never checked the stats flag. This caused ~360 unnecessary handler wakeups per hour while screen off. Fix: check the stats flag at the start of updateStats() and bail out without rescheduling if stats have been stopped. https://claude.ai/code/session_01XpUzMgtH7S4K72UX2DQ1cC * Cache getPackagesForUid() result in shouldTrackApp() to avoid per-packet Binder IPC shouldTrackApp() is called twice per packet (from blockKnownTracker and from log) and each call made an uncached getPackagesForUid() Binder IPC. Cache uid→packageName in a ConcurrentHashMap, invalidated on package removal and cleared during 12-hour housekeeping. https://claude.ai/code/session_01XpUzMgtH7S4K72UX2DQ1cC --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 13f66c3 commit 29dc05d

1 file changed

Lines changed: 25 additions & 14 deletions

File tree

app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,7 @@ private void householding(Intent intent) {
731731
ipToHost.clear();
732732
ipToTracker.clear();
733733
uidToApp.clear();
734+
uidToPackage.clear();
734735

735736
// Check for update
736737
if (!Util.isPlayStoreInstall(ServiceSinkhole.this)
@@ -1091,9 +1092,13 @@ private void stopStats() {
10911092
}
10921093

10931094
private void updateStats() {
1095+
// Stop the stats loop if stats were stopped (e.g. screen turned off)
1096+
if (!stats) {
1097+
return;
1098+
}
1099+
10941100
// Skip stats updates when screen is off to save battery
10951101
if (!last_interactive) {
1096-
this.sendEmptyMessageDelayed(MSG_STATS_UPDATE, 10000);
10971102
return;
10981103
}
10991104

@@ -2144,6 +2149,7 @@ private boolean isSupported(int protocol) {
21442149
}
21452150

21462151
static ConcurrentHashMap<Integer, String> uidToApp = new ConcurrentHashMap<>();
2152+
private static final ConcurrentHashMap<Integer, String> uidToPackage = new ConcurrentHashMap<>();
21472153
static ConcurrentHashMap<String, Expiring<String>> ipToHost = new ConcurrentHashMap<>();
21482154
static ConcurrentHashMap<String, Expiring<Tracker>> ipToTracker = new ConcurrentHashMap<>();
21492155
static String NO_DNAME = "null"; // use a String, unequal the real null
@@ -2236,20 +2242,24 @@ private Allowed isAddressAllowed(Packet packet) {
22362242
* - It's a system app and manage_system is disabled
22372243
*/
22382244
private boolean shouldTrackApp(int uid) {
2239-
String[] packages;
2240-
try {
2241-
packages = getPackageManager().getPackagesForUid(uid);
2242-
} catch (SecurityException ex) {
2243-
// In work profiles, getPackagesForUid throws SecurityException for
2244-
// cross-user UIDs (requires INTERACT_ACROSS_USERS_FULL permission).
2245-
// Default to tracking so blocking/logging still works.
2246-
Log.w(TAG, "SecurityException in shouldTrackApp for uid " + uid + ": " + ex.getMessage());
2247-
return true;
2248-
}
2249-
if (packages == null || packages.length == 0) {
2250-
return true; // Unknown UID, default to tracking
2245+
String packageName = uidToPackage.get(uid);
2246+
if (packageName == null) {
2247+
String[] packages;
2248+
try {
2249+
packages = getPackageManager().getPackagesForUid(uid);
2250+
} catch (SecurityException ex) {
2251+
// In work profiles, getPackagesForUid throws SecurityException for
2252+
// cross-user UIDs (requires INTERACT_ACROSS_USERS_FULL permission).
2253+
// Default to tracking so blocking/logging still works.
2254+
Log.w(TAG, "SecurityException in shouldTrackApp for uid " + uid + ": " + ex.getMessage());
2255+
return true;
2256+
}
2257+
if (packages == null || packages.length == 0) {
2258+
return true; // Unknown UID, default to tracking
2259+
}
2260+
packageName = packages[0];
2261+
uidToPackage.put(uid, packageName);
22512262
}
2252-
String packageName = packages[0];
22532263

22542264
// Check if tracker protection is enabled for this app
22552265
SharedPreferences trackerProtectPrefs = cachedTrackerProtectPrefs;
@@ -2686,6 +2696,7 @@ public void onReceive(Context context, Intent intent) {
26862696
dh.clearLog(uid);
26872697
dh.clearAccess(uid, false);
26882698
uidToApp.remove(uid);
2699+
uidToPackage.remove(uid);
26892700

26902701
NotificationManagerCompat.from(context).cancel(uid); // installed notification
26912702
NotificationManagerCompat.from(context).cancel(uid + 10000); // access notification

0 commit comments

Comments
 (0)