Android传感器开发与智能设备案例实战
上QQ阅读APP看书,第一时间看更新

第3章 获取并编译源码

要想掌握Android智能设备底层开发相关技术,需要先了解Android系统源码的基本知识。在了解Android系统源码之前,需要先获取其具体源码。因为目前市面上主流的操作系统有Windows、Linux和Mac OS的操作系统,由于Mac OS源自于Linux系统,所以本章讲解分别在Windows系统和Linux系统中获取Android源码的知识,并讲解编译Android源码的具体过程,为读者步入本书后面知识的学习打下基础。

3.1 在Linux系统获取Android源码

在Linux系统中,通常使用Ubuntu来下载和编译Android源码。由于Android的源码内容很多,Google采用了git的版本控制工具,并对不同的模块设置不同的git服务器,可以用repo自动化脚本来下载Android源码,下面介绍如何一步一步地获取Android源码的过程。

1.下载repo

在用户目录下,创建bin文件夹,用于存储repo,并把该路径设置到环境变量中去,命令如下:

      $ mkdir ~/bin
      $ PATH=~/bin:$PATH

下载repo的脚本,用于执行repo,命令如下:

      $ curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo

设置可执行权限,命令如下:

      $ chmod a+x ~/bin/repo

2.初始化一个repo的客户端

在用户目录下,创建一个空目录,用于存储Android源码,命令如下:

      $ mkdir AndroidCode
      $ cd AndroidCode

进入AndroidCode目录,并运行repo下载源码,下载主线分支的代码,主线分支包括最新修改的bug以及并未正式发出版本的最新源码,命令如下:

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

下载其他分支,正式发布的版本,可以通过添加-b参数来下载,命令如下:

      $ repo init -u https://android.googlesource.com/platform/manifest -b
      android-4.4_r1

在下载过程中需要填写Name和E-mail,填写完毕之后,选择Y进行确认,最后提示repo初始化完成,这时可以开始同步Android源码,同步过程很漫长,需要耐心的等待,执行下面命令开始同步代码:

      $ repo sync

经过上述步骤后,便开始下载并同步Android源码,界面效果如图3-1所示。在Windows平台获取Android源码

▲图3-1 下载同步

3.2 在Windows平台获取Android源码

Windows平台上获取源码和Linux原理相同,但是需要预先在Windows平台上面搭建一个Linux环境,此处需要用到cygwin工具。cygwin的作用是构建一套在Windows上的Linux模拟环境,下载cygwin工具的地址如下:

      http://cygwin.com/install.html

下载成功后会得到一个名为“setup.exe”可执行文件,通过此文件可以更新和下载最新的工具版本,具体流程如下所示。

(1)启动cygwin,如图3-2所示。

▲图3-2 启动cygwin

(2)单击“下一步”按钮,选择第一个选项:从网络下载安装,如图3-3所示。

▲图3-3 选择从网络下载安装

(3)单击“下一步”按钮,选择安装根目录,如图3-4所示。

▲图3-4 选择安装根目录

(4)单击“下一步”按钮,选择临时文件目录,如图3-5所示。

▲图3-5 选择临时文件目录

(5)单击“下一步”按钮,设置网络代理。如果所在网络需要代理,则在这一步进行设置,如果不用代理,则选择直接下载,如图3-6所示。

▲图3-6 设置网络代理

(6)单击“下一步”按钮,选择下载站点。一般选择比较近的站点,速度会比较快,如图3-7所示。

▲图3-7 选择下载站点

(7)单击“下一步”按钮,开始更新工具列表,如图3-8所示。

▲图3-8 更新工具列表

(8)单击“下一步”按钮,选择需要下载的工具包。在此需要依次下载curl、git和python这些工具。如图3-9所示。

▲图3-9 依次下载工具

为了确保能够安装上述工具,一定要用鼠标双击变为Install形式,如图3-10所示。

▲图3-10 务必设置为Install形式

(9)单击“下一步”按钮,经过漫长的等待过程,如图3-11所示。

▲图3-11 下载进度条

如果下载安装成功会出现提示信息,单击“完成”按钮即完成安装。当安装好cygwin后,打开cygwin,会模拟出一个Linux的工作环境,然后按照Linux平台的源码下载方法就可以下载Android源码了。

建议读者在下载Android源码时,严格按照官方提供的步骤进行,地址是:http://source.android.com/source/downloading.html,这一点对初学者来说尤为重要。另外,整个下载过程比较漫长,需要大家耐心的等待。例如图3-12所示是下载Android 4.4时的机器命令截图。

▲图3-12 在Windows中用cygwin工具下载Android源码的截图

3.3 分析Android源码结构

获得Android源码后,可以将源码的全部工程分为如下3个部分。

· Core Project:核心工程部分,这是建立Android系统的基础,被保存在根目录的各个文件夹中。

· External Project:扩展工程部分,可以使其他开源项目具有扩展功能,被保存在“external”文件夹中。

· Package:包部分,提供了Android的应用程序、内容提供者、输入法和服务,被保存在“package”文件夹中。

无论是Android 1.5还是Android 4.4,各个版本的源码目录基本类似。在里面包含了原始Android的目标机代码、主机编译工具和仿真环境。解压缩下载的Android 4.4源码包后,第一级别目录结构的具体说明如表3-1所示。

表3-1 Android 4.4源码的根目录

3.3.1 应用程序

应用程序主要是UI界面的实现,广大开发者基于SDK上开发的一个个独立的APK包,都是属于应用程序这一层的,应用程序在Android系统中处于最上层的位置。源码结构中的packages目录用于实现系统的应用程序,packages的目录结构如下所示。

      packages /
      ├—— apps //应用程序库
      |   ├—— BasicSmsReceiver           //基础短信接收
      |   ├—— Bluetooth                  //蓝牙
      |   ├—— Browser                    //浏览器
      |   ├—— Calculator                 //计算器
      |   ├—— Calendar                   //日历
      |   ├—— Camera                     //照相机
      |   ├—— CellBroadcastReceiver      //单元广播接收
      |   ├—— CertInstaller              //被调用的包,在Android中安装数字签名
      |   ├—— Contacts                   //联系人
      |   ├—— DeskClock                  //桌面时钟
      |   ├—— Email                      //电子邮件
      |   ├—— Exchange                   //Exchange服务
      |   ├—— Gallery                    //图库
      |   ├—— Gallery2                   //图库2
      |   ├—— HTMLViewer                 //HTML查看器
      |   ├—— KeyChain                   //密码管理
      |   ├—— Launcher2                  //启动器2
      |   ├—— Mms                         //彩信
      |   ├—— Music                      //音乐
      |   ├—— MusicFX                    //音频增强
      |   ├—— Nfc                         //近场通信
      |   ├—— PackageInstaller           //包安装器
      |   ├—— Phone                      //电话
      |   ├—— Protips                    //主屏幕提示
      |   ├—— Provision                  //引导设置

      |   ├—— QuickSearchBox             //快速搜索框
      |   ├—— Settings                   //设置
      |   ├—— SoundRecorder              //录音机
      |   ├—— SpareParts                 //系统设置
      |   ├—— SpeechRecorder             //录音程序
      |   ├—— Stk                         //sim卡相关
      |   ├—— Tag                         //标签
      |   ├—— VideoEditor                //视频编辑
      |   └—— VoiceDialer                //语音编号
      ├—— experimental                    //非官方的应用程序
      |   ├—— BugReportSender            //bug的报告程序
      |   ├—— Bummer
      |   ├—— CameraPreviewTest          //照相机预览测试车程序
      |   ├—— DreamTheater
      |   ├—— ExampleImsFramework
      |   ├—— LoaderApp
      |   ├—— NotificationLog
      |   ├—— NotificationShowcase
      |   ├—— procstatlog
      |   ├—— RpcPerformance
      |   └—— StrictModeTest
      ├—— inputmethods                    //输入法
      |   ├—— LatinIME                   //拉丁文输入法
      |   ├—— OpenWnn                    //OpenWnn输入法
      |   └—— PinyinIME                  //拼音输入法
      ├—— providers                       //提供器
      |   ├—— ApplicationsProvider       //应用程序提供器,提供应用程序所需要的界面
      |   ├—— CalendarProvider           //日历提供器
      |   ├—— ContactsProvider           //联系人提供器
      |   ├—— DownloadProvider           //下载管理提供器
      |   ├—— DrmProvider                //数据库相关
      |   ├—— GoogleContactsProvider     //Google联系人提供器
      |   ├—— MediaProvider              //媒体提供器
      |   ├—— TelephonyProvider          //彩信提供器
      |   └—— UserDictionaryProvider     //用户字典提供器
      ├—— screensavers                    //屏幕保护
      |   ├—— Basic                      //基本屏幕保护
      |   ├—— PhotoTable                 //照片方格
      |   ├—— WebView                    //网页
      └—— wallpapers                      //墙纸
      ├—— Basic                           //系统内置墙纸
      ├—— Galaxy4                         //S4内置墙纸
      ├—— HoloSpiral                      //手枪皮套墙纸
      ├—— LivePicker
      ├—— MagicSmoke
      ├—— MusicVisualization
      ├—— NoiseField
      └—— PhaseBeam

