一个月前,Android闭源的消息铺天盖地。后来大家弄清楚了,Google只是将Android的开发全都切为内部分支,而代码依旧开源(虽然会滞后)。因此,如果我们现在登录AOSP的网页,将会看到这样的置顶信息:

今天我们就来试一试,在后AOSP时代,还能不能贡献代码?以及该如何贡献代码?
起源
(本章节涉及技术细节,如果不感兴趣可以跳过)
事情的起源是我们内部测试碰到一个非常诡异的问题:一些核心系统进程消失了,init认为它启动成功了,可是进程列表中根本找不到。通过分析dump和加日志的方式,我们找到了问题的根因:Android 15中引入了一个功能,在传统cgroup的树状结构中增加apps和system两个根节点,这样我们对系统进程和普通应用可以执行不同的策略。

因此传统的cgroup目录结构就发生了如下变化:
bash
/sys/fs/cgroup/uid_10118/pid_4980
↓↓↓
/sys/fs/cgroup/apps/uid_10228/pid_5621
/sys/fs/cgroup/system/uid_1000/pid_1348
虽然这个功能在Android 15中就已引入,但直到Android 16才默认开启。
正常情况下,当App死亡后,system_server会收到通知,然后它会负责清理该App对应的cgroup节点。但如果是系统重启(zygote死亡),system_server是来不及为众多App回收cgroup节点的。因此init在回收zygote时,会执行一个removeAllEmptyProcessGroups
的操作,将cgroup系统中那些空节点删除,相当于干了本该system_server干的活。
整个流程没有缺陷,但新引入的功能却忽略了removeAllEmptyProcessGroups
。removeAllEmptyProcessGroups
依然按照老的cgroup目录结构进行处理,结果就是那些空节点无法删除,会被fork出的新进程继承使用。一旦这些节点的freeze状态为true,就会使得新进程被冻结。这个影响无疑是灾难性的。
后续
弄清楚事情的原委后,便可以着手准备fix patch。
首先是下载源码,我们需要更改的文件是/system/core/libprocessgroup/processgroup.cpp,它属于的仓库是platform/system/core。这里简单介绍下Android源码的结构,它由上百个不同的Git仓库组成,譬如常见的platform/frameworks/base和platform/art。而最后组合这些Git仓库的地方就叫做manifest,它里面指定了每个仓库需要用到的版本。至于常用的工具repo,它底层调用的就是git命令,只不过它可以基于manifest帮我们便捷管理成百上千个Git仓库。
通过这个网页,我们可以获取platform/system/core这个仓库的下载地址,以及生成Change-Id的Hook:
bash
git clone https://android.googlesource.com/platform/system/core && (cd core && f=`git rev-parse --git-dir`/hooks/commit-msg ; mkdir -p $(dirname $f) ; curl -Lo $f https://gerrit-review.googlesource.com/tools/hooks/commit-msg ; chmod +x $f)
下载完代码后,默认的分支为main。这个分支已经停止更新,因此我们需要按照AOSP的要求切换到android-latest-release。但其实android-latest-release并不是一个仓库分支,而是mainfest的分支。我们可以根据它来找到具体仓库的版本。

根据上图的内容,我们可以知道当下每个具体Git仓库的版本为android15-qpr2-release。这里的qpr指的是"Quarterly Platform Release",qpr2表示Android 15的第二次季度更新。因此我们可以采用如下命令切换到android15-qpr2-release分支。
arduino
git checkout -b android15-qpr2-release origin/android15-qpr2-release
之后是代码改动,提交。Commit message里需要填入Google Issue Tracker的Bug ID,因此我们需要提前创建好Ticket,在里面描述清楚问题,以及和Google做必要的沟通。提交后的代码需要push到远程仓库,采用如下命令:
ruby
git push --no-thin origin HEAD:refs/for/android15-qpr2-release
提交完之后,系统会返回一个android-review的链接给你。拿着它,我们便可以看到具体的提交。

Google的工程师结合Google Issue上的描述和这里的提交便可以对代码进行review。通过review comment,我们可以不断更新patchset,让修复尽可能完美。
最后,Google的工程师会将我们的代码cherrypick进他们的内部版本,这样我们便可以在日后的开源版本中看见自己的改动。而android-review上的这笔改动会被abandoned,表明这笔改动已被处理。
那我们如何知道自己的改动是被接纳了还是拒绝了呢?其实大多数Google的工程师都会友好地在Google Issue上回复状态,就像下面这样。

感受
虽然后AOSP时代依然可以贡献代码,但感觉远没有以前"友好",具体原因如下:
- android-latest-release是已经成熟的Android源码,而Google内部版本可能已经领先大半年了。这样一来,我们基于老版本不管是提交bug fix还是feature,Google的内部版本可能都已经有了。因此提交很可能变成无用功。
- 由于版本差异过大,Google cherrypick时大概率需要解冲突,这既增加了他们的人力开支,也让他们更倾向于自己改动,而非cherrypick。
- Google内部版本合入时碰到的错误我们看不到,黑盒般的来回沟通会费力费神。
事物的发展大抵如此,曾经开放的走向封闭,曾经繁华的走向萧条。不过,我们也不必为这种变化感到伤怀,就像千年前刘禹锡曾告诉我们的那样:沉舟侧畔千帆过,病树前头万木春。