# How State, Removal, and List Filters Are Implemented in the UAD-ng CLI

> Learn how UAD-ng CLI implements state removal and list filters using specialized enums and match methods. Understand package filtering logic.

- Repository: [Universal-Debloater-Alliance/universal-android-debloater-next-generation](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation)
- Tags: internals
- Published: 2026-06-18

---

**The UAD-ng CLI implements package filtering through three specialized enums—`StateFilter`, `RemovalFilter`, and `ListFilter`—defined in [`crates/uad-cli/src/filters.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-cli/src/filters.rs), which provide `matches` methods to evaluate packages against user-specified criteria.**

The Universal Android Debloater Next Generation (UAD-ng) CLI provides granular control over Android package management through a sophisticated filtering system. Located in the `crates/uad-cli` directory, this implementation allows users to narrow down packages by installation state, recommended removal safety level, and upstream list origin using a composable filter architecture.

## Filter Architecture: The Three Core Enums

The filtering system centers on three distinct enums, each responsible for a specific dimension of package selection.

- **`StateFilter`** (lines 5‑15 of [`crates/uad-cli/src/filters.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-cli/src/filters.rs)): Limits results to packages that are **enabled**, **disabled**, **uninstalled**, or **all** packages.
- **`RemovalFilter`** (lines 18‑25): Selects packages based on the UAD removal classification (**Recommended**, **Advanced**, **Expert**, **Unsafe**, or **Unlisted**).
- **`ListFilter`** (lines 28‑37): Restricts output to a specific upstream list (**AOSP**, **Carrier**, **Google**, **Misc**, **OEM**, **Pending**, or **Unlisted**).

Each enum implements a `matches` method that receives the relevant package data and returns a boolean indicating whether the package satisfies the filter criteria.

## Core Filter Logic Implementation

Each filter enum provides a `matches` method that performs direct comparison against package metadata.

For installation state filtering, `StateFilter::matches` (lines 48‑55) evaluates the `PackageState`:

```rust
pub fn matches(self, pkg_state: PackageState) -> bool {
    match self {
        Self::All => true,
        Self::Enabled => pkg_state == PackageState::Enabled,
        Self::Disabled => pkg_state == PackageState::Disabled,
        Self::Uninstalled => pkg_state == PackageState::Uninstalled,
    }
}

```

Similar implementations exist for `RemovalFilter::matches` (lines 62‑74), which checks the package's removal classification, and `ListFilter::matches` (lines 82‑95), which validates the `UadList` origin.

## Filter Composition via PackageListContext

Individual filters are aggregated into a composite structure that handles multi-criteria queries. The `PackageListContext` struct (lines 46‑53 of [`crates/uad-cli/src/commands.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-cli/src/commands.rs)) holds optional instances of all three filter types plus a free-text search term:

```rust
pub struct PackageListContext {
    pub state_filter: Option<StateFilter>,
    pub removal_filter: Option<RemovalFilter>,
    pub list_filter: Option<ListFilter>,
    pub search: Option<String>,
}

```

The `filter_package` method (lines 55‑92) implements sequential evaluation using short-circuit logic. It processes filters in the order **removal → state → list → search**, returning `false` immediately if any filter fails:

```rust
if let Some(removal) = self.removal_filter {
    if !removal.matches(pkg_info) { return false; }
}
// …repeat for state and list…

```

## ADB Integration and Performance Optimization

The CLI optimizes performance by pushing state filtering down to the ADB layer. `StateFilter::to_pm_flag()` (lines 39‑46) converts the filter into a `PmListPacksFlag` that is passed directly to the `pm list packages` command. This reduces the initial data transfer by limiting the raw package set to enabled, disabled, or uninstalled packages before Rust-side filtering begins.

## Dynamic Output Configuration

The filtering system influences terminal output through `PackageListContext::display_config` (lines 94‑100). This method determines column visibility based on filter specificity, hiding the *state* or *removal* columns when the corresponding filter is set to `All`:

```rust
DisplayConfig {
    show_state: self.state_filter.is_none_or(|f| !f.is_specific()),
    show_removal: self.removal_filter.is_none_or(|f| !f.is_specific()),
}

```

## Practical CLI Usage Examples

### Listing enabled packages marked Recommended for removal

```bash
uad list --state enabled --removal recommended

```

- `--state enabled` instantiates `StateFilter::Enabled`
- `--removal recommended` instantiates `RemovalFilter::Recommended`
- `PackageListContext` requires both conditions to return `true`

### Querying uninstalled packages from the Google list

```bash
uad list --list google --state uninstalled

```

- `ListFilter::Google` restricts matches to `UadList::Google`
- `StateFilter::Uninstalled` triggers `PmListPacksFlag::IncludeUninstalled` in the ADB query

### Dry-run removal of unsafe packages

```bash
uad remove --removal unsafe --dry-run

```

- `RemovalFilter::Unsafe` selects packages with `Removal::Unsafe` classification
- The filter pipeline validates each package before generating the change plan

## Summary

- **Three specialized enums** (`StateFilter`, `RemovalFilter`, `ListFilter`) defined in [`crates/uad-cli/src/filters.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-cli/src/filters.rs) handle distinct filtering dimensions.
- **Composable architecture** via `PackageListContext` in [`crates/uad-cli/src/commands.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-cli/src/commands.rs) enables multi-criteria filtering with short-circuit evaluation.
- **ADB optimization** through `to_pm_flag()` pushes state filtering to the device level, minimizing data transfer.
- **Dynamic UI** adjusts column visibility based on active filters to maintain concise output.
- **Sequential evaluation** in `filter_package` processes removal, state, list, and search criteria in order of computational cost.

## Frequently Asked Questions

### How does UAD-ng filter packages by installation state before fetching from the device?

The CLI converts `StateFilter` variants into `PmListPacksFlag` values via `to_pm_flag()` (lines 39‑46 of [`filters.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/filters.rs)). These flags are passed to the `pm list packages` ADB command, instructing the Android package manager to return only enabled, disabled, or uninstalled packages. This pre-filtering occurs before the Rust-side `matches` logic processes the results.

### What is the difference between RemovalFilter and ListFilter in UAD-ng?

`RemovalFilter` selects packages based on the safety classification of removal (Recommended, Advanced, Expert, Unsafe, or Unlisted) as defined in the UAD database. `ListFilter` selects packages based on their upstream source category (AOSP, Google, OEM, Carrier, Misc, Pending, or Unlisted). A package can belong to the Google list while having an Unsafe removal classification, requiring both filters to isolate it.

### Can multiple filters be combined in a single UAD-ng CLI command?

Yes. The `PackageListContext` struct accepts optional instances of all three filter types simultaneously. When you execute a command like `uad list --state enabled --removal recommended --list google`, the `filter_package` method evaluates each condition sequentially. A package must satisfy all specified filters to appear in the output.

### How does the CLI determine which columns to display when filters are applied?

The `display_config` method (lines 94‑100 of [`commands.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/commands.rs)) checks whether `state_filter` or `removal_filter` is set to a specific variant (not `All`). If a specific filter is active, the corresponding column is hidden because the information is redundant—all displayed packages share that state or removal classification. This keeps the terminal output concise and readable.