通过上面的目录结构可以看出,package目录主要存储的是Android系统应用层相关的内容,包括应用程序相关的包或者资源文件,其中既包括系统自带的应用程序,又有第三方开发的应用程序,还有屏幕保护和墙纸等应用,所以源码中package目录对应着系统的应用层。

3.3.2 应用程序框架

应用程序框架是Android系统中的核心部分,也就是SDK部分,它会提供接口给应用程序使用,同时应用程序框架又会和系统服务、系统程序库、硬件抽象层有关联,所以其作用十分重要,应用程序框架的实现代码大部分都在/frameworks/base和/frameworks/av目录下,/frameworks/base的目录结构如下所示。

      frameworks/base
      ├—— api             //全是XML文件,定义了API
      ├—— cmds            //Android中的重要命令(am、app_proce等)
      ├—— core            //核心库
      ├—— data            //声音字体等数据文件

      ├—— docs            //文档
      ├—— drm             //数字版权管理
      ├—— graphics       //图形图像
      ├—— icu4j           //用于解决国际化问题
      ├—— include        //头文件
      ├—— keystore       //数字签名证书相关
      ├—— libs            //库
      ├—— location       //地理位置
      ├—— media           //多媒体
      ├—— native         //本地库
      ├—— nfc-extras     //NFC相关
      ├—— obex            //蓝牙传输
      ├—— opengl         //opengl相关
      ├—— packages       //设置、TTS、VPN程序
      ├—— policy         //锁屏界面相关
      ├—— sax             //XML解析器
      ├—— services       //Android的服务
      ├—— telephony      //电话相关
      ├—— test-runner    //测试相关
      ├—— tests           //测试相关
      ├—— tools           //工具
      ├—— voip            //可视通话
      └—— wifi            //无线网络

以上这些文件夹包含了应用程序框架层的大部分代码,正是这些目录下的文件构成了Android的应用程序框架层,提供接口给应用程序调用,同时衔接系统程序库和硬件抽象层,形成一个由上至下的调用过程。在/frameworks/base目录下也涉及系统服务、程序库中的一些代码,将在后面的两个小节中会详细分析。

3.3.3 系统服务

在3.3.2中介绍了应用程序框架层的内容,了解到大部分的实现代码保存在“/frameworks/base”目录下。其实在这个目录中还有一个名为“service”的目录,里面的代码是用于实现Android系统服务的。接下来将详细介绍service目录下的内容,其目录结构如下所示。

      frameworks/base/services
      ├—— common_time          //时间日期相关的服务
      ├—— input                //输入系统服务
      ├—— java                 //其他重要服务的Java层
      ├—— jni                  //其他重要服务的JNI层
      └—— tests                //测试相关

其中java和jni两个目录分别是一些其他的服务的Java层和JNI层实现,java目录下更详细的目录结构以及其他Android系统服务的说明如下所示。

      frameworks/base/services/java/com/android/server
      ├—— accessibility
      ├—— am
      ├—— connectivity
      ├—— display
      ├—— dreams
      ├—— drm
      ├—— input
      ├—— location
      ├—— net
      ├—— pm
      ├—— power
      ├—— updates
      ├—— usb
      ├—— wm
      ├—— AlarmManagerService.java            //闹钟服务
      ├—— AppWidgetService.java                //应用程序小工具服务
      ├—— AppWidgetServiceImpl.java
      ├—— AttributeCache.java
      ├—— BackupManagerService.java           //备份服务

      ├—— BatteryService.java                  //电池相关服务
      ├—— BluetoothManagerService.java        //蓝牙
      ├—— BootReceiver.java
      ├—— BrickReceiver.java
      ├—— CertBlacklister.java
      ├—— ClipboardService.java
      ├—— CommonTimeManagementService.java    //时间管理服务
      ├—— ConnectivityService.java
      ├—— CountryDetectorService.java
      ├—— DevicePolicyManagerService.java
      ├—— DeviceStorageMonitorService.java    //设备存储器监听服务
      ├—— DiskStatsService.java                //磁盘状态服务
      ├—— DockObserver.java                    //底座监视服务
      ├—— DropBoxManagerService.java
      ├—— EntropyMixer.java
      ├—— EventLogTags.logtags
      ├—— INativeDaemonConnectorCallbacks.java
      ├—— InputMethodManagerService.java      //输入法管理服务
      ├—— IntentResolver.java
      ├—— IntentResolverOld.java
      ├—— LightsService.java
      ├—— LocationManagerService.java         //地理位置服务
      ├—— MasterClearReceiver.java
      ├—— MountService.java                    //挂载服务
      ├—— NativeDaemonConnector.java
      ├—— NativeDaemonConnectorException.java
      ├—— NativeDaemonEvent.java
      ├—— NetworkManagementService.java       //网络管理服务
      ├—— NetworkTimeUpdateService.java
      ├—— NotificationManagerService.java     //通知服务
      ├—— NsdService.java
      ├—— PackageManagerBackupAgent.java
      ├—— PreferredComponent.java
      ├—— ProcessMap.java
      ├—— RandomBlock.java
      ├—— RecognitionManagerService.java
      ├—— SamplingProfilerService.java
      ├—— SerialService.java                   //NFC相关
      ├—— ServiceWatcher.java
      ├—— ShutdownActivity.java
      ├—— StatusBarManagerService.java        //状态栏管理服务
      ├—— SystemBackupAgent.java
      ├—— SystemServer.java
      ├—— TelephonyRegistry.java
      ├—— TextServicesManagerService.java
      ├—— ThrottleService.java
      ├—— TwilightCalculator.java
      ├—— TwilightService.java
      ├—— UiModeManagerService.java
      ├—— UpdateLockService.java               //锁屏更新服务
      ├—— VibratorService.java                 //震动服务
      ├—— WallpaperManagerService.java        //壁纸服务
      ├—— Watchdog.java                        //看门狗
      ├—— WifiService.java                     //无线网络服务
      └—— WiredAccessoryManager.java          //无线设备管理服务

从上面的文件夹和文件可以看出,Android中涉及的服务种类非常多,包括界面、网络、电话等核心模块基本上都有其专属的服务,这些是属于系统级别的服务,这些系统服务一般都会在Android系统启动的时候加载,在系统关闭的时候结束,受到系统的管理,应用程序并没有权力去打开或者关闭,它们会随着系统的运行一直在后台运行,供应用程序和其他的组件来使用。

另外在frameworks/av/下面也有一个services目录,这个目录下存储的是音频和照相机的服务的实现代码,目录结构如下所示。

      frameworks/av/services
     ├—— audioflinger         //音频管理服务
     └—— camera               //照相机的管理服务

