Technical Advisory: System Settings Persistence Failure via 64KB UTF-8 Serialization Overflow in OxygenOS / ColorOS
Component: frameworks/base/packages/SettingsProvider / com.android.providers.settings.SettingsState
Affected Platforms: OxygenOS 13.x / ColorOS 13.x (Android 13, Snapdragon 865 / SM8250 platform tested)
Impact: Complete breakdown of Settings.Secure disk persistence; all user-configured settings revert upon system reboot.
On certain OxygenOS/ColorOS firmware releases, modifications made to system settings (including timepower_config, display timeout, accessibility, and security preferences) revert to default values after a device reboot.
Initial triage often misattributes this issue to third-party Xposed/LSPosed hooks, corrupt SQLite databases, or read-only filesystem mounts. Empirical tracing reveals the true root cause: an uncaught java.io.UTFDataFormatException within SettingsState.doWriteState(), triggered when a single OEM setting entry exceeds the 65,535-byte serialization limit enforced by Java's FastDataOutput.writeUTF() implementation.
This document details the serialization failure mechanism, traces the specific OEM string leakage key (op_sysui_qs_tiles_hide), and provides a non-destructive remediation procedure.
Android's SettingsProvider maintains in-memory setting tables (system, secure, global) that are periodically flushed to disk at /data/system/users/0/settings_*.xml.
During XML binary/text serialization, com.android.internal.util.FastDataOutput.writeUTF() serializes string attributes. Java's Modified UTF-8 specification encodes string length as an unsigned 16-bit integer (u2), capping the maximum string length at 65,535 bytes:
If a single setting string exceeds 65,535 bytes, the length calculation overflows to a negative integer value. FastDataOutput.writeUTF() throws a UTFDataFormatException:
E SettingsState: Failed to write settings, restoring backup
E SettingsState: java.io.IOException: Modified UTF-8 length too large: -66142
at com.android.internal.util.FastDataOutput.writeUTF(FastDataOutput.java:167)
at com.android.internal.util.BinaryXmlSerializer.attribute(BinaryXmlSerializer.java:209)
at com.android.providers.settings.SettingsState.setValueAttribute(SettingsState.java:991)
at com.android.providers.settings.SettingsState.writeSingleSetting(SettingsState.java:968)
at com.android.providers.settings.SettingsState.doWriteState(SettingsState.java:853)
Inspection of Settings.Secure revealed an OEM-specific key:
-
Key:
op_sysui_qs_tiles_hide - Measured String Length: 66,165 characters
-
Target Package:
/system_ext/priv-app/SystemUI/SystemUI.apk(classes.dex) -
Disassembled Class:
Lcom/android/systemui/qs/QSTileHost$OperatorCustom$HideTileBroadcastReceiver;(Class Def #5093, Bytecode Offsets:0x51d9a2,0x51dd12)
Mechanism: OxygenOS/ColorOS SystemUI dynamically updates op_sysui_qs_tiles_hide when tile visibility changes (e.g., Wi-Fi Calling status updates, SIM card state transitions, or Quick Settings customization). Within QSTileHost$OperatorCustom$HideTileBroadcastReceiver.onReceive(), string updates lack a deduplication check (.contains()). Tile package strings are repeatedly appended:
Over extended device usage, the string size eventually passes the 65,535-byte threshold.
When any key in Settings.Secure is modified (via the Settings GUI or ADB), SettingsState schedules a batch write to disk.
When serialization reaches the oversized op_sysui_qs_tiles_hide entry, FastDataOutput.writeUTF() throws UTFDataFormatException. SettingsState catches the exception, aborts the write transaction, and restores /data/system/users/0/settings_secure.xml from the backup file on disk.
Consequently:
- In-memory changes persist only for the duration of the current boot session.
- The physical disk file
/data/system/users/0/settings_secure.xmlis never updated. - Upon reboot,
SettingsProviderreloads the stale XML file from disk, discarding all user modifications.
For scheduled power configurations (timepower_config), OxygenOS maintains a system property:
persist.sys.poweralarm.time(Unix epoch timestamp in milliseconds).
Because SettingsState fails to commit the updated timepower_config to disk, persist.sys.poweralarm.time retains a legacy timestamp (e.g., 1785020404000, corresponding to 07:00 AM). On system boot, init reloads persist.sys.poweralarm.time, and com.qualcomm.qti.poweroffalarm re-injects the 07:00 AM power-on alarm into SettingsProvider, enforcing a persistent state loop.
Filter system logs for serialization failures within SettingsState:
adb shell logcat -d | grep -i SettingsStateLook for the following signature:
E SettingsState: Failed to write settings, restoring backup : Modified UTF-8 length too large
Identify keys exceeding byte limits using Python over ADB:
python -c "import subprocess; out=subprocess.check_output(['adb', 'shell', 'settings list secure']).decode('utf-8', errors='ignore'); [print(line[:80], '-> len=', len(line)) for line in out.splitlines() if len(line) > 1000]"Execute the following standard ADB shell commands (no root required for standard devices; use su -c if LSPosed hooks block shell writes).
adb shell settings delete secure op_sysui_qs_tiles_hideadb shell setprop persist.sys.poweralarm.time 0
adb shell setprop sys.power_off_alarm 0adb shell settings put secure timepower_config 070000127230000127Confirm that SettingsState successfully serializes to disk without throwing exceptions (requires root file read):
adb shell "su -c 'strings /data/system/users/0/settings_secure.xml | grep -A 2 -B 2 timepower_config'"Expected Output: The target string 070000127230000127 is present in settings_secure.xml.
Reboot the device:
adb rebootAfter boot completion, query the setting and property states:
adb shell settings get secure timepower_config
# Expected: 070000127230000127
adb shell getprop persist.sys.poweralarm.time
# Expected: 0- AOSP Issue Tracker & FastDataOutput Specification:
com.android.internal.util.FastDataOutput.writeUTF()implementation details and unsigned 16-bit string length boundaries. - CVE-2024-31317 Research / PoC:
SettingsStatedenial-of-service vector triggered via intentionally oversized string attributes inSettingsProvider. Reference Gist:https://gist.github.com/rabits/eef4fad0bd024786a3afde2bc1f32b7e