# How Backup and Restore Functionality Works in UAD-ng: A Technical Deep Dive

> Explore UAD-ng backup and restore functionality. Learn how timestamped JSON files and a three-layer architecture manage debloating states across Android devices.

- 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

---

**UAD-ng stores device debloating states in timestamped JSON files and can re-apply those states to any compatible Android device through a three-layer architecture spanning the UI, core backup logic, and data structures.**

The **backup and restore functionality in UAD-ng** (Universal Android Debloater Next Generation) enables users to capture a snapshot of removed or disabled packages and replicate that configuration across devices. According to the Universal-Debloater-Alliance/universal-android-debloater-next-generation source code, this system relies on JSON serialization, user mapping validation, and dynamic ADB command generation. The implementation separates concerns between the graphical interface, core backup operations, and underlying data structures.

## Architecture Overview

The backup system operates across three distinct layers that handle user interaction, data persistence, and device state management.

| Layer | Responsibility | Key Source Files |
|-------|---------------|------------------|
| **UI Layer** | Renders backup controls, file pickers, and status messages. | [`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) |
| **Core Logic** | Executes backup creation, directory scanning, and restoration workflows. | [`crates/uad-core/src/save.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/save.rs) |
| **Data Structures** | Defines serializable entities and configuration paths. | [`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), [`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) |

## Creating Device Backups

### Data Collection and Serialization

When a user initiates a backup, the system first collects the current device state. The UI layer gathers the list of users (`Vec<User>`) and per-user package vectors (`Vec<CorePackage>`) from the `sync::Phone` struct. It then invokes `backup_phone(users, device_id, phone_packages)` in [`crates/uad-core/src/save.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/save.rs).

This function constructs a `PhoneBackup` struct containing the device ID and a `UserBackup` for each user. Each `UserBackup` captures only packages marked as **Uninstalled** or **Disabled**, filtering out enabled system apps to minimize file size. The `serde_json::to_string_pretty` method serializes these structures into human-readable JSON.

### File Storage and Naming Convention

The backup directory defaults to `<cache_dir>/backups` as defined in `Config::load_configuration_file().general.backup_folder`, though users may select custom locations through the GUI. The system creates a subfolder named after the device ID (`backup_path = backup_dir.join(device_id)`) and generates timestamped filenames using the `generate_backup_name` helper in [`utils.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/utils.rs), producing files like [`2024-01-15_14-30-22.json`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/2024-01-15_14-30-22.json).

```rust
use uad_core::save::backup_phone;
use uad_core::sync::{User, CorePackage};

// users: Vec<User> from device sync
// device_id: String (ADB serial number)
// phone_packages: &[Vec<CorePackage>] indexed by user
match backup_phone(users, device_id.clone(), phone_packages) {
    Ok(true) => println!("Backup created for device {}", device_id),
    Err(e) => eprintln!("Backup failed: {}", e),
}

```

If any step fails, the function returns a descriptive error string; successful completion yields `Ok(true)`.

## Discovering and Selecting Backups

### Listing Available Backup Files

The `list_available_backups(dir)` function in [`save.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/save.rs) scans the configured backup directory using `fs::read_dir` and returns a vector of `DisplayablePath` wrappers. These paths populate the UI's backup selection dropdown in [`settings.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/settings.rs).

### Extracting User Information

Before restoration, `list_available_backup_user(backup)` deserializes the selected JSON file into a `PhoneBackup` struct and extracts user IDs. This validation ensures the target device contains compatible user profiles before attempting restoration.

```rust
use uad_core::save::{list_available_backups, list_available_backup_user};
use uad_core::config::Config;

let backup_dir = Config::load_configuration_file().general.backup_folder;
let backups = list_available_backups(&backup_dir);

for backup in backups {
    println!("Available backup: {}", backup.path.display());
    
    // Check which users exist in this backup
    if let Ok(users) = list_available_backup_user(&backup) {
        println!("Contains {} user profiles", users.len());
    }
}

```

## Restoring from Backup

### User Mapping and Validation

The `restore_backup(selected_device, packages, settings)` function in [`save.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/save.rs) begins by deserializing the selected backup file. It attempts to map each `UserBackup` in the file to a corresponding `User` on the target device using `selected_device.user_list`. If any backup user lacks a matching target device user, the entire restore operation aborts with an error to prevent partial system modifications.

### Package State Reconciliation

For each package stored in the backup, the system locates the current package instance in the device's package matrix (`packages[i_user]`). Missing packages—those uninstalled or renamed since the backup—are counted and logged but skipped rather than causing failures. This tolerance ensures backups remain usable across ROM updates or device migrations where specific packages may no longer exist.

### ADB Command Generation

The `apply_pkg_state_commands` function generates precise ADB commands to transition each package to its backed-up state (Uninstalled, Disabled, or Enabled). These commands wrap in `BackupPackage` structs containing the user index, package index, and command list. The UI receives a `RestoreResult { packages, skipped_count }` and executes the commands through the CLI/ADB layer.

```rust
use uad_core::save::restore_backup;
use uad_core::sync::Phone;

// selected_device: &Phone (target device)
// packages: Current package matrix from device
// settings: UI state containing selected backup path
match restore_backup(&selected_device, &packages, &settings) {
    Ok(result) => {
        for pkg in result.packages {
            for cmd in pkg.commands {
                // Execute via your ADB runner
                run_adb_command(&cmd);
            }
        }
        println!("Restore complete. Skipped {} packages", result.skipped_count);
    }
    Err(e) => eprintln!("Restore error: {}", e),
}

```

## Configuration and Storage Paths

Backup storage locations are controlled through `Config.general.backup_folder` 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 default location uses the system cache directory, but the Settings view in [`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) provides a "Choose backup folder" button that updates this configuration via `Message::ChooseBackUpFolder`. This path persistence ensures subsequent backup and restore operations use the user-defined directory across application restarts.

## Summary

- **UAD-ng** uses JSON serialization via `serde` to capture debloating states in `PhoneBackup` and `UserBackup` structures.
- The **core backup logic** resides in [`crates/uad-core/src/save.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/save.rs), implementing `backup_phone`, `restore_backup`, and directory scanning functions.
- **Timestamped filenames** and device-specific subfolders organize backups in the configured `backup_folder` directory.
- **Restoration validates user mapping** before generating ADB commands, skipping missing packages rather than failing entirely.
- The **UI layer** in [`settings.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/settings.rs) handles user interaction, file selection, and configuration updates through typed message passing.

## Frequently Asked Questions

### Where does UAD-ng store backup files by default?

By default, UAD-ng stores backups in the system cache directory under a `backups` subfolder, specifically at `CACHE_DIR/backups/<device_id>/`. This path is defined in `Config.general.backup_folder` within [`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 can be customized through the Settings interface.

### What happens if a package from the backup no longer exists on the device?

The restore process skips missing packages and continues with the remaining entries. The `restore_backup` function counts these skipped packages and returns the total in `RestoreResult.skipped_count`, allowing the UI to inform the user without aborting the entire restoration.

### Can I restore a backup to a different device than the original?

Yes, provided the target device contains the same user profiles as those stored in the backup. The restoration logic validates user mapping between the `PhoneBackup` struct and the target device's `user_list` before applying any changes, returning an error only if user accounts are incompatible.

### What package states are included in a backup?

Backups capture only packages marked as **Uninstalled** or **Disabled**. Enabled packages are filtered out during the `backup_phone` execution to minimize file size and focus the backup on modifications made through the debloating process.