这个av/services目录下的文件主要是用于支持Android系统中的音频和照相机服务的,这是两个非常重要的系统服务,在开发应用程序时会经常依赖这两个服务的。

3.3.4 系统程序库

Android的系统程序库类型非常多,功能也非常强大,正是有了这些程序库,Android系统才能运行多种多样的应用程序。在接下来的内容中,本书选择了一些很常用的也是很重要的系统程序库来分析它们在源码中所处的位置。

1.系统C库

Android系统采用的是一个从BSD继承而来的标准的系统函数库bionic,在源码根目录下有这个文件夹,其目录结构如下所示。

      bionic/
      ├—— libc            //C库
      ├—— libdl           //动态链接库相关
      ├—— libm            //数学库
      ├—— libstdc++      //C++实现库
      ├—— libthread_db   //线程库
      ├—— linker         //连接器相关
      └—— test            //测试相关

2.媒体库

Android中的媒体库在2.3之前是由OpenCore实现的,2.3之后Stragefright被替换了,OpenCore成为了新的多媒体的实现库。同时Android也自带了一些音视频的管理库,用于管理多媒体的录制、播放、编码和解码等功能。Android的多媒体程序库的实现代码主要在/frameworks/av/media目录下,其目录结构如下所示。

      frameworks/av/media/
      ├—— common_time               //时间相关
      ├—— libeffects                //多媒体效果
      ├—— libmedia                  //多媒体录制、播放
      ├—— libmedia_native           //里面只有一个Android.mk,用来编译native文件
      ├—— libmediaplayerservice     //多媒体播放服务的实现库
      ├—— libstagefright            //stagefright的实现库
      ├—— mediaserver               //跨进程多媒体服务
      └—— mtp                       //mtp协议的实现(媒体传输协议)

3.图层显示库

Android中的图层显示库主要负责对显示子系统的管理,负责图层的渲染、叠加、绘制等功能,提供了2D和3D图层的无缝融合,是整个Android系统显示的“大脑中枢”,其代码在/frameworks/native/services/surfaceflinger/目录下,其目录结构如下所示。

      frameworks/native/services/surfaceflinger/
      ├—— DisplayHardware           //显示底层相关
      ├—— tests                     //测试
      ├—— Android.mk                //MakeFile文件
      ├—— Barrier.h
      ├—— Client.cpp                //显示的客户端实现文件
      ├—— Client.h
      ├—— clz.cpp
      ├—— clz.h
      ├—— DdmConnection.cpp
      ├—— DdmConnection.h
      ├—— DisplayDevice.cpp         //显示设备相关
      ├—— DisplayDevice.h
      ├—— EventThread.cpp           //消息线程

      ├—— EventThread.h
      ├—— GLExtensions.cpp          //opengl扩展
      ├—— GLExtensions.h
      ├—— Layer.cpp                 //图层相关
      ├—— Layer.h
      ├—— LayerBase.cpp             //图层基类
      ├—— LayerBase.h
      ├—— LayerDim.cpp              //图层相关
      ├—— LayerDim.h
      ├—— LayerScreenshot.cpp       //图层相关
      ├—— LayerScreenshot.h
      ├—— MessageQueue.cpp          //消息队列
      ├—— GLExtensions.h
      ├—— MessageQueue.h
      ├—— MODULE_LICENSE_APACHE2    //证书
      ├—— SurfaceFlinger.cpp        //图层管理者,图层管理的核心类
      ├—— SurfaceFlinger.h
      ├—— SurfaceTextureLayer.cpp   //文字图层
      ├—— SurfaceTextureLayer.h
      ├—— Transform.cpp
      └—— Transform.h

4.网络引擎库

网络引擎库主要是用来实现Web浏览器的引擎,支持Android的Web浏览器和一个可嵌入的Web视图,这个是采用第三方开发的浏览器引擎Webkit实现的,Webkit的代码在/external/webkit/目录下,其目录结构如下所示。

      external/webkit/
      ├—— Examples                  //Webkit例子
      ├—— LayoutTests               //布局测试
      ├—— PerformanceTests          //表现测试
      ├—— Source                    //Webkit源代码
      ├—— Tools                     //工具
      ├—— WebKitLibraries           //Webkit用到的库
      ├—— Android.mk                //Makefile
      ├—— bison_check.mk
      ├—— CleanSpec.mk
      ├—— MODULE_LICENSE_LGPL       //证书
      ├—— NOTICE
      └—— WEBKIT_MERGE_REVISION     //版本信息

5.3D图形库

Android中的3D图形渲染是采用Opengl来实现的,Opengl是开源的第三方图形渲染库,使用该库可以实现Android中的3D图形硬件加速或者3D图形软件加速功能,是一个非常重要的功能库。从Android 4.3开始,支持最新、最强大的OpenGL ES 3.0。其实现代码在/frameworks/native/opengl中,其目录结构如下所示。

      frameworks/native/opengl/
      ├—— include                   //Opengl中的头文件
      ├—— libagl                    //在mac os上的库
      ├—— libs                      //Opengl的接口和实现库
      ├—— specs                     //Opengl的文档
      ├—— tests                     //测试相关
      └—— tools                     //工具库

6.SQLite

SQLite是Android系统自带的一个轻量级关系数据库,其实现源代码已经在网上开源。SQLite的具有操作简单方便、运行速度较快和占用资源较少等特点,比较适合在嵌入式设备上面使用。SQLite是Android系统自带的实现数据库功能的核心库,其代码实现分为Java和C两个部分,Java部分的代码在/frameworks/base/core/java/android/database,目录结构如下所示。

      frameworks/base/core/java/android/database/
      ├—— sqlite                                     //SQLite的框架文件
      ├—— AbstractCursor.java                       //游标的抽象类
      ├—— AbstractWindowedCursor.java
      ├—— BulkCursorDescriptor.java
      ├—— BulkCursorNative.java
      ├—— BulkCursorToCursorAdaptor.java            //游标适配器
      ├—— CharArrayBuffer.java
      ├—— ContentObservable.java
      ├—— ContentObserver.java                      //内容观察者
      ├—— CrossProcessCursor.java
      ├—— CrossProcessCursorWrapper.java            //CrossProcessCursor的封装类
      ├—— Cursor.java                               //游标实现类
      ├—— CursorIndexOutOfBoundsException.java     //游标出界异常
      ├—— CursorJoiner.java
      ├—— CursorToBulkCursorAdaptor.java            //适配器
      ├—— CursorWindow.java                         //游标窗口
      ├—— CursorWindowAllocationException.java     //游标窗口异常
      ├—— CursorWrapper.java                        //游标封装类
      ├—— DatabaseErrorHandler.java                 //数据库错误句柄
      ├—— DatabaseUtils.java                        //数据库工具类
      ├—— DataSetObservable.java
      ├—— DataSetObserver.java
      ├—— DefaultDatabaseErrorHandler.java         //默认数据库错误句柄
      ├—— IBulkCursor.java
      ├—— IContentObserver.aidl                     //aidl用于跨进程通信
      ├—— MatrixCursor.java
      ├—— MergeCursor.java
      ├—— Observable.java
      ├—— package.html
      ├—— SQLException.java                         //数据库异常
      └—— StaleDataException.java

Java层的代码主要是实现SQLite的框架和接口,方便用户开发应用程序的时候能很简单地操作数据库,并且捕获数据库异常。

C++层的代码在/external/sqlite路径下,其目录结构如下所示。

      external/sqlite/
      ├—— android        //Android数据库的一些工具包
      └—— dist            //Android数据库底层实现

从上面Java和C部分的代码目录结构可以看出,SQLite在Android中还是有很重要的地位的,并且在SDK中会有开放的接口让应用程序可以很简单方便地操作数据库,例如对数据进行存储和删除。

3.3.5 系统运行库

众所周知,Android系统的应用层是采用Java开发的,由于Java语言的跨平台特性,Java代码必须运行在虚拟机中。正是因为这个特性,Android系统自己也实现了一个类似JVM但是更适用于嵌入式平台的Java虚拟机,这被称为dalvik。

