“This app is fully offline, so its data never leaves the device” is not always accurate. Apps targeting Android 6.0 or later normally participate in Auto Backup by default. The system can upload files from an app’s private storage into the user’s cloud backup or move them during device-to-device migration. An app without network permission is not automatically an app whose data can never leave the device.

The answer is not to disable every backup. A good policy restores progress and preferences that matter to the user while excluding credentials, device identifiers, caches, and state that cannot safely survive migration.

Classify by meaning rather than directory

Before writing XML rules, ask whether each value remains valid after restoration on a different device:

Data Typical policy Reason
Game progress and user-created content Include Valuable and difficult to reconstruct
Theme, difficulty, accessibility preferences Include Still valid after migration
Caches, thumbnails, temporary exports Exclude Reconstructable and consumes quota
Login tokens and session keys Exclude Potentially unsafe or invalid on another device
FCM token and device ID Exclude Bound to the original installation or device
External file URI Usually exclude It may be invalid or identify different content after restore

Android documents a 25 MB file quota per app for Auto Backup. Even non-sensitive downloadable assets can displace the information a user actually expects to recover.

Configure both generations of backup rules

An app targeting API 31 or later uses data-extraction-rules on devices running Android 12 or later. It still needs the older fullBackupContent format for Android 11 and earlier:

<application
    android:allowBackup="true"
    android:fullBackupContent="@xml/backup_rules"
    android:dataExtractionRules="@xml/data_extraction_rules" />

The newer format can make separate choices for cloud backup and device transfer:

<?xml version="1.0" encoding="utf-8"?>
<data-extraction-rules>
    <cloud-backup disableIfNoEncryptionCapabilities="true">
        <include domain="file"
                 path="datastore/user_settings.preferences_pb" />
        <include domain="database" path="progress.db" />
    </cloud-backup>
    <device-transfer>
        <include domain="file"
                 path="datastore/user_settings.preferences_pb" />
        <include domain="database" path="progress.db" />
    </device-transfer>
</data-extraction-rules>

Adding an <include> changes the default behavior into an allowlist. That is safer than trying to enumerate every sensitive file: a new file does not silently enter the backup before someone reviews it. The legacy rules should express the same policy so that OS versions do not produce opposite privacy behavior.

Separate sensitive settings into another file

If one DataStore file contains both a theme preference and authentication material, XML can only include or exclude that file as a unit. Put transferable settings and sensitive local state in separate DataStore files. The same principle applies to Room: durable user progress and reconstructable cache data should not share a database that cannot be split by backup rules.

Use noBackupFilesDir for files that explicitly must not be backed up. Cache directories are excluded by the backup system. Storage location is part of the privacy design, not a detail to repair with a long exclusion list just before release.

Cloud backup and device transfer have different risks

<cloud-backup> sends eligible data to a remote backup service, while <device-transfer> covers migration between devices. A large local resource might be a poor use of cloud quota but useful in a direct transfer. A credential might be inappropriate for either path.

disableIfNoEncryptionCapabilities can prevent cloud backup when the device cannot provide the required encryption capability. Encryption does not replace classification: an encrypted token can still be invalid or dangerous when restored onto another device.

Restoration must handle version skew

A backup can be created by an old app version and restored as part of installing a newer one. Room migrations, DataStore schemas, and business defaults must support that order. Restoration should also be idempotent. A repeated system action or first-launch initializer must not overwrite data that was just restored.

Regenerate device-bound values after restoration, validate any account session, and treat missing cache files as normal. The app cannot assume that backup always succeeds. A device might lack an eligible account or network, and the user can disable backup.

Test a real uninstall and restore

Reading the XML is not evidence that the policy works. Android’s documented test flow uses bmgr to request a backup, then removes, reinstalls, and restores the app. The smallest useful commands include:

adb shell bmgr enable true
adb shell bmgr backupnow com.example.app

The test device must have a usable backup transport. A complete test prepares meaningful preferences and progress, performs the backup, clears the app, and reinstalls it. Verify that durable values return, excluded tokens and device IDs are regenerated, database migrations succeed, and the app starts without its cache.

Cover the current Android release and at least one Android 11-or-earlier device because the two XML formats can drift apart. Repeat restoration tests whenever a DataStore filename, Room database name, or storage directory changes.

Describe system backup in the privacy policy

If a policy says the app “does not upload data,” explain whether Android can perform encrypted cloud backup or device migration under the user’s system settings and whether the developer can directly access those backups. Distinguish app-initiated upload, operating-system backup, and user export instead of collapsing three data flows into one vague promise.

An offline app reduces active data flows, but system backup remains a real path. An allowlist for valuable data, separated sensitive storage, matching rules for old and new Android versions, and a repeatable uninstall-and-restore test provide both a credible privacy boundary and a migration experience that does not lose user progress.