@@ -24,6 +24,7 @@ import android.util.Pair
2424import androidx.preference.PreferenceManager
2525import eu.faircode.netguard.DatabaseHelper
2626import net.kollnig.missioncontrol.Common
27+ import java.util.Locale
2728
2829/* *
2930 * Provider class that computes InsightsData from the database.
@@ -53,10 +54,12 @@ class InsightsDataProvider(context: Context) {
5354 val showSystem = prefs.getBoolean(" show_system" , false )
5455 val applyPrefs = context.getSharedPreferences(" apply" , Context .MODE_PRIVATE )
5556 val trackerProtectPrefs = context.getSharedPreferences(" tracker_protect" , Context .MODE_PRIVATE )
57+ val blockingMode = BlockingMode .getMode(context)
5658
5759 // Cache for UID -> package info lookups
5860 val uidPackageCache = mutableMapOf<Int , String ?>()
5961 val uidSystemCache = mutableMapOf<Int , Boolean >()
62+ val seenContacts = mutableSetOf<String >()
6063
6164 // Maps for aggregation
6265 val appTrackerCounts = mutableMapOf<Int , Int >() // uid -> unique tracker hosts
@@ -69,15 +72,21 @@ class InsightsDataProvider(context: Context) {
6972
7073 // Top domains: domain -> set of UIDs
7174 val domainToApps = mutableMapOf<String , MutableSet <Int >>()
75+ val uncertainDomains = mutableSetOf<String >()
7276
7377 databaseHelper.getInsightsData7Days().use { cursor ->
7478 if (cursor != null && cursor.moveToFirst()) {
7579 val uidIndex = cursor.getColumnIndexOrThrow(" uid" )
7680 val daddrIndex = cursor.getColumnIndexOrThrow(" daddr" )
81+ val allowedIndex = cursor.getColumnIndex(" allowed" )
82+ val uncertainIndex = cursor.getColumnIndex(" uncertain" )
7783
7884 do {
7985 val uid = cursor.getInt(uidIndex)
8086 val daddr = cursor.getString(daddrIndex)
87+ val contactKey = " $uid |$daddr "
88+
89+ if (! seenContacts.add(contactKey)) continue
8190
8291 // Get package name (cached)
8392 val packageName = uidPackageCache.getOrPut(uid) {
@@ -101,16 +110,29 @@ class InsightsDataProvider(context: Context) {
101110
102111 // Find tracker company for this hostname
103112 val tracker = TrackerList .findTracker(daddr) ? : continue
113+ val allowed = if (allowedIndex >= 0 && ! cursor.isNull(allowedIndex))
114+ cursor.getInt(allowedIndex)
115+ else
116+ - 1
117+ val uncertainty = if (uncertainIndex >= 0 && ! cursor.isNull(uncertainIndex))
118+ cursor.getInt(uncertainIndex)
119+ else
120+ DatabaseHelper .ACCESS_UNCERTAIN_NONE
104121
105122 val companyName = tracker.name ? : daddr
106123 uniqueCompanies.add(companyName)
107124 appsWithTrackers.add(uid)
108125
109- // Count tracker hosts seen over the last 7 days.
126+ // Count latest unique app-host contacts seen over the last 7 days.
110127 data.totalTrackingAttempts + = 1
111128
112- // Check if this tracker is currently blocked
113- val isBlocked = trackerBlocklist.blockedTracker(uid, tracker)
129+ val isBlocked = isTrackerContactBlocked(
130+ uid,
131+ tracker,
132+ allowed,
133+ uncertainty,
134+ blockingMode
135+ )
114136 if (isBlocked) {
115137 data.blockedTrackingAttempts + = 1
116138 } else {
@@ -125,9 +147,11 @@ class InsightsDataProvider(context: Context) {
125147
126148 // Track which apps contact each company
127149 companyToApps.getOrPut(companyName) { mutableSetOf () }.add(uid)
128-
150+
129151 // Track which apps contact each domain
130152 domainToApps.getOrPut(daddr) { mutableSetOf () }.add(uid)
153+ if (uncertainty > DatabaseHelper .ACCESS_UNCERTAIN_NONE )
154+ uncertainDomains.add(daddr)
131155
132156 } while (cursor.moveToNext())
133157 }
@@ -164,26 +188,7 @@ class InsightsDataProvider(context: Context) {
164188 // Group by TLD+1 (e.g., ads.google.com -> google.com) to reduce clutter
165189 // For uncertain entries, show alternate tracker domains inline
166190 val tldPlusOneToApps = mutableMapOf<String , MutableSet <Int >>()
167-
168- // Track which domains are uncertain (need to show alternates)
169- val uncertainDomains = mutableSetOf<String >()
170-
171- // First pass: collect all uncertain domains from the cursor data
172- // Re-query to get uncertain info (since we need fresh cursor)
173- databaseHelper.getInsightsData7Days().use { cursor ->
174- if (cursor != null && cursor.moveToFirst()) {
175- val daddrIndex = cursor.getColumnIndexOrThrow(" daddr" )
176- val uncertainIndex = cursor.getColumnIndex(" uncertain" )
177-
178- do {
179- val daddr = cursor.getString(daddrIndex)
180- if (uncertainIndex >= 0 && cursor.getInt(uncertainIndex) > 0 ) {
181- uncertainDomains.add(daddr)
182- }
183- } while (cursor.moveToNext())
184- }
185- }
186-
191+
187192 for ((daddr, uids) in domainToApps) {
188193 val tldPlusOne = extractTldPlusOne(daddr)
189194
@@ -235,12 +240,16 @@ class InsightsDataProvider(context: Context) {
235240 * Extract TLD+1 from a domain name (e.g., ads.google.com -> google.com)
236241 */
237242 private fun extractTldPlusOne (domain : String ): String {
238- val parts = domain.split(" ." )
239- return if (parts.size >= 2 ) {
240- " ${parts[parts.size - 2 ]} .${parts[parts.size - 1 ]} "
241- } else {
242- domain
243- }
243+ val normalized = domain.trim(' .' ).lowercase(Locale .ROOT )
244+ val parts = normalized.split(" ." ).filter { it.isNotEmpty() }
245+ if (parts.size < 2 )
246+ return normalized
247+
248+ val suffix = parts.takeLast(2 ).joinToString(" ." )
249+ return if (parts.size >= 3 && MULTI_LABEL_PUBLIC_SUFFIXES .contains(suffix))
250+ " ${parts[parts.size - 3 ]} .$suffix "
251+ else
252+ suffix
244253 }
245254
246255 /* *
@@ -270,4 +279,44 @@ class InsightsDataProvider(context: Context) {
270279 tldPlusOne
271280 }
272281 }
282+
283+ private fun isTrackerContactBlocked (
284+ uid : Int ,
285+ tracker : Tracker ,
286+ allowed : Int ,
287+ uncertainty : Int ,
288+ blockingMode : String
289+ ): Boolean {
290+ if (allowed >= 0 )
291+ return allowed == 0
292+
293+ if (! BlockingMode .isStrictMode(context)
294+ && uncertainty == DatabaseHelper .ACCESS_UNCERTAIN_MIXED_TRACKER_AND_NON_TRACKER ) {
295+ return false
296+ }
297+
298+ val blockedByGranularRule = if (BlockingMode .MODE_MINIMAL == blockingMode) {
299+ false
300+ } else {
301+ trackerBlocklist.blockedTracker(uid, tracker)
302+ }
303+
304+ return BlockingModeLogic .shouldBlockKnownTracker(
305+ blockingMode,
306+ tracker.category,
307+ blockedByGranularRule
308+ )
309+ }
310+
311+ companion object {
312+ private val MULTI_LABEL_PUBLIC_SUFFIXES = setOf (
313+ " ac.uk" , " co.uk" , " gov.uk" , " org.uk" ,
314+ " co.jp" , " ne.jp" , " or.jp" ,
315+ " com.au" , " edu.au" , " gov.au" , " net.au" , " org.au" ,
316+ " ac.nz" , " co.nz" , " govt.nz" , " org.nz" ,
317+ " co.in" , " firm.in" , " gen.in" , " ind.in" , " net.in" , " org.in" ,
318+ " com.br" , " com.cn" , " com.hk" , " com.mx" , " com.my" , " com.sg" ,
319+ " com.tr" , " com.tw" , " net.cn" , " org.cn"
320+ )
321+ }
273322}
0 commit comments