Android深度探索(卷2):系统应用源代码分析与ROM定制
上QQ阅读APP看书,第一时间看更新

第1章 学习前的准备工作

在这一章主要学习如何安装和配置Android的底层开发环境,并且将详细介绍如何编译官方和CM的Android源代码和Linux内核源代码,最后会尝试将编译生成的ROM刷到手机上。本书所使用的开发环境是Ubuntu Linux 12.04 LTS。建议读者使用root用户登录Linux,这样可以拥有最高的权限,从而避免在使用的过程中提升用户权限的麻烦。本章使用的Android源代码版本是4.2, Linux内核源代码版本是3.x。

1.1 安装和配置JDK

无论是编译Android源代码,还是编写Android系统应用,都需要在Java环境下完成,所以必须在Ubuntu Linux下安装JDK。对于开发Android应用来说,JDK5、JDK6和JDK7都没问题,不过编译Android源代码要求在JDK5或JDK6下进行,所以本书使用了JDK6作为Java的编译和运行环境。同时Google公司官方要求,Linux必须是Ubuntu Linux10.04 LTS或以上版本,其他的Linux发行版可能无法运行脚本中的某些命令,所以本节会在Ubuntu Linux12.04 LTS下安装JDK6。

如果读者要使用JDK6的最新版本,可以到如下的地址下载。

        http://www.oracle.com/technetwork/java/javase/downloads/index.html

为了方便读者,在随书光盘中已经提供了JDK的安装包(jdk-6u33-linux-i586.bin),读者可以直接在Linux终端中执行该安装文件,并按提示操作即可。最后需要将JDK安装路径的bin目录加到PATH环境变量中,通常在/root/.profile文件中修改PATH环境变量。假设JDK的安装路径为/root/jdk6,那么可以按着如下形式设置PATH环境变量。

        export PATH=$PATH:/root/jdk6/bin

1.2 安装和配置Android开发环境

Android开发环境主要包括Eclipse、Android SDK、ADT和Android NDK。其中Eclipse将作为开发Android应用的IDE; Android SDK为应用开发包;ADT为Eclipse的插件,用于开发Android应用;Android NDK用于开发C/C++程序,实际上就是标准的Linux动态链接库(.so文件),这些文件可以嵌入到Android应用程序中(APK文件),并通过JNI技术调用。

读者可以到如下的地址下载Eclipse的最新版。也可以使用随书光盘中带的Eclipse(eclipse.7z),这个Eclipse压缩文件包含了最新的Eclipse4(juno),以及ADT,所以直接解压即可使用,并不需要再次安装ADT。

        http://www.eclipse.org/downloads

如果不想单独安装Eclipse、ADT和Android SDK,也可以到如下地址下载Android开发环境的集成版。

        http://developer.android.com/sdk/index.html

该集成环境包括如下几部分。

□ Eclipse IDE。

□ ADT插件。

□ Android SDK工具集。

□ Android平台工具集。

□ 最新的Android SDK。

□ 最新的用于Android模拟器的Android系统镜像(system.img)。

如果读者已经有了Eclipse,可以使用下面的Url在线安装ADT。

        https://dl-ssl.google.com/android/eclipse

或者到下面的地址下载ADT的离线安装包。

        http://developer.android.com/sdk/installing/installing-adt.html

最后一个需要安装的是Android NDK,读者可以到如下地址下载Android NDK的最新版。

        http://developer.android.com/tools/sdk/ndk/index.html

1.3 官方Android源代码

本书使用了Android的最新版本4.2.2,可能在读者阅读本书时Google公司又发布了更新的Android版本,到时读者只要按着本节的方法下载最新版本并编译即可。

1.3.1 下载Android源代码

下载Android源代码之前需要先配置Linux环境,其中环境配置主要就是安装JDK,以及对于不同Linux版本,可能需要为某些.so文件建立符号链接,当然,还有一些其他的配置,不过这些设置大多数时候都不需要,详细的配置方式Google公司官方已经给出,页面地址如下,本节不再详细介绍。

        https://source.android.com/source/initializing.html

配置完环境后就可以直接通过git下载Android源代码,不过为了方便,Google公司提供了一个Python脚本repo,如果网络不中断,repo可以用无人值守的方式安装,即使下载的过程中网络中断了,再次执行repo脚本,仍然会从中断处开始下载。读者可以使用下面的命令将repo脚本文件下载到~/bin目录中,并将repo文件的权限改成可执行。同时将~/bin目录加到PATH环境变量中。

        # mkdir ~/bin
        # PATH=~/bin:$PATH
        # curl http://commondatastorage.googleapis.com/git-repo-downloads/repo > ~/bin/repo

注意

前面的4行命令开始的“#”并不属于命令本身,只是由于本书所有的命令都是在Linux终端中执行的,而且Linux使用root用户登录的,所以前面的终端提示符是“#”,为了表示命令是在终端下执行,本书所有给出的命令前面都会加“#”,但要注意,当前执行命令的路径不一定在根目录或/root目录,所以“#”只表示需要在Linux终端下执行该命令,并不表示当前执行命令的路径。执行路径会在命令前面的描述中提到。如果未提到,表示可以在任何路径下执行这些命令。

下完repo命令后,可以任意建立一个目录,并进入该目录,最后执行下面的命令设置要下载的Android的最新版本。

        # repo init -u https://android.googlesource.com/platform/manifest

如果要指定下载Android的某个版本,需要在下载链接中专门指定,例如下载Android 4.2.2可以使用下面的命令。

        # repo init -u https://android.googlesource.com/platform/manifest -b android-4.2.2_r1

最后需要执行下面的命令下载指定的Android版本。

        # repo sync

下载Android源代码的过程比较缓慢,如果下载的过程中中断,可再次执行repo sync命令即可。

1.3.2 编译Android源代码

下载完Android源代码后,就可以对源代码进行编译了,但在编译之前还需要在Android源代码根目录中执行如下的命令设置一些Shell函数。

        # source build/envsetup.sh

如果执行成功,根据Android版本的不同,会输出类似如图1-1所示的信息。

图1-1 编译环境安装

在编译Android源代码之前还需要做最后一件事,就是设置编译的目标,也就是为哪些设备编译Android源代码。例如,如果要让编译后的目标文件在Android模拟器或Android设备上运行,可以执行下面的命令。

        # lunch full-eng