dalvik功能等同于JVM,为Android平台上的Java代码提供了运行环境,dalvik本身是由C++语言实现的,在源码中根目录下有dalvik文件夹,里面存储的是dalvik虚拟机的实现代码,其目录结构如下所示。

      ./
      ├—— dalvikvm                        //入口目录
      ├—— dexdump                         //dex反汇编
      ├—— dexgen                          //dex生成相关
      ├—— dexlist                         //dex列表
      ├—— dexopt                          //与验证和优化
      ├—— docs                            //文档
      ├—— dvz                             //zygot相关
      ├—— dx                              //dx工具,将多个Java转换为dex

      ├—— hit
      ├—— libdex                          //dex库的实现代码
      ├—— opcode-gen
      ├—— tests                           //测试相关
      ├—— tools                           //工具
      ├—— unit-tests                      //测试相关
      ├—— vm                              //虚拟机的实现
      ├—— Android.mk                      //Makefile
      ├—— CleanSpec.mk
      ├—— MODULE_LICENSE_APACHE2
      ├—— NOTICE
      └—— README.txt

正是有上面这些代码实现的Android虚拟机,所以应用程序生成的二进制执行文件能够快速、稳定地运行在Android系统上。

3.3.6 硬件抽象层

Android的硬件抽象是各种功能的底层实现,理论上不同的硬件平台会有不同的硬件抽象层实现,这一个层次也是与驱动层和硬件层有紧密的联系的,起着承上启下的作用,对上要实现应用程序框架层的接口,对下要实现一些硬件基本功能以及调用驱动层的接口。需要注意的是,这一层也是广大OEM厂商改动最大的一层,因为这一层的代码跟终端采用什么样硬件的硬件平台有很大关系。源码中存储的是硬件抽象层框架的实现代码和一些平台无关性的接口的实现。硬件抽象层代码存储在源码根目录下的hardware文件夹中,其目录结构如下所示。

      hardware/
      ├—— libhardware               //新机制硬件库
      ├—— libhardware_legacy        //旧机制硬件库
      └—— ril                       //ril模块相关的底层实现

从上面的目录结构可以看出,硬件抽象层中主要是实现了一些底层的硬件库,用来实现应用层框架层中的功能,具体硬件库中有哪些内容,可以继续细分其目录结构,例如libhardware目录下的结构如下。

      hardware/libhardware/
      ├—— include                   //入口目录
      ├—— modules                   //dex反汇编
      |   ├—— audio                 //音频相关底层库
      |   ├—— audio_remote_submix  //音频混合相关
      |   ├—— gralloc               //帧缓冲
      |   ├—— hwcomposer            //音频相关
      |   ├—— local_time            //本地时间
      |   ├—— nfc                   //nfc功能
      |   ├—— nfc-nci               //nfc的接口
      |   ├—— power                 //电源
      |   ├—— usbaudio              //USB音频设备
      |   ├—— Android.mk            //Makefile
      |   ├—— README.android
      ├—— tests                     //dex生成相关
      ├—— dexlist                   //dex列表
      ├—— dexopt                    //与验证和优化
      └—— docs                      //文档

从上面的目录结构可以分析出,libhardware目录主要是Android系统的某些功能的底层实现,包括audio、nfc和power。

libhardware_legacy的目录与libhardware大同小异,只是针对旧的实现方式做的一套硬件库,其目录下还有uevent、wifi以及虚拟机的底层实现。这两个目录下的代码一般会由设备厂家根据自身的硬件平台来实现符合Android机制的硬件库。

ril目录下存储的是无线硬件设备与电话的实现,其目录结构如下所示。

      hardware/ril/

      ├—— include                   //头文件
      ├—— libril                    //libril库
      ├—— mock-ril
      ├—— reference-ril             //reference ril库
      ├—— rild                      //ril守护进程
      └—— CleanSpec.mk

3.4 编译源码

编译Android源码的方法非常简单,只需要使用Android源码根目录下的Makefile,执行make命令即可轻松实现。当然在编译Android源码之前,首先要确定已经完成同步工作。进入Android源码目录使用make命令进行编译,使用此命令的格式如下所示。

      $: cd ~/Android4.3(这里的“Android4.3”就是下载源码的保存目录)
      $: make

编译Android源码可以得到“~/project/android/cupcake/out”目录。

整个编译过程是非常漫长的,需要读者耐心等待。

3.4.1 搭建编译环境

在编译Android源码之前,需要先进行环境搭建工作。在接下来的内容中,以Ubuntu系统为例讲解搭建编译环境以及编译Android源码的方法。具体流程如下所示。

(1)安装JDK,编译Android 4.3的源码需要JDK1.6,下载jdk-6u23-linux-i586.bin后进行安装,对应命令如下:

      $ cd /usr
      $ mkdir java
      $ cd java
      $ sudo cp jdk-6u23-linux-i586.bin所在目录 ./
      $ sudo chmod 755 jdk-6u23-linux-i586.bin
      $ sudo sh jdk-6u23-linux-i586.bin

(2)设置JDK环境变量,将如下环境变量添加到主文件夹目录下的.bashrc文件中,然后用source命令使其生效,加入的环境变量代码如下:

      export JAVA_HOME=/usr/java/jdk1.6.0_23
      export JRE_HOME=$JAVA_HOME/jre
      export CLASSPATH=.:$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH
      export PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/bin/tools.jar:$JRE_HOME/bin
      export ANDROID_JAVA_HOME=$JAVA_HOME

(3)安装需要的包,读者可以根据编译过程中的提示进行选择,可能需要的包的安装命令如下:

      $ sudo apt-get install git-core bison zlib1g-dev flex libx13-dev gperf sudo aptitude
      install  git-core  gnupg  flex  bison  gperf  libsdl-dev  libesd0-dev  libwxgtk2.6-dev
      build-essential zip curl libncurses5-dev zlib1g-dev

3.4.2 开始编译

当所依赖的包安装完成之后,就可以开始编译Android源码了,具体步骤如下所示。

(1)首先进行编译初始化工作,在终端中执行下面的命令:

      source build/envsetup.sh

或:

      . build/envsetup.sh

执行后将会输出:

      source build/envsetup.sh

      including device/asus/grouper/vendorsetup.sh
      including device/asus/tilapia/vendorsetup.sh
      including device/generic/armv7-a-neon/vendorsetup.sh
      including device/generic/armv7-a/vendorsetup.sh
      including device/generic/mips/vendorsetup.sh
      including device/generic/x86/vendorsetup.sh
      including device/samsung/maguro/vendorsetup.sh
      including device/samsung/manta/vendorsetup.sh
      including device/samsung/toroplus/vendorsetup.sh
      including device/samsung/toro/vendorsetup.sh
      including device/ti/panda/vendorsetup.sh
      including sdk/bash_completion/adb.bash

(2)然后选择编译目标,命令是:

      lunch full-eng

执行后会输出如下所示的提示信息:

      ============================================
      PLATFORM_VERSION_CODENAME=REL
      PLATFORM_VERSION=4.3
      TARGET_PRODUCT=full
      TARGET_BUILD_VARIANT=eng
      TARGET_BUILD_TYPE=release
      TARGET_BUILD_APPS=
      TARGET_ARCH=arm
      TARGET_ARCH_VARIANT=armv7-a
      HOST_ARCH=x86
      HOST_OS=linux
      HOST_OS_EXTRA=Linux-2.6.33-45-generic-x86_64-with-Ubuntu-10.04-lucid
      HOST_BUILD_TYPE=release
      BUILD_ID=JOP40C
      OUT_DIR=out
      ============================================

(3)接下来开始编译代码,在终端中执行下面的命令:

      make -j4

