Skip to content
Merged
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
49 changes: 44 additions & 5 deletions app/src/main/java/eu/faircode/netguard/AdapterRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@
import net.kollnig.missioncontrol.data.InternetBlocklist;

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

public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> implements Filterable {
private static final String TAG = "TrackerControl.Adapter";
Expand All @@ -78,8 +81,13 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
private boolean wifiActive = true;
private boolean otherActive = true;
private boolean live = true;
private final Context appContext;
private List<Rule> listAll = new ArrayList<>();
private List<Rule> listFiltered = new ArrayList<>();
// Full installed-app set used only for search, so apps hidden from the visible
// list by the display filters remain reachable (#420, #483). Built lazily and
// invalidated whenever the visible list is replaced.
private List<Rule> searchPool = null;
private int iconSize;
private final RequestOptions glideOptions;
private String cachedSort;
Expand All @@ -106,6 +114,7 @@ public ViewHolder(View itemView) {

public AdapterRule(Context context, View anchor) {
this.anchor = anchor;
this.appContext = context.getApplicationContext();
this.inflater = LayoutInflater.from(context);
this.glideOptions = new RequestOptions().format(DecodeFormat.PREFER_RGB_565);

Expand Down Expand Up @@ -147,6 +156,7 @@ public void updateSortPreference(String sort) {

public void set(List<Rule> listRule) {
listAll = listRule;
searchPool = null; // rebuilt lazily on next search
List<Rule> oldList = listFiltered;
listFiltered = new ArrayList<>(listRule);
DiffUtil.DiffResult result = DiffUtil.calculateDiff(new RuleDiffCallback(oldList, listFiltered));
Expand Down Expand Up @@ -379,26 +389,55 @@ private void updateRule(Context context, Rule rule, boolean root, List<Rule> lis
}
}

/**
* The set searched by the search box. It is the visible list plus every other installed
* app, so apps hidden by the display filters (system / frozen / no-internet) but still
* routed and filtered by TC can be found and configured (#420, #483). Visible apps keep
* their fully-populated rules (with tracker counts); hidden apps are added as lightweight
* stubs. Built once per visible-list update and cached.
*/
private synchronized List<Rule> getSearchPool() {
if (searchPool != null)
return searchPool;

List<Rule> pool = new ArrayList<>(listAll);
Set<String> present = new HashSet<>();
for (Rule rule : listAll)
present.add(rule.packageName);
try {
for (Rule stub : Rule.getSearchStubs(appContext))
if (!present.contains(stub.packageName))
pool.add(stub);
} catch (Throwable ex) {
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
}

searchPool = pool;
return pool;
}

@Override
public Filter getFilter() {
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence query) {
List<Rule> listResult = new ArrayList<>();
if (query == null)
String queryStr = query == null ? "" : query.toString().toLowerCase(Locale.ROOT).trim();
if (queryStr.isEmpty())
listResult.addAll(listAll);
else {
String queryStr = query.toString().toLowerCase().trim();
int uid;
try {
uid = Integer.parseInt(queryStr);
} catch (NumberFormatException ignore) {
uid = -1;
}
for (Rule rule : listAll)
// Search the full installed-app set, not just the visible list, so hidden
// but filtered apps stay reachable (#420, #483).
for (Rule rule : getSearchPool())
if (rule.uid == uid ||
rule.packageName.toLowerCase().contains(queryStr) ||
(rule.name != null && rule.name.toLowerCase().contains(queryStr)))
rule.packageName.toLowerCase(Locale.ROOT).contains(queryStr) ||
(rule.name != null && rule.name.toLowerCase(Locale.ROOT).contains(queryStr)))
listResult.add(rule);
}

Expand Down
46 changes: 46 additions & 0 deletions app/src/main/java/eu/faircode/netguard/Rule.java
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,52 @@ else if (rule.getTrackerCount(pastWeekOnly) > other.getTrackerCount(pastWeekOnly
}
}

/**
* Build a lightweight list of rules for ALL installed applications, for search only.
*
* The visible list built by {@link #getRules} applies the show_system / show_frozen /
* show_nointernet display filters. An app hidden by those filters (e.g. an OEM-preinstalled
* app that TC routes because "include system apps" is on, a frozen package, or a shared-UID
* sibling) can still be routed through the tun and filtered/blocked by UID, yet be absent
* from the list and therefore unreachable — the user cannot open it to add an exception or
* exclude it. That is the "blocked but not listed" breakage reported in issues #420
* (Instagram) and #483 (Feeder), whose only workaround is disabling TC entirely.
*
* Search therefore must reach every installed app, not just the currently-visible subset.
* This intentionally skips the expensive / side-effecting work in getRules (tracker counts,
* default seeding, sorting, snackbars): stubs only need enough to be matched by the search
* filter and to open the per-app details screen (name, package, uid, internet).
*/
public static List<Rule> getSearchStubs(Context context) {
synchronized (context.getApplicationContext()) {
List<Rule> listStub = new ArrayList<>();

SharedPreferences apply = context.getSharedPreferences("apply", Context.MODE_PRIVATE);
SharedPreferences tracker_protect = context.getSharedPreferences("tracker_protect", Context.MODE_PRIVATE);
SharedPreferences notify = context.getSharedPreferences("notify", Context.MODE_PRIVATE);

DatabaseHelper dh = DatabaseHelper.getInstance(context);
for (PackageInfo info : getPackages(context))
try {
// Skip self
if (info.applicationInfo.uid == Process.myUid())
continue;

Rule rule = new Rule(dh, info, context);
rule.apply = apply.getBoolean(info.packageName, true);
rule.tracker_protect = BlockingMode.isTrackerProtectionEnabled(
context, tracker_protect, info.packageName);
rule.notify = notify.getBoolean(info.packageName, true);
rule.updateChanged();
listStub.add(rule);
} catch (Throwable ex) {
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
}

return listStub;
}
}

private static List<String> getHandlingPackages(PackageManager pm, Intent intent) {
List<String> packagesList = new ArrayList<>();

Expand Down