如果读者有其他的需要,还可以指定其他的目标,直接执行lunch命令,会显示当前Android支持的所有目标,如图1-2所示。直接输入前面的序号即可选择相应的目标,例如,full_x86-eng适合于在X86架构的计算机上运行。

图1-2 选择目标

最后可以在Android源代码根目录直接执行make命令编译整个Android源代码,接下来又是一段难熬的等待。通常从下载Android源代码到Android源代码编译完成,通常需要一天的时间,而且还是保证中间不断网的情况下。如果读者的机器是多核CPU,可以指定编译时利用的CPU核数。例如,执行下面的代码会利用4个CPU核。

        # make  -j4

但要注意,如果CPU只有4个核,使用上面的命令时可能会造成机器运行缓慢,无法再处理其他任务的情况。所以读者应根据具体的情况选择参与编译的CPU核数。

编译完Android源代码后,会在<Android源代码根目录>生成一个out目录,所以编译生成的目标文件都在该目录的相应子目录中。其中最重要的有3个镜像文件(ramdisk.img、system.img和userdata.img),这3个镜像文件都在如下的目录中。在第2章会详细介绍如何手动生成和解开这3个镜像文件,以及它们的用途。

        <Android源代码本目录>/out/target/product/generic

Google公司已经在官方网站上发布了最新的编译Android源代码的方式,读者可以从如下页面获得更多编译Android源代码的细节。

        https://source.android.com/source/building.html

1.4 官方Linux内核源代码

本节将介绍如何为指定的Android设备下载Linux内核源代码,并从Android设备中获取Linux内核的配置文件,并利用这些配置文件编译Linux内核源代码。

1.4.1 Linux内核支持的Android设备

尽管定制Android ROM并不需要大量修改Linux内核源代码,不过在很多时候,还需要依靠Linux内核来完成更底层的工作。例如,获取ROOT权限就需要利用Linux内核镜像来修改default.prop文件的内容。所以本节会详细描述如何下载和编译Linux内核,在下一章会介绍如何将生成的Linux内核镜像刷到Android设备上,并且对现成的Linux内核镜像反编译,修改其中的内容,然后再重新生成Linux内核镜像,最后再刷回Android设备。

由于Android设备的硬件不同,Linux内核的源代码及其配置文件也有一定的差异,而且有的差异非常大,所以下载Linux内核源代码一定要清楚是为哪些Android设备下载这些源代码。本书主要以Google Nexus系列设备为主。因为这些设备的Linux内核源代码已经在Google公司官方网站发布了,任何人都可以自由下载、编译和分发。当然,Google发布的这些Linux内核源代码除了支持Nexus系列设备外,还支持一些老的Android设备以及Android模拟器。目前官方发布的Linux内核源代码支持的所有设备如下:

□ Android模拟器,也就是金鱼(goldfish)内核。

□ ADP1:也就是Google公司发布的第一款Android手机G1,代号为Dream。

□ ADP2:也就是G2,代号为Sapphire。

□ Nexus One。

□ Nexus S。

□ Nexus 4。

□ 熊猫板(PandaBoard),一种低功耗的开发板。

□ Galaxy Nexus。

□ Nexus 7。

□ Nexus 10。

□ Xoom:摩托罗拉推出的第一款Android平板电脑。

上述的一些Android设备(如Nexus 4、Nexus 7和Nexus 10)都是Google公司最近两年新出的Android设备,所以Linux内核都比较新(都在Linux3.1以上)。本书主要将以这些设备中的Nexus 7为主,因为这些设备中只有Nexus 7最廉价,而且里面的配置正好是我们需要的。其他的Nexus设备的操作方法与Nexus 7类似。

1.4.2 下载Linux内核源代码

本节会详细介绍如何下载Google公司官方发布的Linux内核源代码。Google公司目前提供了7套Linux内核源代码,那么到底应该下载哪个呢?

下载Linux内核源代码要使用git命令,下面是下载这7套Linux内核源代码的命令。

        # git clone https://android.googlesource.com/kernel/common.git
        # git clone https://android.googlesource.com/kernel/exynos.git
        # git clone https://android.googlesource.com/kernel/goldfish.git
        # git clone https://android.googlesource.com/kernel/msm.git
        # git clone https://android.googlesource.com/kernel/omap.git
        # git clone https://android.googlesource.com/kernel/samsung.git
        # git clone https://android.googlesource.com/kernel/tegra.git

我们可以用这7套Linux内核源代码的git文件名来表示它们,也就是common、exynos、goldfish、msm、omap、samsung和tegra。这7套Linux内核源代码中的后6套可以用于上一节给出的一系列的Android设备中,具体的对应关系如下。

□ common:通用的Linux内核,后面6套Linux内核源代码都以该源代码为基础。

□ exynos:用于使用三星的Exynos芯片的Android设备,典型的代表是Nexus 10。

□ goldfish:用于Android模拟器的内核源代码。

□ msm:用于使用高通MSM芯片的Android设备,典型的代表是ADP1(G1)、ADP2(G2)、Nexus One和Nexus 4。

□ omap:用于使用德州仪器(TI)OMAP芯片的Android设备,典型的代表是PandaBoard和Galaxy Nexus。

□ samsung:用于使用三星Hummingbird芯片的Android设备,典型的代表是Nexus S。

□ tegra:用于使用恩威迪亚(NVIDIA)Tegra芯片的Android设备,典型的代表是Xoom和Nexus 7。

如果读者恰好使用了上述的设备,就可以下载相应的Linux内核源代码了。如果上述设备列表中没有读者使用的设备,就只能到相应厂商的官方网站最好到官方网站下载Linux内核源代码,因为第三方网站发布的Linux内核源代码有可能向其中加入不和谐的东西,有可能会发生意想不到的事情。下载Linux内核源代码。例如,使用HTC手机的读者可以从下面的地址下载用于指定型号设备的Linux内核源代码。

        http://www.htcdev.com/devcenter/downloads

从理论上讲,由于Linux内核基于GPL协议,所以任何使用Linux内核的设备都应该开发Linux内核源代码。通常有一定规模的手机厂商会遵守这个协议。不过有很多中小型手机厂商因为要保护知识产权,并未公布自己手机使用的Linux内核源代码,所以使用这些Android设备的读者将无法通过编译Linux内核源代码的方式刷机、获取ROOT权限以及完成其他与Linux内核相关的工作。