其中“-j4”表示用4个线程进行编译。整个编译进度根据不同机器的配置而需要不同的时间。例如这里计算机为intel i5-2300四核2.8 GHz、4 GB内存,经过近4小时才编译完成。当出现下面的信息时表示编译完成:

      target  Java:  ContactsTests  (out/target/common/obj/APPS/ContactsTests_intermediates/
      classes)
      target Dex: Contacts
      Done!
      Install: out/target/product/generic/system/app/Browser.odex
      Install: out/target/product/generic/system/app/Browser.apk
      Note: Some input files use or override a deprecated API.
      Note: Recompile with -Xlint:deprecation for details.
      Copying: out/target/common/obj/APPS/Contacts_intermediates/noproguard.classes.dex
      target Package: Contacts (out/target/product/generic/obj/APPS/Contacts_intermediates/
      package.apk)
       'out/target/common/obj/APPS/Contacts_intermediates/classes.dex' as 'classes.dex'...
      Processing target/product/generic/obj/APPS/Contacts_intermediates/package.apk
      Done!
      Install: out/target/product/generic/system/app/Contacts.odex
      Install: out/target/product/generic/system/app/Contacts.apk
      build/tools/generate-notice-files.py  out/target/product/generic/obj/NOTICE.txt  out/
      target/product/generic/obj/NOTICE.html "Notices for files contained in the filesystem
      images in this directory:" out/target/product/generic/obj/NOTICE_FILES/src
      Combining NOTICE files into HTML
      Combining NOTICE files into text
      Installed file list: out/target/product/generic/installed-files.txt
      Target   system   fs   image:   out/target/product/generic/obj/PACKAGING/systemimage_
      intermediates/system.img
      Running: mkyaffs2image -f out/target/product/generic/system out/target/product/generic/
      obj/PACKAGING/systemimage_intermediates/system.img

        Install system fs image: out/target/product/generic/system.img
        DroidDoc took 5331 sec. to write docs to out/target/common/docs/doc-comment-check

3.4.3 在模拟器中运行

之后在模拟器中运行的步骤就比较简单了,只需要在终端中执行下面的命令即可:

        emulator

运行成功后的效果如图3-13所示。

▲图3-13 在模拟器中的编译执行效果

3.4.4 常见的错误分析

虽然编译方法非常简单,但是作为初学者来说很容易出错,在下面列出了其中常见的编译错误类型。

1.缺少必要的软件

进入到Android目录下,使用make命令编译,可能会出现如下错误提示。

        host C: libneo_cgi <= external/clearsilver/cgi/cgi.c
        external/clearsilver/cgi/cgi.c:22:18: error: zlib.h: No such file or directory

上述错误是因为缺少zlib1g-dev,需要使用apt-get命令从软件仓库中安装zlib1g-dev,具体命令如下所示。

        sudo apt-get install zlib1g-dev

同理需要安装下面的软件,否则也会出现上述类似的错误。

        sudo apt-get install flex
        sudo apt-get install bison
        sudo apt-get install gperf
        sudo apt-get install libsdl-dev
        sudo apt-get install libesd0-dev
        sudo apt-get install libncurses5-dev
        sudo apt-get install libx13-dev

2.没有安装Java环境JDK

当安装所有上述软件后,运行make命令再次编译Android源码。如果在之前忘记安装Java环境JDK,则此时会出现很多Java文件无法编译的错误,如果打开Android的源码,可以看到在下面目录中发现有很多Java源文件。

        android/dalvik/libcore/dom/src/test/java/org/w3c/domts

这充分说明在编译Android之前必须先安装Java环境JDK,安装流程如下所示。

· 从Oracle官方网站下载jdk-6u16-linux-i586.bin文件,然后安装。

在Ubuntu 8.04中,“/etc/profile”文件是全局的环境变量配置文件,它适用于所有的shell。在登录Linux系统时应该先启动“/etc/profile”文件,然后再启动用户目录下的“~/.bash_profile”“~/.bash_login”或“~/.profile”文件中的其中一个,执行的顺序和上面的排序一样。如果“~/.bash_profile”文件存在话,则还会执行“~/.bashrc”文件。在此只需要把JDK的目录存储到“/etc/profile”目录下即可。

        JAVA_HOME=/usr/local/src/jdk1.6.0_16
        PATH=$PATH:$JAVA_HOME/bin:/usr/local/src/android-sdk-linux_x86-1.1_r1/tools:~/bin

· 重新启动机器,输入java -version命令,输出下面的信息则表示配置成功。

        ava version "1.6.0_16"
        Java(TM) SE Runtime Environment (build 1.6.0_16-b01)
        Java HotSpot(TM) Client VM (build 13.3-b01, mixed mode, sharing)

当成功编译Android源码后,在终端会输出如下提示。

        Target system fs image: out/target/product/generic/obj/PACKAGING/systemimage_unopt_
        intermediates/system.img
        Install system fs image: out/target/product/generic/system.img
        Target ram disk: out/target/product/generic/ramdisk.img
        Target userdata fs image: out/target/product/generic/userdata.img
        Installed file list: out/target/product/generic/installed-files.txt
        root@dfsun2009-desktop:/bin/android#

3.4.5 实践演练——演示两种编译Android程序的方法

Android编译环境本身比较复杂,并且不像普通的编译环境那样只有顶层目录下才有Makefile文件,而其他的每个component都使用统一标准的Android.mk文件。不过这并不是人们熟悉的Makefile,而是经过Android自身编译系统的很多处理。所以说要真正理清楚其中的联系还比较复杂,不过这种方式的好处在于,编写一个新的Android.mk给Android增加一个新的component会变得比较简单。为了使读者更加深入地理解在Linux环境下编译Android程序的方法,在接下来的内容中,将分别演示两种编译Android程序的方法。

1.编译Native C(本地C程序)的helloworld模块

编译Java程序可以直接采用Eclipse的集成环境来完成,实现方法非常简单,在这里就不再重复了。接下来将主要针对C/C++进行说明,通过一个例子来讲解在Android中增加一个C程序的Hello World的方法。

(1)在“$(YOUR_ANDROID)/development”目录下创建一个名为“hello”的目录,并用“$(YOUR_ANDROID)”指向Android源代码所在的目录。

        - # mkdir $(YOUR_ANDROID)/development/hello

(2)在目录“$(YOUR_ANDROID)/development/hello/”下编写一个名为“hello.c”的C语言文件,文件hello.c的实现代码如下所示。

        #include <stdio.h>
        int main()
        {
            printf("Hello World! \n"); //输出Hello World
        return 0;
        }

(3)在目录“$(YOUR_ANDROID)/development/hello/”下编写Android.mk文件。这是Android Makefile的标准命名,不能更改。文件Android.mk的格式和内容可以参考其他已有的Android.mk文件的写法,针对helloworld程序的Android.mk文件内容如下所示。

        LOCAL_PATH:= $(call my-dir)
        include $(CLEAR_VARS)
        LOCAL_SRC_FILES:= \
            hello.c
        LOCAL_MODULE := helloworld
        include $(BUILD_EXECUTABLE)

上述各个内容的具体说明如下所示。

· LOCAL_SRC_FILES:用来指定源文件用;

· LOCAL_MODULE:指定要编译的模块的名字,在下一步骤编译时将会用到;

· include $(BUILD_EXECUTABLE):表示要编译成一个可执行文件,如果想编译成动态库则可用BUILD_SHARED_LIBRARY,这些具体用法可以在“$(YOUR_ANDROID)/build/core/config.mk”查到。(4)回到Android源代码顶层目录进行编译。

      # cd $(YOUR_ANDROID) && make helloworld

在此需要注意,make helloworld中的目标名helloworld就是上面Android.mk文件中由LOCAL_MODULE指定的模块名。最终的编译结果如下所示。

      target thumb C: helloworld <= development/hello/hello.c
      target Executable: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_
      intermediates/LINKED/helloworld)
      target   Non-prelinked:   helloworld   (out/target/product/generic/symbols/system/bin/
      helloworld)
      target   Strip:   helloworld   (out/target/product/generic/obj/EXECUTABLES/helloworld_
      intermediates/helloworld)
      Install: out/target/product/generic/system/bin/helloworld

