# How the UAD-NG Architecture Supports Future ADB Client Implementation Alternatives

> Discover how UAD-NG's architecture supports future ADB client alternatives. Easily swap ADB CLI with Rust libraries or mock transports without altering core code.

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

---

**The Universal Android Debloater Next Generation isolates all Android Debug Bridge logic behind a thin-wrapper `ACommand` struct in [`crates/uad-core/src/adb.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/adb.rs), enabling developers to swap the underlying `adb` CLI implementation for a native Rust library or mock transport without touching the GUI or CLI code.**

The Universal Android Debloater Next Generation (UAD-NG) repository is organized as a modular Rust workspace that deliberately separates low-level ADB handling from application logic. This architectural choice ensures that **future ADB client implementation alternatives** can be introduced with minimal refactoring, as only the core crate contains knowledge of the concrete transport mechanism.

## Thin-Wrapper Abstraction Pattern

At the heart of the architecture lies the `ACommand` struct defined in [`crates/uad-core/src/adb.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/adb.rs). This struct implements a *thin wrapper* around the external `adb` binary, following **type-state** and **new-type** patterns to expose only the subset of ADB commands required by the application.

The module documentation explicitly notes that this implementation may be replaced by an `adb_client` library in the future. Because `ACommand` encapsulates all direct interaction with the ADB transport—handling process spawning, output parsing, and error conversion—the rest of the codebase remains agnostic to whether commands execute via CLI or native sockets.

Key methods on `ACommand` include:
- `devices()` – Lists attached devices
- `shell(serial)` – Returns a `ShellCommand` builder for device-specific operations  
- `version()` – Queries the ADB server version

## Clear Separation of Responsibilities

The workspace divides functionality into distinct crates that communicate through public APIs rather than implementation details. The `uad-gui` and `uad-cli` crates depend exclusively on the `uad_core` crate's public interface, never invoking the `adb` binary directly.

This means:
- **UI code** (`crates/uad-gui/src/widgets/...`) calls high-level sync operations without knowing about `ACommand`
- **CLI code** ([`crates/uad-cli/src/device.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-cli/src/device.rs)) obtains device handles through the core API
- **Core logic** ([`crates/uad-core/src/adb.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/adb.rs)) remains the sole location where ADB transport details reside

## Designing for Future ADB Client Implementation Alternatives

The architecture anticipates migration to native ADB implementations through its trait-like boundaries. While the current codebase constructs `ACommand::new()` directly, the struct's method signatures (`devices`, `shell`, `getprop`, etc.) form a stable contract that alternative implementations can satisfy.

For example, a future `adb_client` crate could expose a struct implementing identical methods, allowing a simple type alias swap:

```rust
// Current implementation
pub struct ACommand { /* ... */ }

// Future replacement could be:
// pub type ACommand = adb_client::Command;

```

Because the sync layer and UI components interact only with the `ACommand` API surface, such a change propagates automatically without requiring updates to calling code.

## Modular Sync Layer

High-level device operations—such as listing devices, querying properties, and managing packages—reside 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). This module consumes the `ACommand` abstraction internally, orchestrating multiple ADB operations into cohesive workflows.

Since [`sync.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/sync.rs) depends only on the `ACommand` interface rather than concrete ADB transport details, any change to the underlying client automatically benefits all sync operations. The module functions as an adapter between the low-level transport and application-specific business logic.

## Dependency-Injection-Ready Design

The thin-wrapper pattern facilitates testing and alternative transports through its injectable design. Developers can implement mock ADB clients that satisfy the same method signatures as `ACommand`:

```rust
use uad_core::adb::ACommand;

// Example: Listing devices with the current wrapper
fn list_devices() -> Result<Vec<(String, String)>, String> {
    ACommand::new().devices()
}

// Example: Running shell commands on a specific device
fn get_prop(serial: &str, key: &str) -> Result<String, String> {
    ACommand::new()
        .shell(serial)
        .getprop(key)
}

// Mock implementation for testing
struct MockCommand;

impl MockCommand {
    fn new() -> Self { MockCommand }
    
    fn devices(&self) -> Result<Vec<(String, String)>, String> {
        Ok(vec![("mock123".into(), "device".into())])
    }
    
    fn shell(&self, _serial: &str) -> Self { MockCommand }
    fn getprop(&self, _key: &str) -> Result<String, String> {
        Ok("mock_value".into())
    }
}

```

Swapping implementations requires only changing the type imported by the sync layer, leaving all business logic intact.

## Summary

- **Single source of truth**: Only [`crates/uad-core/src/adb.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/adb.rs) contains knowledge of the concrete ADB transport mechanism.
- **Stable API boundary**: The `ACommand` struct provides a consistent interface that hides implementation details from GUI and CLI crates.
- **Modular workspace**: Separation between `uad-core`, `uad-cli`, and `uad-gui` ensures changes to the ADB client affect only the core crate.
- **Future-ready**: The architecture explicitly supports replacement with native `adb_client` libraries through its thin-wrapper abstraction and type-state patterns.

## Frequently Asked Questions

### What is the ACommand struct in UAD-NG?

The `ACommand` struct is a thin wrapper defined in [`crates/uad-core/src/adb.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/adb.rs) that encapsulates all direct interaction with the Android Debug Bridge. It provides methods like `devices()` and `shell()` that abstract the underlying transport—currently the `adb` CLI—while exposing a stable interface for the rest of the application.

### How does the architecture prevent ADB changes from breaking the GUI?

The GUI crate (`uad-gui`) and CLI crate (`uad-cli`) depend only on the public API of `uad-core`, never calling `adb` directly. Because all ADB logic is isolated in the core crate behind the `ACommand` abstraction, changing the transport mechanism requires modifications only to [`crates/uad-core/src/adb.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/adb.rs), leaving the UI and CLI layers untouched.

### Can I use a mock ADB client for testing UAD-NG?

Yes. The architecture supports dependency injection through its thin-wrapper design. You can implement a mock struct with the same method signatures as `ACommand` (such as `new()`, `devices()`, and `shell()`) and substitute it via a type alias or factory pattern, allowing unit tests to run without requiring actual Android devices or the ADB binary.

### Where are high-level device operations implemented in UAD-NG?

High-level operations like package installation and property querying are implemented 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). This module builds upon the `ACommand` abstraction to orchestrate complex workflows, ensuring that any changes to the underlying ADB client automatically propagate to all device management functions.