Android S packages.xml乱码的原因

问题背景

客户项目遇到PackageManagerService扫描出现错误导致不停重启,然后进入recovery的问题。查看log是在开机读取Settings(packages.xml)中某个应用的签名信息时出错,系统构造了一个null的签名信息(数组)。导致之后比较签名数组长度时出现空指针。于是想把手机中的packages.xml做修改后模拟问题出现的原因。但是把这个文件pull出来之后发现里面是乱码,切换编码也没用。Android 11的模拟器没有这个问题,于是查看了这个文件的序列化过程,略作纪录。

Android S xml序列化变更

frameworks/base/services/core/java/com/android/server/pm/Settings.javawriteLPr方法中序列化过程为例,Android S中的序列化工具创建代码如下:

1
2
3
4
5
6
final FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
final TypedXmlSerializer serializer = Xml.resolveSerializer(fstr);
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);

serializer.startTag(null, "packages");

Android R中代码如下:

1
2
3
4
5
6
7
8
9
10
FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
BufferedOutputStream str = new BufferedOutputStream(fstr);

//XmlSerializer serializer = XmlUtils.serializerInstance();
XmlSerializer serializer = new FastXmlSerializer();
serializer.setOutput(str, StandardCharsets.UTF_8.name());
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);

serializer.startTag(null, "packages");

主要差别就在Xml Serializer的构建上,FastXmlSerializer序列化的xml文件可以正常查看,那TypedXmlSerializer有什么不同呢?先看下TypedXmlSerializer的注释:

1
2
3
4
5
6
7
8
9
/**
* Specialization of {@link XmlSerializer} which adds explicit methods to
* support consistent and efficient conversion of primitive data types.
*
* @hide
*/
public interface TypedXmlSerializer extends XmlSerializer {
//……
}

也就是所TypedXmlSerializer在序列化过程中会对原生数据类型进行转换。TypedXmlSerializer是个接口,具体构建的对象类型要看Xml.resolveSerializer的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* Creates a new {@link XmlSerializer} which is optimized for use inside the
* system, typically by supporting only a basic set of features.
* <p>
* This returned instance may be configured to write using an efficient
* binary format instead of a human-readable text format, depending on
* device feature flags.
* <p>
* To ensure that both formats are detected and transparently handled
* correctly, you must shift to using both {@link #resolveSerializer} and
* {@link #resolvePullParser}.
*
* @hide
*/
public static @NonNull TypedXmlSerializer resolveSerializer(@NonNull OutputStream out)
throws IOException {
final TypedXmlSerializer xml;
if (ENABLE_BINARY_DEFAULT) {
xml = newBinarySerializer();
} else {
xml = newFastSerializer();
}
xml.setOutput(out, StandardCharsets.UTF_8.name());
return xml;
}

从上述代码来看根据系统ENABLE_BINARY_DEFAULT的配置,可以选择构建BinarySerializer或是FastSerializerENABLE_BINARY_DEFAULT的定义如下:

1
2
public static final boolean ENABLE_BINARY_DEFAULT = SystemProperties
.getBoolean("persist.sys.binary_xml", true);

所以在调试的时候可以设置persist.sys.binary_xml的值为false然后重启来切换xml serializer并更新xml的内容。

BinarySerializer的注释如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 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 {@code Xml.newFastSerializer()} for a typical {@code packages.xml}.
* <p>
* 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 the {@link TypedXmlSerializer} interface.
* <p>
* Each serialized event is a single byte where the lower half is a normal
* {@link XmlPullParser} token and the upper half is an optional data type
* signal, such as {@link #TYPE_INT}.
* <p>
* This serializer has some specific limitations:
* <ul>
* <li>Only the UTF-8 encoding is supported.
* <li>Variable length values, such as {@code byte[]} or {@link String}, are
* limited to 65,535 bytes in length. Note that {@link String} values are stored
* as UTF-8 on the wire.
* <li>Namespaces, prefixes, properties, and options are unsupported.
* </ul>
*/

可见使用新的serializer的目的和Android资源编译的目的类似,一个是节省存储空间,一个是加快访问速度。

Wire protocol(传输协议)更多的是表达传输格式。因为代码层面的数据(链表、队列、二叉树)都是结构化的,但网络层看到的都是二进制流,所以把结构化的数据序列化为二进制流发送出去,并且对方也能以同样的格式反序列化出来,这就是wire protocol。跟把对象存储在文件里,重启后再从文件读出来有点类似。(https://www.zhihu.com/question/413112626)

Powered by Hexo and Hexo-theme-hiker

Copyright © 2018 - 2022 得一 All Rights Reserved.

访客数 : | 访问量 :