Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions app/src/main/java/eu/faircode/netguard/AdapterRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -1039,10 +1039,19 @@ private void updateRule(Context context, Rule rule, boolean root, List<Rule> lis
rule.updateChanged(context);
Log.i(TAG, "Updated " + rule);

// Optimize: Use HashMap to avoid O(n*m) nested loop complexity
List<Rule> listModified = new ArrayList<>();
for (String pkg : rule.related) {
for (Rule related : listAll)
if (related.packageName.equals(pkg)) {
if (!rule.related.isEmpty()) {
// Build a map for O(1) lookup
java.util.HashMap<String, Rule> packageMap = new java.util.HashMap<>();
for (Rule r : listAll) {
packageMap.put(r.packageName, r);
}

// Find and update related rules in O(n) time
for (String pkg : rule.related) {
Rule related = packageMap.get(pkg);
if (related != null) {
related.wifi_blocked = rule.wifi_blocked;
related.other_blocked = rule.other_blocked;
related.apply = rule.apply;
Expand All @@ -1053,6 +1062,7 @@ private void updateRule(Context context, Rule rule, boolean root, List<Rule> lis
related.notify = rule.notify;
listModified.add(related);
}
}
}

List<Rule> listSearch = (root ? new ArrayList<>(listAll) : listAll);
Expand Down
161 changes: 86 additions & 75 deletions app/src/main/java/eu/faircode/netguard/DatabaseHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -501,23 +501,24 @@ public Cursor getLog(boolean udp, boolean tcp, boolean other, boolean allowed, b
SQLiteDatabase db = this.getReadableDatabase();
// There is an index on time
// There is no index on protocol/allowed for write performance
String query = "SELECT ID AS _id, *";
query += " FROM log";
query += " WHERE (0 = 1";
// Optimize: Use StringBuilder for efficient string concatenation
StringBuilder query = new StringBuilder("SELECT ID AS _id, *");
query.append(" FROM log");
query.append(" WHERE (0 = 1");
if (udp)
query += " OR protocol = 17";
query.append(" OR protocol = 17");
if (tcp)
query += " OR protocol = 6";
query.append(" OR protocol = 6");
if (other)
query += " OR (protocol <> 6 AND protocol <> 17)";
query += ") AND (0 = 1";
query.append(" OR (protocol <> 6 AND protocol <> 17)");
query.append(") AND (0 = 1");
if (allowed)
query += " OR allowed = 1";
query.append(" OR allowed = 1");
if (blocked)
query += " OR allowed = 0";
query += ")";
query += " ORDER BY time DESC";
return db.rawQuery(query, new String[] {});
query.append(" OR allowed = 0");
query.append(")");
query.append(" ORDER BY time DESC");
return db.rawQuery(query.toString(), new String[] {});
} finally {
lock.readLock().unlock();
}
Expand All @@ -528,11 +529,12 @@ public Cursor searchLog(String find) {
try {
SQLiteDatabase db = this.getReadableDatabase();
// There is an index on daddr, dname, dport and uid
String query = "SELECT ID AS _id, *";
query += " FROM log";
query += " WHERE daddr LIKE ? OR dname LIKE ? OR dport = ? OR uid = ?";
query += " ORDER BY time DESC";
return db.rawQuery(query, new String[] { "%" + find + "%", "%" + find + "%", find, find });
// Optimize: Use StringBuilder for efficient string concatenation
StringBuilder query = new StringBuilder("SELECT ID AS _id, *");
query.append(" FROM log");
query.append(" WHERE daddr LIKE ? OR dname LIKE ? OR dport = ? OR uid = ?");
query.append(" ORDER BY time DESC");
return db.rawQuery(query.toString(), new String[] { "%" + find + "%", "%" + find + "%", find, find });
} finally {
lock.readLock().unlock();
}
Expand Down Expand Up @@ -740,13 +742,14 @@ public Cursor getAccess(int uid) {
SQLiteDatabase db = this.getReadableDatabase();
// There is a segmented index on uid
// There is no index on time for write performance
String query = "SELECT a.ID AS _id, a.*";
query += ", (SELECT COUNT(DISTINCT d.qname) FROM dns d WHERE d.resource IN (SELECT d1.resource FROM dns d1 WHERE d1.qname = a.daddr)) count";
query += " FROM access a";
query += " WHERE a.uid = ?";
query += " ORDER BY a.time DESC";
query += " LIMIT 250";
return db.rawQuery(query, new String[] { Integer.toString(uid) });
// Optimize: Use StringBuilder for efficient string concatenation
StringBuilder query = new StringBuilder("SELECT a.ID AS _id, a.*");
query.append(", (SELECT COUNT(DISTINCT d.qname) FROM dns d WHERE d.resource IN (SELECT d1.resource FROM dns d1 WHERE d1.qname = a.daddr)) count");
query.append(" FROM access a");
query.append(" WHERE a.uid = ?");
query.append(" ORDER BY a.time DESC");
query.append(" LIMIT 250");
return db.rawQuery(query.toString(), new String[] { Integer.toString(uid) });
} finally {
lock.readLock().unlock();
}
Expand All @@ -770,16 +773,17 @@ public Cursor getAccessUnset(int uid, int limit, long since) {
SQLiteDatabase db = this.getReadableDatabase();
// There is a segmented index on uid, block and daddr
// There is no index on allowed and time for write performance
String query = "SELECT MAX(time) AS time, daddr, allowed";
query += " FROM access";
query += " WHERE uid = ?";
query += " AND block < 0";
query += " AND time >= ?";
query += " GROUP BY daddr, allowed";
query += " ORDER BY time DESC";
// Optimize: Use StringBuilder for efficient string concatenation
StringBuilder query = new StringBuilder("SELECT MAX(time) AS time, daddr, allowed");
query.append(" FROM access");
query.append(" WHERE uid = ?");
query.append(" AND block < 0");
query.append(" AND time >= ?");
query.append(" GROUP BY daddr, allowed");
query.append(" ORDER BY time DESC");
if (limit > 0)
query += " LIMIT " + limit;
return db.rawQuery(query, new String[] { Integer.toString(uid), Long.toString(since) });
query.append(" LIMIT ").append(limit);
return db.rawQuery(query.toString(), new String[] { Integer.toString(uid), Long.toString(since) });
} finally {
lock.readLock().unlock();
}
Expand Down Expand Up @@ -923,14 +927,15 @@ public String getQName(int uid, String ip) {
readableDb = this.getReadableDatabase();
SQLiteDatabase db = readableDb;
// There is a segmented index on resource
String query = "SELECT d.qname";
query += " FROM dns AS d";
query += " WHERE d.resource = '" + ip.replace("'", "''") + "'";
query += " ORDER BY d.qname";
query += " LIMIT 1";
// Optimize: Use StringBuilder for efficient string concatenation
StringBuilder query = new StringBuilder("SELECT d.qname");
query.append(" FROM dns AS d");
query.append(" WHERE d.resource = '").append(ip.replace("'", "''")).append("'");
query.append(" ORDER BY d.qname");
query.append(" LIMIT 1");
// There is no way to known for sure which domain name an app used, so just pick
// the first one
return db.compileStatement(query).simpleQueryForString();
return db.compileStatement(query.toString()).simpleQueryForString();
} catch (SQLiteDoneException ignored) {
// Not found
return null;
Expand All @@ -947,14 +952,15 @@ public Cursor getQAName(int uid, String ip, boolean alive) {
readableDb = this.getReadableDatabase();
SQLiteDatabase db = readableDb;
// There is a segmented index on resource
String query = "SELECT d.qname, d.aname, d.time, d.ttl";
query += " FROM dns AS d";
query += " WHERE d.resource = '" + ip.replace("'", "''") + "'";
// Optimize: Use StringBuilder for efficient string concatenation
StringBuilder query = new StringBuilder("SELECT d.qname, d.aname, d.time, d.ttl");
query.append(" FROM dns AS d");
query.append(" WHERE d.resource = '").append(ip.replace("'", "''")).append("'");
if (alive)
query += " AND (d.time IS NULL OR d.time + d.ttl >= " + now + ")";
query += " GROUP BY d.qname"; // remove duplicates
query += " ORDER BY d.qname";
return db.rawQuery(query, new String[] {});
query.append(" AND (d.time IS NULL OR d.time + d.ttl >= ").append(now).append(")");
query.append(" GROUP BY d.qname"); // remove duplicates
query.append(" ORDER BY d.qname");
return db.rawQuery(query.toString(), new String[] {});
} finally {
lock.readLock().unlock();
}
Expand All @@ -964,13 +970,14 @@ public Cursor getAlternateQNames(String qname) {
lock.readLock().lock();
try {
SQLiteDatabase db = this.getReadableDatabase();
String query = "SELECT DISTINCT d2.qname";
query += " FROM dns d1";
query += " JOIN dns d2";
query += " ON d2.resource = d1.resource AND d2.id <> d1.id";
query += " WHERE d1.qname = ?";
query += " ORDER BY d2.qname";
return db.rawQuery(query, new String[] { qname });
// Optimize: Use StringBuilder for efficient string concatenation
StringBuilder query = new StringBuilder("SELECT DISTINCT d2.qname");
query.append(" FROM dns d1");
query.append(" JOIN dns d2");
query.append(" ON d2.resource = d1.resource AND d2.id <> d1.id");
query.append(" WHERE d1.qname = ?");
query.append(" ORDER BY d2.qname");
return db.rawQuery(query.toString(), new String[] { qname });
} finally {
lock.readLock().unlock();
}
Expand All @@ -981,13 +988,14 @@ public Cursor getAName(String qname, boolean alive) {
lock.readLock().lock();
try {
SQLiteDatabase db = this.getReadableDatabase();
String query = "SELECT d.qname, d.aname, d.time, d.ttl";
query += " FROM dns d";
query += " WHERE d.qname = ?";
// Optimize: Use StringBuilder for efficient string concatenation
StringBuilder query = new StringBuilder("SELECT d.qname, d.aname, d.time, d.ttl");
query.append(" FROM dns d");
query.append(" WHERE d.qname = ?");
if (alive)
query += " AND (d.time IS NULL OR d.time + d.ttl >= " + now + ")";
query += " LIMIT 1";
return db.rawQuery(query, new String[] { qname });
query.append(" AND (d.time IS NULL OR d.time + d.ttl >= ").append(now).append(")");
query.append(" LIMIT 1");
return db.rawQuery(query.toString(), new String[] { qname });
} finally {
lock.readLock().unlock();
}
Expand All @@ -999,10 +1007,11 @@ public Cursor getDns() {
SQLiteDatabase db = this.getReadableDatabase();
// There is an index on resource
// There is a segmented index on qname
String query = "SELECT ID AS _id, *";
query += " FROM dns";
query += " ORDER BY resource, qname";
return db.rawQuery(query, new String[] {});
// Optimize: Use StringBuilder for efficient string concatenation
StringBuilder query = new StringBuilder("SELECT ID AS _id, *");
query.append(" FROM dns");
query.append(" ORDER BY resource, qname");
return db.rawQuery(query.toString(), new String[] {});
} finally {
lock.readLock().unlock();
}
Expand All @@ -1016,16 +1025,17 @@ public Cursor getAccessDns(String dname) {

// There is a segmented index on dns.qname
// There is an index on access.daddr and access.block
String query = "SELECT a.uid, a.version, a.protocol, a.daddr, d.resource, a.dport, a.block, d.time, d.ttl";
query += " FROM access AS a";
query += " LEFT JOIN dns AS d";
query += " ON d.qname = a.daddr";
query += " WHERE a.block >= 0";
query += " AND (d.time IS NULL OR d.time + d.ttl >= " + now + ")";
// Optimize: Use StringBuilder for efficient string concatenation
StringBuilder query = new StringBuilder("SELECT a.uid, a.version, a.protocol, a.daddr, d.resource, a.dport, a.block, d.time, d.ttl");
query.append(" FROM access AS a");
query.append(" LEFT JOIN dns AS d");
query.append(" ON d.qname = a.daddr");
query.append(" WHERE a.block >= 0");
query.append(" AND (d.time IS NULL OR d.time + d.ttl >= ").append(now).append(")");
if (dname != null)
query += " AND a.daddr = ?";
query.append(" AND a.daddr = ?");

return db.rawQuery(query, dname == null ? new String[] {} : new String[] { dname });
return db.rawQuery(query.toString(), dname == null ? new String[] {} : new String[] { dname });
} finally {
lock.readLock().unlock();
}
Expand Down Expand Up @@ -1103,10 +1113,11 @@ public Cursor getForwarding() {
lock.readLock().lock();
try {
SQLiteDatabase db = this.getReadableDatabase();
String query = "SELECT ID AS _id, *";
query += " FROM forward";
query += " ORDER BY dport";
return db.rawQuery(query, new String[] {});
// Optimize: Use StringBuilder for efficient string concatenation
StringBuilder query = new StringBuilder("SELECT ID AS _id, *");
query.append(" FROM forward");
query.append(" ORDER BY dport");
return db.rawQuery(query.toString(), new String[] {});
} finally {
lock.readLock().unlock();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,16 @@ private void cleanup() {
if (files == null)
return;

// Optimize: Use HashSet to avoid O(n*m) nested loop complexity
List<Blocklist> list = getBlocklists();
java.util.HashSet<String> validFiles = new java.util.HashSet<>();
for (Blocklist item : list) {
validFiles.add("blocklist_" + item.uuid + ".txt");
}

// Check each file in O(1) time using HashSet
for (File file : files) {
boolean found = false;
for (Blocklist item : list) {
if (file.getName().equals("blocklist_" + item.uuid + ".txt")) {
found = true;
break;
}
}
if (!found) {
if (!validFiles.contains(file.getName())) {
Log.i(TAG, "Deleting orphaned file " + file.getName());
file.delete();
}
Expand Down
20 changes: 20 additions & 0 deletions app/src/main/java/net/kollnig/missioncontrol/data/Tracker.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,18 @@

import androidx.annotation.NonNull;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* Store information about tracker companies, and tracker found in apps' network traffic
*/
public class Tracker {
private final Set<String> hosts = new HashSet<>();
private List<String> sortedHostsCache = null; // Cache sorted hosts for performance
public String name;
public String category;
public Long lastSeen;
Expand Down Expand Up @@ -97,6 +101,8 @@ public String getCategory() {
*/
void addHost(String host) {
this.hosts.add(host);
// Invalidate cache when host is added
sortedHostsCache = null;
}

/**
Expand All @@ -107,4 +113,18 @@ void addHost(String host) {
public Set<String> getHosts() {
return hosts;
}

/**
* Get sorted list of hosts. This method caches the sorted result for performance.
* Calling this repeatedly is more efficient than sorting in UI code.
*
* @return Sorted list of hosts
*/
public List<String> getSortedHosts() {
if (sortedHostsCache == null) {
sortedHostsCache = new ArrayList<>(hosts);
Collections.sort(sortedHostsCache);
}
return sortedHostsCache;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,8 @@ private void updateText(TextView tv, Tracker t) {
if (t.lastSeen != 0)
title += " (" + Util.relativeTime(t.lastSeen) + ")";

List<String> sortedHosts = new ArrayList<>(t.getHosts());
Collections.sort(sortedHosts);
// Optimize: Use cached sorted hosts instead of creating new list and sorting every time
List<String> sortedHosts = t.getSortedHosts();
String hosts = TextUtils.join("\n• ", sortedHosts);

boolean categoryBlocked = b.blocked(mAppUid, trackerCategoryName);
Expand Down