[Andoid][踩坑]CTS 11_r3开始出现的testBootClassPathAndSystemServerClasspath_nonDuplicateClasses FAIL问题分析

[Andoid][踩坑]CTS 11_r3开始出现的testBootClassPathAndSystemServerClasspath_nonDuplicateClasses FAIL问题分析

问题背景

从CTS测试结果可以清晰看到导致FAIL的类;这个类是我自己加的,为了在Installd与system_server之间注册回调使用;
由于项目进度紧张,最初报出该问题的项目采用了代码回退的方式,保证CTS测试结果正常;
在通过将近2天的分析中,逐步把该问题的原因以及解决方案理清,现总结如下:

问题发生原因

根据问题描述,使用CTS 11_r3测试时会FAIL,而使用11_r2测试的结果是PASS;
测试指令为:

run cts -m  CtsStrictJavaPackagesTestCases -t android.compat.sjp.cts.StrictJavaPackagesTest#testBootClassPathAndSystemServerClasspath_nonDuplicateClasses

根据查看代码,梳理出该测试项的逻辑与目的如下:

  1. 获取环境变量BOOTCLASSPATH与SYSTEMSERVERCLASSPATH的值;
  2. 将BOOTCLASSPATH与SYSTEMSERVERCLASSPATH的值对应的jar包拉取到主机端;
  3. 使用dexlib2对这些jar包进行反编译,获取其中dex文件;
  4. 基于dex文件获取其中包含的类;
  5. 将所有类进行查重;
  6. 重复的类再进行白名单筛选;
  7. 最终通过比对记录所有重复类的列表,如果不为空,则测试FAIL,反之则PASS;

从逻辑不难看出,这是Google想保障system_server与framework中不包含重复类的一个手段;

但是为什么11_r2可以PASS,到了11_r3就不行了呢?
根据对比基线代码与AOSP最新代码后可以发现;
Google在18-Nov-2020时对改测试项进行过一次更新,改进的地方主要有两点:

  1. 增加白名单中类的数量;
  2. 修改getDuplicateClasses方法的实现,以支持multi-dex的jar包;

由此可见,11_r2可以PASS完全是钻了该测试项的漏洞,因为我们添加的ICallback正好被打包到非第一个classes.dex中了;而在11_r3中对multi-dex进行了支持,这个问题就暴露出来了;

不过这里插一句,Google也知道自己的IInstalld也是FAIL的,因此自己更新了下白名单把IInstalld排除了,这操作也只能说是佩服…

问题解决思路

好了,不管Google这手操作如何,我们总归是要解决这个问题的。但是在进行了更进一步分析后发现,这个问题比想想中的麻烦。

下图是AOSP原始设计结构,可以看到:installd_aidl这个filegroup分别被services与framework两个模块导入、编译了;

原始架构

而我添加的用于注册回调的AIDL文件(暂且命名为ICallback.aidl,下同),需要的ICallback.aidl也是在这里面追加的:

filegroup {
    
    
    name: "installd_aidl",
    srcs: [
        "binder/android/os/IInstalld.aidl",
        "binder/android/os/storage/CrateMetadata.aidl",
        "binder/android/os/ICallback.aidl", //Added
    ],
    path: "binder",
}

从而导致ICallback及其内部类与IInstalld一样,被编译了两次,分别打入了services.jar与framework.jar中了;

到这里,解决思路应该是比较清晰了:

让ICallback只编译进framework.jar,而不编译进services.jar中;

解决步骤

  1. 首先我们需要将ICallback从installd_aidl这个filegroup中剥离,并进行单独控制;同时,我们需要定义规则将这ICallback导出,使全局可见:
filegroup {
    
    
    name: "installd_aidl_ext",
    srcs: [
        "binder/android/os/ICallback.aidl",
    ],
    path: "binder",
    visibility: ["//visibility:public"],
}

cc_defaults {
    
    
    name: "installd_aidl_ext_cc_defaults",
    aidl: {
    
    
        //Note, this path should vary according to current location
        include_dirs: ["xxx(当前目录)/binder/"],
        export_aidl_headers: true,
    },
    export_include_dirs : ["."],
    srcs: [
        ":installd_aidl_ext",
    ],
}

java_defaults {
    
    
    name: "installd_aidl_ext_java_defaults",
    aidl: {
    
    
        //Note, this path should vary according to current location
        include_dirs: ["xxx(当前目录)/binder/"],
    },
}

  1. 然后在installd的编译规则中照旧引入:
cc_defaults {
    
    
    name: "installd_defaults",
    defaults: ["installd_aidl_ext_cc_defaults"],//Added
    ...
        srcs: [
        ...
        "InstalldNativeService.cpp",
        ...
        ":installd_aidl",
    ],
    ...
}
  1. 而frameworks/base下的是最麻烦的,基于最小改动原则,我结合上面的结构图,决定在framework-non-updatable-sources的filegroup中(与原先的:installd_aidl放在一起):
filegroup {
    
    
    name: "framework-non-updatable-sources",
    srcs: [
        ...
        ":installd_aidl",
        ...
        ":installd_aidl_ext",//Added
    ],
}
  1. 同时需要使services在编译installd_aidl时有ICallback这个类可以引用,在services.core.unboosted中添加:
java_library_static {
    
    
    name: "services.core.unboosted",
    defaults: ["installd_aidl_ext_java_defaults"],//Added
    srcs: [
        ...
        ":installd_aidl",
        ...
    ],
    ...
}
  1. 编译后替换system.img重新测试CTS FAIL项,结果PASS;

修改后的结构示意图如下:(实际上只是单独加了一个installd_aidl_ext)

修改后结构

后记

上面只是最终的结果,中间探索的坎坷并未提及;
整个过程最难、也是我印象最深的地方,莫过于“怎么让aidl解析AIDL文件时可以找到ICallback,但不把它编译进services”
而我最终能确定上述的方案,很大程度上时依赖于这里提到的手段:通过定位到编译的参数,再倒过来查看Android.bp支持的选项,从而使用最小的改动,解决这一问题;

猜你喜欢

转载自blog.csdn.net/u014175785/article/details/115333461