由于本书主要使用Nexus 7来讲解与Linux内核相关的技术(如获取ROOT权限),所以会使用如下的命令下载Linux内核。

        #git clone https://android.googlesource.com/kernel/tegra.git

注意

尽管前面介绍的几套Linux内核源代码都是基于相应芯片的,但这并不意味着使用这些芯片的Android设备就可以直接使用这些Linux内核源代码。使用这些芯片只是可使用相应Linux内核的必要条件,并不是充分条件。还需要对Linux内核进一步地调整。但可以在相应Linux内核源代码的基础上进行。例如,如果某一部Android设备基于Tegra芯片,就可以在相应的Linux内核源代码的基础上进行调整。

1.4.3 查看Linux内核的分支

可能对git命令不熟悉的读者会感到惊讶,为什么下载完Linux内核源代码后,在下载目录什么都没有呢,其实所有的Linux内核源代码已经下载到本地了,只是都在版本库中。而git的版本库都在下载目录的“.git”子目录中。由于“.git”是隐藏目录,所以直接使用ls命令是看不到“.git”目录的,使用ls -al命令可以查看当前目录中的所有子目录和文件。

每一套Linux内核源代码都有多个分支,所谓分支就是在对同一套Linux源代码进行微调后产生的不同版本。如果不使用git进行管理,就需要备份完整的Linux内核源代码,而有了git,只需要存储修改过的部分即可。

现在进入Linux内核源代码的下载目录,然后执行下面的命令查看所有的分支。

        # git branch -a

如果成功执行上面的命令,会输出如图1-3所示的内容。

图1-3 当前Linux内核源代码的所有分支

可能有很多读者一看到这些分支有些发蒙,从内核版本来看,只有2.6和3.1。从2.6前面的moto字样可以判断,该Linux内核用于Xoom。而后面6个分支都可以用于Nexus 7。不过这6个分支的版本都一样,只是分支名结尾处不一样。到底使用哪个Linux内核呢?一开始笔者也有些迷惑,通过在网上搜索,发现很多国人和老外也在问同样的问题。最后发现有个人回答了句“看看Google公司的人在干什么就知道选哪个了”。那么笔者怎么知道Google公司内部在干什么?进入Google公司总部?哦,应该没这么麻烦。在下一节将告诉读者该如何做。

1.4.4 看看Google公司的人在做什么

在1.4.3小节留了一个问题,应该使用哪个分支呢?这一点当然Google公司会告诉我们。从理论上说,既然Google公司公开了Linux内核源代码,就应该派人去维护,也就是说应该可以查到维护的日志。

Google公司已经将Linux内核和Android源代码的各个分支和维护情况都放到了如下的网站。

        https://android.googlesource.com

打开该页面,会看到一个很长的列表。现在需要找到用于Nexus 7的Linux内核维护日志。在列表右侧的描述(Description)中很容易找到“Files specific to Nexus 7”的字样,左侧对应的是“device/asus/grouper”,如图1-4所示。

图1-4 Google Android源代码维护日志总目录

读者要记住一点,与Nexus 7相关的代码名是grouper通常为每一个设备单独开发和维护的Android源代码和Linux内核源代码都会有一个开发代号,例如,Nexus 7叫grouper。在后面的章节会看到更多设备的开发代号。在这里先留个悬念,看看读者能从书中找到多少种设备的开发代号。,所以不管是Linux内核,还是Android源代码,或是其他的东西,凡是带有grouper字样的,都与Nexus 7相关。现在单击进入“device/asus/grouper”链接,会看到有一个长长的日志列表。现在仔细看一下这个日志列表,找一下规律和出现最多,并与Linux内核分支有关的词汇。从理论上说,与当前正在使用和维护的Linux内核分支的字样会频繁出现在维护日志中,可能眼尖的读者会发现至少出现了5次“jb-mr1.1”可能在读者看到本书时日志内容会改变,不过查找的方法类似。,如图1-5黑框中所示。而对照上一小节中如图1-3所示的分支列表,正好倒数第二个分支remotes/origin/android-tegra3-grouper-3.1-jb-mr1.1包含“jb-mr1.1”。而其他的分支名称不是多一些内容,就是少一些内容,所以可以基本断定,Google公司目前经常维护该分支,那么很有可能该分支就是Nexus 7最新使用的Linux内核现在只是基本断定,只有编译完后刷机成功才能100%肯定。关于刷Linux内核的方法将在下一章详细介绍。

图1-5 官方维护Linux内核源代码的日志

下面开始切换到该分支。

如果是第一次切换到该分支,使用如下的命令。

        # git checkout -b android-tegra3-grouper-3.1-jb-mr1.1
                      remotes/origin/android-tegra3-grouper-3.1-jb-mr1.1

以后再切换到该分支,可以使用下面的命令。

        # git checkout android-tegra3-grouper-3.1-jb-mr1.1

成功切换分支后,再次执行“git branch -a”命令,会看到输出如图1-6所示的信息。其中星号标注的就是当前使用的Linux内核源代码分支。

图1-6 当前使用的Linux内核源代码分支

1.4.5 获取Linux内核的配置文件

Linux内核被设计成可插拔式的,也就是说Linux内核的所有组件(包括驱动、内存管理、I/O管理等)都可以很容易移除和添加,而完成组装和下载任务的就是一个叫“.config”的配置文件。该配置文件在Linux内核源代码的根目录。尽管tegra可以用于Nexus 7,但直接编译该tegra的源代码还是会有一些错误的,原因就是“.config”文件中的某些配置有问题。当然,我们可以通过自己修改“.config”文件来解决问题,但比较麻烦,所以可以找一部已经刷了最新版本(Linux内核版本需要是3.1.10)的Nexus 7,进入Nexus 7的Shell,在“/proc”目录下寻找一个叫config.gz的文件。该文件就是已经配置好的“.config”文件的压缩版本,将该文件从Nexus 7上下载到本地,并将config.gz解压,会得到config文件,最后用config文件覆盖“.config”文件即可如果要保留Linux内核源代码旧的配置,可以在覆盖“.config”文件之前先备份该文件。

覆盖“.config”文件后,执行make命令编译Tegra Linux内核时就不会有任何错误了,编译完后会在<Linux内核源代码根目录>/arch/arm/boot目录中生成一个zImage文件,该文件就是Linux内核源代码编译后生成的二进制版本,但需要将zImage文件制作成内核镜像文件(boot.img)才能刷到Nexus 7上。这些内容会在下一章详细介绍。