(5)如果和上述编译结果相同,则编译后的可执行文件存储在如下目录:

      out/target/product/generic/system/bin/helloworld

这样通过”adb push”将它传送到模拟器上,再通过”adb shell”登录到模拟器终端后就可以执行了。

2.手工编译C模块

在前面讲解了通过标准的Android.mk文件来编译C模块的具体流程,其实可以直接运用gcc命令行来编译C程序,这样可以更好地了解Android编译环境的细节。具体流程如下所示。

(1)在Android编译环境中,提供了”showcommands”选项来显示编译命令行,可以通过打开这个选项来查看一些编译时的细节。

(2)在具体操作之前需要使用如下命令把前面中的helloworld模块清除。

      # make clean-helloworld

上面的“make clean-$(LOCAL_MODULE)”命令是Android编译环境提供的make clean的方式。

(3)使用showcommands选项重新编译helloworld,具体命令如下所示。

      # make helloworld showcommands
      build/core/product_config.mk:229: WARNING: adding test OTA key
      target thumb C: helloworld <= development/hello/hello.c
      prebuilt/linux-x86/toolchain/arm-eabi-4.3.1/bin/arm-eabi-gcc  -I system/core/include
      -I hardware/libhardware/include   -I hardware/ril/include   -I dalvik/libnativehelper/
      include  -I frameworks/base/include   -I external/skia/include   -I out/target/product/
      generic/obj/include   -I bionic/libc/arch-arm/include   -I bionic/libc/include   -I
      bionic/libstdc++/include   -I bionic/libc/kernel/common   -I bionic/libc/kernel/arch-
      arm   -I bionic/libm/include   -I bionic/libm/include/arch/arm   -I bionic/libthread_
      db/include    -I  development/hello    -I  out/target/product/generic/obj/EXECUTABLES/
      helloworld_intermediates  -c  -fno-exceptions -Wno-multichar -march=armv5te -mtune=
      xscale  -msoft-float  -fpic  -mthumb-interwork  -ffunction-sections  -funwind-tables
      -fstack-protector -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_
      5TE__ -include system/core/include/arch/linux-arm/AndroidConfig.h -DANDROID -fmessage-
      length=0-W -Wall -Wno-unused -DSK_RELEASE -DNDEBUG -O2-g -Wstrict-aliasing=2-finline-
      functions -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop
      -frename-registers  -DNDEBUG  -UDEBUG  -mthumb  -Os  -fomit-frame-pointer  -fno-strict-
      aliasing  -finline-limit=64     -MD  -o  out/target/product/generic/obj/EXECUTABLES/
      helloworld_intermediates/hello.o development/hello/hello.c

      target Executable: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_
      intermediates/LINKED/helloworld)

      prebuilt/linux-x86/toolchain/arm-eabi-4.3.1/bin/arm-eabi-g++ -nostdlib -Bdynamic -Wl,
      -T, build/core/armelf.x -Wl, -dynamic-linker, /system/bin/linker -Wl, --gc-sections -Wl, -z,
      nocopyreloc -o out/target/product/generic/obj/EXECUTABLES/helloworld_ intermediates/
      LINKED/helloworld  -Lout/target/product/generic/obj/lib  -Wl, -rpath-link=  out/target/
      product/generic/obj/lib-lc-lstdc++-lm  out/target/product/generic/obj/lib/crtbegin_
      dynamic.o out/target/product/generic/obj/EXECUTABLES/helloworld_ intermediates/hello.o

      -Wl, --no-undefined  prebuilt/linux-x86/toolchain/arm-eabi-4.3.1/  bin/../lib/gcc/arm-
      eabi/4.3.1/interwork/libgcc.a out/target/product/generic/obj/lib/crtend_android.o

      target Non-prelinked: helloworld (out/target/product/generic/symbols/system/bin/
      helloworld)

      out/host/linux-x86/bin/acp -fpt out/target/product/generic/obj/EXECUTABLES/
      helloworld_intermediates/LINKED/helloworld out/target/product/generic/symbols/system/
      bin/helloworld

      target Strip: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_
      intermediates/helloworld)

      out/host/linux-x86/bin/soslim --strip --shady --quiet out/target/product/generic/
      symbols/system/bin/helloworld --outfile out/target/product/generic/obj/EXECUTABLES/
      helloworld_intermediates/helloworld

      Install: out/target/product/generic/system/bin/helloworld

      out/host/linux-x86/bin/acp -fpt out/target/product/generic/obj/EXECUTABLES/
      helloworld_intermediates/helloworld out/target/product/generic/system/bin/helloworld

从上述命令行可以看到,Android编译环境所用的交叉编译工具链如下所示。

      prebuilt/linux-x86/toolchain/arm-eabi-4.3.1/bin/arm-eabi-gcc

其中参数“-I”和“-L”分别指定了所用的C库头文件和动态库文件路径,分别是“bionic/libc/include”和“out/target/product/generic/obj/lib”,其他还包括很多编译选项以及-D所定义的预编译宏。

(4)此时可以利用上面的编译命令手工编译helloworld程序,首先手工删除上次编译得到的helloworld程序。

      # rm out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/hello.o
      # rm out/target/product/generic/system/bin/helloworld

然后再用gcc编译以生成目标文件。

      #  prebuilt/linux-x86/toolchain/arm-eabi-4.3.1/bin/arm-eabi-gcc  -I  bionic/libc/arch-
      arm/include  -I  bionic/libc/include  -I  bionic/libc/kernel/common    -I  bionic/libc/
      kernel/arch-arm  -c    -fno-exceptions  -Wno-multichar  -march=armv5te  -mtune=xscale
      -msoft-float  -fpic  -mthumb-interwork  -ffunction-sections  -funwind-tables  -fstack-
      protector  -D__ARM_ARCH_5__  -D__ARM_ARCH_5T__  -D__ARM_ARCH_5E__  -D__ARM_ARCH_5TE__
      -include   system/core/include/arch/linux-arm/AndroidConfig.h   -DANDROID   -fmessage-
      length=0  -W  -Wall  -Wno-unused  -DSK_RELEASE  -DNDEBUG  -O2  -g  -Wstrict-aliasing=2
      -finline-functions -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-
      after-loop  -frename-registers  -DNDEBUG  -UDEBUG  -mthumb  -Os  -fomit-frame-pointer
      -fno-strict-aliasing -finline-limit=64-MD -o out/target/product/generic/obj/ EXECUTABLES/
      helloworld_intermediates/hello.o development/hello/hello.c

如果此时与Android.mk编译参数进行比较,会发现上面主要减少了不必要的参数“-I”。

(5)接下来开始生成可执行文件。

      #  prebuilt/linux-x86/toolchain/arm-eabi-4.3.1/bin/arm-eabi-gcc  -nostdlib  -Bdynamic
      -Wl, -T, build/core/armelf.x  -Wl, -dynamic-linker, /system/bin/linker  -Wl, --gc-sections
      -Wl, -z, nocopyreloc -o out/target/product/generic/obj/EXECUTABLES/helloworld_ intermediates/
      LINKED/helloworld  -Lout/target/product/generic/obj/lib  -Wl, -rpath-link=out/target/
      product/generic/obj/lib   -lc   -lm   out/target/product/generic/obj/EXECUTABLES/
      helloworld_intermediates/hello.o out/target/product/generic/obj/lib/ crtbegin_dynamic.o
      -Wl, --no-undefined ./prebuilt/linux-x86/toolchain/arm-eabi-4.3.1/ bin/../lib/gcc/arm-
      eabi/4.3.1/interwork/libgcc.a out/target/product/generic/obj/lib/crtend_android.o

在此需要特别注意的是参数“-Wl, -dynamic-linker, /system/bin/linker”,它指定了Android专用的动态链接器是“/system/bin/linker”,而不是平常使用的“ld.so”。

