1、近年来,手机移动平台越来越火爆。打开自己的 QQ 算了一下,在线好友共有 31 个,其中用手机上的有 13 个,手机用户占了 42%;打开新浪微博、天涯和猫扑等网站,各种终端设备的发贴、回贴更是数不胜数。目前,手机平台如日中天的要数是 iOS 和 Android 了,尤其是 iOS, Symbian 已没落,WP7 在挣扎。iOS 一如既往地走Apple 特色的封闭道路,然而它的强大在于能够建立一个完整的生态系统,乔布斯确实是天之骄子,没有之一;相反,Android 奉行开放政策。注意, Android 是开放,不是开源,从 Android 被踢出 Linux Kernel 主线代码,可以看出
2、为什么说 Android 是开放而不是开源的: http:/ Android 的开放政策的,毕竟手机厂商还是要赚钱吃饭养家的。今天 iOS 的封闭和 Android 的开放,令人不禁想起来当年封闭的 Apple Mac 是如何被开放的 IBM PC 打得一败涂地的。Google 能够重现 IBM 当年的辉煌吗?不好说,吃一堑,长一智,Apple 还是一个伟大的公司的。然而,个人还是看好 Google,看好 Android.既然看好移动平台,看好 Android,那还等什么,赶紧研究一下吧。工欲善其事,必先利其器。相信大家都知道 Android 的 APP 的是用 Java 写的,运行在 Dal
3、vik 虚拟机上,还有, Android 的系统是基于 Linux Kernel 2.6 的。那么,要想深入了解 Android 系统的各种细节,当然少不了 Linux Kernel 的知识了。本人水平有限,对 Linux Kernel 也是略知皮毛,望与大家共勉,在此,推荐几本几天 Linux Kernel 的经典书籍:1. Linux Kernel Development.2. Understanding the Linux Kernel.3. Linux Device Drivers.4. Linux 内核源代码情景分析.看了 Linux Kernel 的书,当然也要看一下 Androi
4、d 的书了,这样可以对 Android 平台长什么样子有一个感性的认识,推荐下面两本书:1. Professional Android Application Development.2. Google Android SDK 开发范例大全.如果可以一边看书,一边照着例子实战一下,那就更好不过了:) 。后面,我将会总结一些自己学习过程觉得有用的知识出来,与大家分享、交流。开始我们的 Android 之旅吧。在 Ubuntu 上下载、编译和安装 Android 最新源代码分类: Android2011-06-21 23:15 4357 人阅读 评论(41) 收藏 举报看完了前面说的几本书之后,对
5、 Linux Kernel 和 Android 有一定的认识了,是不是心里蠢蠢欲动,想小试牛刀自己编译一把 Android 源代码了呢?一直习惯使用 Windows 系统,而 Android 源代码是不支持在 Windows 上编译上,于是决定使用虚拟机安装 Ubuntu,然后下载、编译和安装 Android 源代码。一. 环境准备。1. 磁盘空间预留 20G 左右,内存 3G,因为一边要跑主机,一边要跑虚拟机,内存要求还是比较高的,这样才会比较流畅。2. 安装 VMWare 7.1.4。我的操作系统是 Win7,VMWare 的版本要新一点的,旧版本的 VMWare 在网络支持上比较差,由于
6、要在虚拟机上下载 Android 源代码,没有网络是万万不行的。3. 安装好 VMWare 后,接下来就安装 Ubuntu 系统了。我选择目前最新的版本 ubuntu-11.04-alternate-i386,从网上查到的资料说,要编译 Android 源代码,Ubuntu 的最低版本是 8.04。下载好后,安装时采用一直默认安装即可。4. 安装 Git 工具。Android 源代码采用 Git 工具来管理,与 SVN 相比,这是一种分布式的源代码管理工具,而SVN 是集中式的源代码管理工具。要安装 Git 工具,在 Ubuntu 上执行以下命令即可:USER-NAMEMACHINE-NAME
7、:$ sudo apt-get install git-core gnupg5. 安装 Java SDK。在 Ubuntu 上执行以下命令:USER-NAMEMACHINE-NAME:$ sudo add-apt-repository ppa:ferramroberto/javaUSER-NAMEMACHINE-NAME:$ sudo apt-get updateUSER-NAMEMACHINE-NAME:$ sudo apt-get install sun-java6-jre sun-java6-pluginUSER-NAMEMACHINE-NAME:$ sudo apt-get insta
8、ll sun-java6-jdk6. 依赖的其它包。在 Ubuntu 上执行以下命令:USER-NAMEMACHINE-NAME:$ sudo apt-get install flex bison gperf libsdl-dev libesd0-dev libwxgtk2.6-dev build-essential zip curl7. 调试工具。在 Ubuntu 上执行以下命令:USER-NAMEMACHINE-NAME:$ sudo apt-get install valgrind二. 下载 Android 源代码工程。1. 下载 repo 工具。在 Ubuntu 上执行以下命令:USE
9、R-NAMEMACHINE-NAME:$ wget http:/android.git.kernel.org/repoUSER-NAMEMACHINE-NAME:$ chmod 777 repoUSER-NAMEMACHINE-NAME:$ cp repo /bin/2. 下载 Android 最新版本源代码。在 Ubuntu 上执行以下命令:USER-NAMEMACHINE-NAME:$ mkdir AndroidUSER-NAMEMACHINE-NAME:$ cd AndroidUSER-NAMEMACHINE-NAME:/Android$ repo init -u git:/androi
10、d.git.kernel.org/platform/manifest.gitUSER-NAMEMACHINE-NAME:/Android$ repo sync经过漫长的等待(我下载了两三天)后,就可以把 Android 源代码下载下来了。其间可能还有经历下载中断的情况,这时只要重新执行 repo sync 就可以了。三. 编译 Android 源代码。1. 编译。在 Android 目录下执行以下命令:USER-NAMEMACHINE-NAME:/Android$ make第一次编译要等待比较久的时间,编译成功后,可以看到下面的输出:Target system fs image: out/ta
11、rget/product/generic/obj/PACKAGING/systemimage_intermediates/system.imgInstall system fs image: out/target/product/generic/system.imgTarget ram disk: out/target/product/generic/ramdisk.imgTarget userdata fs image: out/target/product/generic/userdata.imgInstalled file list: out/target/product/generic
12、/installed-files.txt 2. 编译过程中可能会遇到的问题。问题一: You are attempting to build on a 32-bit system.两个地方需要个修改:1)修改 build/core 目录下的 main.mk 文件:ifeq ($(BUILD_OS),linux)build_arch := $(shell uname -m)#Change the following line for building on a 32-bit system.#ifneq (64,$(findstring 64,$(build_arch)ifneq (i686,$(
13、findstring i686,$(build_arch)$(warning *)$(warning You are attempting to build on a 32-bit system.)$(warning Only 64-bit build environments are supported beyond froyo/2.2.)2)找到下列文件:/external/clearsilver/cgi/Android.mk/external/clearsilver/cs/Android.mk/external/clearsilver/java-jni/Android.mk/extern
14、al/clearsilver/util/Android.mk修改 LOCAL_CFLAGS 和 LOCAL_LDFLAGS 变量:# This forces a 64-bit build for Java6# Change the following two lines for building on a 32-bit system.# LOCAL_CFLAGS += -m64# LOCAL_LDFLAGS += -m64LOCAL_CFLAGS += -m32LOCAL_LDFLAGS += -m32问题二: Undefined reference to _dso_handle extern
15、al/stlport/src/monetary.cpp:39: undefined reference to _dso_handleout/target/product/vm/obj/SHARED_LIBRARIES/libstlport_intermediates/src/locale.o: In function _static_initialization_and_destruction_0:external/stlport/src/locale.cpp:29: undefined reference to _dso_handleout/target/product/vm/obj/SHA
16、RED_LIBRARIES/libstlport_intermediates/src/locale_impl.o: In function _static_initialization_and_destruction_0:external/stlport/src/locale_impl.cpp:31: undefined reference to _dso_handleout/target/product/vm/obj/SHARED_LIBRARIES/libstlport_intermediates/src/locale_impl.o: In function std:_Locale_imp
17、l:make_classic_locale():external/stlport/src/locale_impl.cpp:670: undefined reference to _dso_handleexternal/stlport/src/locale_impl.cpp:667: undefined reference to _dso_handleout/target/product/vm/obj/SHARED_LIBRARIES/libstlport_intermediates/src/locale_impl.o:external/stlport/src/locale_impl.cpp:6
18、04: more undefinedreferences to _dso_handle followcollect2: ld returned 1 exit status 修改 external/stlport/dll_main.cpp,加入以下声明:extern “C“ void * _dso_handle = 0; 四. 编译 SDK,这一步是可选的。1. 编译。执行以下命令:USER-NAMEMACHINE-NAME:/Android$ make sdk2. 编译过程中可能会遇到的问题。问题一:找不到 bios.bin 和 vgabios-cirrus.bin 文件couldnt loc
19、ate source file: usr/share/pc-bios/bios.bincouldnt locate source file: usr/share/pc-bios/vgabios-cirrus.bin注意,这里的 usr/share 目录指的是/Android/out/host/linux-x86 目录下的 usr/share 目录,修改办法是复制/Android/prebuilt/common 下的 pc-bios 文件夹到/Android/out/host/linux-x86/usr/share 即可:USER-NAMEMACHINE-NAME:/Android$ cp /A
20、ndroid/prebuilt/common/pc-bios /Android/out/host/linux-x86/usr/share问题二:找不到 ddmlib-tests.jar、 ninepath-tests.jar 、 common-tests.jar 和 sdkuilib-tests.jar 文件在/Android/out/host/linux-x86/framework 这个目录下,可以找到以下几个文件common.jar、ddmlib.jar、ninepatch.jar、sdkuilib.jar 这四个文件,然后将它们分别复制一份,并重命名,命名的原则很简单,就是在原有的名字后
21、面跟上tests 即可。五. 安装编译好的 Android 镜像到模拟器上。1. 设置环境变量:USER-NAMEMACHINE-NAME:/Android$ export PATH=$PATH:/Android/out/host/linux-x86/bin USER-NAMEMACHINE-NAME:/Android$ export ANDROID_PRODUCT_OUT=/Android/out/target/product/generic其中,/Android/out/host/linux-x86/bin 有我们要执行的 emulator 命令,而/Android/out/target/
22、product/generic是 Android 镜像存放目录,下面执行 emulator 命令时会用到。2. 运行模拟器。USER-NAMEMACHINE-NAME:/Android$ emulator模拟器运行需要四个文件,分别是 Linux Kernel 镜像 zImage 和 Android 镜像文件 system.img、userdata.img和 ramdisk.img。执行 emulator 命令时,如果不带任何参数,则 Linux Kernel 镜像默认使用/Android/prebuilt/android-arm/kernel 目录下的 kernel-qemu 文件,而 An
23、droid 镜像文件则默认使用ANDROID_PRODUCT_OUT 目录下的 system.img、userdata.img 和 ramdisk.img,也就是我们刚刚编译出来的镜像问题。当然,我们也可以以指定的镜像文件来运行模拟器,即运行 emulator 时,即:USER-NAMEMACHINE-NAME:/Android$ emulator -kernel ./prebuilt/android-arm/kernel/kernel-qemu -sysdir ./out/target/product/generic -system system.img -data userdata.img
24、 -ramdisk ramdisk.img到这里,我们就可以在模拟器上运行我们自己编译的 Android 镜像文件了,是不是很酷呢?但是注意,这里说的 Android 镜像文件,只是包括 system.img、userdata.img 和 ramdisk.img 这三个文件,而 Linux Kernel 镜像用的是 Android 为我们预编译好的 kernel-qemu 镜像。那么,有没有办法使用我们自己编译的 Linux Kernel 镜像呢?答案上肯定的,这样我们就可以完全 DIY 自己的 Android 系统了!我将在下一篇文章描述如果编译自己的 Linux Kernel 镜像,敬请期
25、待 在 Ubuntu 上下载、编译和安装 Android 最新内核源代码(Linux Kernel)分类: Android2011-06-23 22:53 3612 人阅读 评论(58) 收藏 举报在前一篇文章提到,从源代码树下载下来的最新 Android 源代码,是不包括内核代码的,也就是 Android 源代码工程默认不包含 Linux Kernel 代码,而是使用预先编译好的内核,也就是 prebuilt/android-arm/kernel/kernel-qemu 文件。那么,如何才能 DIY 自己的内核呢?这篇文章一一道来。一. 首选,参照前一篇 在 Ubuntu 上下载、编译和安装
26、 Android 最新源代码准备好 Android 源代码目录。二. 下载 Linux Kernel for Android 源代码。1. 使用 GIT 工具下载,执行以下命令:USER-NAMEMACHINE-NAME:/Android$ mkdir kernelUSER-NAMEMACHINE-NAME:/Android$ cd kernelUSER-NAMEMACHINE-NAME:/Android/kernel$ git clone git:/android.git.kernel.org/kernel/common.git同样是经过漫长的等待后,在 kernel 目录下有一个 comm
27、on 目录,Linux 内核代码就在这里了。2. 下载完成后,可以查看下载的内核代码版本:USER-NAMEMACHINE-NAME:/Android/kernel$ cd commonUSER-NAMEMACHINE-NAME:/Android/kernel/common$ git branchandroid-2.6.363. 下载完 Android 的 Linux 内核代码后,会发现在 arch/arm/configs 下没有模拟器要使用的硬件配置文件goldfish_defconfig,而这个配置文件是编译内核代码时需要用到的,因此,需要 checkout goldfish 版本:USE
28、R-NAMEMACHINE-NAME:/Android/kernel/common$ git branch -a* android-2.6.36remotes/origin/HEAD - origin/android-2.6.36remotes/origin/android-2.6.35remotes/origin/android-2.6.36remotes/origin/archive/android-2.6.25remotes/origin/archive/android-2.6.27remotes/origin/archive/android-2.6.29remotes/origin/a
29、rchive/android-2.6.32remotes/origin/archive/android-gldfish-2.6.29remotes/origin/archive/android-goldfish-2.6.27选择 android-gldfish-2.6.29:USER-NAMEMACHINE-NAME:/Android/kernel/common$ git checkout remotes/origin/archive/android-gldfish-2.6.29下载完毕后,就能在 arch/arm/configs 下看到 goldfish_defconfig 这个文件了。三.
30、 编译内核代码。1. 导出交叉编译工具目录到 $PATH 环境变量中去:USER-NAMEMACHINE-NAME:/Android/kernel/common$ export PATH=$PATH:/Android/prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin2. 修改 common 目录下的 Makefile 文件的以下两行为:# ARCH ?= (SUBARCH)# CROSS_COMPILE?= ARCH ?= arm #体系结构为 armCROSS_COMPILE ?= arm-eabi- #交叉编译工具链前缀,参考/Android/
31、prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin 目录 3. 开始编译:USER-NAMEMACHINE-NAME:/Android/kernel/common$ make goldfish_defconfigUSER-NAMEMACHINE-NAME:/Android/kernel/common$ make编译成功后,可看到下面两行:OBJCOPY arch/arm/boot/zImageKernel: arch/arm/boot/zImage is ready在执行 make 命令前,你也可以执行 make menuconfig 先配置一下编译
32、选项。四. 在模拟器中运行编译好的内核。1. 在启动模拟器之前,先设置模拟器的目录到环境变量$PATH 中去:USER-NAMEMACHINE-NAME:/Android$ export PATH=$PATH:/Android/out/host/linux-x86/bin2. 设置 ANDROID_PRODUCT_OUT 环境变量:USER-NAMEMACHINE-NAME:/Android$ export ANDROID_PRODUCT_OUT=/Android/out/target/product/generic3. 在后台中指定内核文件启动模拟器:USER-NAMEMACHINE-NAM
33、E:/Android$ emulator -kernel ./kernel/common/arch/arm/boot/zImage 14. struct semaphore sem; 15. struct cdev dev; 16.; 17. 18.#endif 这个头文件定义了一些字符串常量宏,在后面我们要用到。此外,还定义了一个字符设备结构体hello_android_dev,这个就是我们虚拟的硬件设备了,val 成员变量就代表设备里面的寄存器,它的类型为int,sem 成员变量是一个信号量,是用同步访问寄存器 val 的,dev 成员变量是一个内嵌的字符设备,这个Linux 驱动程序自定
34、义字符设备结构体的标准方法。四.在 hello 目录中增加 hello.c 文件,这是驱动程序的实现部分。驱动程序的功能主要是向上层提供访问设备的寄存器的值,包括读和写。这里,提供了三种访问设备寄存器的方法,一是通过 proc 文件系统来访问,二是通过传统的设备文件的方法来访问,三是通过 devfs 文件系统来访问。下面分段描述该驱动程序的实现。首先是包含必要的头文件和定义三种访问设备的方法:view plain1. #include 2. #include 3. #include 4. #include 5. #include 6. #include 7. #include 8. 9. #i
35、nclude “hello.h“ 10. 11./*主设备和从设备号变量*/ 12.static int hello_major = 0; 13.static int hello_minor = 0; 14. 15./*设备类别和设备变量*/ 16.static struct class* hello_class = NULL; 17.static struct hello_android_dev* hello_dev = NULL; 18. 19./*传统的设备文件操作方法*/ 20.static int hello_open(struct inode* inode, struct file
36、* filp); 21.static int hello_release(struct inode* inode, struct file* filp); 22.static ssize_t hello_read(struct file* filp, char _user *buf, size_t count, loff_t*f_pos); 23.static ssize_t hello_write(struct file* filp, const char _user *buf, size_t count, loff_t* f_pos); 24. 25./*设备文件操作方法表*/ 26.st
37、atic struct file_operations hello_fops = 27. .owner = THIS_MODULE, 28. .open = hello_open, 29. .release = hello_release, 30. .read = hello_read, 31. .write = hello_write, 32.; 33. 34./*访问设置属性方法*/ 35.static ssize_t hello_val_show(struct device* dev, struct device_attribute* attr, char* buf); 36.stati
38、c ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count); 37. 38./*定义设备属性*/ 39.static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, hello_val_show, hello_val_store); 定义传统的设备文件访问方法,主要是定义 hello_open、hello_release、hello_read 和 hello_write 这四个打开、释放、读和写设备文件的方法:vie
39、w plain1. /*打开设备方法*/ 2. static int hello_open(struct inode* inode, struct file* filp) 3. struct hello_android_dev* dev; 4. 5. /*将自定义设备结构体保存在文件指针的私有数据域中,以便访问设备时拿来用*/ 6. dev = container_of(inode-i_cdev, struct hello_android_dev, dev); 7. filp-private_data = dev; 8. 9. return 0; 10. 11. 12./*设备文件释放时调用,
40、空实现*/ 13.static int hello_release(struct inode* inode, struct file* filp) 14. return 0; 15. 16. 17./*读取设备的寄存器 val 的值*/ 18.static ssize_t hello_read(struct file* filp, char _user *buf, size_t count, loff_t*f_pos) 19. ssize_t err = 0; 20. struct hello_android_dev* dev = filp-private_data; 21. 22. /*同步
41、访问*/ 23. if(down_interruptible( 25. 26. 27. if(count val) 28. goto out; 29. 30. 31. /*将寄存器 val 的值拷贝到用户提供的缓冲区*/ 32. if(copy_to_user(buf, 34. goto out; 35. 36. 37. err = sizeof(dev-val); 38. 39.out: 40. up( 41. return err; 42. 43. 44./*写设备的寄存器值 val*/ 45.static ssize_t hello_write(struct file* filp, co
42、nst char _user *buf, size_t count, loff_t* f_pos) 46. struct hello_android_dev* dev = filp-private_data; 47. ssize_t err = 0; 48. 49. /*同步访问*/ 50. if(down_interruptible( 52. 53. 54. if(count != sizeof(dev-val) 55. goto out; 56. 57. 58. /*将用户提供的缓冲区的值写到设备寄存器去*/ 59. if(copy_from_user( 61. goto out; 62.
43、 63. 64. err = sizeof(dev-val); 65. 66.out: 67. up( 68. return err; 69. 定义通过 devfs 文件系统访问方法,这里把设备的寄存器 val 看成是设备的一个属性,通过读写这个属性来对设备进行访问,主要是实现 hello_val_show 和 hello_val_store 两个方法,同时定义了两个内部使用的访问 val值的方法_hello_get_val 和_hello_set_val:view plain1. /*读取寄存器 val 的值到缓冲区 buf 中,内部使用*/ 2. static ssize_t _hello
44、_get_val(struct hello_android_dev* dev, char* buf) 3. int val = 0; 4. 5. /*同步访问*/ 6. if(down_interruptible( 8. 9. 10. val = dev-val; 11. up( 12. 13. return snprintf(buf, PAGE_SIZE, “%d/n“, val); 14. 15. 16./*把缓冲区 buf 的值写到设备寄存器 val 中去,内部使用 */ 17.static ssize_t _hello_set_val(struct hello_android_dev*
45、 dev, const char* buf, size_t count) 18. int val = 0; 19. 20. /*将字符串转换成数字*/ 21. val = simple_strtol(buf, NULL, 10); 22. 23. /*同步访问*/ 24. if(down_interruptible( 26. 27. 28. dev-val = val; 29. up( 30. 31. return count; 32. 33. 34./*读取设备属性 val*/ 35.static ssize_t hello_val_show(struct device* dev, stru
46、ct device_attribute* attr, char* buf) 36. struct hello_android_dev* hdev = (struct hello_android_dev*)dev_get_drvdata(dev); 37. 38. return _hello_get_val(hdev, buf); 39. 40. 41./*写设备属性 val*/ 42.static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t
47、count) 43. struct hello_android_dev* hdev = (struct hello_android_dev*)dev_get_drvdata(dev); 44. 45. return _hello_set_val(hdev, buf, count); 46. 定义通过 proc 文件系统访问方法,主要实现了 hello_proc_read 和 hello_proc_write 两个方法,同时定义了在 proc 文件系统创建和删除文件的方法 hello_create_proc 和 hello_remove_proc:view plain1. /*读取设备寄存器 v
48、al 的值,保存在 page 缓冲区中*/ 2. static ssize_t hello_proc_read(char* page, char* start, off_t off, int count, int* eof, void* data) 3. if(off 0) 4. *eof = 1; 5. return 0; 6. 7. 8. return _hello_get_val(hello_dev, page); 9. 10. 11./*把缓冲区的值 buff 保存到设备寄存器 val 中去*/ 12.static ssize_t hello_proc_write(struct file* filp, const char _user *buff, unsignedlong len, void* data) 13. int err = 0; 14. char