可能有的读者并未在/proc目录中找到config.gz文件。实际上,config.gz文件是否存在与Linux内核的配置有关。如果要想在zImage文件中包含config.gz文件,必须在“.config”文件中将CONFIG_IKCONFIG_PROC设为“y”,否则zImage是不会在/proc目录生成config.gz文件的。当然,也可以执行make menuconfig命令查看Linux内核的配置菜单,进入“General setup”菜单项,找到“Enable access to .config through /proc/config.gz”菜单项,按空格键,将该菜单项设为“[*]”,最后退出界面并保存配置即可,配置界面如图1-7所示。

图1-7 允许生成/proc/config.gz文件

除了可以通过直接覆盖“.config”文件和使用make menuconfig重新保存配置文件的方式外,还可以将config文件放到如下的目录,并将该文件改名为new_defconfig。然后执行make new_defconfig命令用new_defconfig文件覆盖“.config”文件的内容,然后再执行make命令编译Linux内核源代码即可。

        <Linux内核源代码根目录>/arch/arm/configs

注意

文件名new_defconfig分为两部分,前一部分是“new”,这部分名称可以随便起,而后一部分“_defconfig”是固定的,也就是说所有用于Linux内核配置的文件名都必须以“_defconfig”结尾。

本节涉及的config.gz和config文件已经包含在随书光盘中,读者可以直接使用这两个文件,而且带的Tegra Linux内核源代码(tegra.gz)也已经使用config文件替换了“.config”文件,所以可以直接编译Tegra Linux内核的源代码。

1.4.6 安装交叉编译器

由于我们需要将编译的Linux内核放到Nexus 7或其他Android设备上运行,并且这些设备都是ARM架构的CPU,所以就不能使用Linux下的gcc对Linux内核源代码进行编译。因此要使用一种称为“交叉编译器”的工具。这里所说的交叉编译器就是可以在X86架构的PC上编译生成可以在ARM架构的Android设备ARM架构的不光是Android设备,其他设备,例如,微软的Surface,也是ARM架构的。只是本身的主题是Android,所以这里特指Android设备。上运行的二进制文件。这里的二进制文件主要指Linux内核的zImage文件。

其实Android源代码本身已经带了一套交叉编译器(在prebuilt目录中),虽然Android带的交叉编译器可以完美地编译Linux内核,但由于Linux内核源代码对编译环境的要求非常苛刻,编译器的版本、配置文件(.config)等条件只要有一点差异,都可能无法成功在Nexus 7上运行,所以需要了解当前Linux内核版本需要使用哪个版本的交叉编译器进行编译。

Google公司官方编译用于Nexus 7的最新Linux内核源代码使用的交叉编译器版本是4.4.3,所以需要使用下面的命令下载该版本的交叉编译器。

        # git clone https://android.googlesource.com/platform/prebuilt

为了使用方便,读者可以将交叉编译器的bin子目录加到PATH环境变量中,也可以在编译Linux内核源代码的终端中执行下面的命令,临时将bin目录加到PATH环境变量中。这里假设交叉编译器正好在当前目录的prebuilt子目录中。

        # export PATH=$(pwd)/prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin:$PATH

1.4.7 编译Linux内核源代码

前面几个小节做的都是编译Linux内核源代码的准备工作。本小节将正式编译Linux内核源代码。首先在Linux终端进入Tegra或其他Linux内核源代码的根目录,然后执行下面的命令设置必要的环境变量。

        # export PATH=$(pwd)/prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin:$PATH
        # export ARCH=arm
        # export CROSS_COMPILE=arm-eabi-

其中第1行命令用于设置交叉编译器编译命令的路径;第2条命令告诉Linux内核要为ARM架构的设备生成Linux内核(zImage文件);第3条命令设置交叉编译器的前缀,在编译Linux内核源代码的过程中可能会调用一系列编译命令,这些编译命令文件名的前缀都是“arm-eabi-”。读者可以进入交叉编译器的$(pwd)/prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin看一下,该目录中的文件名几乎都是以“arm-eabi-”作为前缀的。

最后执行make命令即可完美编译Linux内核源代码。完整编译的时间大概20分钟左右。编译完后,就会在<Linux内核源代码根目录>/arch/arm/boot目录中生成一个zImage文件。这就是在Nexus 7上运行的Linux内核的二进制版本。

1.5 Cyanogenmod(CM)源代码

尽管使用官方的Android源代码和Linux内核源代码可以紧跟Google的步伐,第一时间体验Android的最新版本,不过官方的这些源代码只适合Nexus系列的设备。而其他很多流行的Android设备都无法直接使用官方的源代码制作ROM。由于很多读者使用的都是非Nexus系列的Android设备,所以本书为了满足这些读者的需求,大多数内容将以另外一个经过第三方组织CM修改的Android源代码和Linux内核源代码为主,尽管CM发布修改版源代码的时间可能比官方发布相应源代码的时间晚一些(一般也晚不了多长时间),但使用CM源代码制作的ROM可以运行在更多的Android设备上,例如,HTC、三星等国内主流的机型都在CM的支持范围内。如果读者对CM还不太了解,强烈建议仔细阅读本节的内容。因为通过本节的学习,就足以使读者通过CM提供的源代码制作一个可以完美刷机的ROM。当然,在后面的章节还会更深入地研究ROM的制作过程。

1.5.1 什么是CM

经常刷机的读者肯定对CM ROM不陌生。CM是Cyanogenmod的缩写。CM是目前最受关注的Android ROM适配团队甚至有很多人认为,如果CM不支持某一款Android设备,有可能会影响该款Android设备的销量。不过大家也别当真,这也有些夸大的成分。但至少可以看出,CM团队在Android领域的影响不可小觑!。目前世界上绝大多数第三方Android ROM团队出品的ROM都是以CM ROM为基础制作的。例如,小米ROM、还有老罗的锤子ROM都是在CM ROM的基础上修改而来的。

