执行摘要
如果你在往嵌入式设备中植入Google的Android平台过程中,发现不能准确预测所需的时间和复杂度,那么我们将Android植入诺基亚N810的经验可能能帮到你。
Android概述
Android是由Google为移动手机创造的软件平台。它是由Linux Kernel、一些由C写成的软件库、用来运行代码的类似Java虚拟机以及全套的移动电话应用程序组成的。
Android不用付费,源代码也都是免费开放的。它的证书条款(参见结尾的Apache 证书链接)显示了商业友好。条款几乎在说:你喜欢用它做什么都可 以,但是出问题的时候别来找我们!但是Linux Kernel例外,其证书属于GNU公共证书。因此,制造商们在完成设备生产后必须开放该设备的Linux Kernel的源代码。
台湾HTC公司在2008年10月发行了第一款Android手机-G1。其他手机制造商们,包括摩托罗拉、LG、三星也相应的开始开发Android手机。另外,在中国也有很多新生和小型制造商利用Android作为一个吸引点,想占据智能手机市场的一席之地。
Android平台提供你想在一部手机上拥有的所有功能服务。包括接打电话、网络连接、短信以及多媒体。我们到目前为止对源代码的分析理解是这些功能服务都是完整的,并且建立在一个出色的架构之上,但是它并没有实现现代手机中的所有功能。毕竟,这只是1.0。
对于应用程序开发者来说,Android建立在广受欢迎的开源Eclipse综合开发环境之上。开发者可以利用Eclipse写Java应用程序,然后汇编到Java字节码,之后由Android的Dalvik虚拟机(VM)解释并执行。
Google声明他们的Dalvik虚拟机在速度和内存上与Java相比更有效,巧妙暗示 了Google出于性能的原因写了自己的VM。然而,在我们还没有看到任何支持数据之前,我们猜想Google创造Dalvik的真正原因是不想从Sun 那里取得授权或者购买许可。Sun是Java的创造者和管理者。
将Java翻译成Dalvik字节码,Google就可以利用这个成熟的工具和庞大的 Java开发群体。缺点是在Android手机上能够运行应用程序之前,开发者必须在编译的Java代码上运行Google翻译工具(然而Google将 这一步骤在Eclipse插件中自动化了)。
Google 还提供Android模拟器。该模拟器是一个虚拟化的ARM微处理器,它运行与设备相同的系统代码和几乎一模一样的Linux Kernel。Google提供一个Eclipse插件以便于Android程序在模拟器中运行时能在Eclipse中编译调试。Android模拟器是 我们见过的最完整的模拟器,只要保证开发人员的电脑有足够的内存来同时运行Eclipse和模拟器就可以了。
Google能从中得到什么呢?
Google给予了手机制造商们所有他们想要得-免费、开放的移动操作系统;同时,作为回 报,Google只要求设备上可以运行Android应用程序。我们推测Google期望最终可以通过消费者使用Android设备和Google服务赚 取比开发和维护Android更多。
诺基亚N810概述
在恩氏科诺,我们广泛的应用嵌入式Linux系统,因此我们认为如果能把Android植入到一个现有的Linux设备上应该会很有挑战性,同时也对我们目前的项目有所帮助。
巧的是我们正好有两部诺基亚N810 Internet Tablet。N810是掌上移动设备,配有高像素4.1英寸手触屏和滑盖标准英文键盘。N810具有400MHz Texas Instruments OMAP2420 CPU,128MB内存和WiFi。N810不具备的一个功能是接打电话,这也就是为什么诺基亚将N810市场定位为Internet Tablet,可以网上冲浪却不是电话。目前N810市场价是425美金。
一个活跃的软件开发群体热衷于鼓捣(Hacking) N810 基于Linux的软件。其中一些人已经将预发布的Android SDK移植到N810,不过我们尚未发现有人把Android 1.0植入到N810过。所以我们决定试一试,看看需要多长时间,能从过程中学到些什么。
开发步骤
前边提到过,Android系统软件在Linux Kernel上运行,Linux Kernel给应用程序提供服务,比如文件访问、进程调度和内部进程沟通。
Google为支持Android,对Linux Kernel进行了一系列的修改。同样,诺基亚修改了标准Linux Kenel以支持自己的硬件,比如键盘、手触屏和文件系统。
我们很快发现诺基亚N810的变更是针对早先Linux Kernel 2.6.21版本的。而且,与早期未发布的Android不同,Android 1.0的变更是针对Linux Kernel 2.6.25版本的。我们通过调查发现两个版本相隔1年之久,Linux群体对Kernel源代码进行了无数次的修 改。
因此,想让诺基亚和Google的修改版本一起工作,我们要么将N810的变化植入新2.6.25Linux Kernel,要么将Android的变化植入N810早期的2.6.21 Linux Kernel。
Google对Kernel做了哪些改动?
我们比较了一下Android Kernel和标准Linux Kernel的区别,发现Google变更了75个文件并增加了88个文件。我们准备了一个带注释的变更文件清单,附于文章末尾;在这里有一个简单总结。
Goldfish-44个文件
Android模拟器运行一个虚拟的CPU,Google叫这个CPU为Goldfish。Goldfish执行ARM926T的指令,并且有用于输入和输出的钩子,好比模拟器上的读取键和播放视频的输出键。
这些接口在定义Goldfish模拟器的文件中实现,并且不会被汇编到在真实设备上运行的Kernel中。所以我们可以放心的忽略这些文件。
YAFFS2-35个文件
与PC在磁盘上存储文件不同,手机在固态闪存芯片上存储文件。HTC G1用的是NAND闪存,一种因其高品质低价格而变得越来越流行的闪存。
YAFFS2是“Yet Another Flash File System, 2nd edition(另一个闪存系统文件,第二版)的缩写,为Linux Kernel和NAND闪存设备提供高性能接口。YAFFS2对Linux早就免费开放。然而,它不是标准2.6.25 Linux Kernel的一部分,所以Google将它添加到Android上。
蓝牙-10个文件
Google在蓝牙通讯协议栈中对10个文件进行了修改。修改的结果修正了与蓝牙耳机关联的6个明显错误,同时添加了蓝牙调试和访问控制功能。
调度-5个文件
Android Kernel同时包含了对CPU进程调度和时间维持算法的略微修改。我们还不知道关于这些修改的历史,但是其影响在粗略检查下并不明显。
新Android功能-28个文件
除了改错和其他一些小变动外,Android涵盖了一系列新的子系统值得一提,包括下列:
IPC Binder
IPC Binder是一个进程间通信机制。它允许程序通过一套高端API设置向其他程序提供服务,而不是通过标准Linux。网络搜索显示Binder概念始于Be公司,在Google为Android编写新Binder之前已经在Palm软件中实现了。
低内存杀手
Android增加了一个低内存杀手,每次使用这个功能,扫描正在运行的程序,然后杀掉一个。在我们的粗略测试中不能清楚的显示为什么Android在标准Linux Kernel已有的低内存杀手之上又增加了一个。
Ashmem
Ashmem是一个匿名共享内存(Anonymous SHared MEMory)系统,该系统增加了接口因此进程间可以共享具名内存块。举一个例子,系统可以利用Ashmem存储图标,当绘制用户界面的时候多个进程也可 以访问。Ashmem优于传统Linux共享内存表现在当共享内存块不再被用的时候,它为Kernel提供一种回收这些共享内存块的手段。如果一个程序尝 试访问Kernel释放的一个共享内存块,它将会收到一个错误提示,然后重新分配内存并重载数据。
内存控制台和日志装置
Android为调试错误增加了将Kernel记录信息储存到内存缓冲上的功能。另外,Android还增加了单独日志模块,这样用户进程就可以读写用户日志信息了。
Android调试桥
调试嵌入式设备可以说是个挑战。Google创造了Android调试桥(简称ADB)来简化调试,该调试通过一个USB连接运行Android的硬件设备和开发者用于编写应用程序的台式电脑之间。
Android同时增加了一个新的实时时钟、一个交换支持和一个计时的GPIO支持。我们在文章结尾列举了影响这些新模块的文件。
电源管理-5个文件
电源管理是一个好的移动设备中最难处理的问题之一,因此我们将其单独分成一组。有趣的是Google给Linux增加了一个新的电源管理系统,而不是利用现有的。我们在文章结尾列举了受影响的文件。
其他改变-36个文件
除了上边列举的例子之外,我们还发现一系列“各式各样”得改变。和其他的改变相比,这些改变包括了额外调试支持、键盘灯控制和TCP网络管理。
网络过滤器-0个文件
最后,我们的变更文件列表显示网络过滤器有22个被改变的文件。然而,测试显示只有文件名称得大小写不同(xt_DSCP.c对应xc_dscp.c),文件内容完全一致。因此在本文中我们将这些文件忽略不计。
植入N810的改变到新版本还是Android的改变到旧版本?
综上所述能看出Android对标准Linux Kernel进行了显著的修改。另外,Google的系统软件自动检测以保证Kernel的版本为2.6.25甚至更新。这使我们猜想Android是否需要标准2.6.25 Kernel的新特性,而不能使用较早版本。
从另一方面看,N810 Kernel的改变只是在硬件上加入了支持。诺基亚并没有改变核心操作系统或增加新的接口。
因此我们认为将N810的改变植入到2.6.25 Linux Kernel上会有更高的成功几率。
下边是操作步骤
第一步:将N810 Kernel的改变植入2.6.25 Linux Kernel
这一步花费了一个开发员7天时间完成。其中包括下列几步:
-从Montavista下载最新的Linux-OMAP-2.6 Kernel
-从Kernel下载中选择2.6.25版本(用“git重置-hard v2.6.25-omap1”)
-用下列N810 2.6.21 Kernel的补丁给2.6.25 Kernel打补丁
-N810多媒体卡(MMC)驱动
-N810 I2C串行总线控制器芯片驱动
-N810手触屏驱动
-N810键盘映射
-N810声卡和显卡DSP驱动
-N810 DMA驱动
-N810主板修改补丁
完成了几次一系列软件的编译/烧写/测试/调试之后,我们的新Kernel可以启动N810的默认软件和功能了。
不过我们有过一个问题,CX3110 WiFi芯片的Kernel驱动只有二进制的Kernel模块,而不是可以编译到新Kernel上的源代码。这意味着在N810上我们不能使新Kernel支持WiFi。
对于那些正在尝试做类似移植的人们这是个重要的提示:如果你有一个二进制驱动,你应该确保驱动已经针对你计划使用的Kernel基线编译的。
第二步:给2.6.25 N810 Linux Kernel增加Android Kernel补丁
随着新的N810 Linux Kernel的启动和运行,我们现在可以安装Android补丁了。这部分基本上很直观,一个开发员花费了2天半的时间安装补丁,Flashing Kernel并检测补丁可以正常工作。
如果你在用跟Android SDK不同的Kernel基线,你应该花点时间和资源保证它能正常工作。
幸运的是,Android和N810的补丁不涉及内核的相同部分。这就使合并两处修改容易了 许多。
第三步:在我们的新Kernel上启动Android
连续两周的补丁和测试后,我们最终为启动完整Android系统做好了准备。如果所有的部分一开始就能行,软件开发就不好玩了。有问题才有幸运伴随(开个玩笑)。
下边是我们做过的步骤:
-在mmap系统调用中N810 jffs2文件系统缺少一个Android需要的一个特征
-所以我们切换到ext2文件系统,但是系统报告数据损坏
-我们又切换到ext3文件系统,数据损坏没有了
-系统在启动时挂机,然后当我们要调试时,机器重启动了
-我们发现是看门狗计时器使得机器重启,于是关掉这个功能
-然后,有时系统能够成功启动,但是白屏
-给N810装了双缓存显卡补丁后能看到图像
-然后,有时能启动主屏幕,但是经常死机
-我们往N810上重新拷贝了一些系统库,不再死机了
-我们通过在系统软件中改变电源设置来激活手触屏
-我们在系统软件中修复按键映射来激活键盘
终于在19天后,我们成功了。Android的小机器人在开机后跳了出来,然后我们看到了主菜单和应用程序。
第四步:试玩
这个当然是回报。如果你能走到这么远,我们建议你给机器拍个照然后发给你最“技术”的那个朋友和同事显摆一下你的成就!
下一步
我们想创造自己的Android应用程序,然后在N810上运行。我们需要做的包括支持Android调试桥、修复系统偶然死机、然后看看我们能不 能在连接电源的情况下给电池充电。另外,我们的应用程序需要可以访问网络,因此要么使用WiFi芯片,要么使用USB Ethernet通过USB给PC发送网络数据,然后发送N810的数据到网络。
收尾
最终完成之前我们将在Android和N810社区发布我们做的修改,让大家一起利用一起享受。
在你的项目中应该注意的
如果你在往一个新设备中植入Android,我们建议你注意以下几点:
第一,我们所做得对任何即将发布的产品来说,只能是一个里程碑。在让N810能更好地满足我们开发程序之前,还有很多工作要做。N810不是一款电 话,因此我们没有花费任何时间在调试通话协议栈上,而且还有很多部分还不行-电源管理、WiFi,这些都需要更多时间解决,比这次移植的时间长的多。困难 的还在后边呢。
第二,N810是一款已经上市的商业产品,它经过了诺基亚严格的质量控制程序,很多黑客都青睐于它。我们以前也用过质量一般也没有制造商支持得设备 (那曾经是个巨大的错误,在确定合同后情况更糟糕了)。如果你在往你自己的设备中植入Android,要考虑清楚使用硬件的危险所导致的时间和资源因素。 我们强烈建议你先保证有软件工程师能够使硬件驱动发挥其作用。
第三,最开始时搞清楚你要用哪个版本的Kernel基线。这样你可以验证或者增加新任务以确保你那版本的Linux Kernel硬件有开发好的驱动。同时外部供应商经常提供二进制驱动,你也应该将这些驱动编译好备用。
第四,利用正确的工具和程序-最好与Linux和Android开发员使用的相同。我们曾经和一些将开源软件倒入闭源开发模块的大公司合作。虽然这 样在开始的时候很容易,但是过不了一段时间他们就发现把新版本的开源软件合并到他们的代码库中不是那么容易了。另外,如果开发员只想用windows,我 们建议你赶紧把他们赶出蚕茧快速进入到Linux世界。一个高速有效率的团队应该是在他们的电脑中运行Linux、git版本控制和vim或者emacs 编辑C代码。
感谢
首先,我想感谢唐永俊。他是将Android植入到N810的真正实施者。我不知道我能不能像他一样那么有耐心的完成这项工作。
我们俩同时要感谢PenguinBait, B-man, and QWERTY-12,是的,很酷的名字。是他们最初将预发布的Android SDK植入到N810中。他们的经验让我们少走了很多弯路。
最后,这是一个时时更新的文件,如果你发现什么错误,或者有什么好的经验想跟其他人分享而恰恰是我们没有的,请给我发个email,我会考虑修改进去。
下载
我们在下边的SourceForge.Net网页上放置了我们的Kernel补丁,一个编译好的Kernel映像,一个编译好的用户空间映像和一些笔记方便下载使用。
附件
注释索引-Android Kernel源的不同
资源