(6)最后可以使用命令file和readelf来查看生成的可执行程序。

      #  file  out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/
      helloworld
      out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworl

      d: ELF 33-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs),
      not stripped
      #   readelf  -d  out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/
      LINKED/helloworld |grep NEEDED
       0x00000001 (NEEDED)                  Shared library: [libc.so]
       0x00000001 (NEEDED)                  Shared library: [libm.so]

这就是ARM格式的动态链接可执行文件,在运行时需要libc.so和libm.so。当提示“not stripped”时表示它还没被STRIP(剥离)。嵌入式系统中为节省空间通常将编译完成的可执行文件或动态库进行剥离,即去掉其中多余的符号表信息。在前面“make helloworld showcommands”命令的最后也可以看到,Android编译环境中使用了“out/host/linux-x86/bin/soslim”工具进行STRIP。

3.5 编译Android Kernel

编译Android Kernel代码就是编译Android内核代码,在进行具体编译工作,之前需要先了解在Android开源系统中包含的如下3部分代码。

· 仿真器公共代码:对应的工程名是kernel/common.get;

· MSM平台的内核代码:对应的工程名是kernel/msm.get;

· OMAP平台的内核代码:对应的工程名是kernel/omap.get。

在本节的内容中,将详细讲解编译上述Android Kernel的基本知识。

3.5.1 获取Goldfish内核代码

Goldfish是一种虚拟的ARM处理器,通常在Android的仿真环境中使用。在Linux的内核中,Goldfish作为ARM体系结构的一种“机器”。在Android的发展过程中,Goldfish内核的版本也从Linux 2.6.25升级到了Linux 3.4,此处理器的Linux内核和标准的Linux内核有以下3个方面的差别。

· Goldfish机器的移植;

· Goldfish一些虚拟设备的驱动程序;

· Android中特有的驱动程序和组件。

Goldfish处理器有两个版本,分别是ARMv5和ARMv7,在一般情况下,只需要使用ARMv5版本即可。在Android开源工程的代码仓库中,使用git工具得到Goldfish内核代码的命令如下所示。

      $ git clone git://android.git.kernel.org/kernel/common.git

在其Linux源代码的根目录中,配置和编译Goldfish内核的过程如下所示。

      $make ARCH=arm goldfish_defconfig .config
      $make ARCH=arm CROSS_COMPILE={path}/arm-none-linux-gnueabi-

其中,CROSS_COMPILE的path值用于指定交叉编译工具的路径。

编译结果如下所示。

      LD vmlinux
      SYSMAP system.map
      SYSMAP .tmp_system.map
      OBJCOPY arch/arm/boot/Image
      Kernel: arch/arm/boot/Image is ready
      AS arch/arm/boot/compressed/head.o
      GZIP arch/arm/boot/compressed/piggy.gz
      AS arch/arm/boot/compressed/piggy.o
      CC arch/arm/boot/compressed/misc.o
      LD arch/arm/boot/compressed/vmlinux
        OBJCONPY arch/arm/boot/zImage
        Kernel: arch/arm/boot/zImage is ready

· vmlinux:是Linux进行编译和连接之后生成的Elf格式的文件;

· Image:是未经过压缩的二进制文件;

· piggy:是一个解压缩程序;

· zImage:是解压缩程序和压缩内核的组合。

在Android源代码的根目录中,vmlinux和zImage对应Android代码prebuilt中的预编译的arm内核。使用zImage可以替换prebuilt中的“prebuilt/android-arm/”目录下的goldfish_defconfig,此文件的主要片断如下所示。

      CONFIG_ARM=y
      #
      # System Type
      #
      CONFIG_ARCH_GOLDFISH=y
      #
      # Goldfish options
      #
      CONFIG_MACH_GOLDFISH=y
      # CONFIG_MACH_GOLDFISH_ARMV7 is not set

因为GoldFish是ARM处理器,所以CONFIG_ARM宏需要被使用,CONFIG_ARCH_GOLDFISH和CONFIG_MACH_GOLDFISH宏是GoldFish处理器这类机器使用的配置宏。

在gildfish_defconfig中,与Android系统相关的宏如下所示。

      #
      # android
      #
      CONFIG_ANDROID=y
      CONFIG_ANDROID_BUNDER_IPC=y #binder ipc驱动程序
      CONFIG_ANDROID_LOGGER=y #log记录器驱动程序
      # CONFIG_ANDROID_RAM_CONSOLE is not set
      CONFIG_ANDROID_TIMED_OUTPUT=y #定时输出驱动程序框架
      CONFIG_ANDROID_LOW_MEMORY_KILLER=y
      CONFIG_ANDROID_PMEM=y #物理内存驱动程序
      CONFIG_ASHMEM=y #匿名共享内存驱动程序
      CONFIG_RTC_INTF_ALARM=y
      CONFIG_HAS_WAKELOCK=y电源管理相关的部分wakelock和earlysuspend
      CONFIG_HAS_EARLYSUSPEND=y
      CONFIG_WAKELOCK=y
      CONFIG_WAKELOCK_STAT=y
      CONFIG_USER_WAKELOCK=y
      CONFIG_EARLYSUSPEND=y
      goldfish_defconfig配置文件中,另外有一个宏是处理器虚拟设备的“驱动程序”,其内容如下所示。
      CONFIG_MTD_GOLDFISH_NAND=y
      CONFIG_KEYBOARD_GOLDFISH_EVENTS=y
      CONFIG_GOLDFISH_TTY=y
      CONFIG_BATTERY_GOLDFISH=y
      CONFIG_FB_GOLDFISH=y
      CONFIG_MMC_GOLDFISH=y
      CONFIG_RTC_DRV_GOLDFISH=y

在Goldfish处理器的各个配置选项中,体系结构和Goldfish的虚拟驱动程序基于标准Linux的内容的驱动程序框架,但是这些设备在不同的硬件平台的移植方式不同;Android专用的驱动程序是Android中特有的内容,非Linux标准,但是和硬件平台无关。

和原Linux内核相比,Android内核增加了Android的相关Driver,对应的目录如下所示。

      kernel/drivers/android

主要分为以下几类Driver。

· Android IPC系统:Binder (binder.c);

· Android日志系统:Logger (logger.c);

· Android电源管理:Power (power.c);

· Android闹钟管理:Alarm (alarm.c);

· Android内存控制台:Ram_console (ram_console.c);

· Android时钟控制的gpio:Timed_gpio (timed_gpio.c)。

对于本书讲解的驱动程序开发来说,人们比较关心的是GoldFish平台下相关的驱动文件,具体说明如下所示。

1.字符输出设备

      kernel/drivers/char/goldfish_tty.c

2.图像显示设备(Frame Buffer)

      kernel/drivers/video/goldfishfb.c

3.键盘输入设备文件

      kernel/drivers/input/keyboard/goldfish_events.c

4.RTC设备(Real Time Clock)文件

      kernel/drivers/rtc/rtc-goldfish.c

5.USB Device设备文件

      kernel/drivers/usb/gadget/android_adb.c

6.SD卡设备文件

      kernel/drivers/mmc/host/goldfish.c

7.FLASH设备文件

      kernel/drivers/mtd/devices/goldfish_nand.c
      kernel/drivers/mtd/devices/goldfish_nand_reg.h

8.LED设备文件

      kernel/drivers/leds/ledtrig-sleep.c

9.电源设备

      kernel/drivers/power/goldfish_battery.c

10.音频设备

      kernel/arch/arm/mach-goldfish/audio.c

11.电源管理

      kernel/arch/arm/mach-goldfish/pm.c

12.时钟管理

      kernel/arch/arm/mach-goldfish/timer.c

3.5.2 获取MSM内核代码

在当前市面中,谷歌的手机产品G1是基于MSM内核的,MSM是高通公司的应用处理器,在Android代码库中公开了对应的MSM的源代码。在Android开源工程的代码仓库中,使用git工具得到MSM内核代码的命令如下所示。

      $ git clone git://android.git.kernel.org/kernel/msm.git

3.5.3 获取OMAP内核代码

