# How Dry-Run Mode Works in UAD-ng CLI Operations: A Complete Technical Guide

> Explore how UAD-ng CLI dry-run mode works. Discover the full package-state workflow without device interaction, perfect for safe testing. Learn to use --dry-run effectively.

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

---

**When you append `--dry-run` to UAD-ng CLI commands like `uninstall`, `enable`, or `disable`, the tool executes the full package-state workflow but only prints the ADB commands it would run, without ever sending them to the device.**

The **Universal Android Debloater Next Generation (UAD-ng)** provides a safe preview mechanism through its dry-run mode, allowing users to verify exactly which shell commands will be executed before modifying their Android device. This feature is crucial when auditing pre-installed bloatware or testing package state changes across multiple user profiles. Understanding how dry-run mode works in UAD-ng CLI operations helps prevent accidental data loss and ensures predictable device management.

## What Is Dry-Run Mode in UAD-ng?

Dry-run mode is a safety feature implemented in the command-line interface of `universal-android-debloater-next-generation`. When activated via the `--dry-run` flag, the CLI processes all logic required to transition packages to their target states—building ADB command lists, validating device connections, and iterating through selected packages—but stops short of actual execution. Instead, it outputs the exact shell commands that would run during a live operation, prefixed with "Would run:" for clarity.

## How the Dry-Run Flag Is Parsed

The dry-run functionality begins at the argument parsing level in [`crates/uad-cli/src/main.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-cli/src/main.rs). For sub-commands such as **Uninstall**, **Enable**, and **Disable**, the CLI defines a boolean `dry_run` flag that is passed directly to the core command handler.

When you invoke a command like `uad uninstall com.example.app --dry-run`, the flag is captured in the command-line interface definitions and forwarded to `commands::change_package_state`. This ensures the dry-run state persists through the entire execution chain, from initial parsing through to the final package processing loop.

## The Dry-Run Execution Flow

Inside [`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), the `change_package_state` function orchestrates the dry-run behavior through a conditional execution path that prioritizes transparency over action.

### Displaying the Dry-Run Notice

Upon entering the command execution logic, the system immediately checks the `dry_run` boolean. If set to `true`, it prints a clear header to stderr/logger:

```rust
if dry_run {
    println!("DRY RUN - No changes will be made\n");
}

```

This notification appears at lines 68-71 of [`commands.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/commands.rs), ensuring users cannot miss the fact that they are in simulation mode before any package processing begins.

### Generating ADB Commands Without Execution

For each target package, the function calls `process_package_state_change`, which internally utilizes `apply_pkg_state_commands` to construct the specific ADB shell commands required to reach the desired state. However, the critical branch occurs at lines 30-34 of [`commands.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/commands.rs):

```rust
if dry_run {
    for cmd in &commands {
        println!("    Would run: {}", cmd);
    }
} else {
    execute_with_fallback(...);
}

```

When dry-run is active, the system iterates through the prepared command vectors and prints them with indentation. The `execute_with_fallback` function—responsible for actual ADB shell execution—is completely bypassed, guaranteeing zero device interaction.

## Practical Examples of UAD-ng Dry-Run Commands

The following CLI commands demonstrate how dry-run mode behaves across different package operations:

```bash

# Preview uninstall commands for a single package

uad uninstall com.example.app --dry-run

# Preview enable commands for a disabled package

uad enable com.example.app --dry-run

# Preview disable commands across multiple packages on a specific device

uad disable com.one.app com.two.app --dry-run -d xyz123

```

Typical output when running these commands:

```

Uninstalling 2 package(s) on: Nexus 6P (XYZ123)

DRY RUN - No changes will be made

  com.one.app (Enabled)
    Would run: pm uninstall -k --user 0 com.one.app

  com.two.app (Disabled)
    Would run: pm uninstall -k --user 0 com.two.app

Dry run completed. No changes were made.

```

## Key Source Files Involved

Understanding the dry-run implementation requires familiarity with three primary locations in the UAD-ng codebase:

- **[`crates/uad-cli/src/main.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-cli/src/main.rs)** – Defines the `--dry-run` CLI flags for all state-changing sub-commands and routes them to the appropriate handlers.
- **[`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)** – Contains the `change_package_state` function, the dry-run notice logic, and the conditional branch that prints "Would run:" instead of calling `execute_with_fallback`.
- **[`crates/uad-core/src/sync.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/sync.rs)** – Provides the `apply_pkg_state_commands` utility that generates the actual shell command strings; dry-run mode simply displays these strings rather than executing them.

## Summary

- **Dry-run mode in UAD-ng CLI operations** allows safe previewing of ADB commands before execution.
- The `--dry-run` flag is parsed in [`main.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/main.rs) and passed to `change_package_state` in [`commands.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/commands.rs).
- When active, the system prints "DRY RUN - No changes will be made" followed by indented "Would run:" statements for each generated command.
- No actual ADB shell commands are executed; the `execute_with_fallback` function is skipped entirely.
- This mode works with `uninstall`, `enable`, and `disable` sub-commands across single or multiple packages.

## Frequently Asked Questions

### What happens when I use --dry-run with uninstall?

The CLI processes the uninstall logic for the specified package, generates the ADB command (typically `pm uninstall -k --user 0 <package>`), and prints it with the "Would run:" prefix. The package remains installed on the device, and no user data is removed.

### Does dry-run mode require a connected device?

Yes, dry-run mode still requires a connected Android device because the logic path validates device connectivity and enumerates packages to determine the correct state transitions. However, it only displays the commands it would execute rather than modifying the device state.

### Can I use dry-run with multiple packages?

Absolutely. You can pass multiple package names to any state-changing command with `--dry-run`, and UAD-ng will display the generated ADB commands for each package sequentially. This is useful for auditing bulk operations before committing changes.

### Where is the dry-run logic implemented?

The core logic resides 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) within the `change_package_state` function. The flag definition exists in [`crates/uad-cli/src/main.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-cli/src/main.rs), while the command generation utilities are located in [`crates/uad-core/src/sync.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/sync.rs).