大家都知道,Android系统本质上由Android和Linux内核两部分组成。而决定Android系统是否能在Android设备上成功运行的主要因素就是Linux内核中的驱动Android设备和PC一样,不同厂商的Android设备使用的硬件不同,所以要求不同的Linux驱动。。而Google公司官方提供的Linux内核源代码只支持数量有限的几种Nexus系统的Android设备,所以CM团队的主要工作就是在Google公司官方提供的Linux内核源代码有的CM修改版的Linux内核源代码可能不是来自Google公司官方,而是其他厂商发布的Linux内核源代码,不过这已经不重要了,我们直接享用CM的成果就好了!的基础上,添加适合各种Android设备的驱动,而CM对Android源代码的改动不大,UI仍然保持着原生Android的风格。只是修改了其中一些系统应用程序,并添加了一些自己的应用程序,例如,手电筒、从CM升级ROM、Root等功能。所以CM团队的主要成果就是为不同的Android设备适配Linux内核中的驱动。

CM的这些工作也为其他的Android ROM团队提供了方便之门。因为有了CM适配的Linux内核,定制新的ROM,主要的工作就是修改CM提供的Android源代码。尽管每一款Android设备都有一套自己独立的Linux内核源代码,但Android源代码的大多数部分却是共享的,也就是说,对于某个版本的CM Android源代码(如CM 10.1),只需要下载一套,并且和相应Android设备的Linux内核源代码搭配即可制作成在不同Android设备上运行的ROM。而我们要做的只是维护一套Android源代码,多套Linux内核源代码(这些源代码大多数由CM团队负责维护)。这样将大大减轻制作Android ROM的复杂度和适配各种Android设备的烦恼。

CM的官方地址是http://www.cyanogenmod.org。读者可以从CM官网的相应页面下载适合不同Android设备的Android源代码和Linux内核源代码。Android源代码和Linux内核源代码都在github(http://www.github.com)上托管,本章后面的部分会详细介绍如何下载、编译CM源代码,并且从CM源代码制作我们的第一个完美的ROM。如果读者要想在真机上刷这个ROM,只需要有一个CM支持的Android设备即可,要想了解CM支持哪些Android设备,请看下一小节的内容。

1.5.2 CM支持哪些Android设备

在下载和使用CM源代码之前了解CM支持哪些设备非常必要。因为如果CM如果不支持您的Android设备,下载的CM源代码生成的ROM即使可以成功刷机,也很可能启动不了或遇到其他莫名其妙的现象。不过读者也不用担心,CM支持的Android设备种类是非常多的。国外很多常见的品牌都在支持之列,例如,三星、摩托罗拉、索爱、HTC的大多数型号都支持。甚至华为等国内厂商的部分型号的Android设备也在支持之列。当然,Nexus系列就更不在话下了。

