# How Device Detection and Connection State Management Works in Universal Android Debloater Next-Generation

> Learn how Universal Android Debloater Next-Generation detects devices via adb polling and manages connection states. Discover comprehensive phone data for CLI & GUI.

- 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-20

---

**The Universal Android Debloater Next-Generation detects Android devices by polling `adb devices` through a retry-aware wrapper that filters for status `"device"`, then enriches each serial with brand, model, SDK version, and user data to build a comprehensive `Phone` struct for CLI and GUI operations.**

The Universal Android Debloater Next-Generation relies on robust device detection to communicate with Android hardware via ADB. Understanding how this open-source tool manages connection states and discovers attached devices reveals the architecture behind its reliable cross-platform debloating capabilities. This implementation centers on a core sync module that transforms raw ADB output into structured device profiles.

## Core Device Detection Architecture

The detection pipeline begins 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), where the `get_devices_list()` function serves as the primary entry point for discovering connected Android devices. This function orchestrates ADB communication, state validation, and metadata enrichment.

### Querying ADB for Attached Devices

The low-level ADB interaction resides 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). The `AdbCommand::devices()` method executes the standard `adb devices` command and parses the tab-delimited output into structured tuples containing serial numbers and their connection statuses.

```rust
// adb.rs – devices()
pub fn devices(mut self) -> Result<Vec<(String, String)>, String> {
    self.0.arg("devices");
    Ok(self
        .run()?
        .lines()
        .skip(1) // header
        .map(|dev_stat| {
            let tab_idx = dev_stat
                .find('\t')
                .expect("There must be 1 tab after serial");
            (
                dev_stat[..tab_idx].to_string(),
                dev_stat[(tab_idx + 1)..].to_string(),
            )
        })
        .collect())
}

```

This method guarantees a one-to-one mapping between Rust methods and ADB commands, returning a `Vec<(String, String)>` where the first element represents the device serial and the second indicates the connection state (`device`, `unauthorized`, `offline`, etc.).

### Retry Logic and Connection State Filtering

The `get_devices_list()` function implements a retry mechanism using `Fixed::from_millis(500)` with 10 attempts in release builds and 3 in debug builds. It strictly filters for devices reporting status `"device"`, discarding unauthorized or offline states to ensure only ready devices are processed.

```rust
// sync.rs – get_devices_list
pub fn get_devices_list() -> Vec<Phone> {
    retry(
        Fixed::from_millis(500).take(if cfg!(debug_assertions) { 3 } else { 10 }),
        || match AdbCommand::new().devices() {
            Ok(devices) => {
                // Only keep devices whose status is "device"
                if devices.iter().all(|(_, stat)| stat != "device") {
                    return OperationResult::Retry(vec![]);
                }
                let mut device_list = vec![];
                for (serial, _) in devices {
                    device_list.push(Phone {
                        model: format!("{} {}", get_device_brand(&serial), get_device_model(&serial)),
                        android_sdk: get_android_sdk(&serial),
                        user_list: list_users_idx_prot(&serial),
                        adb_id: serial.clone(),
                    });
                }
                OperationResult::Ok(device_list)
            }
            Err(err) => {
                error!("get_devices_list() -> {err}");
                OperationResult::Retry(vec![])
            }
        },
    )
    .unwrap_or_default()
}

```

## Building the Device Profile

Once a device confirms its ready state, the system collects comprehensive metadata through helper functions defined in [`sync.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/sync.rs).

### Metadata Extraction via Android Properties

The enrichment process queries device properties via `adb shell getprop` to populate the `Phone` struct fields. The system calls `get_device_brand()` to read `ro.product.brand`, `get_device_model()` to read `ro.product.model`, and `get_android_sdk()` to parse `ro.build.version.sdk` into a `u8` value.

### User Enumeration and Protection Status

The `list_users_idx_prot` helper executes `pm list users` to identify protected user accounts, ensuring the debloater respects multi-user Android configurations. This function marks protected users in the returned data structure, preventing accidental modifications to system-critical profiles.

## Device Selection Strategies

### Selecting a Target Device from the CLI

The CLI layer in [`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) provides `get_target_device()`, which either matches a specific serial number provided via the `--device` flag or defaults to the first available ready device when multiple connections exist.

```rust
// device.rs – get_target_device
let devices = get_devices_list();
if devices.is_empty() {
    eprintln!("Error: No devices found");
    return Err("No devices found".into());
}
let target_device = if let Some(device_id) = device {
    devices.iter()
        .find(|d| d.adb_id == device_id)
        .ok_or("Device not found")?
        .clone()
} else {
    // First device is used if multiple are present
    devices[0].clone()
};
Ok(target_device)

```

## GUI Integration and Real-Time State Synchronization

The graphical interface in [`crates/uad-gui/src/gui.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-gui/src/gui.rs) maintains device state through asynchronous tasks that periodically invoke `get_devices_list()`. This ensures the device selector reflects current ADB connections without blocking the UI thread.

```rust
// gui.rs – device loading task
Task::perform(async { get_devices_list() }, Message::LoadDevices)

```

When `Message::LoadDevices` receives the updated device list, the UI refreshes its state to display newly connected devices or remove disconnected ones.

## Summary

- **Device detection** relies on `get_devices_list()` 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) to poll ADB and construct `Phone` structs.
- **Connection state management** filters for devices reporting `"device"` status, retrying up to 10 times (500ms intervals) to handle transient ADB states.
- **Metadata enrichment** gathers brand, model, SDK version, and user information through property queries and package manager commands.
- **CLI selection** supports explicit serial targeting or defaults to the first ready device, while the **GUI** maintains synchronization through asynchronous task polling.

## Frequently Asked Questions

### How does the Universal Android Debloater handle unauthorized or offline devices?

Devices reporting statuses other than `"device"` (such as `unauthorized` or `offline`) trigger the retry mechanism in `get_devices_list()`. The function discards these entries and attempts the query again until either ready devices appear or the retry budget exhausts. This prevents the application from attempting operations on devices that cannot yet accept ADB commands.

### What information does the Phone struct contain?

The `Phone` struct contains four primary fields: `adb_id` (the device serial number), `model` (concatenated brand and model strings), `android_sdk` (the API level as a `u8`), and `user_list` (a vector of user indices with protection status markers). This structure provides sufficient context for the debloater to display device information and respect multi-user boundaries.

### How does the retry mechanism work in device detection?

The retry logic uses a fixed 500-millisecond interval between attempts, allowing 3 tries in debug builds and 10 in release builds. This configuration balances development responsiveness with production stability, accommodating ADB daemon startup delays and USB connection initialization without indefinite blocking.

### Can I specify which device to target when multiple are connected?

Yes. The CLI accepts a `--device` flag followed by the serial number. The `get_target_device()` function in [`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) searches the device list for an exact `adb_id` match, returning an error if the specified serial is not found among ready devices. Without this flag, the system selects the first device in the list, though users should verify the correct device when multiple Android devices are attached.