In Android 12, a new file format was introduced to store system settings. Previously, the Android OS stored the settings in XML format. It was easy to read and edit them using any text editor (of course with the root permission).
There are several files that are interesting to us:
String SETTINGS_FILE_GLOBAL = "settings_global.xml";
String SETTINGS_FILE_SYSTEM = "settings_system.xml";
String SETTINGS_FILE_SECURE = "settings_secure.xml";
String SETTINGS_FILE_SSAID = "settings_ssaid.xml";
String SETTINGS_FILE_CONFIG = "settings_config.xml";
In spite of the extension of the files is still xml
they are not in XML format:
sdex@rs:~$ file settings_ssaid.xml
settings_ssaid.xml: data
sdex@rs:~$ xxd settings_ssaid.xml
00000000: 4142 5800 1032 ffff 0008 7365 7474 696e ABX..2....settin
00000010: 6773 6fff ff00 0776 6572 7369 6f6e ffff gso....version..
00000020: ffff 32ff ff00 0773 6574 7469 6e67 2fff ..2....setting/.
00000030: ff00 0269 6400 0130 2fff ff00 046e 616d ...id..0/....nam
00000040: 6500 0775 7365 726b 6579 2fff ff00 0576 e..userkey/....v
00000050: 616c 7565 0040 4441 3845 4638 3643 3638 alue.@DA8EF86C68
It’s still possible to read the data stored in the file in the hex editor, it’s quite inconvenient though. The file starts with ABX
header and it looks like the data is in some binary format.
A quick search led me to this great post - Android ABX – Binary XML, definitely check it out if you want to know more about the file format. We will focus on the framework implementation instead.
On the high level, there are two classes that are responsible for writing and reading the binary data: BinaryXmlSerializer
and BinaryXmlPullParser
.
The documentation for BinaryXmlSerializer
explains what is going on:
Serializer that writes XML documents using a custom binary wire protocol which benchmarking has shown to be 4.3x faster and use 2.4x less disk space than
Xml.newFastSerializer()
for a typicalpackages.xml
. The high-level design of the wire protocol is to directly serialize the event stream, while efficiently and compactly writing strongly-typed primitives delivered through theTypedXmlSerializer
interface. Each serialized event is a single byte where the lower half is a normalXmlPullParser
token and the upper half is an optional data type signal, such asTYPE_INT
. This serializer has some specific limitations:
- Only the UTF-8 encoding is supported.
- Variable length values, such as
byte[]
orString
, are limited to 65,535 bytes in length. Note that String values are stored as UTF-8 on the wire.- Namespaces, prefixes, properties, and options are unsupported.
To instantiate the instances of these classes we can use Xml.newBinarySerializer()
and Xml.newBinaryPullParser()
respectively.
Or Xml.resolveSerializer()
and Xml.resolvePullParser()
to let the system decide which serializer or parser to use based on the system preferences and the input. All these methods of android.util.Xml
class are hidden, but it’s quite easy to bypass using reflection.
Once we instantiated the parser we can start to parse the files. It’s very similar to using a regular XmlPullParser
where need to check the tags and read attributes:
val parser: XmlPullParser = instantiateParser(input)
var eventType = parser.eventType
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG && parser.name == "setting") {
val id = parser.getAttributeValue(null, "id")
// ...
}
eventType = parser.next()
}
And writing is similar to using XmlSerializer
:
val serializer: XmlSerializer = instantiateSerializer(output)
serializer.startDocument(null, true)
serializer.startTag(null, TAG_SETTINGS)
serializer.attribute(null, ATTR_VERSION, version)
serializer.endTag(null, TAG_SETTINGS);
serializer.endDocument();
For more advanced usage both the parser and serializer have their own specific methods, refer to the source code for details.
The AOSP is supplied with a utility abx.jar
that converts binary XML to human-readable XML and vice versa. It’s located at /system/framework/abx.jar
. It can be called from the shell using the next commands:
abx2xml.sh
, xml2abx.sh
, or even abx.sh
.
They all do the same thing and only the order of arguments matters.
The source code of the utility is located in /platform/frameworks/base/+/master/cmds/abx/.
To edit the files on a device there is no need to write our own parser and serializer, we can delegate all the work to the framework. But be careful, there are many flags and conditions for the system to decide to store the files in a different format, or use another version of ABX format. Remember, corrupting system settings files may end up in a bootloop, make sure you can recover your device before editing the settings.
Device ID changer uses this technique to edit the device ID values.