要想知道CM是否支持自己的Android设备,可以进入CM的主页面,然后单击右上角的“Devices”链接(如果因网页改版没有该链接,可以直接访问http://wiki.cyanogenmod. org/w/Devices),进入设备列表页面。在页面的中上部找到如图1-8左上角所示的“Filter”过滤框。先单击右侧的“show all devices”链接,然后在“Filter”过滤框输入要查找的关键字,例如要查找支持哪些Nexus设备,可以输入“nexus”,这时在下方就会显示如图1-8所示的相关Android设备列表。

图1-8 查找CM支持的Android设备

如果在图1-8所示的设备列表中找到自己的Android设备,可以直接单击该设备,进入下一个页面。在该页面中会告诉你如何下载Android源代码和Linux内核源代码,以及其他相关的资源。

1.5.3 与Android设备对应的Codename和CM版本

在下载和编译CM Android源代码之前需要知道如下两个信息。

□ 当前Android设备对应的Android源代码的代码名称(Codename),也就是开发代号。

□ 支持当前Android设备的CM源代码版本。

代码名称在编译Android源代码(选择target)和下载对应的Linux内核源代码的过程中非常重要。例如,Nexus 7 Wi-Fi版对应的代码名称是grouper, Nexus 4对应的代码名称是mako。也就是说,如果读者看到grouper的字样,就说明对应的Android源代码和Linux内核源代码是为Nexus 7 Wi-Fi版准备的,而看到mako的字样,就是为Nexus 4准备的。那么对于不熟悉的设备,怎么知道与其对应的代码名称呢?

图1-9 Nexus 4的规范列表

如果读者按着上一小节的方法找到自己的Android设备,并进入与该设备相关页面。可以在页面的右侧看到一个设备规范列表,例如,进入的是Nexus 4设备的页面,会看到“Nexus 4 Specifications”列表,在设备图像下面有若干与该设备相关的参数,如图1-9所示。通常第一个参数就是代码名称(Codename),最后一个或倒数第二个参数(CM Support)就是CM的版本(由于参数列表太长,图1-9未显示全,具体的参数值请查看实际的页面)。例如,适合Nexus 4的只有CM 10.1,对应Android 4.2和Linux 3.1.10同一个CM版本,对应的Linux内核版本不一定相同。例如,对于CM 10.1来说,Nexus 7 Wi-Fi版的Linux内核版本是3.1.10,而Nexus S的Linux内核版本是3.0.50。

1.5.4 下载CM Android源代码

下载CM Android源代码与下载官方Android源代码的方法及其类似,同样也是用repo脚本(要保证当前Ubuntu Linux已经安装了git)下载,只是下载的地址不同。下面看一下详细的下载步骤。

第1步:下载repo脚本文件

        # mkdir -p ~/bin
        # curl http://commondatastorage.googleapis.com/git-repo-downloads/repo > ~/bin/repo

第2步:设置repo脚本文件的路径

        export PATH=${PATH}:~/bin

为了方便,可以将上述代码添加到~/.profile文件中,这样就不需要每次使用新的Linux终端,或重启机器后都要重新设置repo脚本文件的路径了。

第3步:初始化下载地址和版本

        # mkdir -p /sources/cyanogenmod
        # cd /sources/cyanogenmod
        # repo init -u git://github.com/CyanogenMod/android.git -b cm-10.1

第4步:开始下载

        # repo sync

从这4个下载步骤可以看出,除了第3步与下载官方Android源代码不同外,其他的步骤完全一样(只是将CM源代码下载到了“/sources/cyanogenmod”目录)。

在第3步的一个关键是了解需要下载哪一个版本的CM Android源代码。为了了解这些信息,首先需要确定读者手中Android设备的型号。例如,如果是Nexus S,需要按着上一节的方法进入Nexus S设备页面,然后在右下方找到“CM Support”参数项。从该参数的值可以看出,支持Nexus S的CM版本是7、9、10、10.1,共4个版本。当然,下载哪个版本都可以,不过要想使用最新的Android版本,就需要下载10.1,该版本对应的Android版本是4.2。如果读者手中的Android设备是Nexus 4,那么只有CM 10.1支持该设备,所以只能下载CM 10.1。一旦确定了要下载哪个CM版本,就需要在执行repo init命令之前指定要下载的具体分支,例如,本例要下载CM 10.1,所以需要使用下面的命令执行分支。

        # repo init -u git://github.com/CyanogenMod/android.git -b cm-10.1

不过要注意的是,在CM 10.1之前的版本,指定分支通常使用有意义的名称,例如,比较古老的HTC Hero(G3)只有CM7支持,所以需要使用下面的命令初始化要下载的分支。

        # repo init -u git://github.com/CyanogenMod/android.git -b gingerbread

要想知道与某一Android设备对应的CM版本分支,首先按着1.5.2小节的方法找到该设备,并进入该设备的页面。如果在右侧的Specifications部分的最下方找到“Latest CM version”字段,则“-b”后面直接根该字段值即可。如果没有“Latest CM version”字段,那就是CM 10.1或更新的版本,所以“-b”后面直接跟cm-10.1即可。

如果读者要想了解更详细的下载过程,可以查看下面的页面(这里Codename需要替换成相应的字段值)。

        http://wiki.cyanogenmod.org/w/Build_for_Codename

例如,要想进入HTC Hero的下载描述页面,首先要知道HTC Hero的Codename是hero,然后进入如下的页面。

        http://wiki.cyanogenmod.org/w/Build_for_hero

注意

下载CM Android尽管需要考虑具体的Android设备,但这只是为了确定要下载哪个CM版本,而每一个CM版本可以用在一系列的Android设备中。所以CM Android源代码与CM Linux内核源代码的主要版本差异就是前者共享于多款Android设备,而后者对于每一款Android设备都是独立的。例如,Nexus 4和Nexus 7 Wi-Fi都可以使用CM 10.1的Android源代码,但这两款设备的Linux内核源代码是各自独立的,不可以互换。所以为多款Android设备制作ROM, Android源代码只需要一套,而每一款Android设备都需要有独立的一套Linux内核源代码。

1.5.5 下载经过CM适配的Linux内核源代码

CM Linux内核源代码在github上托管,在CM官网上可以找到相应的下载链接。首先进入CM官网与某一Android设备相关的页面,例如,Nexus S的页面地址如下:

        http://wiki.cyanogenmod.org/w/Crespo_Info

在页面的中部可以找到如图1-10所示的两个链接,其中Kernel后面的链接就是github上的Linux内核源代码下载地址。

图1-10 CM Linux内核源代码下载链接

单击该链接进入github下载页面,可以看到在下载页面上方会有一个如图1-11所示的git下载地址。如果读者不想通过git下载,也可以单击左侧的“ZIP”按钮下载zip压缩版本。

图1-11 CM Linux内核源代码在github上的下载页面

CM Linux内核源代码与官方提供的Linux内核源代码在编译方法上完全一样,读者可以参阅1.4.7小节的Linux内核源代码的编译方法。

1.5.6 编译Android源代码生成Recovery ROM

尽管Android源代码和Linux内核源代码都可以单独下载和编译,不过要想利用它们单独编译生成的二进制文件编译Android源代码生成的二进制文件主要是一些镜像文件,例如,system.img、userdata.img等。编译Linux内核源代码生成的二进制文件主要是zImage文件。如果要通过手工方式制作完整的Android ROM,需要对这些文件做进一步的处理,还需要编写一些脚本。制作ROM还是比较麻烦的。CM团队为了更方便地制作ROM,允许将Android源代码与Linux内核源代码放在一起,并通过统一的命令进行编译,最后生成的目标文件就是一个完整的刷机包(zip文件)。这个刷机包是Recovery ROMAndroid设备的刷机包(ROM)有两种:Bootloader和Recovery。其中Bootloader ROM必须要有一台PC或笔记本电脑与Android设备通过USB线相连才能刷机,而Recovery ROM既可以通过USB线进行刷机,也可以将刷机包(通常为zip压缩文件)复制到Android设备的SD卡中,完全脱离计算机进行刷机。在后面的章节会详细讨论这两种ROM的制作和刷机方法。,所以可以自己进入Recovery模式进行刷机。

现在开始利用1.5.4小节下载的CM Android源代码进一步准备制作ROM的环境。读者可以按照如下几步进行操作。

第1步:下载一些必要的文件

编译Android源代码之前需要下载一些必要的Library和apk文件。这些文件也不需要自己一个个下载,只需要按照如下方式执行get-prebuilts脚本文件即可。

        # cd /sources/cyanogenmod/vendor/cm
        # ./get-prebuilts

如果成功执行上面的命令,会看到在Linux终端输出如图1-12所示的信息,这些信息表明了get-prebuilts脚本下载了哪些文件。

图1-12 执行get-prebuilts脚本文件输出的信息

第2步:下载与Android设备相关的源代码

这里的源代码主要指的是Linux内核源代码,只不过这一步会将Linux内核源代码与Android源代码放到一起,编译时统一处理。在这一步需要使用Android设备的Codename,如果读者使用的是Nexus S,需要执行下面的命令初始化安装环境,如果是第一次执行这些命令,会下载相应的Linux内核源代码。要注意,当前的Linux终端应该在Android源代码的根目录。

        # source build/envsetup.sh
        # breakfast crespo

其中breakfast命令是在envsetup.sh脚本文件中定义的函数,用于根据Codename检测相应的Linux内核源代码是否存在,如果不存在,就直接从相应的地址下载。如果读者的机器上以前就有repo脚本文件,建议使用repo selfupdate升级到最新版本。正在下载Linux内核源代码的效果如图1-13所示。这些输出信息说明正在下载Nexus 7 Wi-Fi的Linux内核源代码(Codename=grouper)。下载完后,会在Android源代码根目录生成一个kernel目录,并且在kernel目录中还会根据不同的厂商建立相应的子目录。例如,Nexus S是三星代工的,所以Nexus S对应的Linux内核源代码目录是<Android源代码根目录>/kernel/samsung/crespo,而Nexus 7 Wi-Fi是由华硕代工的,所以对应的Linux内核源代码目录是<Android源代码根目录>/kernel/asus/grouper。

图1-13 下载Linux内核源代码

第3步:下载与设备有关的私有(proprietary)文件

尽管这一步与第1步一样,也需要下载文件。不过这里的下载可不是从Internet上下载,而是从Android设备上下载到PC上。也就是说要完成这一步的操作就需要有一部Android设备。而且还必须为哪款Android设备制作ROM,就需要哪一款Android设备。例如,要为Nexus S制作ROM,就需要一部Nexus S手机,并且要通过USB线与PC相连。

注意

这一步需要adb命令与Android设备交互,而这时Android源代码还没有编译,在out目录的相应子目录还没有生成adb命令,所以进行这一步之前还需要安装Android SDK,并且需要将<Android SDK根目录>/platform-tools目录添加到PATH环境变量中。

在PC与Android设备通过USB线连接好后,执行如下的命令下载相应的私有文件。这里下载的是Nexus S的私有文件。如果下载其他Android设备的私有文件,需要进入相应的目录,同样是执行extract-files.sh脚本文件。device目录中与各个设备相关的子目录的命名规则与Linux内核源代码目录的命名规则相同。

        #  cd  /sources/cyanogenmod/device/samsung/crespo
        #  ./extract-files.sh

第4步:打开缓存(Cache),加速编译

尽管这一步不是必须的,但可能会获得更快的编译速度,执行下面的命令可以打开用于编译的缓存。或者将该命令加入/root/.profile文件中。

        # export USE_CCACHE=1

第5步:编译

在这一步将编译Android源代码和Linux内核源代码,并最终生成一个可刷机的ROM(zip压缩文件)。编译源代码也需要指定具体设备的Codename,因为除了编译Android源代码,还要编译Linux内核源代码。执行下面的命令可进行编译,并为Nexus S生成ROM压缩包。

        # croot
        # brunch crespo

其中croot是envsetup.sh脚本文件中定义的函数,用于回到Android源代码的根目录。brunch是编译命令,后面要跟Codename。例如,要为Nexus 7 Wi-Fi编译并生成ROM压缩包,需要使用bruch grouper。

编译的过程非常缓慢,请耐心等待。编译完后,对于Nexus S,会在<Android源代码根目录>/out/target/product/crespo目录中生成一个cm-10.1-20130418-UNOFFICIAL-crespo.zip文件(或叫类似的名,因为文件名包含编译的日期)。如果是其他的Android设备,会在<Android源代码根目录>/out/target/product目录以相应的Codename命名生成目录,例如,Nexus 7 Wi-Fi的刷机包在grouper目录中。接下来就是刷机了,在1.5.8小节将详细介绍如何将这个生成的ROM刷到相应的Android设备上。

答疑解惑:为什么APK程序不带odex文件

也许很多读者会提出这样的问题,其他很多Android设备的/system/app目录中的系统应用都是由“APK文件”+“同名的odex文件”组成的,为什么自己用CM Android源代码编译生成的APK程序(在<Android源代码根目录>/packages/apps目录中)只有APK文件呢?odex文件到哪去了?

实际上,CM Android源代码默认生成的APK程序是userdebug模式的,也就是说并没有生成经过优化的odex文件(是对dex文件优化后生成的文件)。不过这对于开发状态并没有任何影响。但在开发完成后,需要编译生成最终的ROM。在这种情况下,最好使用user模式进行编译。由于lunch命令显示的编译模式列表(也可称为Target列表)并没有user,所以可以修改<Android源代码根目录>/build/core/main.mk文件的内容来达到这个目的。现在打开该文件,找到如下的内容。

        ifneq (true, $(DISABLE_DEXPREOPT))
          ifeq ($(user_variant), user)
            ifeq ($(HOST_OS), linux)
              WITH_DEXPREOPT := true
            endif
          endif
        endif

其实上面代码的核心就是WITH_DEXPREOPT := true。如果将WITH_DEXPREOPT属性设为true,则使用brunch命令编译Android源代码,就会提取APK程序中的classes.dex,并将其优化成odex文件。现在将上面代码中前两个条件语句注释掉,如下面代码所示。

        #ifneq (true, $(DISABLE_DEXPREOPT))
          #ifeq ($(user_variant), user)
            ifeq ($(HOST_OS), linux)
              WITH_DEXPREOPT := true
            endif
          #endif
        #endif

现在重新执行brunch crespo命令,就会为Nexus S重新生成经过优化的ROM。查看其中的APK程序,已经全部由APK和odex两个文件组成了。

1.5.7 单独编译CM Linux内核源代码

尽管CM提供的Linux内核源代码与Android源代码放在了一起,但实际上,在编译Android源代码时只是生成了一个内核文件(kernel),并没有完整地编译Linux内核源代码。如果读者要修改Linux内核源代码,例如,添加或修改某个驱动程序,还需要重新编译Linux内核源代码,以便生成zImage文件。

CM Linux内核源代码的编译方法与官方Linux内核源代码的编译方法类似。首先需要从特定的Android设备的/proc目录获取config.gz文件,并将其解压,然后覆盖Linux内核源代码根目录的.config文件。最后再执行make命令编译Linux内核源代码。详细的操作方法见1.4.5小节的内容。

经测试发现,CM Linux内核源代码在编译时可能会有一些小错误。例如,在编译Nexus S的Linux内核源代码时会出现如下的错误。其大意是fbmem.c文件中包含了s3cfb.h头文件,但却没找到该文件。

        drivers/video/fbmem.c:39:27: fatal error: samsung/s3cfb.h: No such file or directory.
        …

为了排除这个编译错误,先来看一下fbmem.c文件的源代码,找到第39行,该行前后与错误相关的代码如下:

        #ifdef CONFIG_FB_S3C
        #include <samsung/s3cfb.h>
        #endif

很明显,samsung/s3cfb.h文件在搜索路径中不存在。这个samsung目录实际上是相对于<Linux内核源代码>/include目录的,查看include目录,并没有找到samsung目录,这就更谈不上s3cfb.h文件了。其中samsung目录被放到了<Linux内核源代码>/drivers/video目录中,只要将该目录中的samsung目录复制到<Linux内核源代码>/include目录即可成功编译Linux内核源代码。编译生成的zImage文件(该文件与编译Android源代码生成的kernel文件是一样的)可以按照第4章的方法生成boot.img镜像文件,然后在Bootloader模式下使用fastboot命令重新刷内核镜像(boot.img)。

1.5.8 刷机!刷机!

现在即将迎来激动人心的时刻:刷机。这回刷机可和传统意义上的刷机不同。一般的刷机都是从网上下载现成的ROM,然后按说明一步步刷机。而这回可是直接从Android源代码和Linux内核源代码编译而成的,尽管我们没有修改这些代码,但对于第一次用源代码制作ROM的读者来说仍然足够兴奋,因为这将是成为一个Geek的第一步。

可能很多从未亲手刷过机的读者会认为刷机存在一些危险,可能会将自己心爱的手机刷成“砖”。没错,刷机的确有风险,不过风险远没有炒股大,但回报却可能比炒股大。学会自己从最原始状态(源代码)制作ROM将会对自己创业或找高薪的工作有非常大的帮助。而刷机带来的风险只要注意一下就基本上可以忽略不计。在后面的章节会详细介绍ROM的各种组成部分,不过这里先提一下。一个完整的ROM主要由system、recovery、userdata、boot和bootloader五部分组成。其中system和userdata属于应用层部分。recovery、boot和bootloader属于系统层部分。这五部分除了bootloader外,其他的可以任意修改。不会有任何危险,但要注意,在刷机之前,最好有一个保证好用的ROM作为备份,万一刷坏了,可以很容易恢复,否则手机就用不了了(硬件没坏,只是软件的问题)。尽管bootloader的源代码也随Android源代码一起发布,但尽量不要自己修改bootloader。Bootloader就像PC的BIOS,一旦坏了,手机就真的成砖了。因为即使要恢复bootloader,也需要进入bootloader模式,一旦bootloader模式进不去,就不能通过常规的方法刷机了(需要利用特殊的设备,但大多数人没有这种设备)。所以读者记住一点,只要bootloader不动(官方提供的bootloader除外),其他的随便刷,毫无危险,即使最终自己不知道怎么恢复。淘宝上有一些商家提供了恢复的服务,几十元就可恢复如初,所以头一次刷机的读者并不需要有任何担心。

在刷机之前需要保证bootloader是解锁的。如果未解锁,需要在Android设备正常开机的情况下执行adb reboot bootloader命令进入bootloader模式,然后执行fastboot oem unlock命令解锁。如果成功解锁,会在bootloader模式界面的最后显示红色的“UNLOCKED”。

由于上一节生成的是Recovery ROM,所以需要进入Recovery模式刷机。读者可以在bootloader模式下直接进入recovery模式,或正常启动Android设备,然后执行adb reboot recovery命令进入Recovery模式。建议读者使用第三方的ClockworkMod Recovery刷机(简称CWM Recovery)。虽然可以直接到http://www.clockworkmod.com下载最新版的Recovery。但并不需要这么麻烦,CM Android源代码带的Recovery就是ClockworkMod Recovery,而且在刷机包所在的目录生成了一个recovery.img镜像文件。只要按着前面的方法进入bootloader模式,然后在Linux终端进入recovery.img文件所在的目录,并执行如下的命令即可刷这个Recovery。

        # fastboot flash recovery recovery.img

注意

刷Recovery时要注意,Recovery的镜像与Linux内核类似,也是与具体的Android设备有关的。不要将其他Android设备的Recovery镜像刷到自己的Android设备上,否则可能进不了Recovery模式。当然,刷错了也没关系。进入bootloader模式重新刷正确的Recovery镜像即可。

刷完Recovery镜像后,正常启动Android设备,然后将ROM文件复制到SD卡的根目录(也可以是SD卡的其他的目录),并且备份手机中的重要数据。现在执行adb reboot recovery命令进入Recovery模式,先选择“wipe data/factory reset”和“wipe cache partition”,清空手机上的数据,然后选择“install zip from sdcard”,在SD卡上找到ROM压缩文件,然后开始刷机。刷完后,选择“reboot system now”重启手机,现在就可以体验新成果了!

注意

由于CM Android源代码与官方提供的Android源代码一样,所以生成的ROM中并不包含Google Map、Google Play等应用。这些软件Google公司仍保留了版权,所以很多第三方的ROM都没有带这些应用,而且Google公司也未提供源代码。需要软件商店或地图的读者,可以另行安装(在后面的章节会详细介绍如何安装这些应用)!

1.5.9 下载现成的CM ROM

如果读者不想通过编译Android源代码和Linux内核源代码的方式制作ROM,只想刷CM ROM,那么可以下载CM网站提供的已经制作好的ROM。

现在进入与某一Android设备相关的页面,例如,Nexus S的页面是http://wiki.cyanogenmod. org/w/Crespo_Info,然后在页面上方会找到如图1-14所示的4个圆形标志(在其下方就是Linux内核的下载链接)。这4个圆形标志分别表示Nightly、M Snapshot、Release Candidate和Stable。它们是Linux源代码的4类发行版本,从左到右稳定性逐渐增强。如果读者想体验最新修改的版本,可以进入Nightly下载页面,通常Nightly每天都会进行微调,不过该版本不是特别稳定,如果不是为了做实验,而只想刷一个比较稳定的Linux内核,建议使用Stable版本。

图1-14 CM ROM的下载链接

下面任选一个进入相应的页面,如进入Nightly下载页面,如图1-15所示。从该页面每一行最后的下载日期可以看出,只要当前Linux版本还在维护,Nightly类别的Linux源代码每天晚上都会进行更新(通常只会保留最近6天的更新)。读者可以单击某一个链接下载Linux页面的zip压缩包。

图1-15 CM Linux源代码的Nightly下载页面

读者也可以在http://get.cm页面寻找与某一Android设备对应的ROM。该页面列出了CM维护的所有支持设备的ROM,读者需要根据Codename进行搜索,例如,Nexus 7 Wi-Fi的ROM需要使用“grouper”进行搜索。

1.6 小结

本章尽管是本书的第1章,但对于大多数读者来说足够酷。因为读者不仅从本章学到了如何下载Android源代码和Linux内核源代码这些常规的操作,还会体验到目前最流行的CM ROM的制作方法,并且第一次体验了刷机的乐趣,而且刷的是自己从源代码直接制作的ROM。

为了满足大多数读者的需求,本书将以CM提供的源代码为主,因为通过这些源代码制作的ROM可以运行在目前绝大多数的Android设备上,当然,官方的相关源代码也会有所涉猎。读者可根据实际情况决定使用哪些源代码,并通过修改这些源代码制作完全属于自己的ROM。