OMAP是德州仪器公司的应用处理器,为Android使用的是OMAP3系列的处理器。在Android代码库中公开了对应的MSM的源代码,使用git工具得到MSM内核代码的命令如下所示。

      $ git clone git://android.git.kernel.org/kernel/omap.git

3.5.4 编译Android的Linux内核

了解了上述3类Android内核后,下面开始讲解编译Android内核的方法。在此假设以Ubuntu 8.10为例,完整编译Android内核的流程如下所示。

1.构建交叉编译环境

Android的默认硬件处理器是ARM,因此需要在自己的机器上构建交叉编译环境。交叉编译器GNU Toolchain for ARM Processors下载地址如下所示。

      http://www.codesourcery.com/gnu_toolchains/arm/download.html

单击GNU/Linux对应的链接,之后单击“Download Sourcery CodeBench Lite 5.12012.03-117”链接后直接下载,如图3-14所示。

▲图3-14 下载交叉编译器

把arm-2008q3-73-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2解压到一目录下,例如“~/programes/”,并加入PATH环境变量:

      vim ~/.bashrc

然后添加:

      ARM_TOOLCHIAN=~/programes/arm-2008q3/bin/
      export PATH=${PATH}:${ARM_TOOLCHIAN};

保存后并入source ~/.bashrc。

2.获取内核源码

源码地址如下所示。

      http://code.google.com/p/android/downloads/list

选择的内核版本要与选用的模拟器版本尽量一致。下载后并解压后得到kernel.git文件夹:

      tar -xvf ~/download/linux-3.2.5-android-4.3_r1.tar.gz

3.获取内核编译配置信息文件

编译内核时需要使用configure,通常configure有很多选项,人们往往不知道需要哪些选项。在运行Android模拟器时,有一个文件“/proc/config.gz”,这是当前内核的配置信息文件,把config.gz获取并解压到“kernel.git/”下,然后改名为.config。命令如下所示。

      cd kernel.git/
      emulator &
      adb pull /proc/config.gz
      gunzip config.gz
      mv config .config

4.修改Makefile

修改195行的代码:

      CROSS_COMPILE    = arm-none-linux-gnueabi-

将CROSS_COMPILE值改为arm-none-linux-gnueabi-,这是安装的交叉编译工具链的前缀,修改此处意在告诉make在编译的时候要使用该工具链。然后注释掉562和563行的如下代码:

      #LDFLAGS_BUILD_ID = $(patsubst -Wl$(comma)%, %, /
      #$(call ld-option, -Wl$(comma)--build-id, ))

必须将上述代码中的build id值注释掉,因为目前版本的Android内核不支持该选项。

5.编译

使用make进行编译,并同时生成zImage。

      LD     arch/arm/boot/compressed/vmlinux
      OBJCOPY arch/arm/boot/zImage
      Kernel: arch/arm/boot/zImage is ready

这样生成zImage大小为1.23 MB, android- sdk-linux_x86-4.3_r1/tools/lib/images/kernel-qemu是1.24 MB。

6.使用模拟器加载内核测试

命令如下所示。

      cd android/out/cupcake/out/target/product/generic
      emulator   -image   system.img   -data   userdata.img   -ramdisk   ramdisk.img   -kernel
      ~/project/android/kernel.git/arch/arm/boot/zImage &

到此为止,模拟器就加载成功了。

3.6 编译源码生成SDK

平时大部分Android应用程序开发都是基于SDK实现的,其过程是使用SDK中的接口实现各种各样的功能。可以在Android的官方网站上面直接下载最新的SDK版本,不过也可以从源码中生成SDK,因为源码里面也包含有SDK的代码。

在下载的Android 4.3的源码的根目录下有个SDK目录,所有的SDK相关的代码都存储在这个目录,包括镜像文件、模拟器和ADB等常用工具以及SDK中的开发包的文档,可以通过编译的方式来生成开发需要的SDK,编译命令如下所示。

      $ Make SDK

当编译完成后,会在/out/host/linux-x86/sdk/目录下生成SDK,这个SDK是完全跟源码同步的,与官网上下载的SDK功能完全相同,会有开发用的JAR包、模拟器管理工具和ADB调试工具,可以使用这个编译生成的SDK来开发应用程序。

对于Android系统的开发,基本可以分为如下两种开发方式。

· 基于SDK的开发;

· 基于源码的开发。

在一般的情况下,开发的应用程序都是基于SDK的开发,比较方便而且兼容性比较好。基于源码的开发相对于基于SDK的开发要求对源码的架构认识更深刻,一般用于需要修改系统层面的场合。两种方式应用场景不同,各有优缺点,在本节将主要介绍基于SDK的开发。

如果想基于SDK开发Android的应用程序,需要JDK、SDK和一个开发环境,JDK和SDK在不同的平台下有不同的版本,本章主要讨论Windows 7平台下的开发环境搭建。

1.安装JDK

由于Android的应用程序是使用Java语言开发的,所以首先需要安装Java的JDK,下载链接为http://java.sun.com/javase/downloads/index.jsp,进入后选择合适的平台以及下载最新版本的JDK,安装成功后在命令行下可以查看JDK版本,如图3-15所示。

▲图3-15 成功安装的JDK

2.安装Eclipse

Eclipse是开发Android应用程序的IDE环境,有非常丰富的插件可以使用,单击http://www.eclipse.org/downloads/可以下载合适平台的最新版本Eclipse。

3.安装Android SDK

Android SDK是Google对外发布的专门用于Android开发的工具包,里面有各种版本的开发框架和工具以及丰富的文档,打开http://developer.android.com/sdk/index.html可以下载最新版本的针对Window 7平台的SDK。

当下载完成上述3个工具之后,需要对开发环境进行如下所示的配置。

1.配置Eclipse

第1步:打开Eclipse,在菜单栏上选择“help”|“Install New SoftWare”,出现图3-16所示的界面。

▲图3-16 “Install”界面

第2步:单击“Add”按钮,会出现图3-17所示的界面。

▲图3-17 “Add Repository”界面

第3步:在Name栏里面输入“Android”或者自定义任何名字,在Location里面输入“https://dl-ssl.google.com/android/eclipse/”,输入后的效果如图3-18所示。

▲图3-18 “Add Site”界面

第4步:如果发现https://无法使用,可以改成http://尝试下,当输入完名字和地址之后,单击“OK”按钮,会出现图3-19所示的界面。

▲图3-19 “Install”界面

图3-19中的两个插件都是开发Android必不可少的工具包,Android DDMS是可以用来调试、管理Android进程和存储器、查看日志的工具,Android Development Tool简称ADT,是开发Android的插件,只有装了ADT才能创建Android工程。

第5步:单击“Next”按钮,出现图3-20所示的界面。

▲图3-20 选择安装

在图3-20中列出了将会安装的工具包,选中“I accept the terms of the license agreements”选项,单击“Next”按钮会开始安装插件,界面如图3-21所示。

▲图3-21 开始安装

第6步:当所有插件安装成功后,会弹出提示界面,如图3-22所示。

▲图3-22 成功

这时需要单击“Yes”按钮重启Eclipse让所有插件生效。

2.配置Android SDK

打开Eclipse,单击“Window”|“Preferences”,进入图3-23所示的界面。

▲图3-23 配置界面

这样就可以从Eclipse中新建Android工程,要想新建工程是基于何种版本的Android系统,可以打开SDK根目录下的SDK管理工具SDK Manager.exe,双击后会进入SDK工具包管理界面,如图3-24所示。

▲图3-24 Android SDK管理

在图3-24中可以看到,很清晰地列出了当前版本SDK中包含的工具包以及已经安装的和没有安装的版本。可以继续单击“Install 11 packages”或者“Delete 8 packages”按钮安装和删除SDK中的工具包。如果是安装,则过程会比较慢,与网速的关系比较密切。当SDK中的工具包安装完毕,同时也就完成了Eclipse和SDK的配置工作,至此在Windows 7平台下基于SDK的Android的开发环境搭建全部完成。