# How the Configuration System Handles Device-Specific Settings in Universal Android Debloater

> Discover how the Universal Android Debloater's TOML configuration system manages device-specific settings using ADB serial identifiers, ensuring tailored debloating.

- 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 Universal Android Debloater Next Generation uses a TOML-based configuration system that stores global settings in `GeneralSettings` and per-device preferences in a `Vec<DeviceSettings>`, keyed by ADB serial identifiers, with all persistence logic centralized in [`crates/uad-core/src/config.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/config.rs).**

The Universal Android Debloater Next Generation (UAD-ng) manages both global application preferences and individual device configurations through a unified **configuration system** written in Rust. This architecture allows users to maintain distinct **device-specific settings**—such as disable mode or multi-user preferences—for each connected Android device while keeping universal options like themes and backup folders consistent across sessions.

## Core Configuration Architecture

The configuration system is defined in [`crates/uad-core/src/config.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/config.rs) and revolves around three primary structures that separate global concerns from device-specific settings.

### The Config Struct

The `Config` struct serves as the top-level container that binds general application settings to a collection of **device-specific settings**. It is defined as:

```rust
struct Config {
    general: GeneralSettings,
    devices: Vec<DeviceSettings>,
}

```

This structure maps directly to the [`config.toml`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/config.toml) file stored in the user's configuration directory. The file path is determined at runtime by the static `CONFIG_FILE` constant, which uses `LazyLock` to resolve to `<CONFIG_DIR>/config.toml` according to the source code.

### GeneralSettings vs DeviceSettings

The system cleanly separates universal preferences from per-device configurations:

- **GeneralSettings** — Contains global options including `theme: String`, `expert_mode: bool`, and `backup_folder: PathBuf`. These apply to the entire application regardless of which device is connected.

- **DeviceSettings** — Contains options specific to a single Android device, identified by its ADB serial number in the `device_id: String` field. Key fields include `disable_mode: bool` and `multi_user_mode: bool`, allowing different debloating behaviors for different phones or tablets.

## How Device-Specific Settings Are Stored and Retrieved

**Device-specific settings** are stored as elements within the `devices` vector in the `Config` struct. The `device_id` field acts as the unique primary key for lookup operations.

When the application starts, `Config::load_configuration_file()` reads the TOML file from disk or creates a fresh configuration with defaults if the file is missing. To retrieve settings for a specific device, the code iterates through the vector matching against the ADB serial:

```rust
use uad_core::config::{Config, DeviceSettings};

let cfg = Config::load_configuration_file();
let target_id = "emulator-5554".to_string();

let device_cfg: Option<&DeviceSettings> = cfg.devices
    .iter()
    .find(|d| d.device_id == target_id);

if let Some(dev) = device_cfg {
    println!("Disable mode: {}", dev.disable_mode);
}

```

## Persisting Changes with save_device_settings

Updates to **device-specific settings** are centralized through the `Config::save_device_settings()` method. This routine handles both insertion of new device entries and updates to existing ones, ensuring the TOML file on disk remains synchronized with runtime state.

The method performs the following operations:

1. Searches the `devices` vector for an existing entry matching the incoming `device_id`.
2. Overwrites the existing entry if found, or pushes the new `DeviceSettings` onto the vector.
3. Updates the `general` field with the supplied `GeneralSettings`.
4. Serializes the entire `Config` struct to TOML and writes it to `CONFIG_FILE`.

```rust
// Simplified excerpt from crates/uad-core/src/config.rs
if let Some(device) = self.devices.iter_mut()
                                   .find(|x| x.device_id == device_settings.device_id) {
    *device = device_settings;
} else {
    self.devices.push(device_settings);
}
self.general = general;
let toml = toml::to_string(&self).unwrap();
fs::write(&*CONFIG_FILE, toml).expect("Could not write config file to disk!");

```

## Integration with the GUI and CLI

The **configuration system** is consumed by both the graphical and command-line interfaces, ensuring consistent behavior across interaction modes.

In the GUI layer ([`crates/uad-gui/src/views/settings.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-gui/src/views/settings.rs)), the settings view loads the configuration, locates the entry matching the currently selected phone's `adb_id`, and populates the UI widgets. When users toggle options like "Disable mode," the view constructs a new `DeviceSettings` instance and invokes `save_device_settings()` to persist the changes immediately.

For CLI operations, [`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) resolves target device IDs and retrieves their specific configurations, while [`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) implements batch operations that update settings programmatically.

## Handling Backup Data and Defaults

The `DeviceSettings` struct includes a `backup` field of type `BackupSettings`, but this data is intentionally transient. Marked with `#[serde(skip)]`, the field is excluded from serialization and exists only in memory during runtime. This design keeps the TOML configuration file focused on user preferences while backup metadata is handled separately.

If `load_configuration_file()` encounters a corrupted or unreadable configuration file, it falls back to `Config::default()`. This default implementation produces an empty `devices` vector and initializes `GeneralSettings` with `DEFAULT_THEME` and a backup folder located in the system's cache directory.

## Summary

- The **configuration system** uses a TOML file ([`config.toml`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/config.toml)) stored in the user's configuration directory, with paths managed by the `CONFIG_FILE` constant in [`crates/uad-core/src/config.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/config.rs).
- **Global settings** are handled by `GeneralSettings` (theme, expert mode, backup folder), while **device-specific settings** use `DeviceSettings` keyed by ADB serial numbers.
- The `devices` vector within the `Config` struct stores all per-device preferences, with `save_device_settings()` managing upsert operations and disk persistence.
- Backup-related data in `DeviceSettings` is marked with `#[serde(skip)]` and remains in memory only, never written to the configuration file.
- Both the GUI ([`crates/uad-gui/src/views/settings.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-gui/src/views/settings.rs)) and CLI ([`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)) interact with the same core configuration API, ensuring consistent device-specific behavior across interfaces.

## Frequently Asked Questions

### Where is the configuration file stored on my system?

The configuration file is located at `<CONFIG_DIR>/config.toml`, where `<CONFIG_DIR>` resolves to the user's standard configuration directory (e.g., `~/.config/universal-android-debloater/` on Linux). The exact path is determined at runtime by the `CONFIG_FILE` static variable in [`crates/uad-core/src/config.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/config.rs).

### How does UAD-ng distinguish settings between different Android devices?

Each device is identified by its unique ADB serial number, stored in the `device_id` field of the `DeviceSettings` struct. When you connect a device, the application looks up its serial in the `devices` vector of the configuration; if found, it loads the associated settings, otherwise it creates a new entry with defaults.

### What happens to my settings if the configuration file becomes corrupted?

If the TOML file cannot be parsed during `load_configuration_file()`, the system automatically falls back to `Config::default()`. This generates a fresh configuration with an empty device list and default global settings, effectively resetting the application to factory defaults without crashing.

### Why don't backup settings persist in the configuration file?

The `backup` field within `DeviceSettings` is annotated with `#[serde(skip)]`, which instructs the TOML serializer to ignore it. This intentional design choice keeps backup metadata in memory only, preventing sensitive or temporary path data from accumulating in the persistent configuration file while maintaining per-device backup state during the session.