Skip to content

Commit 347f2f3

Browse files
committed
fix minimal mode
1 parent 5db25a4 commit 347f2f3

13 files changed

Lines changed: 140 additions & 84 deletions

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ android {
99
minSdk 23
1010
targetSdk 36
1111
//noinspection HighAppVersionCode
12-
versionCode 2026012301
13-
versionName "2026.01.23"
12+
versionCode 2026040101
13+
versionName "2026.04.01"
1414

1515
// Secure DNS (DoH) configuration
1616
buildConfigField "String", "DEFAULT_DOH_ENDPOINT", "\"https://dns.quad9.net/dns-query\""

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@
4444
import java.util.concurrent.locks.ReentrantReadWriteLock;
4545

4646
public class DatabaseHelper extends SQLiteOpenHelper {
47+
public static final int ACCESS_UNCERTAIN_NONE = 0;
48+
public static final int ACCESS_UNCERTAIN_SHARED_IP = 1;
49+
public static final int ACCESS_UNCERTAIN_MIXED_TRACKER_AND_NON_TRACKER = 2;
50+
public static final int ACCESS_UNCERTAIN_MULTIPLE_TRACKERS = 3;
51+
4752
public Cursor getHosts(int uid) {
4853
lock.readLock().lock();
4954
try {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,8 @@ public static List<Rule> getRules(final boolean all, boolean self, Context conte
416416
rule.lockdown = lockdown.getBoolean(info.packageName, false);
417417

418418
rule.apply = apply.getBoolean(info.packageName, true);
419-
rule.tracker_protect = tracker_protect.getBoolean(info.packageName, true);
419+
rule.tracker_protect = BlockingMode.isTrackerProtectionEnabled(
420+
context, tracker_protect, info.packageName);
420421
rule.notify = notify.getBoolean(info.packageName, true);
421422

422423
// Related packages

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

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -883,15 +883,18 @@ private void log(Packet packet, int connection, boolean interactive) {
883883
String dname = null;
884884
String originalDname = null;
885885

886-
int uncertain = 0;
886+
int uncertain = DatabaseHelper.ACCESS_UNCERTAIN_NONE;
887887
boolean isTracker = false;
888888
try (Cursor lookup = dh.getQAName(packet.uid, packet.daddr, false)) {
889889
uncertain = (lookup != null
890-
&& lookup.getCount() > 1) ? 1 : 0;
890+
&& lookup.getCount() > 1) ? DatabaseHelper.ACCESS_UNCERTAIN_SHARED_IP
891+
: DatabaseHelper.ACCESS_UNCERTAIN_NONE;
891892

892893
// Loop until we find tracker or reach last entry
893894
if (lookup != null) {
894895
Pair<Tracker, String> foundTracker = new Pair<>(NO_TRACKER, null);
896+
boolean sawNonTrackerEvidence = false;
897+
boolean sawDifferentTrackerEvidence = false;
895898

896899
while (lookup.moveToNext()) {
897900
dname = lookup.getString(lookup.getColumnIndex("qname"));
@@ -908,19 +911,23 @@ private void log(Packet packet, int connection, boolean interactive) {
908911
}
909912

910913
if (foundTracker.first != NO_TRACKER
911-
&& (p.first == null // could have uncertain tracker company if no company found for
912-
// an observed domain
913-
|| !Objects.equals(foundTracker.first.name, p.first.name)) // we have an
914-
// uncertain
915-
// tracker
916-
// company
917-
&& uncertain == 1) {
918-
uncertain = 2;
919-
break;
914+
&& p.first != null
915+
&& !Objects.equals(foundTracker.first.name, p.first.name)) {
916+
sawDifferentTrackerEvidence = true;
917+
} else if (p.first == null) {
918+
sawNonTrackerEvidence = true;
920919
}
921920
}
922921
}
923922

923+
if (uncertain == DatabaseHelper.ACCESS_UNCERTAIN_SHARED_IP && foundTracker.first != NO_TRACKER) {
924+
if (sawNonTrackerEvidence) {
925+
uncertain = DatabaseHelper.ACCESS_UNCERTAIN_MIXED_TRACKER_AND_NON_TRACKER;
926+
} else if (sawDifferentTrackerEvidence) {
927+
uncertain = DatabaseHelper.ACCESS_UNCERTAIN_MULTIPLE_TRACKERS;
928+
}
929+
}
930+
924931
if (foundTracker.first != NO_TRACKER)
925932
dname = foundTracker.second;
926933
}
@@ -933,15 +940,15 @@ private void log(Packet packet, int connection, boolean interactive) {
933940
if (prefs.getBoolean("sni_enabled", false)
934941
&& packet.data != null
935942
&& !packet.data.isEmpty()) {
936-
uncertain = 0;
943+
uncertain = DatabaseHelper.ACCESS_UNCERTAIN_NONE;
937944
if (!packet.data.equals(originalDname)) {
938945
Log.d(TAG, "Using SNI " + packet.data + " instead of originalDname " + originalDname);
939946
dname = packet.data;
940947
isTracker = getDecloakedTracker(dname, dh).first != null;
941948
}
942949
}
943950

944-
if (uncertain == 1) // multiple dnames correspond to same IP address
951+
if (uncertain == DatabaseHelper.ACCESS_UNCERTAIN_SHARED_IP) // multiple dnames correspond to same IP address
945952
Log.d(TAG, "Found uncertain entry: " + dname);
946953

947954
// Traffic log
@@ -2207,7 +2214,7 @@ private boolean shouldTrackApp(int uid) {
22072214
if (trackerProtectPrefs == null) {
22082215
trackerProtectPrefs = getSharedPreferences("tracker_protect", Context.MODE_PRIVATE);
22092216
}
2210-
if (!trackerProtectPrefs.getBoolean(packageName, true)) {
2217+
if (!BlockingMode.isTrackerProtectionEnabled(this, trackerProtectPrefs, packageName)) {
22112218
return false;
22122219
}
22132220

app/src/main/java/net/kollnig/missioncontrol/InsightsActivity.kt

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -152,23 +152,12 @@ class InsightsActivity : AppCompatActivity() {
152152
// Animate main number
153153
animateNumber(tvTotalAttempts, 0, data.totalTrackingAttempts)
154154

155-
// Hide blocked/allowed stats for Play Store version
156-
val isPlayStore = Util.isPlayStoreInstall()
157-
llBlockedAllowed.visibility = if (isPlayStore) View.GONE else View.VISIBLE
158-
159-
// Set shocking fact - different text for Play Store
160-
if (isPlayStore) {
161-
tvShockingFact.text = getString(
162-
R.string.insights_shocking_fact_playstore,
163-
data.uniqueTrackerCompanies
164-
)
165-
} else {
166-
tvShockingFact.text = getString(
167-
R.string.insights_shocking_fact,
168-
data.uniqueTrackerCompanies,
169-
data.totalTrackingAttempts
170-
)
171-
}
155+
llBlockedAllowed.visibility = View.VISIBLE
156+
tvShockingFact.text = getString(
157+
R.string.insights_shocking_fact,
158+
data.uniqueTrackerCompanies,
159+
data.totalTrackingAttempts
160+
)
172161

173162
// Blocked/Allowed stats
174163
animateNumber(tvBlockedCount, 0, data.blockedTrackingAttempts)
@@ -303,12 +292,9 @@ class InsightsActivity : AppCompatActivity() {
303292
imageFile
304293
)
305294

306-
val isPlayStore = Util.isPlayStoreInstall()
307-
val shareMsgRes = if (isPlayStore) R.string.insights_share_message_playstore else R.string.insights_share_message
308-
309295
val shareText = getString(
310-
shareMsgRes,
311-
if (isPlayStore) data.totalTrackingAttempts else data.blockedTrackingAttempts,
296+
R.string.insights_share_message,
297+
data.blockedTrackingAttempts,
312298
data.uniqueTrackerCompanies
313299
)
314300

@@ -345,13 +331,8 @@ class InsightsActivity : AppCompatActivity() {
345331
// Hero stat: Total Hosts
346332
tvTotalBlocked.text = nf.format(data.totalTrackingAttempts)
347333

348-
// Blocked stat: hide on Play Store
349-
if (Util.isPlayStoreInstall()) {
350-
llBlockedStat.visibility = View.GONE
351-
} else {
352-
llBlockedStat.visibility = View.VISIBLE
353-
tvBlockedCount.text = nf.format(data.blockedTrackingAttempts)
354-
}
334+
llBlockedStat.visibility = View.VISIBLE
335+
tvBlockedCount.text = nf.format(data.blockedTrackingAttempts)
355336

356337
// Companies count
357338
tvCompanies.text = data.uniqueTrackerCompanies.toString()
@@ -425,4 +406,3 @@ class InsightsActivity : AppCompatActivity() {
425406
private const val TAG = "InsightsActivity"
426407
}
427408
}
428-

app/src/main/java/net/kollnig/missioncontrol/InsightsHeaderAdapter.java

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,9 @@ private void shareInsights() {
137137
context.getPackageName() + ".provider",
138138
imageFile);
139139

140-
boolean isPlayStore = Util.isPlayStoreInstall();
141-
int shareMsgRes = isPlayStore ? R.string.insights_share_message_playstore
142-
: R.string.insights_share_message;
143-
144140
String shareText = context.getString(
145-
shareMsgRes,
146-
isPlayStore ? data.getTotalTrackingAttempts() : data.getBlockedTrackingAttempts(),
141+
R.string.insights_share_message,
142+
data.getBlockedTrackingAttempts(),
147143
data.getUniqueTrackerCompanies());
148144

149145
Intent intent = new Intent(Intent.ACTION_SEND);
@@ -186,13 +182,8 @@ private File generateShareImage() {
186182
// Hero stat: Total Hosts
187183
tvTotalBlocked.setText(nf.format(data.getTotalTrackingAttempts()));
188184

189-
// Blocked stat: hide on Play Store
190-
if (Util.isPlayStoreInstall()) {
191-
llBlockedStat.setVisibility(View.GONE);
192-
} else {
193-
llBlockedStat.setVisibility(View.VISIBLE);
194-
tvBlockedCount.setText(nf.format(data.getBlockedTrackingAttempts()));
195-
}
185+
llBlockedStat.setVisibility(View.VISIBLE);
186+
tvBlockedCount.setText(nf.format(data.getBlockedTrackingAttempts()));
196187

197188
// Companies count
198189
tvCompanies.setText(String.valueOf(data.getUniqueTrackerCompanies()));

app/src/main/java/net/kollnig/missioncontrol/data/BlockingMode.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,16 @@ public static boolean isStrictMode(Context c) {
7777
return MODE_STRICT.equals(getMode(c));
7878
}
7979

80+
/**
81+
* Minimal mode does not support per-app tracker protection toggles.
82+
* Apps are either included in the VPN or excluded from it entirely.
83+
*/
84+
public static boolean isTrackerProtectionEnabled(Context c,
85+
SharedPreferences trackerProtectPrefs,
86+
String packageName) {
87+
return !isMinimalMode(c) || trackerProtectPrefs.getBoolean(packageName, true);
88+
}
89+
8090
/**
8191
* Get the default blocking mode for new users.
8292
* Minimal is the safest default — least app breakage.

app/src/main/java/net/kollnig/missioncontrol/data/InsightsDataProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class InsightsDataProvider(context: Context) {
9797
if (!applyPrefs.getBoolean(packageName, true)) continue
9898

9999
// Check if tracker protection is disabled for this app
100-
if (!trackerProtectPrefs.getBoolean(packageName, true)) continue
100+
if (!BlockingMode.isTrackerProtectionEnabled(context, trackerProtectPrefs, packageName)) continue
101101

102102
// Find tracker company for this hostname
103103
val tracker = TrackerList.findTracker(daddr) ?: continue

app/src/main/java/net/kollnig/missioncontrol/data/Tracker.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
*/
2828
public class Tracker {
2929
private final Set<String> hosts = new HashSet<>();
30+
private boolean uncertain;
31+
private boolean allowedInStandardMode;
3032
public String name;
3133
public String category;
3234
public Long lastSeen;
@@ -107,4 +109,27 @@ void addHost(String host) {
107109
public Set<String> getHosts() {
108110
return hosts;
109111
}
112+
113+
/**
114+
* Whether any of this tracker's hosts are uncertain/ambiguous.
115+
*/
116+
public boolean isUncertain() {
117+
return uncertain;
118+
}
119+
120+
void setUncertain(boolean uncertain) {
121+
this.uncertain = uncertain;
122+
}
123+
124+
/**
125+
* Whether any observed host would be allowed in standard mode because
126+
* the same IP also had non-tracker evidence.
127+
*/
128+
public boolean isAllowedInStandardMode() {
129+
return allowedInStandardMode;
130+
}
131+
132+
void setAllowedInStandardMode(boolean allowedInStandardMode) {
133+
this.allowedInStandardMode = allowedInStandardMode;
134+
}
110135
}

app/src/main/java/net/kollnig/missioncontrol/data/TrackerList.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import androidx.collection.ArrayMap;
2929
import androidx.preference.PreferenceManager;
3030

31+
import eu.faircode.netguard.DatabaseHelper;
32+
3133
import org.json.JSONArray;
3234
import org.json.JSONException;
3335
import org.json.JSONObject;
@@ -271,7 +273,10 @@ public synchronized List<TrackerCategory> getAppTrackers(Context c, int uid) {
271273
outer: do {
272274
String host = cursor.getString(cursor.getColumnIndexOrThrow("daddr"));
273275
long lastSeen = cursor.getLong(cursor.getColumnIndexOrThrow("time"));
274-
boolean uncertain = cursor.getInt(cursor.getColumnIndexOrThrow("uncertain")) == 2;
276+
int uncertainty = cursor.getInt(cursor.getColumnIndexOrThrow("uncertain"));
277+
boolean uncertain = uncertainty >= DatabaseHelper.ACCESS_UNCERTAIN_MIXED_TRACKER_AND_NON_TRACKER;
278+
boolean allowedInStandardMode =
279+
uncertainty == DatabaseHelper.ACCESS_UNCERTAIN_MIXED_TRACKER_AND_NON_TRACKER;
275280

276281
Tracker tracker = findTracker(host);
277282
if (tracker == null)
@@ -301,6 +306,10 @@ public synchronized List<TrackerCategory> getAppTrackers(Context c, int uid) {
301306
if (child.name != null
302307
&& child.name.equals(name)) {
303308
child.addHost(host);
309+
if (uncertain)
310+
child.setUncertain(true);
311+
if (allowedInStandardMode)
312+
child.setAllowedInStandardMode(true);
304313

305314
if (child.lastSeen < lastSeen)
306315
child.lastSeen = lastSeen;
@@ -311,6 +320,8 @@ public synchronized List<TrackerCategory> getAppTrackers(Context c, int uid) {
311320

312321
Tracker child = new Tracker(name, category, lastSeen);
313322
child.addHost(host);
323+
child.setUncertain(uncertain);
324+
child.setAllowedInStandardMode(allowedInStandardMode);
314325
categoryCompany.getChildren().add(child);
315326
} while (cursor.moveToNext());
316327
}

0 commit comments

Comments
 (0)